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