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