1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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.log.Log;
28  import com.liferay.portal.kernel.log.LogFactoryUtil;
29  import com.liferay.portal.kernel.util.Constants;
30  import com.liferay.portal.kernel.util.GetterUtil;
31  import com.liferay.portal.kernel.util.HttpUtil;
32  import com.liferay.portal.kernel.util.LocaleUtil;
33  import com.liferay.portal.kernel.util.OrderByComparator;
34  import com.liferay.portal.kernel.util.PropertiesUtil;
35  import com.liferay.portal.kernel.util.PropsKeys;
36  import com.liferay.portal.kernel.util.StringPool;
37  import com.liferay.portal.kernel.util.StringUtil;
38  import com.liferay.portal.kernel.util.Time;
39  import com.liferay.portal.kernel.util.Validator;
40  import com.liferay.portal.kernel.xml.Document;
41  import com.liferay.portal.kernel.xml.Element;
42  import com.liferay.portal.kernel.xml.Node;
43  import com.liferay.portal.kernel.xml.SAXReaderUtil;
44  import com.liferay.portal.kernel.xml.XPath;
45  import com.liferay.portal.model.Group;
46  import com.liferay.portal.model.Layout;
47  import com.liferay.portal.model.LayoutSet;
48  import com.liferay.portal.model.User;
49  import com.liferay.portal.service.ImageLocalServiceUtil;
50  import com.liferay.portal.service.LayoutLocalServiceUtil;
51  import com.liferay.portal.service.UserLocalServiceUtil;
52  import com.liferay.portal.theme.ThemeDisplay;
53  import com.liferay.portal.util.ContentUtil;
54  import com.liferay.portal.util.PropsUtil;
55  import com.liferay.portal.util.WebKeys;
56  import com.liferay.portlet.journal.model.JournalArticle;
57  import com.liferay.portlet.journal.model.JournalStructure;
58  import com.liferay.portlet.journal.model.JournalTemplate;
59  import com.liferay.portlet.journal.model.impl.JournalStructureImpl;
60  import com.liferay.portlet.journal.model.impl.JournalTemplateImpl;
61  import com.liferay.portlet.journal.service.JournalArticleImageLocalServiceUtil;
62  import com.liferay.portlet.journal.service.JournalTemplateLocalServiceUtil;
63  import com.liferay.portlet.journal.util.comparator.ArticleCreateDateComparator;
64  import com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator;
65  import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
66  import com.liferay.portlet.journal.util.comparator.ArticleModifiedDateComparator;
67  import com.liferay.portlet.journal.util.comparator.ArticleReviewDateComparator;
68  import com.liferay.portlet.journal.util.comparator.ArticleTitleComparator;
69  import com.liferay.util.FiniteUniqueStack;
70  import com.liferay.util.LocalizationUtil;
71  import com.liferay.util.xml.XMLFormatter;
72  
73  import java.io.IOException;
74  
75  import java.util.ArrayList;
76  import java.util.Date;
77  import java.util.HashMap;
78  import java.util.Iterator;
79  import java.util.List;
80  import java.util.Map;
81  import java.util.Stack;
82  
83  import javax.portlet.PortletPreferences;
84  import javax.portlet.PortletRequest;
85  import javax.portlet.PortletSession;
86  
87  /**
88   * <a href="JournalUtil.java.html"><b><i>View Source</i></b></a>
89   *
90   * @author Brian Wing Shun Chan
91   * @author Raymond Augé
92   * @author Wesley Gong
93   */
94  public class JournalUtil {
95  
96      public static final int MAX_STACK_SIZE = 20;
97  
98      public static final String XML_INDENT = "  ";
99  
100     public static void addRecentArticle(
101         PortletRequest portletRequest, JournalArticle article) {
102 
103         if (article != null) {
104             Stack<JournalArticle> stack = getRecentArticles(portletRequest);
105 
106             stack.push(article);
107         }
108     }
109 
110     public static void addRecentStructure(
111         PortletRequest portletRequest, JournalStructure structure) {
112 
113         if (structure != null) {
114             Stack<JournalStructure> stack = getRecentStructures(portletRequest);
115 
116             stack.push(structure);
117         }
118     }
119 
120     public static void addRecentTemplate(
121         PortletRequest portletRequest, JournalTemplate template) {
122 
123         if (template != null) {
124             Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
125 
126             stack.push(template);
127         }
128     }
129 
130     public static void addReservedEl(
131         Element root, Map<String, String> tokens, String name, double value) {
132 
133         addReservedEl(root, tokens, name, String.valueOf(value));
134     }
135 
136     public static void addReservedEl(
137         Element root, Map<String, String> tokens, String name, Date value) {
138 
139         addReservedEl(root, tokens, name, Time.getRFC822(value));
140     }
141 
142     public static void addReservedEl(
143         Element root, Map<String, String> tokens, String name, String value) {
144 
145         // XML
146 
147         if (root != null) {
148             Element dynamicEl = SAXReaderUtil.createElement("dynamic-element");
149 
150             dynamicEl.add(
151                 SAXReaderUtil.createAttribute(dynamicEl, "name", name));
152             dynamicEl.add(
153                 SAXReaderUtil.createAttribute(dynamicEl, "type", "text"));
154 
155             Element dynamicContent = SAXReaderUtil.createElement(
156                 "dynamic-content");
157 
158             //dynamicContent.setText("<![CDATA[" + value + "]]>");
159             dynamicContent.setText(value);
160 
161             dynamicEl.add(dynamicContent);
162 
163             root.add(dynamicEl);
164         }
165 
166         // Tokens
167 
168         tokens.put(
169             StringUtil.replace(name, StringPool.DASH, StringPool.UNDERLINE),
170             value);
171     }
172 
173     public static void addAllReservedEls(
174         Element root, Map<String, String> tokens, JournalArticle article) {
175 
176         JournalUtil.addReservedEl(
177             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_ID,
178             article.getArticleId());
179 
180         JournalUtil.addReservedEl(
181             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_VERSION,
182             article.getVersion());
183 
184         JournalUtil.addReservedEl(
185             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_TITLE,
186             article.getTitle());
187 
188         JournalUtil.addReservedEl(
189             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_URL_TITLE,
190             article.getUrlTitle());
191 
192         JournalUtil.addReservedEl(
193             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_DESCRIPTION,
194             article.getDescription());
195 
196         JournalUtil.addReservedEl(
197             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_TYPE,
198             article.getType());
199 
200         JournalUtil.addReservedEl(
201             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_CREATE_DATE,
202             article.getCreateDate());
203 
204         JournalUtil.addReservedEl(
205             root, tokens,
206             JournalStructureImpl.RESERVED_ARTICLE_MODIFIED_DATE,
207             article.getModifiedDate());
208 
209         if (article.getDisplayDate() != null) {
210             JournalUtil.addReservedEl(
211                 root, tokens,
212                 JournalStructureImpl.RESERVED_ARTICLE_DISPLAY_DATE,
213                 article.getDisplayDate());
214         }
215 
216         JournalUtil.addReservedEl(
217             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_SMALL_IMAGE_URL,
218             article.getSmallImageURL());
219 
220         JournalUtil.addReservedEl(
221             root, tokens, JournalStructureImpl.RESERVED_ARTICLE_AUTHOR_ID,
222             String.valueOf(article.getUserId()));
223 
224         String userName = StringPool.BLANK;
225         String userEmailAddress = StringPool.BLANK;
226         String userComments = StringPool.BLANK;
227         String userJobTitle = StringPool.BLANK;
228 
229         User user = null;
230 
231         try {
232             user = UserLocalServiceUtil.getUserById(article.getUserId());
233 
234             userName = user.getFullName();
235             userEmailAddress = user.getEmailAddress();
236             userComments = user.getComments();
237             userJobTitle = user.getJobTitle();
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 preferences) {
325         String emailFromAddress = PropsUtil.get(
326             PropsKeys.JOURNAL_EMAIL_FROM_ADDRESS);
327 
328         return preferences.getValue("email-from-address", emailFromAddress);
329     }
330 
331     public static String getEmailFromName(PortletPreferences preferences) {
332         String emailFromName = PropsUtil.get(
333             PropsKeys.JOURNAL_EMAIL_FROM_NAME);
334 
335         return preferences.getValue("email-from-name", emailFromName);
336     }
337 
338     public static boolean getEmailArticleApprovalDeniedEnabled(
339         PortletPreferences preferences) {
340 
341         String emailArticleApprovalDeniedEnabled = preferences.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 preferences) {
355 
356         String emailArticleApprovalDeniedBody = preferences.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 preferences) {
370 
371         String emailArticleApprovalDeniedSubject = preferences.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 preferences) {
385 
386         String emailArticleApprovalGrantedEnabled = preferences.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 preferences) {
400 
401         String emailArticleApprovalGrantedBody = preferences.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 preferences) {
415 
416         String emailArticleApprovalGrantedSubject = preferences.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 preferences) {
430 
431         String emailArticleApprovalRequestedEnabled = preferences.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 preferences) {
445 
446         String emailArticleApprovalRequestedBody = preferences.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 preferences) {
460 
461         String emailArticleApprovalRequestedSubject = preferences.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 preferences) {
475 
476         String emailArticleReviewEnabled = preferences.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(
489         PortletPreferences preferences) {
490 
491         String emailArticleReviewBody = preferences.getValue(
492             "email-article-review-body", StringPool.BLANK);
493 
494         if (Validator.isNotNull(emailArticleReviewBody)) {
495             return emailArticleReviewBody;
496         }
497         else {
498             return ContentUtil.get(PropsUtil.get(
499                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_BODY));
500         }
501     }
502 
503     public static String getEmailArticleReviewSubject(
504         PortletPreferences preferences) {
505 
506         String emailArticleReviewSubject = preferences.getValue(
507             "email-article-review-subject", StringPool.BLANK);
508 
509         if (Validator.isNotNull(emailArticleReviewSubject)) {
510             return emailArticleReviewSubject;
511         }
512         else {
513             return ContentUtil.get(PropsUtil.get(
514                 PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_SUBJECT));
515         }
516     }
517 
518     public static Stack<JournalArticle> getRecentArticles(
519         PortletRequest portletRequest) {
520 
521         PortletSession portletSession = portletRequest.getPortletSession();
522 
523         Stack<JournalArticle> recentArticles =
524             (Stack<JournalArticle>)portletSession.getAttribute(
525                 WebKeys.JOURNAL_RECENT_ARTICLES);
526 
527         if (recentArticles == null) {
528             recentArticles = new FiniteUniqueStack<JournalArticle>(
529                 MAX_STACK_SIZE);
530 
531             portletSession.setAttribute(
532                 WebKeys.JOURNAL_RECENT_ARTICLES, recentArticles);
533         }
534 
535         return recentArticles;
536     }
537 
538     public static Stack<JournalStructure> getRecentStructures(
539         PortletRequest portletRequest) {
540 
541         PortletSession portletSession = portletRequest.getPortletSession();
542 
543         Stack<JournalStructure> recentStructures =
544             (Stack<JournalStructure>)portletSession.getAttribute(
545                 WebKeys.JOURNAL_RECENT_STRUCTURES);
546 
547         if (recentStructures == null) {
548             recentStructures = new FiniteUniqueStack<JournalStructure>(
549                 MAX_STACK_SIZE);
550 
551             portletSession.setAttribute(
552                 WebKeys.JOURNAL_RECENT_STRUCTURES, recentStructures);
553         }
554 
555         return recentStructures;
556     }
557 
558     public static Stack<JournalTemplate> getRecentTemplates(
559         PortletRequest portletRequest) {
560 
561         PortletSession portletSession = portletRequest.getPortletSession();
562 
563         Stack<JournalTemplate> recentTemplates =
564             (Stack<JournalTemplate>)portletSession.getAttribute(
565                 WebKeys.JOURNAL_RECENT_TEMPLATES);
566 
567         if (recentTemplates == null) {
568             recentTemplates = new FiniteUniqueStack<JournalTemplate>(
569                 MAX_STACK_SIZE);
570 
571             portletSession.setAttribute(
572                 WebKeys.JOURNAL_RECENT_TEMPLATES, recentTemplates);
573         }
574 
575         return recentTemplates;
576     }
577 
578     public static String getTemplateScript(
579             long groupId, String templateId, Map<String, String> tokens,
580             String languageId)
581         throws PortalException, SystemException {
582 
583         return getTemplateScript(groupId, templateId, tokens, languageId, true);
584     }
585 
586     public static String getTemplateScript(
587             long groupId, String templateId, Map<String, String> tokens,
588             String languageId, boolean transform)
589         throws PortalException, SystemException {
590 
591         JournalTemplate template = JournalTemplateLocalServiceUtil.getTemplate(
592             groupId, templateId);
593 
594         return getTemplateScript(template, tokens, languageId, transform);
595     }
596 
597     public static String getTemplateScript(
598         JournalTemplate template, Map<String, String> tokens, String languageId,
599         boolean transform) {
600 
601         String script = template.getXsl();
602 
603         if (transform) {
604 
605             // Listeners
606 
607             String[] listeners =
608                 PropsUtil.getArray(PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
609 
610             for (int i = 0; i < listeners.length; i++) {
611                 TransformerListener listener = null;
612 
613                 try {
614                     listener =
615                         (TransformerListener)Class.forName(
616                             listeners[i]).newInstance();
617 
618                     listener.setTemplateDriven(true);
619                     listener.setLanguageId(languageId);
620                     listener.setTokens(tokens);
621                 }
622                 catch (Exception e) {
623                     _log.error(e, e);
624                 }
625 
626                 // Modify transform script
627 
628                 if (listener != null) {
629                     script = listener.onScript(script);
630                 }
631             }
632         }
633 
634         return script;
635     }
636 
637     public static Map<String, String> getTokens(
638         long groupId, ThemeDisplay themeDisplay) {
639 
640         return getTokens(groupId, themeDisplay, null);
641     }
642 
643     public static Map<String, String> getTokens(
644         long groupId, ThemeDisplay themeDisplay, String xmlRequest) {
645 
646         Map<String, String> tokens = new HashMap<String, String>();
647 
648         if (themeDisplay != null) {
649             _populateTokens(tokens, groupId, themeDisplay);
650         }
651         else if (Validator.isNotNull(xmlRequest)) {
652             try {
653                 _populateTokens(tokens, groupId, xmlRequest);
654             }
655             catch (Exception e) {
656                 if (_log.isWarnEnabled()) {
657                     _log.warn(e, e);
658                 }
659             }
660         }
661 
662         return tokens;
663     }
664 
665     public static String mergeArticleContent(
666         String curContent, String newContent) {
667 
668         try {
669             Document curDocument = SAXReaderUtil.read(curContent);
670             Document newDocument = SAXReaderUtil.read(newContent);
671 
672             Element curRoot = curDocument.getRootElement();
673             Element newRoot = newDocument.getRootElement();
674 
675             curRoot.addAttribute(
676                 "default-locale",
677                 newRoot.attributeValue("default-locale"));
678             curRoot.addAttribute(
679                 "available-locales",
680                 newRoot.attributeValue("available-locales"));
681 
682             _mergeArticleContentUpdate(
683                 curDocument, newRoot,
684                 LocaleUtil.toLanguageId(LocaleUtil.getDefault()));
685             _mergeArticleContentDelete(curRoot, newDocument);
686 
687             curContent = JournalUtil.formatXML(curDocument);
688         }
689         catch (Exception e) {
690             _log.error(e, e);
691         }
692 
693         return curContent;
694     }
695 
696     public static String removeArticleLocale(
697         String content, String languageId) {
698 
699         try {
700             Document doc = SAXReaderUtil.read(content);
701 
702             Element root = doc.getRootElement();
703 
704             String availableLocales = root.attributeValue("available-locales");
705 
706             if (availableLocales == null) {
707                 return content;
708             }
709 
710             availableLocales = StringUtil.remove(availableLocales, languageId);
711 
712             if (availableLocales.endsWith(",")) {
713                 availableLocales = availableLocales.substring(
714                     0, availableLocales.length() - 1);
715             }
716 
717             root.addAttribute("available-locales", availableLocales);
718 
719             removeArticleLocale(root, languageId);
720 
721             content = formatXML(doc);
722         }
723         catch (Exception e) {
724             _log.error(e, e);
725         }
726 
727         return content;
728     }
729 
730     public static void removeArticleLocale(Element el, String languageId)
731         throws PortalException, SystemException {
732 
733         for (Element dynamicEl : el.elements("dynamic-element")) {
734             for (Element dynamicContentEl :
735                     dynamicEl.elements("dynamic-content")) {
736 
737                 String curLanguageId = GetterUtil.getString(
738                     dynamicContentEl.attributeValue("language-id"));
739 
740                 if (curLanguageId.equals(languageId)) {
741                     long id = GetterUtil.getLong(
742                         dynamicContentEl.attributeValue("id"));
743 
744                     if (id > 0) {
745                         ImageLocalServiceUtil.deleteImage(id);
746                     }
747 
748                     dynamicContentEl.detach();
749                 }
750             }
751 
752             removeArticleLocale(dynamicEl, languageId);
753         }
754     }
755 
756     public static String removeOldContent(String content, String xsd) {
757         try {
758             Document contentDoc = SAXReaderUtil.read(content);
759             Document xsdDoc = SAXReaderUtil.read(xsd);
760 
761             Element contentRoot = contentDoc.getRootElement();
762 
763             Stack<String> path = new Stack<String>();
764 
765             path.push(contentRoot.getName());
766 
767             _removeOldContent(path, contentRoot, xsdDoc);
768 
769             content = formatXML(contentDoc);
770         }
771         catch (Exception e) {
772             _log.error(e, e);
773         }
774 
775         return content;
776     }
777 
778     public static void removeRecentArticle(
779         PortletRequest portletRequest, String articleId) {
780 
781         Stack<JournalArticle> stack = getRecentArticles(portletRequest);
782 
783         Iterator<JournalArticle> itr = stack.iterator();
784 
785         while (itr.hasNext()) {
786             JournalArticle journalArticle = itr.next();
787 
788             if (journalArticle.getArticleId().equals(articleId)) {
789                 itr.remove();
790 
791                 break;
792             }
793         }
794     }
795 
796     public static void removeRecentStructure(
797         PortletRequest portletRequest, String structureId) {
798 
799         Stack<JournalStructure> stack = getRecentStructures(portletRequest);
800 
801         Iterator<JournalStructure> itr = stack.iterator();
802 
803         while (itr.hasNext()) {
804             JournalStructure journalStructure = itr.next();
805 
806             if (journalStructure.getStructureId().equals(structureId)) {
807                 itr.remove();
808 
809                 break;
810             }
811         }
812     }
813 
814     public static void removeRecentTemplate(
815         PortletRequest portletRequest, String templateId) {
816 
817         Stack<JournalTemplate> stack = getRecentTemplates(portletRequest);
818 
819         Iterator<JournalTemplate> itr = stack.iterator();
820 
821         while (itr.hasNext()) {
822             JournalTemplate journalTemplate = itr.next();
823 
824             if (journalTemplate.getTemplateId().equals(templateId)) {
825                 itr.remove();
826 
827                 break;
828             }
829         }
830     }
831 
832     public static String transform(
833             Map<String, String> tokens, String viewMode, String languageId,
834             String xml, String script, String langType)
835         throws Exception {
836 
837         // Setup Listeners
838 
839         if (_log.isDebugEnabled()) {
840             _log.debug("Language " + languageId);
841         }
842 
843         if (Validator.isNull(viewMode)) {
844             viewMode = Constants.VIEW;
845         }
846 
847         if (_logTokens.isDebugEnabled()) {
848             String tokensString = PropertiesUtil.list(tokens);
849 
850             _logTokens.debug(tokensString);
851         }
852 
853         if (_logTransformBefore.isDebugEnabled()) {
854             _logTransformBefore.debug(xml);
855         }
856 
857         List<TransformerListener> listenersList =
858             new ArrayList<TransformerListener>();
859 
860         String[] listeners = PropsUtil.getArray(
861             PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
862 
863         for (int i = 0; i < listeners.length; i++) {
864             TransformerListener listener = null;
865 
866             try {
867                 if (_log.isDebugEnabled()) {
868                     _log.debug("Instantiate listener " + listeners[i]);
869                 }
870 
871                 boolean templateDriven = Validator.isNotNull(langType);
872 
873                 listener = (TransformerListener)Class.forName(
874                     listeners[i]).newInstance();
875 
876                 listener.setTemplateDriven(templateDriven);
877                 listener.setLanguageId(languageId);
878                 listener.setTokens(tokens);
879 
880                 listenersList.add(listener);
881             }
882             catch (Exception e) {
883                 _log.error(e, e);
884             }
885 
886             // Modify XML
887 
888             if (_logXmlBeforeListener.isDebugEnabled()) {
889                 _logXmlBeforeListener.debug(xml);
890             }
891 
892             if (listener != null) {
893                 xml = listener.onXml(xml);
894 
895                 if (_logXmlAfterListener.isDebugEnabled()) {
896                     _logXmlAfterListener.debug(xml);
897                 }
898             }
899 
900             // Modify script
901 
902             if (_logScriptBeforeListener.isDebugEnabled()) {
903                 _logScriptBeforeListener.debug(script);
904             }
905 
906             if (listener != null) {
907                 script = listener.onScript(script);
908 
909                 if (_logScriptAfterListener.isDebugEnabled()) {
910                     _logScriptAfterListener.debug(script);
911                 }
912             }
913         }
914 
915         // Transform
916 
917         String output = null;
918 
919         if (Validator.isNull(langType)) {
920             output = LocalizationUtil.getLocalization(xml, languageId);
921         }
922         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_VM)) {
923             output = JournalVmUtil.transform(
924                 tokens, viewMode, languageId, xml, script);
925         }
926         else if (langType.equals(JournalTemplateImpl.LANG_TYPE_XSL)) {
927             output = JournalXslUtil.transform(
928                 tokens, viewMode, languageId, xml, script);
929         }
930 
931         // Postprocess output
932 
933         for (int i = 0; i < listenersList.size(); i++) {
934             TransformerListener listener = listenersList.get(i);
935 
936             // Modify output
937 
938             if (_logOutputBeforeListener.isDebugEnabled()) {
939                 _logOutputBeforeListener.debug(output);
940             }
941 
942             output = listener.onOutput(output);
943 
944             if (_logOutputAfterListener.isDebugEnabled()) {
945                 _logOutputAfterListener.debug(output);
946             }
947         }
948 
949         if (_logTransfromAfter.isDebugEnabled()) {
950             _logTransfromAfter.debug(output);
951         }
952 
953         return output;
954     }
955 
956     private static void _addElementOptions (
957         Element curContentElement, Element newContentElement) {
958 
959         List<Element> newElementOptions = newContentElement.elements("option");
960 
961         for (Element newElementOption : newElementOptions) {
962             Element curElementOption = SAXReaderUtil.createElement("option");
963 
964             curElementOption.addCDATA(newElementOption.getText());
965 
966             curContentElement.add(curElementOption);
967         }
968     }
969 
970     private static Element _getElementByInstanceId(
971         Document document, String instanceId) {
972 
973         XPath xPathSelector = SAXReaderUtil.createXPath(
974             "//dynamic-element[@instance-id='" + instanceId + "']");
975 
976         List<Node> nodes = xPathSelector.selectNodes(document);
977 
978         if (nodes.size() == 1) {
979             return (Element)nodes.get(0);
980         }
981         else {
982             return null;
983         }
984     }
985 
986     private static void _mergeArticleContentDelete(
987             Element curParentElement, Document newDocument)
988         throws Exception {
989 
990         List<Element> curElements = curParentElement.elements(
991             "dynamic-element");
992 
993         for (int i = 0; i < curElements.size(); i++) {
994             Element curElement = curElements.get(i);
995 
996             _mergeArticleContentDelete(curElement, newDocument);
997 
998             String instanceId = curElement.attributeValue("instance-id");
999 
1000            Element newElement = _getElementByInstanceId(
1001                newDocument, instanceId);
1002
1003            if (newElement == null) {
1004                curElement.detach();
1005
1006                String type = curElement.attributeValue("type");
1007
1008                if (type.equals("image")) {
1009                    _mergeArticleContentDeleteImages(
1010                        curElement.elements("dynamic-content"));
1011                }
1012            }
1013        }
1014    }
1015
1016    private static void _mergeArticleContentDeleteImages(List<Element> elements)
1017        throws Exception {
1018
1019        for (Element element : elements) {
1020            long articleImageId = GetterUtil.getLong(
1021                element.attributeValue("id"));
1022
1023            JournalArticleImageLocalServiceUtil.deleteArticleImage(
1024                articleImageId);
1025        }
1026    }
1027
1028    private static void _mergeArticleContentUpdate(
1029            Document curDocument, Element newParentElement,
1030            String defaultLocale)
1031        throws Exception {
1032
1033        List<Element> newElements = newParentElement.elements(
1034            "dynamic-element");
1035
1036        for (int i = 0; i < newElements.size(); i++) {
1037            Element newElement = newElements.get(i);
1038
1039            _mergeArticleContentUpdate(
1040                curDocument, newParentElement, newElement, i, defaultLocale);
1041        }
1042    }
1043
1044    private static void _mergeArticleContentUpdate(
1045            Document curDocument, Element newParentElement, Element newElement,
1046            int pos, String defaultLocale)
1047        throws Exception {
1048
1049        _mergeArticleContentUpdate(curDocument, newElement, defaultLocale);
1050
1051        String instanceId = newElement.attributeValue("instance-id");
1052
1053        Element curElement = _getElementByInstanceId(curDocument, instanceId);
1054
1055        if (curElement != null) {
1056            _mergeArticleContentUpdate(curElement, newElement, defaultLocale);
1057        }
1058        else {
1059            String parentInstanceId = newParentElement.attributeValue(
1060                "instance-id");
1061
1062            if (Validator.isNull(parentInstanceId)) {
1063                Element curRoot = curDocument.getRootElement();
1064
1065                List<Element> curRootElements = curRoot.elements();
1066
1067                curRootElements.add(pos, newElement.createCopy());
1068            }
1069            else {
1070                Element curParentElement = _getElementByInstanceId(
1071                    curDocument, parentInstanceId);
1072
1073                if (curParentElement != null) {
1074                    List<Element> curParentElements =
1075                        curParentElement.elements();
1076
1077                    curParentElements.add(pos, newElement.createCopy());
1078                }
1079            }
1080        }
1081    }
1082
1083    private static void _mergeArticleContentUpdate(
1084        Element curElement, Element newElement, String defaultLocale) {
1085
1086        Element newContentElement = newElement.elements(
1087            "dynamic-content").get(0);
1088
1089        String newLanguageId = newContentElement.attributeValue("language-id");
1090        String newValue = newContentElement.getText();
1091
1092        List<Element> curContentElements = curElement.elements(
1093            "dynamic-content");
1094
1095        if (Validator.isNull(newLanguageId)) {
1096            for (Element curContentElement : curContentElements) {
1097                curContentElement.detach();
1098            }
1099
1100            Element curContentElement = SAXReaderUtil.createElement(
1101                "dynamic-content");
1102
1103            if (newContentElement.element("option") != null) {
1104                _addElementOptions(curContentElement, newContentElement);
1105            }
1106            else {
1107                curContentElement.addCDATA(newValue);
1108            }
1109
1110            curElement.add(curContentElement);
1111        }
1112        else {
1113            boolean alreadyExists = false;
1114
1115            for (Element curContentElement : curContentElements) {
1116                String curLanguageId = curContentElement.attributeValue(
1117                    "language-id");
1118
1119                if (newLanguageId.equals(curLanguageId)) {
1120                    alreadyExists = true;
1121
1122                    curContentElement.clearContent();
1123
1124                    if (newContentElement.element("option") != null) {
1125                        _addElementOptions(
1126                            curContentElement, newContentElement);
1127                    }
1128                    else {
1129                        curContentElement.addCDATA(newValue);
1130                    }
1131
1132                    break;
1133                }
1134            }
1135
1136            if (!alreadyExists) {
1137                Element curContentElement = curContentElements.get(0);
1138
1139                String curLanguageId = curContentElement.attributeValue(
1140                    "language-id");
1141
1142                if (Validator.isNull(curLanguageId)) {
1143                    curContentElement.detach();
1144                }
1145
1146                curElement.add(newContentElement.createCopy());
1147            }
1148        }
1149    }
1150
1151    private static void _populateTokens(
1152            Map<String, String> tokens, long groupId, String xmlRequest)
1153        throws Exception {
1154
1155        Document request = SAXReaderUtil.read(xmlRequest);
1156
1157        Element root = request.getRootElement();
1158
1159        Element themeDisplayEl = root.element("theme-display");
1160
1161        Layout layout = LayoutLocalServiceUtil.getLayout(
1162            GetterUtil.getLong(themeDisplayEl.elementText("plid")));
1163
1164        Group group = layout.getGroup();
1165
1166        LayoutSet layoutSet = layout.getLayoutSet();
1167
1168        String friendlyUrlCurrent = null;
1169
1170        if (layout.isPublicLayout()) {
1171            friendlyUrlCurrent = themeDisplayEl.elementText(
1172                "path-friendly-url-public");
1173        }
1174        else if (group.isUserGroup()) {
1175            friendlyUrlCurrent = themeDisplayEl.elementText(
1176                "path-friendly-url-private-user");
1177        }
1178        else {
1179            friendlyUrlCurrent = themeDisplayEl.elementText(
1180                "path-friendly-url-private-group");
1181        }
1182
1183        String layoutSetFriendlyUrl = StringPool.BLANK;
1184
1185        String virtualHost = layoutSet.getVirtualHost();
1186
1187        if (Validator.isNull(virtualHost) ||
1188            !virtualHost.equals(themeDisplayEl.elementText("server-name"))) {
1189
1190            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1191        }
1192
1193        tokens.put("cdn_host", themeDisplayEl.elementText("cdn-host"));
1194        tokens.put("company_id", themeDisplayEl.elementText("company-id"));
1195        tokens.put("friendly_url_current", friendlyUrlCurrent);
1196        tokens.put(
1197            "friendly_url_private_group",
1198            themeDisplayEl.elementText("path-friendly-url-private-group"));
1199        tokens.put(
1200            "friendly_url_private_user",
1201            themeDisplayEl.elementText("path-friendly-url-private-user"));
1202        tokens.put(
1203            "friendly_url_public",
1204            themeDisplayEl.elementText("path-friendly-url-public"));
1205        tokens.put("group_friendly_url", group.getFriendlyURL());
1206        tokens.put("group_id", String.valueOf(groupId));
1207        tokens.put("image_path", themeDisplayEl.elementText("path-image"));
1208        tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1209        tokens.put("main_path", themeDisplayEl.elementText("path-main"));
1210        tokens.put("portal_ctx", themeDisplayEl.elementText("path-context"));
1211        tokens.put(
1212            "portal_url",
1213            HttpUtil.removeProtocol(themeDisplayEl.elementText("url-portal")));
1214        tokens.put(
1215            "protocol",
1216            HttpUtil.getProtocol(themeDisplayEl.elementText("url-portal")));
1217        tokens.put("root_path", themeDisplayEl.elementText("path-context"));
1218        tokens.put(
1219            "theme_image_path",
1220            themeDisplayEl.elementText("path-theme-images"));
1221
1222        // Deprecated tokens
1223
1224        tokens.put(
1225            "friendly_url",
1226            themeDisplayEl.elementText("path-friendly-url-public"));
1227        tokens.put(
1228            "friendly_url_private",
1229            themeDisplayEl.elementText("path-friendly-url-private-group"));
1230        tokens.put(
1231            "page_url", themeDisplayEl.elementText("path-friendly-url-public"));
1232    }
1233
1234    private static void _populateTokens(
1235        Map<String, String> tokens, long groupId, ThemeDisplay themeDisplay) {
1236
1237        Layout layout = themeDisplay.getLayout();
1238
1239        Group group = layout.getGroup();
1240
1241        LayoutSet layoutSet = layout.getLayoutSet();
1242
1243        String friendlyUrlCurrent = null;
1244
1245        if (layout.isPublicLayout()) {
1246            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPublic();
1247        }
1248        else if (group.isUserGroup()) {
1249            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateUser();
1250        }
1251        else {
1252            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateGroup();
1253        }
1254
1255        String layoutSetFriendlyUrl = StringPool.BLANK;
1256
1257        String virtualHost = layoutSet.getVirtualHost();
1258
1259        if (Validator.isNull(virtualHost) ||
1260            !virtualHost.equals(themeDisplay.getServerName())) {
1261
1262            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1263        }
1264
1265        tokens.put("cdn_host", themeDisplay.getCDNHost());
1266        tokens.put("company_id", String.valueOf(themeDisplay.getCompanyId()));
1267        tokens.put("friendly_url_current", friendlyUrlCurrent);
1268        tokens.put(
1269            "friendly_url_private_group",
1270            themeDisplay.getPathFriendlyURLPrivateGroup());
1271        tokens.put(
1272            "friendly_url_private_user",
1273            themeDisplay.getPathFriendlyURLPrivateUser());
1274        tokens.put(
1275            "friendly_url_public", themeDisplay.getPathFriendlyURLPublic());
1276        tokens.put("group_friendly_url", group.getFriendlyURL());
1277        tokens.put("group_id", String.valueOf(groupId));
1278        tokens.put("image_path", themeDisplay.getPathImage());
1279        tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1280        tokens.put("main_path", themeDisplay.getPathMain());
1281        tokens.put("portal_ctx", themeDisplay.getPathContext());
1282        tokens.put(
1283            "portal_url", HttpUtil.removeProtocol(themeDisplay.getURLPortal()));
1284        tokens.put(
1285            "protocol", HttpUtil.getProtocol(themeDisplay.getURLPortal()));
1286        tokens.put("root_path", themeDisplay.getPathContext());
1287        tokens.put("theme_image_path", themeDisplay.getPathThemeImages());
1288
1289        // Deprecated tokens
1290
1291        tokens.put("friendly_url", themeDisplay.getPathFriendlyURLPublic());
1292        tokens.put(
1293            "friendly_url_private",
1294            themeDisplay.getPathFriendlyURLPrivateGroup());
1295        tokens.put("page_url", themeDisplay.getPathFriendlyURLPublic());
1296    }
1297
1298    private static void _removeOldContent(
1299            Stack<String> path, Element contentEl, Document xsdDoc)
1300        throws SystemException {
1301
1302        String elPath = "";
1303
1304        for (int i = 0; i < path.size(); i++) {
1305            elPath += "/" + path.elementAt(i);
1306        }
1307
1308        for (int i = 0; i < contentEl.nodeCount(); i++) {
1309            Node contentNode = contentEl.node(i);
1310
1311            if (contentNode instanceof Element) {
1312                _removeOldContent(path, (Element)contentNode, xsdDoc, elPath);
1313            }
1314        }
1315    }
1316
1317    private static void _removeOldContent(
1318            Stack<String> path, Element contentEl, Document xsdDoc,
1319            String elPath)
1320        throws SystemException {
1321
1322        String name = contentEl.attributeValue("name");
1323
1324        if (Validator.isNull(name)) {
1325            return;
1326        }
1327
1328        String localPath = "dynamic-element[@name='" + name + "']";
1329
1330        String fullPath = elPath + "/" + localPath;
1331
1332        XPath xPathSelector = SAXReaderUtil.createXPath(fullPath);
1333
1334        List<Node> curNodes = xPathSelector.selectNodes(xsdDoc);
1335
1336        if (curNodes.size() == 0) {
1337            contentEl.detach();
1338        }
1339
1340        path.push(localPath);
1341
1342        _removeOldContent(path, contentEl, xsdDoc);
1343
1344        path.pop();
1345    }
1346
1347    private static Log _log = LogFactoryUtil.getLog(JournalUtil.class);
1348
1349    private static Log _logOutputAfterListener = LogFactoryUtil.getLog(
1350        JournalUtil.class.getName() + ".OutputAfterListener");
1351
1352    private static Log _logOutputBeforeListener = LogFactoryUtil.getLog(
1353        JournalUtil.class.getName() + ".OutputBeforeListener");
1354
1355    private static Log _logScriptAfterListener = LogFactoryUtil.getLog(
1356        JournalUtil.class.getName() + ".ScriptAfterListener");
1357
1358    private static Log _logScriptBeforeListener = LogFactoryUtil.getLog(
1359        JournalUtil.class.getName() + ".ScriptBeforeListener");
1360
1361    private static Log _logTransfromAfter = LogFactoryUtil.getLog(
1362        JournalUtil.class.getName() + ".TransformAfter");
1363
1364    private static Log _logTransformBefore = LogFactoryUtil.getLog(
1365        JournalUtil.class.getName() + ".BeforeTransform");
1366
1367    private static Log _logTokens = LogFactoryUtil.getLog(
1368        JournalUtil.class.getName() + ".Tokens");
1369
1370    private static Log _logXmlAfterListener = LogFactoryUtil.getLog(
1371        JournalUtil.class.getName() + ".XmlAfterListener");
1372
1373    private static Log _logXmlBeforeListener = LogFactoryUtil.getLog(
1374        JournalUtil.class.getName() + ".XmlBeforeListener");
1375
1376}