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