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