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