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