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