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