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