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