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