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