1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.journal.util;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.util.GetterUtil;
28  import com.liferay.portal.kernel.util.HttpUtil;
29  import com.liferay.portal.kernel.util.LocaleUtil;
30  import com.liferay.portal.kernel.util.OrderByComparator;
31  import com.liferay.portal.kernel.util.PropertiesUtil;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.util.Validator;
35  import com.liferay.portal.model.Contact;
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.FiniteUniqueStack;
57  import com.liferay.util.Time;
58  import com.liferay.util.xml.XMLFormatter;
59  
60  import java.io.IOException;
61  import java.io.StringReader;
62  import java.io.UnsupportedEncodingException;
63  
64  import java.util.ArrayList;
65  import java.util.Date;
66  import java.util.HashMap;
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<JournalArticle> 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<JournalStructure> 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<JournalTemplate> stack = getRecentTemplates(req);
126 
127             stack.push(template);
128         }
129     }
130 
131     public static void addReservedEl(
132         Element root, Map<String, String> 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<String, String> 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<String, String> 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<String, String> 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<JournalArticle> getRecentArticles(PortletRequest req) {
530         PortletSession ses = req.getPortletSession();
531 
532         Stack<JournalArticle> recentArticles =
533             (Stack<JournalArticle>)ses.getAttribute(
534                 WebKeys.JOURNAL_RECENT_ARTICLES);
535 
536         if (recentArticles == null) {
537             recentArticles = new FiniteUniqueStack<JournalArticle>(
538                 MAX_STACK_SIZE);
539 
540             ses.setAttribute(WebKeys.JOURNAL_RECENT_ARTICLES, recentArticles);
541         }
542 
543         return recentArticles;
544     }
545 
546     public static Stack<JournalStructure> getRecentStructures(
547         PortletRequest req) {
548 
549         PortletSession ses = req.getPortletSession();
550 
551         Stack<JournalStructure> recentStructures =
552             (Stack<JournalStructure>)ses.getAttribute(
553                 WebKeys.JOURNAL_RECENT_STRUCTURES);
554 
555         if (recentStructures == null) {
556             recentStructures = new FiniteUniqueStack<JournalStructure>(
557                 MAX_STACK_SIZE);
558 
559             ses.setAttribute(
560                 WebKeys.JOURNAL_RECENT_STRUCTURES, recentStructures);
561         }
562 
563         return recentStructures;
564     }
565 
566     public static Stack<JournalTemplate> getRecentTemplates(
567         PortletRequest req) {
568 
569         PortletSession ses = req.getPortletSession();
570 
571         Stack<JournalTemplate> recentTemplates =
572             (Stack<JournalTemplate>)ses.getAttribute(
573                 WebKeys.JOURNAL_RECENT_TEMPLATES);
574 
575         if (recentTemplates == null) {
576             recentTemplates = new FiniteUniqueStack<JournalTemplate>(
577                 MAX_STACK_SIZE);
578 
579             ses.setAttribute(WebKeys.JOURNAL_RECENT_TEMPLATES, recentTemplates);
580         }
581 
582         return recentTemplates;
583     }
584 
585     public static String getTemplateScript(
586             long groupId, String templateId, Map<String, String> tokens,
587             String languageId)
588         throws PortalException, SystemException {
589 
590         return getTemplateScript(groupId, templateId, tokens, languageId, true);
591     }
592 
593     public static String getTemplateScript(
594             long groupId, String templateId, Map<String, String> tokens,
595             String languageId, boolean transform)
596         throws PortalException, SystemException {
597 
598         JournalTemplate template = JournalTemplateLocalServiceUtil.getTemplate(
599             groupId, templateId);
600 
601         return getTemplateScript(template, tokens, languageId, transform);
602     }
603 
604     public static String getTemplateScript(
605             JournalTemplate template, Map<String, String> tokens,
606             String languageId, boolean transform)
607         throws PortalException, SystemException {
608 
609         String script = template.getXsl();
610 
611         if (transform) {
612 
613             // Listeners
614 
615             String[] listeners =
616                 PropsUtil.getArray(PropsUtil.JOURNAL_TRANSFORMER_LISTENER);
617 
618             for (int i = 0; i < listeners.length; i++) {
619                 TransformerListener listener = null;
620 
621                 try {
622                     listener =
623                         (TransformerListener)Class.forName(
624                             listeners[i]).newInstance();
625 
626                     listener.setTemplateDriven(true);
627                     listener.setLanguageId(languageId);
628                     listener.setTokens(tokens);
629                 }
630                 catch (Exception e) {
631                     e.printStackTrace();
632                 }
633 
634                 // Modify transform script
635 
636                 if (listener != null) {
637                     script = listener.onScript(script);
638                 }
639             }
640         }
641 
642         return script;
643     }
644 
645     public static Map<String, String> getTokens(
646         long groupId, ThemeDisplay themeDisplay) {
647 
648         Map<String, String> tokens = new HashMap<String, String>();
649 
650         if (themeDisplay == null) {
651             return tokens;
652         }
653 
654         tokens.put("cdn_host", themeDisplay.getCDNHost());
655         tokens.put("company_id", String.valueOf(themeDisplay.getCompanyId()));
656         tokens.put("group_id", String.valueOf(groupId));
657         tokens.put("cms_url", themeDisplay.getPathContext() + "/cms/servlet");
658         tokens.put("image_path", themeDisplay.getPathImage());
659         tokens.put(
660             "friendly_url_private_group",
661             themeDisplay.getPathFriendlyURLPrivateGroup());
662         tokens.put(
663             "friendly_url_private_user",
664             themeDisplay.getPathFriendlyURLPrivateUser());
665         tokens.put(
666             "friendly_url_public", themeDisplay.getPathFriendlyURLPublic());
667         tokens.put("main_path", themeDisplay.getPathMain());
668         tokens.put("portal_ctx", themeDisplay.getPathContext());
669         tokens.put(
670             "portal_url", HttpUtil.removeProtocol(themeDisplay.getURLPortal()));
671         tokens.put("root_path", themeDisplay.getPathContext());
672         tokens.put("theme_image_path", themeDisplay.getPathThemeImages());
673 
674         // Deprecated tokens
675 
676         tokens.put("friendly_url", themeDisplay.getPathFriendlyURLPublic());
677         tokens.put(
678             "friendly_url_private",
679             themeDisplay.getPathFriendlyURLPrivateGroup());
680         tokens.put("page_url", themeDisplay.getPathFriendlyURLPublic());
681 
682         return tokens;
683     }
684 
685     public static String mergeLocaleContent(
686         String curContent, String newContent, String xsd) {
687 
688         try {
689             SAXReader reader = new SAXReader();
690 
691             Document curContentDoc = reader.read(new StringReader(curContent));
692             Document newContentDoc = reader.read(new StringReader(newContent));
693             Document xsdDoc = reader.read(new StringReader(xsd));
694 
695             Element curContentRoot = curContentDoc.getRootElement();
696             Element newContentRoot = newContentDoc.getRootElement();
697             Element xsdRoot = xsdDoc.getRootElement();
698 
699             curContentRoot.addAttribute(
700                 "default-locale",
701                 newContentRoot.attributeValue("default-locale"));
702             curContentRoot.addAttribute(
703                 "available-locales",
704                 newContentRoot.attributeValue("available-locales"));
705 
706             Stack<String> path = new Stack<String>();
707 
708             path.push(xsdRoot.getName());
709 
710             _mergeLocaleContent(
711                 path, curContentDoc, newContentDoc, xsdRoot,
712                 LocaleUtil.toLanguageId(LocaleUtil.getDefault()));
713 
714             curContent = formatXML(curContentDoc);
715         }
716         catch (Exception e) {
717             _log.error(e);
718         }
719 
720         return curContent;
721     }
722 
723     public static String removeArticleLocale(
724         String content, String languageId) {
725 
726         try {
727             SAXReader reader = new SAXReader();
728 
729             Document doc = reader.read(new StringReader(content));
730 
731             Element root = doc.getRootElement();
732 
733             String availableLocales = root.attributeValue("available-locales");
734 
735             if (availableLocales == null) {
736                 return content;
737             }
738 
739             availableLocales = StringUtil.remove(availableLocales, languageId);
740 
741             if (availableLocales.endsWith(",")) {
742                 availableLocales = availableLocales.substring(
743                     0, availableLocales.length() - 1);
744             }
745 
746             root.addAttribute("available-locales", availableLocales);
747 
748             removeArticleLocale(root, languageId);
749 
750             content = formatXML(doc);
751         }
752         catch (Exception e) {
753             _log.error(e);
754         }
755 
756         return content;
757     }
758 
759     public static void removeArticleLocale(Element el, String languageId)
760         throws SystemException {
761 
762         for (Element dynamicEl :
763                 (List<Element>)el.elements("dynamic-element")) {
764 
765             for (Element dynamicContentEl :
766                     (List<Element>)dynamicEl.elements("dynamic-content")) {
767 
768                 String curLanguageId = GetterUtil.getString(
769                     dynamicContentEl.attributeValue("language-id"));
770 
771                 if (curLanguageId.equals(languageId)) {
772                     long id = GetterUtil.getLong(
773                         dynamicContentEl.attributeValue("id"));
774 
775                     if (id > 0) {
776                         ImageLocalUtil.deleteImage(id);
777                     }
778 
779                     dynamicContentEl.detach();
780                 }
781             }
782 
783             removeArticleLocale(dynamicEl, languageId);
784         }
785     }
786 
787     public static String removeOldContent(String content, String xsd) {
788         try {
789             SAXReader reader = new SAXReader();
790 
791             Document contentDoc = reader.read(new StringReader(content));
792             Document xsdDoc = reader.read(new StringReader(xsd));
793 
794             Element contentRoot = contentDoc.getRootElement();
795 
796             Stack<String> path = new Stack<String>();
797 
798             path.push(contentRoot.getName());
799 
800             _removeOldContent(path, contentRoot, xsdDoc);
801 
802             content = formatXML(contentDoc);
803         }
804         catch (Exception e) {
805             _log.error(e);
806         }
807 
808         return content;
809     }
810 
811     public static void removeRecentArticle(
812         PortletRequest req, String articleId) {
813 
814         Stack<JournalArticle> stack = getRecentArticles(req);
815 
816         Iterator<JournalArticle> itr = stack.iterator();
817 
818         while (itr.hasNext()) {
819             JournalArticle journalArticle = itr.next();
820 
821             if (journalArticle.getArticleId().equals(articleId)) {
822                 itr.remove();
823 
824                 break;
825             }
826         }
827     }
828 
829     public static void removeRecentStructure(
830         PortletRequest req, String structureId) {
831 
832         Stack<JournalStructure> stack = getRecentStructures(req);
833 
834         Iterator<JournalStructure> itr = stack.iterator();
835 
836         while (itr.hasNext()) {
837             JournalStructure journalStructure = itr.next();
838 
839             if (journalStructure.getStructureId().equals(structureId)) {
840                 itr.remove();
841 
842                 break;
843             }
844         }
845     }
846 
847     public static void removeRecentTemplate(
848         PortletRequest req, String templateId) {
849 
850         Stack<JournalTemplate> stack = getRecentTemplates(req);
851 
852         Iterator<JournalTemplate> itr = stack.iterator();
853 
854         while (itr.hasNext()) {
855             JournalTemplate journalTemplate = itr.next();
856 
857             if (journalTemplate.getTemplateId().equals(templateId)) {
858                 itr.remove();
859 
860                 break;
861             }
862         }
863     }
864 
865     public static String transform(
866             Map<String, String> tokens, String languageId, String xml,
867             String script, String langType)
868         throws TransformException, UnsupportedEncodingException {
869 
870         // Setup Listeners
871 
872         if (_log.isDebugEnabled()) {
873             _log.debug("Language " + languageId);
874         }
875 
876         if (_logTokens.isDebugEnabled()) {
877             String tokensString = PropertiesUtil.list(tokens);
878 
879             _logTokens.debug(tokensString);
880         }
881 
882         if (_logTransformBefore.isDebugEnabled()) {
883             _logTransformBefore.debug(xml);
884         }
885 
886         List<TransformerListener> listenersList =
887             new ArrayList<TransformerListener>();
888 
889         String[] listeners = PropsUtil.getArray(
890             PropsUtil.JOURNAL_TRANSFORMER_LISTENER);
891 
892         for (int i = 0; i < listeners.length; i++) {
893             TransformerListener listener = null;
894 
895             try {
896                 if (_log.isDebugEnabled()) {
897                     _log.debug("Instantiate listener " + listeners[i]);
898                 }
899 
900                 boolean templateDriven = Validator.isNotNull(langType);
901 
902                 listener = (TransformerListener)Class.forName(
903                     listeners[i]).newInstance();
904 
905                 listener.setTemplateDriven(templateDriven);
906                 listener.setLanguageId(languageId);
907                 listener.setTokens(tokens);
908 
909                 listenersList.add(listener);
910             }
911             catch (Exception e) {
912                 _log.error(e, e);
913             }
914 
915             // Modify XML
916 
917             if (_logXmlBeforeListener.isDebugEnabled()) {
918                 _logXmlBeforeListener.debug(xml);
919             }
920 
921             if (listener != null) {
922                 xml = listener.onXml(xml);
923 
924                 if (_logXmlAfterListener.isDebugEnabled()) {
925                     _logXmlAfterListener.debug(xml);
926                 }
927             }
928 
929             // Modify script
930 
931             if (_logScriptBeforeListener.isDebugEnabled()) {
932                 _logScriptBeforeListener.debug(script);
933             }
934 
935             if (listener != null) {
936                 script = listener.onScript(script);
937 
938                 if (_logScriptAfterListener.isDebugEnabled()) {
939                     _logScriptAfterListener.debug(script);
940                 }
941             }
942         }
943 
944         // Transform
945 
946         String output = null;
947 
948         if (Validator.isNull(langType)) {
949             output = xml;
950         }
951         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_VM)) {
952             output = JournalVmUtil.transform(tokens, languageId, xml, script);
953         }
954         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_XSL)) {
955             output = JournalXslUtil.transform(tokens, languageId, xml, script);
956         }
957 
958         // Postprocess output
959 
960         for (int i = 0; i < listenersList.size(); i++) {
961             TransformerListener listener = listenersList.get(i);
962 
963             // Modify output
964 
965             if (_logOutputBeforeListener.isDebugEnabled()) {
966                 _logOutputBeforeListener.debug(output);
967             }
968 
969             output = listener.onOutput(output);
970 
971             if (_logOutputAfterListener.isDebugEnabled()) {
972                 _logOutputAfterListener.debug(output);
973             }
974         }
975 
976         if (_logTransfromAfter.isDebugEnabled()) {
977             _logTransfromAfter.debug(output);
978         }
979 
980         return output;
981     }
982 
983     private static void _mergeLocaleContent(
984             Stack<String> path, Document curDoc, Document newDoc, Element xsdEl,
985             String defaultLocale)
986         throws SystemException {
987 
988         String elPath = "";
989 
990         for (int i = 0; i < path.size(); i++) {
991             elPath += "/" + path.elementAt(i);
992         }
993 
994         for (int i = 0; i < xsdEl.nodeCount(); i++) {
995             Node xsdNode = xsdEl.node(i);
996 
997             if ((xsdNode instanceof Element) &&
998                 (xsdNode.getName().equals("dynamic-element"))) {
999 
1000                _mergeLocaleContent(
1001                    path, curDoc, newDoc, (Element)xsdNode, defaultLocale,
1002                    elPath);
1003            }
1004        }
1005    }
1006
1007    private static void _mergeLocaleContent(
1008            Stack<String> path, Document curDoc, Document newDoc, Element xsdEl,
1009            String defaultLocale, String elPath)
1010        throws SystemException {
1011
1012        String name = xsdEl.attributeValue("name");
1013
1014        String localPath = "dynamic-element[@name='" + name + "']";
1015
1016        String fullPath = elPath + "/" + localPath;
1017
1018        XPath xPathSelector = DocumentHelper.createXPath(fullPath);
1019
1020        List<Element> curElements = xPathSelector.selectNodes(curDoc);
1021
1022        Element newEl = (Element)xPathSelector.selectNodes(newDoc).get(0);
1023
1024        if (curElements.size() > 0) {
1025            Element curEl = curElements.get(0);
1026
1027            List<Element> curDynamicContents = curEl.elements(
1028                "dynamic-content");
1029
1030            Element newContentEl = newEl.element("dynamic-content");
1031
1032            String newContentLanguageId = newContentEl.attributeValue(
1033                "language-id", StringPool.BLANK);
1034
1035            if (newContentLanguageId.equals(StringPool.BLANK)) {
1036                for (int k = curDynamicContents.size() - 1; k >= 0 ; k--) {
1037                    Element curContentEl = curDynamicContents.get(k);
1038
1039                    String curContentLanguageId = curContentEl.attributeValue(
1040                        "language-id", StringPool.BLANK);
1041
1042                    if ((curEl.attributeValue("type").equals("image")) &&
1043                        (!curContentLanguageId.equals(defaultLocale) &&
1044                         !curContentLanguageId.equals(StringPool.BLANK))) {
1045
1046                        long id = GetterUtil.getLong(
1047                            curContentEl.attributeValue("id"));
1048
1049                        ImageLocalUtil.deleteImage(id);
1050                    }
1051
1052                    curContentEl.detach();
1053                }
1054
1055                curEl.content().add(newContentEl.createCopy());
1056            }
1057            else {
1058                boolean match = false;
1059
1060                for (int k = curDynamicContents.size() - 1; k >= 0 ; k--) {
1061                    Element curContentEl = curDynamicContents.get(k);
1062
1063                    String curContentLanguageId = curContentEl.attributeValue(
1064                        "language-id", StringPool.BLANK);
1065
1066                    if ((newContentLanguageId.equals(curContentLanguageId)) ||
1067                        (newContentLanguageId.equals(defaultLocale) &&
1068                         curContentLanguageId.equals(StringPool.BLANK))) {
1069
1070                        curContentEl.detach();
1071
1072                        curEl.content().add(k, newContentEl.createCopy());
1073
1074                        match = true;
1075                    }
1076
1077                    if (curContentLanguageId.equals(StringPool.BLANK)) {
1078                        curContentEl.addAttribute("language-id", defaultLocale);
1079                    }
1080                }
1081
1082                if (!match) {
1083                    curEl.content().add(newContentEl.createCopy());
1084                }
1085            }
1086        }
1087        else {
1088            xPathSelector = DocumentHelper.createXPath(elPath);
1089
1090            Element parentEl =
1091                (Element)xPathSelector.selectNodes(curDoc).get(0);
1092
1093            parentEl.content().add(newEl.createCopy());
1094        }
1095
1096        String type = xsdEl.attributeValue("type", StringPool.BLANK);
1097
1098        if (!type.equals("list") && !type.equals("multi-list")) {
1099            path.push(localPath);
1100
1101            _mergeLocaleContent(path, curDoc, newDoc, xsdEl, defaultLocale);
1102
1103            path.pop();
1104        }
1105    }
1106
1107    private static void _removeOldContent(
1108            Stack<String> path, Element contentEl, Document xsdDoc)
1109        throws SystemException {
1110
1111        String elPath = "";
1112
1113        for (int i = 0; i < path.size(); i++) {
1114            elPath += "/" + path.elementAt(i);
1115        }
1116
1117        for (int i = 0; i < contentEl.nodeCount(); i++) {
1118            Node contentNode = contentEl.node(i);
1119
1120            if (contentNode instanceof Element) {
1121                _removeOldContent(path, (Element)contentNode, xsdDoc, elPath);
1122            }
1123        }
1124    }
1125
1126    private static void _removeOldContent(
1127            Stack<String> path, Element contentEl, Document xsdDoc,
1128            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<Element> 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    private static Log _logOutputAfterListener = LogFactory.getLog(
1159        JournalUtil.class.getName() + ".OutputAfterListener");
1160
1161    private static Log _logOutputBeforeListener = LogFactory.getLog(
1162        JournalUtil.class.getName() + ".OutputBeforeListener");
1163
1164    private static Log _logScriptAfterListener = LogFactory.getLog(
1165        JournalUtil.class.getName() + ".ScriptAfterListener");
1166
1167    private static Log _logScriptBeforeListener = LogFactory.getLog(
1168        JournalUtil.class.getName() + ".ScriptBeforeListener");
1169
1170    private static Log _logTransfromAfter = LogFactory.getLog(
1171        JournalUtil.class.getName() + ".TransformAfter");
1172
1173    private static Log _logTransformBefore = LogFactory.getLog(
1174        JournalUtil.class.getName() + ".BeforeTransform");
1175
1176    private static Log _logTokens = LogFactory.getLog(
1177        JournalUtil.class.getName() + ".Tokens");
1178
1179    private static Log _logXmlAfterListener = LogFactory.getLog(
1180        JournalUtil.class.getName() + ".XmlAfterListener");
1181
1182    private static Log _logXmlBeforeListener = LogFactory.getLog(
1183        JournalUtil.class.getName() + ".XmlBeforeListener");
1184
1185}