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