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