1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.journal.util;
16  
17  import com.liferay.portal.kernel.configuration.Filter;
18  import com.liferay.portal.kernel.exception.PortalException;
19  import com.liferay.portal.kernel.exception.SystemException;
20  import com.liferay.portal.kernel.log.Log;
21  import com.liferay.portal.kernel.log.LogFactoryUtil;
22  import com.liferay.portal.kernel.util.Constants;
23  import com.liferay.portal.kernel.util.GetterUtil;
24  import com.liferay.portal.kernel.util.HttpUtil;
25  import com.liferay.portal.kernel.util.InstancePool;
26  import com.liferay.portal.kernel.util.LocaleUtil;
27  import com.liferay.portal.kernel.util.LocalizationUtil;
28  import com.liferay.portal.kernel.util.OrderByComparator;
29  import com.liferay.portal.kernel.util.PropertiesUtil;
30  import com.liferay.portal.kernel.util.PropsKeys;
31  import com.liferay.portal.kernel.util.StringBundler;
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.kernel.xml.Document;
37  import com.liferay.portal.kernel.xml.Element;
38  import com.liferay.portal.kernel.xml.Node;
39  import com.liferay.portal.kernel.xml.SAXReaderUtil;
40  import com.liferay.portal.kernel.xml.XPath;
41  import com.liferay.portal.model.Group;
42  import com.liferay.portal.model.Layout;
43  import com.liferay.portal.model.LayoutSet;
44  import com.liferay.portal.model.User;
45  import com.liferay.portal.service.ImageLocalServiceUtil;
46  import com.liferay.portal.service.LayoutLocalServiceUtil;
47  import com.liferay.portal.service.UserLocalServiceUtil;
48  import com.liferay.portal.theme.ThemeDisplay;
49  import com.liferay.portal.util.ContentUtil;
50  import com.liferay.portal.util.FriendlyURLNormalizer;
51  import com.liferay.portal.util.PropsUtil;
52  import com.liferay.portal.util.PropsValues;
53  import com.liferay.portal.util.WebKeys;
54  import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
55  import com.liferay.portlet.journal.TransformException;
56  import com.liferay.portlet.journal.model.JournalArticle;
57  import com.liferay.portlet.journal.model.JournalStructure;
58  import com.liferay.portlet.journal.model.JournalStructureConstants;
59  import com.liferay.portlet.journal.model.JournalTemplate;
60  import com.liferay.portlet.journal.service.JournalArticleImageLocalServiceUtil;
61  import com.liferay.portlet.journal.service.JournalTemplateLocalServiceUtil;
62  import com.liferay.portlet.journal.util.comparator.ArticleCreateDateComparator;
63  import com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator;
64  import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
65  import com.liferay.portlet.journal.util.comparator.ArticleModifiedDateComparator;
66  import com.liferay.portlet.journal.util.comparator.ArticleReviewDateComparator;
67  import com.liferay.portlet.journal.util.comparator.ArticleTitleComparator;
68  import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
69  import com.liferay.util.FiniteUniqueStack;
70  import com.liferay.util.xml.XMLFormatter;
71  
72  import java.io.IOException;
73  
74  import java.util.ArrayList;
75  import java.util.Date;
76  import java.util.HashMap;
77  import java.util.Iterator;
78  import java.util.List;
79  import java.util.Map;
80  import java.util.Stack;
81  
82  import javax.portlet.PortletPreferences;
83  import javax.portlet.PortletRequest;
84  import javax.portlet.PortletSession;
85  
86  /**
87   * <a href="JournalUtil.java.html"><b><i>View Source</i></b></a>
88   *
89   * @author Brian Wing Shun Chan
90   * @author Raymond Augé
91   * @author Wesley Gong
92   */
93  public class JournalUtil {
94  
95      public static final int MAX_STACK_SIZE = 20;
96  
97      public static final String POP_PORTLET_PREFIX = "journal.";
98  
99      public static final String XML_INDENT = "  ";
100 
101     public static void addAllReservedEls(
102         Element root, Map<String, String> tokens, JournalArticle article) {
103 
104         JournalUtil.addReservedEl(
105             root, tokens, JournalStructureConstants.RESERVED_ARTICLE_ID,
106             article.getArticleId());
107 
108         JournalUtil.addReservedEl(
109             root, tokens, JournalStructureConstants.RESERVED_ARTICLE_VERSION,
110             article.getVersion());
111 
112         JournalUtil.addReservedEl(
113             root, tokens, JournalStructureConstants.RESERVED_ARTICLE_TITLE,
114             article.getTitle());
115 
116         JournalUtil.addReservedEl(
117             root, tokens,
118             JournalStructureConstants.RESERVED_ARTICLE_URL_TITLE,
119             article.getUrlTitle());
120 
121         JournalUtil.addReservedEl(
122             root, tokens,
123             JournalStructureConstants.RESERVED_ARTICLE_DESCRIPTION,
124             article.getDescription());
125 
126         JournalUtil.addReservedEl(
127             root, tokens, JournalStructureConstants.RESERVED_ARTICLE_TYPE,
128             article.getType());
129 
130         JournalUtil.addReservedEl(
131             root, tokens,
132             JournalStructureConstants.RESERVED_ARTICLE_CREATE_DATE,
133             article.getCreateDate());
134 
135         JournalUtil.addReservedEl(
136             root, tokens,
137             JournalStructureConstants.RESERVED_ARTICLE_MODIFIED_DATE,
138             article.getModifiedDate());
139 
140         if (article.getDisplayDate() != null) {
141             JournalUtil.addReservedEl(
142                 root, tokens,
143                 JournalStructureConstants.RESERVED_ARTICLE_DISPLAY_DATE,
144                 article.getDisplayDate());
145         }
146 
147         JournalUtil.addReservedEl(
148             root, tokens,
149             JournalStructureConstants.RESERVED_ARTICLE_SMALL_IMAGE_URL,
150             article.getSmallImageURL());
151 
152         String[] assetTagNames = new String[0];
153 
154         try {
155             assetTagNames = AssetTagLocalServiceUtil.getTagNames(
156                 JournalArticle.class.getName(), article.getResourcePrimKey());
157         }
158         catch (SystemException se) {
159         }
160 
161         JournalUtil.addReservedEl(
162             root, tokens,
163             JournalStructureConstants.RESERVED_ARTICLE_ASSET_TAG_NAMES,
164             StringUtil.merge(assetTagNames));
165 
166         JournalUtil.addReservedEl(
167             root, tokens, JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_ID,
168             String.valueOf(article.getUserId()));
169 
170         String userName = StringPool.BLANK;
171         String userEmailAddress = StringPool.BLANK;
172         String userComments = StringPool.BLANK;
173         String userJobTitle = StringPool.BLANK;
174 
175         User user = null;
176 
177         try {
178             user = UserLocalServiceUtil.getUserById(article.getUserId());
179 
180             userName = user.getFullName();
181             userEmailAddress = user.getEmailAddress();
182             userComments = user.getComments();
183             userJobTitle = user.getJobTitle();
184         }
185         catch (PortalException pe) {
186         }
187         catch (SystemException se) {
188         }
189 
190         JournalUtil.addReservedEl(
191             root, tokens,
192             JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_NAME, userName);
193 
194         JournalUtil.addReservedEl(
195             root, tokens,
196             JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_EMAIL_ADDRESS,
197             userEmailAddress);
198 
199         JournalUtil.addReservedEl(
200             root, tokens,
201             JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_COMMENTS,
202             userComments);
203 
204         JournalUtil.addReservedEl(
205             root, tokens,
206             JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_JOB_TITLE,
207             userJobTitle);
208     }
209 
210     public static void addRecentArticle(
211         PortletRequest portletRequest, JournalArticle article) {
212 
213         if (article != null) {
214             Stack<JournalArticle> stack = getRecentArticles(portletRequest);
215 
216             stack.push(article);
217         }
218     }
219 
220     public static void addRecentStructure(
221         PortletRequest portletRequest, JournalStructure structure) {
222 
223         if (structure != null) {
224             Stack<JournalStructure> stack = getRecentStructures(portletRequest);
225 
226             stack.push(structure);
227         }
228     }
229 
230     public static void addRecentTemplate(
231         PortletRequest portletRequest, JournalTemplate template) {
232 
233         if (template != null) {
234             Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
235 
236             stack.push(template);
237         }
238     }
239 
240     public static void addReservedEl(
241         Element root, Map<String, String> tokens, String name, Date value) {
242 
243         addReservedEl(root, tokens, name, Time.getRFC822(value));
244     }
245 
246     public static void addReservedEl(
247         Element root, Map<String, String> tokens, String name, double value) {
248 
249         addReservedEl(root, tokens, name, String.valueOf(value));
250     }
251 
252     public static void addReservedEl(
253         Element root, Map<String, String> tokens, String name, String value) {
254 
255         // XML
256 
257         if (root != null) {
258             Element dynamicEl = SAXReaderUtil.createElement("dynamic-element");
259 
260             dynamicEl.add(
261                 SAXReaderUtil.createAttribute(dynamicEl, "name", name));
262             dynamicEl.add(
263                 SAXReaderUtil.createAttribute(dynamicEl, "type", "text"));
264 
265             Element dynamicContent = SAXReaderUtil.createElement(
266                 "dynamic-content");
267 
268             //dynamicContent.setText("<![CDATA[" + value + "]]>");
269             dynamicContent.setText(value);
270 
271             dynamicEl.add(dynamicContent);
272 
273             root.add(dynamicEl);
274         }
275 
276         // Tokens
277 
278         tokens.put(
279             StringUtil.replace(name, StringPool.DASH, StringPool.UNDERLINE),
280             value);
281     }
282 
283     public static String formatVM(String vm) {
284         return vm;
285     }
286 
287     public static String formatXML(Document doc) throws IOException {
288         return doc.formattedString(XML_INDENT);
289     }
290 
291     public static String formatXML(String xml)
292         throws org.dom4j.DocumentException, IOException {
293 
294         // This is only supposed to format your xml, however, it will also
295         // unwantingly change &#169; and other characters like it into their
296         // respective readable versions
297 
298         xml = StringUtil.replace(xml, "&#", "[$SPECIAL_CHARACTER$]");
299 
300         xml = XMLFormatter.toString(xml, XML_INDENT);
301 
302         xml = StringUtil.replace(xml, "[$SPECIAL_CHARACTER$]", "&#");
303 
304         return xml;
305     }
306 
307     public static OrderByComparator getArticleOrderByComparator(
308         String orderByCol, String orderByType) {
309 
310         boolean orderByAsc = false;
311 
312         if (orderByType.equals("asc")) {
313             orderByAsc = true;
314         }
315 
316         OrderByComparator orderByComparator = null;
317 
318         if (orderByCol.equals("create-date")) {
319             orderByComparator = new ArticleCreateDateComparator(orderByAsc);
320         }
321         else if (orderByCol.equals("display-date")) {
322             orderByComparator = new ArticleDisplayDateComparator(orderByAsc);
323         }
324         else if (orderByCol.equals("id")) {
325             orderByComparator = new ArticleIDComparator(orderByAsc);
326         }
327         else if (orderByCol.equals("modified-date")) {
328             orderByComparator = new ArticleModifiedDateComparator(orderByAsc);
329         }
330         else if (orderByCol.equals("review-date")) {
331             orderByComparator = new ArticleReviewDateComparator(orderByAsc);
332         }
333         else if (orderByCol.equals("title")) {
334             orderByComparator = new ArticleTitleComparator(orderByAsc);
335         }
336         else if (orderByCol.equals("version")) {
337             orderByComparator = new ArticleVersionComparator(orderByAsc);
338         }
339 
340         return orderByComparator;
341     }
342 
343     public static String getEmailArticleAddedBody(
344         PortletPreferences preferences) {
345 
346         String emailArticleAddedBody = preferences.getValue(
347             "email-article-added-body", StringPool.BLANK);
348 
349         if (Validator.isNotNull(emailArticleAddedBody)) {
350             return emailArticleAddedBody;
351         }
352         else {
353             return ContentUtil.get(PropsUtil.get(
354                 PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_BODY));
355         }
356     }
357 
358     public static boolean getEmailArticleAddedEnabled(
359         PortletPreferences preferences) {
360 
361         String emailArticleAddedEnabled = preferences.getValue(
362             "email-article-added-enabled", StringPool.BLANK);
363 
364         if (Validator.isNotNull(emailArticleAddedEnabled)) {
365             return GetterUtil.getBoolean(emailArticleAddedEnabled);
366         }
367         else {
368             return GetterUtil.getBoolean(PropsUtil.get(
369                 PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_ENABLED));
370         }
371     }
372 
373     public static String getEmailArticleAddedSubject(
374         PortletPreferences preferences) {
375 
376         String emailArticleAddedSubject = preferences.getValue(
377             "email-article-added-subject", StringPool.BLANK);
378 
379         if (Validator.isNotNull(emailArticleAddedSubject)) {
380             return emailArticleAddedSubject;
381         }
382         else {
383             return ContentUtil.get(PropsUtil.get(
384                 PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_SUBJECT));
385         }
386     }
387 
388     public static String getEmailArticleApprovalDeniedBody(
389         PortletPreferences preferences) {
390 
391         String emailArticleApprovalDeniedBody = preferences.getValue(
392             "email-article-approval-denied-body", StringPool.BLANK);
393 
394         if (Validator.isNotNull(emailArticleApprovalDeniedBody)) {
395             return emailArticleApprovalDeniedBody;
396         }
397         else {
398             return ContentUtil.get(PropsUtil.get(
399                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_BODY));
400         }
401     }
402 
403     public static boolean getEmailArticleApprovalDeniedEnabled(
404         PortletPreferences preferences) {
405 
406         String emailArticleApprovalDeniedEnabled = preferences.getValue(
407             "email-article-approval-denied-enabled", StringPool.BLANK);
408 
409         if (Validator.isNotNull(emailArticleApprovalDeniedEnabled)) {
410             return GetterUtil.getBoolean(emailArticleApprovalDeniedEnabled);
411         }
412         else {
413             return GetterUtil.getBoolean(PropsUtil.get(
414                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_ENABLED));
415         }
416     }
417 
418     public static String getEmailArticleApprovalDeniedSubject(
419         PortletPreferences preferences) {
420 
421         String emailArticleApprovalDeniedSubject = preferences.getValue(
422             "email-article-approval-denied-subject", StringPool.BLANK);
423 
424         if (Validator.isNotNull(emailArticleApprovalDeniedSubject)) {
425             return emailArticleApprovalDeniedSubject;
426         }
427         else {
428             return ContentUtil.get(PropsUtil.get(
429                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_SUBJECT));
430         }
431     }
432 
433     public static String getEmailArticleApprovalGrantedBody(
434         PortletPreferences preferences) {
435 
436         String emailArticleApprovalGrantedBody = preferences.getValue(
437             "email-article-approval-granted-body", StringPool.BLANK);
438 
439         if (Validator.isNotNull(emailArticleApprovalGrantedBody)) {
440             return emailArticleApprovalGrantedBody;
441         }
442         else {
443             return ContentUtil.get(PropsUtil.get(
444                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_BODY));
445         }
446     }
447 
448     public static boolean getEmailArticleApprovalGrantedEnabled(
449         PortletPreferences preferences) {
450 
451         String emailArticleApprovalGrantedEnabled = preferences.getValue(
452             "email-article-approval-granted-enabled", StringPool.BLANK);
453 
454         if (Validator.isNotNull(emailArticleApprovalGrantedEnabled)) {
455             return GetterUtil.getBoolean(emailArticleApprovalGrantedEnabled);
456         }
457         else {
458             return GetterUtil.getBoolean(PropsUtil.get(
459                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_ENABLED));
460         }
461     }
462 
463     public static String getEmailArticleApprovalGrantedSubject(
464         PortletPreferences preferences) {
465 
466         String emailArticleApprovalGrantedSubject = preferences.getValue(
467             "email-article-approval-granted-subject", StringPool.BLANK);
468 
469         if (Validator.isNotNull(emailArticleApprovalGrantedSubject)) {
470             return emailArticleApprovalGrantedSubject;
471         }
472         else {
473             return ContentUtil.get(PropsUtil.get(
474                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_SUBJECT));
475         }
476     }
477 
478     public static String getEmailArticleApprovalRequestedBody(
479         PortletPreferences preferences) {
480 
481         String emailArticleApprovalRequestedBody = preferences.getValue(
482             "email-article-approval-requested-body", StringPool.BLANK);
483 
484         if (Validator.isNotNull(emailArticleApprovalRequestedBody)) {
485             return emailArticleApprovalRequestedBody;
486         }
487         else {
488             return ContentUtil.get(PropsUtil.get(
489                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_BODY));
490         }
491     }
492 
493     public static boolean getEmailArticleApprovalRequestedEnabled(
494         PortletPreferences preferences) {
495 
496         String emailArticleApprovalRequestedEnabled = preferences.getValue(
497             "email-article-approval-requested-enabled", StringPool.BLANK);
498 
499         if (Validator.isNotNull(emailArticleApprovalRequestedEnabled)) {
500             return GetterUtil.getBoolean(emailArticleApprovalRequestedEnabled);
501         }
502         else {
503             return GetterUtil.getBoolean(PropsUtil.get(
504                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_ENABLED));
505         }
506     }
507 
508     public static String getEmailArticleApprovalRequestedSubject(
509         PortletPreferences preferences) {
510 
511         String emailArticleApprovalRequestedSubject = preferences.getValue(
512             "email-article-approval-requested-subject", StringPool.BLANK);
513 
514         if (Validator.isNotNull(emailArticleApprovalRequestedSubject)) {
515             return emailArticleApprovalRequestedSubject;
516         }
517         else {
518             return ContentUtil.get(PropsUtil.get(
519                 PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_SUBJECT));
520         }
521     }
522 
523     public static String getEmailArticleReviewBody(
524         PortletPreferences preferences) {
525 
526         String emailArticleReviewBody = preferences.getValue(
527             "email-article-review-body", StringPool.BLANK);
528 
529         if (Validator.isNotNull(emailArticleReviewBody)) {
530             return emailArticleReviewBody;
531         }
532         else {
533             return ContentUtil.get(PropsUtil.get(
534                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_BODY));
535         }
536     }
537 
538     public static boolean getEmailArticleReviewEnabled(
539         PortletPreferences preferences) {
540 
541         String emailArticleReviewEnabled = preferences.getValue(
542             "email-article-review-enabled", StringPool.BLANK);
543 
544         if (Validator.isNotNull(emailArticleReviewEnabled)) {
545             return GetterUtil.getBoolean(emailArticleReviewEnabled);
546         }
547         else {
548             return GetterUtil.getBoolean(PropsUtil.get(
549                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_ENABLED));
550         }
551     }
552 
553     public static String getEmailArticleReviewSubject(
554         PortletPreferences preferences) {
555 
556         String emailArticleReviewSubject = preferences.getValue(
557             "email-article-review-subject", StringPool.BLANK);
558 
559         if (Validator.isNotNull(emailArticleReviewSubject)) {
560             return emailArticleReviewSubject;
561         }
562         else {
563             return ContentUtil.get(PropsUtil.get(
564                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_SUBJECT));
565         }
566     }
567 
568     public static String getEmailArticleUpdatedBody(
569         PortletPreferences preferences) {
570 
571         String emailArticleUpdatedBody = preferences.getValue(
572             "email-article-updated-body", StringPool.BLANK);
573 
574         if (Validator.isNotNull(emailArticleUpdatedBody)) {
575             return emailArticleUpdatedBody;
576         }
577         else {
578             return ContentUtil.get(PropsUtil.get(
579                 PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_BODY));
580         }
581     }
582 
583     public static boolean getEmailArticleUpdatedEnabled(
584         PortletPreferences preferences) {
585 
586         String emailArticleUpdatedEnabled = preferences.getValue(
587             "email-article-updated-enabled", StringPool.BLANK);
588 
589         if (Validator.isNotNull(emailArticleUpdatedEnabled)) {
590             return GetterUtil.getBoolean(emailArticleUpdatedEnabled);
591         }
592         else {
593             return GetterUtil.getBoolean(PropsUtil.get(
594                 PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_ENABLED));
595         }
596     }
597 
598     public static String getEmailArticleUpdatedSubject(
599         PortletPreferences preferences) {
600 
601         String emailArticleUpdatedSubject = preferences.getValue(
602             "email-article-updated-subject", StringPool.BLANK);
603 
604         if (Validator.isNotNull(emailArticleUpdatedSubject)) {
605             return emailArticleUpdatedSubject;
606         }
607         else {
608             return ContentUtil.get(PropsUtil.get(
609                 PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_SUBJECT));
610         }
611     }
612 
613     public static String getEmailFromAddress(PortletPreferences preferences) {
614         String emailFromAddress = PropsUtil.get(
615             PropsKeys.JOURNAL_EMAIL_FROM_ADDRESS);
616 
617         return preferences.getValue("email-from-address", emailFromAddress);
618     }
619 
620     public static String getEmailFromName(PortletPreferences preferences) {
621         String emailFromName = PropsUtil.get(
622             PropsKeys.JOURNAL_EMAIL_FROM_NAME);
623 
624         return preferences.getValue("email-from-name", emailFromName);
625     }
626 
627     public static String getMailId(String mx, String articleId) {
628         StringBundler sb = new StringBundler(8);
629 
630         sb.append(StringPool.LESS_THAN);
631         sb.append(POP_PORTLET_PREFIX);
632         sb.append(articleId);
633         sb.append(StringPool.AT);
634         sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
635         sb.append(StringPool.PERIOD);
636         sb.append(mx);
637         sb.append(StringPool.GREATER_THAN);
638 
639         return sb.toString();
640     }
641 
642     public static Stack<JournalArticle> getRecentArticles(
643         PortletRequest portletRequest) {
644 
645         PortletSession portletSession = portletRequest.getPortletSession();
646 
647         Stack<JournalArticle> recentArticles =
648             (Stack<JournalArticle>)portletSession.getAttribute(
649                 WebKeys.JOURNAL_RECENT_ARTICLES);
650 
651         if (recentArticles == null) {
652             recentArticles = new FiniteUniqueStack<JournalArticle>(
653                 MAX_STACK_SIZE);
654 
655             portletSession.setAttribute(
656                 WebKeys.JOURNAL_RECENT_ARTICLES, recentArticles);
657         }
658 
659         return recentArticles;
660     }
661 
662     public static Stack<JournalStructure> getRecentStructures(
663         PortletRequest portletRequest) {
664 
665         PortletSession portletSession = portletRequest.getPortletSession();
666 
667         Stack<JournalStructure> recentStructures =
668             (Stack<JournalStructure>)portletSession.getAttribute(
669                 WebKeys.JOURNAL_RECENT_STRUCTURES);
670 
671         if (recentStructures == null) {
672             recentStructures = new FiniteUniqueStack<JournalStructure>(
673                 MAX_STACK_SIZE);
674 
675             portletSession.setAttribute(
676                 WebKeys.JOURNAL_RECENT_STRUCTURES, recentStructures);
677         }
678 
679         return recentStructures;
680     }
681 
682     public static Stack<JournalTemplate> getRecentTemplates(
683         PortletRequest portletRequest) {
684 
685         PortletSession portletSession = portletRequest.getPortletSession();
686 
687         Stack<JournalTemplate> recentTemplates =
688             (Stack<JournalTemplate>)portletSession.getAttribute(
689                 WebKeys.JOURNAL_RECENT_TEMPLATES);
690 
691         if (recentTemplates == null) {
692             recentTemplates = new FiniteUniqueStack<JournalTemplate>(
693                 MAX_STACK_SIZE);
694 
695             portletSession.setAttribute(
696                 WebKeys.JOURNAL_RECENT_TEMPLATES, recentTemplates);
697         }
698 
699         return recentTemplates;
700     }
701 
702     public static String getTemplateScript(
703         JournalTemplate template, Map<String, String> tokens, String languageId,
704         boolean transform) {
705 
706         String script = template.getXsl();
707 
708         if (transform) {
709 
710             // Listeners
711 
712             String[] listeners =
713                 PropsUtil.getArray(PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
714 
715             for (int i = 0; i < listeners.length; i++) {
716                 TransformerListener listener = null;
717 
718                 try {
719                     listener =
720                         (TransformerListener)Class.forName(
721                             listeners[i]).newInstance();
722 
723                     listener.setTemplateDriven(true);
724                     listener.setLanguageId(languageId);
725                     listener.setTokens(tokens);
726                 }
727                 catch (Exception e) {
728                     _log.error(e, e);
729                 }
730 
731                 // Modify transform script
732 
733                 if (listener != null) {
734                     script = listener.onScript(script);
735                 }
736             }
737         }
738 
739         return script;
740     }
741 
742     public static String getTemplateScript(
743             long groupId, String templateId, Map<String, String> tokens,
744             String languageId)
745         throws PortalException, SystemException {
746 
747         return getTemplateScript(groupId, templateId, tokens, languageId, true);
748     }
749 
750     public static String getTemplateScript(
751             long groupId, String templateId, Map<String, String> tokens,
752             String languageId, boolean transform)
753         throws PortalException, SystemException {
754 
755         JournalTemplate template = JournalTemplateLocalServiceUtil.getTemplate(
756             groupId, templateId);
757 
758         return getTemplateScript(template, tokens, languageId, transform);
759     }
760 
761     public static Map<String, String> getTokens(
762             long groupId, ThemeDisplay themeDisplay)
763         throws PortalException, SystemException {
764 
765         return getTokens(groupId, themeDisplay, null);
766     }
767 
768     public static Map<String, String> getTokens(
769             long groupId, ThemeDisplay themeDisplay, String xmlRequest)
770         throws PortalException, SystemException {
771 
772         Map<String, String> tokens = new HashMap<String, String>();
773 
774         if (themeDisplay != null) {
775             _populateTokens(tokens, groupId, themeDisplay);
776         }
777         else if (Validator.isNotNull(xmlRequest)) {
778             try {
779                 _populateTokens(tokens, groupId, xmlRequest);
780             }
781             catch (Exception e) {
782                 if (_log.isWarnEnabled()) {
783                     _log.warn(e, e);
784                 }
785             }
786         }
787 
788         return tokens;
789     }
790 
791     public static String getUrlTitle(long id, String title) {
792         title = title.trim().toLowerCase();
793 
794         if (Validator.isNull(title) || Validator.isNumber(title) ||
795             title.equals("rss")) {
796 
797             return String.valueOf(id);
798         }
799         else {
800             return FriendlyURLNormalizer.normalize(
801                 title, _URL_TITLE_REPLACE_CHARS);
802         }
803     }
804 
805     public static String mergeArticleContent(
806         String curContent, String newContent) {
807 
808         try {
809             Document curDocument = SAXReaderUtil.read(curContent);
810             Document newDocument = SAXReaderUtil.read(newContent);
811 
812             Element curRoot = curDocument.getRootElement();
813             Element newRoot = newDocument.getRootElement();
814 
815             curRoot.addAttribute(
816                 "default-locale",
817                 newRoot.attributeValue("default-locale"));
818             curRoot.addAttribute(
819                 "available-locales",
820                 newRoot.attributeValue("available-locales"));
821 
822             _mergeArticleContentUpdate(
823                 curDocument, newRoot,
824                 LocaleUtil.toLanguageId(LocaleUtil.getDefault()));
825             _mergeArticleContentDelete(curRoot, newDocument);
826 
827             curContent = JournalUtil.formatXML(curDocument);
828         }
829         catch (Exception e) {
830             _log.error(e, e);
831         }
832 
833         return curContent;
834     }
835 
836     public static void removeArticleLocale(Element el, String languageId)
837         throws PortalException, SystemException {
838 
839         for (Element dynamicEl : el.elements("dynamic-element")) {
840             for (Element dynamicContentEl :
841                     dynamicEl.elements("dynamic-content")) {
842 
843                 String curLanguageId = GetterUtil.getString(
844                     dynamicContentEl.attributeValue("language-id"));
845 
846                 if (curLanguageId.equals(languageId)) {
847                     long id = GetterUtil.getLong(
848                         dynamicContentEl.attributeValue("id"));
849 
850                     if (id > 0) {
851                         ImageLocalServiceUtil.deleteImage(id);
852                     }
853 
854                     dynamicContentEl.detach();
855                 }
856             }
857 
858             removeArticleLocale(dynamicEl, languageId);
859         }
860     }
861 
862     public static String removeArticleLocale(
863         String content, String languageId) {
864 
865         try {
866             Document doc = SAXReaderUtil.read(content);
867 
868             Element root = doc.getRootElement();
869 
870             String availableLocales = root.attributeValue("available-locales");
871 
872             if (availableLocales == null) {
873                 return content;
874             }
875 
876             availableLocales = StringUtil.remove(availableLocales, languageId);
877 
878             if (availableLocales.endsWith(",")) {
879                 availableLocales = availableLocales.substring(
880                     0, availableLocales.length() - 1);
881             }
882 
883             root.addAttribute("available-locales", availableLocales);
884 
885             removeArticleLocale(root, languageId);
886 
887             content = formatXML(doc);
888         }
889         catch (Exception e) {
890             _log.error(e, e);
891         }
892 
893         return content;
894     }
895 
896     public static String removeOldContent(String content, String xsd) {
897         try {
898             Document contentDoc = SAXReaderUtil.read(content);
899             Document xsdDoc = SAXReaderUtil.read(xsd);
900 
901             Element contentRoot = contentDoc.getRootElement();
902 
903             Stack<String> path = new Stack<String>();
904 
905             path.push(contentRoot.getName());
906 
907             _removeOldContent(path, contentRoot, xsdDoc);
908 
909             content = formatXML(contentDoc);
910         }
911         catch (Exception e) {
912             _log.error(e, e);
913         }
914 
915         return content;
916     }
917 
918     public static void removeRecentArticle(
919         PortletRequest portletRequest, String articleId) {
920 
921         Stack<JournalArticle> stack = getRecentArticles(portletRequest);
922 
923         Iterator<JournalArticle> itr = stack.iterator();
924 
925         while (itr.hasNext()) {
926             JournalArticle journalArticle = itr.next();
927 
928             if (journalArticle.getArticleId().equals(articleId)) {
929                 itr.remove();
930 
931                 break;
932             }
933         }
934     }
935 
936     public static void removeRecentStructure(
937         PortletRequest portletRequest, String structureId) {
938 
939         Stack<JournalStructure> stack = getRecentStructures(portletRequest);
940 
941         Iterator<JournalStructure> itr = stack.iterator();
942 
943         while (itr.hasNext()) {
944             JournalStructure journalStructure = itr.next();
945 
946             if (journalStructure.getStructureId().equals(structureId)) {
947                 itr.remove();
948 
949                 break;
950             }
951         }
952     }
953 
954     public static void removeRecentTemplate(
955         PortletRequest portletRequest, String templateId) {
956 
957         Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
958 
959         Iterator<JournalTemplate> itr = stack.iterator();
960 
961         while (itr.hasNext()) {
962             JournalTemplate journalTemplate = itr.next();
963 
964             if (journalTemplate.getTemplateId().equals(templateId)) {
965                 itr.remove();
966 
967                 break;
968             }
969         }
970     }
971 
972     public static String transform(
973             ThemeDisplay themeDisplay, Map<String, String> tokens,
974             String viewMode, String languageId, String xml, String script,
975             String langType)
976         throws Exception {
977 
978         // Setup Listeners
979 
980         if (_log.isDebugEnabled()) {
981             _log.debug("Language " + languageId);
982         }
983 
984         if (Validator.isNull(viewMode)) {
985             viewMode = Constants.VIEW;
986         }
987 
988         if (_logTokens.isDebugEnabled()) {
989             String tokensString = PropertiesUtil.list(tokens);
990 
991             _logTokens.debug(tokensString);
992         }
993 
994         if (_logTransformBefore.isDebugEnabled()) {
995             _logTransformBefore.debug(xml);
996         }
997 
998         List<TransformerListener> listenersList =
999             new ArrayList<TransformerListener>();
1000
1001        String[] listeners = PropsUtil.getArray(
1002            PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
1003
1004        for (int i = 0; i < listeners.length; i++) {
1005            TransformerListener listener = null;
1006
1007            try {
1008                if (_log.isDebugEnabled()) {
1009                    _log.debug("Instantiate listener " + listeners[i]);
1010                }
1011
1012                boolean templateDriven = Validator.isNotNull(langType);
1013
1014                listener = (TransformerListener)Class.forName(
1015                    listeners[i]).newInstance();
1016
1017                listener.setTemplateDriven(templateDriven);
1018                listener.setLanguageId(languageId);
1019                listener.setTokens(tokens);
1020
1021                listenersList.add(listener);
1022            }
1023            catch (Exception e) {
1024                _log.error(e, e);
1025            }
1026
1027            // Modify XML
1028
1029            if (_logXmlBeforeListener.isDebugEnabled()) {
1030                _logXmlBeforeListener.debug(xml);
1031            }
1032
1033            if (listener != null) {
1034                xml = listener.onXml(xml);
1035
1036                if (_logXmlAfterListener.isDebugEnabled()) {
1037                    _logXmlAfterListener.debug(xml);
1038                }
1039            }
1040
1041            // Modify script
1042
1043            if (_logScriptBeforeListener.isDebugEnabled()) {
1044                _logScriptBeforeListener.debug(script);
1045            }
1046
1047            if (listener != null) {
1048                script = listener.onScript(script);
1049
1050                if (_logScriptAfterListener.isDebugEnabled()) {
1051                    _logScriptAfterListener.debug(script);
1052                }
1053            }
1054        }
1055
1056        // Transform
1057
1058        String output = null;
1059
1060        if (Validator.isNull(langType)) {
1061            output = LocalizationUtil.getLocalization(xml, languageId);
1062        }
1063        else {
1064            String templateParserClassName = PropsUtil.get(
1065                PropsKeys.JOURNAL_TEMPLATE_LANGUAGE_PARSER,
1066                new Filter(langType));
1067
1068            if (_log.isDebugEnabled()) {
1069                _log.debug(
1070                    "Template parser class name " + templateParserClassName);
1071            }
1072
1073            if (Validator.isNotNull(templateParserClassName)) {
1074                TemplateParser templateParser =
1075                    (TemplateParser)InstancePool.get(templateParserClassName);
1076
1077                if (templateParser == null) {
1078                    throw new TransformException(
1079                        "No template parser found for " +
1080                            templateParserClassName);
1081                }
1082
1083                output = templateParser.transform(
1084                    themeDisplay, tokens, viewMode, languageId, xml, script);
1085            }
1086        }
1087
1088        // Postprocess output
1089
1090        for (int i = 0; i < listenersList.size(); i++) {
1091            TransformerListener listener = listenersList.get(i);
1092
1093            // Modify output
1094
1095            if (_logOutputBeforeListener.isDebugEnabled()) {
1096                _logOutputBeforeListener.debug(output);
1097            }
1098
1099            output = listener.onOutput(output);
1100
1101            if (_logOutputAfterListener.isDebugEnabled()) {
1102                _logOutputAfterListener.debug(output);
1103            }
1104        }
1105
1106        if (_logTransfromAfter.isDebugEnabled()) {
1107            _logTransfromAfter.debug(output);
1108        }
1109
1110        return output;
1111    }
1112
1113    private static void _addElementOptions (
1114        Element curContentElement, Element newContentElement) {
1115
1116        List<Element> newElementOptions = newContentElement.elements("option");
1117
1118        for (Element newElementOption : newElementOptions) {
1119            Element curElementOption = SAXReaderUtil.createElement("option");
1120
1121            curElementOption.addCDATA(newElementOption.getText());
1122
1123            curContentElement.add(curElementOption);
1124        }
1125    }
1126
1127    private static Element _getElementByInstanceId(
1128        Document document, String instanceId) {
1129
1130        XPath xPathSelector = SAXReaderUtil.createXPath(
1131            "//dynamic-element[@instance-id='" + instanceId + "']");
1132
1133        List<Node> nodes = xPathSelector.selectNodes(document);
1134
1135        if (nodes.size() == 1) {
1136            return (Element)nodes.get(0);
1137        }
1138        else {
1139            return null;
1140        }
1141    }
1142
1143    private static void _mergeArticleContentDelete(
1144            Element curParentElement, Document newDocument)
1145        throws Exception {
1146
1147        List<Element> curElements = curParentElement.elements(
1148            "dynamic-element");
1149
1150        for (int i = 0; i < curElements.size(); i++) {
1151            Element curElement = curElements.get(i);
1152
1153            _mergeArticleContentDelete(curElement, newDocument);
1154
1155            String instanceId = curElement.attributeValue("instance-id");
1156
1157            Element newElement = _getElementByInstanceId(
1158                newDocument, instanceId);
1159
1160            if (newElement == null) {
1161                curElement.detach();
1162
1163                String type = curElement.attributeValue("type");
1164
1165                if (type.equals("image")) {
1166                    _mergeArticleContentDeleteImages(
1167                        curElement.elements("dynamic-content"));
1168                }
1169            }
1170        }
1171    }
1172
1173    private static void _mergeArticleContentDeleteImages(List<Element> elements)
1174        throws Exception {
1175
1176        for (Element element : elements) {
1177            long articleImageId = GetterUtil.getLong(
1178                element.attributeValue("id"));
1179
1180            JournalArticleImageLocalServiceUtil.deleteArticleImage(
1181                articleImageId);
1182        }
1183    }
1184
1185    private static void _mergeArticleContentUpdate(
1186            Document curDocument, Element newParentElement, Element newElement,
1187            int pos, String defaultLocale)
1188        throws Exception {
1189
1190        _mergeArticleContentUpdate(curDocument, newElement, defaultLocale);
1191
1192        String instanceId = newElement.attributeValue("instance-id");
1193
1194        Element curElement = _getElementByInstanceId(curDocument, instanceId);
1195
1196        if (curElement != null) {
1197            _mergeArticleContentUpdate(curElement, newElement, defaultLocale);
1198        }
1199        else {
1200            String parentInstanceId = newParentElement.attributeValue(
1201                "instance-id");
1202
1203            if (Validator.isNull(parentInstanceId)) {
1204                Element curRoot = curDocument.getRootElement();
1205
1206                List<Element> curRootElements = curRoot.elements();
1207
1208                curRootElements.add(pos, newElement.createCopy());
1209            }
1210            else {
1211                Element curParentElement = _getElementByInstanceId(
1212                    curDocument, parentInstanceId);
1213
1214                if (curParentElement != null) {
1215                    List<Element> curParentElements =
1216                        curParentElement.elements();
1217
1218                    curParentElements.add(pos, newElement.createCopy());
1219                }
1220            }
1221        }
1222    }
1223
1224    private static void _mergeArticleContentUpdate(
1225            Document curDocument, Element newParentElement,
1226            String defaultLocale)
1227        throws Exception {
1228
1229        List<Element> newElements = newParentElement.elements(
1230            "dynamic-element");
1231
1232        for (int i = 0; i < newElements.size(); i++) {
1233            Element newElement = newElements.get(i);
1234
1235            _mergeArticleContentUpdate(
1236                curDocument, newParentElement, newElement, i, defaultLocale);
1237        }
1238    }
1239
1240    private static void _mergeArticleContentUpdate(
1241        Element curElement, Element newElement, String defaultLocale) {
1242
1243        Element newContentElement = newElement.elements(
1244            "dynamic-content").get(0);
1245
1246        String newLanguageId = newContentElement.attributeValue("language-id");
1247        String newValue = newContentElement.getText();
1248
1249        List<Element> curContentElements = curElement.elements(
1250            "dynamic-content");
1251
1252        if (Validator.isNull(newLanguageId)) {
1253            for (Element curContentElement : curContentElements) {
1254                curContentElement.detach();
1255            }
1256
1257            Element curContentElement = SAXReaderUtil.createElement(
1258                "dynamic-content");
1259
1260            if (newContentElement.element("option") != null) {
1261                _addElementOptions(curContentElement, newContentElement);
1262            }
1263            else {
1264                curContentElement.addCDATA(newValue);
1265            }
1266
1267            curElement.add(curContentElement);
1268        }
1269        else {
1270            boolean alreadyExists = false;
1271
1272            for (Element curContentElement : curContentElements) {
1273                String curLanguageId = curContentElement.attributeValue(
1274                    "language-id");
1275
1276                if (newLanguageId.equals(curLanguageId)) {
1277                    alreadyExists = true;
1278
1279                    curContentElement.clearContent();
1280
1281                    if (newContentElement.element("option") != null) {
1282                        _addElementOptions(
1283                            curContentElement, newContentElement);
1284                    }
1285                    else {
1286                        curContentElement.addCDATA(newValue);
1287                    }
1288
1289                    break;
1290                }
1291            }
1292
1293            if (!alreadyExists) {
1294                Element curContentElement = curContentElements.get(0);
1295
1296                String curLanguageId = curContentElement.attributeValue(
1297                    "language-id");
1298
1299                if (Validator.isNull(curLanguageId)) {
1300                    curContentElement.detach();
1301                }
1302
1303                curElement.add(newContentElement.createCopy());
1304            }
1305        }
1306    }
1307
1308    private static void _populateCustomTokens(Map<String, String> tokens) {
1309        if (_customTokens == null) {
1310            synchronized (JournalUtil.class) {
1311                _customTokens = new HashMap<String, String>();
1312
1313                for (String customToken :
1314                        PropsValues.JOURNAL_ARTICLE_CUSTOM_TOKENS) {
1315
1316                    String value = PropsUtil.get(
1317                        PropsKeys.JOURNAL_ARTICLE_CUSTOM_TOKEN_VALUE,
1318                        new Filter(customToken));
1319
1320                    _customTokens.put(customToken, value);
1321                }
1322            }
1323        }
1324
1325        if (!_customTokens.isEmpty()) {
1326            tokens.putAll(_customTokens);
1327        }
1328    }
1329
1330    private static void _populateTokens(
1331            Map<String, String> tokens, long groupId, String xmlRequest)
1332        throws Exception {
1333
1334        Document request = SAXReaderUtil.read(xmlRequest);
1335
1336        Element root = request.getRootElement();
1337
1338        Element themeDisplayEl = root.element("theme-display");
1339
1340        Layout layout = LayoutLocalServiceUtil.getLayout(
1341            GetterUtil.getLong(themeDisplayEl.elementText("plid")));
1342
1343        Group group = layout.getGroup();
1344
1345        LayoutSet layoutSet = layout.getLayoutSet();
1346
1347        String friendlyUrlCurrent = null;
1348
1349        if (layout.isPublicLayout()) {
1350            friendlyUrlCurrent = themeDisplayEl.elementText(
1351                "path-friendly-url-public");
1352        }
1353        else if (group.isUserGroup()) {
1354            friendlyUrlCurrent = themeDisplayEl.elementText(
1355                "path-friendly-url-private-user");
1356        }
1357        else {
1358            friendlyUrlCurrent = themeDisplayEl.elementText(
1359                "path-friendly-url-private-group");
1360        }
1361
1362        String layoutSetFriendlyUrl = StringPool.BLANK;
1363
1364        String virtualHost = layoutSet.getVirtualHost();
1365
1366        if (Validator.isNull(virtualHost) ||
1367            !virtualHost.equals(themeDisplayEl.elementText("server-name"))) {
1368
1369            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1370        }
1371
1372        tokens.put("cdn_host", themeDisplayEl.elementText("cdn-host"));
1373        tokens.put("company_id", themeDisplayEl.elementText("company-id"));
1374        tokens.put("friendly_url_current", friendlyUrlCurrent);
1375        tokens.put(
1376            "friendly_url_private_group",
1377            themeDisplayEl.elementText("path-friendly-url-private-group"));
1378        tokens.put(
1379            "friendly_url_private_user",
1380            themeDisplayEl.elementText("path-friendly-url-private-user"));
1381        tokens.put(
1382            "friendly_url_public",
1383            themeDisplayEl.elementText("path-friendly-url-public"));
1384        tokens.put("group_friendly_url", group.getFriendlyURL());
1385        tokens.put("group_id", String.valueOf(groupId));
1386        tokens.put("image_path", themeDisplayEl.elementText("path-image"));
1387        tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1388        tokens.put("main_path", themeDisplayEl.elementText("path-main"));
1389        tokens.put("portal_ctx", themeDisplayEl.elementText("path-context"));
1390        tokens.put(
1391            "portal_url",
1392            HttpUtil.removeProtocol(themeDisplayEl.elementText("url-portal")));
1393        tokens.put(
1394            "protocol",
1395            HttpUtil.getProtocol(themeDisplayEl.elementText("url-portal")));
1396        tokens.put("root_path", themeDisplayEl.elementText("path-context"));
1397        tokens.put(
1398            "theme_image_path",
1399            themeDisplayEl.elementText("path-theme-images"));
1400
1401        _populateCustomTokens(tokens);
1402
1403        // Deprecated tokens
1404
1405        tokens.put(
1406            "friendly_url",
1407            themeDisplayEl.elementText("path-friendly-url-public"));
1408        tokens.put(
1409            "friendly_url_private",
1410            themeDisplayEl.elementText("path-friendly-url-private-group"));
1411        tokens.put(
1412            "page_url", themeDisplayEl.elementText("path-friendly-url-public"));
1413    }
1414
1415    private static void _populateTokens(
1416            Map<String, String> tokens, long groupId, ThemeDisplay themeDisplay)
1417        throws PortalException, SystemException {
1418
1419        Layout layout = themeDisplay.getLayout();
1420
1421        Group group = layout.getGroup();
1422
1423        LayoutSet layoutSet = layout.getLayoutSet();
1424
1425        String friendlyUrlCurrent = null;
1426
1427        if (layout.isPublicLayout()) {
1428            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPublic();
1429        }
1430        else if (group.isUserGroup()) {
1431            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateUser();
1432        }
1433        else {
1434            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateGroup();
1435        }
1436
1437        String layoutSetFriendlyUrl = StringPool.BLANK;
1438
1439        String virtualHost = layoutSet.getVirtualHost();
1440
1441        if (Validator.isNull(virtualHost) ||
1442            !virtualHost.equals(themeDisplay.getServerName())) {
1443
1444            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1445        }
1446
1447        tokens.put("cdn_host", themeDisplay.getCDNHost());
1448        tokens.put("company_id", String.valueOf(themeDisplay.getCompanyId()));
1449        tokens.put("friendly_url_current", friendlyUrlCurrent);
1450        tokens.put(
1451            "friendly_url_private_group",
1452            themeDisplay.getPathFriendlyURLPrivateGroup());
1453        tokens.put(
1454            "friendly_url_private_user",
1455            themeDisplay.getPathFriendlyURLPrivateUser());
1456        tokens.put(
1457            "friendly_url_public", themeDisplay.getPathFriendlyURLPublic());
1458        tokens.put("group_friendly_url", group.getFriendlyURL());
1459        tokens.put("group_id", String.valueOf(groupId));
1460        tokens.put("image_path", themeDisplay.getPathImage());
1461        tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1462        tokens.put("main_path", themeDisplay.getPathMain());
1463        tokens.put("portal_ctx", themeDisplay.getPathContext());
1464        tokens.put(
1465            "portal_url", HttpUtil.removeProtocol(themeDisplay.getURLPortal()));
1466        tokens.put(
1467            "protocol", HttpUtil.getProtocol(themeDisplay.getURLPortal()));
1468        tokens.put("root_path", themeDisplay.getPathContext());
1469        tokens.put("theme_image_path", themeDisplay.getPathThemeImages());
1470
1471        _populateCustomTokens(tokens);
1472
1473        // Deprecated tokens
1474
1475        tokens.put("friendly_url", themeDisplay.getPathFriendlyURLPublic());
1476        tokens.put(
1477            "friendly_url_private",
1478            themeDisplay.getPathFriendlyURLPrivateGroup());
1479        tokens.put("page_url", themeDisplay.getPathFriendlyURLPublic());
1480    }
1481
1482    private static void _removeOldContent(
1483            Stack<String> path, Element contentEl, Document xsdDoc)
1484        throws SystemException {
1485
1486        String elPath = "";
1487
1488        for (int i = 0; i < path.size(); i++) {
1489            elPath += "/" + path.elementAt(i);
1490        }
1491
1492        for (int i = 0; i < contentEl.nodeCount(); i++) {
1493            Node contentNode = contentEl.node(i);
1494
1495            if (contentNode instanceof Element) {
1496                _removeOldContent(path, (Element)contentNode, xsdDoc, elPath);
1497            }
1498        }
1499    }
1500
1501    private static void _removeOldContent(
1502            Stack<String> path, Element contentEl, Document xsdDoc,
1503            String elPath)
1504        throws SystemException {
1505
1506        String name = contentEl.attributeValue("name");
1507
1508        if (Validator.isNull(name)) {
1509            return;
1510        }
1511
1512        String localPath = "dynamic-element[@name='" + name + "']";
1513
1514        String fullPath = elPath + "/" + localPath;
1515
1516        XPath xPathSelector = SAXReaderUtil.createXPath(fullPath);
1517
1518        List<Node> curNodes = xPathSelector.selectNodes(xsdDoc);
1519
1520        if (curNodes.size() == 0) {
1521            contentEl.detach();
1522        }
1523
1524        path.push(localPath);
1525
1526        _removeOldContent(path, contentEl, xsdDoc);
1527
1528        path.pop();
1529    }
1530
1531    private static final char[] _URL_TITLE_REPLACE_CHARS = new char[] {
1532        '.', '/'
1533    };
1534
1535    private static Log _log = LogFactoryUtil.getLog(JournalUtil.class);
1536
1537    private static Log _logOutputAfterListener = LogFactoryUtil.getLog(
1538        JournalUtil.class.getName() + ".OutputAfterListener");
1539
1540    private static Log _logOutputBeforeListener = LogFactoryUtil.getLog(
1541        JournalUtil.class.getName() + ".OutputBeforeListener");
1542
1543    private static Log _logScriptAfterListener = LogFactoryUtil.getLog(
1544        JournalUtil.class.getName() + ".ScriptAfterListener");
1545
1546    private static Log _logScriptBeforeListener = LogFactoryUtil.getLog(
1547        JournalUtil.class.getName() + ".ScriptBeforeListener");
1548
1549    private static Log _logTokens = LogFactoryUtil.getLog(
1550        JournalUtil.class.getName() + ".Tokens");
1551
1552    private static Log _logTransformBefore = LogFactoryUtil.getLog(
1553        JournalUtil.class.getName() + ".BeforeTransform");
1554
1555    private static Log _logTransfromAfter = LogFactoryUtil.getLog(
1556        JournalUtil.class.getName() + ".TransformAfter");
1557
1558    private static Log _logXmlAfterListener = LogFactoryUtil.getLog(
1559        JournalUtil.class.getName() + ".XmlAfterListener");
1560
1561    private static Log _logXmlBeforeListener = LogFactoryUtil.getLog(
1562        JournalUtil.class.getName() + ".XmlBeforeListener");
1563
1564    private static Map<String, String> _customTokens;
1565
1566}