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