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