1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.journal.service.impl;
16  
17  import com.liferay.portal.NoSuchImageException;
18  import com.liferay.portal.NoSuchUserException;
19  import com.liferay.portal.kernel.exception.PortalException;
20  import com.liferay.portal.kernel.exception.SystemException;
21  import com.liferay.portal.kernel.log.Log;
22  import com.liferay.portal.kernel.log.LogFactoryUtil;
23  import com.liferay.portal.kernel.mail.MailMessage;
24  import com.liferay.portal.kernel.messaging.DestinationNames;
25  import com.liferay.portal.kernel.messaging.Message;
26  import com.liferay.portal.kernel.messaging.MessageBusUtil;
27  import com.liferay.portal.kernel.search.Indexer;
28  import com.liferay.portal.kernel.search.IndexerRegistryUtil;
29  import com.liferay.portal.kernel.servlet.ImageServletTokenUtil;
30  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
31  import com.liferay.portal.kernel.util.ContentTypes;
32  import com.liferay.portal.kernel.util.FileUtil;
33  import com.liferay.portal.kernel.util.GetterUtil;
34  import com.liferay.portal.kernel.util.HtmlUtil;
35  import com.liferay.portal.kernel.util.HttpUtil;
36  import com.liferay.portal.kernel.util.LocaleUtil;
37  import com.liferay.portal.kernel.util.MathUtil;
38  import com.liferay.portal.kernel.util.OrderByComparator;
39  import com.liferay.portal.kernel.util.PropsKeys;
40  import com.liferay.portal.kernel.util.StringPool;
41  import com.liferay.portal.kernel.util.StringUtil;
42  import com.liferay.portal.kernel.util.Time;
43  import com.liferay.portal.kernel.util.Validator;
44  import com.liferay.portal.kernel.workflow.StatusConstants;
45  import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
46  import com.liferay.portal.kernel.xml.Document;
47  import com.liferay.portal.kernel.xml.DocumentException;
48  import com.liferay.portal.kernel.xml.Element;
49  import com.liferay.portal.kernel.xml.Node;
50  import com.liferay.portal.kernel.xml.SAXReaderUtil;
51  import com.liferay.portal.kernel.xml.XPath;
52  import com.liferay.portal.model.Company;
53  import com.liferay.portal.model.Image;
54  import com.liferay.portal.model.PortletPreferencesIds;
55  import com.liferay.portal.model.ResourceConstants;
56  import com.liferay.portal.model.User;
57  import com.liferay.portal.service.ServiceContext;
58  import com.liferay.portal.service.ServiceContextUtil;
59  import com.liferay.portal.servlet.filters.cache.CacheUtil;
60  import com.liferay.portal.theme.ThemeDisplay;
61  import com.liferay.portal.util.PortalUtil;
62  import com.liferay.portal.util.PortletKeys;
63  import com.liferay.portal.util.PrefsPropsUtil;
64  import com.liferay.portal.util.PropsUtil;
65  import com.liferay.portal.util.PropsValues;
66  import com.liferay.portlet.expando.model.ExpandoBridge;
67  import com.liferay.portlet.journal.ArticleContentException;
68  import com.liferay.portlet.journal.ArticleDisplayDateException;
69  import com.liferay.portlet.journal.ArticleExpirationDateException;
70  import com.liferay.portlet.journal.ArticleIdException;
71  import com.liferay.portlet.journal.ArticleReviewDateException;
72  import com.liferay.portlet.journal.ArticleSmallImageNameException;
73  import com.liferay.portlet.journal.ArticleSmallImageSizeException;
74  import com.liferay.portlet.journal.ArticleTitleException;
75  import com.liferay.portlet.journal.ArticleTypeException;
76  import com.liferay.portlet.journal.DuplicateArticleIdException;
77  import com.liferay.portlet.journal.NoSuchArticleException;
78  import com.liferay.portlet.journal.NoSuchArticleResourceException;
79  import com.liferay.portlet.journal.NoSuchTemplateException;
80  import com.liferay.portlet.journal.StructureXsdException;
81  import com.liferay.portlet.journal.model.JournalArticle;
82  import com.liferay.portlet.journal.model.JournalArticleConstants;
83  import com.liferay.portlet.journal.model.JournalArticleDisplay;
84  import com.liferay.portlet.journal.model.JournalStructure;
85  import com.liferay.portlet.journal.model.JournalTemplate;
86  import com.liferay.portlet.journal.model.impl.JournalArticleDisplayImpl;
87  import com.liferay.portlet.journal.service.base.JournalArticleLocalServiceBaseImpl;
88  import com.liferay.portlet.journal.util.JournalUtil;
89  import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
90  import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
91  import com.liferay.portlet.journalcontent.util.JournalContentUtil;
92  import com.liferay.util.LocalizationUtil;
93  
94  import java.io.File;
95  import java.io.IOException;
96  
97  import java.util.Calendar;
98  import java.util.Date;
99  import java.util.HashSet;
100 import java.util.List;
101 import java.util.Map;
102 import java.util.Set;
103 
104 import javax.mail.internet.InternetAddress;
105 
106 import javax.portlet.PortletPreferences;
107 
108 /**
109  * <a href="JournalArticleLocalServiceImpl.java.html"><b><i>View Source</i></b>
110  * </a>
111  *
112  * @author Brian Wing Shun Chan
113  * @author Raymond Augé
114  * @author Bruno Farache
115  */
116 public class JournalArticleLocalServiceImpl
117     extends JournalArticleLocalServiceBaseImpl {
118 
119     private static Log _log = LogFactoryUtil.getLog(
120         JournalArticleLocalServiceImpl.class);
121 
122     private static final String _TOKEN_PAGE_BREAK = PropsUtil.get(
123         PropsKeys.JOURNAL_ARTICLE_TOKEN_PAGE_BREAK);
124 
125     private long _journalArticleCheckInterval =
126         PropsValues.JOURNAL_ARTICLE_CHECK_INTERVAL * Time.MINUTE;
127 
128     public JournalArticle addArticle(
129             long userId, long groupId, String articleId, boolean autoArticleId,
130             double version, String title, String description, String content,
131             String type, String structureId, String templateId,
132             int displayDateMonth, int displayDateDay, int displayDateYear,
133             int displayDateHour, int displayDateMinute, int expirationDateMonth,
134             int expirationDateDay, int expirationDateYear,
135             int expirationDateHour, int expirationDateMinute,
136             boolean neverExpire, int reviewDateMonth, int reviewDateDay,
137             int reviewDateYear, int reviewDateHour, int reviewDateMinute,
138             boolean neverReview, boolean indexable, boolean smallImage,
139             String smallImageURL, File smallFile, Map<String, byte[]> images,
140             String articleURL, ServiceContext serviceContext)
141         throws PortalException, SystemException {
142 
143         return addArticle(
144             null, userId, groupId, articleId, autoArticleId, version, title,
145             description, content, type, structureId, templateId,
146             displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
147             displayDateMinute, expirationDateMonth, expirationDateDay,
148             expirationDateYear, expirationDateHour, expirationDateMinute,
149             neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
150             reviewDateHour, reviewDateMinute, neverReview, indexable,
151             smallImage, smallImageURL, smallFile, images, articleURL,
152             serviceContext);
153     }
154 
155     public JournalArticle addArticle(
156             long userId, long groupId, String articleId, boolean autoArticleId,
157             String title, String description, String content, String type,
158             String structureId, String templateId, int displayDateMonth,
159             int displayDateDay, int displayDateYear, int displayDateHour,
160             int displayDateMinute, int expirationDateMonth,
161             int expirationDateDay, int expirationDateYear,
162             int expirationDateHour, int expirationDateMinute,
163             boolean neverExpire, int reviewDateMonth, int reviewDateDay,
164             int reviewDateYear, int reviewDateHour, int reviewDateMinute,
165             boolean neverReview, boolean indexable, boolean smallImage,
166             String smallImageURL, File smallFile, Map<String, byte[]> images,
167             String articleURL, ServiceContext serviceContext)
168         throws PortalException, SystemException {
169 
170         double version = JournalArticleConstants.DEFAULT_VERSION;
171 
172         return addArticle(
173             userId, groupId, articleId, autoArticleId, version, title,
174             description, content, type, structureId, templateId,
175             displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
176             displayDateMinute, expirationDateMonth, expirationDateDay,
177             expirationDateYear, expirationDateHour, expirationDateMinute,
178             neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
179             reviewDateHour, reviewDateMinute, neverReview, indexable,
180             smallImage, smallImageURL, smallFile, images, articleURL,
181             serviceContext);
182     }
183 
184     public JournalArticle addArticle(
185             String uuid, long userId, long groupId, String articleId,
186             boolean autoArticleId, double version, String title,
187             String description, String content, String type, String structureId,
188             String templateId, int displayDateMonth, int displayDateDay,
189             int displayDateYear, int displayDateHour, int displayDateMinute,
190             int expirationDateMonth, int expirationDateDay,
191             int expirationDateYear, int expirationDateHour,
192             int expirationDateMinute, boolean neverExpire, int reviewDateMonth,
193             int reviewDateDay, int reviewDateYear, int reviewDateHour,
194             int reviewDateMinute, boolean neverReview, boolean indexable,
195             boolean smallImage, String smallImageURL, File smallFile,
196             Map<String, byte[]> images, String articleURL,
197             ServiceContext serviceContext)
198         throws PortalException, SystemException {
199 
200         // Article
201 
202         User user = userPersistence.findByPrimaryKey(userId);
203         articleId = articleId.trim().toUpperCase();
204 
205         Date displayDate = PortalUtil.getDate(
206             displayDateMonth, displayDateDay, displayDateYear,
207             displayDateHour, displayDateMinute, user.getTimeZone(),
208             new ArticleDisplayDateException());
209 
210         Date expirationDate = null;
211 
212         if (!neverExpire) {
213             expirationDate = PortalUtil.getDate(
214                 expirationDateMonth, expirationDateDay, expirationDateYear,
215                 expirationDateHour, expirationDateMinute, user.getTimeZone(),
216                 new ArticleExpirationDateException());
217         }
218 
219         Date reviewDate = null;
220 
221         if (!neverReview) {
222             reviewDate = PortalUtil.getDate(
223                 reviewDateMonth, reviewDateDay, reviewDateYear, reviewDateHour,
224                 reviewDateMinute, user.getTimeZone(),
225                 new ArticleReviewDateException());
226         }
227 
228         byte[] smallBytes = null;
229 
230         try {
231             smallBytes = FileUtil.getBytes(smallFile);
232         }
233         catch (IOException ioe) {
234         }
235 
236         Date now = new Date();
237 
238         validate(
239             groupId, articleId, autoArticleId, version, title, content, type,
240             structureId, templateId, smallImage, smallImageURL, smallFile,
241             smallBytes);
242 
243         if (autoArticleId) {
244             articleId = String.valueOf(counterLocalService.increment());
245         }
246 
247         long id = counterLocalService.increment();
248 
249         long resourcePrimKey =
250             journalArticleResourceLocalService.getArticleResourcePrimKey(
251                 groupId, articleId);
252 
253         JournalArticle article = journalArticlePersistence.create(id);
254 
255         content = format(
256             groupId, articleId, version, false, content, structureId, images);
257 
258         article.setUuid(uuid);
259         article.setResourcePrimKey(resourcePrimKey);
260         article.setGroupId(groupId);
261         article.setCompanyId(user.getCompanyId());
262         article.setUserId(user.getUserId());
263         article.setUserName(user.getFullName());
264         article.setCreateDate(now);
265         article.setModifiedDate(now);
266         article.setArticleId(articleId);
267         article.setVersion(version);
268         article.setTitle(title);
269         article.setUrlTitle(getUniqueUrlTitle(id, groupId, articleId, title));
270         article.setDescription(description);
271         article.setContent(content);
272         article.setType(type);
273         article.setStructureId(structureId);
274         article.setTemplateId(templateId);
275         article.setDisplayDate(displayDate);
276 
277         if ((expirationDate == null) || expirationDate.after(now)) {
278             article.setStatus(StatusConstants.PENDING);
279         }
280         else {
281             article.setStatus(StatusConstants.EXPIRED);
282         }
283 
284         article.setExpirationDate(expirationDate);
285         article.setReviewDate(reviewDate);
286         article.setIndexable(indexable);
287         article.setSmallImage(smallImage);
288         article.setSmallImageId(counterLocalService.increment());
289         article.setSmallImageURL(smallImageURL);
290 
291         journalArticlePersistence.update(article, false);
292 
293         updateUrlTitles(groupId, articleId, article.getUrlTitle());
294 
295         // Resources
296 
297         if (serviceContext.getAddCommunityPermissions() ||
298             serviceContext.getAddGuestPermissions()) {
299 
300             addArticleResources(
301                 article, serviceContext.getAddCommunityPermissions(),
302                 serviceContext.getAddGuestPermissions());
303         }
304         else {
305             addArticleResources(
306                 article, serviceContext.getCommunityPermissions(),
307                 serviceContext.getGuestPermissions());
308         }
309 
310         // Expando
311 
312         ExpandoBridge expandoBridge = article.getExpandoBridge();
313 
314         expandoBridge.setAttributes(serviceContext);
315 
316         // Small image
317 
318         saveImages(
319             smallImage, article.getSmallImageId(), smallFile, smallBytes);
320 
321         // Asset
322 
323         updateAsset(
324             userId, article, serviceContext.getAssetCategoryIds(),
325             serviceContext.getAssetTagNames());
326 
327         // Message boards
328 
329         if (PropsValues.JOURNAL_ARTICLE_COMMENTS_ENABLED) {
330             mbMessageLocalService.addDiscussionMessage(
331                 userId, article.getUserName(),
332                 JournalArticle.class.getName(), resourcePrimKey,
333                 StatusConstants.APPROVED);
334         }
335 
336         // Email
337 
338         PortletPreferences preferences =
339             ServiceContextUtil.getPortletPreferences(serviceContext);
340 
341         try {
342             sendEmail(article, articleURL, preferences, "requested");
343         }
344         catch (IOException ioe) {
345             throw new SystemException(ioe);
346         }
347 
348         // Workflow
349 
350         if (serviceContext.isStartWorkflow()) {
351             try {
352                 WorkflowHandlerRegistryUtil.startWorkflowInstance(
353                     user.getCompanyId(), groupId, userId,
354                     JournalArticle.class.getName(), article.getId(), article);
355             }
356             catch (Exception e) {
357                 throw new SystemException(e);
358             }
359         }
360 
361         return article;
362     }
363 
364     public void addArticleResources(
365             JournalArticle article, boolean addCommunityPermissions,
366             boolean addGuestPermissions)
367         throws PortalException, SystemException {
368 
369         resourceLocalService.addResources(
370             article.getCompanyId(), article.getGroupId(),
371             article.getUserId(), JournalArticle.class.getName(),
372             article.getResourcePrimKey(), false, addCommunityPermissions,
373             addGuestPermissions);
374     }
375 
376     public void addArticleResources(
377             JournalArticle article, String[] communityPermissions,
378             String[] guestPermissions)
379         throws PortalException, SystemException {
380 
381         resourceLocalService.addModelResources(
382             article.getCompanyId(), article.getGroupId(),
383             article.getUserId(), JournalArticle.class.getName(),
384             article.getResourcePrimKey(), communityPermissions,
385             guestPermissions);
386     }
387 
388     public void addArticleResources(
389             long groupId, String articleId, boolean addCommunityPermissions,
390             boolean addGuestPermissions)
391         throws PortalException, SystemException {
392 
393         JournalArticle article = getLatestArticle(groupId, articleId);
394 
395         addArticleResources(
396             article, addCommunityPermissions, addGuestPermissions);
397     }
398 
399     public void addArticleResources(
400             long groupId, String articleId, String[] communityPermissions,
401             String[] guestPermissions)
402         throws PortalException, SystemException {
403 
404         JournalArticle article = getLatestArticle(groupId, articleId);
405 
406         addArticleResources(article, communityPermissions, guestPermissions);
407     }
408 
409     public JournalArticle checkArticleResourcePrimKey(
410             long groupId, String articleId, double version)
411         throws PortalException, SystemException {
412 
413         JournalArticle article = journalArticlePersistence.findByG_A_V(
414             groupId, articleId, version);
415 
416         if (article.getResourcePrimKey() > 0) {
417             return article;
418         }
419 
420         long resourcePrimKey =
421             journalArticleResourceLocalService.getArticleResourcePrimKey(
422                 groupId, articleId);
423 
424         article.setResourcePrimKey(resourcePrimKey);
425 
426         journalArticlePersistence.update(article, false);
427 
428         return article;
429     }
430 
431     public void checkArticles() throws PortalException, SystemException {
432         Date now = new Date();
433 
434         List<JournalArticle> articles =
435             journalArticleFinder.findByExpirationDate(
436                 StatusConstants.APPROVED, now,
437                 new Date(now.getTime() - _journalArticleCheckInterval));
438 
439         if (_log.isDebugEnabled()) {
440             _log.debug("Expiring " + articles.size() + " articles");
441         }
442 
443         Set<Long> companyIds = new HashSet<Long>();
444 
445         for (JournalArticle article : articles) {
446             article.setStatus(StatusConstants.EXPIRED);
447 
448             journalArticlePersistence.update(article, false);
449 
450             if (article.isIndexable()) {
451                 Indexer indexer = IndexerRegistryUtil.getIndexer(
452                     JournalArticle.class);
453 
454                 indexer.delete(article);
455             }
456 
457             JournalContentUtil.clearCache(
458                 article.getGroupId(), article.getArticleId(),
459                 article.getTemplateId());
460 
461             companyIds.add(article.getCompanyId());
462         }
463 
464         for (long companyId : companyIds) {
465             CacheUtil.clearCache(companyId);
466         }
467 
468         articles = journalArticleFinder.findByReviewDate(
469             now, new Date(now.getTime() - _journalArticleCheckInterval));
470 
471         if (_log.isDebugEnabled()) {
472             _log.debug(
473                 "Sending review notifications for " + articles.size() +
474                     " articles");
475         }
476 
477         for (JournalArticle article : articles) {
478             String articleURL = StringPool.BLANK;
479 
480             long ownerId = article.getGroupId();
481             int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
482             long plid = PortletKeys.PREFS_PLID_SHARED;
483             String portletId = PortletKeys.JOURNAL;
484 
485             PortletPreferences preferences =
486                 portletPreferencesLocalService.getPreferences(
487                     article.getCompanyId(), ownerId, ownerType, plid,
488                     portletId);
489 
490             try {
491                 sendEmail(article, articleURL, preferences, "review");
492             }
493             catch (IOException ioe) {
494                 throw new SystemException(ioe);
495             }
496         }
497     }
498 
499     public void checkNewLine(long groupId, String articleId, double version)
500         throws PortalException, SystemException {
501 
502         JournalArticle article = journalArticlePersistence.findByG_A_V(
503             groupId, articleId, version);
504 
505         String content = GetterUtil.getString(article.getContent());
506 
507         if (content.indexOf("\\n") != -1) {
508             content = StringUtil.replace(
509                 content,
510                 new String[] {"\\n", "\\r"},
511                 new String[] {"\n", "\r"});
512 
513             article.setContent(content);
514 
515             journalArticlePersistence.update(article, false);
516         }
517     }
518 
519     public void checkStructure(long groupId, String articleId, double version)
520         throws PortalException, SystemException {
521 
522         JournalArticle article = journalArticlePersistence.findByG_A_V(
523             groupId, articleId, version);
524 
525         if (Validator.isNull(article.getStructureId())) {
526             return;
527         }
528 
529         try {
530             checkStructure(article);
531         }
532         catch (DocumentException de) {
533             _log.error(de, de);
534         }
535     }
536 
537     public JournalArticle copyArticle(
538             long userId, long groupId, String oldArticleId, String newArticleId,
539             boolean autoArticleId, double version)
540         throws PortalException, SystemException {
541 
542         // Article
543 
544         User user = userPersistence.findByPrimaryKey(userId);
545         oldArticleId = oldArticleId.trim().toUpperCase();
546         newArticleId = newArticleId.trim().toUpperCase();
547         Date now = new Date();
548 
549         JournalArticle oldArticle = journalArticlePersistence.findByG_A_V(
550             groupId, oldArticleId, version);
551 
552         if (autoArticleId) {
553             newArticleId = String.valueOf(counterLocalService.increment());
554         }
555         else {
556             validate(newArticleId);
557 
558             JournalArticle newArticle = journalArticlePersistence.fetchByG_A_V(
559                 groupId, newArticleId, version);
560 
561             if (newArticle != null) {
562                 throw new DuplicateArticleIdException();
563             }
564         }
565 
566         long id = counterLocalService.increment();
567 
568         long resourcePrimKey =
569             journalArticleResourceLocalService.getArticleResourcePrimKey(
570                 groupId, newArticleId);
571 
572         JournalArticle newArticle = journalArticlePersistence.create(id);
573 
574         newArticle.setResourcePrimKey(resourcePrimKey);
575         newArticle.setGroupId(groupId);
576         newArticle.setCompanyId(user.getCompanyId());
577         newArticle.setUserId(user.getUserId());
578         newArticle.setUserName(user.getFullName());
579         newArticle.setCreateDate(now);
580         newArticle.setModifiedDate(now);
581         newArticle.setArticleId(newArticleId);
582         newArticle.setVersion(JournalArticleConstants.DEFAULT_VERSION);
583         newArticle.setTitle(oldArticle.getTitle());
584         newArticle.setDescription(oldArticle.getDescription());
585 
586         try {
587             copyArticleImages(oldArticle, newArticle);
588         }
589         catch (Exception e) {
590             newArticle.setContent(oldArticle.getContent());
591         }
592 
593         newArticle.setType(oldArticle.getType());
594         newArticle.setStructureId(oldArticle.getStructureId());
595         newArticle.setTemplateId(oldArticle.getTemplateId());
596         newArticle.setDisplayDate(oldArticle.getDisplayDate());
597         newArticle.setStatus(oldArticle.getStatus());
598         newArticle.setExpirationDate(oldArticle.getExpirationDate());
599         newArticle.setReviewDate(oldArticle.getReviewDate());
600         newArticle.setIndexable(oldArticle.isIndexable());
601         newArticle.setSmallImage(oldArticle.isSmallImage());
602         newArticle.setSmallImageId(counterLocalService.increment());
603         newArticle.setSmallImageURL(oldArticle.getSmallImageURL());
604 
605         journalArticlePersistence.update(newArticle, false);
606 
607         // Resources
608 
609         addArticleResources(newArticle, true, true);
610 
611         // Small image
612 
613         if (oldArticle.getSmallImage()) {
614             Image image = imageLocalService.getImage(
615                 oldArticle.getSmallImageId());
616 
617             byte[] smallBytes = image.getTextObj();
618 
619             imageLocalService.updateImage(
620                 newArticle.getSmallImageId(), smallBytes);
621         }
622 
623         // Asset
624 
625         String[] assetTagNames = assetTagLocalService.getTagNames(
626             JournalArticle.class.getName(), oldArticle.getResourcePrimKey());
627 
628         updateAsset(userId, newArticle, null, assetTagNames);
629 
630         return newArticle;
631     }
632 
633     public void deleteArticle(
634             JournalArticle article, String articleURL,
635             ServiceContext serviceContext)
636         throws PortalException, SystemException {
637 
638         // Indexer
639 
640         if (article.isApproved() && article.isIndexable()) {
641             Indexer indexer = IndexerRegistryUtil.getIndexer(
642                 JournalArticle.class);
643 
644             indexer.delete(article);
645         }
646 
647         // Email
648 
649         PortletPreferences preferences =
650             ServiceContextUtil.getPortletPreferences(serviceContext);
651 
652         if ((preferences != null) && !article.isApproved() &&
653             isLatestVersion(
654                 article.getGroupId(), article.getArticleId(),
655                 article.getVersion())) {
656 
657             try {
658                 sendEmail(article, articleURL, preferences, "denied");
659             }
660             catch (IOException ioe) {
661                 throw new SystemException(ioe);
662             }
663         }
664 
665         // Images
666 
667         journalArticleImageLocalService.deleteImages(
668             article.getGroupId(), article.getArticleId(), article.getVersion());
669 
670         int articlesCount = journalArticlePersistence.countByG_A(
671             article.getGroupId(), article.getArticleId());
672 
673         if (articlesCount == 1) {
674 
675             // Workflow
676 
677             workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
678                 article.getCompanyId(), article.getGroupId(),
679                 JournalArticle.class.getName(), article.getResourcePrimKey());
680 
681             // Ratings
682 
683             ratingsStatsLocalService.deleteStats(
684                 JournalArticle.class.getName(), article.getResourcePrimKey());
685 
686             // Message boards
687 
688             mbMessageLocalService.deleteDiscussionMessages(
689                 JournalArticle.class.getName(), article.getResourcePrimKey());
690 
691             // Asset
692 
693             assetEntryLocalService.deleteEntry(
694                 JournalArticle.class.getName(), article.getResourcePrimKey());
695 
696             // Content searches
697 
698             journalContentSearchLocalService.deleteArticleContentSearches(
699                 article.getGroupId(), article.getArticleId());
700 
701             // Small image
702 
703             imageLocalService.deleteImage(article.getSmallImageId());
704 
705             // Expando
706 
707             expandoValueLocalService.deleteValues(
708                 JournalArticle.class.getName(), article.getResourcePrimKey());
709 
710             // Resources
711 
712             resourceLocalService.deleteResource(
713                 article.getCompanyId(), JournalArticle.class.getName(),
714                 ResourceConstants.SCOPE_INDIVIDUAL,
715                 article.getResourcePrimKey());
716 
717             // Resource
718 
719             try {
720                 journalArticleResourceLocalService.deleteArticleResource(
721                     article.getGroupId(), article.getArticleId());
722             }
723             catch (NoSuchArticleResourceException nsare) {
724             }
725         }
726 
727         // Article
728 
729         journalArticlePersistence.remove(article);
730     }
731 
732     public void deleteArticle(
733             long groupId, String articleId, double version, String articleURL,
734             ServiceContext serviceContext)
735         throws PortalException, SystemException {
736 
737         JournalArticle article = journalArticlePersistence.findByG_A_V(
738             groupId, articleId, version);
739 
740         deleteArticle(article, articleURL, serviceContext);
741     }
742 
743     public void deleteArticles(long groupId)
744         throws PortalException, SystemException {
745 
746         for (JournalArticle article :
747                 journalArticlePersistence.findByGroupId(groupId)) {
748 
749             deleteArticle(article, null, null);
750         }
751     }
752 
753     public JournalArticle getArticle(long id)
754         throws PortalException, SystemException {
755 
756         return journalArticlePersistence.findByPrimaryKey(id);
757     }
758 
759     public JournalArticle getArticle(long groupId, String articleId)
760         throws PortalException, SystemException {
761 
762         // Get the latest article that is approved, if none are approved, get
763         // the latest unapproved article
764 
765         try {
766             return getLatestArticle(
767                 groupId, articleId, StatusConstants.APPROVED);
768         }
769         catch (NoSuchArticleException nsae) {
770             return getLatestArticle(
771                 groupId, articleId, StatusConstants.PENDING);
772         }
773     }
774 
775     public JournalArticle getArticle(
776             long groupId, String articleId, double version)
777         throws PortalException, SystemException {
778 
779         return journalArticlePersistence.findByG_A_V(
780             groupId, articleId, version);
781     }
782 
783     public JournalArticle getArticleByUrlTitle(long groupId, String urlTitle)
784         throws PortalException, SystemException {
785 
786         // Get the latest article that is approved, if none are approved, get
787         // the latest unapproved article
788 
789         try {
790             return getLatestArticleByUrlTitle(
791                 groupId, urlTitle, StatusConstants.APPROVED);
792         }
793         catch (NoSuchArticleException nsae) {
794             return getLatestArticleByUrlTitle(
795                 groupId, urlTitle, StatusConstants.PENDING);
796         }
797     }
798 
799     public String getArticleContent(
800             JournalArticle article, String templateId, String viewMode,
801             String languageId, ThemeDisplay themeDisplay)
802         throws SystemException {
803 
804         JournalArticleDisplay articleDisplay = getArticleDisplay(
805             article, templateId, viewMode, languageId, 1, null, themeDisplay);
806 
807         if (articleDisplay == null) {
808             return StringPool.BLANK;
809         }
810         else {
811             return articleDisplay.getContent();
812         }
813     }
814 
815     public String getArticleContent(
816             long groupId, String articleId, double version, String viewMode,
817             String templateId, String languageId, ThemeDisplay themeDisplay)
818         throws PortalException, SystemException {
819 
820         JournalArticleDisplay articleDisplay = getArticleDisplay(
821             groupId, articleId, version, templateId, viewMode, languageId,
822             themeDisplay);
823 
824         if (articleDisplay == null) {
825             return StringPool.BLANK;
826         }
827         else {
828             return articleDisplay.getContent();
829         }
830     }
831 
832     public String getArticleContent(
833             long groupId, String articleId, double version, String viewMode,
834             String languageId, ThemeDisplay themeDisplay)
835         throws PortalException, SystemException {
836 
837         return getArticleContent(
838             groupId, articleId, version, viewMode, null, languageId,
839             themeDisplay);
840     }
841 
842     public String getArticleContent(
843             long groupId, String articleId, String viewMode, String templateId,
844             String languageId, ThemeDisplay themeDisplay)
845         throws PortalException, SystemException {
846 
847         JournalArticleDisplay articleDisplay = getArticleDisplay(
848             groupId, articleId, templateId, viewMode, languageId, themeDisplay);
849 
850         return articleDisplay.getContent();
851     }
852 
853     public String getArticleContent(
854             long groupId, String articleId, String viewMode, String languageId,
855             ThemeDisplay themeDisplay)
856         throws PortalException, SystemException {
857 
858         return getArticleContent(
859             groupId, articleId, viewMode, null, languageId, themeDisplay);
860     }
861 
862     public JournalArticleDisplay getArticleDisplay(
863             JournalArticle article, String templateId, String viewMode,
864             String languageId, int page, String xmlRequest,
865             ThemeDisplay themeDisplay)
866         throws SystemException {
867 
868         String content = null;
869 
870         if (page < 1) {
871             page = 1;
872         }
873 
874         int numberOfPages = 1;
875         boolean paginate = false;
876         boolean pageFlow = false;
877 
878         boolean cacheable = true;
879 
880         if (Validator.isNull(xmlRequest)) {
881             xmlRequest = "<request />";
882         }
883 
884         Map<String, String> tokens = JournalUtil.getTokens(
885             article.getGroupId(), themeDisplay, xmlRequest);
886 
887         tokens.put(
888             "article_resource_pk",
889             String.valueOf(article.getResourcePrimKey()));
890 
891         String defaultTemplateId = article.getTemplateId();
892 
893         if (article.isTemplateDriven()) {
894             if (Validator.isNull(templateId)) {
895                 templateId = defaultTemplateId;
896             }
897 
898             tokens.put("structure_id", article.getStructureId());
899             tokens.put("template_id", templateId);
900         }
901 
902         String xml = article.getContent();
903 
904         try {
905             Document doc = null;
906 
907             Element root = null;
908 
909             if (article.isTemplateDriven()) {
910                 doc = SAXReaderUtil.read(xml);
911 
912                 root = doc.getRootElement();
913 
914                 Document request = SAXReaderUtil.read(xmlRequest);
915 
916                 List<Element> pages = root.elements("page");
917 
918                 if (pages.size() > 0) {
919                     pageFlow = true;
920 
921                     String targetPage = request.valueOf(
922                         "/request/parameters/parameter[name='targetPage']/" +
923                             "value");
924 
925                     Element pageEl = null;
926 
927                     if (Validator.isNotNull(targetPage)) {
928                         XPath xpathSelector = SAXReaderUtil.createXPath(
929                             "/root/page[@id = '" + targetPage + "']");
930 
931                         pageEl = (Element)xpathSelector.selectSingleNode(doc);
932                     }
933 
934                     if (pageEl != null) {
935                         doc = SAXReaderUtil.createDocument(pageEl);
936 
937                         root = doc.getRootElement();
938 
939                         numberOfPages = pages.size();
940                     }
941                     else {
942                         if (page > pages.size()) {
943                             page = 1;
944                         }
945 
946                         pageEl = pages.get(page - 1);
947 
948                         doc = SAXReaderUtil.createDocument(pageEl);
949 
950                         root = doc.getRootElement();
951 
952                         numberOfPages = pages.size();
953                         paginate = true;
954                     }
955                 }
956 
957                 root.add(request.getRootElement().createCopy());
958 
959                 JournalUtil.addAllReservedEls(root, tokens, article);
960 
961                 xml = JournalUtil.formatXML(doc);
962             }
963         }
964         catch (DocumentException de) {
965             throw new SystemException(de);
966         }
967         catch (IOException ioe) {
968             throw new SystemException(ioe);
969         }
970 
971         try {
972             if (_log.isDebugEnabled()) {
973                 _log.debug(
974                     "Transforming " + article.getArticleId() + " " +
975                         article.getVersion() + " " + languageId);
976             }
977 
978             String script = null;
979             String langType = null;
980 
981             if (article.isTemplateDriven()) {
982 
983                 // Try with specified template first. If a template is not
984                 // specified, use the default one. If the specified template
985                 // does not exit, use the default one. If the default one does
986                 // not exist, throw an exception.
987 
988                 JournalTemplate template = null;
989 
990                 try {
991                     template = journalTemplatePersistence.findByG_T(
992                         article.getGroupId(), templateId);
993                 }
994                 catch (NoSuchTemplateException nste) {
995                     if (!defaultTemplateId.equals(templateId)) {
996                         template = journalTemplatePersistence.findByG_T(
997                             article.getGroupId(), defaultTemplateId);
998                     }
999                     else {
1000                        throw nste;
1001                    }
1002                }
1003
1004                script = template.getXsl();
1005                langType = template.getLangType();
1006                cacheable = template.isCacheable();
1007            }
1008
1009            content = JournalUtil.transform(
1010                tokens, viewMode, languageId, xml, script, langType);
1011
1012            if (!pageFlow) {
1013                String[] pieces = StringUtil.split(content, _TOKEN_PAGE_BREAK);
1014
1015                if (pieces.length > 1) {
1016                    if (page > pieces.length) {
1017                        page = 1;
1018                    }
1019
1020                    content = pieces[page - 1];
1021                    numberOfPages = pieces.length;
1022                    paginate = true;
1023                }
1024            }
1025        }
1026        catch (Exception e) {
1027            throw new SystemException(e);
1028        }
1029
1030        return new JournalArticleDisplayImpl(
1031            article.getId(), article.getResourcePrimKey(), article.getGroupId(),
1032            article.getUserId(), article.getArticleId(), article.getVersion(),
1033            article.getTitle(), article.getUrlTitle(), article.getDescription(),
1034            article.getAvailableLocales(), content, article.getType(),
1035            article.getStructureId(), templateId, article.isSmallImage(),
1036            article.getSmallImageId(), article.getSmallImageURL(),
1037            numberOfPages, page, paginate, cacheable);
1038    }
1039
1040    public JournalArticleDisplay getArticleDisplay(
1041            long groupId, String articleId, double version, String templateId,
1042            String viewMode, String languageId, int page, String xmlRequest,
1043            ThemeDisplay themeDisplay)
1044        throws PortalException, SystemException {
1045
1046        Date now = new Date();
1047
1048        JournalArticle article = journalArticlePersistence.findByG_A_V(
1049            groupId, articleId, version);
1050
1051        if (article.isExpired()) {
1052            Date expirationDate = article.getExpirationDate();
1053
1054            if ((expirationDate != null) && expirationDate.before(now)) {
1055                return null;
1056            }
1057        }
1058
1059        if (article.getDisplayDate().after(now)) {
1060            return null;
1061        }
1062
1063        return getArticleDisplay(
1064            article, templateId, viewMode, languageId, page, xmlRequest,
1065            themeDisplay);
1066    }
1067
1068    public JournalArticleDisplay getArticleDisplay(
1069            long groupId, String articleId, double version, String templateId,
1070            String viewMode, String languageId, ThemeDisplay themeDisplay)
1071        throws PortalException, SystemException {
1072
1073        return getArticleDisplay(
1074            groupId, articleId, version, templateId, viewMode, languageId, 1,
1075            null, themeDisplay);
1076    }
1077
1078    public JournalArticleDisplay getArticleDisplay(
1079            long groupId, String articleId, String viewMode, String languageId,
1080            int page, String xmlRequest, ThemeDisplay themeDisplay)
1081        throws PortalException, SystemException {
1082
1083        return getArticleDisplay(
1084            groupId, articleId, null, viewMode, languageId, page, xmlRequest,
1085            themeDisplay);
1086    }
1087
1088    public JournalArticleDisplay getArticleDisplay(
1089            long groupId, String articleId, String templateId, String viewMode,
1090            String languageId, int page, String xmlRequest,
1091            ThemeDisplay themeDisplay)
1092        throws PortalException, SystemException {
1093
1094        JournalArticle article = getDisplayArticle(groupId, articleId);
1095
1096        return getArticleDisplay(
1097            groupId, articleId, article.getVersion(), templateId, viewMode,
1098            languageId, page, xmlRequest, themeDisplay);
1099    }
1100
1101    public JournalArticleDisplay getArticleDisplay(
1102            long groupId, String articleId, String templateId, String viewMode,
1103            String languageId, ThemeDisplay themeDisplay)
1104        throws PortalException, SystemException {
1105
1106        JournalArticle article = getDisplayArticle(groupId, articleId);
1107
1108        return getArticleDisplay(
1109            groupId, articleId, article.getVersion(), templateId, viewMode,
1110            languageId, themeDisplay);
1111    }
1112
1113    public JournalArticleDisplay getArticleDisplay(
1114            long groupId, String articleId, String viewMode, String languageId,
1115            ThemeDisplay themeDisplay)
1116        throws PortalException, SystemException {
1117
1118        return getArticleDisplay(
1119            groupId, articleId, null, viewMode, languageId, themeDisplay);
1120    }
1121
1122    public List<JournalArticle> getArticles() throws SystemException {
1123        return journalArticlePersistence.findAll();
1124    }
1125
1126    public List<JournalArticle> getArticles(long groupId)
1127        throws SystemException {
1128
1129        return journalArticlePersistence.findByGroupId(groupId);
1130    }
1131
1132    public List<JournalArticle> getArticles(long groupId, int start, int end)
1133        throws SystemException {
1134
1135        return journalArticlePersistence.findByGroupId(groupId, start, end);
1136    }
1137
1138    public List<JournalArticle> getArticles(
1139            long groupId, int start, int end, OrderByComparator obc)
1140        throws SystemException {
1141
1142        return journalArticlePersistence.findByGroupId(
1143            groupId, start, end, obc);
1144    }
1145
1146    public List<JournalArticle> getArticles(long groupId, String articleId)
1147        throws SystemException {
1148
1149        return journalArticlePersistence.findByG_A(groupId, articleId);
1150    }
1151
1152    public List<JournalArticle> getArticlesBySmallImageId(long smallImageId)
1153        throws SystemException {
1154
1155        return journalArticlePersistence.findBySmallImageId(smallImageId);
1156    }
1157
1158    public int getArticlesCount(long groupId) throws SystemException {
1159        return journalArticlePersistence.countByGroupId(groupId);
1160    }
1161
1162    public List<JournalArticle> getCompanyArticles(
1163            long companyId, int status, int start, int end)
1164        throws SystemException {
1165
1166        if (status == StatusConstants.ANY) {
1167            return journalArticlePersistence.findByCompanyId(
1168                companyId, start, end, new ArticleIDComparator(true));
1169        }
1170        else {
1171            return journalArticlePersistence.findByC_S(
1172                companyId, status, start, end, new ArticleIDComparator(true));
1173        }
1174    }
1175
1176    public int getCompanyArticlesCount(long companyId, int status)
1177        throws SystemException {
1178
1179        if (status == StatusConstants.ANY) {
1180            return journalArticlePersistence.countByCompanyId(companyId);
1181        }
1182        else {
1183            return journalArticlePersistence.countByC_S(companyId, status);
1184        }
1185    }
1186
1187    public JournalArticle getDisplayArticle(long groupId, String articleId)
1188        throws PortalException, SystemException {
1189
1190        List<JournalArticle> articles = journalArticlePersistence.findByG_A_S(
1191            groupId, articleId, StatusConstants.APPROVED);
1192
1193        if (articles.size() == 0) {
1194            throw new NoSuchArticleException();
1195        }
1196
1197        Date now = new Date();
1198
1199        for (int i = 0; i < articles.size(); i++) {
1200            JournalArticle article = articles.get(i);
1201
1202            Date expirationDate = article.getExpirationDate();
1203
1204            if (article.getDisplayDate().before(now) &&
1205                ((expirationDate == null) || expirationDate.after(now))) {
1206
1207                return article;
1208            }
1209        }
1210
1211        return articles.get(0);
1212    }
1213
1214    public JournalArticle getLatestArticle(long resourcePrimKey)
1215        throws PortalException, SystemException {
1216
1217        return getLatestArticle(resourcePrimKey, StatusConstants.ANY);
1218    }
1219
1220    public JournalArticle getLatestArticle(long resourcePrimKey, int status)
1221        throws PortalException, SystemException {
1222
1223        List<JournalArticle> articles = null;
1224
1225        OrderByComparator orderByComparator = new ArticleVersionComparator();
1226
1227        if (status == StatusConstants.ANY) {
1228            articles = journalArticlePersistence.findByR_S(
1229                resourcePrimKey, StatusConstants.APPROVED, 0, 1,
1230                orderByComparator);
1231
1232            if (articles.size() == 0) {
1233                articles = journalArticlePersistence.findByR_S(
1234                    resourcePrimKey, StatusConstants.PENDING, 0, 1,
1235                    orderByComparator);
1236            }
1237        }
1238        else {
1239            articles = journalArticlePersistence.findByR_S(
1240                resourcePrimKey, status, 0, 1, orderByComparator);
1241        }
1242
1243        if (articles.size() == 0) {
1244            throw new NoSuchArticleException();
1245        }
1246
1247        return articles.get(0);
1248    }
1249
1250    public JournalArticle getLatestArticle(long groupId, String articleId)
1251        throws PortalException, SystemException {
1252
1253        return getLatestArticle(groupId, articleId, StatusConstants.ANY);
1254    }
1255
1256    public JournalArticle getLatestArticle(
1257            long groupId, String articleId, int status)
1258        throws PortalException, SystemException {
1259
1260        List<JournalArticle> articles = null;
1261
1262        OrderByComparator orderByComparator = new ArticleVersionComparator();
1263
1264        if (status == StatusConstants.ANY) {
1265            articles = journalArticlePersistence.findByG_A(
1266                groupId, articleId, 0, 1, orderByComparator);
1267        }
1268        else {
1269            articles = journalArticlePersistence.findByG_A_S(
1270                groupId, articleId, status, 0, 1, orderByComparator);
1271        }
1272
1273        if (articles.size() == 0) {
1274            throw new NoSuchArticleException();
1275        }
1276
1277        return articles.get(0);
1278    }
1279
1280    public JournalArticle getLatestArticleByUrlTitle(
1281            long groupId, String urlTitle, int status)
1282        throws PortalException, SystemException {
1283
1284        List<JournalArticle> articles = null;
1285
1286        OrderByComparator orderByComparator = new ArticleVersionComparator();
1287
1288        if (status == StatusConstants.ANY) {
1289            articles = journalArticlePersistence.findByG_UT(
1290                groupId, urlTitle, 0, 1, orderByComparator);
1291        }
1292        else {
1293            articles = journalArticlePersistence.findByG_UT_S(
1294                groupId, urlTitle, status, 0, 1, orderByComparator);
1295        }
1296
1297        if (articles.size() == 0) {
1298            throw new NoSuchArticleException();
1299        }
1300
1301        return articles.get(0);
1302    }
1303
1304    public double getLatestVersion(long groupId, String articleId)
1305        throws PortalException, SystemException {
1306
1307        JournalArticle article = getLatestArticle(groupId, articleId);
1308
1309        return article.getVersion();
1310    }
1311
1312    public double getLatestVersion(
1313            long groupId, String articleId, int status)
1314        throws PortalException, SystemException {
1315
1316        JournalArticle article = getLatestArticle(groupId, articleId, status);
1317
1318        return article.getVersion();
1319    }
1320
1321    public List<JournalArticle> getStructureArticles(
1322            long groupId, String structureId)
1323        throws SystemException {
1324
1325        return journalArticlePersistence.findByG_S(groupId, structureId);
1326    }
1327
1328    public List<JournalArticle> getStructureArticles(
1329            long groupId, String structureId, int start, int end,
1330            OrderByComparator obc)
1331        throws SystemException {
1332
1333        return journalArticlePersistence.findByG_S(
1334            groupId, structureId, start, end, obc);
1335    }
1336
1337    public int getStructureArticlesCount(long groupId, String structureId)
1338        throws SystemException {
1339
1340        return journalArticlePersistence.countByG_S(groupId, structureId);
1341    }
1342
1343    public List<JournalArticle> getTemplateArticles(
1344            long groupId, String templateId)
1345        throws SystemException {
1346
1347        return journalArticlePersistence.findByG_T(groupId, templateId);
1348    }
1349
1350    public List<JournalArticle> getTemplateArticles(
1351            long groupId, String templateId, int start, int end,
1352            OrderByComparator obc)
1353        throws SystemException {
1354
1355        return journalArticlePersistence.findByG_T(
1356            groupId, templateId, start, end, obc);
1357    }
1358
1359    public int getTemplateArticlesCount(long groupId, String templateId)
1360        throws SystemException {
1361
1362        return journalArticlePersistence.countByG_T(groupId, templateId);
1363    }
1364
1365    public boolean hasArticle(long groupId, String articleId)
1366        throws SystemException {
1367
1368        try {
1369            getArticle(groupId, articleId);
1370
1371            return true;
1372        }
1373        catch (PortalException pe) {
1374            return false;
1375        }
1376    }
1377
1378    public boolean isLatestVersion(
1379            long groupId, String articleId, double version)
1380        throws PortalException, SystemException {
1381
1382        if (getLatestVersion(groupId, articleId) == version) {
1383            return true;
1384        }
1385        else {
1386            return false;
1387        }
1388    }
1389
1390    public boolean isLatestVersion(
1391            long groupId, String articleId, double version, int status)
1392        throws PortalException, SystemException {
1393
1394        if (getLatestVersion(groupId, articleId, status) == version) {
1395            return true;
1396        }
1397        else {
1398            return false;
1399        }
1400    }
1401
1402    public JournalArticle removeArticleLocale(
1403            long groupId, String articleId, double version, String languageId)
1404        throws PortalException, SystemException {
1405
1406        JournalArticle article = journalArticlePersistence.findByG_A_V(
1407            groupId, articleId, version);
1408
1409        String content = article.getContent();
1410
1411        if (article.isTemplateDriven()) {
1412            content = JournalUtil.removeArticleLocale(content, languageId);
1413        }
1414        else {
1415            content = LocalizationUtil.removeLocalization(
1416                content, "static-content", languageId, true);
1417        }
1418
1419        article.setContent(content);
1420
1421        journalArticlePersistence.update(article, false);
1422
1423        return article;
1424    }
1425
1426    public List<JournalArticle> search(
1427            long companyId, long groupId, String keywords, Double version,
1428            String type, String structureId, String templateId,
1429            Date displayDateGT, Date displayDateLT, int status, Date reviewDate,
1430            int start, int end, OrderByComparator obc)
1431        throws SystemException {
1432
1433        return journalArticleFinder.findByKeywords(
1434            companyId, groupId, keywords, version, type, structureId,
1435            templateId, displayDateGT, displayDateLT, status, reviewDate, start,
1436            end, obc);
1437    }
1438
1439    public List<JournalArticle> search(
1440            long companyId, long groupId, String articleId, Double version,
1441            String title, String description, String content, String type,
1442            String structureId, String templateId, Date displayDateGT,
1443            Date displayDateLT, int status, Date reviewDate,
1444            boolean andOperator, int start, int end, OrderByComparator obc)
1445        throws SystemException {
1446
1447        return journalArticleFinder.findByC_G_A_V_T_D_C_T_S_T_D_S_R(
1448            companyId, groupId, articleId, version, title, description, content,
1449            type, structureId, templateId, displayDateGT, displayDateLT,
1450            status, reviewDate, andOperator, start, end, obc);
1451    }
1452
1453    public List<JournalArticle> search(
1454            long companyId, long groupId, String articleId, Double version,
1455            String title, String description, String content, String type,
1456            String[] structureIds, String[] templateIds, Date displayDateGT,
1457            Date displayDateLT, int status, Date reviewDate,
1458            boolean andOperator, int start, int end, OrderByComparator obc)
1459        throws SystemException {
1460
1461        return journalArticleFinder.findByC_G_A_V_T_D_C_T_S_T_D_S_R(
1462            companyId, groupId, articleId, version, title, description, content,
1463            type, structureIds, templateIds, displayDateGT, displayDateLT,
1464            status, reviewDate, andOperator, start, end, obc);
1465    }
1466
1467    public int searchCount(
1468            long companyId, long groupId, String keywords, Double version,
1469            String type, String structureId, String templateId,
1470            Date displayDateGT, Date displayDateLT, int status, Date reviewDate)
1471        throws SystemException {
1472
1473        return journalArticleFinder.countByKeywords(
1474            companyId, groupId, keywords, version, type, structureId,
1475            templateId, displayDateGT, displayDateLT, status, reviewDate);
1476    }
1477
1478    public int searchCount(
1479            long companyId, long groupId, String articleId, Double version,
1480            String title, String description, String content, String type,
1481            String structureId, String templateId, Date displayDateGT,
1482            Date displayDateLT, int status, Date reviewDate,
1483            boolean andOperator)
1484        throws SystemException {
1485
1486        return journalArticleFinder.countByC_G_A_V_T_D_C_T_S_T_D_S_R(
1487            companyId, groupId, articleId, version, title, description, content,
1488            type, structureId, templateId, displayDateGT, displayDateLT,
1489            status, reviewDate, andOperator);
1490    }
1491
1492    public int searchCount(
1493            long companyId, long groupId, String articleId, Double version,
1494            String title, String description, String content, String type,
1495            String[] structureIds, String[] templateIds, Date displayDateGT,
1496            Date displayDateLT, int status, Date reviewDate,
1497            boolean andOperator)
1498        throws SystemException {
1499
1500        return journalArticleFinder.countByC_G_A_V_T_D_C_T_S_T_D_S_R(
1501            companyId, groupId, articleId, version, title, description, content,
1502            type, structureIds, templateIds, displayDateGT, displayDateLT,
1503            status, reviewDate, andOperator);
1504    }
1505
1506    public JournalArticle updateArticle(
1507            long userId, long groupId, String articleId, double version,
1508            boolean incrementVersion, String content)
1509        throws PortalException, SystemException {
1510
1511        User user = userPersistence.findByPrimaryKey(userId);
1512
1513        JournalArticle article = journalArticlePersistence.findByG_A_V(
1514            groupId, articleId, version);
1515
1516        Date displayDate = article.getDisplayDate();
1517
1518        int displayDateMonth = 0;
1519        int displayDateDay = 0;
1520        int displayDateYear = 0;
1521        int displayDateHour = 0;
1522        int displayDateMinute = 0;
1523
1524        if (displayDate != null) {
1525            Calendar displayCal = CalendarFactoryUtil.getCalendar(
1526                user.getTimeZone());
1527
1528            displayCal.setTime(displayDate);
1529
1530            displayDateMonth = displayCal.get(Calendar.MONTH);
1531            displayDateDay = displayCal.get(Calendar.DATE);
1532            displayDateYear = displayCal.get(Calendar.YEAR);
1533            displayDateHour = displayCal.get(Calendar.HOUR);
1534            displayDateMinute = displayCal.get(Calendar.MINUTE);
1535
1536            if (displayCal.get(Calendar.AM_PM) == Calendar.PM) {
1537                displayDateHour += 12;
1538            }
1539        }
1540
1541        Date expirationDate = article.getExpirationDate();
1542
1543        int expirationDateMonth = 0;
1544        int expirationDateDay = 0;
1545        int expirationDateYear = 0;
1546        int expirationDateHour = 0;
1547        int expirationDateMinute = 0;
1548        boolean neverExpire = true;
1549
1550        if (expirationDate != null) {
1551            Calendar expirationCal = CalendarFactoryUtil.getCalendar(
1552                user.getTimeZone());
1553
1554            expirationCal.setTime(expirationDate);
1555
1556            expirationDateMonth = expirationCal.get(Calendar.MONTH);
1557            expirationDateDay = expirationCal.get(Calendar.DATE);
1558            expirationDateYear = expirationCal.get(Calendar.YEAR);
1559            expirationDateHour = expirationCal.get(Calendar.HOUR);
1560            expirationDateMinute = expirationCal.get(Calendar.MINUTE);
1561            neverExpire = false;
1562
1563            if (expirationCal.get(Calendar.AM_PM) == Calendar.PM) {
1564                expirationDateHour += 12;
1565            }
1566        }
1567
1568        Date reviewDate = article.getReviewDate();
1569
1570        int reviewDateMonth = 0;
1571        int reviewDateDay = 0;
1572        int reviewDateYear = 0;
1573        int reviewDateHour = 0;
1574        int reviewDateMinute = 0;
1575        boolean neverReview = true;
1576
1577        if (reviewDate != null) {
1578            Calendar reviewCal = CalendarFactoryUtil.getCalendar(
1579                user.getTimeZone());
1580
1581            reviewCal.setTime(reviewDate);
1582
1583            reviewDateMonth = reviewCal.get(Calendar.MONTH);
1584            reviewDateDay = reviewCal.get(Calendar.DATE);
1585            reviewDateYear = reviewCal.get(Calendar.YEAR);
1586            reviewDateHour = reviewCal.get(Calendar.HOUR);
1587            reviewDateMinute = reviewCal.get(Calendar.MINUTE);
1588            neverReview = false;
1589
1590            if (reviewCal.get(Calendar.AM_PM) == Calendar.PM) {
1591                reviewDateHour += 12;
1592            }
1593        }
1594
1595        PortletPreferencesIds portletPreferencesIds = new PortletPreferencesIds(
1596            article.getCompanyId(), PortletKeys.PREFS_OWNER_ID_DEFAULT,
1597            PortletKeys.PREFS_OWNER_TYPE_LAYOUT, PortletKeys.PREFS_PLID_SHARED,
1598            PortletKeys.JOURNAL);
1599
1600        ServiceContext serviceContext = new ServiceContext();
1601
1602        serviceContext.setPortletPreferencesIds(portletPreferencesIds);
1603
1604        return updateArticle(
1605            userId, groupId, articleId, version, incrementVersion,
1606            article.getTitle(), article.getDescription(), content,
1607            article.getType(), article.getStructureId(),
1608            article.getTemplateId(), displayDateMonth, displayDateDay,
1609            displayDateYear, displayDateHour, displayDateMinute,
1610            expirationDateMonth, expirationDateDay, expirationDateYear,
1611            expirationDateHour, expirationDateMinute, neverExpire,
1612            reviewDateMonth, reviewDateDay, reviewDateYear, reviewDateHour,
1613            reviewDateMinute, neverReview, article.getIndexable(),
1614            article.isSmallImage(), article.getSmallImageURL(), null, null,
1615            null, serviceContext);
1616    }
1617
1618    public JournalArticle updateArticle(
1619            long userId, long groupId, String articleId, double version,
1620            boolean incrementVersion, String title, String description,
1621            String content, String type, String structureId, String templateId,
1622            int displayDateMonth, int displayDateDay, int displayDateYear,
1623            int displayDateHour, int displayDateMinute, int expirationDateMonth,
1624            int expirationDateDay, int expirationDateYear,
1625            int expirationDateHour, int expirationDateMinute,
1626            boolean neverExpire, int reviewDateMonth, int reviewDateDay,
1627            int reviewDateYear, int reviewDateHour, int reviewDateMinute,
1628            boolean neverReview, boolean indexable, boolean smallImage,
1629            String smallImageURL, File smallFile, Map<String, byte[]> images,
1630            String articleURL, ServiceContext serviceContext)
1631        throws PortalException, SystemException {
1632
1633        // Article
1634
1635        User user = userPersistence.findByPrimaryKey(userId);
1636        articleId = articleId.trim().toUpperCase();
1637
1638        Date displayDate = PortalUtil.getDate(
1639            displayDateMonth, displayDateDay, displayDateYear,
1640            displayDateHour, displayDateMinute, user.getTimeZone(),
1641            new ArticleDisplayDateException());
1642
1643        Date expirationDate = null;
1644
1645        if (!neverExpire) {
1646            expirationDate = PortalUtil.getDate(
1647                expirationDateMonth, expirationDateDay, expirationDateYear,
1648                expirationDateHour, expirationDateMinute, user.getTimeZone(),
1649                new ArticleExpirationDateException());
1650        }
1651
1652        Date reviewDate = null;
1653
1654        if (!neverReview) {
1655            reviewDate = PortalUtil.getDate(
1656                reviewDateMonth, reviewDateDay, reviewDateYear, reviewDateHour,
1657                reviewDateMinute, user.getTimeZone(),
1658                new ArticleReviewDateException());
1659        }
1660
1661        byte[] smallBytes = null;
1662
1663        try {
1664            smallBytes = FileUtil.getBytes(smallFile);
1665        }
1666        catch (IOException ioe) {
1667        }
1668
1669        Date now = new Date();
1670
1671        validate(
1672            groupId, title, content, type, structureId, templateId, smallImage,
1673            smallImageURL, smallFile, smallBytes);
1674
1675        JournalArticle oldArticle = journalArticlePersistence.findByG_A_V(
1676            groupId, articleId, version);
1677
1678        JournalArticle article = null;
1679
1680        if (incrementVersion) {
1681            double latestVersion = getLatestVersion(groupId, articleId);
1682
1683            long id = counterLocalService.increment();
1684
1685            article = journalArticlePersistence.create(id);
1686
1687            article.setResourcePrimKey(oldArticle.getResourcePrimKey());
1688            article.setGroupId(oldArticle.getGroupId());
1689            article.setCompanyId(user.getCompanyId());
1690            article.setUserId(user.getUserId());
1691            article.setUserName(user.getFullName());
1692            article.setCreateDate(now);
1693            article.setArticleId(articleId);
1694            article.setVersion(MathUtil.format(latestVersion + 0.1, 1, 1));
1695            article.setSmallImageId(oldArticle.getSmallImageId());
1696        }
1697        else {
1698            article = oldArticle;
1699        }
1700
1701        content = format(
1702            groupId, articleId, article.getVersion(), incrementVersion, content,
1703            structureId, images);
1704
1705        int status = oldArticle.getStatus();
1706
1707        if (incrementVersion) {
1708            status = StatusConstants.PENDING;
1709        }
1710
1711        article.setModifiedDate(now);
1712        article.setTitle(title);
1713        article.setUrlTitle(
1714            getUniqueUrlTitle(article.getId(), groupId, articleId, title));
1715        article.setDescription(description);
1716        article.setContent(content);
1717        article.setType(type);
1718        article.setStructureId(structureId);
1719        article.setTemplateId(templateId);
1720        article.setDisplayDate(displayDate);
1721
1722        if ((expirationDate == null) || expirationDate.after(now)) {
1723            article.setStatus(status);
1724        }
1725        else {
1726            article.setStatus(StatusConstants.EXPIRED);
1727        }
1728
1729        article.setExpirationDate(expirationDate);
1730        article.setReviewDate(reviewDate);
1731        article.setIndexable(indexable);
1732        article.setSmallImage(smallImage);
1733
1734        if (article.getSmallImageId() == 0) {
1735            article.setSmallImageId(counterLocalService.increment());
1736        }
1737
1738        article.setSmallImageURL(smallImageURL);
1739
1740        journalArticlePersistence.update(article, false);
1741
1742        updateUrlTitles(groupId, articleId, article.getUrlTitle());
1743
1744        // Asset
1745
1746        long[] assetCategoryIds = serviceContext.getAssetCategoryIds();
1747        String[] assetTagNames = serviceContext.getAssetTagNames();
1748
1749        updateAsset(userId, article, assetCategoryIds, assetTagNames);
1750
1751        // Expando
1752
1753        ExpandoBridge expandoBridge = article.getExpandoBridge();
1754
1755        expandoBridge.setAttributes(serviceContext);
1756
1757        // Small image
1758
1759        saveImages(
1760            smallImage, article.getSmallImageId(), smallFile, smallBytes);
1761
1762        // Email
1763
1764        PortletPreferences preferences =
1765            ServiceContextUtil.getPortletPreferences(serviceContext);
1766
1767        if (incrementVersion) {
1768            try {
1769                sendEmail(article, articleURL, preferences, "requested");
1770            }
1771            catch (IOException ioe) {
1772                throw new SystemException(ioe);
1773            }
1774        }
1775
1776        // Indexer
1777
1778        Indexer indexer = IndexerRegistryUtil.getIndexer(JournalArticle.class);
1779
1780        indexer.reindex(article);
1781
1782        // Workflow
1783
1784        if (serviceContext.isStartWorkflow()) {
1785            try {
1786                WorkflowHandlerRegistryUtil.startWorkflowInstance(
1787                    user.getCompanyId(), groupId, userId,
1788                    JournalArticle.class.getName(), article.getId(), article);
1789            }
1790            catch (Exception e) {
1791                throw new SystemException(e);
1792            }
1793        }
1794
1795        return article;
1796    }
1797
1798    public void updateAsset(
1799            long userId, JournalArticle article, long[] assetCategoryIds,
1800            String[] assetTagNames)
1801        throws PortalException, SystemException {
1802
1803        // Get the earliest display date and latest expiration date among
1804        // all article versions
1805
1806        Date[] dateInterval = getDateInterval(
1807            article.getGroupId(), article.getArticleId(),
1808            article.getDisplayDate(), article.getExpirationDate());
1809
1810        Date displayDate = dateInterval[0];
1811        Date expirationDate = dateInterval[1];
1812
1813        boolean visible = article.isApproved();
1814
1815        if (!visible &&
1816            (article.getVersion() != JournalArticleConstants.DEFAULT_VERSION)) {
1817
1818            int approvedArticlesCount =
1819                journalArticlePersistence.countByG_A_S(
1820                    article.getGroupId(), article.getArticleId(),
1821                    StatusConstants.APPROVED);
1822
1823            if (approvedArticlesCount > 0) {
1824                visible = true;
1825            }
1826        }
1827
1828        assetEntryLocalService.updateEntry(
1829            userId, article.getGroupId(), JournalArticle.class.getName(),
1830            article.getResourcePrimKey(), assetCategoryIds, assetTagNames,
1831            visible, null, null, displayDate, expirationDate,
1832            ContentTypes.TEXT_HTML, article.getTitle(),
1833            article.getDescription(), null, null, 0, 0, null, false);
1834    }
1835
1836    public JournalArticle updateContent(
1837            long groupId, String articleId, double version, String content)
1838        throws PortalException, SystemException {
1839
1840        JournalArticle article = journalArticlePersistence.findByG_A_V(
1841            groupId, articleId, version);
1842
1843        article.setContent(content);
1844
1845        journalArticlePersistence.update(article, false);
1846
1847        return article;
1848    }
1849
1850    public JournalArticle updateStatus(
1851            long userId, JournalArticle article, int status, String articleURL,
1852            ServiceContext serviceContext)
1853        throws PortalException, SystemException {
1854
1855        User user = userPersistence.findByPrimaryKey(userId);
1856        Date now = new Date();
1857
1858        int oldStatus = article.getStatus();
1859
1860        // Article
1861
1862        article.setModifiedDate(now);
1863        article.setStatus(status);
1864        article.setStatusByUserId(user.getUserId());
1865        article.setStatusByUserName(user.getFullName());
1866        article.setStatusDate(now);
1867
1868        if ((article.getExpirationDate() != null) &&
1869            (article.getExpirationDate().before(now))) {
1870
1871            article.setExpirationDate(null);
1872        }
1873
1874        journalArticlePersistence.update(article, false);
1875
1876        if (isLatestVersion(
1877                article.getGroupId(), article.getArticleId(),
1878                article.getVersion())) {
1879
1880            if (status == StatusConstants.APPROVED) {
1881
1882                // Asset
1883
1884                assetEntryLocalService.updateVisible(
1885                    JournalArticle.class.getName(),
1886                    article.getResourcePrimKey(), true);
1887
1888                // Expando
1889
1890                ExpandoBridge expandoBridge = article.getExpandoBridge();
1891
1892                expandoBridge.setAttributes(serviceContext);
1893
1894                // Indexer
1895
1896                Indexer indexer = IndexerRegistryUtil.getIndexer(
1897                    JournalArticle.class);
1898
1899                indexer.reindex(article);
1900            }
1901            else {
1902
1903                // Asset
1904
1905                assetEntryLocalService.updateVisible(
1906                    JournalArticle.class.getName(),
1907                    article.getResourcePrimKey(), false);
1908
1909                // Indexer
1910
1911                if (article.isIndexable()) {
1912                    Indexer indexer = IndexerRegistryUtil.getIndexer(
1913                        JournalArticle.class);
1914
1915                    indexer.delete(article);
1916                }
1917            }
1918        }
1919
1920        // Email
1921
1922        if ((oldStatus == StatusConstants.PENDING) &&
1923            ((status == StatusConstants.APPROVED) ||
1924             (status == StatusConstants.DENIED))) {
1925
1926            PortletPreferences preferences =
1927                ServiceContextUtil.getPortletPreferences(serviceContext);
1928
1929            try {
1930                String msg = "granted";
1931
1932                if (status == StatusConstants.DENIED) {
1933                    msg = "denied";
1934                }
1935
1936                sendEmail(article, articleURL, preferences, msg);
1937            }
1938            catch (IOException ioe) {
1939                throw new SystemException(ioe);
1940            }
1941        }
1942
1943        // Subscriptions
1944
1945        notifySubscribers(article, serviceContext);
1946
1947        return article;
1948    }
1949
1950    public JournalArticle updateStatus(
1951            long userId, long classPK, int status,
1952            ServiceContext serviceContext)
1953        throws PortalException, SystemException {
1954
1955        JournalArticle article = getArticle(classPK);
1956
1957        return updateStatus(userId, article, status, null, serviceContext);
1958    }
1959
1960    public JournalArticle updateStatus(
1961            long userId, long groupId, String articleId, double version,
1962            int status, String articleURL, ServiceContext serviceContext)
1963        throws PortalException, SystemException {
1964
1965        JournalArticle article = journalArticlePersistence.findByG_A_V(
1966            groupId, articleId, version);
1967
1968        return updateStatus(
1969            userId, article, status, articleURL, serviceContext);
1970    }
1971
1972    protected void checkStructure(Document contentDoc, Element root)
1973        throws PortalException {
1974
1975        for (Element el : root.elements()) {
1976            checkStructureField(el, contentDoc);
1977
1978            checkStructure(contentDoc, el);
1979        }
1980    }
1981
1982    protected void checkStructure(JournalArticle article)
1983        throws DocumentException, PortalException, SystemException {
1984
1985        JournalStructure structure = journalStructurePersistence.findByG_S(
1986            article.getGroupId(), article.getStructureId());
1987
1988        String content = GetterUtil.getString(article.getContent());
1989
1990        Document contentDoc = SAXReaderUtil.read(content);
1991        Document xsdDoc = SAXReaderUtil.read(structure.getXsd());
1992
1993        try {
1994            checkStructure(contentDoc, xsdDoc.getRootElement());
1995        }
1996        catch (StructureXsdException sxsde) {
1997            long groupId = article.getGroupId();
1998            String articleId = article.getArticleId();
1999            double version = article.getVersion();
2000
2001            if (_log.isWarnEnabled()) {
2002                _log.warn(
2003                    "Article {groupId=" + groupId + ", articleId=" +
2004                        articleId + ", version=" + version +
2005                            "} has content that does not match its " +
2006                                "structure: " + sxsde.getMessage());
2007            }
2008        }
2009    }
2010
2011    protected void checkStructureField(Element el, Document contentDoc)
2012        throws PortalException {
2013
2014        StringBuilder elPath = new StringBuilder();
2015
2016        elPath.append(el.attributeValue("name"));
2017
2018        Element elParent = el.getParent();
2019
2020        for (;;) {
2021            if ((elParent == null) ||
2022                (elParent.getName().equals("root"))) {
2023
2024                break;
2025            }
2026
2027            elPath.insert(
2028                0, elParent.attributeValue("name") + StringPool.COMMA);
2029
2030            elParent = elParent.getParent();
2031        }
2032
2033        String[] elPathNames = StringUtil.split(elPath.toString());
2034
2035        Element contentEl = contentDoc.getRootElement();
2036
2037        for (int i = 0; i < elPathNames.length; i++) {
2038            boolean foundEl = false;
2039
2040            for (Element tempEl : contentEl.elements()) {
2041                if (elPathNames[i].equals(
2042                        tempEl.attributeValue("name", StringPool.BLANK))) {
2043
2044                    contentEl = tempEl;
2045                    foundEl = true;
2046
2047                    break;
2048                }
2049            }
2050
2051            if (!foundEl) {
2052                String elType = contentEl.attributeValue(
2053                    "type", StringPool.BLANK);
2054
2055                if (!elType.equals("list") && !elType.equals("multi-list")) {
2056                    throw new StructureXsdException(elPath.toString());
2057                }
2058
2059                break;
2060            }
2061        }
2062    }
2063
2064    protected void copyArticleImages(
2065            JournalArticle oldArticle, JournalArticle newArticle)
2066        throws Exception {
2067
2068        Document contentDoc = SAXReaderUtil.read(oldArticle.getContent());
2069
2070        XPath xpathSelector = SAXReaderUtil.createXPath(
2071            "//dynamic-element[@type='image']");
2072
2073        List<Node> imageNodes = xpathSelector.selectNodes(contentDoc);
2074
2075        for (Node imageNode : imageNodes) {
2076            Element imageEl = (Element)imageNode;
2077
2078            String instanceId = imageEl.attributeValue("instance-id");
2079            String name = imageEl.attributeValue("name");
2080
2081            List<Element> dynamicContentEls = imageEl.elements(
2082                "dynamic-content");
2083
2084            for (Element dynamicContentEl : dynamicContentEls) {
2085                long imageId = GetterUtil.getLong(
2086                    dynamicContentEl.attributeValue("id"));
2087                String languageId = dynamicContentEl.attributeValue(
2088                    "language-id");
2089
2090                Image oldImage = null;
2091
2092                try {
2093                    oldImage = imageLocalService.getImage(imageId);
2094                }
2095                catch (NoSuchImageException nsie) {
2096                    continue;
2097                }
2098
2099                imageId = journalArticleImageLocalService.getArticleImageId(
2100                    newArticle.getGroupId(), newArticle.getArticleId(),
2101                    newArticle.getVersion(), instanceId, name, languageId);
2102
2103                imageLocalService.updateImage(imageId, oldImage.getTextObj());
2104
2105                String elContent =
2106                    "/image/journal/article?img_id=" + imageId + "&t=" +
2107                        ImageServletTokenUtil.getToken(imageId);
2108
2109                dynamicContentEl.setText(elContent);
2110                dynamicContentEl.addAttribute("id", String.valueOf(imageId));
2111            }
2112        }
2113
2114        newArticle.setContent(contentDoc.formattedString());
2115    }
2116
2117    protected void format(
2118            long groupId, String articleId, double version,
2119            boolean incrementVersion, Element root, Map<String, byte[]> images)
2120        throws PortalException, SystemException {
2121
2122        for (Element el : root.elements()) {
2123            String elInstanceId = el.attributeValue(
2124                "instance-id", StringPool.BLANK);
2125            String elName = el.attributeValue("name", StringPool.BLANK);
2126            String elType = el.attributeValue("type", StringPool.BLANK);
2127
2128            if (elType.equals("image")) {
2129                formatImage(
2130                    groupId, articleId, version, incrementVersion, el,
2131                    elInstanceId, elName, images);
2132            }
2133            /*else if (elType.equals("text_area")) {
2134                Element dynamicContent = el.element("dynamic-content");
2135
2136                String text = dynamicContent.getText();
2137
2138                // LEP-1594
2139
2140                try {
2141                    text = ParserUtils.trimTags(
2142                        text, new String[] {"script"}, false, true);
2143                }
2144                catch (ParserException pe) {
2145                    text = pe.getLocalizedMessage();
2146                }
2147                catch (UnsupportedEncodingException uee) {
2148                    text = uee.getLocalizedMessage();
2149                }
2150
2151                dynamicContent.setText(text);
2152            }*/
2153
2154            format(groupId, articleId, version, incrementVersion, el, images);
2155        }
2156    }
2157
2158    protected String format(
2159            long groupId, String articleId, double version,
2160            boolean incrementVersion, String content, String structureId,
2161            Map<String, byte[]> images)
2162        throws PortalException, SystemException {
2163
2164        if (Validator.isNotNull(structureId)) {
2165            Document doc = null;
2166
2167            try {
2168                doc = SAXReaderUtil.read(content);
2169
2170                Element root = doc.getRootElement();
2171
2172                format(
2173                    groupId, articleId, version, incrementVersion, root,
2174                    images);
2175
2176                content = JournalUtil.formatXML(doc);
2177            }
2178            catch (DocumentException de) {
2179                _log.error(de);
2180            }
2181            catch (IOException ioe) {
2182                _log.error(ioe);
2183            }
2184        }
2185
2186        content = HtmlUtil.replaceMsWordCharacters(content);
2187
2188        return content;
2189    }
2190
2191    protected void formatImage(
2192            long groupId, String articleId, double version,
2193            boolean incrementVersion, Element el, String elInstanceId,
2194            String elName, Map<String, byte[]> images)
2195        throws PortalException, SystemException {
2196
2197        List<Element> imageContents = el.elements("dynamic-content");
2198
2199        for (Element dynamicContent : imageContents) {
2200            String elLanguage = dynamicContent.attributeValue(
2201                "language-id", StringPool.BLANK);
2202
2203            if (!elLanguage.equals(StringPool.BLANK)) {
2204                elLanguage = "_" + elLanguage;
2205            }
2206
2207            long imageId =
2208                journalArticleImageLocalService.getArticleImageId(
2209                    groupId, articleId, version, elInstanceId, elName,
2210                    elLanguage);
2211
2212            double oldVersion = MathUtil.format(version - 0.1, 1, 1);
2213
2214            long oldImageId = 0;
2215
2216            if ((oldVersion >= 1) && incrementVersion) {
2217                oldImageId =
2218                    journalArticleImageLocalService.getArticleImageId(
2219                        groupId, articleId, oldVersion, elInstanceId, elName,
2220                        elLanguage);
2221            }
2222
2223            String elContent =
2224                "/image/journal/article?img_id=" + imageId + "&t=" +
2225                    ImageServletTokenUtil.getToken(imageId);
2226
2227            if (dynamicContent.getText().equals("delete")) {
2228                dynamicContent.setText(StringPool.BLANK);
2229
2230                imageLocalService.deleteImage(imageId);
2231
2232                String defaultElLanguage = "";
2233
2234                if (!Validator.isNotNull(elLanguage)) {
2235                    defaultElLanguage =
2236                        "_" + LocaleUtil.toLanguageId(LocaleUtil.getDefault());
2237                }
2238
2239                long defaultImageId =
2240                    journalArticleImageLocalService.getArticleImageId(
2241                        groupId, articleId, version, elInstanceId, elName,
2242                        defaultElLanguage);
2243
2244                imageLocalService.deleteImage(defaultImageId);
2245
2246                continue;
2247            }
2248
2249            byte[] bytes = images.get(elInstanceId + "_" + elName + elLanguage);
2250
2251            if (bytes != null && (bytes.length > 0)) {
2252                dynamicContent.setText(elContent);
2253                dynamicContent.addAttribute("id", String.valueOf(imageId));
2254
2255                imageLocalService.updateImage(imageId, bytes);
2256
2257                continue;
2258            }
2259
2260            if ((version > JournalArticleConstants.DEFAULT_VERSION) &&
2261                (incrementVersion)) {
2262
2263                Image oldImage = null;
2264
2265                if (oldImageId > 0) {
2266                    oldImage = imageLocalService.getImage(oldImageId);
2267                }
2268
2269                if (oldImage != null) {
2270                    dynamicContent.setText(elContent);
2271                    dynamicContent.addAttribute("id", String.valueOf(imageId));
2272
2273                    bytes = oldImage.getTextObj();
2274
2275                    imageLocalService.updateImage(imageId, bytes);
2276                }
2277
2278                continue;
2279            }
2280
2281            Image image = imageLocalService.getImage(imageId);
2282
2283            if (image != null) {
2284                dynamicContent.setText(elContent);
2285                dynamicContent.addAttribute("id", String.valueOf(imageId));
2286
2287                continue;
2288            }
2289
2290            long contentImageId = GetterUtil.getLong(HttpUtil.getParameter(
2291                dynamicContent.getText(), "img_id"));
2292
2293            if (contentImageId <= 0) {
2294                contentImageId = GetterUtil.getLong(HttpUtil.getParameter(
2295                    dynamicContent.getText(), "img_id", false));
2296            }
2297
2298            if (contentImageId > 0) {
2299                image = imageLocalService.getImage(contentImageId);
2300
2301                if (image != null) {
2302                    dynamicContent.addAttribute(
2303                        "id", String.valueOf(contentImageId));
2304
2305                    continue;
2306                }
2307            }
2308
2309            String defaultElLanguage = "";
2310
2311            if (!Validator.isNotNull(elLanguage)) {
2312                defaultElLanguage =
2313                    "_" + LocaleUtil.toLanguageId(LocaleUtil.getDefault());
2314            }
2315
2316            long defaultImageId =
2317                journalArticleImageLocalService.getArticleImageId(
2318                    groupId, articleId, version, elInstanceId, elName,
2319                    defaultElLanguage);
2320
2321            Image defaultImage = imageLocalService.getImage(defaultImageId);
2322
2323            if (defaultImage != null) {
2324                dynamicContent.setText(elContent);
2325                dynamicContent.addAttribute(
2326                    "id", String.valueOf(defaultImageId));
2327
2328                bytes = defaultImage.getTextObj();
2329
2330                imageLocalService.updateImage(defaultImageId, bytes);
2331
2332                continue;
2333            }
2334
2335            dynamicContent.setText(StringPool.BLANK);
2336        }
2337    }
2338
2339    protected Date[] getDateInterval(
2340            long groupId, String articleId, Date earliestDisplayDate,
2341            Date latestExpirationDate)
2342        throws SystemException {
2343
2344        Date[] dateInterval = new Date[2];
2345
2346        List<JournalArticle> articles = journalArticlePersistence.findByG_A_S(
2347            groupId, articleId, StatusConstants.APPROVED);
2348
2349        boolean expiringArticle = true;
2350
2351        if (latestExpirationDate == null) {
2352            expiringArticle = false;
2353        }
2354
2355        for (JournalArticle article : articles) {
2356            if ((earliestDisplayDate == null) ||
2357                ((article.getDisplayDate() != null) &&
2358                 earliestDisplayDate.after(article.getDisplayDate()))) {
2359
2360                earliestDisplayDate = article.getDisplayDate();
2361            }
2362
2363            if (expiringArticle &&
2364                ((latestExpirationDate == null) ||
2365                 ((article.getExpirationDate() != null) &&
2366                  latestExpirationDate.before(article.getExpirationDate())))) {
2367
2368                latestExpirationDate = article.getExpirationDate();
2369            }
2370
2371            if (expiringArticle && (article.getExpirationDate() == null)) {
2372                latestExpirationDate = null;
2373                expiringArticle = false;
2374            }
2375        }
2376
2377        dateInterval[0] = earliestDisplayDate;
2378        dateInterval[1] = latestExpirationDate;
2379
2380        return dateInterval;
2381    }
2382
2383    protected String getUniqueUrlTitle(
2384            long id, long groupId, String articleId, String title)
2385        throws PortalException, SystemException {
2386
2387        String urlTitle = JournalUtil.getUrlTitle(id, title);
2388
2389        String newUrlTitle = urlTitle;
2390
2391        for (int i = 1;; i++) {
2392            JournalArticle article = null;
2393
2394            try {
2395                article = getArticleByUrlTitle(groupId, newUrlTitle);
2396            }
2397            catch (NoSuchArticleException nsae) {
2398            }
2399
2400            if ((article == null) || article.getArticleId().equals(articleId)) {
2401                break;
2402            }
2403            else {
2404                newUrlTitle = urlTitle + StringPool.DASH + i;
2405            }
2406        }
2407
2408        return newUrlTitle;
2409    }
2410
2411    protected void notifySubscribers(
2412            JournalArticle article, ServiceContext serviceContext)
2413        throws PortalException, SystemException {
2414
2415        if (!article.isApproved()) {
2416            return;
2417        }
2418
2419        String articleURL = PortalUtil.getControlPanelFullURL(
2420            serviceContext.getScopeGroupId(), PortletKeys.JOURNAL, null);
2421
2422        if (Validator.isNull(articleURL)) {
2423            return;
2424        }
2425
2426        PortletPreferences preferences =
2427            ServiceContextUtil.getPortletPreferences(serviceContext);
2428
2429        if (preferences == null) {
2430            long ownerId = article.getGroupId();
2431            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
2432            long plid = PortletKeys.PREFS_PLID_SHARED;
2433            String portletId = PortletKeys.JOURNAL;
2434            String defaultPreferences = null;
2435
2436            preferences = portletPreferencesLocalService.getPreferences(
2437                article.getCompanyId(), ownerId, ownerType, plid, portletId,
2438                defaultPreferences);
2439        }
2440
2441        if ((article.getVersion() == 1.0) &&
2442            JournalUtil.getEmailArticleAddedEnabled(preferences)) {
2443        }
2444        else if ((article.getVersion() != 1.0) &&
2445                 JournalUtil.getEmailArticleUpdatedEnabled(preferences)) {
2446        }
2447        else {
2448            return;
2449        }
2450
2451        Company company = companyPersistence.findByPrimaryKey(
2452            article.getCompanyId());
2453
2454        String emailAddress = StringPool.BLANK;
2455        String fullName = article.getUserName();
2456
2457        try {
2458            User user = userPersistence.findByPrimaryKey(article.getUserId());
2459
2460            emailAddress = user.getEmailAddress();
2461            fullName = user.getFullName();
2462        }
2463        catch (NoSuchUserException nsue) {
2464        }
2465
2466        String portletName = PortalUtil.getPortletTitle(
2467            PortletKeys.JOURNAL, LocaleUtil.getDefault());
2468
2469        String fromName = JournalUtil.getEmailFromName(preferences);
2470        String fromAddress = JournalUtil.getEmailFromAddress(preferences);
2471
2472        fromName = StringUtil.replace(
2473            fromName,
2474            new String[] {
2475                "[$ARTICLE_ID$]",
2476                "[$ARTICLE_TITLE$]",
2477                "[$ARTICLE_USER_ADDRESS$]",
2478                "[$ARTICLE_USER_NAME$]",
2479                "[$ARTICLE_VERSION$]",
2480                "[$FROM_ADDRESS$]",
2481                "[$FROM_NAME$]",
2482                "[$PORTAL_URL$]",
2483                "[$PORTLET_NAME$]",
2484            },
2485            new String[] {
2486                article.getArticleId(),
2487                article.getTitle(),
2488                emailAddress,
2489                fullName,
2490                String.valueOf(article.getVersion()),
2491                fromAddress,
2492                fromName,
2493                company.getVirtualHost(),
2494                portletName,
2495            });
2496
2497        fromAddress = StringUtil.replace(
2498            fromAddress,
2499            new String[] {
2500                "[$ARTICLE_ID$]",
2501                "[$ARTICLE_TITLE$]",
2502                "[$ARTICLE_USER_ADDRESS$]",
2503                "[$ARTICLE_USER_NAME$]",
2504                "[$ARTICLE_VERSION$]",
2505                "[$FROM_ADDRESS$]",
2506                "[$FROM_NAME$]",
2507                "[$PORTAL_URL$]",
2508                "[$PORTLET_NAME$]",
2509            },
2510            new String[] {
2511                article.getArticleId(),
2512                article.getTitle(),
2513                emailAddress,
2514                fullName,
2515                String.valueOf(article.getVersion()),
2516                fromAddress,
2517                fromName,
2518                company.getVirtualHost(),
2519                portletName,
2520            });
2521
2522        String subject = null;
2523        String body = null;
2524
2525        if (article.getVersion() == 1.0) {
2526            subject = JournalUtil.getEmailArticleAddedSubject(preferences);
2527            body = JournalUtil.getEmailArticleAddedBody(preferences);
2528        }
2529        else {
2530            subject = JournalUtil.getEmailArticleUpdatedSubject(preferences);
2531            body = JournalUtil.getEmailArticleUpdatedBody(preferences);
2532        }
2533
2534        subject = StringUtil.replace(
2535            subject,
2536            new String[] {
2537                "[$ARTICLE_ID$]",
2538                "[$ARTICLE_TITLE$]",
2539                "[$ARTICLE_URL$]",
2540                "[$ARTICLE_USER_ADDRESS$]",
2541                "[$ARTICLE_USER_NAME$]",
2542                "[$ARTICLE_VERSION$]",
2543                "[$FROM_ADDRESS$]",
2544                "[$FROM_NAME$]",
2545                "[$PORTAL_URL$]",
2546                "[$PORTLET_NAME$]",
2547            },
2548            new String[] {
2549                article.getArticleId(),
2550                article.getTitle(),
2551                articleURL,
2552                emailAddress,
2553                fullName,
2554                String.valueOf(article.getVersion()),
2555                fromAddress,
2556                fromName,
2557                company.getVirtualHost(),
2558                portletName,
2559            });
2560
2561        body = StringUtil.replace(
2562            body,
2563            new String[] {
2564                "[$ARTICLE_ID$]",
2565                "[$ARTICLE_TITLE$]",
2566                "[$ARTICLE_URL$]",
2567                "[$ARTICLE_USER_ADDRESS$]",
2568                "[$ARTICLE_USER_NAME$]",
2569                "[$ARTICLE_VERSION$]",
2570                "[$FROM_ADDRESS$]",
2571                "[$FROM_NAME$]",
2572                "[$PORTAL_URL$]",
2573                "[$PORTLET_NAME$]",
2574            },
2575            new String[] {
2576                article.getArticleId(),
2577                article.getTitle(),
2578                articleURL,
2579                emailAddress,
2580                fullName,
2581                String.valueOf(article.getVersion()),
2582                fromAddress,
2583                fromName,
2584                company.getVirtualHost(),
2585                portletName,
2586            });
2587
2588        Message message = new Message();
2589
2590        message.put("companyId", article.getCompanyId());
2591        message.put("userId", article.getUserId());
2592        message.put("groupId", article.getGroupId());
2593        message.put("articleId", article.getArticleId());
2594        message.put("fromName", fromName);
2595        message.put("fromAddress", fromAddress);
2596        message.put("subject", subject);
2597        message.put("body", body);
2598        message.put("replyToAddress", fromAddress);
2599        message.put(
2600            "mailId",
2601            JournalUtil.getMailId(company.getMx(), article.getArticleId()));
2602        message.put("htmlFormat", Boolean.TRUE);
2603
2604        MessageBusUtil.sendMessage(DestinationNames.JOURNAL, message);
2605    }
2606
2607    protected void saveImages(
2608            boolean smallImage, long smallImageId, File smallFile,
2609            byte[] smallBytes)
2610        throws PortalException, SystemException {
2611
2612        if (smallImage) {
2613            if ((smallFile != null) && (smallBytes != null)) {
2614                imageLocalService.updateImage(smallImageId, smallBytes);
2615            }
2616        }
2617        else {
2618            imageLocalService.deleteImage(smallImageId);
2619        }
2620    }
2621
2622    protected void sendEmail(
2623            JournalArticle article, String articleURL,
2624            PortletPreferences preferences, String emailType)
2625        throws IOException, PortalException, SystemException {
2626
2627        if (preferences == null) {
2628            return;
2629        }
2630        else if (emailType.equals("denied") &&
2631            JournalUtil.getEmailArticleApprovalDeniedEnabled(preferences)) {
2632        }
2633        else if (emailType.equals("granted") &&
2634                 JournalUtil.getEmailArticleApprovalGrantedEnabled(
2635                    preferences)) {
2636        }
2637        else if (emailType.equals("requested") &&
2638                 JournalUtil.getEmailArticleApprovalRequestedEnabled(
2639                    preferences)) {
2640        }
2641        else if (emailType.equals("review") &&
2642                 JournalUtil.getEmailArticleReviewEnabled(preferences)) {
2643        }
2644        else {
2645            return;
2646        }
2647
2648        Company company = companyPersistence.findByPrimaryKey(
2649            article.getCompanyId());
2650
2651        User user = userPersistence.findByPrimaryKey(article.getUserId());
2652
2653        articleURL +=
2654            "&groupId=" + article.getGroupId() + "&articleId=" +
2655                article.getArticleId() + "&version=" + article.getVersion();
2656
2657        String portletName = PortalUtil.getPortletTitle(
2658            PortletKeys.JOURNAL, user);
2659
2660        String fromName = JournalUtil.getEmailFromName(preferences);
2661        String fromAddress = JournalUtil.getEmailFromAddress(preferences);
2662
2663        String toName = user.getFullName();
2664        String toAddress = user.getEmailAddress();
2665
2666        if (emailType.equals("requested") ||
2667            emailType.equals("review")) {
2668
2669            String tempToName = fromName;
2670            String tempToAddress = fromAddress;
2671
2672            fromName = toName;
2673            fromAddress = toAddress;
2674
2675            toName = tempToName;
2676            toAddress = tempToAddress;
2677        }
2678
2679        String subject = null;
2680        String body = null;
2681
2682        if (emailType.equals("denied")) {
2683            subject =
2684                JournalUtil.getEmailArticleApprovalDeniedSubject(preferences);
2685            body = JournalUtil.getEmailArticleApprovalDeniedBody(preferences);
2686        }
2687        else if (emailType.equals("granted")) {
2688            subject =
2689                JournalUtil.getEmailArticleApprovalGrantedSubject(preferences);
2690            body = JournalUtil.getEmailArticleApprovalGrantedBody(preferences);
2691        }
2692        else if (emailType.equals("requested")) {
2693            subject =
2694                JournalUtil.getEmailArticleApprovalRequestedSubject(
2695                preferences);
2696            body = JournalUtil.getEmailArticleApprovalRequestedBody(
2697                preferences);
2698        }
2699        else if (emailType.equals("review")) {
2700            subject = JournalUtil.getEmailArticleReviewSubject(preferences);
2701            body = JournalUtil.getEmailArticleReviewBody(preferences);
2702        }
2703
2704        subject = StringUtil.replace(
2705            subject,
2706            new String[] {
2707                "[$ARTICLE_ID$]",
2708                "[$ARTICLE_TITLE$]",
2709                "[$ARTICLE_URL$]",
2710                "[$ARTICLE_USER_NAME$]",
2711                "[$ARTICLE_VERSION$]",
2712                "[$FROM_ADDRESS$]",
2713                "[$FROM_NAME$]",
2714                "[$PORTAL_URL$]",
2715                "[$PORTLET_NAME$]",
2716                "[$TO_ADDRESS$]",
2717                "[$TO_NAME$]"
2718            },
2719            new String[] {
2720                article.getArticleId(),
2721                article.getTitle(),
2722                articleURL,
2723                article.getUserName(),
2724                String.valueOf(article.getVersion()),
2725                fromAddress,
2726                fromName,
2727                company.getVirtualHost(),
2728                portletName,
2729                toAddress,
2730                toName,
2731            });
2732
2733        body = StringUtil.replace(
2734            body,
2735            new String[] {
2736                "[$ARTICLE_ID$]",
2737                "[$ARTICLE_TITLE$]",
2738                "[$ARTICLE_URL$]",
2739                "[$ARTICLE_USER_NAME$]",
2740                "[$ARTICLE_VERSION$]",
2741                "[$FROM_ADDRESS$]",
2742                "[$FROM_NAME$]",
2743                "[$PORTAL_URL$]",
2744                "[$PORTLET_NAME$]",
2745                "[$TO_ADDRESS$]",
2746                "[$TO_NAME$]"
2747            },
2748            new String[] {
2749                article.getArticleId(),
2750                article.getTitle(),
2751                articleURL,
2752                article.getUserName(),
2753                String.valueOf(article.getVersion()),
2754                fromAddress,
2755                fromName,
2756                company.getVirtualHost(),
2757                portletName,
2758                toAddress,
2759                toName,
2760            });
2761
2762        InternetAddress from = new InternetAddress(fromAddress, fromName);
2763
2764        InternetAddress to = new InternetAddress(toAddress, toName);
2765
2766        MailMessage message = new MailMessage(from, to, subject, body, true);
2767
2768        mailService.sendEmail(message);
2769    }
2770
2771    protected void updateUrlTitles(
2772            long groupId, String articleId, String urlTitle)
2773        throws SystemException {
2774
2775        List<JournalArticle> articles = journalArticlePersistence.findByG_A(
2776            groupId, articleId);
2777
2778        for (JournalArticle article : articles) {
2779            if (!article.getUrlTitle().equals(urlTitle)) {
2780                article.setUrlTitle(urlTitle);
2781
2782                journalArticlePersistence.update(article, false);
2783            }
2784        }
2785    }
2786
2787    protected void validate(
2788            long groupId, String articleId, boolean autoArticleId,
2789            double version, String title, String content, String type,
2790            String structureId, String templateId, boolean smallImage,
2791            String smallImageURL, File smallFile, byte[] smallBytes)
2792        throws PortalException, SystemException {
2793
2794        if (!autoArticleId) {
2795            validate(articleId);
2796
2797            JournalArticle article = journalArticlePersistence.fetchByG_A_V(
2798                groupId, articleId, version);
2799
2800            if (article != null) {
2801                throw new DuplicateArticleIdException();
2802            }
2803        }
2804
2805        validate(
2806            groupId, title, content, type, structureId, templateId,
2807            smallImage, smallImageURL, smallFile, smallBytes);
2808    }
2809
2810    protected void validate(
2811            long groupId, String title, String content, String type,
2812            String structureId, String templateId, boolean smallImage,
2813            String smallImageURL, File smallFile, byte[] smallBytes)
2814        throws PortalException, SystemException {
2815
2816        if (Validator.isNull(title)) {
2817            throw new ArticleTitleException();
2818        }
2819        else if (Validator.isNull(content)) {
2820            throw new ArticleContentException();
2821        }
2822        else if (Validator.isNull(type)) {
2823            throw new ArticleTypeException();
2824        }
2825
2826        if (Validator.isNotNull(structureId)) {
2827            journalStructurePersistence.findByG_S(groupId, structureId);
2828
2829            JournalTemplate template = journalTemplatePersistence.findByG_T(
2830                groupId, templateId);
2831
2832            if (!template.getStructureId().equals(structureId)) {
2833                throw new NoSuchTemplateException();
2834            }
2835        }
2836
2837        String[] imageExtensions = PrefsPropsUtil.getStringArray(
2838            PropsKeys.JOURNAL_IMAGE_EXTENSIONS, StringPool.COMMA);
2839
2840        if (smallImage && Validator.isNull(smallImageURL) &&
2841            smallFile != null && smallBytes != null) {
2842
2843            String smallImageName = smallFile.getName();
2844
2845            if (smallImageName != null) {
2846                boolean validSmallImageExtension = false;
2847
2848                for (int i = 0; i < imageExtensions.length; i++) {
2849                    if (StringPool.STAR.equals(imageExtensions[i]) ||
2850                        StringUtil.endsWith(
2851                            smallImageName, imageExtensions[i])) {
2852
2853                        validSmallImageExtension = true;
2854
2855                        break;
2856                    }
2857                }
2858
2859                if (!validSmallImageExtension) {
2860                    throw new ArticleSmallImageNameException(smallImageName);
2861                }
2862            }
2863
2864            long smallImageMaxSize = PrefsPropsUtil.getLong(
2865                PropsKeys.JOURNAL_IMAGE_SMALL_MAX_SIZE);
2866
2867            if ((smallImageMaxSize > 0) &&
2868                ((smallBytes == null) ||
2869                    (smallBytes.length > smallImageMaxSize))) {
2870
2871                throw new ArticleSmallImageSizeException();
2872            }
2873        }
2874    }
2875
2876    protected void validate(String articleId) throws PortalException {
2877        if ((Validator.isNull(articleId)) ||
2878            (articleId.indexOf(StringPool.SPACE) != -1)) {
2879
2880            throw new ArticleIdException();
2881        }
2882    }
2883
2884}