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