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