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