1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.journal.util;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.util.GetterUtil;
28  import com.liferay.portal.kernel.util.HttpUtil;
29  import com.liferay.portal.kernel.util.LocaleUtil;
30  import com.liferay.portal.kernel.util.OrderByComparator;
31  import com.liferay.portal.kernel.util.PropertiesUtil;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.util.Time;
35  import com.liferay.portal.kernel.util.Validator;
36  import com.liferay.portal.model.Contact;
37  import com.liferay.portal.model.User;
38  import com.liferay.portal.service.ImageLocalServiceUtil;
39  import com.liferay.portal.service.UserLocalServiceUtil;
40  import com.liferay.portal.theme.ThemeDisplay;
41  import com.liferay.portal.util.ContentUtil;
42  import com.liferay.portal.util.PropsKeys;
43  import com.liferay.portal.util.PropsUtil;
44  import com.liferay.portal.util.WebKeys;
45  import com.liferay.portlet.journal.model.JournalArticle;
46  import com.liferay.portlet.journal.model.JournalStructure;
47  import com.liferay.portlet.journal.model.JournalTemplate;
48  import com.liferay.portlet.journal.model.impl.JournalStructureImpl;
49  import com.liferay.portlet.journal.model.impl.JournalTemplateImpl;
50  import com.liferay.portlet.journal.service.JournalTemplateLocalServiceUtil;
51  import com.liferay.portlet.journal.util.comparator.ArticleCreateDateComparator;
52  import com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator;
53  import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
54  import com.liferay.portlet.journal.util.comparator.ArticleModifiedDateComparator;
55  import com.liferay.portlet.journal.util.comparator.ArticleReviewDateComparator;
56  import com.liferay.portlet.journal.util.comparator.ArticleTitleComparator;
57  import com.liferay.util.FiniteUniqueStack;
58  import com.liferay.util.xml.XMLFormatter;
59  
60  import java.io.IOException;
61  import java.io.StringReader;
62  
63  import java.util.ArrayList;
64  import java.util.Date;
65  import java.util.HashMap;
66  import java.util.Iterator;
67  import java.util.List;
68  import java.util.Map;
69  import java.util.Stack;
70  
71  import javax.portlet.PortletPreferences;
72  import javax.portlet.PortletRequest;
73  import javax.portlet.PortletSession;
74  
75  import org.apache.commons.logging.Log;
76  import org.apache.commons.logging.LogFactory;
77  
78  import org.dom4j.Document;
79  import org.dom4j.DocumentException;
80  import org.dom4j.DocumentFactory;
81  import org.dom4j.DocumentHelper;
82  import org.dom4j.Element;
83  import org.dom4j.Node;
84  import org.dom4j.XPath;
85  import org.dom4j.io.SAXReader;
86  
87  /**
88   * <a href="JournalUtil.java.html"><b><i>View Source</i></b></a>
89   *
90   * @author Brian Wing Shun Chan
91   * @author Raymond Augé
92   *
93   */
94  public class JournalUtil {
95  
96      public static final int MAX_STACK_SIZE = 20;
97  
98      public static final String XML_INDENT = "  ";
99  
100     public static void addRecentArticle(
101         PortletRequest portletRequest, JournalArticle article) {
102 
103         if (article != null) {
104             Stack<JournalArticle> stack = getRecentArticles(portletRequest);
105 
106             stack.push(article);
107         }
108     }
109 
110     public static void addRecentStructure(
111         PortletRequest portletRequest, JournalStructure structure) {
112 
113         if (structure != null) {
114             Stack<JournalStructure> stack = getRecentStructures(portletRequest);
115 
116             stack.push(structure);
117         }
118     }
119 
120     public static void addRecentTemplate(
121         PortletRequest portletRequest, JournalTemplate template) {
122 
123         if (template != null) {
124             Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
125 
126             stack.push(template);
127         }
128     }
129 
130     public static void addReservedEl(
131         Element root, Map<String, String> tokens, String name, double value) {
132 
133         addReservedEl(root, tokens, name, String.valueOf(value));
134     }
135 
136     public static void addReservedEl(
137         Element root, Map<String, String> tokens, String name, Date value) {
138 
139         addReservedEl(root, tokens, name, Time.getRFC822(value));
140     }
141 
142     public static void addReservedEl(
143         Element root, Map<String, String> tokens, String name, String value) {
144 
145         // XML
146 
147         if (root != null) {
148             DocumentFactory docFactory = DocumentFactory.getInstance();
149 
150             Element dynamicEl = docFactory.createElement("dynamic-element");
151 
152             dynamicEl.add(docFactory.createAttribute(dynamicEl, "name", name));
153             dynamicEl.add(
154                 docFactory.createAttribute(dynamicEl, "type", "text"));
155 
156             Element dynamicContent =
157                 docFactory.createElement("dynamic-content");
158 
159             //dynamicContent.setText("<![CDATA[" + value + "]]>");
160             dynamicContent.setText(value);
161 
162             dynamicEl.add(dynamicContent);
163 
164             root.add(dynamicEl);
165         }
166 
167         // Tokens
168 
169         tokens.put(
170             StringUtil.replace(name, StringPool.DASH, StringPool.UNDERLINE),
171             value);
172     }
173 
174     public static void addAllReservedEls(
175         Element root, Map<String, String> tokens, JournalArticle article) {
176 
177         JournalUtil.addReservedEl(
178             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_ID,
179             article.getArticleId());
180 
181         JournalUtil.addReservedEl(
182             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_VERSION,
183             article.getVersion());
184 
185         JournalUtil.addReservedEl(
186             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_TITLE,
187             article.getTitle());
188 
189         JournalUtil.addReservedEl(
190             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_DESCRIPTION,
191             article.getDescription());
192 
193         JournalUtil.addReservedEl(
194             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_TYPE,
195             article.getType());
196 
197         JournalUtil.addReservedEl(
198             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_CREATE_DATE,
199             article.getCreateDate());
200 
201         JournalUtil.addReservedEl(
202             root, tokens,
203             JournalStructureImpl.RESERVED_ARTICLE_MODIFIED_DATE,
204             article.getModifiedDate());
205 
206         if (article.getDisplayDate() != null) {
207             JournalUtil.addReservedEl(
208                 root, tokens,
209                 JournalStructureImpl.RESERVED_ARTICLE_DISPLAY_DATE,
210                 article.getDisplayDate());
211         }
212 
213         JournalUtil.addReservedEl(
214             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_SMALL_IMAGE_URL,
215             article.getSmallImageURL());
216 
217         JournalUtil.addReservedEl(
218             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_ID,
219             String.valueOf(article.getUserId()));
220 
221         String userName = StringPool.BLANK;
222         String userEmailAddress = StringPool.BLANK;
223         String userComments = StringPool.BLANK;
224         String userJobTitle = StringPool.BLANK;
225 
226         User user = null;
227 
228         try {
229             user = UserLocalServiceUtil.getUserById(article.getUserId());
230 
231             userName = user.getFullName();
232             userEmailAddress = user.getEmailAddress();
233             userComments = user.getComments();
234 
235             Contact contact = user.getContact();
236 
237             if (contact != null) {
238                 userJobTitle = contact.getJobTitle();
239             }
240         }
241         catch (PortalException pe) {
242         }
243         catch (SystemException se) {
244         }
245 
246         JournalUtil.addReservedEl(
247             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_NAME,
248             userName);
249 
250         JournalUtil.addReservedEl(
251             root, tokens,
252             JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_EMAIL_ADDRESS,
253             userEmailAddress);
254 
255         JournalUtil.addReservedEl(
256             root, tokens,
257             JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_COMMENTS,
258             userComments);
259 
260         JournalUtil.addReservedEl(
261             root, tokens,
262             JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_JOB_TITLE,
263             userJobTitle);
264     }
265 
266     public static String formatVM(String vm) {
267         return vm;
268     }
269 
270     public static String formatXML(String xml)
271         throws DocumentException, IOException {
272 
273         // This is only supposed to format your xml, however, it will also
274         // unwantingly change &#169; and other characters like it into their
275         // respective readable versions
276 
277         xml = StringUtil.replace(xml, "&#", "[$SPECIAL_CHARACTER$]");
278 
279         xml = XMLFormatter.toString(xml, XML_INDENT);
280 
281         xml = StringUtil.replace(xml, "[$SPECIAL_CHARACTER$]", "&#");
282 
283         return xml;
284     }
285 
286     public static String formatXML(Document doc) throws IOException {
287         return XMLFormatter.toString(doc, XML_INDENT);
288     }
289 
290     public static OrderByComparator getArticleOrderByComparator(
291         String orderByCol, String orderByType) {
292 
293         boolean orderByAsc = false;
294 
295         if (orderByType.equals("asc")) {
296             orderByAsc = true;
297         }
298 
299         OrderByComparator orderByComparator = null;
300 
301         if (orderByCol.equals("create-date")) {
302             orderByComparator = new ArticleCreateDateComparator(orderByAsc);
303         }
304         else if (orderByCol.equals("display-date")) {
305             orderByComparator = new ArticleDisplayDateComparator(orderByAsc);
306         }
307         else if (orderByCol.equals("id")) {
308             orderByComparator = new ArticleIDComparator(orderByAsc);
309         }
310         else if (orderByCol.equals("modified-date")) {
311             orderByComparator = new ArticleModifiedDateComparator(orderByAsc);
312         }
313         else if (orderByCol.equals("review-date")) {
314             orderByComparator = new ArticleReviewDateComparator(orderByAsc);
315         }
316         else if (orderByCol.equals("title")) {
317             orderByComparator = new ArticleTitleComparator(orderByAsc);
318         }
319         else if (orderByCol.equals("version")) {
320             orderByComparator = new ArticleModifiedDateComparator(orderByAsc);
321         }
322 
323         return orderByComparator;
324     }
325 
326     public static String getEmailFromAddress(PortletPreferences prefs) {
327         String emailFromAddress = PropsUtil.get(
328             PropsKeys.JOURNAL_EMAIL_FROM_ADDRESS);
329 
330         return prefs.getValue("email-from-address", emailFromAddress);
331     }
332 
333     public static String getEmailFromName(PortletPreferences prefs) {
334         String emailFromName = PropsUtil.get(
335             PropsKeys.JOURNAL_EMAIL_FROM_NAME);
336 
337         return prefs.getValue("email-from-name", emailFromName);
338     }
339 
340     public static boolean getEmailArticleApprovalDeniedEnabled(
341         PortletPreferences prefs) {
342 
343         String emailArticleApprovalDeniedEnabled = prefs.getValue(
344             "email-article-approval-denied-enabled", StringPool.BLANK);
345 
346         if (Validator.isNotNull(emailArticleApprovalDeniedEnabled)) {
347             return GetterUtil.getBoolean(emailArticleApprovalDeniedEnabled);
348         }
349         else {
350             return GetterUtil.getBoolean(PropsUtil.get(
351                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_ENABLED));
352         }
353     }
354 
355     public static String getEmailArticleApprovalDeniedBody(
356         PortletPreferences prefs) {
357 
358         String emailArticleApprovalDeniedBody = prefs.getValue(
359             "email-article-approval-denied-body", StringPool.BLANK);
360 
361         if (Validator.isNotNull(emailArticleApprovalDeniedBody)) {
362             return emailArticleApprovalDeniedBody;
363         }
364         else {
365             return ContentUtil.get(PropsUtil.get(
366                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_BODY));
367         }
368     }
369 
370     public static String getEmailArticleApprovalDeniedSubject(
371         PortletPreferences prefs) {
372 
373         String emailArticleApprovalDeniedSubject = prefs.getValue(
374             "email-article-approval-denied-subject", StringPool.BLANK);
375 
376         if (Validator.isNotNull(emailArticleApprovalDeniedSubject)) {
377             return emailArticleApprovalDeniedSubject;
378         }
379         else {
380             return ContentUtil.get(PropsUtil.get(
381                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_SUBJECT));
382         }
383     }
384 
385     public static boolean getEmailArticleApprovalGrantedEnabled(
386         PortletPreferences prefs) {
387 
388         String emailArticleApprovalGrantedEnabled = prefs.getValue(
389             "email-article-approval-granted-enabled", StringPool.BLANK);
390 
391         if (Validator.isNotNull(emailArticleApprovalGrantedEnabled)) {
392             return GetterUtil.getBoolean(emailArticleApprovalGrantedEnabled);
393         }
394         else {
395             return GetterUtil.getBoolean(PropsUtil.get(
396                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_ENABLED));
397         }
398     }
399 
400     public static String getEmailArticleApprovalGrantedBody(
401         PortletPreferences prefs) {
402 
403         String emailArticleApprovalGrantedBody = prefs.getValue(
404             "email-article-approval-granted-body", StringPool.BLANK);
405 
406         if (Validator.isNotNull(emailArticleApprovalGrantedBody)) {
407             return emailArticleApprovalGrantedBody;
408         }
409         else {
410             return ContentUtil.get(PropsUtil.get(
411                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_BODY));
412         }
413     }
414 
415     public static String getEmailArticleApprovalGrantedSubject(
416         PortletPreferences prefs) {
417 
418         String emailArticleApprovalGrantedSubject = prefs.getValue(
419             "email-article-approval-granted-subject", StringPool.BLANK);
420 
421         if (Validator.isNotNull(emailArticleApprovalGrantedSubject)) {
422             return emailArticleApprovalGrantedSubject;
423         }
424         else {
425             return ContentUtil.get(PropsUtil.get(
426                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_SUBJECT));
427         }
428     }
429 
430     public static boolean getEmailArticleApprovalRequestedEnabled(
431         PortletPreferences prefs) {
432 
433         String emailArticleApprovalRequestedEnabled = prefs.getValue(
434             "email-article-approval-requested-enabled", StringPool.BLANK);
435 
436         if (Validator.isNotNull(emailArticleApprovalRequestedEnabled)) {
437             return GetterUtil.getBoolean(emailArticleApprovalRequestedEnabled);
438         }
439         else {
440             return GetterUtil.getBoolean(PropsUtil.get(
441                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_ENABLED));
442         }
443     }
444 
445     public static String getEmailArticleApprovalRequestedBody(
446         PortletPreferences prefs) {
447 
448         String emailArticleApprovalRequestedBody = prefs.getValue(
449             "email-article-approval-requested-body", StringPool.BLANK);
450 
451         if (Validator.isNotNull(emailArticleApprovalRequestedBody)) {
452             return emailArticleApprovalRequestedBody;
453         }
454         else {
455             return ContentUtil.get(PropsUtil.get(
456                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_BODY));
457         }
458     }
459 
460     public static String getEmailArticleApprovalRequestedSubject(
461         PortletPreferences prefs) {
462 
463         String emailArticleApprovalRequestedSubject = prefs.getValue(
464             "email-article-approval-requested-subject", StringPool.BLANK);
465 
466         if (Validator.isNotNull(emailArticleApprovalRequestedSubject)) {
467             return emailArticleApprovalRequestedSubject;
468         }
469         else {
470             return ContentUtil.get(PropsUtil.get(
471                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_SUBJECT));
472         }
473     }
474 
475     public static boolean getEmailArticleReviewEnabled(
476         PortletPreferences prefs) {
477 
478         String emailArticleReviewEnabled = prefs.getValue(
479             "email-article-review-enabled", StringPool.BLANK);
480 
481         if (Validator.isNotNull(emailArticleReviewEnabled)) {
482             return GetterUtil.getBoolean(emailArticleReviewEnabled);
483         }
484         else {
485             return GetterUtil.getBoolean(PropsUtil.get(
486                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_ENABLED));
487         }
488     }
489 
490     public static String getEmailArticleReviewBody(PortletPreferences prefs) {
491         String emailArticleReviewBody = prefs.getValue(
492             "email-article-review-body", StringPool.BLANK);
493 
494         if (Validator.isNotNull(emailArticleReviewBody)) {
495             return emailArticleReviewBody;
496         }
497         else {
498             return ContentUtil.get(PropsUtil.get(
499                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_BODY));
500         }
501     }
502 
503     public static String getEmailArticleReviewSubject(
504         PortletPreferences prefs) {
505 
506         String emailArticleReviewSubject = prefs.getValue(
507             "email-article-review-subject", StringPool.BLANK);
508 
509         if (Validator.isNotNull(emailArticleReviewSubject)) {
510             return emailArticleReviewSubject;
511         }
512         else {
513             return ContentUtil.get(PropsUtil.get(
514                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_SUBJECT));
515         }
516     }
517 
518     public static Stack<JournalArticle> getRecentArticles(
519         PortletRequest portletRequest) {
520 
521         PortletSession portletSession = portletRequest.getPortletSession();
522 
523         Stack<JournalArticle> recentArticles =
524             (Stack<JournalArticle>)portletSession.getAttribute(
525                 WebKeys.JOURNAL_RECENT_ARTICLES);
526 
527         if (recentArticles == null) {
528             recentArticles = new FiniteUniqueStack<JournalArticle>(
529                 MAX_STACK_SIZE);
530 
531             portletSession.setAttribute(
532                 WebKeys.JOURNAL_RECENT_ARTICLES, recentArticles);
533         }
534 
535         return recentArticles;
536     }
537 
538     public static Stack<JournalStructure> getRecentStructures(
539         PortletRequest portletRequest) {
540 
541         PortletSession portletSession = portletRequest.getPortletSession();
542 
543         Stack<JournalStructure> recentStructures =
544             (Stack<JournalStructure>)portletSession.getAttribute(
545                 WebKeys.JOURNAL_RECENT_STRUCTURES);
546 
547         if (recentStructures == null) {
548             recentStructures = new FiniteUniqueStack<JournalStructure>(
549                 MAX_STACK_SIZE);
550 
551             portletSession.setAttribute(
552                 WebKeys.JOURNAL_RECENT_STRUCTURES, recentStructures);
553         }
554 
555         return recentStructures;
556     }
557 
558     public static Stack<JournalTemplate> getRecentTemplates(
559         PortletRequest portletRequest) {
560 
561         PortletSession portletSession = portletRequest.getPortletSession();
562 
563         Stack<JournalTemplate> recentTemplates =
564             (Stack<JournalTemplate>)portletSession.getAttribute(
565                 WebKeys.JOURNAL_RECENT_TEMPLATES);
566 
567         if (recentTemplates == null) {
568             recentTemplates = new FiniteUniqueStack<JournalTemplate>(
569                 MAX_STACK_SIZE);
570 
571             portletSession.setAttribute(
572                 WebKeys.JOURNAL_RECENT_TEMPLATES, recentTemplates);
573         }
574 
575         return recentTemplates;
576     }
577 
578     public static String getTemplateScript(
579             long groupId, String templateId, Map<String, String> tokens,
580             String languageId)
581         throws PortalException, SystemException {
582 
583         return getTemplateScript(groupId, templateId, tokens, languageId, true);
584     }
585 
586     public static String getTemplateScript(
587             long groupId, String templateId, Map<String, String> tokens,
588             String languageId, boolean transform)
589         throws PortalException, SystemException {
590 
591         JournalTemplate template = JournalTemplateLocalServiceUtil.getTemplate(
592             groupId, templateId);
593 
594         return getTemplateScript(template, tokens, languageId, transform);
595     }
596 
597     public static String getTemplateScript(
598         JournalTemplate template, Map<String, String> tokens, String languageId,
599         boolean transform) {
600 
601         String script = template.getXsl();
602 
603         if (transform) {
604 
605             // Listeners
606 
607             String[] listeners =
608                 PropsUtil.getArray(PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
609 
610             for (int i = 0; i < listeners.length; i++) {
611                 TransformerListener listener = null;
612 
613                 try {
614                     listener =
615                         (TransformerListener)Class.forName(
616                             listeners[i]).newInstance();
617 
618                     listener.setTemplateDriven(true);
619                     listener.setLanguageId(languageId);
620                     listener.setTokens(tokens);
621                 }
622                 catch (Exception e) {
623                     _log.error(e, e);
624                 }
625 
626                 // Modify transform script
627 
628                 if (listener != null) {
629                     script = listener.onScript(script);
630                 }
631             }
632         }
633 
634         return script;
635     }
636 
637     public static Map<String, String> getTokens(
638         long groupId, ThemeDisplay themeDisplay) {
639 
640         Map<String, String> tokens = new HashMap<String, String>();
641 
642         if (themeDisplay == null) {
643             return tokens;
644         }
645 
646         tokens.put("cdn_host", themeDisplay.getCDNHost());
647         tokens.put("company_id", String.valueOf(themeDisplay.getCompanyId()));
648         tokens.put("group_id", String.valueOf(groupId));
649         tokens.put("cms_url", themeDisplay.getPathContext() + "/cms/servlet");
650         tokens.put("image_path", themeDisplay.getPathImage());
651         tokens.put(
652             "friendly_url_private_group",
653             themeDisplay.getPathFriendlyURLPrivateGroup());
654         tokens.put(
655             "friendly_url_private_user",
656             themeDisplay.getPathFriendlyURLPrivateUser());
657         tokens.put(
658             "friendly_url_public", themeDisplay.getPathFriendlyURLPublic());
659         tokens.put("main_path", themeDisplay.getPathMain());
660         tokens.put("portal_ctx", themeDisplay.getPathContext());
661         tokens.put(
662             "portal_url", HttpUtil.removeProtocol(themeDisplay.getURLPortal()));
663         tokens.put("root_path", themeDisplay.getPathContext());
664         tokens.put("theme_image_path", themeDisplay.getPathThemeImages());
665 
666         // Deprecated tokens
667 
668         tokens.put("friendly_url", themeDisplay.getPathFriendlyURLPublic());
669         tokens.put(
670             "friendly_url_private",
671             themeDisplay.getPathFriendlyURLPrivateGroup());
672         tokens.put("page_url", themeDisplay.getPathFriendlyURLPublic());
673 
674         return tokens;
675     }
676 
677     public static String mergeLocaleContent(
678         String curContent, String newContent, String xsd) {
679 
680         try {
681             SAXReader reader = new SAXReader();
682 
683             Document curContentDoc = reader.read(new StringReader(curContent));
684             Document newContentDoc = reader.read(new StringReader(newContent));
685             Document xsdDoc = reader.read(new StringReader(xsd));
686 
687             Element curContentRoot = curContentDoc.getRootElement();
688             Element newContentRoot = newContentDoc.getRootElement();
689             Element xsdRoot = xsdDoc.getRootElement();
690 
691             curContentRoot.addAttribute(
692                 "default-locale",
693                 newContentRoot.attributeValue("default-locale"));
694             curContentRoot.addAttribute(
695                 "available-locales",
696                 newContentRoot.attributeValue("available-locales"));
697 
698             Stack<String> path = new Stack<String>();
699 
700             path.push(xsdRoot.getName());
701 
702             _mergeLocaleContent(
703                 path, curContentDoc, newContentDoc, xsdRoot,
704                 LocaleUtil.toLanguageId(LocaleUtil.getDefault()));
705 
706             curContent = formatXML(curContentDoc);
707         }
708         catch (Exception e) {
709             _log.error(e);
710         }
711 
712         return curContent;
713     }
714 
715     public static String removeArticleLocale(
716         String content, String languageId) {
717 
718         try {
719             SAXReader reader = new SAXReader();
720 
721             Document doc = reader.read(new StringReader(content));
722 
723             Element root = doc.getRootElement();
724 
725             String availableLocales = root.attributeValue("available-locales");
726 
727             if (availableLocales == null) {
728                 return content;
729             }
730 
731             availableLocales = StringUtil.remove(availableLocales, languageId);
732 
733             if (availableLocales.endsWith(",")) {
734                 availableLocales = availableLocales.substring(
735                     0, availableLocales.length() - 1);
736             }
737 
738             root.addAttribute("available-locales", availableLocales);
739 
740             removeArticleLocale(root, languageId);
741 
742             content = formatXML(doc);
743         }
744         catch (Exception e) {
745             _log.error(e);
746         }
747 
748         return content;
749     }
750 
751     public static void removeArticleLocale(Element el, String languageId)
752         throws PortalException, SystemException {
753 
754         for (Element dynamicEl :
755                 (List<Element>)el.elements("dynamic-element")) {
756 
757             for (Element dynamicContentEl :
758                     (List<Element>)dynamicEl.elements("dynamic-content")) {
759 
760                 String curLanguageId = GetterUtil.getString(
761                     dynamicContentEl.attributeValue("language-id"));
762 
763                 if (curLanguageId.equals(languageId)) {
764                     long id = GetterUtil.getLong(
765                         dynamicContentEl.attributeValue("id"));
766 
767                     if (id > 0) {
768                         ImageLocalServiceUtil.deleteImage(id);
769                     }
770 
771                     dynamicContentEl.detach();
772                 }
773             }
774 
775             removeArticleLocale(dynamicEl, languageId);
776         }
777     }
778 
779     public static String removeOldContent(String content, String xsd) {
780         try {
781             SAXReader reader = new SAXReader();
782 
783             Document contentDoc = reader.read(new StringReader(content));
784             Document xsdDoc = reader.read(new StringReader(xsd));
785 
786             Element contentRoot = contentDoc.getRootElement();
787 
788             Stack<String> path = new Stack<String>();
789 
790             path.push(contentRoot.getName());
791 
792             _removeOldContent(path, contentRoot, xsdDoc);
793 
794             content = formatXML(contentDoc);
795         }
796         catch (Exception e) {
797             _log.error(e);
798         }
799 
800         return content;
801     }
802 
803     public static void removeRecentArticle(
804         PortletRequest portletRequest, String articleId) {
805 
806         Stack<JournalArticle> stack = getRecentArticles(portletRequest);
807 
808         Iterator<JournalArticle> itr = stack.iterator();
809 
810         while (itr.hasNext()) {
811             JournalArticle journalArticle = itr.next();
812 
813             if (journalArticle.getArticleId().equals(articleId)) {
814                 itr.remove();
815 
816                 break;
817             }
818         }
819     }
820 
821     public static void removeRecentStructure(
822         PortletRequest portletRequest, String structureId) {
823 
824         Stack<JournalStructure> stack = getRecentStructures(portletRequest);
825 
826         Iterator<JournalStructure> itr = stack.iterator();
827 
828         while (itr.hasNext()) {
829             JournalStructure journalStructure = itr.next();
830 
831             if (journalStructure.getStructureId().equals(structureId)) {
832                 itr.remove();
833 
834                 break;
835             }
836         }
837     }
838 
839     public static void removeRecentTemplate(
840         PortletRequest portletRequest, String templateId) {
841 
842         Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
843 
844         Iterator<JournalTemplate> itr = stack.iterator();
845 
846         while (itr.hasNext()) {
847             JournalTemplate journalTemplate = itr.next();
848 
849             if (journalTemplate.getTemplateId().equals(templateId)) {
850                 itr.remove();
851 
852                 break;
853             }
854         }
855     }
856 
857     public static String transform(
858             Map<String, String> tokens, String languageId, String xml,
859             String script, String langType)
860         throws Exception {
861 
862         // Setup Listeners
863 
864         if (_log.isDebugEnabled()) {
865             _log.debug("Language " + languageId);
866         }
867 
868         if (_logTokens.isDebugEnabled()) {
869             String tokensString = PropertiesUtil.list(tokens);
870 
871             _logTokens.debug(tokensString);
872         }
873 
874         if (_logTransformBefore.isDebugEnabled()) {
875             _logTransformBefore.debug(xml);
876         }
877 
878         List<TransformerListener> listenersList =
879             new ArrayList<TransformerListener>();
880 
881         String[] listeners = PropsUtil.getArray(
882             PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
883 
884         for (int i = 0; i < listeners.length; i++) {
885             TransformerListener listener = null;
886 
887             try {
888                 if (_log.isDebugEnabled()) {
889                     _log.debug("Instantiate listener " + listeners[i]);
890                 }
891 
892                 boolean templateDriven = Validator.isNotNull(langType);
893 
894                 listener = (TransformerListener)Class.forName(
895                     listeners[i]).newInstance();
896 
897                 listener.setTemplateDriven(templateDriven);
898                 listener.setLanguageId(languageId);
899                 listener.setTokens(tokens);
900 
901                 listenersList.add(listener);
902             }
903             catch (Exception e) {
904                 _log.error(e, e);
905             }
906 
907             // Modify XML
908 
909             if (_logXmlBeforeListener.isDebugEnabled()) {
910                 _logXmlBeforeListener.debug(xml);
911             }
912 
913             if (listener != null) {
914                 xml = listener.onXml(xml);
915 
916                 if (_logXmlAfterListener.isDebugEnabled()) {
917                     _logXmlAfterListener.debug(xml);
918                 }
919             }
920 
921             // Modify script
922 
923             if (_logScriptBeforeListener.isDebugEnabled()) {
924                 _logScriptBeforeListener.debug(script);
925             }
926 
927             if (listener != null) {
928                 script = listener.onScript(script);
929 
930                 if (_logScriptAfterListener.isDebugEnabled()) {
931                     _logScriptAfterListener.debug(script);
932                 }
933             }
934         }
935 
936         // Transform
937 
938         String output = null;
939 
940         if (Validator.isNull(langType)) {
941             output = xml;
942         }
943         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_VM)) {
944             output = JournalVmUtil.transform(tokens, languageId, xml, script);
945         }
946         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_XSL)) {
947             output = JournalXslUtil.transform(tokens, languageId, xml, script);
948         }
949 
950         // Postprocess output
951 
952         for (int i = 0; i < listenersList.size(); i++) {
953             TransformerListener listener = listenersList.get(i);
954 
955             // Modify output
956 
957             if (_logOutputBeforeListener.isDebugEnabled()) {
958                 _logOutputBeforeListener.debug(output);
959             }
960 
961             output = listener.onOutput(output);
962 
963             if (_logOutputAfterListener.isDebugEnabled()) {
964                 _logOutputAfterListener.debug(output);
965             }
966         }
967 
968         if (_logTransfromAfter.isDebugEnabled()) {
969             _logTransfromAfter.debug(output);
970         }
971 
972         return output;
973     }
974 
975     private static void _mergeLocaleContent(
976             Stack<String> path, Document curDoc, Document newDoc, Element xsdEl,
977             String defaultLocale)
978         throws PortalException, SystemException {
979 
980         String elPath = "";
981 
982         for (int i = 0; i < path.size(); i++) {
983             elPath += "/" + path.elementAt(i);
984         }
985 
986         for (int i = 0; i < xsdEl.nodeCount(); i++) {
987             Node xsdNode = xsdEl.node(i);
988 
989             if ((xsdNode instanceof Element) &&
990                 (xsdNode.getName().equals("dynamic-element"))) {
991 
992                 _mergeLocaleContent(
993                     path, curDoc, newDoc, (Element)xsdNode, defaultLocale,
994                     elPath);
995             }
996         }
997     }
998 
999     private static void _mergeLocaleContent(
1000            Stack<String> path, Document curDoc, Document newDoc, Element xsdEl,
1001            String defaultLocale, String elPath)
1002        throws PortalException, SystemException {
1003
1004        String name = xsdEl.attributeValue("name");
1005
1006        String localPath = "dynamic-element[@name='" + name + "']";
1007
1008        String fullPath = elPath + "/" + localPath;
1009
1010        XPath xPathSelector = DocumentHelper.createXPath(fullPath);
1011
1012        List<Element> curElements = xPathSelector.selectNodes(curDoc);
1013
1014        Element newEl = (Element)xPathSelector.selectNodes(newDoc).get(0);
1015
1016        if (curElements.size() > 0) {
1017            Element curEl = curElements.get(0);
1018
1019            List<Element> curDynamicContents = curEl.elements(
1020                "dynamic-content");
1021
1022            Element newContentEl = newEl.element("dynamic-content");
1023
1024            String newContentLanguageId = newContentEl.attributeValue(
1025                "language-id", StringPool.BLANK);
1026
1027            if (newContentLanguageId.equals(StringPool.BLANK)) {
1028                for (int k = curDynamicContents.size() - 1; k >= 0 ; k--) {
1029                    Element curContentEl = curDynamicContents.get(k);
1030
1031                    String curContentLanguageId = curContentEl.attributeValue(
1032                        "language-id", StringPool.BLANK);
1033
1034                    if ((curEl.attributeValue("type").equals("image")) &&
1035                        (!curContentLanguageId.equals(defaultLocale) &&
1036                         !curContentLanguageId.equals(StringPool.BLANK))) {
1037
1038                        long id = GetterUtil.getLong(
1039                            curContentEl.attributeValue("id"));
1040
1041                        ImageLocalServiceUtil.deleteImage(id);
1042                    }
1043
1044                    curContentEl.detach();
1045                }
1046
1047                curEl.content().add(newContentEl.createCopy());
1048            }
1049            else {
1050                boolean match = false;
1051
1052                for (int k = curDynamicContents.size() - 1; k >= 0 ; k--) {
1053                    Element curContentEl = curDynamicContents.get(k);
1054
1055                    String curContentLanguageId = curContentEl.attributeValue(
1056                        "language-id", StringPool.BLANK);
1057
1058                    if ((newContentLanguageId.equals(curContentLanguageId)) ||
1059                        (newContentLanguageId.equals(defaultLocale) &&
1060                         curContentLanguageId.equals(StringPool.BLANK))) {
1061
1062                        curContentEl.detach();
1063
1064                        curEl.content().add(k, newContentEl.createCopy());
1065
1066                        match = true;
1067                    }
1068
1069                    if (curContentLanguageId.equals(StringPool.BLANK)) {
1070                        curContentEl.addAttribute("language-id", defaultLocale);
1071                    }
1072                }
1073
1074                if (!match) {
1075                    curEl.content().add(newContentEl.createCopy());
1076                }
1077            }
1078        }
1079        else {
1080            xPathSelector = DocumentHelper.createXPath(elPath);
1081
1082            Element parentEl =
1083                (Element)xPathSelector.selectNodes(curDoc).get(0);
1084
1085            parentEl.content().add(newEl.createCopy());
1086        }
1087
1088        String type = xsdEl.attributeValue("type", StringPool.BLANK);
1089
1090        if (!type.equals("list") && !type.equals("multi-list")) {
1091            path.push(localPath);
1092
1093            _mergeLocaleContent(path, curDoc, newDoc, xsdEl, defaultLocale);
1094
1095            path.pop();
1096        }
1097    }
1098
1099    private static void _removeOldContent(
1100            Stack<String> path, Element contentEl, Document xsdDoc)
1101        throws SystemException {
1102
1103        String elPath = "";
1104
1105        for (int i = 0; i < path.size(); i++) {
1106            elPath += "/" + path.elementAt(i);
1107        }
1108
1109        for (int i = 0; i < contentEl.nodeCount(); i++) {
1110            Node contentNode = contentEl.node(i);
1111
1112            if (contentNode instanceof Element) {
1113                _removeOldContent(path, (Element)contentNode, xsdDoc, elPath);
1114            }
1115        }
1116    }
1117
1118    private static void _removeOldContent(
1119            Stack<String> path, Element contentEl, Document xsdDoc,
1120            String elPath)
1121        throws SystemException {
1122
1123        String name = contentEl.attributeValue("name");
1124
1125        if (Validator.isNull(name)) {
1126            return;
1127        }
1128
1129        String localPath = "dynamic-element[@name='" + name + "']";
1130
1131        String fullPath = elPath + "/" + localPath;
1132
1133        XPath xPathSelector = DocumentHelper.createXPath(fullPath);
1134
1135        List<Element> curElements = xPathSelector.selectNodes(xsdDoc);
1136
1137        if (curElements.size() == 0) {
1138            contentEl.detach();
1139        }
1140
1141        path.push(localPath);
1142
1143        _removeOldContent(path, contentEl, xsdDoc);
1144
1145        path.pop();
1146    }
1147
1148    private static Log _log = LogFactory.getLog(JournalUtil.class);
1149
1150    private static Log _logOutputAfterListener = LogFactory.getLog(
1151        JournalUtil.class.getName() + ".OutputAfterListener");
1152
1153    private static Log _logOutputBeforeListener = LogFactory.getLog(
1154        JournalUtil.class.getName() + ".OutputBeforeListener");
1155
1156    private static Log _logScriptAfterListener = LogFactory.getLog(
1157        JournalUtil.class.getName() + ".ScriptAfterListener");
1158
1159    private static Log _logScriptBeforeListener = LogFactory.getLog(
1160        JournalUtil.class.getName() + ".ScriptBeforeListener");
1161
1162    private static Log _logTransfromAfter = LogFactory.getLog(
1163        JournalUtil.class.getName() + ".TransformAfter");
1164
1165    private static Log _logTransformBefore = LogFactory.getLog(
1166        JournalUtil.class.getName() + ".BeforeTransform");
1167
1168    private static Log _logTokens = LogFactory.getLog(
1169        JournalUtil.class.getName() + ".Tokens");
1170
1171    private static Log _logXmlAfterListener = LogFactory.getLog(
1172        JournalUtil.class.getName() + ".XmlAfterListener");
1173
1174    private static Log _logXmlBeforeListener = LogFactory.getLog(
1175        JournalUtil.class.getName() + ".XmlBeforeListener");
1176
1177}