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