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