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