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