001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.journal.service.impl;
016    
017    import com.liferay.portal.LocaleException;
018    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
020    import com.liferay.portal.kernel.dao.orm.Property;
021    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
022    import com.liferay.portal.kernel.dao.orm.QueryDefinition;
023    import com.liferay.portal.kernel.dao.orm.QueryUtil;
024    import com.liferay.portal.kernel.exception.PortalException;
025    import com.liferay.portal.kernel.exception.SystemException;
026    import com.liferay.portal.kernel.json.JSONFactoryUtil;
027    import com.liferay.portal.kernel.json.JSONObject;
028    import com.liferay.portal.kernel.language.LanguageUtil;
029    import com.liferay.portal.kernel.lar.ExportImportThreadLocal;
030    import com.liferay.portal.kernel.log.Log;
031    import com.liferay.portal.kernel.log.LogFactoryUtil;
032    import com.liferay.portal.kernel.sanitizer.SanitizerUtil;
033    import com.liferay.portal.kernel.search.Field;
034    import com.liferay.portal.kernel.search.Hits;
035    import com.liferay.portal.kernel.search.Indexable;
036    import com.liferay.portal.kernel.search.IndexableType;
037    import com.liferay.portal.kernel.search.Indexer;
038    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
039    import com.liferay.portal.kernel.search.QueryConfig;
040    import com.liferay.portal.kernel.search.SearchContext;
041    import com.liferay.portal.kernel.search.Sort;
042    import com.liferay.portal.kernel.systemevent.SystemEvent;
043    import com.liferay.portal.kernel.systemevent.SystemEventHierarchyEntryThreadLocal;
044    import com.liferay.portal.kernel.template.TemplateConstants;
045    import com.liferay.portal.kernel.util.ArrayUtil;
046    import com.liferay.portal.kernel.util.CalendarFactoryUtil;
047    import com.liferay.portal.kernel.util.CharPool;
048    import com.liferay.portal.kernel.util.Constants;
049    import com.liferay.portal.kernel.util.ContentTypes;
050    import com.liferay.portal.kernel.util.FileUtil;
051    import com.liferay.portal.kernel.util.GetterUtil;
052    import com.liferay.portal.kernel.util.HtmlUtil;
053    import com.liferay.portal.kernel.util.HttpUtil;
054    import com.liferay.portal.kernel.util.ListUtil;
055    import com.liferay.portal.kernel.util.LocaleUtil;
056    import com.liferay.portal.kernel.util.LocalizationUtil;
057    import com.liferay.portal.kernel.util.MathUtil;
058    import com.liferay.portal.kernel.util.ObjectValuePair;
059    import com.liferay.portal.kernel.util.OrderByComparator;
060    import com.liferay.portal.kernel.util.ParamUtil;
061    import com.liferay.portal.kernel.util.PropsKeys;
062    import com.liferay.portal.kernel.util.StringBundler;
063    import com.liferay.portal.kernel.util.StringPool;
064    import com.liferay.portal.kernel.util.StringUtil;
065    import com.liferay.portal.kernel.util.Time;
066    import com.liferay.portal.kernel.util.UnicodeProperties;
067    import com.liferay.portal.kernel.util.Validator;
068    import com.liferay.portal.kernel.workflow.WorkflowConstants;
069    import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
070    import com.liferay.portal.kernel.xml.Document;
071    import com.liferay.portal.kernel.xml.DocumentException;
072    import com.liferay.portal.kernel.xml.Element;
073    import com.liferay.portal.kernel.xml.Node;
074    import com.liferay.portal.kernel.xml.SAXReaderUtil;
075    import com.liferay.portal.kernel.xml.XPath;
076    import com.liferay.portal.model.Company;
077    import com.liferay.portal.model.Group;
078    import com.liferay.portal.model.Image;
079    import com.liferay.portal.model.ResourceConstants;
080    import com.liferay.portal.model.SystemEventConstants;
081    import com.liferay.portal.model.User;
082    import com.liferay.portal.service.ServiceContext;
083    import com.liferay.portal.service.ServiceContextUtil;
084    import com.liferay.portal.servlet.filters.cache.CacheUtil;
085    import com.liferay.portal.theme.ThemeDisplay;
086    import com.liferay.portal.util.PortalUtil;
087    import com.liferay.portal.util.PortletKeys;
088    import com.liferay.portal.util.PrefsPropsUtil;
089    import com.liferay.portal.util.PropsValues;
090    import com.liferay.portal.util.SubscriptionSender;
091    import com.liferay.portal.webserver.WebServerServletTokenUtil;
092    import com.liferay.portlet.asset.model.AssetEntry;
093    import com.liferay.portlet.asset.model.AssetLink;
094    import com.liferay.portlet.asset.model.AssetLinkConstants;
095    import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
096    import com.liferay.portlet.dynamicdatamapping.NoSuchTemplateException;
097    import com.liferay.portlet.dynamicdatamapping.StorageFieldNameException;
098    import com.liferay.portlet.dynamicdatamapping.StorageFieldRequiredException;
099    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
100    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
101    import com.liferay.portlet.dynamicdatamapping.storage.FieldConstants;
102    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
103    import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
104    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
105    import com.liferay.portlet.journal.ArticleContentException;
106    import com.liferay.portlet.journal.ArticleDisplayDateException;
107    import com.liferay.portlet.journal.ArticleExpirationDateException;
108    import com.liferay.portlet.journal.ArticleIdException;
109    import com.liferay.portlet.journal.ArticleReviewDateException;
110    import com.liferay.portlet.journal.ArticleSmallImageNameException;
111    import com.liferay.portlet.journal.ArticleSmallImageSizeException;
112    import com.liferay.portlet.journal.ArticleTitleException;
113    import com.liferay.portlet.journal.ArticleTypeException;
114    import com.liferay.portlet.journal.ArticleVersionException;
115    import com.liferay.portlet.journal.DuplicateArticleIdException;
116    import com.liferay.portlet.journal.NoSuchArticleException;
117    import com.liferay.portlet.journal.StructureXsdException;
118    import com.liferay.portlet.journal.model.JournalArticle;
119    import com.liferay.portlet.journal.model.JournalArticleConstants;
120    import com.liferay.portlet.journal.model.JournalArticleDisplay;
121    import com.liferay.portlet.journal.model.JournalArticleResource;
122    import com.liferay.portlet.journal.model.JournalFolder;
123    import com.liferay.portlet.journal.model.impl.JournalArticleDisplayImpl;
124    import com.liferay.portlet.journal.service.base.JournalArticleLocalServiceBaseImpl;
125    import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
126    import com.liferay.portlet.journal.social.JournalActivityKeys;
127    import com.liferay.portlet.journal.util.JournalUtil;
128    import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
129    import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
130    import com.liferay.portlet.journalcontent.util.JournalContentUtil;
131    import com.liferay.portlet.social.model.SocialActivityConstants;
132    import com.liferay.portlet.trash.model.TrashEntry;
133    import com.liferay.portlet.trash.model.TrashVersion;
134    import com.liferay.portlet.trash.util.TrashUtil;
135    
136    import java.io.File;
137    import java.io.IOException;
138    import java.io.Serializable;
139    
140    import java.util.ArrayList;
141    import java.util.Calendar;
142    import java.util.Date;
143    import java.util.HashMap;
144    import java.util.HashSet;
145    import java.util.LinkedHashMap;
146    import java.util.List;
147    import java.util.Locale;
148    import java.util.Map;
149    import java.util.Set;
150    
151    import javax.portlet.PortletPreferences;
152    
153    /**
154     * Provides the local service for accessing, adding, deleting, and updating web
155     * content articles.
156     *
157     * <p>
158     * The web content articles hold HTML content wrapped in XML. The XML lets you
159     * specify the article's default locale and available locales. Here is a content
160     * example:
161     * </p>
162     *
163     * <p>
164     * <pre>
165     * <code>
166     * &lt;?xml version='1.0' encoding='UTF-8'?&gt;
167     * &lt;root default-locale="en_US" available-locales="en_US"&gt;
168     *      &lt;static-content language-id="en_US"&gt;
169     *              &lt;![CDATA[&lt;p&gt;&lt;b&gt;&lt;i&gt;test&lt;i&gt; content&lt;b&gt;&lt;/p&gt;]]&gt;
170     *      &lt;/static-content&gt;
171     * &lt;/root&gt;
172     * </code>
173     * </pre>
174     * </p>
175     *
176     * @author Brian Wing Shun Chan
177     * @author Raymond Aug??
178     * @author Bruno Farache
179     * @author Juan Fern??ndez
180     * @author Sergio Gonz??lez
181     */
182    public class JournalArticleLocalServiceImpl
183            extends JournalArticleLocalServiceBaseImpl {
184    
185            /**
186             * Adds a web content article with additional parameters.
187             *
188             * @param  userId the primary key of the web content article's creator/owner
189             * @param  groupId the primary key of the web content article's group
190             * @param  folderId the primary key of the web content article folder
191             * @param  classNameId the primary key of the DDMStructure class if the web
192             *         content article is related to a DDM structure, the primary key of
193             *         the class name associated with the article, or {@link
194             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
195             * @param  classPK the primary key of the DDM structure, if the primary key
196             *         of the DDMStructure class is given as the
197             *         <code>classNameId</code> parameter, the primary key of the class
198             *         associated with the web content article, or <code>0</code>
199             *         otherwise
200             * @param  articleId the primary key of the web content article
201             * @param  autoArticleId whether to auto generate the web content article ID
202             * @param  version the web content article's version
203             * @param  titleMap the web content article's locales and localized titles
204             * @param  descriptionMap the web content article's locales and localized
205             *         descriptions
206             * @param  content the HTML content wrapped in XML. For more information,
207             *         see the content example in the class description for {@link
208             *         JournalArticleLocalServiceImpl}.
209             * @param  type the structure's type, if the web content article is related
210             *         to a DDM structure. For more information, see {@link
211             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
212             * @param  ddmStructureKey the primary key of the web content article's DDM
213             *         structure, if the article is related to a DDM structure, or
214             *         <code>null</code> otherwise
215             * @param  ddmTemplateKey the primary key of the web content article's DDM
216             *         template (optionally <code>null</code>). If the article is
217             *         related to a DDM structure, the template's structure must match
218             *         it.
219             * @param  layoutUuid the unique string identifying the web content
220             *         article's display page
221             * @param  displayDateMonth the month the web content article is set to
222             *         display
223             * @param  displayDateDay the calendar day the web content article is set to
224             *         display
225             * @param  displayDateYear the year the web content article is set to
226             *         display
227             * @param  displayDateHour the hour the web content article is set to
228             *         display
229             * @param  displayDateMinute the minute the web content article is set to
230             *         display
231             * @param  expirationDateMonth the month the web content article is set to
232             *         expire
233             * @param  expirationDateDay the calendar day the web content article is set
234             *         to expire
235             * @param  expirationDateYear the year the web content article is set to
236             *         expire
237             * @param  expirationDateHour the hour the web content article is set to
238             *         expire
239             * @param  expirationDateMinute the minute the web content article is set to
240             *         expire
241             * @param  neverExpire whether the web content article is not set to auto
242             *         expire
243             * @param  reviewDateMonth the month the web content article is set for
244             *         review
245             * @param  reviewDateDay the calendar day the web content article is set for
246             *         review
247             * @param  reviewDateYear the year the web content article is set for review
248             * @param  reviewDateHour the hour the web content article is set for review
249             * @param  reviewDateMinute the minute the web content article is set for
250             *         review
251             * @param  neverReview whether the web content article is not set for review
252             * @param  indexable whether the web content article is searchable
253             * @param  smallImage whether the web content article has a small image
254             * @param  smallImageURL the web content article's small image URL
255             * @param  smallImageFile the web content article's small image file
256             * @param  images the web content's images
257             * @param  articleURL the web content article's accessible URL
258             * @param  serviceContext the service context to be applied. Can set the
259             *         UUID, creation date, modification date, expando bridge
260             *         attributes, guest permissions, group permissions, asset category
261             *         IDs, asset tag names, asset link entry IDs, the "urlTitle"
262             *         attribute, and workflow actions for the web content article. Can
263             *         also set whether to add the default guest and group permissions.
264             * @return the web content article
265             * @throws PortalException if a portal exception occurred
266             * @throws SystemException if a system exception occurred
267             */
268            @Indexable(type = IndexableType.REINDEX)
269            @Override
270            public JournalArticle addArticle(
271                            long userId, long groupId, long folderId, long classNameId,
272                            long classPK, String articleId, boolean autoArticleId,
273                            double version, Map<Locale, String> titleMap,
274                            Map<Locale, String> descriptionMap, String content, String type,
275                            String ddmStructureKey, String ddmTemplateKey, String layoutUuid,
276                            int displayDateMonth, int displayDateDay, int displayDateYear,
277                            int displayDateHour, int displayDateMinute, int expirationDateMonth,
278                            int expirationDateDay, int expirationDateYear,
279                            int expirationDateHour, int expirationDateMinute,
280                            boolean neverExpire, int reviewDateMonth, int reviewDateDay,
281                            int reviewDateYear, int reviewDateHour, int reviewDateMinute,
282                            boolean neverReview, boolean indexable, boolean smallImage,
283                            String smallImageURL, File smallImageFile,
284                            Map<String, byte[]> images, String articleURL,
285                            ServiceContext serviceContext)
286                    throws PortalException, SystemException {
287    
288                    // Article
289    
290                    User user = userPersistence.findByPrimaryKey(userId);
291                    articleId = StringUtil.toUpperCase(articleId.trim());
292    
293                    Date displayDate = null;
294                    Date expirationDate = null;
295                    Date reviewDate = null;
296    
297                    if (classNameId == JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
298                            displayDate = PortalUtil.getDate(
299                                    displayDateMonth, displayDateDay, displayDateYear,
300                                    displayDateHour, displayDateMinute, user.getTimeZone(),
301                                    ArticleDisplayDateException.class);
302    
303                            if (!neverExpire) {
304                                    expirationDate = PortalUtil.getDate(
305                                            expirationDateMonth, expirationDateDay, expirationDateYear,
306                                            expirationDateHour, expirationDateMinute,
307                                            user.getTimeZone(), ArticleExpirationDateException.class);
308                            }
309    
310                            if (!neverReview) {
311                                    reviewDate = PortalUtil.getDate(
312                                            reviewDateMonth, reviewDateDay, reviewDateYear,
313                                            reviewDateHour, reviewDateMinute, user.getTimeZone(),
314                                            ArticleReviewDateException.class);
315                            }
316                    }
317    
318                    byte[] smallImageBytes = null;
319    
320                    try {
321                            smallImageBytes = FileUtil.getBytes(smallImageFile);
322                    }
323                    catch (IOException ioe) {
324                    }
325    
326                    Date now = new Date();
327    
328                    if (autoArticleId) {
329                            articleId = String.valueOf(counterLocalService.increment());
330                    }
331    
332                    validate(
333                            user.getCompanyId(), groupId, classNameId, articleId, autoArticleId,
334                            version, titleMap, content, type, ddmStructureKey, ddmTemplateKey,
335                            expirationDate, smallImage, smallImageURL, smallImageFile,
336                            smallImageBytes, serviceContext);
337    
338                    serviceContext.setAttribute("articleId", articleId);
339    
340                    long id = counterLocalService.increment();
341    
342                    long resourcePrimKey =
343                            journalArticleResourceLocalService.getArticleResourcePrimKey(
344                                    serviceContext.getUuid(), groupId, articleId);
345    
346                    JournalArticle article = journalArticlePersistence.create(id);
347    
348                    Locale locale = getArticleDefaultLocale(content, serviceContext);
349    
350                    String title = titleMap.get(locale);
351    
352                    content = format(
353                            user, groupId, articleId, version, false, content, ddmStructureKey,
354                            images);
355    
356                    article.setResourcePrimKey(resourcePrimKey);
357                    article.setGroupId(groupId);
358                    article.setCompanyId(user.getCompanyId());
359                    article.setUserId(user.getUserId());
360                    article.setUserName(user.getFullName());
361                    article.setCreateDate(serviceContext.getCreateDate(now));
362                    article.setModifiedDate(serviceContext.getModifiedDate(now));
363                    article.setFolderId(folderId);
364                    article.setClassNameId(classNameId);
365                    article.setClassPK(classPK);
366                    article.setTreePath(article.buildTreePath());
367                    article.setArticleId(articleId);
368                    article.setVersion(version);
369                    article.setTitleMap(titleMap, locale);
370                    article.setUrlTitle(
371                            getUniqueUrlTitle(id, articleId, title, null, serviceContext));
372                    article.setDescriptionMap(descriptionMap, locale);
373                    article.setContent(content);
374                    article.setType(type);
375                    article.setStructureId(ddmStructureKey);
376                    article.setTemplateId(ddmTemplateKey);
377                    article.setLayoutUuid(layoutUuid);
378                    article.setDisplayDate(displayDate);
379                    article.setExpirationDate(expirationDate);
380                    article.setReviewDate(reviewDate);
381                    article.setIndexable(indexable);
382                    article.setSmallImage(smallImage);
383                    article.setSmallImageId(counterLocalService.increment());
384                    article.setSmallImageURL(smallImageURL);
385    
386                    if ((expirationDate == null) || expirationDate.after(now)) {
387                            article.setStatus(WorkflowConstants.STATUS_DRAFT);
388                    }
389                    else {
390                            article.setStatus(WorkflowConstants.STATUS_EXPIRED);
391                    }
392    
393                    article.setStatusByUserId(userId);
394                    article.setStatusDate(serviceContext.getModifiedDate(now));
395                    article.setExpandoBridgeAttributes(serviceContext);
396    
397                    journalArticlePersistence.update(article);
398    
399                    // Resources
400    
401                    if (serviceContext.isAddGroupPermissions() ||
402                            serviceContext.isAddGuestPermissions()) {
403    
404                            addArticleResources(
405                                    article, serviceContext.isAddGroupPermissions(),
406                                    serviceContext.isAddGuestPermissions());
407                    }
408                    else {
409                            addArticleResources(
410                                    article, serviceContext.getGroupPermissions(),
411                                    serviceContext.getGuestPermissions());
412                    }
413    
414                    // Small image
415    
416                    saveImages(
417                            smallImage, article.getSmallImageId(), smallImageFile,
418                            smallImageBytes);
419    
420                    // Asset
421    
422                    updateAsset(
423                            userId, article, serviceContext.getAssetCategoryIds(),
424                            serviceContext.getAssetTagNames(),
425                            serviceContext.getAssetLinkEntryIds());
426    
427                    // Dynamic data mapping
428    
429                    if (PortalUtil.getClassNameId(DDMStructure.class) == classNameId) {
430                            updateDDMStructureXSD(classPK, content, serviceContext);
431                    }
432    
433                    // Message boards
434    
435                    if (PropsValues.JOURNAL_ARTICLE_COMMENTS_ENABLED) {
436                            mbMessageLocalService.addDiscussionMessage(
437                                    userId, article.getUserName(), groupId,
438                                    JournalArticle.class.getName(), resourcePrimKey,
439                                    WorkflowConstants.ACTION_PUBLISH);
440                    }
441    
442                    // Email
443    
444                    PortletPreferences preferences =
445                            ServiceContextUtil.getPortletPreferences(serviceContext);
446    
447                    articleURL = buildArticleURL(articleURL, groupId, folderId, articleId);
448    
449                    serviceContext.setAttribute("articleURL", articleURL);
450    
451                    sendEmail(
452                            article, articleURL, preferences, "requested", serviceContext);
453    
454                    // Workflow
455    
456                    if (classNameId == JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
457                            WorkflowHandlerRegistryUtil.startWorkflowInstance(
458                                    user.getCompanyId(), groupId, userId,
459                                    JournalArticle.class.getName(), article.getId(), article,
460                                    serviceContext);
461                    }
462                    else {
463                            updateStatus(
464                                    userId, article, WorkflowConstants.STATUS_APPROVED, null,
465                                    new HashMap<String, Serializable>(), serviceContext);
466                    }
467    
468                    return journalArticlePersistence.findByPrimaryKey(article.getId());
469            }
470    
471            /**
472             * Adds a web content article.
473             *
474             * @param  userId the primary key of the web content article's creator/owner
475             * @param  groupId the primary key of the web content article's group
476             * @param  folderId the primary key of the web content article folder
477             * @param  titleMap the web content article's locales and localized titles
478             * @param  descriptionMap the web content article's locales and localized
479             *         descriptions
480             * @param  content the HTML content wrapped in XML. For more information,
481             *         see the content example in the class description for {@link
482             *         JournalArticleLocalServiceImpl}.
483             * @param  ddmStructureKey the primary key of the web content article's DDM
484             *         structure, if the article is related to a DDM structure, or
485             *         <code>null</code> otherwise
486             * @param  ddmTemplateKey the primary key of the web content article's DDM
487             *         template (optionally <code>null</code>). If the article is
488             *         related to a DDM structure, the template's structure must match
489             *         it.
490             * @param  serviceContext the service context to be applied. Can set the
491             *         UUID, creation date, modification date, expando bridge
492             *         attributes, guest permissions, group permissions, asset category
493             *         IDs, asset tag names, asset link entry IDs, the "urlTitle"
494             *         attribute, and workflow actions for the web content article. Can
495             *         also set whether to add the default guest and group permissions.
496             * @return the web content article
497             * @throws PortalException if a portal exception occurred
498             * @throws SystemException if a system exception occurred
499             */
500            @Override
501            public JournalArticle addArticle(
502                            long userId, long groupId, long folderId,
503                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
504                            String content, String ddmStructureKey, String ddmTemplateKey,
505                            ServiceContext serviceContext)
506                    throws PortalException, SystemException {
507    
508                    User user = userPersistence.findByPrimaryKey(userId);
509    
510                    Calendar calendar = CalendarFactoryUtil.getCalendar(user.getTimeZone());
511    
512                    int displayDateMonth = calendar.get(Calendar.MONTH);
513                    int displayDateDay = calendar.get(Calendar.DAY_OF_MONTH);
514                    int displayDateYear = calendar.get(Calendar.YEAR);
515                    int displayDateHour = calendar.get(Calendar.HOUR_OF_DAY);
516                    int displayDateMinute = calendar.get(Calendar.MINUTE);
517    
518                    return journalArticleLocalService.addArticle(
519                            userId, groupId, folderId,
520                            JournalArticleConstants.CLASSNAME_ID_DEFAULT, 0, StringPool.BLANK,
521                            true, 1, titleMap, descriptionMap, content, "general",
522                            ddmStructureKey, ddmTemplateKey, null, displayDateMonth,
523                            displayDateDay, displayDateYear, displayDateHour, displayDateMinute,
524                            0, 0, 0, 0, 0, true, 0, 0, 0, 0, 0, true, true, false, null, null,
525                            null, null, serviceContext);
526            }
527    
528            /**
529             * Adds the resources to the web content article.
530             *
531             * @param  article the web content article
532             * @param  addGroupPermissions whether to add group permissions
533             * @param  addGuestPermissions whether to add guest permissions
534             * @throws PortalException if no portal actions could be found associated
535             *         with the web content article or if a portal exception occurred
536             * @throws SystemException if a system exception occurred
537             */
538            @Override
539            public void addArticleResources(
540                            JournalArticle article, boolean addGroupPermissions,
541                            boolean addGuestPermissions)
542                    throws PortalException, SystemException {
543    
544                    resourceLocalService.addResources(
545                            article.getCompanyId(), article.getGroupId(), article.getUserId(),
546                            JournalArticle.class.getName(), article.getResourcePrimKey(), false,
547                            addGroupPermissions, addGuestPermissions);
548            }
549    
550            /**
551             * Adds the model resources with the permissions to the web content article.
552             *
553             * @param  article the web content article to add resources to
554             * @param  groupPermissions the group permissions to be added
555             * @param  guestPermissions the guest permissions to be added
556             * @throws PortalException if a portal exception occurred
557             * @throws SystemException if a system exception occurred
558             */
559            @Override
560            public void addArticleResources(
561                            JournalArticle article, String[] groupPermissions,
562                            String[] guestPermissions)
563                    throws PortalException, SystemException {
564    
565                    resourceLocalService.addModelResources(
566                            article.getCompanyId(), article.getGroupId(), article.getUserId(),
567                            JournalArticle.class.getName(), article.getResourcePrimKey(),
568                            groupPermissions, guestPermissions);
569            }
570    
571            /**
572             * Adds the resources to the most recently created web content article.
573             *
574             * @param  groupId the primary key of the web content article's group
575             * @param  articleId the primary key of the web content article
576             * @param  addGroupPermissions whether to add group permissions
577             * @param  addGuestPermissions whether to add guest permissions
578             * @throws PortalException if a portal exception occurred
579             * @throws SystemException if a system exception occurred
580             */
581            @Override
582            public void addArticleResources(
583                            long groupId, String articleId, boolean addGroupPermissions,
584                            boolean addGuestPermissions)
585                    throws PortalException, SystemException {
586    
587                    JournalArticle article = getLatestArticle(groupId, articleId);
588    
589                    addArticleResources(article, addGroupPermissions, addGuestPermissions);
590            }
591    
592            /**
593             * Adds the resources with the permissions to the most recently created web
594             * content article.
595             *
596             * @param  groupId the primary key of the web content article's group
597             * @param  articleId the primary key of the web content article
598             * @param  groupPermissions the group permissions to be added
599             * @param  guestPermissions the guest permissions to be added
600             * @throws PortalException if a portal exception occurred
601             * @throws SystemException if a system exception occurred
602             */
603            @Override
604            public void addArticleResources(
605                            long groupId, String articleId, String[] groupPermissions,
606                            String[] guestPermissions)
607                    throws PortalException, SystemException {
608    
609                    JournalArticle article = getLatestArticle(groupId, articleId);
610    
611                    addArticleResources(article, groupPermissions, guestPermissions);
612            }
613    
614            /**
615             * Returns the web content article with the group, article ID, and version.
616             * This method checks for the article's resource primary key and, if not
617             * found, creates a new one.
618             *
619             * @param  groupId the primary key of the web content article's group
620             * @param  articleId the primary key of the web content article
621             * @param  version the web content article's version
622             * @return the matching web content article
623             * @throws PortalException if a matching web content article could not be
624             *         found
625             * @throws SystemException if a system exception occurred
626             */
627            @Override
628            public JournalArticle checkArticleResourcePrimKey(
629                            long groupId, String articleId, double version)
630                    throws PortalException, SystemException {
631    
632                    JournalArticle article = journalArticlePersistence.findByG_A_V(
633                            groupId, articleId, version);
634    
635                    if (article.getResourcePrimKey() > 0) {
636                            return article;
637                    }
638    
639                    long resourcePrimKey =
640                            journalArticleResourceLocalService.getArticleResourcePrimKey(
641                                    groupId, articleId);
642    
643                    article.setResourcePrimKey(resourcePrimKey);
644    
645                    journalArticlePersistence.update(article);
646    
647                    return article;
648            }
649    
650            /**
651             * Checks all web content articles by handling their expirations and sending
652             * review notifications based on their current workflow.
653             *
654             * @throws PortalException if a portal exception occurred
655             * @throws SystemException if a system exception occurred
656             */
657            @Override
658            public void checkArticles() throws PortalException, SystemException {
659                    Date now = new Date();
660    
661                    checkArticlesByExpirationDate(now);
662    
663                    checkArticlesByReviewDate(now);
664    
665                    checkArticlesByDisplayDate(now);
666    
667                    _previousCheckDate = now;
668            }
669    
670            /**
671             * Checks the web content article matching the group, article ID, and
672             * version, replacing escaped newline and return characters with non-escaped
673             * newline and return characters.
674             *
675             * @param  groupId the primary key of the web content article's group
676             * @param  articleId the primary key of the web content article
677             * @param  version the web content article's version
678             * @throws PortalException if a matching web content article could not be
679             *         found
680             * @throws SystemException if a system exception occurred
681             */
682            @Override
683            public void checkNewLine(long groupId, String articleId, double version)
684                    throws PortalException, SystemException {
685    
686                    JournalArticle article = journalArticlePersistence.findByG_A_V(
687                            groupId, articleId, version);
688    
689                    String content = GetterUtil.getString(article.getContent());
690    
691                    if (content.contains("\\n")) {
692                            content = StringUtil.replace(
693                                    content, new String[] {"\\n", "\\r"},
694                                    new String[] {"\n", "\r"});
695    
696                            article.setContent(content);
697    
698                            journalArticlePersistence.update(article);
699                    }
700            }
701    
702            /**
703             * Checks the web content article matching the group, article ID, and
704             * version for an associated structure. If no structure is associated,
705             * return; otherwise check that the article and structure match.
706             *
707             * @param  groupId the primary key of the web content article's group
708             * @param  articleId the primary key of the web content article
709             * @param  version the web content article's version
710             * @throws PortalException if a matching web content article could not be
711             *         found, if the article's structure does not match it, or if a
712             *         portal exception occurred
713             * @throws SystemException if a system exception occurred
714             */
715            @Override
716            public void checkStructure(long groupId, String articleId, double version)
717                    throws PortalException, SystemException {
718    
719                    JournalArticle article = journalArticlePersistence.findByG_A_V(
720                            groupId, articleId, version);
721    
722                    if (Validator.isNull(article.getStructureId())) {
723                            return;
724                    }
725    
726                    checkStructure(article);
727            }
728    
729            /**
730             * Copies the web content article matching the group, article ID, and
731             * version. This method creates a new article, extracting all the values
732             * from the old one and updating its article ID.
733             *
734             * @param  userId the primary key of the web content article's creator/owner
735             * @param  groupId the primary key of the web content article's group
736             * @param  oldArticleId the primary key of the old web content article
737             * @param  newArticleId the primary key of the new web content article
738             * @param  autoArticleId whether to auto-generate the web content article ID
739             * @param  version the web content article's version
740             * @return the new web content article
741             * @throws PortalException if a matching web content article could not be
742             *         found or if a portal exception occurred
743             * @throws SystemException if a system exception occurred
744             */
745            @Indexable(type = IndexableType.REINDEX)
746            @Override
747            public JournalArticle copyArticle(
748                            long userId, long groupId, String oldArticleId, String newArticleId,
749                            boolean autoArticleId, double version)
750                    throws PortalException, SystemException {
751    
752                    // Article
753    
754                    User user = userPersistence.findByPrimaryKey(userId);
755                    oldArticleId = StringUtil.toUpperCase(oldArticleId.trim());
756                    newArticleId = StringUtil.toUpperCase(newArticleId.trim());
757                    Date now = new Date();
758    
759                    JournalArticle oldArticle = journalArticlePersistence.findByG_A_V(
760                            groupId, oldArticleId, version);
761    
762                    if (autoArticleId) {
763                            newArticleId = String.valueOf(counterLocalService.increment());
764                    }
765                    else {
766                            validate(newArticleId);
767    
768                            if (journalArticlePersistence.countByG_A(
769                                            groupId, newArticleId) > 0) {
770    
771                                    StringBundler sb = new StringBundler(5);
772    
773                                    sb.append("{groupId=");
774                                    sb.append(groupId);
775                                    sb.append(", articleId=");
776                                    sb.append(newArticleId);
777                                    sb.append("}");
778    
779                                    throw new DuplicateArticleIdException(sb.toString());
780                            }
781                    }
782    
783                    long id = counterLocalService.increment();
784    
785                    long resourcePrimKey =
786                            journalArticleResourceLocalService.getArticleResourcePrimKey(
787                                    groupId, newArticleId);
788    
789                    JournalArticle newArticle = journalArticlePersistence.create(id);
790    
791                    newArticle.setResourcePrimKey(resourcePrimKey);
792                    newArticle.setGroupId(groupId);
793                    newArticle.setCompanyId(user.getCompanyId());
794                    newArticle.setUserId(user.getUserId());
795                    newArticle.setUserName(user.getFullName());
796                    newArticle.setCreateDate(now);
797                    newArticle.setModifiedDate(now);
798                    newArticle.setFolderId(oldArticle.getFolderId());
799                    newArticle.setTreePath(oldArticle.getTreePath());
800                    newArticle.setArticleId(newArticleId);
801                    newArticle.setVersion(JournalArticleConstants.VERSION_DEFAULT);
802                    newArticle.setTitle(oldArticle.getTitle());
803                    newArticle.setUrlTitle(
804                            getUniqueUrlTitle(
805                                    id, groupId, newArticleId, oldArticle.getTitleCurrentValue()));
806                    newArticle.setDescription(oldArticle.getDescription());
807    
808                    try {
809                            copyArticleImages(oldArticle, newArticle);
810                    }
811                    catch (Exception e) {
812                            newArticle.setContent(oldArticle.getContent());
813                    }
814    
815                    newArticle.setType(oldArticle.getType());
816                    newArticle.setStructureId(oldArticle.getStructureId());
817                    newArticle.setTemplateId(oldArticle.getTemplateId());
818                    newArticle.setLayoutUuid(oldArticle.getLayoutUuid());
819                    newArticle.setDisplayDate(oldArticle.getDisplayDate());
820                    newArticle.setExpirationDate(oldArticle.getExpirationDate());
821                    newArticle.setReviewDate(oldArticle.getReviewDate());
822                    newArticle.setIndexable(oldArticle.isIndexable());
823                    newArticle.setSmallImage(oldArticle.isSmallImage());
824                    newArticle.setSmallImageId(counterLocalService.increment());
825                    newArticle.setSmallImageURL(oldArticle.getSmallImageURL());
826    
827                    if (oldArticle.isPending() ||
828                            workflowDefinitionLinkLocalService.hasWorkflowDefinitionLink(
829                                    user.getCompanyId(), groupId, JournalArticle.class.getName())) {
830    
831                            newArticle.setStatus(WorkflowConstants.STATUS_DRAFT);
832                    }
833                    else {
834                            newArticle.setStatus(oldArticle.getStatus());
835                    }
836    
837                    newArticle.setExpandoBridgeAttributes(oldArticle);
838    
839                    journalArticlePersistence.update(newArticle);
840    
841                    // Resources
842    
843                    addArticleResources(newArticle, true, true);
844    
845                    // Small image
846    
847                    if (oldArticle.isSmallImage()) {
848                            Image image = imageLocalService.fetchImage(
849                                    oldArticle.getSmallImageId());
850    
851                            if (image != null) {
852                                    byte[] smallImageBytes = image.getTextObj();
853    
854                                    imageLocalService.updateImage(
855                                            newArticle.getSmallImageId(), smallImageBytes);
856                            }
857                    }
858    
859                    // Asset
860    
861                    long[] assetCategoryIds = assetCategoryLocalService.getCategoryIds(
862                            JournalArticle.class.getName(), oldArticle.getResourcePrimKey());
863                    String[] assetTagNames = assetTagLocalService.getTagNames(
864                            JournalArticle.class.getName(), oldArticle.getResourcePrimKey());
865    
866                    AssetEntry oldAssetEntry = assetEntryLocalService.getEntry(
867                            JournalArticle.class.getName(), oldArticle.getResourcePrimKey());
868    
869                    List<AssetLink> assetLinks = assetLinkLocalService.getDirectLinks(
870                            oldAssetEntry.getEntryId());
871    
872                    long[] assetLinkEntryIds = StringUtil.split(
873                            ListUtil.toString(assetLinks, AssetLink.ENTRY_ID2_ACCESSOR), 0L);
874    
875                    updateAsset(
876                            userId, newArticle, assetCategoryIds, assetTagNames,
877                            assetLinkEntryIds);
878    
879                    return newArticle;
880            }
881    
882            /**
883             * Deletes the web content article and its resources.
884             *
885             * @param  article the web content article
886             * @throws PortalException if a portal exception occurred
887             * @throws SystemException if a system exception occurred
888             */
889            @Override
890            @SystemEvent(
891                    action = SystemEventConstants.ACTION_SKIP, send = false)
892            public JournalArticle deleteArticle(JournalArticle article)
893                    throws PortalException, SystemException {
894    
895                    return journalArticleLocalService.deleteArticle(
896                            article, StringPool.BLANK, null);
897            }
898    
899            /**
900             * Deletes the web content article and its resources, optionally sending
901             * email notifying denial of the article if it had not yet been approved.
902             *
903             * @param  article the web content article
904             * @param  articleURL the web content article's accessible URL to include in
905             *         email notifications (optionally <code>null</code>)
906             * @param  serviceContext the service context to be applied (optionally
907             *         <code>null</code>). Can set the portlet preferences that include
908             *         email information to notify recipients of the unapproved web
909             *         content's denial.
910             * @throws PortalException if a portal exception occurred
911             * @throws SystemException if a system exception occurred
912             */
913            @Indexable(type = IndexableType.DELETE)
914            @Override
915            @SystemEvent(
916                    action = SystemEventConstants.ACTION_SKIP, send = false)
917            public JournalArticle deleteArticle(
918                            JournalArticle article, String articleURL,
919                            ServiceContext serviceContext)
920                    throws PortalException, SystemException {
921    
922                    JournalArticleResource articleResource =
923                            journalArticleResourceLocalService.fetchArticleResource(
924                                    article.getGroupId(), article.getArticleId());
925    
926                    if (article.isApproved() &&
927                            isLatestVersion(
928                                    article.getGroupId(), article.getArticleId(),
929                                    article.getVersion(), WorkflowConstants.STATUS_APPROVED)) {
930    
931                            updatePreviousApprovedArticle(article);
932                    }
933    
934                    // Email
935    
936                    if ((serviceContext != null) && Validator.isNotNull(articleURL)) {
937                            PortletPreferences preferences =
938                                    ServiceContextUtil.getPortletPreferences(serviceContext);
939    
940                            if ((preferences != null) && !article.isApproved() &&
941                                    isLatestVersion(
942                                            article.getGroupId(), article.getArticleId(),
943                                            article.getVersion())) {
944    
945                                    articleURL = buildArticleURL(
946                                            articleURL, article.getGroupId(), article.getFolderId(),
947                                            article.getArticleId());
948    
949                                    sendEmail(
950                                            article, articleURL, preferences, "denied", serviceContext);
951                            }
952                    }
953    
954                    // Images
955    
956                    journalArticleImageLocalService.deleteImages(
957                            article.getGroupId(), article.getArticleId(), article.getVersion());
958    
959                    // Expando
960    
961                    expandoRowLocalService.deleteRows(article.getId());
962    
963                    // Trash
964    
965                    if (article.isInTrash()) {
966                            TrashEntry trashEntry = article.getTrashEntry();
967    
968                            if (trashEntry != null) {
969                                    trashVersionLocalService.deleteTrashVersion(
970                                            trashEntry.getEntryId(), JournalArticle.class.getName(),
971                                            article.getId());
972                            }
973                    }
974    
975                    // Workflow
976    
977                    if (!article.isDraft()) {
978                            workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
979                                    article.getCompanyId(), article.getGroupId(),
980                                    JournalArticle.class.getName(), article.getId());
981                    }
982    
983                    int articlesCount = journalArticlePersistence.countByG_A(
984                            article.getGroupId(), article.getArticleId());
985    
986                    if (articlesCount == 1) {
987    
988                            // Subscriptions
989    
990                            subscriptionLocalService.deleteSubscriptions(
991                                    article.getCompanyId(), JournalArticle.class.getName(),
992                                    article.getResourcePrimKey());
993    
994                            // Ratings
995    
996                            ratingsStatsLocalService.deleteStats(
997                                    JournalArticle.class.getName(), article.getResourcePrimKey());
998    
999                            // Message boards
1000    
1001                            mbMessageLocalService.deleteDiscussionMessages(
1002                                    JournalArticle.class.getName(), article.getResourcePrimKey());
1003    
1004                            // Asset
1005    
1006                            assetEntryLocalService.deleteEntry(
1007                                    JournalArticle.class.getName(), article.getResourcePrimKey());
1008    
1009                            // Content searches
1010    
1011                            journalContentSearchLocalService.deleteArticleContentSearches(
1012                                    article.getGroupId(), article.getArticleId());
1013    
1014                            // Small image
1015    
1016                            imageLocalService.deleteImage(article.getSmallImageId());
1017    
1018                            // Trash
1019    
1020                            trashEntryLocalService.deleteEntry(
1021                                    JournalArticle.class.getName(), article.getResourcePrimKey());
1022    
1023                            // Resources
1024    
1025                            resourceLocalService.deleteResource(
1026                                    article.getCompanyId(), JournalArticle.class.getName(),
1027                                    ResourceConstants.SCOPE_INDIVIDUAL,
1028                                    article.getResourcePrimKey());
1029    
1030                            // Resource
1031    
1032                            if (articleResource != null) {
1033                                    journalArticleResourceLocalService.deleteJournalArticleResource(
1034                                            articleResource);
1035                            }
1036                    }
1037    
1038                    // Article
1039    
1040                    journalArticlePersistence.remove(article);
1041    
1042                    // System event
1043    
1044                    if (articleResource != null) {
1045                            JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
1046    
1047                            extraDataJSONObject.put("version", article.getVersion());
1048    
1049                            systemEventLocalService.addSystemEvent(
1050                                    0, article.getGroupId(), article.getModelClassName(),
1051                                    article.getPrimaryKey(), articleResource.getUuid(), null,
1052                                    SystemEventConstants.TYPE_DELETE,
1053                                    extraDataJSONObject.toString());
1054                    }
1055    
1056                    return article;
1057            }
1058    
1059            /**
1060             * Deletes the web content article and its resources matching the group,
1061             * article ID, and version, optionally sending email notifying denial of the
1062             * web content article if it had not yet been approved.
1063             *
1064             * @param  groupId the primary key of the web content article's group
1065             * @param  articleId the primary key of the web content article
1066             * @param  version the web content article's version
1067             * @param  articleURL the web content article's accessible URL
1068             * @param  serviceContext the service context to be applied. Can set the
1069             *         portlet preferences that include email information to notify
1070             *         recipients of the unapproved web content article's denial.
1071             * @throws PortalException if a matching web content article could not be
1072             *         found or if a portal exception occurred
1073             * @throws SystemException if a system exception occurred
1074             */
1075            @Override
1076            public JournalArticle deleteArticle(
1077                            long groupId, String articleId, double version, String articleURL,
1078                            ServiceContext serviceContext)
1079                    throws PortalException, SystemException {
1080    
1081                    JournalArticle article = journalArticlePersistence.findByG_A_V(
1082                            groupId, articleId, version);
1083    
1084                    return journalArticleLocalService.deleteArticle(
1085                            article, articleURL, serviceContext);
1086            }
1087    
1088            /**
1089             * Deletes all web content articles and their resources matching the group
1090             * and article ID, optionally sending email notifying denial of article if
1091             * it had not yet been approved.
1092             *
1093             * @param  groupId the primary key of the web content article's group
1094             * @param  articleId the primary key of the web content article
1095             * @param  serviceContext the service context to be applied. Can set the
1096             *         portlet preferences that include email information to notify
1097             *         recipients of the unapproved web content article's denial.
1098             * @throws PortalException if a portal exception occurred
1099             * @throws SystemException if a system exception occurred
1100             */
1101            @Override
1102            public void deleteArticle(
1103                            long groupId, String articleId, ServiceContext serviceContext)
1104                    throws PortalException, SystemException {
1105    
1106                    SystemEventHierarchyEntryThreadLocal.push(JournalArticle.class);
1107    
1108                    JournalArticleResource articleResource =
1109                            journalArticleResourceLocalService.fetchArticleResource(
1110                                    groupId, articleId);
1111    
1112                    try {
1113                            List<JournalArticle> articles = journalArticlePersistence.findByG_A(
1114                                    groupId, articleId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
1115                                    new ArticleVersionComparator(true));
1116    
1117                            for (JournalArticle article : articles) {
1118                                    journalArticleLocalService.deleteArticle(
1119                                            article, null, serviceContext);
1120                            }
1121                    }
1122                    finally {
1123                            SystemEventHierarchyEntryThreadLocal.pop(JournalArticle.class);
1124                    }
1125    
1126                    if (articleResource != null) {
1127                            systemEventLocalService.addSystemEvent(
1128                                    0, groupId, JournalArticle.class.getName(),
1129                                    articleResource.getResourcePrimKey(), articleResource.getUuid(),
1130                                    null, SystemEventConstants.TYPE_DELETE, StringPool.BLANK);
1131                    }
1132            }
1133    
1134            /**
1135             * Deletes all the group's web content articles and resources.
1136             *
1137             * @param  groupId the primary key of the web content article's group
1138             * @throws PortalException if a portal exception occurred
1139             * @throws SystemException if a system exception occurred
1140             */
1141            @Override
1142            public void deleteArticles(long groupId)
1143                    throws PortalException, SystemException {
1144    
1145                    SystemEventHierarchyEntryThreadLocal.push(JournalArticle.class);
1146    
1147                    List<JournalArticleResource> articleResources =
1148                            new ArrayList<JournalArticleResource>();
1149    
1150                    try {
1151                            JournalArticleResource articleResource = null;
1152    
1153                            for (JournalArticle article :
1154                                            journalArticlePersistence.findByGroupId(groupId)) {
1155    
1156                                    if ((articleResource == null) ||
1157                                            (articleResource.getPrimaryKey() !=
1158                                                    article.getResourcePrimKey())) {
1159    
1160                                            articleResource =
1161                                                    journalArticleResourceLocalService.getArticleResource(
1162                                                            article.getResourcePrimKey());
1163    
1164                                            articleResources.add(articleResource);
1165                                    }
1166    
1167                                    journalArticleLocalService.deleteArticle(article, null, null);
1168                            }
1169                    }
1170                    finally {
1171                            SystemEventHierarchyEntryThreadLocal.pop(JournalArticle.class);
1172                    }
1173    
1174                    for (JournalArticleResource articleResource : articleResources) {
1175                            systemEventLocalService.addSystemEvent(
1176                                    0, groupId, JournalArticle.class.getName(),
1177                                    articleResource.getResourcePrimKey(), articleResource.getUuid(),
1178                                    null, SystemEventConstants.TYPE_DELETE, StringPool.BLANK);
1179                    }
1180            }
1181    
1182            /**
1183             * Deletes all the group's web content articles and resources in the folder,
1184             * including recycled articles.
1185             *
1186             * @param  groupId the primary key of the web content article's group
1187             * @param  folderId the primary key of the web content article folder
1188             * @throws PortalException if a portal exception occurred
1189             * @throws SystemException if a system exception occurred
1190             */
1191            @Override
1192            public void deleteArticles(long groupId, long folderId)
1193                    throws PortalException, SystemException {
1194    
1195                    deleteArticles(groupId, folderId, true);
1196            }
1197    
1198            /**
1199             * Deletes all the group's web content articles and resources in the folder,
1200             * optionally including recycled articles.
1201             *
1202             * @param  groupId the primary key of the web content article's group
1203             * @param  folderId the primary key of the web content article folder
1204             * @param  includeTrashedEntries whether to include recycled web content
1205             *         articles
1206             * @throws PortalException if a portal exception occurred
1207             * @throws SystemException if a system exception occurred
1208             */
1209            @Override
1210            public void deleteArticles(
1211                            long groupId, long folderId, boolean includeTrashedEntries)
1212                    throws PortalException, SystemException {
1213    
1214                    SystemEventHierarchyEntryThreadLocal.push(JournalArticle.class);
1215    
1216                    List<JournalArticleResource> articleResources =
1217                            new ArrayList<JournalArticleResource>();
1218    
1219                    try {
1220                            JournalArticleResource articleResource = null;
1221    
1222                            for (JournalArticle article :
1223                                            journalArticlePersistence.findByG_F(groupId, folderId)) {
1224    
1225                                    if ((articleResource == null) ||
1226                                            (articleResource.getPrimaryKey() !=
1227                                                    article.getResourcePrimKey())) {
1228    
1229                                            articleResource =
1230                                                    journalArticleResourceLocalService.getArticleResource(
1231                                                            article.getResourcePrimKey());
1232    
1233                                            articleResources.add(articleResource);
1234                                    }
1235    
1236                                    if (includeTrashedEntries || !article.isInTrashExplicitly()) {
1237                                            journalArticleLocalService.deleteArticle(
1238                                                    article, null, null);
1239                                    }
1240                                    else {
1241                                            articleResources.remove(articleResource);
1242                                    }
1243                            }
1244                    }
1245                    finally {
1246                            SystemEventHierarchyEntryThreadLocal.pop(JournalArticle.class);
1247                    }
1248    
1249                    for (JournalArticleResource articleResource : articleResources) {
1250                            systemEventLocalService.addSystemEvent(
1251                                    0, groupId, JournalArticle.class.getName(),
1252                                    articleResource.getResourcePrimKey(), articleResource.getUuid(),
1253                                    null, SystemEventConstants.TYPE_DELETE, StringPool.BLANK);
1254                    }
1255            }
1256    
1257            /**
1258             * Deletes the layout's association with the web content articles for the
1259             * group.
1260             *
1261             * @param  groupId the primary key of the web content article's group
1262             * @param  layoutUuid the unique string identifying the web content
1263             *         article's display page
1264             * @throws SystemException if a system exception occurred
1265             */
1266            @Override
1267            public void deleteLayoutArticleReferences(long groupId, String layoutUuid)
1268                    throws SystemException {
1269    
1270                    List<JournalArticle> articles = journalArticlePersistence.findByG_L(
1271                            groupId, layoutUuid);
1272    
1273                    for (JournalArticle article : articles) {
1274                            article.setLayoutUuid(StringPool.BLANK);
1275    
1276                            journalArticlePersistence.update(article);
1277                    }
1278            }
1279    
1280            /**
1281             * Expires the web content article matching the group, article ID, and
1282             * version.
1283             *
1284             * @param  userId the primary key of the user updating the web content
1285             *         article
1286             * @param  groupId the primary key of the web content article's group
1287             * @param  articleId the primary key of the web content article
1288             * @param  version the web content article's version
1289             * @param  articleURL the web content article's accessible URL
1290             * @param  serviceContext the service context to be applied. Can set the
1291             *         modification date, status date, portlet preferences, and can set
1292             *         whether to add the default command update for the web content
1293             *         article. With respect to social activities, by setting the
1294             *         service context's command to {@link
1295             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
1296             *         is considered a web content update activity; otherwise it is
1297             *         considered a web content add activity.
1298             * @return the web content article
1299             * @throws PortalException if a matching web content article could not be
1300             *         found or if a portal exception occurred
1301             * @throws SystemException if a system exception occurred
1302             */
1303            @Indexable(type = IndexableType.REINDEX)
1304            @Override
1305            public JournalArticle expireArticle(
1306                            long userId, long groupId, String articleId, double version,
1307                            String articleURL, ServiceContext serviceContext)
1308                    throws PortalException, SystemException {
1309    
1310                    return updateStatus(
1311                            userId, groupId, articleId, version,
1312                            WorkflowConstants.STATUS_EXPIRED, articleURL,
1313                            new HashMap<String, Serializable>(), serviceContext);
1314            }
1315    
1316            /**
1317             * Expires the web content article matching the group and article ID,
1318             * expiring all of its versions if the
1319             * <code>journal.article.expire.all.versions</code> portal property is
1320             * <code>true</code>, otherwise expiring only its latest approved version.
1321             *
1322             * @param  userId the primary key of the user updating the web content
1323             *         article
1324             * @param  groupId the primary key of the web content article's group
1325             * @param  articleId the primary key of the web content article
1326             * @param  articleURL the web content article's accessible URL
1327             * @param  serviceContext the service context to be applied. Can set the
1328             *         modification date, status date, portlet preferences, and can set
1329             *         whether to add the default command update for the web content
1330             *         article. With respect to social activities, by setting the
1331             *         service context's command to {@link
1332             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
1333             *         is considered a web content update activity; otherwise it is
1334             *         considered a web content add activity.
1335             * @throws PortalException if a matching web content article could not be
1336             *         found or if a portal exception occurred
1337             * @throws SystemException if a system exception occurred
1338             */
1339            @Override
1340            public void expireArticle(
1341                            long userId, long groupId, String articleId, String articleURL,
1342                            ServiceContext serviceContext)
1343                    throws PortalException, SystemException {
1344    
1345                    if (PropsValues.JOURNAL_ARTICLE_EXPIRE_ALL_VERSIONS) {
1346                            List<JournalArticle> articles = journalArticlePersistence.findByG_A(
1347                                    groupId, articleId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
1348                                    new ArticleVersionComparator(true));
1349    
1350                            for (JournalArticle article : articles) {
1351                                    journalArticleLocalService.expireArticle(
1352                                            userId, groupId, article.getArticleId(),
1353                                            article.getVersion(), articleURL, serviceContext);
1354                            }
1355                    }
1356                    else {
1357                            JournalArticle article = getLatestArticle(
1358                                    groupId, articleId, WorkflowConstants.STATUS_APPROVED);
1359    
1360                            journalArticleLocalService.expireArticle(
1361                                    userId, groupId, article.getArticleId(), article.getVersion(),
1362                                    articleURL, serviceContext);
1363                    }
1364            }
1365    
1366            @Override
1367            public JournalArticle fetchArticle(long groupId, String articleId)
1368                    throws SystemException {
1369    
1370                    // Get the latest article that is approved, if none are approved, get
1371                    // the latest unapproved article
1372    
1373                    JournalArticle article = fetchLatestArticle(
1374                            groupId, articleId, WorkflowConstants.STATUS_APPROVED);
1375    
1376                    if (article != null) {
1377                            return article;
1378                    }
1379    
1380                    return fetchLatestArticle(
1381                            groupId, articleId, WorkflowConstants.STATUS_ANY);
1382            }
1383    
1384            /**
1385             * Returns the web content article matching the group, article ID, and
1386             * version.
1387             *
1388             * @param  groupId the primary key of the web content article's group
1389             * @param  articleId the primary key of the web content article
1390             * @param  version the web content article's version
1391             * @return the web content article matching the group, article ID, and
1392             *         version, or <code>null</code> if no web content article could be
1393             *         found
1394             */
1395            @Override
1396            public JournalArticle fetchArticle(
1397                            long groupId, String articleId, double version)
1398                    throws SystemException {
1399    
1400                    return journalArticlePersistence.fetchByG_A_V(
1401                            groupId, articleId, version);
1402            }
1403    
1404            @Override
1405            public JournalArticle fetchArticleByUrlTitle(long groupId, String urlTitle)
1406                    throws SystemException {
1407    
1408                    JournalArticle article = fetchLatestArticleByUrlTitle(
1409                            groupId, urlTitle, WorkflowConstants.STATUS_APPROVED);
1410    
1411                    if (article != null) {
1412                            return article;
1413                    }
1414    
1415                    return fetchLatestArticleByUrlTitle(
1416                            groupId, urlTitle, WorkflowConstants.STATUS_PENDING);
1417            }
1418    
1419            @Override
1420            public JournalArticle fetchDisplayArticle(long groupId, String articleId)
1421                    throws SystemException {
1422    
1423                    List<JournalArticle> articles = journalArticlePersistence.findByG_A_ST(
1424                            groupId, articleId, WorkflowConstants.STATUS_APPROVED);
1425    
1426                    if (articles.isEmpty()) {
1427                            return null;
1428                    }
1429    
1430                    Date now = new Date();
1431    
1432                    for (JournalArticle article : articles) {
1433                            Date displayDate = article.getDisplayDate();
1434                            Date expirationDate = article.getExpirationDate();
1435    
1436                            if (((displayDate == null) || displayDate.before(now)) &&
1437                                    ((expirationDate == null) || expirationDate.after(now))) {
1438    
1439                                    return article;
1440                            }
1441                    }
1442    
1443                    return articles.get(0);
1444            }
1445    
1446            @Override
1447            public JournalArticle fetchLatestArticle(long resourcePrimKey)
1448                    throws SystemException {
1449    
1450                    return fetchLatestArticle(
1451                            resourcePrimKey, WorkflowConstants.STATUS_ANY);
1452            }
1453    
1454            @Override
1455            public JournalArticle fetchLatestArticle(long resourcePrimKey, int status)
1456                    throws SystemException {
1457    
1458                    return fetchLatestArticle(resourcePrimKey, status, true);
1459            }
1460    
1461            public JournalArticle fetchLatestArticle(
1462                            long resourcePrimKey, int[] statuses)
1463                    throws SystemException {
1464    
1465                    OrderByComparator orderByComparator = new ArticleVersionComparator();
1466    
1467                    List<JournalArticle> articles = journalArticlePersistence.findByR_ST(
1468                            resourcePrimKey, statuses, 0, 1, orderByComparator);
1469    
1470                    if (!articles.isEmpty()) {
1471                            return articles.get(0);
1472                    }
1473    
1474                    return null;
1475            }
1476    
1477            @Override
1478            public JournalArticle fetchLatestArticle(
1479                            long resourcePrimKey, int status, boolean preferApproved)
1480                    throws SystemException {
1481    
1482                    JournalArticle article = null;
1483    
1484                    OrderByComparator orderByComparator = new ArticleVersionComparator();
1485    
1486                    if (status == WorkflowConstants.STATUS_ANY) {
1487                            if (preferApproved) {
1488                                    article = journalArticlePersistence.fetchByR_ST_First(
1489                                            resourcePrimKey, WorkflowConstants.STATUS_APPROVED,
1490                                            orderByComparator);
1491                            }
1492    
1493                            if (article == null) {
1494                                    article =
1495                                            journalArticlePersistence.fetchByResourcePrimKey_First(
1496                                                    resourcePrimKey, orderByComparator);
1497                            }
1498                    }
1499                    else {
1500                            article = journalArticlePersistence.fetchByR_ST_First(
1501                                    resourcePrimKey, status, orderByComparator);
1502                    }
1503    
1504                    return article;
1505            }
1506    
1507            @Override
1508            public JournalArticle fetchLatestArticle(
1509                            long groupId, String articleId, int status)
1510                    throws SystemException {
1511    
1512                    OrderByComparator orderByComparator = new ArticleVersionComparator();
1513    
1514                    if (status == WorkflowConstants.STATUS_ANY) {
1515                            return journalArticlePersistence.fetchByG_A_NotST_First(
1516                                    groupId, articleId, WorkflowConstants.STATUS_IN_TRASH,
1517                                    orderByComparator);
1518                    }
1519    
1520                    return journalArticlePersistence.fetchByG_A_ST_First(
1521                            groupId, articleId, status, orderByComparator);
1522            }
1523    
1524            @Override
1525            public JournalArticle fetchLatestArticleByUrlTitle(
1526                            long groupId, String urlTitle, int status)
1527                    throws SystemException {
1528    
1529                    List<JournalArticle> articles = null;
1530    
1531                    OrderByComparator orderByComparator = new ArticleVersionComparator();
1532    
1533                    if (status == WorkflowConstants.STATUS_ANY) {
1534                            articles = journalArticlePersistence.findByG_UT(
1535                                    groupId, urlTitle, 0, 1, orderByComparator);
1536                    }
1537                    else {
1538                            articles = journalArticlePersistence.findByG_UT_ST(
1539                                    groupId, urlTitle, status, 0, 1, orderByComparator);
1540                    }
1541    
1542                    if (articles.isEmpty()) {
1543                            return null;
1544                    }
1545    
1546                    return articles.get(0);
1547            }
1548    
1549            /**
1550             * Returns the latest indexable web content article matching the resource
1551             * primary key.
1552             *
1553             * @param  resourcePrimKey the primary key of the resource instance
1554             * @return the latest indexable web content article matching the resource
1555             *         primary key, or <code>null</code> if no matching web content
1556             *         article could be found
1557             */
1558            @Override
1559            public JournalArticle fetchLatestIndexableArticle(long resourcePrimKey)
1560                    throws SystemException {
1561    
1562                    OrderByComparator orderByComparator = new ArticleVersionComparator();
1563    
1564                    int[] statuses = new int[] {
1565                            WorkflowConstants.STATUS_APPROVED, WorkflowConstants.STATUS_IN_TRASH
1566                    };
1567    
1568                    List<JournalArticle> articles =
1569                            journalArticlePersistence.findByR_I_S(
1570                                    resourcePrimKey, true, statuses, 0, 1, orderByComparator);
1571    
1572                    if (articles.isEmpty()) {
1573                            return null;
1574                    }
1575    
1576                    return articles.get(0);
1577            }
1578    
1579            /**
1580             * Returns the web content article with the ID.
1581             *
1582             * @param  id the primary key of the web content article
1583             * @return the web content article with the ID
1584             * @throws PortalException if a matching web content article could not be
1585             *         found
1586             * @throws SystemException if a system exception occurred
1587             */
1588            @Override
1589            public JournalArticle getArticle(long id)
1590                    throws PortalException, SystemException {
1591    
1592                    return journalArticlePersistence.findByPrimaryKey(id);
1593            }
1594    
1595            /**
1596             * Returns the latest approved web content article, or the latest unapproved
1597             * article if none are approved. Both approved and unapproved articles must
1598             * match the group and article ID.
1599             *
1600             * @param  groupId the primary key of the web content article's group
1601             * @param  articleId the primary key of the web content article
1602             * @return the matching web content article
1603             * @throws PortalException if a matching web content article could not be
1604             *         found
1605             * @throws SystemException if a system exception occurred
1606             */
1607            @Override
1608            public JournalArticle getArticle(long groupId, String articleId)
1609                    throws PortalException, SystemException {
1610    
1611                    // Get the latest article that is approved, if none are approved, get
1612                    // the latest unapproved article
1613    
1614                    JournalArticle article = fetchLatestArticle(
1615                            groupId, articleId, WorkflowConstants.STATUS_APPROVED);
1616    
1617                    if (article != null) {
1618                            return article;
1619                    }
1620    
1621                    return getLatestArticle(
1622                            groupId, articleId, WorkflowConstants.STATUS_ANY);
1623            }
1624    
1625            /**
1626             * Returns the web content article matching the group, article ID, and
1627             * version.
1628             *
1629             * @param  groupId the primary key of the web content article's group
1630             * @param  articleId the primary key of the web content article
1631             * @param  version the web content article's version
1632             * @return the matching web content article
1633             * @throws PortalException if a matching web content article could not be
1634             *         found
1635             * @throws SystemException if a system exception occurred
1636             */
1637            @Override
1638            public JournalArticle getArticle(
1639                            long groupId, String articleId, double version)
1640                    throws PortalException, SystemException {
1641    
1642                    return journalArticlePersistence.findByG_A_V(
1643                            groupId, articleId, version);
1644            }
1645    
1646            /**
1647             * Returns the web content article matching the group, class name, and class
1648             * PK.
1649             *
1650             * @param  groupId the primary key of the web content article's group
1651             * @param  className the DDMStructure class name if the web content article
1652             *         is related to a DDM structure, the primary key of the class name
1653             *         associated with the article, or {@link
1654             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
1655             * @param  classPK the primary key of the DDM structure, if the the
1656             *         DDMStructure class name is given as the <code>className</code>
1657             *         parameter, the primary key of the class associated with the web
1658             *         content article, or <code>0</code> otherwise
1659             * @return the matching web content article
1660             * @throws PortalException if a matching web content article could not be
1661             *         found
1662             * @throws SystemException if a system exception occurred
1663             */
1664            @Override
1665            public JournalArticle getArticle(
1666                            long groupId, String className, long classPK)
1667                    throws PortalException, SystemException {
1668    
1669                    long classNameId = PortalUtil.getClassNameId(className);
1670    
1671                    List<JournalArticle> articles = journalArticlePersistence.findByG_C_C(
1672                            groupId, classNameId, classPK);
1673    
1674                    if (articles.isEmpty()) {
1675                            throw new NoSuchArticleException(
1676                                    "No approved JournalArticle exists with the key {groupId=" +
1677                                            groupId + ", className=" + className + ", classPK=" +
1678                                                    classPK + "}");
1679                    }
1680    
1681                    return articles.get(0);
1682            }
1683    
1684            /**
1685             * Returns the latest web content article that is approved, or the latest
1686             * unapproved article if none are approved. Both approved and unapproved
1687             * articles must match the group and URL title.
1688             *
1689             * @param  groupId the primary key of the web content article's group
1690             * @param  urlTitle the web content article's accessible URL title
1691             * @return the matching web content article
1692             * @throws PortalException if a portal exception occurred
1693             * @throws SystemException if a system exception occurred
1694             */
1695            @Override
1696            public JournalArticle getArticleByUrlTitle(long groupId, String urlTitle)
1697                    throws PortalException, SystemException {
1698    
1699                    // Get the latest article that is approved, if none are approved, get
1700                    // the latest unapproved article
1701    
1702                    JournalArticle article = fetchLatestArticleByUrlTitle(
1703                            groupId, urlTitle, WorkflowConstants.STATUS_APPROVED);
1704    
1705                    if (article != null) {
1706                            return article;
1707                    }
1708    
1709                    return getLatestArticleByUrlTitle(
1710                            groupId, urlTitle, WorkflowConstants.STATUS_PENDING);
1711            }
1712    
1713            /**
1714             * Returns the web content associated with the web content article and DDM
1715             * template.
1716             *
1717             * @param  article the web content article
1718             * @param  ddmTemplateKey the primary key of the web content article's DDM
1719             *         template (optionally <code>null</code>). If the article is
1720             *         related to a DDM structure, the template's structure must match
1721             *         it.
1722             * @param  viewMode the mode in which the web content is being viewed
1723             * @param  languageId the primary key of the language translation to get
1724             * @param  themeDisplay the theme display
1725             * @return the web content associated with the DDM template
1726             * @throws PortalException if a matching DDM template could not be found or
1727             *         if a portal exception occurred
1728             * @throws SystemException if a system exception occurred
1729             */
1730            @Override
1731            public String getArticleContent(
1732                            JournalArticle article, String ddmTemplateKey, String viewMode,
1733                            String languageId, ThemeDisplay themeDisplay)
1734                    throws PortalException, SystemException {
1735    
1736                    JournalArticleDisplay articleDisplay = getArticleDisplay(
1737                            article, ddmTemplateKey, viewMode, languageId, 1, null,
1738                            themeDisplay);
1739    
1740                    if (articleDisplay == null) {
1741                            return StringPool.BLANK;
1742                    }
1743                    else {
1744                            return articleDisplay.getContent();
1745                    }
1746            }
1747    
1748            /**
1749             * Returns the web content matching the group, article ID, and version, and
1750             * associated with the DDM template.
1751             *
1752             * @param  groupId the primary key of the web content article's group
1753             * @param  articleId the primary key of the web content article
1754             * @param  version the web content article's version
1755             * @param  viewMode the mode in which the web content is being viewed
1756             * @param  ddmTemplateKey the primary key of the web content article's DDM
1757             *         template (optionally <code>null</code>). If the article is
1758             *         related to a DDM structure, the template's structure must match
1759             *         it.
1760             * @param  languageId the primary key of the language translation to get
1761             * @param  themeDisplay the theme display
1762             * @return the matching web content
1763             * @throws PortalException if a matching web content article or DDM template
1764             *         could not be found, or if a portal exception occurred
1765             * @throws SystemException if a system exception occurred
1766             */
1767            @Override
1768            public String getArticleContent(
1769                            long groupId, String articleId, double version, String viewMode,
1770                            String ddmTemplateKey, String languageId, ThemeDisplay themeDisplay)
1771                    throws PortalException, SystemException {
1772    
1773                    JournalArticleDisplay articleDisplay = getArticleDisplay(
1774                            groupId, articleId, version, ddmTemplateKey, viewMode, languageId,
1775                            themeDisplay);
1776    
1777                    if (articleDisplay == null) {
1778                            return StringPool.BLANK;
1779                    }
1780                    else {
1781                            return articleDisplay.getContent();
1782                    }
1783            }
1784    
1785            /**
1786             * Returns the web content matching the group, article ID, and version.
1787             *
1788             * @param  groupId the primary key of the web content article's group
1789             * @param  articleId the primary key of the web content article
1790             * @param  version the web content article's version
1791             * @param  viewMode the mode in which the web content is being viewed
1792             * @param  languageId the primary key of the language translation to get
1793             * @param  themeDisplay the theme display
1794             * @return the matching web content
1795             * @throws PortalException if a matching web content article or DDM template
1796             *         could not be found, or if a portal exception occurred
1797             * @throws SystemException if a system exception occurred
1798             */
1799            @Override
1800            public String getArticleContent(
1801                            long groupId, String articleId, double version, String viewMode,
1802                            String languageId, ThemeDisplay themeDisplay)
1803                    throws PortalException, SystemException {
1804    
1805                    return getArticleContent(
1806                            groupId, articleId, version, viewMode, null, languageId,
1807                            themeDisplay);
1808            }
1809    
1810            /**
1811             * Returns the latest web content matching the group and article ID, and
1812             * associated with DDM template key.
1813             *
1814             * @param  groupId the primary key of the web content article's group
1815             * @param  articleId the primary key of the web content article
1816             * @param  viewMode the mode in which the web content is being viewed
1817             * @param  ddmTemplateKey the primary key of the web content article's DDM
1818             *         template (optionally <code>null</code>). If the article is
1819             *         related to a DDM structure, the template's structure must match
1820             *         it.
1821             * @param  languageId the primary key of the language translation to get
1822             * @param  themeDisplay the theme display
1823             * @return the matching web content
1824             * @throws PortalException if a matching web content article or DDM template
1825             *         could not be found, or if a portal exception occurred
1826             * @throws SystemException if a system exception occurred
1827             */
1828            @Override
1829            public String getArticleContent(
1830                            long groupId, String articleId, String viewMode,
1831                            String ddmTemplateKey, String languageId, ThemeDisplay themeDisplay)
1832                    throws PortalException, SystemException {
1833    
1834                    JournalArticleDisplay articleDisplay = getArticleDisplay(
1835                            groupId, articleId, ddmTemplateKey, viewMode, languageId,
1836                            themeDisplay);
1837    
1838                    return articleDisplay.getContent();
1839            }
1840    
1841            /**
1842             * Returns the latest web content matching the group and article ID.
1843             *
1844             * @param  groupId the primary key of the web content article's group
1845             * @param  articleId the primary key of the web content article
1846             * @param  viewMode the mode in which the web content is being viewed
1847             * @param  languageId the primary key of the language translation to get
1848             * @param  themeDisplay the theme display
1849             * @return the matching web content
1850             * @throws PortalException if a matching web content article or DDM template
1851             *         could not be found, or if a portal exception occurred
1852             * @throws SystemException if a system exception occurred
1853             */
1854            @Override
1855            public String getArticleContent(
1856                            long groupId, String articleId, String viewMode, String languageId,
1857                            ThemeDisplay themeDisplay)
1858                    throws PortalException, SystemException {
1859    
1860                    return getArticleContent(
1861                            groupId, articleId, viewMode, null, languageId, themeDisplay);
1862            }
1863    
1864            /**
1865             * Returns a web content article display for the specified page of the
1866             * latest version of the web content article, optionally based on the DDM
1867             * template if the article is template driven. If the article is template
1868             * driven, web content transformation tokens are added from the theme
1869             * display (if not <code>null</code>) or the XML request otherwise.
1870             *
1871             * @param  article the web content article
1872             * @param  ddmTemplateKey the primary key of the web content article's DDM
1873             *         template (optionally <code>null</code>). If the article is
1874             *         related to a DDM structure, the template's structure must match
1875             *         it.
1876             * @param  viewMode the mode in which the web content is being viewed
1877             * @param  languageId the primary key of the language translation to get
1878             * @param  page the web content's page number. Page numbers start at
1879             *         <code>1</code>.
1880             * @param  xmlRequest the request that serializes the web content into a
1881             *         hierarchical hash map (optionally <code>null</code>)
1882             * @param  themeDisplay the theme display
1883             * @return the web content article display
1884             * @throws PortalException if a matching DDM template could not be found or
1885             *         if a portal exception occurred
1886             * @throws SystemException if a system exception occurred
1887             */
1888            @Override
1889            public JournalArticleDisplay getArticleDisplay(
1890                            JournalArticle article, String ddmTemplateKey, String viewMode,
1891                            String languageId, int page, String xmlRequest,
1892                            ThemeDisplay themeDisplay)
1893                    throws PortalException, SystemException {
1894    
1895                    String content = null;
1896    
1897                    if (page < 1) {
1898                            page = 1;
1899                    }
1900    
1901                    int numberOfPages = 1;
1902                    boolean paginate = false;
1903                    boolean pageFlow = false;
1904    
1905                    boolean cacheable = true;
1906    
1907                    if (Validator.isNull(xmlRequest)) {
1908                            xmlRequest = "<request />";
1909                    }
1910    
1911                    Map<String, String> tokens = JournalUtil.getTokens(
1912                            article.getGroupId(), themeDisplay, xmlRequest);
1913    
1914                    if ((themeDisplay == null) && xmlRequest.equals("<request />")) {
1915                            tokens.put("company_id", String.valueOf(article.getCompanyId()));
1916    
1917                            Group companyGroup = groupLocalService.getCompanyGroup(
1918                                    article.getCompanyId());
1919    
1920                            tokens.put(
1921                                    "article_group_id", String.valueOf(article.getGroupId()));
1922                            tokens.put(
1923                                    "company_group_id", String.valueOf(companyGroup.getGroupId()));
1924    
1925                            // Deprecated tokens
1926    
1927                            tokens.put("group_id", String.valueOf(article.getGroupId()));
1928                    }
1929    
1930                    tokens.put(
1931                            "article_resource_pk",
1932                            String.valueOf(article.getResourcePrimKey()));
1933    
1934                    String defaultDDMTemplateKey = article.getTemplateId();
1935    
1936                    if (article.isTemplateDriven()) {
1937                            if (Validator.isNull(ddmTemplateKey)) {
1938                                    ddmTemplateKey = defaultDDMTemplateKey;
1939                            }
1940    
1941                            tokens.put(
1942                                    TemplateConstants.CLASS_NAME_ID,
1943                                    String.valueOf(PortalUtil.getClassNameId(DDMStructure.class)));
1944                            tokens.put("structure_id", article.getStructureId());
1945                            tokens.put("template_id", ddmTemplateKey);
1946                    }
1947    
1948                    String xml = article.getContent();
1949    
1950                    try {
1951                            Document document = null;
1952    
1953                            Element rootElement = null;
1954    
1955                            if (article.isTemplateDriven()) {
1956                                    document = SAXReaderUtil.read(xml);
1957    
1958                                    rootElement = document.getRootElement();
1959    
1960                                    Document requestDocument = SAXReaderUtil.read(xmlRequest);
1961    
1962                                    List<Element> pages = rootElement.elements("page");
1963    
1964                                    if (!pages.isEmpty()) {
1965                                            pageFlow = true;
1966    
1967                                            String targetPage = requestDocument.valueOf(
1968                                                    "/request/parameters/parameter[name='targetPage']/" +
1969                                                            "value");
1970    
1971                                            Element pageElement = null;
1972    
1973                                            if (Validator.isNotNull(targetPage)) {
1974                                                    targetPage = HtmlUtil.escapeXPathAttribute(targetPage);
1975    
1976                                                    XPath xPathSelector = SAXReaderUtil.createXPath(
1977                                                            "/root/page[@id = " + targetPage + "]");
1978    
1979                                                    pageElement = (Element)xPathSelector.selectSingleNode(
1980                                                            document);
1981                                            }
1982    
1983                                            if (pageElement != null) {
1984                                                    document = SAXReaderUtil.createDocument(pageElement);
1985    
1986                                                    rootElement = document.getRootElement();
1987    
1988                                                    numberOfPages = pages.size();
1989                                            }
1990                                            else {
1991                                                    if (page > pages.size()) {
1992                                                            page = 1;
1993                                                    }
1994    
1995                                                    pageElement = pages.get(page - 1);
1996    
1997                                                    document = SAXReaderUtil.createDocument(pageElement);
1998    
1999                                                    rootElement = document.getRootElement();
2000    
2001                                                    numberOfPages = pages.size();
2002                                                    paginate = true;
2003                                            }
2004                                    }
2005    
2006                                    rootElement.add(requestDocument.getRootElement().createCopy());
2007    
2008                                    JournalUtil.addAllReservedEls(
2009                                            rootElement, tokens, article, languageId, themeDisplay);
2010    
2011                                    xml = DDMXMLUtil.formatXML(document);
2012                            }
2013                    }
2014                    catch (DocumentException de) {
2015                            throw new SystemException(de);
2016                    }
2017    
2018                    try {
2019                            if (_log.isDebugEnabled()) {
2020                                    _log.debug(
2021                                            "Transforming " + article.getArticleId() + " " +
2022                                                    article.getVersion() + " " + languageId);
2023                            }
2024    
2025                            String script = null;
2026                            String langType = null;
2027    
2028                            if (article.isTemplateDriven()) {
2029    
2030                                    // Try with specified template first (in the current group and
2031                                    // the global group). If a template is not specified, use the
2032                                    // default one. If the specified template does not exist, use
2033                                    // the default one. If the default one does not exist, throw an
2034                                    // exception.
2035    
2036                                    DDMTemplate ddmTemplate = null;
2037    
2038                                    try {
2039                                            ddmTemplate = ddmTemplatePersistence.findByG_C_T(
2040                                                    PortalUtil.getSiteGroupId(article.getGroupId()),
2041                                                    PortalUtil.getClassNameId(DDMStructure.class),
2042                                                    ddmTemplateKey);
2043                                    }
2044                                    catch (NoSuchTemplateException nste1) {
2045                                            try {
2046                                                    Group companyGroup = groupLocalService.getCompanyGroup(
2047                                                            article.getCompanyId());
2048    
2049                                                    ddmTemplate = ddmTemplatePersistence.findByG_C_T(
2050                                                            companyGroup.getGroupId(),
2051                                                            PortalUtil.getClassNameId(DDMStructure.class),
2052                                                            ddmTemplateKey);
2053    
2054                                                    tokens.put(
2055                                                            "company_group_id",
2056                                                            String.valueOf(companyGroup.getGroupId()));
2057                                            }
2058                                            catch (NoSuchTemplateException nste2) {
2059                                                    if (!defaultDDMTemplateKey.equals(ddmTemplateKey)) {
2060                                                            ddmTemplate = ddmTemplatePersistence.findByG_C_T(
2061                                                                    PortalUtil.getSiteGroupId(article.getGroupId()),
2062                                                                    PortalUtil.getClassNameId(DDMStructure.class),
2063                                                                    defaultDDMTemplateKey);
2064                                                    }
2065                                                    else {
2066                                                            throw nste1;
2067                                                    }
2068                                            }
2069                                    }
2070    
2071                                    script = ddmTemplate.getScript();
2072                                    langType = ddmTemplate.getLanguage();
2073                                    cacheable = ddmTemplate.isCacheable();
2074                            }
2075    
2076                            content = JournalUtil.transform(
2077                                    themeDisplay, tokens, viewMode, languageId, xml, script,
2078                                    langType);
2079    
2080                            if (!pageFlow) {
2081                                    String[] pieces = StringUtil.split(
2082                                            content, PropsValues.JOURNAL_ARTICLE_TOKEN_PAGE_BREAK);
2083    
2084                                    if (pieces.length > 1) {
2085                                            if (page > pieces.length) {
2086                                                    page = 1;
2087                                            }
2088    
2089                                            content = pieces[page - 1];
2090                                            numberOfPages = pieces.length;
2091                                            paginate = true;
2092                                    }
2093                            }
2094                    }
2095                    catch (Exception e) {
2096                            throw new SystemException(e);
2097                    }
2098    
2099                    return new JournalArticleDisplayImpl(
2100                            article.getCompanyId(), article.getId(),
2101                            article.getResourcePrimKey(), article.getGroupId(),
2102                            article.getUserId(), article.getArticleId(), article.getVersion(),
2103                            article.getTitle(languageId), article.getUrlTitle(),
2104                            article.getDescription(languageId),
2105                            article.getAvailableLanguageIds(), content, article.getType(),
2106                            article.getStructureId(), ddmTemplateKey, article.isSmallImage(),
2107                            article.getSmallImageId(), article.getSmallImageURL(),
2108                            numberOfPages, page, paginate, cacheable);
2109            }
2110    
2111            /**
2112             * Returns a web content article display for the first page of the specified
2113             * version of the web content article, optionally based on the DDM template
2114             * if the article is template driven. If the article is template driven, web
2115             * content transformation tokens are added from the theme display (if not
2116             * <code>null</code>) or the XML request otherwise.
2117             *
2118             * @param  groupId the primary key of the web content article's group
2119             * @param  articleId the primary key of the web content article
2120             * @param  version the web content article's version
2121             * @param  ddmTemplateKey the primary key of the web content article's DDM
2122             *         template (optionally <code>null</code>). If the article is
2123             *         related to a DDM structure, the template's structure must match
2124             *         it.
2125             * @param  viewMode the mode in which the web content is being viewed
2126             * @param  languageId the primary key of the language translation to get
2127             * @param  page the web content's page number
2128             * @param  xmlRequest the request that serializes the web content into a
2129             *         hierarchical hash map
2130             * @param  themeDisplay the theme display
2131             * @return the web content article display, or <code>null</code> if the
2132             *         article has expired or if article's display date/time is after
2133             *         the current date/time
2134             * @throws PortalException if a matching web content article or DDM template
2135             *         could not be found, or if a portal exception occurred
2136             * @throws SystemException if a system exception occurred
2137             */
2138            @Override
2139            public JournalArticleDisplay getArticleDisplay(
2140                            long groupId, String articleId, double version,
2141                            String ddmTemplateKey, String viewMode, String languageId, int page,
2142                            String xmlRequest, ThemeDisplay themeDisplay)
2143                    throws PortalException, SystemException {
2144    
2145                    Date now = new Date();
2146    
2147                    JournalArticle article = journalArticlePersistence.findByG_A_V(
2148                            groupId, articleId, version);
2149    
2150                    if (article.isExpired()) {
2151                            Date expirationDate = article.getExpirationDate();
2152    
2153                            if ((expirationDate != null) && expirationDate.before(now)) {
2154                                    return null;
2155                            }
2156                    }
2157    
2158                    Date displayDate = article.getDisplayDate();
2159    
2160                    if (displayDate.after(now)) {
2161                            return null;
2162                    }
2163    
2164                    return getArticleDisplay(
2165                            article, ddmTemplateKey, viewMode, languageId, page, xmlRequest,
2166                            themeDisplay);
2167            }
2168    
2169            /**
2170             * Returns a web content article display for the first page of the specified
2171             * version of the web content article matching the group and article ID,
2172             * optionally based on the DDM template if the article is template driven.
2173             * If the article is template driven, web content transformation tokens are
2174             * added from the theme display (if not <code>null</code>).
2175             *
2176             * @param  groupId the primary key of the web content article's group
2177             * @param  articleId the primary key of the web content article
2178             * @param  version the web content article's version
2179             * @param  ddmTemplateKey the primary key of the web content article's DDM
2180             *         template (optionally <code>null</code>). If the article is
2181             *         related to a DDM structure, the template's structure must match
2182             *         it.
2183             * @param  viewMode the mode in which the web content is being viewed
2184             * @param  languageId the primary key of the language translation to get
2185             * @param  themeDisplay the theme display
2186             * @return the web content article display, or <code>null</code> if the
2187             *         article has expired or if article's display date/time is after
2188             *         the current date/time
2189             * @throws PortalException if a matching web content article or DDM template
2190             *         could not be found, or if a portal exception occurred
2191             * @throws SystemException if a system exception occurred
2192             */
2193            @Override
2194            public JournalArticleDisplay getArticleDisplay(
2195                            long groupId, String articleId, double version,
2196                            String ddmTemplateKey, String viewMode, String languageId,
2197                            ThemeDisplay themeDisplay)
2198                    throws PortalException, SystemException {
2199    
2200                    return getArticleDisplay(
2201                            groupId, articleId, version, ddmTemplateKey, viewMode, languageId,
2202                            1, null, themeDisplay);
2203            }
2204    
2205            /**
2206             * Returns a web content article display for the first page of the latest
2207             * version of the web content article matching the group and article ID. If
2208             * the article is template driven, web content transformation tokens are
2209             * added from the theme display (if not <code>null</code>) or the XML
2210             * request otherwise.
2211             *
2212             * @param  groupId the primary key of the web content article's group
2213             * @param  articleId the primary key of the web content article
2214             * @param  viewMode the mode in which the web content is being viewed
2215             * @param  languageId the primary key of the language translation to get
2216             * @param  page the web content's page number
2217             * @param  xmlRequest the request that serializes the web content into a
2218             *         hierarchical hash map
2219             * @param  themeDisplay the theme display
2220             * @return the web content article display, or <code>null</code> if the
2221             *         article has expired or if article's display date/time is after
2222             *         the current date/time
2223             * @throws PortalException if a matching web content article or DDM template
2224             *         could not be found, or if a portal exception occurred
2225             * @throws SystemException if a system exception occurred
2226             */
2227            @Override
2228            public JournalArticleDisplay getArticleDisplay(
2229                            long groupId, String articleId, String viewMode, String languageId,
2230                            int page, String xmlRequest, ThemeDisplay themeDisplay)
2231                    throws PortalException, SystemException {
2232    
2233                    return getArticleDisplay(
2234                            groupId, articleId, null, viewMode, languageId, page, xmlRequest,
2235                            themeDisplay);
2236            }
2237    
2238            /**
2239             * Returns a web content article display for the specified page of the
2240             * latest version of the web content article matching the group and article
2241             * ID, optionally based on the DDM template if the article is template
2242             * driven. If the article is template driven, web content transformation
2243             * tokens are added from the theme display (if not <code>null</code>) or the
2244             * XML request otherwise.
2245             *
2246             * @param  groupId the primary key of the web content article's group
2247             * @param  articleId the primary key of the web content article
2248             * @param  ddmTemplateKey the primary key of the web content article's DDM
2249             *         template (optionally <code>null</code>). If the article is
2250             *         related to a DDM structure, the template's structure must match
2251             *         it.
2252             * @param  viewMode the mode in which the web content is being viewed
2253             * @param  languageId the primary key of the language translation to get
2254             * @param  page the web content's page number
2255             * @param  xmlRequest the request that serializes the web content into a
2256             *         hierarchical hash map
2257             * @param  themeDisplay the theme display
2258             * @return the web content article display, or <code>null</code> if the
2259             *         article has expired or if article's display date/time is after
2260             *         the current date/time
2261             * @throws PortalException if a matching web content article or DDM template
2262             *         could not be found, or if a portal exception occurred
2263             * @throws SystemException if a system exception occurred
2264             */
2265            @Override
2266            public JournalArticleDisplay getArticleDisplay(
2267                            long groupId, String articleId, String ddmTemplateKey,
2268                            String viewMode, String languageId, int page, String xmlRequest,
2269                            ThemeDisplay themeDisplay)
2270                    throws PortalException, SystemException {
2271    
2272                    JournalArticle article = getDisplayArticle(groupId, articleId);
2273    
2274                    return getArticleDisplay(
2275                            groupId, articleId, article.getVersion(), ddmTemplateKey, viewMode,
2276                            languageId, page, xmlRequest, themeDisplay);
2277            }
2278    
2279            /**
2280             * Returns a web content article display for the first page of the latest
2281             * version of the web content article matching the group and article ID,
2282             * optionally based on the DDM template if the article is template driven.
2283             * If the article is template driven, web content transformation tokens are
2284             * added from the theme display (if not <code>null</code>).
2285             *
2286             * @param  groupId the primary key of the web content article's group
2287             * @param  articleId the primary key of the web content article
2288             * @param  ddmTemplateKey the primary key of the web content article's DDM
2289             *         template (optionally <code>null</code>). If the article is
2290             *         related to a DDM structure, the template's structure must match
2291             *         it.
2292             * @param  viewMode the mode in which the web content is being viewed
2293             * @param  languageId the primary key of the language translation to get
2294             * @param  themeDisplay the theme display
2295             * @return the web content article display, or <code>null</code> if the
2296             *         article has expired or if article's display date/time is after
2297             *         the current date/time
2298             * @throws PortalException if a matching web content article or DDM template
2299             *         could not be found, or if a portal exception occurred
2300             * @throws SystemException if a system exception occurred
2301             */
2302            @Override
2303            public JournalArticleDisplay getArticleDisplay(
2304                            long groupId, String articleId, String ddmTemplateKey,
2305                            String viewMode, String languageId, ThemeDisplay themeDisplay)
2306                    throws PortalException, SystemException {
2307    
2308                    JournalArticle article = getDisplayArticle(groupId, articleId);
2309    
2310                    return getArticleDisplay(
2311                            groupId, articleId, article.getVersion(), ddmTemplateKey, viewMode,
2312                            languageId, themeDisplay);
2313            }
2314    
2315            /**
2316             * Returns a web content article display for the first page of the latest
2317             * version of the web content article matching the group and article ID. If
2318             * the article is template driven, web content transformation tokens are
2319             * added from the theme display (if not <code>null</code>).
2320             *
2321             * @param  groupId the primary key of the web content article's group
2322             * @param  articleId the primary key of the web content article
2323             * @param  viewMode the mode in which the web content is being viewed
2324             * @param  languageId the primary key of the language translation to get
2325             * @param  themeDisplay the theme display
2326             * @return the web content article display, or <code>null</code> if the
2327             *         article has expired or if article's display date/time is after
2328             *         the current date/time
2329             * @throws PortalException if a matching web content article or DDM template
2330             *         could not be found, or if a portal exception occurred
2331             * @throws SystemException if a system exception occurred
2332             */
2333            @Override
2334            public JournalArticleDisplay getArticleDisplay(
2335                            long groupId, String articleId, String viewMode, String languageId,
2336                            ThemeDisplay themeDisplay)
2337                    throws PortalException, SystemException {
2338    
2339                    return getArticleDisplay(
2340                            groupId, articleId, null, viewMode, languageId, themeDisplay);
2341            }
2342    
2343            /**
2344             * Returns all the web content articles present in the system.
2345             *
2346             * @return the web content articles present in the system
2347             * @throws SystemException if a system exception occurred
2348             */
2349            @Override
2350            public List<JournalArticle> getArticles() throws SystemException {
2351                    return journalArticlePersistence.findAll();
2352            }
2353    
2354            /**
2355             * Returns all the web content articles belonging to the group.
2356             *
2357             * @param  groupId the primary key of the web content article's group
2358             * @return the web content articles belonging to the group
2359             * @throws SystemException if a system exception occurred
2360             */
2361            @Override
2362            public List<JournalArticle> getArticles(long groupId)
2363                    throws SystemException {
2364    
2365                    return journalArticlePersistence.findByGroupId(groupId);
2366            }
2367    
2368            /**
2369             * Returns a range of all the web content articles belonging to the group.
2370             *
2371             * <p>
2372             * Useful when paginating results. Returns a maximum of <code>end -
2373             * start</code> instances. <code>start</code> and <code>end</code> are not
2374             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2375             * refers to the first result in the set. Setting both <code>start</code>
2376             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2377             * result set.
2378             * </p>
2379             *
2380             * @param  groupId the primary key of the web content article's group
2381             * @param  start the lower bound of the range of web content articles to
2382             *         return
2383             * @param  end the upper bound of the range of web content articles to
2384             *         return (not inclusive)
2385             * @return the range of matching web content articles
2386             * @throws SystemException if a system exception occurred
2387             */
2388            @Override
2389            public List<JournalArticle> getArticles(long groupId, int start, int end)
2390                    throws SystemException {
2391    
2392                    return journalArticlePersistence.findByGroupId(groupId, start, end);
2393            }
2394    
2395            /**
2396             * Returns an ordered range of all the web content articles belonging to the
2397             * group.
2398             *
2399             * <p>
2400             * Useful when paginating results. Returns a maximum of <code>end -
2401             * start</code> instances. <code>start</code> and <code>end</code> are not
2402             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2403             * refers to the first result in the set. Setting both <code>start</code>
2404             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2405             * result set.
2406             * </p>
2407             *
2408             * @param  groupId the primary key of the web content article's group
2409             * @param  start the lower bound of the range of web content articles to
2410             *         return
2411             * @param  end the upper bound of the range of web content articles to
2412             *         return (not inclusive)
2413             * @param  obc the comparator to order the web content articles
2414             * @return the range of matching web content articles ordered by the
2415             *         comparator
2416             * @throws SystemException if a system exception occurred
2417             */
2418            @Override
2419            public List<JournalArticle> getArticles(
2420                            long groupId, int start, int end, OrderByComparator obc)
2421                    throws SystemException {
2422    
2423                    return journalArticlePersistence.findByGroupId(
2424                            groupId, start, end, obc);
2425            }
2426    
2427            /**
2428             * Returns all the web content articles matching the group and folder.
2429             *
2430             * @param  groupId the primary key of the web content article's group
2431             * @param  folderId the primary key of the web content article folder
2432             * @return the matching web content articles
2433             * @throws SystemException if a system exception occurred
2434             */
2435            @Override
2436            public List<JournalArticle> getArticles(long groupId, long folderId)
2437                    throws SystemException {
2438    
2439                    return journalArticlePersistence.findByG_F(groupId, folderId);
2440            }
2441    
2442            /**
2443             * Returns a range of all the web content articles matching the group and
2444             * folder.
2445             *
2446             * <p>
2447             * Useful when paginating results. Returns a maximum of <code>end -
2448             * start</code> instances. <code>start</code> and <code>end</code> are not
2449             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2450             * refers to the first result in the set. Setting both <code>start</code>
2451             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2452             * result set.
2453             * </p>
2454             *
2455             * @param  groupId the primary key of the web content article's group
2456             * @param  folderId the primary key of the web content article's folder
2457             * @param  start the lower bound of the range of web content articles to
2458             *         return
2459             * @param  end the upper bound of the range of web content articles to
2460             *         return (not inclusive)
2461             * @return the range of matching web content articles
2462             * @throws SystemException if a system exception occurred
2463             */
2464            @Override
2465            public List<JournalArticle> getArticles(
2466                            long groupId, long folderId, int start, int end)
2467                    throws SystemException {
2468    
2469                    return journalArticlePersistence.findByG_F(
2470                            groupId, folderId, start, end);
2471            }
2472    
2473            @Override
2474            public List<JournalArticle> getArticles(
2475                            long groupId, long folderId, int status, int start, int end)
2476                    throws SystemException {
2477    
2478                    return journalArticlePersistence.findByG_F_ST(
2479                            groupId, folderId, status, start, end);
2480            }
2481    
2482            /**
2483             * Returns an ordered range of all the web content articles matching the
2484             * group and folder.
2485             *
2486             * <p>
2487             * Useful when paginating results. Returns a maximum of <code>end -
2488             * start</code> instances. <code>start</code> and <code>end</code> are not
2489             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2490             * refers to the first result in the set. Setting both <code>start</code>
2491             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2492             * result set.
2493             * </p>
2494             *
2495             * @param  groupId the primary key of the web content article's group
2496             * @param  folderId the primary key of the web content article's folder
2497             * @param  start the lower bound of the range of web content articles to
2498             *         return
2499             * @param  end the upper bound of the range of web content articles to
2500             *         return (not inclusive)
2501             * @param  orderByComparator the comparator to order the web content
2502             *         articles
2503             * @return the range of matching web content articles ordered by the
2504             *         comparator
2505             * @throws SystemException if a system exception occurred
2506             */
2507            @Override
2508            public List<JournalArticle> getArticles(
2509                            long groupId, long folderId, int start, int end,
2510                            OrderByComparator orderByComparator)
2511                    throws SystemException {
2512    
2513                    return journalArticlePersistence.findByG_F(
2514                            groupId, folderId, start, end, orderByComparator);
2515            }
2516    
2517            /**
2518             * Returns all the web content articles matching the group and article ID.
2519             *
2520             * @param  groupId the primary key of the web content article's group
2521             * @param  articleId the primary key of the web content article
2522             * @return the matching web content articles
2523             * @throws SystemException if a system exception occurred
2524             */
2525            @Override
2526            public List<JournalArticle> getArticles(long groupId, String articleId)
2527                    throws SystemException {
2528    
2529                    return journalArticlePersistence.findByG_A(groupId, articleId);
2530            }
2531    
2532            @Override
2533            public List<JournalArticle> getArticlesByResourcePrimKey(
2534                            long resourcePrimKey)
2535                    throws SystemException {
2536    
2537                    return journalArticlePersistence.findByResourcePrimKey(resourcePrimKey);
2538            }
2539    
2540            /**
2541             * Returns all the web content articles matching the small image ID.
2542             *
2543             * @param  smallImageId the primary key of the web content article's small
2544             *         image
2545             * @return the web content articles matching the small image ID
2546             * @throws SystemException if a system exception occurred
2547             */
2548            @Override
2549            public List<JournalArticle> getArticlesBySmallImageId(long smallImageId)
2550                    throws SystemException {
2551    
2552                    return journalArticlePersistence.findBySmallImageId(smallImageId);
2553            }
2554    
2555            /**
2556             * Returns the number of web content articles belonging to the group.
2557             *
2558             * @param  groupId the primary key of the web content article's group
2559             * @return the number of web content articles belonging to the group
2560             * @throws SystemException if a system exception occurred
2561             */
2562            @Override
2563            public int getArticlesCount(long groupId) throws SystemException {
2564                    return journalArticlePersistence.countByGroupId(groupId);
2565            }
2566    
2567            /**
2568             * Returns the number of web content articles matching the group and folder.
2569             *
2570             * @param  groupId the primary key of the web content article's group
2571             * @param  folderId the primary key of the web content article's folder
2572             * @return the number of matching web content articles
2573             * @throws SystemException if a system exception occurred
2574             */
2575            @Override
2576            public int getArticlesCount(long groupId, long folderId)
2577                    throws SystemException {
2578    
2579                    return journalArticlePersistence.countByG_F(groupId, folderId);
2580            }
2581    
2582            @Override
2583            public int getArticlesCount(long groupId, long folderId, int status)
2584                    throws SystemException {
2585    
2586                    return journalArticlePersistence.countByG_F_ST(
2587                            groupId, folderId, status);
2588            }
2589    
2590            @Override
2591            public int getArticlesCount(long groupId, String articleId)
2592                    throws SystemException {
2593    
2594                    return journalArticlePersistence.countByG_A(groupId, articleId);
2595            }
2596    
2597            /**
2598             * Returns an ordered range of all the web content articles matching the
2599             * company, version, and workflow status.
2600             *
2601             * <p>
2602             * Useful when paginating results. Returns a maximum of <code>end -
2603             * start</code> instances. <code>start</code> and <code>end</code> are not
2604             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2605             * refers to the first result in the set. Setting both <code>start</code>
2606             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2607             * result set.
2608             * </p>
2609             *
2610             * @param  companyId the primary key of the web content article's company
2611             * @param  version the web content article's version
2612             * @param  status the web content article's workflow status. For more
2613             *         information see {@link WorkflowConstants} for constants starting
2614             *         with the "STATUS_" prefix.
2615             * @param  start the lower bound of the range of web content articles to
2616             *         return
2617             * @param  end the upper bound of the range of web content articles to
2618             *         return (not inclusive)
2619             * @return the range of matching web content articles ordered by article ID
2620             * @throws SystemException if a system exception occurred
2621             */
2622            @Override
2623            public List<JournalArticle> getCompanyArticles(
2624                            long companyId, double version, int status, int start, int end)
2625                    throws SystemException {
2626    
2627                    if (status == WorkflowConstants.STATUS_ANY) {
2628                            return journalArticlePersistence.findByC_V(
2629                                    companyId, version, start, end, new ArticleIDComparator(true));
2630                    }
2631                    else {
2632                            return journalArticlePersistence.findByC_V_ST(
2633                                    companyId, version, status, start, end,
2634                                    new ArticleIDComparator(true));
2635                    }
2636            }
2637    
2638            /**
2639             * Returns an ordered range of all the web content articles matching the
2640             * company and workflow status.
2641             *
2642             * <p>
2643             * Useful when paginating results. Returns a maximum of <code>end -
2644             * start</code> instances. <code>start</code> and <code>end</code> are not
2645             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2646             * refers to the first result in the set. Setting both <code>start</code>
2647             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2648             * result set.
2649             * </p>
2650             *
2651             * @param  companyId the primary key of the web content article's company
2652             * @param  status the web content article's workflow status. For more
2653             *         information see {@link WorkflowConstants} for constants starting
2654             *         with the "STATUS_" prefix.
2655             * @param  start the lower bound of the range of web content articles to
2656             *         return
2657             * @param  end the upper bound of the range of web content articles to
2658             *         return (not inclusive)
2659             * @return the range of matching web content articles ordered by article ID
2660             * @throws SystemException if a system exception occurred
2661             */
2662            @Override
2663            public List<JournalArticle> getCompanyArticles(
2664                            long companyId, int status, int start, int end)
2665                    throws SystemException {
2666    
2667                    if (status == WorkflowConstants.STATUS_ANY) {
2668                            return journalArticlePersistence.findByCompanyId(
2669                                    companyId, start, end, new ArticleIDComparator(true));
2670                    }
2671                    else {
2672                            return journalArticlePersistence.findByC_ST(
2673                                    companyId, status, start, end, new ArticleIDComparator(true));
2674                    }
2675            }
2676    
2677            /**
2678             * Returns the number of web content articles matching the company, version,
2679             * and workflow status.
2680             *
2681             * <p>
2682             * Useful when paginating results. Returns a maximum of <code>end -
2683             * start</code> instances. <code>start</code> and <code>end</code> are not
2684             * primary keys, they are indexes in the result set. Thus, <code>0</code>
2685             * refers to the first result in the set. Setting both <code>start</code>
2686             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
2687             * result set.
2688             * </p>
2689             *
2690             * @param  companyId the primary key of the web content article's company
2691             * @param  version the web content article's version
2692             * @param  status the web content article's workflow status. For more
2693             *         information see {@link WorkflowConstants} for constants starting
2694             *         with the "STATUS_" prefix.
2695             * @param  start the lower bound of the range of web content articles to
2696             *         return
2697             * @param  end the upper bound of the range of web content articles to
2698             *         return (not inclusive)
2699             * @return the number of matching web content articles
2700             * @throws SystemException if a system exception occurred
2701             */
2702            @Override
2703            public int getCompanyArticlesCount(
2704                            long companyId, double version, int status, int start, int end)
2705                    throws SystemException {
2706    
2707                    if (status == WorkflowConstants.STATUS_ANY) {
2708                            return journalArticlePersistence.countByC_V(companyId, version);
2709                    }
2710                    else {
2711                            return journalArticlePersistence.countByC_V_ST(
2712                                    companyId, version, status);
2713                    }
2714            }
2715    
2716            /**
2717             * Returns the number of web content articles matching the company and
2718             * workflow status.
2719             *
2720             * @param  companyId the primary key of the web content article's company
2721             * @param  status the web content article's workflow status. For more
2722             *         information see {@link WorkflowConstants} for constants starting
2723             *         with the "STATUS_" prefix.
2724             * @return the number of matching web content articles
2725             * @throws SystemException if a system exception occurred
2726             */
2727            @Override
2728            public int getCompanyArticlesCount(long companyId, int status)
2729                    throws SystemException {
2730    
2731                    if (status == WorkflowConstants.STATUS_ANY) {
2732                            return journalArticlePersistence.countByCompanyId(companyId);
2733                    }
2734                    else {
2735                            return journalArticlePersistence.countByC_ST(companyId, status);
2736                    }
2737            }
2738    
2739            /**
2740             * Returns the matching web content article currently displayed or next to
2741             * be displayed if no article is currently displayed.
2742             *
2743             * @param  groupId the primary key of the web content article's group
2744             * @param  articleId the primary key of the web content article
2745             * @return the matching web content article currently displayed, or the next
2746             *         one to be displayed if no version of the article is currently
2747             *         displayed
2748             * @throws PortalException if no approved matching web content articles
2749             *         could be found
2750             * @throws SystemException if a system exception occurred
2751             */
2752            @Override
2753            public JournalArticle getDisplayArticle(long groupId, String articleId)
2754                    throws PortalException, SystemException {
2755    
2756                    JournalArticle article = fetchDisplayArticle(groupId, articleId);
2757    
2758                    if (article == null) {
2759                            throw new NoSuchArticleException(
2760                                    "No approved JournalArticle exists with the key {groupId=" +
2761                                            groupId + ", " + "articleId=" + articleId + "}");
2762                    }
2763    
2764                    return article;
2765            }
2766    
2767            /**
2768             * Returns the web content article matching the URL title that is currently
2769             * displayed or next to be displayed if no article is currently displayed.
2770             *
2771             * @param  groupId the primary key of the web content article's group
2772             * @param  urlTitle the web content article's accessible URL title
2773             * @return the web content article matching the URL title that is currently
2774             *         displayed, or next one to be displayed if no version of the
2775             *         article is currently displayed
2776             * @throws PortalException if no approved matching web content articles
2777             *         could be found
2778             * @throws SystemException if a system exception occurred
2779             */
2780            @Override
2781            public JournalArticle getDisplayArticleByUrlTitle(
2782                            long groupId, String urlTitle)
2783                    throws PortalException, SystemException {
2784    
2785                    List<JournalArticle> articles = null;
2786    
2787                    OrderByComparator orderByComparator = new ArticleVersionComparator();
2788    
2789                    articles = journalArticlePersistence.findByG_UT_ST(
2790                            groupId, urlTitle, WorkflowConstants.STATUS_APPROVED,
2791                            QueryUtil.ALL_POS, QueryUtil.ALL_POS, orderByComparator);
2792    
2793                    if (articles.isEmpty()) {
2794                            throw new NoSuchArticleException(
2795                                    "No JournalArticle exists with the key {groupId=" + groupId +
2796                                            ", urlTitle=" + urlTitle + "}");
2797                    }
2798    
2799                    Date now = new Date();
2800    
2801                    for (JournalArticle article : articles) {
2802                            Date displayDate = article.getDisplayDate();
2803                            Date expirationDate = article.getExpirationDate();
2804    
2805                            if (((displayDate != null) && displayDate.before(now)) &&
2806                                    ((expirationDate == null) || expirationDate.after(now)) ) {
2807    
2808                                    return article;
2809                            }
2810                    }
2811    
2812                    return articles.get(0);
2813            }
2814    
2815            @Override
2816            public List<JournalArticle> getIndexableArticlesByDDMStructureKey(
2817                            String[] ddmStructureKeys)
2818                    throws SystemException {
2819    
2820                    if (PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
2821                            return getStructureArticles(ddmStructureKeys);
2822                    }
2823    
2824                    QueryDefinition approvedQueryDefinition =
2825                            new QueryDefinition(
2826                                    WorkflowConstants.STATUS_APPROVED, QueryUtil.ALL_POS,
2827                                    QueryUtil.ALL_POS, new ArticleVersionComparator());
2828    
2829                    List<JournalArticle> articles = new ArrayList<JournalArticle>();
2830    
2831                    articles.addAll(
2832                            journalArticleFinder.findByG_C_S(
2833                                    0, JournalArticleConstants.CLASSNAME_ID_DEFAULT,
2834                                    ddmStructureKeys, approvedQueryDefinition));
2835    
2836                    QueryDefinition trashQueryDefinition =
2837                            new QueryDefinition(
2838                                    WorkflowConstants.STATUS_IN_TRASH, QueryUtil.ALL_POS,
2839                                    QueryUtil.ALL_POS, new ArticleVersionComparator());
2840    
2841                    articles.addAll(
2842                            journalArticleFinder.findByG_C_S(
2843                                    0, JournalArticleConstants.CLASSNAME_ID_DEFAULT,
2844                                    ddmStructureKeys, trashQueryDefinition));
2845    
2846                    return articles;
2847            }
2848    
2849            @Override
2850            public List<JournalArticle> getIndexableArticlesByResourcePrimKey(
2851                            long resourcePrimKey)
2852                    throws SystemException {
2853    
2854                    return journalArticlePersistence.findByR_I(resourcePrimKey, true);
2855            }
2856    
2857            /**
2858             * Returns the latest web content article matching the resource primary key,
2859             * preferring articles with approved workflow status.
2860             *
2861             * @param  resourcePrimKey the primary key of the resource instance
2862             * @return the latest web content article matching the resource primary key,
2863             *         preferring articles with approved workflow status
2864             * @throws PortalException if a matching web content article could not be
2865             *         found
2866             * @throws SystemException if a system exception occurred
2867             */
2868            @Override
2869            public JournalArticle getLatestArticle(long resourcePrimKey)
2870                    throws PortalException, SystemException {
2871    
2872                    return getLatestArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY);
2873            }
2874    
2875            /**
2876             * Returns the latest web content article matching the resource primary key
2877             * and workflow status, preferring articles with approved workflow status.
2878             *
2879             * @param  resourcePrimKey the primary key of the resource instance
2880             * @param  status the web content article's workflow status. For more
2881             *         information see {@link WorkflowConstants} for constants starting
2882             *         with the "STATUS_" prefix.
2883             * @return the latest web content article matching the resource primary key
2884             *         and workflow status, preferring articles with approved workflow
2885             *         status
2886             * @throws PortalException if a matching web content article could not be
2887             *         found
2888             * @throws SystemException if a system exception occurred
2889             */
2890            @Override
2891            public JournalArticle getLatestArticle(long resourcePrimKey, int status)
2892                    throws PortalException, SystemException {
2893    
2894                    return getLatestArticle(resourcePrimKey, status, true);
2895            }
2896    
2897            /**
2898             * Returns the latest web content article matching the resource primary key
2899             * and workflow status, optionally preferring articles with approved
2900             * workflow status.
2901             *
2902             * @param  resourcePrimKey the primary key of the resource instance
2903             * @param  status the web content article's workflow status. For more
2904             *         information see {@link WorkflowConstants} for constants starting
2905             *         with the "STATUS_" prefix.
2906             * @param  preferApproved whether to prefer returning the latest matching
2907             *         article that has workflow status {@link
2908             *         WorkflowConstants#STATUS_APPROVED} over returning one that has a
2909             *         different status
2910             * @return the latest web content article matching the resource primary key
2911             *         and workflow status, optionally preferring articles with approved
2912             *         workflow status
2913             * @throws PortalException if a matching web content article could not be
2914             *         found
2915             * @throws SystemException if a system exception occurred
2916             */
2917            @Override
2918            public JournalArticle getLatestArticle(
2919                            long resourcePrimKey, int status, boolean preferApproved)
2920                    throws PortalException, SystemException {
2921    
2922                    List<JournalArticle> articles = null;
2923    
2924                    OrderByComparator orderByComparator = new ArticleVersionComparator();
2925    
2926                    if (status == WorkflowConstants.STATUS_ANY) {
2927                            if (preferApproved) {
2928                                    articles = journalArticlePersistence.findByR_ST(
2929                                            resourcePrimKey, WorkflowConstants.STATUS_APPROVED, 0, 1,
2930                                            orderByComparator);
2931                            }
2932    
2933                            if ((articles == null) || (articles.size() == 0)) {
2934                                    articles = journalArticlePersistence.findByResourcePrimKey(
2935                                            resourcePrimKey, 0, 1, orderByComparator);
2936                            }
2937                    }
2938                    else {
2939                            articles = journalArticlePersistence.findByR_ST(
2940                                    resourcePrimKey, status, 0, 1, orderByComparator);
2941                    }
2942    
2943                    if (articles.isEmpty()) {
2944                            throw new NoSuchArticleException(
2945                                    "No JournalArticle exists with the key {resourcePrimKey=" +
2946                                            resourcePrimKey + "}");
2947                    }
2948    
2949                    return articles.get(0);
2950            }
2951    
2952            /**
2953             * Returns the latest web content article with the group and article ID.
2954             *
2955             * @param  groupId the primary key of the web content article's group
2956             * @param  articleId the primary key of the web content article
2957             * @return the latest matching web content article
2958             * @throws PortalException if a matching web content article could not be
2959             *         found
2960             * @throws SystemException if a system exception occurred
2961             */
2962            @Override
2963            public JournalArticle getLatestArticle(long groupId, String articleId)
2964                    throws PortalException, SystemException {
2965    
2966                    return getLatestArticle(
2967                            groupId, articleId, WorkflowConstants.STATUS_ANY);
2968            }
2969    
2970            /**
2971             * Returns the latest web content article matching the group, article ID,
2972             * and workflow status.
2973             *
2974             * @param  groupId the primary key of the web content article's group
2975             * @param  articleId the primary key of the web content article
2976             * @param  status the web content article's workflow status. For more
2977             *         information see {@link WorkflowConstants} for constants starting
2978             *         with the "STATUS_" prefix.
2979             * @return the latest matching web content article
2980             * @throws PortalException if a matching web content article could not be
2981             *         found
2982             * @throws SystemException if a system exception occurred
2983             */
2984            @Override
2985            public JournalArticle getLatestArticle(
2986                            long groupId, String articleId, int status)
2987                    throws PortalException, SystemException {
2988    
2989                    return getFirstArticle(
2990                            groupId, articleId, status, new ArticleVersionComparator());
2991            }
2992    
2993            /**
2994             * Returns the latest web content article matching the group, class name ID,
2995             * and class PK.
2996             *
2997             * @param  groupId the primary key of the web content article's group
2998             * @param  className the DDMStructure class name if the web content article
2999             *         is related to a DDM structure, the class name associated with the
3000             *         article, or {@link JournalArticleConstants#CLASSNAME_ID_DEFAULT}
3001             *         otherwise
3002             * @param  classPK the primary key of the DDM structure, if the DDMStructure
3003             *         class name is given as the <code>className</code> parameter, the
3004             *         primary key of the class associated with the web content article,
3005             *         or <code>0</code> otherwise
3006             * @return the latest matching web content article
3007             * @throws PortalException if a matching web content article could not be
3008             *         found
3009             * @throws SystemException if a system exception occurred
3010             */
3011            @Override
3012            public JournalArticle getLatestArticle(
3013                            long groupId, String className, long classPK)
3014                    throws PortalException, SystemException {
3015    
3016                    long classNameId = PortalUtil.getClassNameId(className);
3017    
3018                    List<JournalArticle> articles = journalArticlePersistence.findByG_C_C(
3019                            groupId, classNameId, classPK, 0, 1,
3020                            new ArticleVersionComparator());
3021    
3022                    if (articles.isEmpty()) {
3023                            throw new NoSuchArticleException(
3024                                    "No JournalArticle exists with the key {groupId=" + groupId +
3025                                            ", className=" + className + ", classPK =" + classPK + "}");
3026                    }
3027    
3028                    return articles.get(0);
3029            }
3030    
3031            /**
3032             * Returns the latest web content article matching the group, URL title, and
3033             * workflow status.
3034             *
3035             * @param  groupId the primary key of the web content article's group
3036             * @param  urlTitle the web content article's accessible URL title
3037             * @param  status the web content article's workflow status. For more
3038             *         information see {@link WorkflowConstants} for constants starting
3039             *         with the "STATUS_" prefix.
3040             * @return the latest matching web content article
3041             * @throws PortalException if a matching web content article could not be
3042             *         found
3043             * @throws SystemException if a system exception occurred
3044             */
3045            @Override
3046            public JournalArticle getLatestArticleByUrlTitle(
3047                            long groupId, String urlTitle, int status)
3048                    throws PortalException, SystemException {
3049    
3050                    JournalArticle article = fetchLatestArticleByUrlTitle(
3051                            groupId, urlTitle, status);
3052    
3053                    if (article == null) {
3054                            throw new NoSuchArticleException(
3055                                    "No JournalArticle exists with the key {groupId=" + groupId +
3056                                            ", urlTitle=" + urlTitle + ", status=" + status + "}");
3057                    }
3058    
3059                    return article;
3060            }
3061    
3062            /**
3063             * Returns the latest version number of the web content with the group and
3064             * article ID.
3065             *
3066             * @param  groupId the primary key of the web content article's group
3067             * @param  articleId the primary key of the web content article
3068             * @return the latest version number of the matching web content
3069             * @throws PortalException if a matching web content article could not be
3070             *         found
3071             * @throws SystemException if a system exception occurred
3072             */
3073            @Override
3074            public double getLatestVersion(long groupId, String articleId)
3075                    throws PortalException, SystemException {
3076    
3077                    JournalArticle article = getLatestArticle(groupId, articleId);
3078    
3079                    return article.getVersion();
3080            }
3081    
3082            /**
3083             * Returns the latest version number of the web content with the group,
3084             * article ID, and workflow status.
3085             *
3086             * @param  groupId the primary key of the web content article's group
3087             * @param  articleId the primary key of the web content article
3088             * @param  status the web content article's workflow status. For more
3089             *         information see {@link WorkflowConstants} for constants starting
3090             *         with the "STATUS_" prefix.
3091             * @return the latest version number of the matching web content
3092             * @throws PortalException if a matching web content article could not be
3093             *         found
3094             * @throws SystemException if a system exception occurred
3095             */
3096            @Override
3097            public double getLatestVersion(long groupId, String articleId, int status)
3098                    throws PortalException, SystemException {
3099    
3100                    JournalArticle article = getLatestArticle(groupId, articleId, status);
3101    
3102                    return article.getVersion();
3103            }
3104    
3105            /**
3106             * Returns the number of web content articles that are not recycled.
3107             *
3108             * @param  groupId the primary key of the web content article's group
3109             * @param  folderId the primary key of the web content article folder
3110             * @return the number of web content articles that are not recycled
3111             * @throws SystemException if a system exception occurred
3112             */
3113            @Override
3114            public int getNotInTrashArticlesCount(long groupId, long folderId)
3115                    throws SystemException {
3116    
3117                    QueryDefinition queryDefinition = new QueryDefinition(
3118                            WorkflowConstants.STATUS_ANY);
3119    
3120                    List<Long> folderIds = new ArrayList<Long>();
3121    
3122                    folderIds.add(folderId);
3123    
3124                    return journalArticleFinder.countByG_F(
3125                            groupId, folderIds, queryDefinition);
3126            }
3127    
3128            @Override
3129            public JournalArticle getOldestArticle(long groupId, String articleId)
3130                    throws PortalException, SystemException {
3131    
3132                    return getOldestArticle(
3133                            groupId, articleId, WorkflowConstants.STATUS_ANY);
3134            }
3135    
3136            @Override
3137            public JournalArticle getOldestArticle(
3138                            long groupId, String articleId, int status)
3139                    throws PortalException, SystemException {
3140    
3141                    return getFirstArticle(
3142                            groupId, articleId, status, new ArticleVersionComparator(false));
3143            }
3144    
3145            /**
3146             * Returns the web content articles matching the group and DDM structure
3147             * key.
3148             *
3149             * @param  groupId the primary key of the web content article's group
3150             * @param  ddmStructureKey the primary key of the web content article's DDM
3151             *         structure
3152             * @return the matching web content articles
3153             * @throws SystemException if a system exception occurred
3154             */
3155            @Override
3156            public List<JournalArticle> getStructureArticles(
3157                            long groupId, String ddmStructureKey)
3158                    throws SystemException {
3159    
3160                    return journalArticlePersistence.findByG_S(groupId, ddmStructureKey);
3161            }
3162    
3163            /**
3164             * Returns an ordered range of all the web content articles matching the
3165             * group and DDM structure key.
3166             *
3167             * <p>
3168             * Useful when paginating results. Returns a maximum of <code>end -
3169             * start</code> instances. <code>start</code> and <code>end</code> are not
3170             * primary keys, they are indexes in the result set. Thus, <code>0</code>
3171             * refers to the first result in the set. Setting both <code>start</code>
3172             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
3173             * result set.
3174             * </p>
3175             *
3176             * @param  groupId the primary key of the web content article's group
3177             * @param  ddmStructureKey the primary key of the web content article's DDM
3178             *         structure
3179             * @param  start the lower bound of the range of web content articles to
3180             *         return
3181             * @param  end the upper bound of the range of web content articles to
3182             *         return (not inclusive)
3183             * @param  obc the comparator to order the web content articles
3184             * @return the range of matching web content articles ordered by the
3185             *         comparator
3186             * @throws SystemException if a system exception occurred
3187             */
3188            @Override
3189            public List<JournalArticle> getStructureArticles(
3190                            long groupId, String ddmStructureKey, int start, int end,
3191                            OrderByComparator obc)
3192                    throws SystemException {
3193    
3194                    return journalArticlePersistence.findByG_S(
3195                            groupId, ddmStructureKey, start, end, obc);
3196            }
3197    
3198            @Override
3199            public List<JournalArticle> getStructureArticles(String[] ddmStructureKeys)
3200                    throws SystemException {
3201    
3202                    return journalArticlePersistence.findByStructureId(ddmStructureKeys);
3203            }
3204    
3205            /**
3206             * Returns the number of web content articles matching the group and DDM
3207             * structure key.
3208             *
3209             * @param  groupId the primary key of the web content article's group
3210             * @param  ddmStructureKey the primary key of the web content article's DDM
3211             *         structure
3212             * @return the number of matching web content articles
3213             * @throws SystemException if a system exception occurred
3214             */
3215            @Override
3216            public int getStructureArticlesCount(long groupId, String ddmStructureKey)
3217                    throws SystemException {
3218    
3219                    return journalArticlePersistence.countByG_S(groupId, ddmStructureKey);
3220            }
3221    
3222            /**
3223             * Returns the web content articles matching the group and DDM template key.
3224             *
3225             * @param  groupId the primary key of the web content article's group
3226             * @param  ddmTemplateKey the primary key of the web content article's DDM
3227             *         template (optionally <code>null</code>). If the article is
3228             *         related to a DDM structure, the template's structure must match
3229             *         it.
3230             * @return the matching web content articles
3231             * @throws SystemException if a system exception occurred
3232             */
3233            @Override
3234            public List<JournalArticle> getTemplateArticles(
3235                            long groupId, String ddmTemplateKey)
3236                    throws SystemException {
3237    
3238                    return journalArticlePersistence.findByG_T(groupId, ddmTemplateKey);
3239            }
3240    
3241            /**
3242             * Returns an ordered range of all the web content articles matching the
3243             * group and DDM template key.
3244             *
3245             * <p>
3246             * Useful when paginating results. Returns a maximum of <code>end -
3247             * start</code> instances. <code>start</code> and <code>end</code> are not
3248             * primary keys, they are indexes in the result set. Thus, <code>0</code>
3249             * refers to the first result in the set. Setting both <code>start</code>
3250             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
3251             * result set.
3252             * </p>
3253             *
3254             * @param  groupId the primary key of the web content article's group
3255             * @param  ddmTemplateKey the primary key of the web content article's DDM
3256             *         template (optionally <code>null</code>). If the article is
3257             *         related to a DDM structure, the template's structure must match
3258             *         it.
3259             * @param  start the lower bound of the range of web content articles to
3260             *         return
3261             * @param  end the upper bound of the range of web content articles to
3262             *         return (not inclusive)
3263             * @param  obc the comparator to order the web content articles
3264             * @return the range of matching web content articles ordered by the
3265             *         comparator
3266             * @throws SystemException if a system exception occurred
3267             */
3268            @Override
3269            public List<JournalArticle> getTemplateArticles(
3270                            long groupId, String ddmTemplateKey, int start, int end,
3271                            OrderByComparator obc)
3272                    throws SystemException {
3273    
3274                    return journalArticlePersistence.findByG_T(
3275                            groupId, ddmTemplateKey, start, end, obc);
3276            }
3277    
3278            /**
3279             * Returns the number of web content articles matching the group and DDM
3280             * template key.
3281             *
3282             * @param  groupId the primary key of the web content article's group
3283             * @param  ddmTemplateKey the primary key of the web content article's DDM
3284             *         template (optionally <code>null</code>). If the article is
3285             *         related to a DDM structure, the template's structure must match
3286             *         it.
3287             * @return the number of matching web content articles
3288             * @throws SystemException if a system exception occurred
3289             */
3290            @Override
3291            public int getTemplateArticlesCount(long groupId, String ddmTemplateKey)
3292                    throws SystemException {
3293    
3294                    return journalArticlePersistence.countByG_T(groupId, ddmTemplateKey);
3295            }
3296    
3297            @Override
3298            public String getUniqueUrlTitle(
3299                            long groupId, String articleId, String urlTitle)
3300                    throws PortalException, SystemException {
3301    
3302                    for (int i = 1;; i++) {
3303                            JournalArticle article = fetchArticleByUrlTitle(groupId, urlTitle);
3304    
3305                            if ((article == null) || articleId.equals(article.getArticleId())) {
3306                                    break;
3307                            }
3308                            else {
3309                                    String suffix = StringPool.DASH + i;
3310    
3311                                    String prefix = urlTitle;
3312    
3313                                    if (urlTitle.length() > suffix.length()) {
3314                                            prefix = urlTitle.substring(
3315                                                    0, urlTitle.length() - suffix.length());
3316                                    }
3317    
3318                                    urlTitle = prefix + suffix;
3319                            }
3320                    }
3321    
3322                    return urlTitle;
3323            }
3324    
3325            /**
3326             * Returns <code>true</code> if the specified web content article exists.
3327             *
3328             * @param  groupId the primary key of the group
3329             * @param  articleId the primary key of the web content article
3330             * @return <code>true</code> if the specified web content article exists;
3331             *         <code>false</code> otherwise
3332             * @throws SystemException if a system exception occurred
3333             */
3334            @Override
3335            public boolean hasArticle(long groupId, String articleId)
3336                    throws SystemException {
3337    
3338                    JournalArticle article = fetchArticle(groupId, articleId);
3339    
3340                    if (article != null) {
3341                            return true;
3342                    }
3343    
3344                    return false;
3345            }
3346    
3347            /**
3348             * Returns <code>true</code> if the web content article, specified by group
3349             * and article ID, is the latest version.
3350             *
3351             * @param  groupId the primary key of the web content article's group
3352             * @param  articleId the primary key of the web content article
3353             * @param  version the web content article's version
3354             * @return <code>true</code> if the specified web content article is the
3355             *         latest version; <code>false</code> otherwise
3356             * @throws PortalException if a matching web content article could not be
3357             *         found
3358             * @throws SystemException if a system exception occurred
3359             */
3360            @Override
3361            public boolean isLatestVersion(
3362                            long groupId, String articleId, double version)
3363                    throws PortalException, SystemException {
3364    
3365                    if (getLatestVersion(groupId, articleId) == version) {
3366                            return true;
3367                    }
3368                    else {
3369                            return false;
3370                    }
3371            }
3372    
3373            /**
3374             * Returns <code>true</code> if the web content article, specified by group,
3375             * article ID, and workflow status, is the latest version.
3376             *
3377             * @param  groupId the primary key of the web content article's group
3378             * @param  articleId the primary key of the web content article
3379             * @param  version the web content article's version
3380             * @param  status the web content article's workflow status. For more
3381             *         information see {@link WorkflowConstants} for constants starting
3382             *         with the "STATUS_" prefix.
3383             * @return <code>true</code> if the specified web content article is the
3384             *         latest version; <code>false</code> otherwise
3385             * @throws PortalException if a matching web content article could not be
3386             *         found
3387             * @throws SystemException if a system exception occurred
3388             */
3389            @Override
3390            public boolean isLatestVersion(
3391                            long groupId, String articleId, double version, int status)
3392                    throws PortalException, SystemException {
3393    
3394                    if (getLatestVersion(groupId, articleId, status) == version) {
3395                            return true;
3396                    }
3397                    else {
3398                            return false;
3399                    }
3400            }
3401    
3402            /**
3403             * Moves the web content article matching the group and article ID to a new
3404             * folder.
3405             *
3406             * @param  groupId the primary key of the web content article's group
3407             * @param  articleId the primary key of the web content article
3408             * @param  newFolderId the primary key of the web content article's new
3409             *         folder
3410             * @return the updated web content article, which was moved to a new folder
3411             * @throws PortalException if a matching web content article could not be
3412             *         found
3413             * @throws SystemException if a system exception occurred
3414             */
3415            @Indexable(type = IndexableType.REINDEX)
3416            @Override
3417            public JournalArticle moveArticle(
3418                            long groupId, String articleId, long newFolderId)
3419                    throws PortalException, SystemException {
3420    
3421                    List<JournalArticle> articles = journalArticlePersistence.findByG_A(
3422                            groupId, articleId);
3423    
3424                    for (JournalArticle article : articles) {
3425                            article.setFolderId(newFolderId);
3426                            article.setModifiedDate(new Date());
3427                            article.setTreePath(article.buildTreePath());
3428    
3429                            journalArticlePersistence.update(article);
3430                    }
3431    
3432                    return getArticle(groupId, articleId);
3433            }
3434    
3435            /**
3436             * Moves the web content article from the Recycle Bin to a new folder.
3437             *
3438             * @param  userId the primary key of the user updating the web content
3439             *         article
3440             * @param  groupId the primary key of the web content article's group
3441             * @param  article the web content article
3442             * @param  newFolderId the primary key of the web content article's new
3443             *         folder
3444             * @param  serviceContext the service context to be applied. Can set the
3445             *         modification date, portlet preferences, and can set whether to
3446             *         add the default command update for the web content article. With
3447             *         respect to social activities, by setting the service context's
3448             *         command to {@link
3449             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
3450             *         is considered a web content update activity; otherwise it is
3451             *         considered a web content add activity.
3452             * @return the updated web content article, which was moved from the Recycle
3453             *         Bin to a new folder
3454             * @throws PortalException if a trashed web content article with the primary
3455             *         key could not be found or if a portal exception occurred
3456             * @throws SystemException if a system exception occurred
3457             */
3458            @Indexable(type = IndexableType.REINDEX)
3459            @Override
3460            public JournalArticle moveArticleFromTrash(
3461                            long userId, long groupId, JournalArticle article, long newFolderId,
3462                            ServiceContext serviceContext)
3463                    throws PortalException, SystemException {
3464    
3465                    if (article.isInTrashExplicitly()) {
3466                            restoreArticleFromTrash(userId, article);
3467                    }
3468                    else {
3469    
3470                            // Article
3471    
3472                            TrashEntry trashEntry = article.getTrashEntry();
3473    
3474                            TrashVersion trashVersion =
3475                                    trashVersionLocalService.fetchVersion(
3476                                            trashEntry.getEntryId(), JournalArticle.class.getName(),
3477                                            article.getResourcePrimKey());
3478    
3479                            int status = WorkflowConstants.STATUS_APPROVED;
3480    
3481                            if (trashVersion != null) {
3482                                    status = trashVersion.getStatus();
3483                            }
3484    
3485                            updateStatus(
3486                                    userId, article, status, null,
3487                                    new HashMap<String, Serializable>(), serviceContext);
3488    
3489                            // Trash
3490    
3491                            if (trashVersion != null) {
3492                                    trashVersionLocalService.deleteTrashVersion(trashVersion);
3493                            }
3494                    }
3495    
3496                    return moveArticle(groupId, article.getArticleId(), newFolderId);
3497            }
3498    
3499            /**
3500             * Moves the latest version of the web content article matching the group
3501             * and article ID to the recycle bin.
3502             *
3503             * @param  userId the primary key of the user updating the web content
3504             *         article
3505             * @param  article the web content article
3506             * @return the updated web content article, which was moved to the Recycle
3507             *         Bin
3508             * @throws PortalException if the user did not have permission to move the
3509             *         article to the Recycle Bin or if a portal exception occurred
3510             * @throws SystemException if a system exception occurred
3511             */
3512            @Indexable(type = IndexableType.REINDEX)
3513            @Override
3514            public JournalArticle moveArticleToTrash(
3515                            long userId, JournalArticle article)
3516                    throws PortalException, SystemException {
3517    
3518                    // Article
3519    
3520                    article.setModifiedDate(new Date());
3521    
3522                    int oldStatus = article.getStatus();
3523    
3524                    if (oldStatus == WorkflowConstants.STATUS_PENDING) {
3525                            article.setStatus(WorkflowConstants.STATUS_DRAFT);
3526                    }
3527    
3528                    journalArticlePersistence.update(article);
3529    
3530                    List<JournalArticle> articleVersions =
3531                            journalArticlePersistence.findByG_A(
3532                                    article.getGroupId(), article.getArticleId());
3533    
3534                    articleVersions = ListUtil.sort(
3535                            articleVersions, new ArticleVersionComparator());
3536    
3537                    List<ObjectValuePair<Long, Integer>> articleVersionStatusOVPs =
3538                            new ArrayList<ObjectValuePair<Long, Integer>>();
3539    
3540                    if ((articleVersions != null) && !articleVersions.isEmpty()) {
3541                            articleVersionStatusOVPs = getArticleVersionStatuses(
3542                                    articleVersions);
3543                    }
3544    
3545                    article = updateStatus(
3546                            userId, article.getId(), WorkflowConstants.STATUS_IN_TRASH,
3547                            new HashMap<String, Serializable>(), new ServiceContext());
3548    
3549                    // Trash
3550    
3551                    JournalArticleResource articleResource =
3552                            journalArticleResourceLocalService.getArticleResource(
3553                                    article.getResourcePrimKey());
3554    
3555                    UnicodeProperties typeSettingsProperties = new UnicodeProperties();
3556    
3557                    typeSettingsProperties.put("title", article.getArticleId());
3558    
3559                    TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
3560                            userId, article.getGroupId(), JournalArticle.class.getName(),
3561                            article.getResourcePrimKey(), articleResource.getUuid(), null,
3562                            oldStatus, articleVersionStatusOVPs, typeSettingsProperties);
3563    
3564                    String trashArticleId = TrashUtil.getTrashTitle(
3565                            trashEntry.getEntryId());
3566    
3567                    for (JournalArticle articleVersion : articleVersions) {
3568                            articleVersion.setArticleId(trashArticleId);
3569                            articleVersion.setStatus(WorkflowConstants.STATUS_IN_TRASH);
3570    
3571                            journalArticlePersistence.update(articleVersion);
3572                    }
3573    
3574                    articleResource.setArticleId(trashArticleId);
3575    
3576                    journalArticleResourcePersistence.update(articleResource);
3577    
3578                    article.setArticleId(trashArticleId);
3579    
3580                    article = journalArticlePersistence.update(article);
3581    
3582                    // Asset
3583    
3584                    assetEntryLocalService.updateVisible(
3585                            JournalArticle.class.getName(), article.getResourcePrimKey(),
3586                            false);
3587    
3588                    // Social
3589    
3590                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
3591    
3592                    extraDataJSONObject.put("title", article.getTitle());
3593    
3594                    socialActivityLocalService.addActivity(
3595                            userId, article.getGroupId(), JournalArticle.class.getName(),
3596                            article.getResourcePrimKey(),
3597                            SocialActivityConstants.TYPE_MOVE_TO_TRASH,
3598                            extraDataJSONObject.toString(), 0);
3599    
3600                    if (oldStatus == WorkflowConstants.STATUS_PENDING) {
3601                            workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
3602                                    article.getCompanyId(), article.getGroupId(),
3603                                    JournalArticle.class.getName(), article.getId());
3604                    }
3605    
3606                    return article;
3607            }
3608    
3609            /**
3610             * Moves the latest version of the web content article matching the group
3611             * and article ID to the recycle bin.
3612             *
3613             * @param  userId the primary key of the user updating the web content
3614             *         article
3615             * @param  groupId the primary key of the web content article's group
3616             * @param  articleId the primary key of the web content article
3617             * @return the moved web content article or <code>null</code> if no matching
3618             *         article was found
3619             * @throws PortalException if the user did not have permission to move the
3620             *         article to the Recycle Bin or if a portal exception occurred
3621             * @throws SystemException if a system exception occurred
3622             */
3623            @Override
3624            public JournalArticle moveArticleToTrash(
3625                            long userId, long groupId, String articleId)
3626                    throws PortalException, SystemException {
3627    
3628                    List<JournalArticle> articles = journalArticlePersistence.findByG_A(
3629                            groupId, articleId, 0, 1, new ArticleVersionComparator());
3630    
3631                    if (!articles.isEmpty()) {
3632                            return journalArticleLocalService.moveArticleToTrash(
3633                                    userId, articles.get(0));
3634                    }
3635    
3636                    return null;
3637            }
3638    
3639            @Override
3640            public void rebuildTree(long companyId)
3641                    throws PortalException, SystemException {
3642                            journalFolderLocalService.rebuildTree(companyId);
3643            }
3644    
3645            /**
3646             * Removes the web content of the web content article matching the group,
3647             * article ID, and version, and language.
3648             *
3649             * @param  groupId the primary key of the web content article's group
3650             * @param  articleId the primary key of the web content article
3651             * @param  version the web content article's version
3652             * @param  languageId the primary key of the language locale to remove
3653             * @return the updated web content article with the locale removed
3654             * @throws PortalException if a matching web content article could not be
3655             *         found
3656             * @throws SystemException if a system exception occurred
3657             */
3658            @Indexable(type = IndexableType.REINDEX)
3659            @Override
3660            public JournalArticle removeArticleLocale(
3661                            long groupId, String articleId, double version, String languageId)
3662                    throws PortalException, SystemException {
3663    
3664                    JournalArticle article = journalArticlePersistence.findByG_A_V(
3665                            groupId, articleId, version);
3666    
3667                    String title = article.getTitle();
3668    
3669                    title = LocalizationUtil.removeLocalization(
3670                            title, "static-content", languageId, true);
3671    
3672                    article.setTitle(title);
3673    
3674                    String description = article.getDescription();
3675    
3676                    description = LocalizationUtil.removeLocalization(
3677                            description, "static-content", languageId, true);
3678    
3679                    article.setDescription(description);
3680    
3681                    String content = article.getContent();
3682    
3683                    if (article.isTemplateDriven()) {
3684                            content = JournalUtil.removeArticleLocale(content, languageId);
3685                    }
3686                    else {
3687                            content = LocalizationUtil.removeLocalization(
3688                                    content, "static-content", languageId, true);
3689                    }
3690    
3691                    article.setContent(content);
3692    
3693                    article.setModifiedDate(new Date());
3694    
3695                    journalArticlePersistence.update(article);
3696    
3697                    return article;
3698            }
3699    
3700            /**
3701             * Restores the web content article from the Recycle Bin.
3702             *
3703             * @param  userId the primary key of the user restoring the web content
3704             *         article
3705             * @param  article the web content article
3706             * @throws PortalException if the web content article with the primary key
3707             *         could not be found in the Recycle Bin, if the user did not have
3708             *         permission to restore the article, or if a portal exception
3709             *         occurred
3710             * @throws SystemException if a system exception occurred
3711             */
3712            @Indexable(type = IndexableType.REINDEX)
3713            @Override
3714            public JournalArticle restoreArticleFromTrash(
3715                            long userId, JournalArticle article)
3716                    throws PortalException, SystemException {
3717    
3718                    // Article
3719    
3720                    String trashArticleId = TrashUtil.getOriginalTitle(
3721                            article.getArticleId());
3722    
3723                    List<JournalArticle> articleVersions =
3724                            journalArticlePersistence.findByG_A(
3725                                    article.getGroupId(), article.getArticleId());
3726    
3727                    for (JournalArticle articleVersion : articleVersions) {
3728                            articleVersion.setArticleId(trashArticleId);
3729    
3730                            journalArticlePersistence.update(articleVersion);
3731                    }
3732    
3733                    article.setArticleId(trashArticleId);
3734                    article.setModifiedDate(new Date());
3735    
3736                    journalArticlePersistence.update(article);
3737    
3738                    JournalArticleResource articleResource =
3739                            journalArticleResourcePersistence.fetchByPrimaryKey(
3740                                    article.getResourcePrimKey());
3741    
3742                    articleResource.setArticleId(trashArticleId);
3743    
3744                    journalArticleResourcePersistence.update(articleResource);
3745    
3746                    TrashEntry trashEntry = trashEntryLocalService.getEntry(
3747                            JournalArticle.class.getName(), article.getResourcePrimKey());
3748    
3749                    ServiceContext serviceContext = new ServiceContext();
3750    
3751                    serviceContext.setScopeGroupId(article.getGroupId());
3752    
3753                    updateStatus(
3754                            userId, article, trashEntry.getStatus(), null,
3755                            new HashMap<String, Serializable>(), serviceContext);
3756    
3757                    // Trash
3758    
3759                    List<TrashVersion> trashVersions = trashVersionLocalService.getVersions(
3760                            trashEntry.getEntryId());
3761    
3762                    for (TrashVersion trashVersion : trashVersions) {
3763                            JournalArticle trashArticleVersion =
3764                                    journalArticlePersistence.findByPrimaryKey(
3765                                            trashVersion.getClassPK());
3766    
3767                            trashArticleVersion.setStatus(trashVersion.getStatus());
3768    
3769                            journalArticlePersistence.update(trashArticleVersion);
3770                    }
3771    
3772                    trashEntryLocalService.deleteEntry(
3773                            JournalArticle.class.getName(), article.getResourcePrimKey());
3774    
3775                    // Social
3776    
3777                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
3778    
3779                    extraDataJSONObject.put("title", article.getTitle());
3780    
3781                    socialActivityLocalService.addActivity(
3782                            userId, article.getGroupId(), JournalArticle.class.getName(),
3783                            article.getResourcePrimKey(),
3784                            SocialActivityConstants.TYPE_RESTORE_FROM_TRASH,
3785                            extraDataJSONObject.toString(), 0);
3786    
3787                    return article;
3788            }
3789    
3790            @Override
3791            public List<JournalArticle> search(
3792                            long groupId, List<Long> folderIds, int status, int start, int end)
3793                    throws SystemException {
3794    
3795                    QueryDefinition queryDefinition = new QueryDefinition(
3796                            status, start, end, null);
3797    
3798                    return journalArticleFinder.findByG_F(
3799                            groupId, folderIds, queryDefinition);
3800            }
3801    
3802            @Override
3803            public List<JournalArticle> search(
3804                            long groupId, long folderId, int status, int start, int end)
3805                    throws SystemException {
3806    
3807                    List<Long> folderIds = new ArrayList<Long>();
3808    
3809                    folderIds.add(folderId);
3810    
3811                    return search(groupId, folderIds, status, start, end);
3812            }
3813    
3814            /**
3815             * Returns an ordered range of all the web content articles matching the
3816             * parameters without using the indexer, including a keywords parameter for
3817             * matching with the article's ID, title, description, and content, a DDM
3818             * structure key parameter, and a DDM template key parameter. It is
3819             * preferable to use the indexed version {@link #search(long, long, List,
3820             * long, String, String, String, LinkedHashMap, int, int, Sort)} instead of
3821             * this method wherever possible for performance reasons.
3822             *
3823             * <p>
3824             * Useful when paginating results. Returns a maximum of <code>end -
3825             * start</code> instances. <code>start</code> and <code>end</code> are not
3826             * primary keys, they are indexes in the result set. Thus, <code>0</code>
3827             * refers to the first result in the set. Setting both <code>start</code>
3828             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
3829             * result set.
3830             * </p>
3831             *
3832             * @param  companyId the primary key of the web content article's company
3833             * @param  groupId the primary key of the group (optionally <code>0</code>)
3834             * @param  folderIds the primary keys of the web content article folders
3835             *         (optionally {@link java.util.Collections#EMPTY_LIST})
3836             * @param  classNameId the primary key of the DDMStructure class if the web
3837             *         content article is related to a DDM structure, the primary key of
3838             *         the class name associated with the article, or {@link
3839             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
3840             * @param  keywords the keywords (space separated), which may occur in the
3841             *         web content article ID, title, description, or content
3842             *         (optionally <code>null</code>). If the keywords value is not
3843             *         <code>null</code>, the search uses the OR operator in connecting
3844             *         query criteria; otherwise it uses the AND operator.
3845             * @param  version the web content article's version (optionally
3846             *         <code>null</code>)
3847             * @param  type the web content article's type (optionally
3848             *         <code>null</code>)
3849             * @param  ddmStructureKey the primary key of the web content article's DDM
3850             *         structure, if the article is related to a DDM structure, or
3851             *         <code>null</code> otherwise
3852             * @param  ddmTemplateKey the primary key of the web content article's DDM
3853             *         template (optionally <code>null</code>). If the article is
3854             *         related to a DDM structure, the template's structure must match
3855             *         it.
3856             * @param  displayDateGT the date after which a matching web content
3857             *         article's display date must be after (optionally
3858             *         <code>null</code>)
3859             * @param  displayDateLT the date before which a matching web content
3860             *         article's display date must be before (optionally
3861             *         <code>null</code>)
3862             * @param  status the web content article's workflow status. For more
3863             *         information see {@link WorkflowConstants} for constants starting
3864             *         with the "STATUS_" prefix.
3865             * @param  reviewDate the web content article's scheduled review date
3866             *         (optionally <code>null</code>)
3867             * @param  start the lower bound of the range of web content articles to
3868             *         return
3869             * @param  end the upper bound of the range of web content articles to
3870             *         return (not inclusive)
3871             * @param  obc the comparator to order the web content articles
3872             * @return the range of matching web content articles ordered by the
3873             *         comparator
3874             * @throws SystemException if a system exception occurred
3875             */
3876            @Override
3877            public List<JournalArticle> search(
3878                            long companyId, long groupId, List<Long> folderIds,
3879                            long classNameId, String keywords, Double version, String type,
3880                            String ddmStructureKey, String ddmTemplateKey, Date displayDateGT,
3881                            Date displayDateLT, int status, Date reviewDate, int start, int end,
3882                            OrderByComparator obc)
3883                    throws SystemException {
3884    
3885                    return journalArticleFinder.findByKeywords(
3886                            companyId, groupId, folderIds, classNameId, keywords, version, type,
3887                            ddmStructureKey, ddmTemplateKey, displayDateGT, displayDateLT,
3888                            status, reviewDate, start, end, obc);
3889            }
3890    
3891            /**
3892             * Returns an ordered range of all the web content articles matching the
3893             * parameters without using the indexer, including keyword parameters for
3894             * article ID, title, description, and content, a DDM structure key
3895             * parameter, a DDM template key parameter, and an AND operator switch. It
3896             * is preferable to use the indexed version {@link #search(long, long, List,
3897             * long, String, String, String, String, String, String, String, String,
3898             * LinkedHashMap, boolean, int, int, Sort)} instead of this method wherever
3899             * possible for performance reasons.
3900             *
3901             * <p>
3902             * Useful when paginating results. Returns a maximum of <code>end -
3903             * start</code> instances. <code>start</code> and <code>end</code> are not
3904             * primary keys, they are indexes in the result set. Thus, <code>0</code>
3905             * refers to the first result in the set. Setting both <code>start</code>
3906             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
3907             * result set.
3908             * </p>
3909             *
3910             * @param  companyId the primary key of the web content article's company
3911             * @param  groupId the primary key of the group (optionally <code>0</code>)
3912             * @param  folderIds the primary keys of the web content article folders
3913             *         (optionally {@link java.util.Collections#EMPTY_LIST})
3914             * @param  classNameId the primary key of the DDMStructure class if the web
3915             *         content article is related to a DDM structure, the primary key of
3916             *         the class name associated with the article, or {@link
3917             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
3918             * @param  articleId the article ID keywords (space separated, optionally
3919             *         <code>null</code>)
3920             * @param  version the web content article's version (optionally
3921             *         <code>null</code>)
3922             * @param  title the title keywords (space separated, optionally
3923             *         <code>null</code>)
3924             * @param  description the description keywords (space separated, optionally
3925             *         <code>null</code>)
3926             * @param  content the content keywords (space separated, optionally
3927             *         <code>null</code>)
3928             * @param  type the web content article's type (optionally
3929             *         <code>null</code>)
3930             * @param  ddmStructureKey the primary key of the web content article's DDM
3931             *         structure, if the article is related to a DDM structure, or
3932             *         <code>null</code> otherwise
3933             * @param  ddmTemplateKey the primary key of the web content article's DDM
3934             *         template (optionally <code>null</code>). If the article is
3935             *         related to a DDM structure, the template's structure must match
3936             *         it.
3937             * @param  displayDateGT the date after which a matching web content
3938             *         article's display date must be after (optionally
3939             *         <code>null</code>)
3940             * @param  displayDateLT the date before which a matching web content
3941             *         article's display date must be before (optionally
3942             *         <code>null</code>)
3943             * @param  status the web content article's workflow status. For more
3944             *         information see {@link WorkflowConstants} for constants starting
3945             *         with the "STATUS_" prefix.
3946             * @param  reviewDate the web content article's scheduled review date
3947             *         (optionally <code>null</code>)
3948             * @param  andOperator whether every field must match its value or keywords,
3949             *         or just one field must match. Company, group, folder IDs, class
3950             *         name ID, and status must all match their values.
3951             * @param  start the lower bound of the range of web content articles to
3952             *         return
3953             * @param  end the upper bound of the range of web content articles to
3954             *         return (not inclusive)
3955             * @param  obc the comparator to order the web content articles
3956             * @return the range of matching web content articles ordered by the
3957             *         comparator
3958             * @throws SystemException if a system exception occurred
3959             */
3960            @Override
3961            public List<JournalArticle> search(
3962                            long companyId, long groupId, List<Long> folderIds,
3963                            long classNameId, String articleId, Double version, String title,
3964                            String description, String content, String type,
3965                            String ddmStructureKey, String ddmTemplateKey, Date displayDateGT,
3966                            Date displayDateLT, int status, Date reviewDate,
3967                            boolean andOperator, int start, int end, OrderByComparator obc)
3968                    throws SystemException {
3969    
3970                    QueryDefinition queryDefinition = new QueryDefinition(
3971                            status, start, end, obc);
3972    
3973                    return journalArticleFinder.findByC_G_F_C_A_V_T_D_C_T_S_T_D_R(
3974                            companyId, groupId, folderIds, classNameId, articleId, version,
3975                            title, description, content, type, ddmStructureKey, ddmTemplateKey,
3976                            displayDateGT, displayDateLT, reviewDate, andOperator,
3977                            queryDefinition);
3978            }
3979    
3980            /**
3981             * Returns an ordered range of all the web content articles matching the
3982             * parameters without using the indexer, including keyword parameters for
3983             * article ID, title, description, and content, a DDM structure keys
3984             * (plural) parameter, a DDM template keys (plural) parameter, and an AND
3985             * operator switch.
3986             *
3987             * <p>
3988             * Useful when paginating results. Returns a maximum of <code>end -
3989             * start</code> instances. <code>start</code> and <code>end</code> are not
3990             * primary keys, they are indexes in the result set. Thus, <code>0</code>
3991             * refers to the first result in the set. Setting both <code>start</code>
3992             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
3993             * result set.
3994             * </p>
3995             *
3996             * @param  companyId the primary key of the web content article's company
3997             * @param  groupId the primary key of the group (optionally <code>0</code>)
3998             * @param  folderIds the primary keys of the web content article folders
3999             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4000             * @param  classNameId the primary key of the DDMStructure class if the web
4001             *         content article is related to a DDM structure, the primary key of
4002             *         the class name associated with the article, or {@link
4003             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4004             * @param  articleId the article ID keywords (space separated, optionally
4005             *         <code>null</code>)
4006             * @param  version the web content article's version (optionally
4007             *         <code>null</code>)
4008             * @param  title the title keywords (space separated, optionally
4009             *         <code>null</code>)
4010             * @param  description the description keywords (space separated, optionally
4011             *         <code>null</code>)
4012             * @param  content the content keywords (space separated, optionally
4013             *         <code>null</code>)
4014             * @param  type the web content article's type (optionally
4015             *         <code>null</code>)
4016             * @param  ddmStructureKeys the primary keys of the web content article's
4017             *         DDM structures, if the article is related to a DDM structure, or
4018             *         <code>null</code> otherwise
4019             * @param  ddmTemplateKeys the primary keys of the web content article's DDM
4020             *         templates (originally <code>null</code>). If the articles are
4021             *         related to a DDM structure, the template's structure must match
4022             *         it.
4023             * @param  displayDateGT the date after which a matching web content
4024             *         article's display date must be after (optionally
4025             *         <code>null</code>)
4026             * @param  displayDateLT the date before which a matching web content
4027             *         article's display date must be before (optionally
4028             *         <code>null</code>)
4029             * @param  status the web content article's workflow status. For more
4030             *         information see {@link WorkflowConstants} for constants starting
4031             *         with the "STATUS_" prefix.
4032             * @param  reviewDate the web content article's scheduled review date
4033             *         (optionally <code>null</code>)
4034             * @param  andOperator whether every field must match its value or keywords,
4035             *         or just one field must match.  Company, group, folder IDs, class
4036             *         name ID, and status must all match their values.
4037             * @param  start the lower bound of the range of web content articles to
4038             *         return
4039             * @param  end the upper bound of the range of web content articles to
4040             *         return (not inclusive)
4041             * @param  obc the comparator to order the web content articles
4042             * @return the range of matching web content articles ordered by the
4043             *         comparator
4044             * @throws SystemException if a system exception occurred
4045             */
4046            @Override
4047            public List<JournalArticle> search(
4048                            long companyId, long groupId, List<Long> folderIds,
4049                            long classNameId, String articleId, Double version, String title,
4050                            String description, String content, String type,
4051                            String[] ddmStructureKeys, String[] ddmTemplateKeys,
4052                            Date displayDateGT, Date displayDateLT, int status, Date reviewDate,
4053                            boolean andOperator, int start, int end, OrderByComparator obc)
4054                    throws SystemException {
4055    
4056                    QueryDefinition queryDefinition = new QueryDefinition(
4057                            status, start, end, obc);
4058    
4059                    return journalArticleFinder.findByC_G_F_C_A_V_T_D_C_T_S_T_D_R(
4060                            companyId, groupId, folderIds, classNameId, articleId, version,
4061                            title, description, content, type, ddmStructureKeys,
4062                            ddmTemplateKeys, displayDateGT, displayDateLT, reviewDate,
4063                            andOperator, queryDefinition);
4064            }
4065    
4066            /**
4067             * Returns an ordered range of all the web content articles matching the
4068             * parameters using the indexer, including a keywords parameter for matching
4069             * an article's ID, title, description, or content, a DDM structure key
4070             * parameter, a DDM template key parameter, and a finder hash map parameter.
4071             * It is preferable to use this method instead of the non-indexed version
4072             * whenever possible for performance reasons.
4073             *
4074             * <p>
4075             * Useful when paginating results. Returns a maximum of <code>end -
4076             * start</code> instances. <code>start</code> and <code>end</code> are not
4077             * primary keys, they are indexes in the result set. Thus, <code>0</code>
4078             * refers to the first result in the set. Setting both <code>start</code>
4079             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
4080             * result set.
4081             * </p>
4082             *
4083             * @param  companyId the primary key of the web content article's company
4084             * @param  groupId the primary key of the group (optionally <code>0</code>)
4085             * @param  folderIds the primary keys of the web content article folders
4086             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4087             * @param  classNameId the primary key of the DDMStructure class if the web
4088             *         content article is related to a DDM structure, the primary key of
4089             *         the class name associated with the article, or {@link
4090             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4091             * @param  ddmStructureKey the primary key of the web content article's DDM
4092             *         structure, if the article is related to a DDM structure, or
4093             *         <code>null</code> otherwise
4094             * @param  ddmTemplateKey the primary key of the web content article's DDM
4095             *         template (optionally <code>null</code>). If the article is
4096             *         related to a DDM structure, the template's structure must match
4097             *         it.
4098             * @param  keywords the keywords (space separated), which may occur in the
4099             *         web content article ID, title, description, or content
4100             *         (optionally <code>null</code>). If the keywords value is not
4101             *         <code>null</code>, the search uses the OR operator in connecting
4102             *         query criteria; otherwise it uses the AND operator.
4103             * @param  params the finder parameters (optionally <code>null</code>)
4104             * @param  start the lower bound of the range of web content articles to
4105             *         return
4106             * @param  end the upper bound of the range of web content articles to
4107             *         return (not inclusive)
4108             * @param  sort the field, type, and direction by which to sort (optionally
4109             *         <code>null</code>)
4110             * @return the matching web content articles ordered by <code>sort</code>
4111             * @throws SystemException if a system exception occurred
4112             */
4113            @Override
4114            public Hits search(
4115                            long companyId, long groupId, List<Long> folderIds,
4116                            long classNameId, String ddmStructureKey, String ddmTemplateKey,
4117                            String keywords, LinkedHashMap<String, Object> params, int start,
4118                            int end, Sort sort)
4119                    throws SystemException {
4120    
4121                    String articleId = null;
4122                    String title = null;
4123                    String description = null;
4124                    String content = null;
4125                    boolean andOperator = false;
4126    
4127                    if (Validator.isNotNull(keywords)) {
4128                            articleId = keywords;
4129                            title = keywords;
4130                            description = keywords;
4131                            content = keywords;
4132                    }
4133                    else {
4134                            andOperator = true;
4135                    }
4136    
4137                    String status = String.valueOf(WorkflowConstants.STATUS_ANY);
4138    
4139                    if (params != null) {
4140                            params.put("keywords", keywords);
4141                    }
4142    
4143                    return search(
4144                            companyId, groupId, folderIds, classNameId, articleId, title,
4145                            description, content, null, status, ddmStructureKey, ddmTemplateKey,
4146                            params, andOperator, start, end, sort);
4147            }
4148    
4149            /**
4150             * Returns an ordered range of all the web content articles matching the
4151             * parameters using the indexer, including a keywords parameter for matching
4152             * an article's ID, title, description, or content, a DDM structure key
4153             * parameter, a DDM template key parameter, an AND operator switch, and
4154             * parameters for type, status, a finder hash map. It is preferable to use
4155             * this method instead of the non-indexed version whenever possible for
4156             * performance reasons.
4157             *
4158             * <p>
4159             * Useful when paginating results. Returns a maximum of <code>end -
4160             * start</code> instances. <code>start</code> and <code>end</code> are not
4161             * primary keys, they are indexes in the result set. Thus, <code>0</code>
4162             * refers to the first result in the set. Setting both <code>start</code>
4163             * and <code>end</code> to {@link QueryUtil#ALL_POS} will return the full
4164             * result set.
4165             * </p>
4166             *
4167             * @param  companyId the primary key of the web content article's company
4168             * @param  groupId the primary key of the group (optionally <code>0</code>)
4169             * @param  folderIds the primary keys of the web content article folders
4170             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4171             * @param  classNameId the primary key of the DDMStructure class if the web
4172             *         content article is related to a DDM structure, the primary key of
4173             *         the class name associated with the article, or {@link
4174             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4175             * @param  articleId the article ID keywords (space separated, optionally
4176             *         <code>null</code>)
4177             * @param  title the title keywords (space separated, optionally
4178             *         <code>null</code>)
4179             * @param  description the description keywords (space separated, optionally
4180             *         <code>null</code>)
4181             * @param  content the content keywords (space separated, optionally
4182             *         <code>null</code>)
4183             * @param  type the web content article's type (optionally
4184             *         <code>null</code>)
4185             * @param  status the web content article's workflow status. For more
4186             *         information see {@link WorkflowConstants} for constants starting
4187             *         with the "STATUS_" prefix.
4188             * @param  ddmStructureKey the primary key of the web content article's DDM
4189             *         structure, if the article is related to a DDM structure, or
4190             *         <code>null</code> otherwise
4191             * @param  ddmTemplateKey the primary key of the web content article's DDM
4192             *         template (optionally <code>null</code>). If the article is
4193             *         related to a DDM structure, the template's structure must match
4194             *         it.
4195             * @param  params the finder parameters (optionally <code>null</code>). Can
4196             *         set parameter <code>"includeDiscussions"</code> to
4197             *         <code>true</code> to search for the keywords in the web content
4198             *         article discussions.
4199             * @param  andSearch whether every field must match its value or keywords,
4200             *         or just one field must match
4201             * @param  start the lower bound of the range of web content articles to
4202             *         return
4203             * @param  end the upper bound of the range of web content articles to
4204             *         return (not inclusive)
4205             * @param  sort the field, type, and direction by which to sort (optionally
4206             *         <code>null</code>)
4207             * @return the matching web content articles ordered by <code>sort</code>
4208             * @throws SystemException if a system exception occurred
4209             */
4210            @Override
4211            public Hits search(
4212                            long companyId, long groupId, List<Long> folderIds,
4213                            long classNameId, String articleId, String title,
4214                            String description, String content, String type, String status,
4215                            String ddmStructureKey, String ddmTemplateKey,
4216                            LinkedHashMap<String, Object> params, boolean andSearch, int start,
4217                            int end, Sort sort)
4218                    throws SystemException {
4219    
4220                    try {
4221                            SearchContext searchContext = new SearchContext();
4222    
4223                            searchContext.setAndSearch(andSearch);
4224    
4225                            Map<String, Serializable> attributes =
4226                                    new HashMap<String, Serializable>();
4227    
4228                            attributes.put(Field.CLASS_NAME_ID, classNameId);
4229                            attributes.put(Field.CONTENT, content);
4230                            attributes.put(Field.DESCRIPTION, description);
4231                            attributes.put(Field.STATUS, status);
4232                            attributes.put(Field.TITLE, title);
4233                            attributes.put(Field.TYPE, type);
4234                            attributes.put("articleId", articleId);
4235                            attributes.put("ddmStructureKey", ddmStructureKey);
4236                            attributes.put("ddmTemplateKey", ddmTemplateKey);
4237                            attributes.put("params", params);
4238    
4239                            searchContext.setAttributes(attributes);
4240    
4241                            searchContext.setCompanyId(companyId);
4242                            searchContext.setEnd(end);
4243                            searchContext.setFolderIds(folderIds);
4244                            searchContext.setGroupIds(new long[] {groupId});
4245                            searchContext.setIncludeDiscussions(
4246                                    GetterUtil.getBoolean(params.get("includeDiscussions")));
4247    
4248                            if (params != null) {
4249                                    String keywords = (String)params.remove("keywords");
4250    
4251                                    if (Validator.isNotNull(keywords)) {
4252                                            searchContext.setKeywords(keywords);
4253                                    }
4254                            }
4255    
4256                            QueryConfig queryConfig = new QueryConfig();
4257    
4258                            queryConfig.setHighlightEnabled(false);
4259                            queryConfig.setScoreEnabled(false);
4260    
4261                            searchContext.setQueryConfig(queryConfig);
4262    
4263                            if (sort != null) {
4264                                    searchContext.setSorts(sort);
4265                            }
4266    
4267                            searchContext.setStart(start);
4268    
4269                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
4270                                    JournalArticle.class);
4271    
4272                            return indexer.search(searchContext);
4273                    }
4274                    catch (Exception e) {
4275                            throw new SystemException(e);
4276                    }
4277            }
4278    
4279            @Override
4280            public Hits search(
4281                            long groupId, long userId, long creatorUserId, int status,
4282                            int start, int end)
4283                    throws PortalException, SystemException {
4284    
4285                    Indexer indexer = IndexerRegistryUtil.getIndexer(
4286                            JournalArticle.class.getName());
4287    
4288                    SearchContext searchContext = new SearchContext();
4289    
4290                    searchContext.setAttribute(Field.STATUS, status);
4291    
4292                    searchContext.setAttribute("paginationType", "none");
4293    
4294                    if (creatorUserId > 0) {
4295                            searchContext.setAttribute(
4296                                    Field.USER_ID, String.valueOf(creatorUserId));
4297                    }
4298    
4299                    Group group = groupLocalService.getGroup(groupId);
4300    
4301                    searchContext.setCompanyId(group.getCompanyId());
4302    
4303                    searchContext.setEnd(end);
4304                    searchContext.setGroupIds(new long[] {groupId});
4305                    searchContext.setSorts(new Sort(Field.MODIFIED_DATE, true));
4306                    searchContext.setStart(start);
4307                    searchContext.setUserId(userId);
4308    
4309                    return indexer.search(searchContext);
4310            }
4311    
4312            @Override
4313            public int searchCount(long groupId, List<Long> folderIds, int status)
4314                    throws SystemException {
4315    
4316                    QueryDefinition queryDefinition = new QueryDefinition(status);
4317    
4318                    return journalArticleFinder.countByG_F(
4319                            groupId, folderIds, queryDefinition);
4320            }
4321    
4322            @Override
4323            public int searchCount(long groupId, long folderId, int status)
4324                    throws SystemException {
4325    
4326                    List<Long> folderIds = new ArrayList<Long>();
4327    
4328                    folderIds.add(folderId);
4329    
4330                    return searchCount(groupId, folderIds, status);
4331            }
4332    
4333            /**
4334             * Returns the number of web content articles matching the parameters,
4335             * including a keywords parameter for matching with the article's ID, title,
4336             * description, and content, a DDM structure key parameter, and a DDM
4337             * template key parameter.
4338             *
4339             * @param  companyId the primary key of the web content article's company
4340             * @param  groupId the primary key of the group (optionally <code>0</code>)
4341             * @param  folderIds the primary keys of the web content article folders
4342             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4343             * @param  classNameId the primary key of the DDMStructure class if the web
4344             *         content article is related to a DDM structure, the primary key of
4345             *         the class name associated with the article, or {@link
4346             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4347             * @param  keywords the keywords (space separated), which may occur in the
4348             *         web content article ID, title, description, or content
4349             *         (optionally <code>null</code>). If the keywords value is not
4350             *         <code>null</code>, the search uses the OR operator in connecting
4351             *         query criteria; otherwise it uses the AND operator.
4352             * @param  version the web content article's version (optionally
4353             *         <code>null</code>)
4354             * @param  type the web content article's type (optionally
4355             *         <code>null</code>)
4356             * @param  ddmStructureKey the primary key of the web content article's DDM
4357             *         structure, if the article is related to a DDM structure, or
4358             *         <code>null</code> otherwise
4359             * @param  ddmTemplateKey the primary key of the web content article's DDM
4360             *         template (optionally <code>null</code>). If the article is
4361             *         related to a DDM structure, the template's structure must match
4362             *         it.
4363             * @param  displayDateGT the date after which a matching web content
4364             *         article's display date must be after (optionally
4365             *         <code>null</code>)
4366             * @param  displayDateLT the date before which a matching web content
4367             *         article's display date must be before (optionally
4368             *         <code>null</code>)
4369             * @param  status the web content article's workflow status. For more
4370             *         information see {@link WorkflowConstants} for constants starting
4371             *         with the "STATUS_" prefix.
4372             * @param  reviewDate the web content article's scheduled review date
4373             *         (optionally <code>null</code>)
4374             * @return the number of matching web content articles
4375             * @throws SystemException if a system exception occurred
4376             */
4377            @Override
4378            public int searchCount(
4379                            long companyId, long groupId, List<Long> folderIds,
4380                            long classNameId, String keywords, Double version, String type,
4381                            String ddmStructureKey, String ddmTemplateKey, Date displayDateGT,
4382                            Date displayDateLT, int status, Date reviewDate)
4383                    throws SystemException {
4384    
4385                    return journalArticleFinder.countByKeywords(
4386                            companyId, groupId, folderIds, classNameId, keywords, version, type,
4387                            ddmStructureKey, ddmTemplateKey, displayDateGT, displayDateLT,
4388                            status, reviewDate);
4389            }
4390    
4391            /**
4392             * Returns the number of web content articles matching the parameters,
4393             * including keyword parameters for article ID, title, description, and
4394             * content, a DDM structure key parameter, a DDM template key parameter, and
4395             * an AND operator switch.
4396             *
4397             * @param  companyId the primary key of the web content article's company
4398             * @param  groupId the primary key of the group (optionally <code>0</code>)
4399             * @param  folderIds the primary keys of the web content article folders
4400             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4401             * @param  classNameId the primary key of the DDMStructure class if the web
4402             *         content article is related to a DDM structure, the primary key of
4403             *         the class name associated with the article, or {@link
4404             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4405             * @param  articleId the article ID keywords (space separated, optionally
4406             *         <code>null</code>)
4407             * @param  version the web content article's version (optionally
4408             *         <code>null</code>)
4409             * @param  title the title keywords (space separated, optionally
4410             *         <code>null</code>)
4411             * @param  description the description keywords (space separated, optionally
4412             *         <code>null</code>)
4413             * @param  content the content keywords (space separated, optionally
4414             *         <code>null</code>)
4415             * @param  type the web content article's type (optionally
4416             *         <code>null</code>)
4417             * @param  ddmStructureKey the primary key of the web content article's DDM
4418             *         structure, if the article is related to a DDM structure, or
4419             *         <code>null</code> otherwise
4420             * @param  ddmTemplateKey the primary key of the web content article's DDM
4421             *         template (optionally <code>null</code>). If the article is
4422             *         related to a DDM structure, the template's structure must match
4423             *         it.
4424             * @param  displayDateGT the date after which a matching web content
4425             *         article's display date must be after (optionally
4426             *         <code>null</code>)
4427             * @param  displayDateLT the date before which a matching web content
4428             *         article's display date must be before (optionally
4429             *         <code>null</code>)
4430             * @param  status the web content article's workflow status. For more
4431             *         information see {@link WorkflowConstants} for constants starting
4432             *         with the "STATUS_" prefix.
4433             * @param  reviewDate the web content article's scheduled review date
4434             *         (optionally <code>null</code>)
4435             * @param  andOperator whether every field must match its value or keywords,
4436             *         or just one field must match. Group, folder IDs, class name ID,
4437             *         and status must all match their values.
4438             * @return the number of matching web content articles
4439             * @throws SystemException if a system exception occurred
4440             */
4441            @Override
4442            public int searchCount(
4443                            long companyId, long groupId, List<Long> folderIds,
4444                            long classNameId, String articleId, Double version, String title,
4445                            String description, String content, String type,
4446                            String ddmStructureKey, String ddmTemplateKey, Date displayDateGT,
4447                            Date displayDateLT, int status, Date reviewDate,
4448                            boolean andOperator)
4449                    throws SystemException {
4450    
4451                    return journalArticleFinder.countByC_G_F_C_A_V_T_D_C_T_S_T_D_R(
4452                            companyId, groupId, folderIds, classNameId, articleId, version,
4453                            title, description, content, type, ddmStructureKey, ddmTemplateKey,
4454                            displayDateGT, displayDateLT, reviewDate, andOperator,
4455                            new QueryDefinition(status));
4456            }
4457    
4458            /**
4459             * Returns the number of web content articles matching the parameters,
4460             * including keyword parameters for article ID, title, description, and
4461             * content, a DDM structure keys (plural) parameter, a DDM template keys
4462             * (plural) parameter, and an AND operator switch.
4463             *
4464             * @param  companyId the primary key of the web content article's company
4465             * @param  groupId the primary key of the group (optionally <code>0</code>)
4466             * @param  folderIds the primary keys of the web content article folders
4467             *         (optionally {@link java.util.Collections#EMPTY_LIST})
4468             * @param  classNameId the primary key of the DDMStructure class if the web
4469             *         content article is related to a DDM structure, the primary key of
4470             *         the class name associated with the article, or {@link
4471             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
4472             * @param  articleId the article ID keywords (space separated, optionally
4473             *         <code>null</code>)
4474             * @param  version the web content article's version (optionally
4475             *         <code>null</code>)
4476             * @param  title the title keywords (space separated, optionally
4477             *         <code>null</code>)
4478             * @param  description the description keywords (space separated, optionally
4479             *         <code>null</code>)
4480             * @param  content the content keywords (space separated, optionally
4481             *         <code>null</code>)
4482             * @param  type the web content article's type (optionally
4483             *         <code>null</code>)
4484             * @param  ddmStructureKeys the primary keys of the web content article's
4485             *         DDM structures, if the article is related to a DDM structure, or
4486             *         <code>null</code> otherwise
4487             * @param  ddmTemplateKeys the primary keys of the web content article's DDM
4488             *         templates (originally <code>null</code>). If the articles are
4489             *         related to a DDM structure, the template's structure must match
4490             *         it.
4491             * @param  displayDateGT the date after which a matching web content
4492             *         article's display date must be after (optionally
4493             *         <code>null</code>)
4494             * @param  displayDateLT the date before which a matching web content
4495             *         article's display date must be before (optionally
4496             *         <code>null</code>)
4497             * @param  status the web content article's workflow status. For more
4498             *         information see {@link WorkflowConstants} for constants starting
4499             *         with the "STATUS_" prefix.
4500             * @param  reviewDate the web content article's scheduled review date
4501             *         (optionally <code>null</code>)
4502             * @param  andOperator whether every field must match its value or keywords,
4503             *         or just one field must match.  Group, folder IDs, class name ID,
4504             *         and status must all match their values.
4505             * @return the number of matching web content articles
4506             * @throws SystemException if a system exception occurred
4507             */
4508            @Override
4509            public int searchCount(
4510                            long companyId, long groupId, List<Long> folderIds,
4511                            long classNameId, String articleId, Double version, String title,
4512                            String description, String content, String type,
4513                            String[] ddmStructureKeys, String[] ddmTemplateKeys,
4514                            Date displayDateGT, Date displayDateLT, int status, Date reviewDate,
4515                            boolean andOperator)
4516                    throws SystemException {
4517    
4518                    return journalArticleFinder.countByC_G_F_C_A_V_T_D_C_T_S_T_D_R(
4519                            companyId, groupId, folderIds, classNameId, articleId, version,
4520                            title, description, content, type, ddmStructureKeys,
4521                            ddmTemplateKeys, displayDateGT, displayDateLT, reviewDate,
4522                            andOperator, new QueryDefinition(status));
4523            }
4524    
4525            @Override
4526            public void setTreePaths(
4527                            final long folderId, final String treePath, final boolean reindex)
4528                    throws PortalException, SystemException {
4529    
4530                    final Indexer indexer = IndexerRegistryUtil.getIndexer(
4531                            JournalArticle.class.getName());
4532    
4533                    ActionableDynamicQuery actionableDynamicQuery =
4534                            new JournalArticleActionableDynamicQuery() {
4535    
4536                            @Override
4537                            protected void addCriteria(DynamicQuery dynamicQuery) {
4538                                    Property folderIdProperty = PropertyFactoryUtil.forName(
4539                                            "folderId");
4540    
4541                                    dynamicQuery.add(folderIdProperty.eq(folderId));
4542    
4543                                    Property treePathProperty = PropertyFactoryUtil.forName(
4544                                            "treePath");
4545    
4546                                    dynamicQuery.add(treePathProperty.ne(treePath));
4547                            }
4548    
4549                            @Override
4550                            protected void performAction(Object object)
4551                                    throws PortalException, SystemException {
4552    
4553                                    JournalArticle article = (JournalArticle)object;
4554    
4555                                    article.setTreePath(treePath);
4556    
4557                                    updateJournalArticle(article);
4558    
4559                                    if (!reindex) {
4560                                            return;
4561                                    }
4562    
4563                                    indexer.reindex(article);
4564                            }
4565    
4566                    };
4567    
4568                    actionableDynamicQuery.performActions();
4569            }
4570    
4571            /**
4572             * Subscribes the user to notifications for the web content article matching
4573             * the group, notifying him the instant versions of the article are created,
4574             * deleted, or modified.
4575             *
4576             * @param  userId the primary key of the user to subscribe
4577             * @param  groupId the primary key of the group
4578             * @throws PortalException if a matching user or group could not be found
4579             * @throws SystemException if a system exception occurred
4580             */
4581            @Override
4582            public void subscribe(long userId, long groupId)
4583                    throws PortalException, SystemException {
4584    
4585                    subscriptionLocalService.addSubscription(
4586                            userId, groupId, JournalArticle.class.getName(), groupId);
4587            }
4588    
4589            /**
4590             * Unsubscribes the user from notifications for the web content article
4591             * matching the group.
4592             *
4593             * @param  userId the primary key of the user to unsubscribe
4594             * @param  groupId the primary key of the group
4595             * @throws PortalException if a matching user or subscription could not be
4596             *         found
4597             * @throws SystemException if a system exception occurred
4598             */
4599            @Override
4600            public void unsubscribe(long userId, long groupId)
4601                    throws PortalException, SystemException {
4602    
4603                    subscriptionLocalService.deleteSubscription(
4604                            userId, JournalArticle.class.getName(), groupId);
4605            }
4606    
4607            /**
4608             * Updates the web content article matching the version, replacing its
4609             * folder, title, description, content, and layout UUID.
4610             *
4611             * @param  userId the primary key of the user updating the web content
4612             *         article
4613             * @param  groupId the primary key of the web content article's group
4614             * @param  folderId the primary key of the web content article folder
4615             * @param  articleId the primary key of the web content article
4616             * @param  version the web content article's version
4617             * @param  titleMap the web content article's locales and localized titles
4618             * @param  descriptionMap the web content article's locales and localized
4619             *         descriptions
4620             * @param  content the HTML content wrapped in XML. For more information,
4621             *         see the content example in the class description for {@link
4622             *         JournalArticleLocalServiceImpl}.
4623             * @param  layoutUuid the unique string identifying the web content
4624             *         article's display page
4625             * @param  serviceContext the service context to be applied. Can set the
4626             *         modification date, expando bridge attributes, asset category IDs,
4627             *         asset tag names, asset link entry IDs, workflow actions, the
4628             *         "defaultLanguageId" and "urlTitle" attributes, and can set
4629             *         whether to add the default command update for the web content
4630             *         article. With respect to social activities, by setting the
4631             *         service context's command to {@link
4632             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
4633             *         is considered a web content update activity; otherwise it is
4634             *         considered a web content add activity.
4635             * @return the updated web content article
4636             * @throws PortalException if a user with the primary key or a matching web
4637             *         content article could not be found, or if a portal exception
4638             *         occurred
4639             * @throws SystemException if a system exception occurred
4640             */
4641            @Override
4642            public JournalArticle updateArticle(
4643                            long userId, long groupId, long folderId, String articleId,
4644                            double version, Map<Locale, String> titleMap,
4645                            Map<Locale, String> descriptionMap, String content,
4646                            String layoutUuid, ServiceContext serviceContext)
4647                    throws PortalException, SystemException {
4648    
4649                    User user = userPersistence.findByPrimaryKey(userId);
4650    
4651                    JournalArticle article = journalArticlePersistence.findByG_A_V(
4652                            groupId, articleId, version);
4653    
4654                    Date displayDate = article.getDisplayDate();
4655    
4656                    int displayDateMonth = 0;
4657                    int displayDateDay = 0;
4658                    int displayDateYear = 0;
4659                    int displayDateHour = 0;
4660                    int displayDateMinute = 0;
4661    
4662                    if (displayDate != null) {
4663                            Calendar displayCal = CalendarFactoryUtil.getCalendar(
4664                                    user.getTimeZone());
4665    
4666                            displayCal.setTime(displayDate);
4667    
4668                            displayDateMonth = displayCal.get(Calendar.MONTH);
4669                            displayDateDay = displayCal.get(Calendar.DATE);
4670                            displayDateYear = displayCal.get(Calendar.YEAR);
4671                            displayDateHour = displayCal.get(Calendar.HOUR);
4672                            displayDateMinute = displayCal.get(Calendar.MINUTE);
4673    
4674                            if (displayCal.get(Calendar.AM_PM) == Calendar.PM) {
4675                                    displayDateHour += 12;
4676                            }
4677                    }
4678    
4679                    Date expirationDate = article.getExpirationDate();
4680    
4681                    int expirationDateMonth = 0;
4682                    int expirationDateDay = 0;
4683                    int expirationDateYear = 0;
4684                    int expirationDateHour = 0;
4685                    int expirationDateMinute = 0;
4686                    boolean neverExpire = true;
4687    
4688                    if (expirationDate != null) {
4689                            Calendar expirationCal = CalendarFactoryUtil.getCalendar(
4690                                    user.getTimeZone());
4691    
4692                            expirationCal.setTime(expirationDate);
4693    
4694                            expirationDateMonth = expirationCal.get(Calendar.MONTH);
4695                            expirationDateDay = expirationCal.get(Calendar.DATE);
4696                            expirationDateYear = expirationCal.get(Calendar.YEAR);
4697                            expirationDateHour = expirationCal.get(Calendar.HOUR);
4698                            expirationDateMinute = expirationCal.get(Calendar.MINUTE);
4699                            neverExpire = false;
4700    
4701                            if (expirationCal.get(Calendar.AM_PM) == Calendar.PM) {
4702                                    expirationDateHour += 12;
4703                            }
4704                    }
4705    
4706                    Date reviewDate = article.getReviewDate();
4707    
4708                    int reviewDateMonth = 0;
4709                    int reviewDateDay = 0;
4710                    int reviewDateYear = 0;
4711                    int reviewDateHour = 0;
4712                    int reviewDateMinute = 0;
4713                    boolean neverReview = true;
4714    
4715                    if (reviewDate != null) {
4716                            Calendar reviewCal = CalendarFactoryUtil.getCalendar(
4717                                    user.getTimeZone());
4718    
4719                            reviewCal.setTime(reviewDate);
4720    
4721                            reviewDateMonth = reviewCal.get(Calendar.MONTH);
4722                            reviewDateDay = reviewCal.get(Calendar.DATE);
4723                            reviewDateYear = reviewCal.get(Calendar.YEAR);
4724                            reviewDateHour = reviewCal.get(Calendar.HOUR);
4725                            reviewDateMinute = reviewCal.get(Calendar.MINUTE);
4726                            neverReview = false;
4727    
4728                            if (reviewCal.get(Calendar.AM_PM) == Calendar.PM) {
4729                                    reviewDateHour += 12;
4730                            }
4731                    }
4732    
4733                    return journalArticleLocalService.updateArticle(
4734                            userId, groupId, folderId, articleId, version, titleMap,
4735                            descriptionMap, content, article.getType(),
4736                            article.getStructureId(), article.getTemplateId(), layoutUuid,
4737                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
4738                            displayDateMinute, expirationDateMonth, expirationDateDay,
4739                            expirationDateYear, expirationDateHour, expirationDateMinute,
4740                            neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
4741                            reviewDateHour, reviewDateMinute, neverReview,
4742                            article.getIndexable(), article.isSmallImage(),
4743                            article.getSmallImageURL(), null, null, null, serviceContext);
4744            }
4745    
4746            /**
4747             * Updates the web content article with additional parameters.
4748             *
4749             * @param  userId the primary key of the user updating the web content
4750             *         article
4751             * @param  groupId the primary key of the web content article's group
4752             * @param  folderId the primary key of the web content article folder
4753             * @param  articleId the primary key of the web content article
4754             * @param  version the web content article's version
4755             * @param  titleMap the web content article's locales and localized titles
4756             * @param  descriptionMap the web content article's locales and localized
4757             *         descriptions
4758             * @param  content the HTML content wrapped in XML. For more information,
4759             *         see the content example in the class description for {@link
4760             *         JournalArticleLocalServiceImpl}.
4761             * @param  type the structure's type, if the web content article is related
4762             *         to a DDM structure. For more information, see {@link
4763             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
4764             * @param  ddmStructureKey the primary key of the web content article's DDM
4765             *         structure, if the article is related to a DDM structure, or
4766             *         <code>null</code> otherwise
4767             * @param  ddmTemplateKey the primary key of the web content article's DDM
4768             *         template (optionally <code>null</code>). If the article is
4769             *         related to a DDM structure, the template's structure must match
4770             *         it.
4771             * @param  layoutUuid the unique string identifying the web content
4772             *         article's display page
4773             * @param  displayDateMonth the month the web content article is set to
4774             *         display
4775             * @param  displayDateDay the calendar day the web content article is set to
4776             *         display
4777             * @param  displayDateYear the year the web content article is set to
4778             *         display
4779             * @param  displayDateHour the hour the web content article is set to
4780             *         display
4781             * @param  displayDateMinute the minute the web content article is set to
4782             *         display
4783             * @param  expirationDateMonth the month the web content article is set to
4784             *         expire
4785             * @param  expirationDateDay the calendar day the web content article is set
4786             *         to expire
4787             * @param  expirationDateYear the year the web content article is set to
4788             *         expire
4789             * @param  expirationDateHour the hour the web content article is set to
4790             *         expire
4791             * @param  expirationDateMinute the minute the web content article is set to
4792             *         expire
4793             * @param  neverExpire whether the web content article is not set to auto
4794             *         expire
4795             * @param  reviewDateMonth the month the web content article is set for
4796             *         review
4797             * @param  reviewDateDay the calendar day the web content article is set for
4798             *         review
4799             * @param  reviewDateYear the year the web content article is set for review
4800             * @param  reviewDateHour the hour the web content article is set for review
4801             * @param  reviewDateMinute the minute the web content article is set for
4802             *         review
4803             * @param  neverReview whether the web content article is not set for review
4804             * @param  indexable whether the web content is searchable
4805             * @param  smallImage whether to update web content article's a small image.
4806             *         A file must be passed in as <code>smallImageFile</code> value,
4807             *         otherwise the current small image is deleted.
4808             * @param  smallImageURL the web content article's small image URL
4809             *         (optionally <code>null</code>)
4810             * @param  smallImageFile the web content article's new small image file
4811             *         (optionally <code>null</code>). Must pass in
4812             *         <code>smallImage</code> value of <code>true</code> to replace the
4813             *         article's small image file.
4814             * @param  images the web content's images (optionally <code>null</code>)
4815             * @param  articleURL the web content article's accessible URL (optionally
4816             *         <code>null</code>)
4817             * @param  serviceContext the service context to be applied. Can set the
4818             *         modification date, expando bridge attributes, asset category IDs,
4819             *         asset tag names, asset link entry IDs, workflow actions, the
4820             *         "defaultLanguageId" and "urlTitle" attributes, and can set
4821             *         whether to add the default command update for the web content
4822             *         article. With respect to social activities, by setting the
4823             *         service context's command to {@link
4824             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
4825             *         is considered a web content update activity; otherwise it is
4826             *         considered a web content add activity.
4827             * @return the updated web content article
4828             * @throws PortalException if a user with the primary key or a matching web
4829             *         content article could not be found, or if a portal exception
4830             *         occurred
4831             * @throws SystemException if a system exception occurred
4832             */
4833            @Indexable(type = IndexableType.REINDEX)
4834            @Override
4835            public JournalArticle updateArticle(
4836                            long userId, long groupId, long folderId, String articleId,
4837                            double version, Map<Locale, String> titleMap,
4838                            Map<Locale, String> descriptionMap, String content, String type,
4839                            String ddmStructureKey, String ddmTemplateKey, String layoutUuid,
4840                            int displayDateMonth, int displayDateDay, int displayDateYear,
4841                            int displayDateHour, int displayDateMinute, int expirationDateMonth,
4842                            int expirationDateDay, int expirationDateYear,
4843                            int expirationDateHour, int expirationDateMinute,
4844                            boolean neverExpire, int reviewDateMonth, int reviewDateDay,
4845                            int reviewDateYear, int reviewDateHour, int reviewDateMinute,
4846                            boolean neverReview, boolean indexable, boolean smallImage,
4847                            String smallImageURL, File smallImageFile,
4848                            Map<String, byte[]> images, String articleURL,
4849                            ServiceContext serviceContext)
4850                    throws PortalException, SystemException {
4851    
4852                    // Article
4853    
4854                    User user = userPersistence.findByPrimaryKey(userId);
4855                    articleId = StringUtil.toUpperCase(articleId.trim());
4856    
4857                    byte[] smallImageBytes = null;
4858    
4859                    try {
4860                            smallImageBytes = FileUtil.getBytes(smallImageFile);
4861                    }
4862                    catch (IOException ioe) {
4863                    }
4864    
4865                    JournalArticle latestArticle = getLatestArticle(
4866                            groupId, articleId, WorkflowConstants.STATUS_ANY);
4867    
4868                    JournalArticle article = latestArticle;
4869    
4870                    boolean imported = ExportImportThreadLocal.isImportInProcess();
4871    
4872                    double latestVersion = latestArticle.getVersion();
4873    
4874                    boolean addNewVersion = false;
4875    
4876                    if (imported) {
4877                            if (latestVersion > version) {
4878                                    JournalArticle existingArticle =
4879                                            journalArticlePersistence.fetchByG_A_V(
4880                                                    groupId, articleId, version);
4881    
4882                                    if (existingArticle != null) {
4883                                            article = existingArticle;
4884                                    }
4885                                    else {
4886                                            addNewVersion = true;
4887                                    }
4888                            }
4889                            else if (latestVersion < version) {
4890                                    addNewVersion = true;
4891                            }
4892                    }
4893                    else {
4894                            if ((version > 0) && (version != latestVersion)) {
4895                                    throw new ArticleVersionException();
4896                            }
4897    
4898                            serviceContext.validateModifiedDate(
4899                                    latestArticle, ArticleVersionException.class);
4900    
4901                            if (latestArticle.isApproved() || latestArticle.isExpired() ||
4902                                    latestArticle.isScheduled()) {
4903    
4904                                    addNewVersion = true;
4905    
4906                                    version = MathUtil.format(latestVersion + 0.1, 1, 1);
4907                            }
4908                    }
4909    
4910                    Date displayDate = null;
4911                    Date expirationDate = null;
4912                    Date reviewDate = null;
4913    
4914                    if (article.getClassNameId() ==
4915                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
4916    
4917                            displayDate = PortalUtil.getDate(
4918                                    displayDateMonth, displayDateDay, displayDateYear,
4919                                    displayDateHour, displayDateMinute, user.getTimeZone(),
4920                                    ArticleDisplayDateException.class);
4921    
4922                            if (!neverExpire) {
4923                                    expirationDate = PortalUtil.getDate(
4924                                            expirationDateMonth, expirationDateDay, expirationDateYear,
4925                                            expirationDateHour, expirationDateMinute,
4926                                            user.getTimeZone(), ArticleExpirationDateException.class);
4927                            }
4928    
4929                            if (!neverReview) {
4930                                    reviewDate = PortalUtil.getDate(
4931                                            reviewDateMonth, reviewDateDay, reviewDateYear,
4932                                            reviewDateHour, reviewDateMinute, user.getTimeZone(),
4933                                            ArticleReviewDateException.class);
4934                            }
4935                    }
4936    
4937                    Date now = new Date();
4938    
4939                    boolean expired = false;
4940    
4941                    if ((expirationDate != null) && expirationDate.before(now)) {
4942                            expired = true;
4943                    }
4944    
4945                    validate(
4946                            user.getCompanyId(), groupId, latestArticle.getClassNameId(),
4947                            titleMap, content, type, ddmStructureKey, ddmTemplateKey,
4948                            expirationDate, smallImage, smallImageURL, smallImageFile,
4949                            smallImageBytes, serviceContext);
4950    
4951                    if (addNewVersion) {
4952                            long id = counterLocalService.increment();
4953    
4954                            article = journalArticlePersistence.create(id);
4955    
4956                            article.setResourcePrimKey(latestArticle.getResourcePrimKey());
4957                            article.setGroupId(latestArticle.getGroupId());
4958                            article.setCompanyId(latestArticle.getCompanyId());
4959                            article.setUserId(user.getUserId());
4960                            article.setUserName(user.getFullName());
4961                            article.setCreateDate(latestArticle.getCreateDate());
4962                            article.setClassNameId(latestArticle.getClassNameId());
4963                            article.setClassPK(latestArticle.getClassPK());
4964                            article.setArticleId(articleId);
4965                            article.setVersion(version);
4966                            article.setSmallImageId(latestArticle.getSmallImageId());
4967                    }
4968    
4969                    Locale locale = getArticleDefaultLocale(content, serviceContext);
4970    
4971                    String title = titleMap.get(locale);
4972    
4973                    content = format(
4974                            user, groupId, articleId, article.getVersion(), addNewVersion,
4975                            content, ddmStructureKey, images);
4976    
4977                    article.setModifiedDate(serviceContext.getModifiedDate(now));
4978                    article.setFolderId(folderId);
4979                    article.setTreePath(article.buildTreePath());
4980                    article.setTitleMap(titleMap, locale);
4981                    article.setUrlTitle(
4982                            getUniqueUrlTitle(
4983                                    article.getId(), article.getArticleId(), title,
4984                                    latestArticle.getUrlTitle(), serviceContext));
4985                    article.setDescriptionMap(descriptionMap, locale);
4986                    article.setContent(content);
4987                    article.setType(type);
4988                    article.setStructureId(ddmStructureKey);
4989                    article.setTemplateId(ddmTemplateKey);
4990                    article.setLayoutUuid(layoutUuid);
4991                    article.setDisplayDate(displayDate);
4992                    article.setExpirationDate(expirationDate);
4993                    article.setReviewDate(reviewDate);
4994                    article.setIndexable(indexable);
4995                    article.setSmallImage(smallImage);
4996    
4997                    if (smallImage) {
4998                            if ((smallImageFile != null) && (smallImageBytes != null)) {
4999                                    article.setSmallImageId(counterLocalService.increment());
5000                            }
5001                    }
5002                    else {
5003                            article.setSmallImageId(0);
5004                    }
5005    
5006                    article.setSmallImageURL(smallImageURL);
5007    
5008                    if (latestArticle.isPending()) {
5009                            article.setStatus(latestArticle.getStatus());
5010                    }
5011                    else if (!expired) {
5012                            article.setStatus(WorkflowConstants.STATUS_DRAFT);
5013                    }
5014                    else {
5015                            article.setStatus(WorkflowConstants.STATUS_EXPIRED);
5016                    }
5017    
5018                    article.setExpandoBridgeAttributes(serviceContext);
5019    
5020                    journalArticlePersistence.update(article);
5021    
5022                    // Asset
5023    
5024                    updateAsset(
5025                            userId, article, serviceContext.getAssetCategoryIds(),
5026                            serviceContext.getAssetTagNames(),
5027                            serviceContext.getAssetLinkEntryIds());
5028    
5029                    // Dynamic data mapping
5030    
5031                    if (PortalUtil.getClassNameId(DDMStructure.class) ==
5032                                    article.getClassNameId()) {
5033    
5034                            updateDDMStructureXSD(
5035                                    article.getClassPK(), content, serviceContext);
5036                    }
5037    
5038                    // Small image
5039    
5040                    saveImages(
5041                            smallImage, article.getSmallImageId(), smallImageFile,
5042                            smallImageBytes);
5043    
5044                    // Email
5045    
5046                    PortletPreferences preferences =
5047                            ServiceContextUtil.getPortletPreferences(serviceContext);
5048    
5049                    // Workflow
5050    
5051                    if (expired && imported) {
5052                            updateStatus(
5053                                    userId, article, article.getStatus(), articleURL,
5054                                    new HashMap<String, Serializable>(), serviceContext);
5055                    }
5056    
5057                    if (serviceContext.getWorkflowAction() ==
5058                                    WorkflowConstants.ACTION_PUBLISH) {
5059    
5060                            articleURL = buildArticleURL(
5061                                    articleURL, groupId, folderId, articleId);
5062    
5063                            serviceContext.setAttribute("articleURL", articleURL);
5064    
5065                            sendEmail(
5066                                    article, articleURL, preferences, "requested", serviceContext);
5067    
5068                            WorkflowHandlerRegistryUtil.startWorkflowInstance(
5069                                    user.getCompanyId(), groupId, userId,
5070                                    JournalArticle.class.getName(), article.getId(), article,
5071                                    serviceContext);
5072                    }
5073    
5074                    return journalArticlePersistence.findByPrimaryKey(article.getId());
5075            }
5076    
5077            /**
5078             * Updates the web content article matching the version, replacing its
5079             * folder and content.
5080             *
5081             * @param  userId the primary key of the user updating the web content
5082             *         article
5083             * @param  groupId the primary key of the web content article's group
5084             * @param  folderId the primary key of the web content article folder
5085             * @param  articleId the primary key of the web content article
5086             * @param  version the web content article's version
5087             * @param  content the HTML content wrapped in XML. For more information,
5088             *         see the content example in the class description for {@link
5089             *         JournalArticleLocalServiceImpl}.
5090             * @param  serviceContext the service context to be applied. Can set the
5091             *         modification date, expando bridge attributes, asset category IDs,
5092             *         asset tag names, asset link entry IDs, workflow actions, the
5093             *         "defaultLanguageId" and "urlTitle" attributes, and can set
5094             *         whether to add the default command update for the web content
5095             *         article. With respect to social activities, by setting the
5096             *         service context's command to {@link
5097             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
5098             *         is considered a web content update activity; otherwise it is
5099             *         considered a web content add activity.
5100             * @return the updated web content article
5101             * @throws PortalException if a user with the primary key or a matching web
5102             *         content article could not be found, or if a portal exception
5103             *         occurred
5104             * @throws SystemException if a system exception occurred
5105             */
5106            @Override
5107            public JournalArticle updateArticle(
5108                            long userId, long groupId, long folderId, String articleId,
5109                            double version, String content, ServiceContext serviceContext)
5110                    throws PortalException, SystemException {
5111    
5112                    JournalArticle article = journalArticlePersistence.findByG_A_V(
5113                            groupId, articleId, version);
5114    
5115                    return journalArticleLocalService.updateArticle(
5116                            userId, groupId, folderId, articleId, version,
5117                            article.getTitleMap(), article.getDescriptionMap(), content,
5118                            article.getLayoutUuid(), serviceContext);
5119            }
5120    
5121            /**
5122             * @deprecated As of 6.2.0, replaced by {@link
5123             *             #updateArticleTranslation(long, String, double, Locale,
5124             *             String, String, String, Map, ServiceContext)}
5125             */
5126            @Override
5127            public JournalArticle updateArticleTranslation(
5128                            long groupId, String articleId, double version, Locale locale,
5129                            String title, String description, String content,
5130                            Map<String, byte[]> images)
5131                    throws PortalException, SystemException {
5132    
5133                    return journalArticleLocalService.updateArticleTranslation(
5134                            groupId, articleId, version, locale, title, description, content,
5135                            images, null);
5136            }
5137    
5138            /**
5139             * Updates the translation of the web content article.
5140             *
5141             * @param  groupId the primary key of the web content article's group
5142             * @param  articleId the primary key of the web content article
5143             * @param  version the web content article's version
5144             * @param  locale the locale of the web content article's display template
5145             * @param  title the translated web content article title
5146             * @param  description the translated web content article description
5147             * @param  content the HTML content wrapped in XML. For more information,
5148             *         see the content example in the class description for {@link
5149             *         JournalArticleLocalServiceImpl}.
5150             * @param  images the web content's images
5151             * @param  serviceContext the service context to be applied. Can set the
5152             *         modification date and "urlTitle" attribute for the web content
5153             *         article.
5154             * @return the updated web content article
5155             * @throws PortalException if a user with the primary key or a matching web
5156             *         content article could not be found, or if a portal exception
5157             *         occurred
5158             * @throws SystemException if a system exception occurred
5159             */
5160            @Indexable(type = IndexableType.REINDEX)
5161            @Override
5162            public JournalArticle updateArticleTranslation(
5163                            long groupId, String articleId, double version, Locale locale,
5164                            String title, String description, String content,
5165                            Map<String, byte[]> images, ServiceContext serviceContext)
5166                    throws PortalException, SystemException {
5167    
5168                    validateContent(content);
5169    
5170                    JournalArticle oldArticle = getLatestArticle(
5171                            groupId, articleId, WorkflowConstants.STATUS_ANY);
5172    
5173                    double oldVersion = oldArticle.getVersion();
5174    
5175                    if ((version > 0) && (version != oldVersion)) {
5176                            throw new ArticleVersionException();
5177                    }
5178    
5179                    boolean incrementVersion = false;
5180    
5181                    if (oldArticle.isApproved() || oldArticle.isExpired()) {
5182                            incrementVersion = true;
5183                    }
5184    
5185                    if (serviceContext != null) {
5186                            serviceContext.validateModifiedDate(
5187                                    oldArticle, ArticleVersionException.class);
5188                    }
5189    
5190                    JournalArticle article = null;
5191    
5192                    User user = userPersistence.fetchByPrimaryKey(
5193                            serviceContext.getUserId());
5194    
5195                    if (user == null) {
5196                            user = userPersistence.fetchByC_U(
5197                                    oldArticle.getCompanyId(), oldArticle.getUserId());
5198    
5199                            if (user == null) {
5200                                    user = userPersistence.fetchByC_DU(
5201                                            oldArticle.getCompanyId(), true);
5202                            }
5203                    }
5204    
5205                    Locale defaultLocale = getArticleDefaultLocale(content, serviceContext);
5206    
5207                    if (incrementVersion) {
5208                            double newVersion = MathUtil.format(oldVersion + 0.1, 1, 1);
5209    
5210                            long id = counterLocalService.increment();
5211    
5212                            article = journalArticlePersistence.create(id);
5213    
5214                            article.setResourcePrimKey(oldArticle.getResourcePrimKey());
5215                            article.setGroupId(oldArticle.getGroupId());
5216                            article.setCompanyId(oldArticle.getCompanyId());
5217                            article.setUserId(user.getUserId());
5218                            article.setUserName(user.getFullName());
5219                            article.setCreateDate(new Date());
5220                            article.setModifiedDate(new Date());
5221                            article.setFolderId(oldArticle.getFolderId());
5222                            article.setClassNameId(oldArticle.getClassNameId());
5223                            article.setClassPK(oldArticle.getClassPK());
5224                            article.setArticleId(articleId);
5225                            article.setVersion(newVersion);
5226                            article.setTitleMap(oldArticle.getTitleMap(), defaultLocale);
5227                            article.setUrlTitle(
5228                                    getUniqueUrlTitle(
5229                                            id, articleId, title, oldArticle.getUrlTitle(),
5230                                            serviceContext));
5231                            article.setDescriptionMap(oldArticle.getDescriptionMap());
5232                            article.setType(oldArticle.getType());
5233                            article.setStructureId(oldArticle.getStructureId());
5234                            article.setTemplateId(oldArticle.getTemplateId());
5235                            article.setLayoutUuid(oldArticle.getLayoutUuid());
5236                            article.setDisplayDate(oldArticle.getDisplayDate());
5237                            article.setExpirationDate(oldArticle.getExpirationDate());
5238                            article.setReviewDate(oldArticle.getReviewDate());
5239                            article.setIndexable(oldArticle.getIndexable());
5240                            article.setSmallImage(oldArticle.getSmallImage());
5241                            article.setSmallImageId(oldArticle.getSmallImageId());
5242    
5243                            if (article.getSmallImageId() == 0) {
5244                                    article.setSmallImageId(counterLocalService.increment());
5245                            }
5246    
5247                            article.setSmallImageURL(oldArticle.getSmallImageURL());
5248    
5249                            article.setStatus(WorkflowConstants.STATUS_DRAFT);
5250                            article.setStatusDate(new Date());
5251                            article.setExpandoBridgeAttributes(oldArticle);
5252                    }
5253                    else {
5254                            article = oldArticle;
5255                    }
5256    
5257                    Map<Locale, String> titleMap = article.getTitleMap();
5258    
5259                    titleMap.put(locale, title);
5260    
5261                    article.setTitleMap(titleMap, defaultLocale);
5262    
5263                    Map<Locale, String> descriptionMap = article.getDescriptionMap();
5264    
5265                    descriptionMap.put(locale, description);
5266    
5267                    article.setDescriptionMap(descriptionMap);
5268    
5269                    content = format(
5270                            user, groupId, articleId, article.getVersion(),
5271                            !oldArticle.isDraft(), content, oldArticle.getStructureId(),
5272                            images);
5273    
5274                    article.setContent(content);
5275    
5276                    journalArticlePersistence.update(article);
5277    
5278                    return article;
5279            }
5280    
5281            /**
5282             * Updates the web content article's asset with the new asset categories,
5283             * tag names, and link entries, removing and adding them as necessary.
5284             *
5285             * @param  userId the primary key of the user updating the web content
5286             *         article's asset
5287             * @param  article the web content article
5288             * @param  assetCategoryIds the primary keys of the new asset categories
5289             * @param  assetTagNames the new asset tag names
5290             * @param  assetLinkEntryIds the primary keys of the new asset link entries
5291             * @throws PortalException if a portal exception occurred
5292             * @throws SystemException if a system exception occurred
5293             */
5294            @Override
5295            public void updateAsset(
5296                            long userId, JournalArticle article, long[] assetCategoryIds,
5297                            String[] assetTagNames, long[] assetLinkEntryIds)
5298                    throws PortalException, SystemException {
5299    
5300                    boolean visible = article.isApproved();
5301    
5302                    if (article.getClassNameId() !=
5303                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
5304    
5305                            visible = false;
5306                    }
5307    
5308                    boolean addDraftAssetEntry = false;
5309    
5310                    if (!article.isApproved() &&
5311                            (article.getVersion() != JournalArticleConstants.VERSION_DEFAULT)) {
5312    
5313                            int approvedArticlesCount = journalArticlePersistence.countByG_A_ST(
5314                                    article.getGroupId(), article.getArticleId(),
5315                                    JournalArticleConstants.ASSET_ENTRY_CREATION_STATUSES);
5316    
5317                            if (approvedArticlesCount > 0) {
5318                                    addDraftAssetEntry = true;
5319                            }
5320                    }
5321    
5322                    AssetEntry assetEntry = null;
5323    
5324                    if (addDraftAssetEntry) {
5325                            assetEntry = assetEntryLocalService.updateEntry(
5326                                    userId, article.getGroupId(), article.getCreateDate(),
5327                                    article.getModifiedDate(), JournalArticle.class.getName(),
5328                                    article.getPrimaryKey(), article.getUuid(),
5329                                    getClassTypeId(article), assetCategoryIds, assetTagNames, false,
5330                                    null, null, null, ContentTypes.TEXT_HTML, article.getTitle(),
5331                                    article.getDescription(), article.getDescription(), null,
5332                                    article.getLayoutUuid(), 0, 0, null, false);
5333                    }
5334                    else {
5335                            JournalArticleResource journalArticleResource =
5336                                    journalArticleResourceLocalService.getArticleResource(
5337                                            article.getResourcePrimKey());
5338    
5339                            assetEntry = assetEntryLocalService.updateEntry(
5340                                    userId, article.getGroupId(), article.getCreateDate(),
5341                                    article.getModifiedDate(), JournalArticle.class.getName(),
5342                                    journalArticleResource.getResourcePrimKey(),
5343                                    journalArticleResource.getUuid(), getClassTypeId(article),
5344                                    assetCategoryIds, assetTagNames, visible, null, null, null,
5345                                    ContentTypes.TEXT_HTML, article.getTitle(),
5346                                    article.getDescription(), article.getDescription(), null,
5347                                    article.getLayoutUuid(), 0, 0, null, false);
5348                    }
5349    
5350                    assetLinkLocalService.updateLinks(
5351                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
5352                            AssetLinkConstants.TYPE_RELATED);
5353            }
5354    
5355            /**
5356             * Updates the web content article matching the group, article ID, and
5357             * version, replacing its content.
5358             *
5359             * @param  groupId the primary key of the web content article's group
5360             * @param  articleId the primary key of the web content article
5361             * @param  version the web content article's version
5362             * @param  content the HTML content wrapped in XML. For more information,
5363             *         see the content example in the class description for {@link
5364             *         JournalArticleLocalServiceImpl}.
5365             * @return the updated web content article
5366             * @throws PortalException if a matching web content article could not be
5367             *         found
5368             * @throws SystemException if a system exception occurred
5369             */
5370            @Indexable(type = IndexableType.REINDEX)
5371            @Override
5372            public JournalArticle updateContent(
5373                            long groupId, String articleId, double version, String content)
5374                    throws PortalException, SystemException {
5375    
5376                    JournalArticle article = journalArticlePersistence.findByG_A_V(
5377                            groupId, articleId, version);
5378    
5379                    article.setContent(content);
5380    
5381                    journalArticlePersistence.update(article);
5382    
5383                    return article;
5384            }
5385    
5386            /**
5387             * Updates the workflow status of the web content article.
5388             *
5389             * @param  userId the primary key of the user updating the web content
5390             *         article's status
5391             * @param  article the web content article
5392             * @param  status the web content article's workflow status. For more
5393             *         information see {@link WorkflowConstants} for constants starting
5394             *         with the "STATUS_" prefix.
5395             * @param  articleURL the web content article's accessible URL
5396             * @param  workflowContext the web content article's configured workflow
5397             *         context
5398             * @param  serviceContext the service context to be applied. Can set the
5399             *         modification date, status date, and portlet preferences. With
5400             *         respect to social activities, by setting the service context's
5401             *         command to {@link
5402             *         com.liferay.portal.kernel.util.Constants#UPDATE}, the invocation
5403             *         is considered a web content update activity; otherwise it is
5404             *         considered a web content add activity.
5405             * @return the updated web content article
5406             * @throws PortalException if a portal exception occurred
5407             * @throws SystemException if a system exception occurred
5408             */
5409            @Indexable(type = IndexableType.REINDEX)
5410            @Override
5411            public JournalArticle updateStatus(
5412                            long userId, JournalArticle article, int status, String articleURL,
5413                            Map<String, Serializable> workflowContext,
5414                            ServiceContext serviceContext)
5415                    throws PortalException, SystemException {
5416    
5417                    // Article
5418    
5419                    User user = userPersistence.findByPrimaryKey(userId);
5420                    Date now = new Date();
5421    
5422                    if ((status == WorkflowConstants.STATUS_APPROVED) &&
5423                            (article.getClassNameId() ==
5424                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) &&
5425                            (article.getDisplayDate() != null) &&
5426                            now.before(article.getDisplayDate())) {
5427    
5428                            status = WorkflowConstants.STATUS_SCHEDULED;
5429                    }
5430    
5431                    int oldStatus = article.getStatus();
5432    
5433                    if (status == WorkflowConstants.STATUS_APPROVED) {
5434                            Date expirationDate = article.getExpirationDate();
5435    
5436                            if ((expirationDate != null) && expirationDate.before(now)) {
5437                                    article.setExpirationDate(null);
5438                            }
5439                    }
5440    
5441                    if (status == WorkflowConstants.STATUS_EXPIRED) {
5442                            article.setExpirationDate(now);
5443                    }
5444    
5445                    article.setStatus(status);
5446                    article.setStatusByUserId(user.getUserId());
5447                    article.setStatusByUserName(user.getFullName());
5448                    article.setStatusDate(serviceContext.getModifiedDate(now));
5449    
5450                    journalArticlePersistence.update(article);
5451    
5452                    if (hasModifiedLatestApprovedVersion(
5453                                    article.getGroupId(), article.getArticleId(),
5454                                    article.getVersion())) {
5455    
5456                            if (status == WorkflowConstants.STATUS_APPROVED) {
5457                                    updateUrlTitles(
5458                                            article.getGroupId(), article.getArticleId(),
5459                                            article.getUrlTitle());
5460    
5461                                    // Asset
5462    
5463                                    if ((oldStatus != WorkflowConstants.STATUS_APPROVED) &&
5464                                            (article.getVersion() !=
5465                                                    JournalArticleConstants.VERSION_DEFAULT)) {
5466    
5467                                            AssetEntry draftAssetEntry =
5468                                                    assetEntryLocalService.fetchEntry(
5469                                                            JournalArticle.class.getName(),
5470                                                            article.getPrimaryKey());
5471    
5472                                            if (draftAssetEntry != null) {
5473                                                    long[] assetCategoryIds =
5474                                                            draftAssetEntry.getCategoryIds();
5475                                                    String[] assetTagNames = draftAssetEntry.getTagNames();
5476    
5477                                                    List<AssetLink> assetLinks =
5478                                                            assetLinkLocalService.getDirectLinks(
5479                                                                    draftAssetEntry.getEntryId(),
5480                                                                    AssetLinkConstants.TYPE_RELATED);
5481    
5482                                                    long[] assetLinkEntryIds = StringUtil.split(
5483                                                            ListUtil.toString(
5484                                                                    assetLinks, AssetLink.ENTRY_ID2_ACCESSOR), 0L);
5485    
5486                                                    AssetEntry assetEntry =
5487                                                            assetEntryLocalService.updateEntry(
5488                                                                    userId, article.getGroupId(),
5489                                                                    article.getCreateDate(),
5490                                                                    article.getModifiedDate(),
5491                                                                    JournalArticle.class.getName(),
5492                                                                    article.getResourcePrimKey(), article.getUuid(),
5493                                                                    getClassTypeId(article), assetCategoryIds,
5494                                                                    assetTagNames, false, null, null, null,
5495                                                                    ContentTypes.TEXT_HTML, article.getTitle(),
5496                                                                    article.getDescription(),
5497                                                                    article.getDescription(), null,
5498                                                                    article.getLayoutUuid(), 0, 0, null, false);
5499    
5500                                                    assetLinkLocalService.updateLinks(
5501                                                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
5502                                                            AssetLinkConstants.TYPE_RELATED);
5503    
5504                                                    SystemEventHierarchyEntryThreadLocal.push(
5505                                                            JournalArticle.class);
5506    
5507                                                    try {
5508                                                            assetEntryLocalService.deleteEntry(
5509                                                                    JournalArticle.class.getName(),
5510                                                                    article.getPrimaryKey());
5511                                                    }
5512                                                    finally {
5513                                                            SystemEventHierarchyEntryThreadLocal.pop(
5514                                                                    JournalArticle.class);
5515                                                    }
5516                                            }
5517                                    }
5518    
5519                                    if (article.getClassNameId() ==
5520                                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
5521    
5522                                            assetEntryLocalService.updateEntry(
5523                                                    JournalArticle.class.getName(),
5524                                                    article.getResourcePrimKey(), article.getDisplayDate(),
5525                                                    article.getExpirationDate(), true);
5526                                    }
5527    
5528                                    // Social
5529    
5530                                    JSONObject extraDataJSONObject =
5531                                            JSONFactoryUtil.createJSONObject();
5532    
5533                                    extraDataJSONObject.put("title", article.getTitle());
5534    
5535                                    if (serviceContext.isCommandUpdate()) {
5536                                            socialActivityLocalService.addActivity(
5537                                                    user.getUserId(), article.getGroupId(),
5538                                                    JournalArticle.class.getName(),
5539                                                    article.getResourcePrimKey(),
5540                                                    JournalActivityKeys.UPDATE_ARTICLE,
5541                                                    extraDataJSONObject.toString(), 0);
5542                                    }
5543                                    else {
5544                                            socialActivityLocalService.addUniqueActivity(
5545                                                    user.getUserId(), article.getGroupId(),
5546                                                    JournalArticle.class.getName(),
5547                                                    article.getResourcePrimKey(),
5548                                                    JournalActivityKeys.ADD_ARTICLE,
5549                                                    extraDataJSONObject.toString(), 0);
5550                                    }
5551                            }
5552                            else if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
5553                                    updatePreviousApprovedArticle(article);
5554                            }
5555                    }
5556    
5557                    if ((article.getClassNameId() ==
5558                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) &&
5559                            (oldStatus != WorkflowConstants.STATUS_IN_TRASH) &&
5560                            (status != WorkflowConstants.STATUS_IN_TRASH)) {
5561    
5562                            // Email
5563    
5564                            if ((oldStatus == WorkflowConstants.STATUS_PENDING) &&
5565                                    ((status == WorkflowConstants.STATUS_APPROVED) ||
5566                                     (status == WorkflowConstants.STATUS_DENIED))) {
5567    
5568                                    String msg = "granted";
5569    
5570                                    if (status == WorkflowConstants.STATUS_DENIED) {
5571                                            msg = "denied";
5572                                    }
5573    
5574                                    try {
5575                                            PortletPreferences preferences =
5576                                                    ServiceContextUtil.getPortletPreferences(
5577                                                            serviceContext);
5578    
5579                                            articleURL = buildArticleURL(
5580                                                    articleURL, article.getGroupId(), article.getFolderId(),
5581                                                    article.getArticleId());
5582    
5583                                            sendEmail(
5584                                                    article, articleURL, preferences, msg, serviceContext);
5585                                    }
5586                                    catch (Exception e) {
5587                                            _log.error(
5588                                                    "Unable to send email to notify the change of status " +
5589                                                            " to " + msg + " for article " + article.getId() +
5590                                                                    ": " + e.getMessage());
5591                                    }
5592                            }
5593    
5594                            // Subscriptions
5595    
5596                            notifySubscribers(article, serviceContext);
5597                    }
5598    
5599                    return article;
5600            }
5601    
5602            /**
5603             * Updates the workflow status of the web content article matching the class
5604             * PK.
5605             *
5606             * @param  userId the primary key of the user updating the web content
5607             *         article's status
5608             * @param  classPK the primary key of the DDM structure, if the web content
5609             *         article is related to a DDM structure, the primary key of the
5610             *         class associated with the article, or <code>0</code> otherwise
5611             * @param  status the web content article's workflow status. For more
5612             *         information see {@link WorkflowConstants} for constants starting
5613             *         with the "STATUS_" prefix.
5614             * @param  workflowContext the web content article's configured workflow
5615             * @param  serviceContext the service context to be applied. Can set the
5616             *         modification date, portlet preferences, and can set whether to
5617             *         add the default command update for the web content article.
5618             * @return the updated web content article
5619             * @throws PortalException if a matching web content article could not be
5620             *         found or if a portal exception occurred
5621             * @throws SystemException if a system exception occurred
5622             */
5623            @Override
5624            public JournalArticle updateStatus(
5625                            long userId, long classPK, int status,
5626                            Map<String, Serializable> workflowContext,
5627                            ServiceContext serviceContext)
5628                    throws PortalException, SystemException {
5629    
5630                    JournalArticle article = getArticle(classPK);
5631    
5632                    return journalArticleLocalService.updateStatus(
5633                            userId, article, status, null, workflowContext, serviceContext);
5634            }
5635    
5636            /**
5637             * Updates the workflow status of the web content article matching the
5638             * group, article ID, and version.
5639             *
5640             * @param  userId the primary key of the user updating the web content
5641             *         article's status
5642             * @param  groupId the primary key of the web content article's group
5643             * @param  articleId the primary key of the web content article
5644             * @param  version the web content article's version
5645             * @param  status the web content article's workflow status. For more
5646             *         information see {@link WorkflowConstants} for constants starting
5647             *         with the "STATUS_" prefix.
5648             * @param  articleURL the web content article's accessible URL
5649             * @param  workflowContext the web content article's configured workflow
5650             * @param  serviceContext the service context to be applied. Can set the
5651             *         modification date, portlet preferences, and can set whether to
5652             *         add the default command update for the web content article.
5653             * @return the updated web content article
5654             * @throws PortalException if a matching web content article could not be
5655             *         found or if a portal exception occurred
5656             * @throws SystemException if a system exception occurred
5657             */
5658            @Override
5659            public JournalArticle updateStatus(
5660                            long userId, long groupId, String articleId, double version,
5661                            int status, String articleURL,
5662                            Map<String, Serializable> workflowContext,
5663                            ServiceContext serviceContext)
5664                    throws PortalException, SystemException {
5665    
5666                    JournalArticle article = journalArticlePersistence.findByG_A_V(
5667                            groupId, articleId, version);
5668    
5669                    return journalArticleLocalService.updateStatus(
5670                            userId, article, status, articleURL, workflowContext,
5671                            serviceContext);
5672            }
5673    
5674            /**
5675             * Updates the web content articles matching the group, class name ID, and
5676             * DDM template key, replacing the DDM template key with a new one.
5677             *
5678             * @param  groupId the primary key of the web content article's group
5679             * @param  classNameId the primary key of the DDMStructure class if the web
5680             *         content article is related to a DDM structure, the primary key of
5681             *         the class name associated with the article, or {@link
5682             *         JournalArticleConstants#CLASSNAME_ID_DEFAULT} otherwise
5683             * @param  oldDDMTemplateKey the primary key of the web content article's
5684             *         old DDM template
5685             * @param  newDDMTemplateKey the primary key of the web content article's
5686             *         new DDM template
5687             * @throws SystemException if a system exception occurred
5688             */
5689            @Override
5690            public void updateTemplateId(
5691                            long groupId, long classNameId, String oldDDMTemplateKey,
5692                            String newDDMTemplateKey)
5693                    throws SystemException {
5694    
5695                    List<JournalArticle> articles = journalArticlePersistence.findByG_C_T(
5696                            groupId, classNameId, oldDDMTemplateKey);
5697    
5698                    for (JournalArticle article : articles) {
5699                            article.setTemplateId(newDDMTemplateKey);
5700    
5701                            journalArticlePersistence.update(article);
5702                    }
5703            }
5704    
5705            protected String buildArticleURL(
5706                    String articleURL, long groupId, long folderId, String articleId) {
5707    
5708                    StringBundler sb = new StringBundler(13);
5709    
5710                    sb.append(articleURL);
5711                    sb.append(StringPool.AMPERSAND);
5712                    sb.append(PortalUtil.getPortletNamespace(PortletKeys.JOURNAL));
5713                    sb.append("groupId=");
5714                    sb.append(groupId);
5715                    sb.append(StringPool.AMPERSAND);
5716                    sb.append(PortalUtil.getPortletNamespace(PortletKeys.JOURNAL));
5717                    sb.append("folderId=");
5718                    sb.append(folderId);
5719                    sb.append(StringPool.AMPERSAND);
5720                    sb.append(PortalUtil.getPortletNamespace(PortletKeys.JOURNAL));
5721                    sb.append("articleId=");
5722                    sb.append(articleId);
5723    
5724                    return sb.toString();
5725            }
5726    
5727            protected void checkArticlesByDisplayDate(Date displayDate)
5728                    throws PortalException, SystemException {
5729    
5730                    List<JournalArticle> articles = journalArticlePersistence.findByLtD_S(
5731                            displayDate, WorkflowConstants.STATUS_SCHEDULED);
5732    
5733                    for (JournalArticle article : articles) {
5734                            ServiceContext serviceContext = new ServiceContext();
5735    
5736                            serviceContext.setCommand(Constants.UPDATE);
5737    
5738                            String layoutFullURL = PortalUtil.getLayoutFullURL(
5739                                    article.getGroupId(), PortletKeys.JOURNAL);
5740    
5741                            serviceContext.setLayoutFullURL(layoutFullURL);
5742    
5743                            serviceContext.setScopeGroupId(article.getGroupId());
5744    
5745                            journalArticleLocalService.updateStatus(
5746                                    article.getUserId(), article, WorkflowConstants.STATUS_APPROVED,
5747                                    null, new HashMap<String, Serializable>(), serviceContext);
5748                    }
5749            }
5750    
5751            protected void checkArticlesByExpirationDate(Date expirationDate)
5752                    throws PortalException, SystemException {
5753    
5754                    List<JournalArticle> articles =
5755                            journalArticleFinder.findByExpirationDate(
5756                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT,
5757                                    new Date(
5758                                            expirationDate.getTime() + _JOURNAL_ARTICLE_CHECK_INTERVAL),
5759                                    new QueryDefinition(WorkflowConstants.STATUS_APPROVED));
5760    
5761                    if (_log.isDebugEnabled()) {
5762                            _log.debug("Expiring " + articles.size() + " articles");
5763                    }
5764    
5765                    Set<Long> companyIds = new HashSet<Long>();
5766    
5767                    for (JournalArticle article : articles) {
5768                            if (PropsValues.JOURNAL_ARTICLE_EXPIRE_ALL_VERSIONS) {
5769                                    List<JournalArticle> currentArticles =
5770                                            journalArticlePersistence.findByG_A(
5771                                                    article.getGroupId(), article.getArticleId(),
5772                                                    QueryUtil.ALL_POS, QueryUtil.ALL_POS,
5773                                                    new ArticleVersionComparator(true));
5774    
5775                                    for (JournalArticle currentArticle : currentArticles) {
5776                                            currentArticle.setExpirationDate(
5777                                                    article.getExpirationDate());
5778                                            currentArticle.setStatus(WorkflowConstants.STATUS_EXPIRED);
5779    
5780                                            journalArticlePersistence.update(currentArticle);
5781                                    }
5782                            }
5783                            else {
5784                                    article.setStatus(WorkflowConstants.STATUS_EXPIRED);
5785    
5786                                    journalArticlePersistence.update(article);
5787                            }
5788    
5789                            updatePreviousApprovedArticle(article);
5790    
5791                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
5792                                    JournalArticle.class);
5793    
5794                            indexer.reindex(article);
5795    
5796                            JournalContentUtil.clearCache(
5797                                    article.getGroupId(), article.getArticleId(),
5798                                    article.getTemplateId());
5799    
5800                            companyIds.add(article.getCompanyId());
5801                    }
5802    
5803                    for (long companyId : companyIds) {
5804                            CacheUtil.clearCache(companyId);
5805                    }
5806    
5807                    if (_previousCheckDate == null) {
5808                            _previousCheckDate = new Date(
5809                                    expirationDate.getTime() - _JOURNAL_ARTICLE_CHECK_INTERVAL);
5810                    }
5811            }
5812    
5813            protected void checkArticlesByReviewDate(Date reviewDate)
5814                    throws PortalException, SystemException {
5815    
5816                    List<JournalArticle> latestArticles = new ArrayList<JournalArticle>();
5817    
5818                    List<JournalArticle> articles = journalArticleFinder.findByReviewDate(
5819                            JournalArticleConstants.CLASSNAME_ID_DEFAULT, reviewDate,
5820                            _previousCheckDate);
5821    
5822                    for (JournalArticle article : articles) {
5823                            long groupId = article.getGroupId();
5824                            String articleId = article.getArticleId();
5825                            double version = article.getVersion();
5826    
5827                            if (!journalArticleLocalService.isLatestVersion(
5828                                            groupId, articleId, version)) {
5829    
5830                                    article = journalArticleLocalService.getLatestArticle(
5831                                            groupId, articleId);
5832                            }
5833    
5834                            if (!latestArticles.contains(article)) {
5835                                    if (_log.isDebugEnabled()) {
5836                                            _log.debug(
5837                                                    "Sending review notification for article " +
5838                                                            article.getId());
5839                                    }
5840    
5841                                    latestArticles.add(article);
5842    
5843                                    String articleURL = StringPool.BLANK;
5844    
5845                                    long ownerId = article.getGroupId();
5846                                    int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
5847                                    long plid = PortletKeys.PREFS_PLID_SHARED;
5848                                    String portletId = PortletKeys.JOURNAL;
5849    
5850                                    PortletPreferences preferences =
5851                                            portletPreferencesLocalService.getPreferences(
5852                                                    article.getCompanyId(), ownerId, ownerType, plid,
5853                                                    portletId);
5854    
5855                                    sendEmail(
5856                                            article, articleURL, preferences, "review",
5857                                            new ServiceContext());
5858                            }
5859                    }
5860            }
5861    
5862            protected void checkStructure(Document contentDoc, Element root)
5863                    throws PortalException {
5864    
5865                    for (Element el : root.elements()) {
5866                            checkStructureField(el, contentDoc);
5867    
5868                            checkStructure(contentDoc, el);
5869                    }
5870            }
5871    
5872            protected void checkStructure(JournalArticle article)
5873                    throws PortalException, SystemException {
5874    
5875                    Group companyGroup = groupLocalService.getCompanyGroup(
5876                            article.getCompanyId());
5877    
5878                    DDMStructure structure = null;
5879    
5880                    try {
5881                            structure = ddmStructurePersistence.findByG_C_S(
5882                                    PortalUtil.getSiteGroupId(article.getGroupId()),
5883                                    PortalUtil.getClassNameId(JournalArticle.class),
5884                                    article.getStructureId());
5885                    }
5886                    catch (NoSuchStructureException nsse) {
5887                            structure = ddmStructurePersistence.findByG_C_S(
5888                                    companyGroup.getGroupId(),
5889                                    PortalUtil.getClassNameId(JournalArticle.class),
5890                                    article.getStructureId());
5891                    }
5892    
5893                    String content = GetterUtil.getString(article.getContent());
5894    
5895                    try {
5896                            Document contentDocument = SAXReaderUtil.read(content);
5897                            Document xsdDocument = SAXReaderUtil.read(structure.getXsd());
5898    
5899                            checkStructure(contentDocument, xsdDocument.getRootElement());
5900                    }
5901                    catch (DocumentException de) {
5902                            throw new SystemException(de);
5903                    }
5904                    catch (StructureXsdException sxsde) {
5905                            long groupId = article.getGroupId();
5906                            String articleId = article.getArticleId();
5907                            double version = article.getVersion();
5908    
5909                            if (_log.isWarnEnabled()) {
5910                                    _log.warn(
5911                                            "Article {groupId=" + groupId + ", articleId=" +
5912                                                    articleId + ", version=" + version +
5913                                                            "} has content that does not match its " +
5914                                                                    "structure: " + sxsde.getMessage());
5915                            }
5916                    }
5917            }
5918    
5919            protected void checkStructureField(Element el, Document contentDoc)
5920                    throws PortalException {
5921    
5922                    StringBuilder elPath = new StringBuilder();
5923    
5924                    elPath.append(el.attributeValue("name"));
5925    
5926                    Element elParent = el.getParent();
5927    
5928                    while (true) {
5929                            if ((elParent == null) || elParent.getName().equals("root")) {
5930                                    break;
5931                            }
5932    
5933                            elPath.insert(
5934                                    0, elParent.attributeValue("name") + StringPool.COMMA);
5935    
5936                            elParent = elParent.getParent();
5937                    }
5938    
5939                    String[] elPathNames = StringUtil.split(elPath.toString());
5940    
5941                    Element contentEl = contentDoc.getRootElement();
5942    
5943                    for (String _elPathName : elPathNames) {
5944                            boolean foundEl = false;
5945    
5946                            for (Element tempEl : contentEl.elements()) {
5947                                    if (_elPathName.equals(
5948                                                    tempEl.attributeValue("name", StringPool.BLANK))) {
5949    
5950                                            contentEl = tempEl;
5951                                            foundEl = true;
5952    
5953                                            break;
5954                                    }
5955                            }
5956    
5957                            if (!foundEl) {
5958                                    String elType = contentEl.attributeValue(
5959                                            "type", StringPool.BLANK);
5960    
5961                                    if (!elType.equals("list") && !elType.equals("multi-list")) {
5962                                            throw new StructureXsdException(elPath.toString());
5963                                    }
5964    
5965                                    break;
5966                            }
5967                    }
5968            }
5969    
5970            protected void copyArticleImages(
5971                            JournalArticle oldArticle, JournalArticle newArticle)
5972                    throws Exception {
5973    
5974                    Document contentDoc = SAXReaderUtil.read(oldArticle.getContent());
5975    
5976                    XPath xPathSelector = SAXReaderUtil.createXPath(
5977                            "//dynamic-element[@type='image']");
5978    
5979                    List<Node> imageNodes = xPathSelector.selectNodes(contentDoc);
5980    
5981                    for (Node imageNode : imageNodes) {
5982                            Element imageEl = (Element)imageNode;
5983    
5984                            String elInstanceId = imageEl.attributeValue("instance-id");
5985                            String elName = imageEl.attributeValue("name");
5986                            String elIndex = imageEl.attributeValue("index");
5987    
5988                            String name = elName + StringPool.UNDERLINE + elIndex;
5989    
5990                            List<Element> dynamicContentEls = imageEl.elements(
5991                                    "dynamic-content");
5992    
5993                            for (Element dynamicContentEl : dynamicContentEls) {
5994                                    long imageId = GetterUtil.getLong(
5995                                            dynamicContentEl.attributeValue("id"));
5996                                    String languageId =
5997                                            StringPool.UNDERLINE +
5998                                                    dynamicContentEl.attributeValue("language-id");
5999    
6000                                    Image oldImage = imageLocalService.fetchImage(imageId);
6001    
6002                                    if (oldImage == null) {
6003                                            continue;
6004                                    }
6005    
6006                                    imageId = journalArticleImageLocalService.getArticleImageId(
6007                                            newArticle.getGroupId(), newArticle.getArticleId(),
6008                                            newArticle.getVersion(), elInstanceId, name, languageId);
6009    
6010                                    imageLocalService.updateImage(imageId, oldImage.getTextObj());
6011    
6012                                    String elContent =
6013                                            "/image/journal/article?img_id=" + imageId + "&t=" +
6014                                                    WebServerServletTokenUtil.getToken(imageId);
6015    
6016                                    dynamicContentEl.setText(elContent);
6017                                    dynamicContentEl.addAttribute("id", String.valueOf(imageId));
6018                            }
6019                    }
6020    
6021                    newArticle.setContent(contentDoc.formattedString());
6022            }
6023    
6024            protected void format(
6025                            User user, long groupId, String articleId, double version,
6026                            boolean incrementVersion, Element root, Map<String, byte[]> images)
6027                    throws PortalException, SystemException {
6028    
6029                    for (Element element : root.elements()) {
6030                            String elInstanceId = element.attributeValue(
6031                                    "instance-id", StringPool.BLANK);
6032                            String elType = element.attributeValue("type", StringPool.BLANK);
6033    
6034                            if (elType.equals("image")) {
6035                                    String elName = element.attributeValue(
6036                                            "name", StringPool.BLANK);
6037                                    String elIndex = element.attributeValue(
6038                                            "index", StringPool.BLANK);
6039    
6040                                    String name = elName + StringPool.UNDERLINE + elIndex;
6041    
6042                                    formatImage(
6043                                            groupId, articleId, version, incrementVersion, element,
6044                                            elInstanceId, name, images);
6045                            }
6046                            else if (elType.equals("text_area") || elType.equals("text") ||
6047                                             elType.equals("text_box")) {
6048    
6049                                    List<Element> dynamicContentElements = element.elements(
6050                                            "dynamic-content");
6051    
6052                                    for (Element dynamicContentElement : dynamicContentElements) {
6053                                            String dynamicContent = dynamicContentElement.getText();
6054    
6055                                            if (Validator.isNotNull(dynamicContent)) {
6056                                                    String contentType = ContentTypes.TEXT_PLAIN;
6057    
6058                                                    if (elType.equals("text_area")) {
6059                                                            contentType = ContentTypes.TEXT_HTML;
6060                                                    }
6061    
6062                                                    dynamicContent = SanitizerUtil.sanitize(
6063                                                            user.getCompanyId(), groupId, user.getUserId(),
6064                                                            JournalArticle.class.getName(), 0, contentType,
6065                                                            dynamicContent);
6066    
6067                                                    dynamicContentElement.clearContent();
6068    
6069                                                    dynamicContentElement.addCDATA(dynamicContent);
6070                                            }
6071                                    }
6072                            }
6073    
6074                            format(
6075                                    user, groupId, articleId, version, incrementVersion, element,
6076                                    images);
6077                    }
6078            }
6079    
6080            protected String format(
6081                            User user, long groupId, String articleId, double version,
6082                            boolean incrementVersion, String content, String ddmStructureKey,
6083                            Map<String, byte[]> images)
6084                    throws PortalException, SystemException {
6085    
6086                    Document document = null;
6087    
6088                    try {
6089                            document = SAXReaderUtil.read(content);
6090    
6091                            Element rootElement = document.getRootElement();
6092    
6093                            if (Validator.isNotNull(ddmStructureKey)) {
6094                                    format(
6095                                            user, groupId, articleId, version, incrementVersion,
6096                                            rootElement, images);
6097                            }
6098                            else {
6099                                    List<Element> staticContentElements = rootElement.elements(
6100                                            "static-content");
6101    
6102                                    for (Element staticContentElement : staticContentElements) {
6103                                            String staticContent = staticContentElement.getText();
6104    
6105                                            staticContent = SanitizerUtil.sanitize(
6106                                                    user.getCompanyId(), groupId, user.getUserId(),
6107                                                    JournalArticle.class.getName(), 0,
6108                                                    ContentTypes.TEXT_HTML, staticContent);
6109    
6110                                            staticContentElement.clearContent();
6111    
6112                                            staticContentElement.addCDATA(staticContent);
6113                                    }
6114                            }
6115    
6116                            content = DDMXMLUtil.formatXML(document);
6117                    }
6118                    catch (DocumentException de) {
6119                            _log.error(de, de);
6120                    }
6121    
6122                    return content;
6123            }
6124    
6125            protected void formatImage(
6126                            long groupId, String articleId, double version,
6127                            boolean incrementVersion, Element el, String elInstanceId,
6128                            String elName, Map<String, byte[]> images)
6129                    throws PortalException, SystemException {
6130    
6131                    List<Element> imageContents = el.elements("dynamic-content");
6132    
6133                    for (Element dynamicContent : imageContents) {
6134                            String elLanguage = dynamicContent.attributeValue(
6135                                    "language-id", StringPool.BLANK);
6136    
6137                            if (!elLanguage.equals(StringPool.BLANK)) {
6138                                    elLanguage = StringPool.UNDERLINE + elLanguage;
6139                            }
6140    
6141                            long imageId = journalArticleImageLocalService.getArticleImageId(
6142                                    groupId, articleId, version, elInstanceId, elName, elLanguage);
6143    
6144                            if (dynamicContent.getText().equals("delete") ||
6145                                    Validator.isNull(dynamicContent.getText())) {
6146    
6147                                    dynamicContent.setText(StringPool.BLANK);
6148    
6149                                    imageLocalService.deleteImage(imageId);
6150    
6151                                    String defaultElLanguage = StringPool.BLANK;
6152    
6153                                    if (Validator.isNull(elLanguage)) {
6154                                            defaultElLanguage =
6155                                                    StringPool.UNDERLINE +
6156                                                            LocaleUtil.toLanguageId(
6157                                                                    LocaleUtil.getSiteDefault());
6158                                    }
6159    
6160                                    long defaultImageId =
6161                                            journalArticleImageLocalService.getArticleImageId(
6162                                                    groupId, articleId, version, elInstanceId, elName,
6163                                                    defaultElLanguage);
6164    
6165                                    imageLocalService.deleteImage(defaultImageId);
6166    
6167                                    continue;
6168                            }
6169    
6170                            String elContent =
6171                                    "/image/journal/article?img_id=" + imageId + "&t=" +
6172                                            WebServerServletTokenUtil.getToken(imageId);
6173    
6174                            byte[] bytes = images.get(
6175                                    elInstanceId + StringPool.UNDERLINE + elName + elLanguage);
6176    
6177                            String defaultElLanguage =
6178                                    StringPool.UNDERLINE +
6179                                            LocaleUtil.toLanguageId(LocaleUtil.getSiteDefault());
6180    
6181                            if (ArrayUtil.isEmpty(bytes) &&
6182                                    !defaultElLanguage.equals(elLanguage)) {
6183    
6184                                    bytes = images.get(
6185                                            elInstanceId + StringPool.UNDERLINE + elName +
6186                                                    defaultElLanguage);
6187                            }
6188    
6189                            if (ArrayUtil.isNotEmpty(bytes)) {
6190                                    dynamicContent.setText(elContent);
6191                                    dynamicContent.addAttribute("id", String.valueOf(imageId));
6192    
6193                                    imageLocalService.updateImage(imageId, bytes);
6194    
6195                                    continue;
6196                            }
6197    
6198                            if ((version > JournalArticleConstants.VERSION_DEFAULT) &&
6199                                    incrementVersion) {
6200    
6201                                    double oldVersion = MathUtil.format(version - 0.1, 1, 1);
6202    
6203                                    long oldImageId = 0;
6204    
6205                                    if ((oldVersion >= 1) && incrementVersion) {
6206                                            oldImageId =
6207                                                    journalArticleImageLocalService.getArticleImageId(
6208                                                            groupId, articleId, oldVersion, elInstanceId,
6209                                                            elName, elLanguage);
6210                                    }
6211    
6212                                    Image oldImage = null;
6213    
6214                                    if (oldImageId > 0) {
6215                                            oldImage = imageLocalService.getImage(oldImageId);
6216                                    }
6217    
6218                                    if (oldImage != null) {
6219                                            dynamicContent.setText(elContent);
6220                                            dynamicContent.addAttribute("id", String.valueOf(imageId));
6221    
6222                                            bytes = oldImage.getTextObj();
6223    
6224                                            imageLocalService.updateImage(imageId, bytes);
6225                                    }
6226                                    else if (dynamicContent.getText().equals("update")) {
6227                                            dynamicContent.setText(StringPool.BLANK);
6228                                    }
6229    
6230                                    continue;
6231                            }
6232    
6233                            Image image = imageLocalService.getImage(imageId);
6234    
6235                            if (image != null) {
6236                                    dynamicContent.setText(elContent);
6237                                    dynamicContent.addAttribute("id", String.valueOf(imageId));
6238    
6239                                    continue;
6240                            }
6241                            else if (dynamicContent.getText().equals("update")) {
6242                                    dynamicContent.setText(StringPool.BLANK);
6243    
6244                                    continue;
6245                            }
6246    
6247                            long contentImageId = GetterUtil.getLong(
6248                                    HttpUtil.getParameter(dynamicContent.getText(), "img_id"));
6249    
6250                            if (contentImageId <= 0) {
6251                                    contentImageId = GetterUtil.getLong(
6252                                            HttpUtil.getParameter(
6253                                                    dynamicContent.getText(), "img_id", false));
6254                            }
6255    
6256                            if (contentImageId > 0) {
6257                                    image = imageLocalService.getImage(contentImageId);
6258    
6259                                    if (image != null) {
6260                                            dynamicContent.addAttribute(
6261                                                    "id", String.valueOf(contentImageId));
6262    
6263                                            continue;
6264                                    }
6265                            }
6266    
6267                            defaultElLanguage = StringPool.BLANK;
6268    
6269                            if (Validator.isNull(elLanguage)) {
6270                                    defaultElLanguage =
6271                                            StringPool.UNDERLINE +
6272                                                    LocaleUtil.toLanguageId(LocaleUtil.getSiteDefault());
6273                            }
6274    
6275                            long defaultImageId =
6276                                    journalArticleImageLocalService.getArticleImageId(
6277                                            groupId, articleId, version, elInstanceId, elName,
6278                                            defaultElLanguage);
6279    
6280                            Image defaultImage = imageLocalService.getImage(defaultImageId);
6281    
6282                            if (defaultImage != null) {
6283                                    dynamicContent.setText(elContent);
6284                                    dynamicContent.addAttribute(
6285                                            "id", String.valueOf(defaultImageId));
6286    
6287                                    bytes = defaultImage.getTextObj();
6288    
6289                                    imageLocalService.updateImage(defaultImageId, bytes);
6290    
6291                                    continue;
6292                            }
6293    
6294                            if (Validator.isNotNull(elLanguage)) {
6295                                    dynamicContent.setText(StringPool.BLANK);
6296                            }
6297                    }
6298            }
6299    
6300            protected Locale getArticleDefaultLocale(
6301                    String content, ServiceContext serviceContext) {
6302    
6303                    String defaultLanguageId = ParamUtil.getString(
6304                            serviceContext, "defaultLanguageId");
6305    
6306                    if (Validator.isNull(defaultLanguageId)) {
6307                            defaultLanguageId = LocalizationUtil.getDefaultLanguageId(content);
6308                    }
6309    
6310                    if (Validator.isNotNull(defaultLanguageId)) {
6311                            return LocaleUtil.fromLanguageId(defaultLanguageId);
6312                    }
6313    
6314                    return LocaleUtil.getSiteDefault();
6315            }
6316    
6317            protected List<ObjectValuePair<Long, Integer>> getArticleVersionStatuses(
6318                    List<JournalArticle> articles) {
6319    
6320                    List<ObjectValuePair<Long, Integer>> articleVersionStatusOVPs =
6321                            new ArrayList<ObjectValuePair<Long, Integer>>(articles.size());
6322    
6323                    for (JournalArticle article : articles) {
6324                            int status = article.getStatus();
6325    
6326                            if (status == WorkflowConstants.STATUS_PENDING) {
6327                                    status = WorkflowConstants.STATUS_DRAFT;
6328                            }
6329    
6330                            ObjectValuePair<Long, Integer> articleVersionStatusOVP =
6331                                    new ObjectValuePair<Long, Integer>(article.getId(), status);
6332    
6333                            articleVersionStatusOVPs.add(articleVersionStatusOVP);
6334                    }
6335    
6336                    return articleVersionStatusOVPs;
6337            }
6338    
6339            protected long getClassTypeId(JournalArticle article) {
6340                    long classTypeId = 0;
6341    
6342                    try {
6343                            long classNameId = PortalUtil.getClassNameId(JournalArticle.class);
6344    
6345                            DDMStructure ddmStructure = ddmStructurePersistence.fetchByG_C_S(
6346                                    article.getGroupId(), classNameId, article.getStructureId());
6347    
6348                            if (ddmStructure == null) {
6349                                    Group companyGroup = groupLocalService.getCompanyGroup(
6350                                            article.getCompanyId());
6351    
6352                                    ddmStructure = ddmStructurePersistence.fetchByG_C_S(
6353                                            companyGroup.getGroupId(), classNameId,
6354                                            article.getStructureId());
6355                            }
6356    
6357                            if (ddmStructure != null) {
6358                                    classTypeId = ddmStructure.getStructureId();
6359                            }
6360                    }
6361                    catch (Exception e) {
6362                            _log.error(e, e);
6363                    }
6364    
6365                    return classTypeId;
6366            }
6367    
6368            protected JournalArticle getFirstArticle(
6369                            long groupId, String articleId, int status,
6370                            OrderByComparator orderByComparator)
6371                    throws PortalException, SystemException {
6372    
6373                    if (status == WorkflowConstants.STATUS_ANY) {
6374                            return journalArticlePersistence.findByG_A_NotST_First(
6375                                    groupId, articleId, WorkflowConstants.STATUS_IN_TRASH,
6376                                    orderByComparator);
6377                    }
6378                    else {
6379                            return journalArticlePersistence.findByG_A_ST_First(
6380                                    groupId, articleId, status, orderByComparator);
6381                    }
6382            }
6383    
6384            protected String getUniqueUrlTitle(
6385                            long id, long groupId, String articleId, String title)
6386                    throws PortalException, SystemException {
6387    
6388                    String urlTitle = JournalUtil.getUrlTitle(id, title);
6389    
6390                    return getUniqueUrlTitle(groupId, articleId, urlTitle);
6391            }
6392    
6393            protected String getUniqueUrlTitle(
6394                            long id, String articleId, String title, String oldUrlTitle,
6395                            ServiceContext serviceContext)
6396                    throws PortalException, SystemException {
6397    
6398                    String serviceContextUrlTitle = ParamUtil.getString(
6399                            serviceContext, "urlTitle");
6400    
6401                    String urlTitle = null;
6402    
6403                    if (Validator.isNotNull(serviceContextUrlTitle)) {
6404                            urlTitle = JournalUtil.getUrlTitle(id, serviceContextUrlTitle);
6405                    }
6406                    else if (Validator.isNotNull(oldUrlTitle)) {
6407                            return oldUrlTitle;
6408                    }
6409                    else {
6410                            urlTitle = getUniqueUrlTitle(
6411                                    id, serviceContext.getScopeGroupId(), articleId, title);
6412                    }
6413    
6414                    JournalArticle urlTitleArticle = fetchArticleByUrlTitle(
6415                            serviceContext.getScopeGroupId(), urlTitle);
6416    
6417                    if ((urlTitleArticle != null) &&
6418                            !Validator.equals(
6419                                    urlTitleArticle.getArticleId(), articleId)) {
6420    
6421                            urlTitle = getUniqueUrlTitle(
6422                                    id, serviceContext.getScopeGroupId(), articleId, urlTitle);
6423                    }
6424    
6425                    return urlTitle;
6426            }
6427    
6428            protected boolean hasModifiedLatestApprovedVersion(
6429                            long groupId, String articleId, double version)
6430                    throws SystemException {
6431    
6432                    JournalArticle article = fetchLatestArticle(
6433                            groupId, articleId, WorkflowConstants.STATUS_APPROVED);
6434    
6435                    if ((article == null) || (article.getVersion() <= version)) {
6436                            return true;
6437                    }
6438    
6439                    return false;
6440            }
6441    
6442            protected void notifySubscribers(
6443                            JournalArticle article, ServiceContext serviceContext)
6444                    throws PortalException, SystemException {
6445    
6446                    if (!article.isApproved()) {
6447                            return;
6448                    }
6449    
6450                    String articleURL = PortalUtil.getControlPanelFullURL(
6451                            serviceContext.getScopeGroupId(), PortletKeys.JOURNAL, null);
6452    
6453                    if (Validator.isNull(articleURL)) {
6454                            return;
6455                    }
6456    
6457                    articleURL = buildArticleURL(
6458                            articleURL, article.getGroupId(), article.getFolderId(),
6459                            article.getArticleId());
6460    
6461                    PortletPreferences preferences =
6462                            ServiceContextUtil.getPortletPreferences(serviceContext);
6463    
6464                    if (preferences == null) {
6465                            long ownerId = article.getGroupId();
6466                            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
6467                            long plid = PortletKeys.PREFS_PLID_SHARED;
6468                            String portletId = PortletKeys.JOURNAL;
6469                            String defaultPreferences = null;
6470    
6471                            preferences = portletPreferencesLocalService.getPreferences(
6472                                    article.getCompanyId(), ownerId, ownerType, plid, portletId,
6473                                    defaultPreferences);
6474                    }
6475    
6476                    if ((article.getVersion() == 1.0) &&
6477                            JournalUtil.getEmailArticleAddedEnabled(preferences)) {
6478                    }
6479                    else if ((article.getVersion() != 1.0) &&
6480                                     JournalUtil.getEmailArticleUpdatedEnabled(preferences)) {
6481                    }
6482                    else {
6483                            return;
6484                    }
6485    
6486                    String fromName = JournalUtil.getEmailFromName(
6487                            preferences, article.getCompanyId());
6488                    String fromAddress = JournalUtil.getEmailFromAddress(
6489                            preferences, article.getCompanyId());
6490    
6491                    String subject = null;
6492                    String body = null;
6493    
6494                    if (article.getVersion() == 1.0) {
6495                            subject = JournalUtil.getEmailArticleAddedSubject(preferences);
6496                            body = JournalUtil.getEmailArticleAddedBody(preferences);
6497                    }
6498                    else {
6499                            subject = JournalUtil.getEmailArticleUpdatedSubject(preferences);
6500                            body = JournalUtil.getEmailArticleUpdatedBody(preferences);
6501                    }
6502    
6503                    SubscriptionSender subscriptionSender = new SubscriptionSender();
6504    
6505                    subscriptionSender.setBody(body);
6506                    subscriptionSender.setCompanyId(article.getCompanyId());
6507                    subscriptionSender.setContextAttributes(
6508                            "[$ARTICLE_ID$]", article.getArticleId(), "[$ARTICLE_TITLE$]",
6509                            article.getTitle(serviceContext.getLanguageId()), "[$ARTICLE_URL$]",
6510                            articleURL, "[$ARTICLE_VERSION$]", article.getVersion());
6511                    subscriptionSender.setContextUserPrefix("ARTICLE");
6512                    subscriptionSender.setFrom(fromAddress, fromName);
6513                    subscriptionSender.setHtmlFormat(true);
6514                    subscriptionSender.setMailId("journal_article", article.getId());
6515                    subscriptionSender.setPortletId(PortletKeys.JOURNAL);
6516                    subscriptionSender.setReplyToAddress(fromAddress);
6517                    subscriptionSender.setScopeGroupId(article.getGroupId());
6518                    subscriptionSender.setServiceContext(serviceContext);
6519                    subscriptionSender.setSubject(subject);
6520                    subscriptionSender.setUserId(article.getUserId());
6521    
6522                    subscriptionSender.addPersistedSubscribers(
6523                            JournalArticle.class.getName(), article.getResourcePrimKey());
6524    
6525                    JournalFolder folder = article.getFolder();
6526    
6527                    List<Long> folderIds = new ArrayList<Long>();
6528    
6529                    if (folder != null) {
6530                            folderIds.add(folder.getFolderId());
6531    
6532                            folderIds.addAll(folder.getAncestorFolderIds());
6533                    }
6534    
6535                    for (long curFolderId : folderIds) {
6536                            subscriptionSender.addPersistedSubscribers(
6537                                    JournalFolder.class.getName(), curFolderId);
6538                    }
6539    
6540                    subscriptionSender.addPersistedSubscribers(
6541                            JournalArticle.class.getName(), article.getGroupId());
6542    
6543                    subscriptionSender.flushNotificationsAsync();
6544            }
6545    
6546            protected void saveImages(
6547                            boolean smallImage, long smallImageId, File smallImageFile,
6548                            byte[] smallImageBytes)
6549                    throws PortalException, SystemException {
6550    
6551                    if (smallImage) {
6552                            if ((smallImageFile != null) && (smallImageBytes != null)) {
6553                                    imageLocalService.updateImage(smallImageId, smallImageBytes);
6554                            }
6555                    }
6556                    else {
6557                            imageLocalService.deleteImage(smallImageId);
6558                    }
6559            }
6560    
6561            protected void sendEmail(
6562                            JournalArticle article, String articleURL,
6563                            PortletPreferences preferences, String emailType,
6564                            ServiceContext serviceContext)
6565                    throws PortalException, SystemException {
6566    
6567                    if (preferences == null) {
6568                            return;
6569                    }
6570                    else if (emailType.equals("denied") &&
6571                                     JournalUtil.getEmailArticleApprovalDeniedEnabled(
6572                                             preferences)) {
6573                    }
6574                    else if (emailType.equals("granted") &&
6575                                     JournalUtil.getEmailArticleApprovalGrantedEnabled(
6576                                             preferences)) {
6577                    }
6578                    else if (emailType.equals("requested") &&
6579                                     JournalUtil.getEmailArticleApprovalRequestedEnabled(
6580                                             preferences)) {
6581                    }
6582                    else if (emailType.equals("review") &&
6583                                     JournalUtil.getEmailArticleReviewEnabled(preferences)) {
6584                    }
6585                    else {
6586                            return;
6587                    }
6588    
6589                    Company company = companyPersistence.findByPrimaryKey(
6590                            article.getCompanyId());
6591    
6592                    User user = userPersistence.findByPrimaryKey(article.getUserId());
6593    
6594                    String fromName = JournalUtil.getEmailFromName(
6595                            preferences, article.getCompanyId());
6596                    String fromAddress = JournalUtil.getEmailFromAddress(
6597                            preferences, article.getCompanyId());
6598    
6599                    String toName = user.getFullName();
6600                    String toAddress = user.getEmailAddress();
6601    
6602                    if (emailType.equals("requested")) {
6603                            String tempToName = fromName;
6604                            String tempToAddress = fromAddress;
6605    
6606                            fromName = toName;
6607                            fromAddress = toAddress;
6608    
6609                            toName = tempToName;
6610                            toAddress = tempToAddress;
6611                    }
6612    
6613                    String subject = null;
6614                    String body = null;
6615    
6616                    if (emailType.equals("denied")) {
6617                            subject = JournalUtil.getEmailArticleApprovalDeniedSubject(
6618                                    preferences);
6619                            body = JournalUtil.getEmailArticleApprovalDeniedBody(preferences);
6620                    }
6621                    else if (emailType.equals("granted")) {
6622                            subject = JournalUtil.getEmailArticleApprovalGrantedSubject(
6623                                    preferences);
6624                            body = JournalUtil.getEmailArticleApprovalGrantedBody(preferences);
6625                    }
6626                    else if (emailType.equals("requested")) {
6627                            subject = JournalUtil.getEmailArticleApprovalRequestedSubject(
6628                                    preferences);
6629                            body = JournalUtil.getEmailArticleApprovalRequestedBody(
6630                                    preferences);
6631                    }
6632                    else if (emailType.equals("review")) {
6633                            subject = JournalUtil.getEmailArticleReviewSubject(preferences);
6634                            body = JournalUtil.getEmailArticleReviewBody(preferences);
6635                    }
6636    
6637                    SubscriptionSender subscriptionSender = new SubscriptionSender();
6638    
6639                    subscriptionSender.setBody(body);
6640                    subscriptionSender.setCompanyId(company.getCompanyId());
6641                    subscriptionSender.setContextAttributes(
6642                            "[$ARTICLE_ID$]", article.getArticleId(), "[$ARTICLE_TITLE$]",
6643                            article.getTitle(serviceContext.getLanguageId()), "[$ARTICLE_URL$]",
6644                            articleURL, "[$ARTICLE_USER_NAME$]", article.getUserName(),
6645                            "[$ARTICLE_VERSION$]", article.getVersion());
6646                    subscriptionSender.setContextUserPrefix("ARTICLE");
6647                    subscriptionSender.setFrom(fromAddress, fromName);
6648                    subscriptionSender.setHtmlFormat(true);
6649                    subscriptionSender.setMailId("journal_article", article.getId());
6650                    subscriptionSender.setPortletId(PortletKeys.JOURNAL);
6651                    subscriptionSender.setScopeGroupId(article.getGroupId());
6652                    subscriptionSender.setServiceContext(serviceContext);
6653                    subscriptionSender.setSubject(subject);
6654                    subscriptionSender.setUserId(article.getUserId());
6655    
6656                    subscriptionSender.addRuntimeSubscribers(toAddress, toName);
6657    
6658                    subscriptionSender.flushNotificationsAsync();
6659            }
6660    
6661            protected void updateDDMStructureXSD(
6662                            long ddmStructureId, String content, ServiceContext serviceContext)
6663                    throws PortalException, SystemException {
6664    
6665                    try {
6666                            DDMStructure ddmStructure =
6667                                    ddmStructureLocalService.fetchDDMStructure(ddmStructureId);
6668    
6669                            if (ddmStructure == null) {
6670                                    return;
6671                            }
6672    
6673                            Document documentXSD = SAXReaderUtil.read(ddmStructure.getXsd());
6674    
6675                            Document document = SAXReaderUtil.read(content);
6676    
6677                            Element rootElement = document.getRootElement();
6678    
6679                            List<Element> elements = rootElement.elements();
6680    
6681                            for (Element element : elements) {
6682                                    String fieldName = element.attributeValue(
6683                                            "name", StringPool.BLANK);
6684    
6685                                    List<Element> dynamicContentElements = element.elements(
6686                                            "dynamic-content");
6687    
6688                                    for (Element dynamicContentElement : dynamicContentElements) {
6689                                            String value = dynamicContentElement.getText();
6690    
6691                                            documentXSD = updateDDMStructureXSDFieldMetadata(
6692                                                    documentXSD, fieldName, FieldConstants.PREDEFINED_VALUE,
6693                                                    value);
6694                                    }
6695                            }
6696    
6697                            ddmStructureLocalService.updateXSD(
6698                                    ddmStructureId, documentXSD.asXML(), serviceContext);
6699                    }
6700                    catch (DocumentException de) {
6701                            throw new SystemException(de);
6702                    }
6703            }
6704    
6705            protected Document updateDDMStructureXSDFieldMetadata(
6706                            Document document, String fieldName, String metadataEntryName,
6707                            String metadataEntryValue)
6708                    throws DocumentException {
6709    
6710                    Element rootElement = document.getRootElement();
6711    
6712                    List<Element> dynamicElementElements = rootElement.elements(
6713                            "dynamic-element");
6714    
6715                    for (Element dynamicElementElement : dynamicElementElements) {
6716                            String dynamicElementElementFieldName = GetterUtil.getString(
6717                                    dynamicElementElement.attributeValue("name"));
6718    
6719                            if (!dynamicElementElementFieldName.equals(fieldName)) {
6720                                    continue;
6721                            }
6722    
6723                            List<Element> metadataElements = dynamicElementElement.elements(
6724                                    "meta-data");
6725    
6726                            for (Element metadataElement : metadataElements) {
6727                                    List<Element> metadataEntryElements =
6728                                            metadataElement.elements();
6729    
6730                                    for (Element metadataEntryElement : metadataEntryElements) {
6731                                            String metadataEntryElementName = GetterUtil.getString(
6732                                                    metadataEntryElement.attributeValue("name"));
6733    
6734                                            if (metadataEntryElementName.equals(metadataEntryName)) {
6735                                                    metadataEntryElement.setText(metadataEntryValue);
6736                                            }
6737                                    }
6738                            }
6739                    }
6740    
6741                    return document;
6742            }
6743    
6744            protected void updatePreviousApprovedArticle(JournalArticle article)
6745                    throws PortalException, SystemException {
6746    
6747                    List<JournalArticle> approvedArticles =
6748                            journalArticlePersistence.findByG_A_ST(
6749                                    article.getGroupId(), article.getArticleId(),
6750                                    WorkflowConstants.STATUS_APPROVED, 0, 2);
6751    
6752                    if (approvedArticles.isEmpty() ||
6753                            ((approvedArticles.size() == 1) &&
6754                             (article.getStatus() == WorkflowConstants.STATUS_APPROVED))) {
6755    
6756                            assetEntryLocalService.updateVisible(
6757                                    JournalArticle.class.getName(), article.getResourcePrimKey(),
6758                                    false);
6759                    }
6760                    else {
6761                            JournalArticle previousApprovedArticle = approvedArticles.get(0);
6762    
6763                            if (article.getStatus() == WorkflowConstants.STATUS_APPROVED) {
6764                                    previousApprovedArticle = approvedArticles.get(1);
6765                            }
6766    
6767                            AssetEntry assetEntry = assetEntryLocalService.updateEntry(
6768                                    JournalArticle.class.getName(), article.getResourcePrimKey(),
6769                                    previousApprovedArticle.getDisplayDate(),
6770                                    previousApprovedArticle.getExpirationDate(), true);
6771    
6772                            assetEntry.setModifiedDate(
6773                                    previousApprovedArticle.getModifiedDate());
6774                            assetEntry.setTitle(previousApprovedArticle.getTitle());
6775    
6776                            assetEntryPersistence.update(assetEntry);
6777                    }
6778            }
6779    
6780            protected void updateUrlTitles(
6781                            long groupId, String articleId, String urlTitle)
6782                    throws PortalException, SystemException {
6783    
6784                    JournalArticle firstArticle = journalArticlePersistence.findByG_A_First(
6785                            groupId, articleId, new ArticleVersionComparator(false));
6786    
6787                    if (firstArticle.getUrlTitle().equals(urlTitle)) {
6788                            return;
6789                    }
6790    
6791                    List<JournalArticle> articles = journalArticlePersistence.findByG_A(
6792                            groupId, articleId);
6793    
6794                    for (JournalArticle article : articles) {
6795                            if (!article.getUrlTitle().equals(urlTitle)) {
6796                                    article.setUrlTitle(urlTitle);
6797    
6798                                    journalArticlePersistence.update(article);
6799                            }
6800                    }
6801            }
6802    
6803            protected void validate(
6804                            long companyId, long groupId, long classNameId,
6805                            Map<Locale, String> titleMap, String content, String type,
6806                            String ddmStructureKey, String ddmTemplateKey, Date expirationDate,
6807                            boolean smallImage, String smallImageURL, File smallImageFile,
6808                            byte[] smallImageBytes, ServiceContext serviceContext)
6809                    throws PortalException, SystemException {
6810    
6811                    Locale articleDefaultLocale = LocaleUtil.fromLanguageId(
6812                            LocalizationUtil.getDefaultLanguageId(content));
6813    
6814                    Locale[] availableLocales = LanguageUtil.getAvailableLocales(groupId);
6815    
6816                    if (!ArrayUtil.contains(availableLocales, articleDefaultLocale)) {
6817                            LocaleException le = new LocaleException(
6818                                    LocaleException.TYPE_CONTENT,
6819                                    "The locale " + articleDefaultLocale +
6820                                            " is not available in site with groupId" + groupId);
6821    
6822                            Locale[] sourceAvailableLocales = {articleDefaultLocale};
6823    
6824                            le.setSourceAvailableLocales(sourceAvailableLocales);
6825                            le.setTargetAvailableLocales(availableLocales);
6826    
6827                            throw le;
6828                    }
6829    
6830                    if ((classNameId == JournalArticleConstants.CLASSNAME_ID_DEFAULT) &&
6831                            (titleMap.isEmpty() ||
6832                             Validator.isNull(titleMap.get(articleDefaultLocale)))) {
6833    
6834                            throw new ArticleTitleException();
6835                    }
6836                    else if (Validator.isNull(type)) {
6837                            throw new ArticleTypeException();
6838                    }
6839    
6840                    validateContent(content);
6841    
6842                    if (Validator.isNotNull(ddmStructureKey)) {
6843                            DDMStructure ddmStructure = ddmStructureLocalService.getStructure(
6844                                    PortalUtil.getSiteGroupId(groupId),
6845                                    PortalUtil.getClassNameId(JournalArticle.class),
6846                                    ddmStructureKey, true);
6847    
6848                            validateDDMStructureFields(ddmStructure, classNameId, content);
6849    
6850                            if (Validator.isNotNull(ddmTemplateKey)) {
6851                                    DDMTemplate ddmTemplate = ddmTemplateLocalService.getTemplate(
6852                                            PortalUtil.getSiteGroupId(groupId),
6853                                            PortalUtil.getClassNameId(DDMStructure.class),
6854                                            ddmTemplateKey, true);
6855    
6856                                    if (ddmTemplate.getClassPK() != ddmStructure.getStructureId()) {
6857                                            throw new NoSuchTemplateException(
6858                                                    "{templateKey=" + ddmTemplateKey + "}");
6859                                    }
6860                            }
6861                            else if (classNameId ==
6862                                                    JournalArticleConstants.CLASSNAME_ID_DEFAULT) {
6863    
6864                                    throw new NoSuchTemplateException();
6865                            }
6866                    }
6867    
6868                    if ((expirationDate != null) && expirationDate.before(new Date()) &&
6869                            !ExportImportThreadLocal.isImportInProcess()) {
6870    
6871                            throw new ArticleExpirationDateException();
6872                    }
6873    
6874                    String[] imageExtensions = PrefsPropsUtil.getStringArray(
6875                            PropsKeys.JOURNAL_IMAGE_EXTENSIONS, StringPool.COMMA);
6876    
6877                    if (!smallImage || Validator.isNotNull(smallImageURL) ||
6878                            (smallImageFile == null) || (smallImageBytes == null)) {
6879    
6880                            return;
6881                    }
6882    
6883                    String smallImageName = smallImageFile.getName();
6884    
6885                    if (smallImageName != null) {
6886                            boolean validSmallImageExtension = false;
6887    
6888                            for (String _imageExtension : imageExtensions) {
6889                                    if (StringPool.STAR.equals(_imageExtension) ||
6890                                            StringUtil.endsWith(smallImageName, _imageExtension)) {
6891    
6892                                            validSmallImageExtension = true;
6893    
6894                                            break;
6895                                    }
6896                            }
6897    
6898                            if (!validSmallImageExtension) {
6899                                    throw new ArticleSmallImageNameException(smallImageName);
6900                            }
6901                    }
6902    
6903                    long smallImageMaxSize = PrefsPropsUtil.getLong(
6904                            PropsKeys.JOURNAL_IMAGE_SMALL_MAX_SIZE);
6905    
6906                    if ((smallImageMaxSize > 0) &&
6907                            ((smallImageBytes == null) ||
6908                             (smallImageBytes.length > smallImageMaxSize))) {
6909    
6910                            throw new ArticleSmallImageSizeException();
6911                    }
6912            }
6913    
6914            protected void validate(
6915                            long companyId, long groupId, long classNameId, String articleId,
6916                            boolean autoArticleId, double version, Map<Locale, String> titleMap,
6917                            String content, String type, String ddmStructureKey,
6918                            String ddmTemplateKey, Date expirationDate, boolean smallImage,
6919                            String smallImageURL, File smallImageFile, byte[] smallImageBytes,
6920                            ServiceContext serviceContext)
6921                    throws PortalException, SystemException {
6922    
6923                    if (!autoArticleId) {
6924                            validate(articleId);
6925                    }
6926    
6927                    JournalArticle article = journalArticlePersistence.fetchByG_A_V(
6928                            groupId, articleId, version);
6929    
6930                    if (article != null) {
6931                            StringBundler sb = new StringBundler(7);
6932    
6933                            sb.append("{groupId=");
6934                            sb.append(groupId);
6935                            sb.append(", articleId=");
6936                            sb.append(articleId);
6937                            sb.append(", version=");
6938                            sb.append(version);
6939                            sb.append("}");
6940    
6941                            throw new DuplicateArticleIdException(sb.toString());
6942                    }
6943    
6944                    validate(
6945                            companyId, groupId, classNameId, titleMap, content, type,
6946                            ddmStructureKey, ddmTemplateKey, expirationDate, smallImage,
6947                            smallImageURL, smallImageFile, smallImageBytes, serviceContext);
6948            }
6949    
6950            protected void validate(String articleId) throws PortalException {
6951                    if (Validator.isNull(articleId) ||
6952                            (articleId.indexOf(CharPool.COMMA) != -1) ||
6953                            (articleId.indexOf(CharPool.SPACE) != -1)) {
6954    
6955                            throw new ArticleIdException();
6956                    }
6957            }
6958    
6959            protected void validateContent(String content) throws PortalException {
6960                    if (Validator.isNull(content)) {
6961                            throw new ArticleContentException("Content is null");
6962                    }
6963    
6964                    try {
6965                            SAXReaderUtil.read(content);
6966                    }
6967                    catch (DocumentException de) {
6968                            if (_log.isDebugEnabled()) {
6969                                    _log.debug("Invalid content:\n" + content);
6970                            }
6971    
6972                            throw new ArticleContentException(
6973                                    "Unable to read content with an XML parser", de);
6974                    }
6975            }
6976    
6977            protected void validateDDMStructureFields(
6978                            DDMStructure ddmStructure, long classNameId, Fields fields)
6979                    throws PortalException, SystemException {
6980    
6981                    for (com.liferay.portlet.dynamicdatamapping.storage.Field field :
6982                                    fields) {
6983    
6984                            if (!ddmStructure.hasField(field.getName())) {
6985                                    throw new StorageFieldNameException();
6986                            }
6987    
6988                            if (ddmStructure.getFieldRequired(field.getName()) &&
6989                                    Validator.isNull(field.getValue()) &&
6990                                    (classNameId == JournalArticleConstants.CLASSNAME_ID_DEFAULT)) {
6991    
6992                                    throw new StorageFieldRequiredException();
6993                            }
6994                    }
6995            }
6996    
6997            protected void validateDDMStructureFields(
6998                            DDMStructure ddmStructure, long classNameId,
6999                            ServiceContext serviceContext)
7000                    throws PortalException, SystemException {
7001    
7002                    Fields fields = DDMUtil.getFields(
7003                            ddmStructure.getStructureId(), serviceContext);
7004    
7005                    validateDDMStructureFields(ddmStructure, classNameId, fields);
7006            }
7007    
7008            protected void validateDDMStructureFields(
7009                            DDMStructure ddmStructure, long classNameId, String content)
7010                    throws PortalException, SystemException {
7011    
7012                    Fields fields = DDMXMLUtil.getFields(ddmStructure, content);
7013    
7014                    validateDDMStructureFields(ddmStructure, classNameId, fields);
7015            }
7016    
7017            private static final long _JOURNAL_ARTICLE_CHECK_INTERVAL =
7018                    PropsValues.JOURNAL_ARTICLE_CHECK_INTERVAL * Time.MINUTE;
7019    
7020            private static Log _log = LogFactoryUtil.getLog(
7021                    JournalArticleLocalServiceImpl.class);
7022    
7023            private Date _previousCheckDate;
7024    
7025    }