001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.journal.util;
016    
017    import com.liferay.portal.kernel.configuration.Filter;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
023    import com.liferay.portal.kernel.portlet.LiferayWindowState;
024    import com.liferay.portal.kernel.search.Field;
025    import com.liferay.portal.kernel.search.Hits;
026    import com.liferay.portal.kernel.search.Indexer;
027    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028    import com.liferay.portal.kernel.templateparser.TransformerListener;
029    import com.liferay.portal.kernel.util.ArrayUtil;
030    import com.liferay.portal.kernel.util.CharPool;
031    import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HtmlUtil;
034    import com.liferay.portal.kernel.util.HttpUtil;
035    import com.liferay.portal.kernel.util.InstanceFactory;
036    import com.liferay.portal.kernel.util.LocaleUtil;
037    import com.liferay.portal.kernel.util.OrderByComparator;
038    import com.liferay.portal.kernel.util.ParamUtil;
039    import com.liferay.portal.kernel.util.PropsKeys;
040    import com.liferay.portal.kernel.util.StringBundler;
041    import com.liferay.portal.kernel.util.StringPool;
042    import com.liferay.portal.kernel.util.StringUtil;
043    import com.liferay.portal.kernel.util.Time;
044    import com.liferay.portal.kernel.util.Tuple;
045    import com.liferay.portal.kernel.util.UnmodifiableList;
046    import com.liferay.portal.kernel.util.Validator;
047    import com.liferay.portal.kernel.workflow.WorkflowConstants;
048    import com.liferay.portal.kernel.xml.Attribute;
049    import com.liferay.portal.kernel.xml.Document;
050    import com.liferay.portal.kernel.xml.Element;
051    import com.liferay.portal.kernel.xml.Node;
052    import com.liferay.portal.kernel.xml.SAXReaderUtil;
053    import com.liferay.portal.kernel.xml.XPath;
054    import com.liferay.portal.model.Group;
055    import com.liferay.portal.model.Layout;
056    import com.liferay.portal.model.LayoutConstants;
057    import com.liferay.portal.model.LayoutSet;
058    import com.liferay.portal.model.ModelHintsUtil;
059    import com.liferay.portal.model.User;
060    import com.liferay.portal.service.ImageLocalServiceUtil;
061    import com.liferay.portal.service.LayoutLocalServiceUtil;
062    import com.liferay.portal.service.UserLocalServiceUtil;
063    import com.liferay.portal.templateparser.Transformer;
064    import com.liferay.portal.theme.ThemeDisplay;
065    import com.liferay.portal.util.PortalUtil;
066    import com.liferay.portal.util.PortletKeys;
067    import com.liferay.portal.util.PropsUtil;
068    import com.liferay.portal.util.PropsValues;
069    import com.liferay.portal.util.WebKeys;
070    import com.liferay.portal.webserver.WebServerServletTokenUtil;
071    import com.liferay.portlet.PortletURLFactoryUtil;
072    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
073    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
074    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
075    import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
076    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
077    import com.liferay.portlet.journal.NoSuchArticleException;
078    import com.liferay.portlet.journal.model.JournalArticle;
079    import com.liferay.portlet.journal.model.JournalFolder;
080    import com.liferay.portlet.journal.model.JournalFolderConstants;
081    import com.liferay.portlet.journal.model.JournalStructure;
082    import com.liferay.portlet.journal.model.JournalStructureAdapter;
083    import com.liferay.portlet.journal.model.JournalStructureConstants;
084    import com.liferay.portlet.journal.model.JournalTemplate;
085    import com.liferay.portlet.journal.model.JournalTemplateAdapter;
086    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
087    import com.liferay.portlet.journal.service.JournalFolderLocalServiceUtil;
088    import com.liferay.portlet.journal.util.comparator.ArticleCreateDateComparator;
089    import com.liferay.portlet.journal.util.comparator.ArticleDisplayDateComparator;
090    import com.liferay.portlet.journal.util.comparator.ArticleIDComparator;
091    import com.liferay.portlet.journal.util.comparator.ArticleModifiedDateComparator;
092    import com.liferay.portlet.journal.util.comparator.ArticleReviewDateComparator;
093    import com.liferay.portlet.journal.util.comparator.ArticleTitleComparator;
094    import com.liferay.portlet.journal.util.comparator.ArticleVersionComparator;
095    import com.liferay.util.ContentUtil;
096    import com.liferay.util.FiniteUniqueStack;
097    
098    import java.util.ArrayList;
099    import java.util.Collections;
100    import java.util.Date;
101    import java.util.HashMap;
102    import java.util.Iterator;
103    import java.util.List;
104    import java.util.Map;
105    import java.util.Stack;
106    import java.util.regex.Pattern;
107    
108    import javax.portlet.PortletPreferences;
109    import javax.portlet.PortletRequest;
110    import javax.portlet.PortletSession;
111    import javax.portlet.PortletURL;
112    import javax.portlet.RenderResponse;
113    
114    import javax.servlet.http.HttpServletRequest;
115    
116    /**
117     * @author Brian Wing Shun Chan
118     * @author Raymond Aug??
119     * @author Wesley Gong
120     * @author Angelo Jefferson
121     * @author Hugo Huijser
122     */
123    public class JournalUtil {
124    
125            public static final int MAX_STACK_SIZE = 20;
126    
127            public static void addAllReservedEls(
128                    Element rootElement, Map<String, String> tokens, JournalArticle article,
129                    String languageId, ThemeDisplay themeDisplay) {
130    
131                    JournalUtil.addReservedEl(
132                            rootElement, tokens, JournalStructureConstants.RESERVED_ARTICLE_ID,
133                            article.getArticleId());
134    
135                    JournalUtil.addReservedEl(
136                            rootElement, tokens,
137                            JournalStructureConstants.RESERVED_ARTICLE_VERSION,
138                            article.getVersion());
139    
140                    JournalUtil.addReservedEl(
141                            rootElement, tokens,
142                            JournalStructureConstants.RESERVED_ARTICLE_TITLE,
143                            article.getTitle(languageId));
144    
145                    JournalUtil.addReservedEl(
146                            rootElement, tokens,
147                            JournalStructureConstants.RESERVED_ARTICLE_URL_TITLE,
148                            article.getUrlTitle());
149    
150                    JournalUtil.addReservedEl(
151                            rootElement, tokens,
152                            JournalStructureConstants.RESERVED_ARTICLE_DESCRIPTION,
153                            article.getDescription(languageId));
154    
155                    JournalUtil.addReservedEl(
156                            rootElement, tokens,
157                            JournalStructureConstants.RESERVED_ARTICLE_TYPE, article.getType());
158    
159                    JournalUtil.addReservedEl(
160                            rootElement, tokens,
161                            JournalStructureConstants.RESERVED_ARTICLE_CREATE_DATE,
162                            article.getCreateDate());
163    
164                    JournalUtil.addReservedEl(
165                            rootElement, tokens,
166                            JournalStructureConstants.RESERVED_ARTICLE_MODIFIED_DATE,
167                            article.getModifiedDate());
168    
169                    if (article.getDisplayDate() != null) {
170                            JournalUtil.addReservedEl(
171                                    rootElement, tokens,
172                                    JournalStructureConstants.RESERVED_ARTICLE_DISPLAY_DATE,
173                                    article.getDisplayDate());
174                    }
175    
176                    String smallImageURL = StringPool.BLANK;
177    
178                    if (Validator.isNotNull(article.getSmallImageURL())) {
179                            smallImageURL = article.getSmallImageURL();
180                    }
181                    else if ((themeDisplay != null) && article.isSmallImage()) {
182                            StringBundler sb = new StringBundler(5);
183    
184                            sb.append(themeDisplay.getPathImage());
185                            sb.append("/journal/article?img_id=");
186                            sb.append(article.getSmallImageId());
187                            sb.append("&t=");
188                            sb.append(
189                                    WebServerServletTokenUtil.getToken(article.getSmallImageId()));
190    
191                            smallImageURL = sb.toString();
192                    }
193    
194                    JournalUtil.addReservedEl(
195                            rootElement, tokens,
196                            JournalStructureConstants.RESERVED_ARTICLE_SMALL_IMAGE_URL,
197                            smallImageURL);
198    
199                    String[] assetTagNames = new String[0];
200    
201                    try {
202                            assetTagNames = AssetTagLocalServiceUtil.getTagNames(
203                                    JournalArticle.class.getName(), article.getResourcePrimKey());
204                    }
205                    catch (SystemException se) {
206                    }
207    
208                    JournalUtil.addReservedEl(
209                            rootElement, tokens,
210                            JournalStructureConstants.RESERVED_ARTICLE_ASSET_TAG_NAMES,
211                            StringUtil.merge(assetTagNames));
212    
213                    JournalUtil.addReservedEl(
214                            rootElement, tokens,
215                            JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_ID,
216                            String.valueOf(article.getUserId()));
217    
218                    String userName = StringPool.BLANK;
219                    String userEmailAddress = StringPool.BLANK;
220                    String userComments = StringPool.BLANK;
221                    String userJobTitle = StringPool.BLANK;
222    
223                    User user = null;
224    
225                    try {
226                            user = UserLocalServiceUtil.getUserById(article.getUserId());
227    
228                            userName = user.getFullName();
229                            userEmailAddress = user.getEmailAddress();
230                            userComments = user.getComments();
231                            userJobTitle = user.getJobTitle();
232                    }
233                    catch (PortalException pe) {
234                    }
235                    catch (SystemException se) {
236                    }
237    
238                    JournalUtil.addReservedEl(
239                            rootElement, tokens,
240                            JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_NAME, userName);
241    
242                    JournalUtil.addReservedEl(
243                            rootElement, tokens,
244                            JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_EMAIL_ADDRESS,
245                            userEmailAddress);
246    
247                    JournalUtil.addReservedEl(
248                            rootElement, tokens,
249                            JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_COMMENTS,
250                            userComments);
251    
252                    JournalUtil.addReservedEl(
253                            rootElement, tokens,
254                            JournalStructureConstants.RESERVED_ARTICLE_AUTHOR_JOB_TITLE,
255                            userJobTitle);
256            }
257    
258            public static void addPortletBreadcrumbEntries(
259                            JournalArticle article, HttpServletRequest request,
260                            RenderResponse renderResponse)
261                    throws Exception {
262    
263                    JournalFolder folder = article.getFolder();
264    
265                    if (folder.getFolderId() !=
266                                    JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
267    
268                            addPortletBreadcrumbEntries(folder, request, renderResponse);
269                    }
270    
271                    JournalArticle unescapedArticle = article.toUnescapedModel();
272    
273                    PortletURL portletURL = renderResponse.createRenderURL();
274    
275                    portletURL.setParameter("struts_action", "/article/view_article");
276                    portletURL.setParameter(
277                            "groupId", String.valueOf(article.getGroupId()));
278                    portletURL.setParameter(
279                            "articleId", String.valueOf(article.getArticleId()));
280    
281                    PortalUtil.addPortletBreadcrumbEntry(
282                            request, unescapedArticle.getTitle(), portletURL.toString());
283            }
284    
285            public static void addPortletBreadcrumbEntries(
286                            JournalFolder folder, HttpServletRequest request,
287                            LiferayPortletResponse liferayPortletResponse)
288                    throws Exception {
289    
290                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
291                            com.liferay.portal.kernel.util.WebKeys.THEME_DISPLAY);
292    
293                    String strutsAction = ParamUtil.getString(request, "struts_action");
294    
295                    PortletURL portletURL = liferayPortletResponse.createRenderURL();
296    
297                    if (strutsAction.equals("/journal/select_folder")) {
298                            portletURL.setParameter("struts_action", "/journal/select_folder");
299                            portletURL.setWindowState(LiferayWindowState.POP_UP);
300    
301                            PortalUtil.addPortletBreadcrumbEntry(
302                                    request, themeDisplay.translate("home"), portletURL.toString());
303                    }
304                    else {
305                            portletURL.setParameter("struts_action", "/journal/view");
306    
307                            Map<String, Object> data = new HashMap<String, Object>();
308    
309                            data.put("direction-right", Boolean.TRUE.toString());
310                            data.put(
311                                    "folder-id", JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID);
312    
313                            PortalUtil.addPortletBreadcrumbEntry(
314                                    request, themeDisplay.translate("home"), portletURL.toString(),
315                                    data);
316                    }
317    
318                    if (folder == null) {
319                            return;
320                    }
321    
322                    List<JournalFolder> ancestorFolders = folder.getAncestors();
323    
324                    Collections.reverse(ancestorFolders);
325    
326                    for (JournalFolder ancestorFolder : ancestorFolders) {
327                            portletURL.setParameter(
328                                    "folderId", String.valueOf(ancestorFolder.getFolderId()));
329    
330                            Map<String, Object> data = new HashMap<String, Object>();
331    
332                            data.put("direction-right", Boolean.TRUE.toString());
333                            data.put("folder-id", ancestorFolder.getFolderId());
334    
335                            PortalUtil.addPortletBreadcrumbEntry(
336                                    request, ancestorFolder.getName(), portletURL.toString(), data);
337                    }
338    
339                    portletURL.setParameter(
340                            "folderId", String.valueOf(folder.getFolderId()));
341    
342                    if (folder.getFolderId() !=
343                                    JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
344    
345                            JournalFolder unescapedFolder = folder.toUnescapedModel();
346    
347                            Map<String, Object> data = new HashMap<String, Object>();
348    
349                            data.put("direction-right", Boolean.TRUE.toString());
350                            data.put("folder-id", folder.getFolderId());
351    
352                            PortalUtil.addPortletBreadcrumbEntry(
353                                    request, unescapedFolder.getName(), portletURL.toString(),
354                                    data);
355                    }
356            }
357    
358            public static void addPortletBreadcrumbEntries(
359                            JournalFolder folder, HttpServletRequest request,
360                            RenderResponse renderResponse)
361                    throws Exception {
362    
363                    LiferayPortletResponse liferayPortletResponse =
364                            (LiferayPortletResponse)renderResponse;
365    
366                    addPortletBreadcrumbEntries(folder, request, liferayPortletResponse);
367            }
368    
369            public static void addPortletBreadcrumbEntries(
370                            long folderId, HttpServletRequest request,
371                            RenderResponse renderResponse)
372                    throws Exception {
373    
374                    if (folderId == JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
375                            return;
376                    }
377    
378                    JournalFolder folder = JournalFolderLocalServiceUtil.getFolder(
379                            folderId);
380    
381                    addPortletBreadcrumbEntries(folder, request, renderResponse);
382            }
383    
384            public static void addRecentArticle(
385                    PortletRequest portletRequest, JournalArticle article) {
386    
387                    if (article != null) {
388                            Stack<JournalArticle> stack = getRecentArticles(portletRequest);
389    
390                            stack.push(article);
391                    }
392            }
393    
394            public static void addRecentDDMStructure(
395                    PortletRequest portletRequest, DDMStructure ddmStructure) {
396    
397                    if (ddmStructure != null) {
398                            Stack<DDMStructure> stack = getRecentDDMStructures(portletRequest);
399    
400                            stack.push(ddmStructure);
401                    }
402            }
403    
404            public static void addRecentDDMTemplate(
405                    PortletRequest portletRequest, DDMTemplate ddmTemplate) {
406    
407                    if (ddmTemplate != null) {
408                            Stack<DDMTemplate> stack = getRecentDDMTemplates(portletRequest);
409    
410                            stack.push(ddmTemplate);
411                    }
412            }
413    
414            public static void addReservedEl(
415                    Element rootElement, Map<String, String> tokens, String name,
416                    Date value) {
417    
418                    addReservedEl(rootElement, tokens, name, Time.getRFC822(value));
419            }
420    
421            public static void addReservedEl(
422                    Element rootElement, Map<String, String> tokens, String name,
423                    double value) {
424    
425                    addReservedEl(rootElement, tokens, name, String.valueOf(value));
426            }
427    
428            public static void addReservedEl(
429                    Element rootElement, Map<String, String> tokens, String name,
430                    String value) {
431    
432                    // XML
433    
434                    if (rootElement != null) {
435                            Element dynamicElementElement = SAXReaderUtil.createElement(
436                                    "dynamic-element");
437    
438                            Attribute nameAttribute = SAXReaderUtil.createAttribute(
439                                    dynamicElementElement, "name", name);
440    
441                            dynamicElementElement.add(nameAttribute);
442    
443                            Attribute typeAttribute = SAXReaderUtil.createAttribute(
444                                    dynamicElementElement, "type", "text");
445    
446                            dynamicElementElement.add(typeAttribute);
447    
448                            Element dynamicContentElement = SAXReaderUtil.createElement(
449                                    "dynamic-content");
450    
451                            //dynamicContentElement.setText("<![CDATA[" + value + "]]>");
452                            dynamicContentElement.setText(value);
453    
454                            dynamicElementElement.add(dynamicContentElement);
455    
456                            rootElement.add(dynamicElementElement);
457                    }
458    
459                    // Tokens
460    
461                    tokens.put(
462                            StringUtil.replace(name, CharPool.DASH, CharPool.UNDERLINE), value);
463            }
464    
465            public static String formatVM(String vm) {
466                    return vm;
467            }
468    
469            public static String getAbsolutePath(
470                            PortletRequest portletRequest, long folderId)
471                    throws PortalException, SystemException {
472    
473                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
474                            WebKeys.THEME_DISPLAY);
475    
476                    if (folderId == JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
477                            return themeDisplay.translate("home");
478                    }
479    
480                    JournalFolder folder = JournalFolderLocalServiceUtil.getFolder(
481                            folderId);
482    
483                    List<JournalFolder> folders = folder.getAncestors();
484    
485                    Collections.reverse(folders);
486    
487                    StringBundler sb = new StringBundler((folders.size() * 3) + 5);
488    
489                    sb.append(themeDisplay.translate("home"));
490                    sb.append(StringPool.SPACE);
491    
492                    for (JournalFolder curFolder : folders) {
493                            sb.append("\u00bb");
494                            sb.append(StringPool.SPACE);
495                            sb.append(curFolder.getName());
496                    }
497    
498                    sb.append("\u00bb");
499                    sb.append(StringPool.SPACE);
500                    sb.append(folder.getName());
501    
502                    return sb.toString();
503            }
504    
505            public static OrderByComparator getArticleOrderByComparator(
506                    String orderByCol, String orderByType) {
507    
508                    boolean orderByAsc = false;
509    
510                    if (orderByType.equals("asc")) {
511                            orderByAsc = true;
512                    }
513    
514                    OrderByComparator orderByComparator = null;
515    
516                    if (orderByCol.equals("create-date")) {
517                            orderByComparator = new ArticleCreateDateComparator(orderByAsc);
518                    }
519                    else if (orderByCol.equals("display-date")) {
520                            orderByComparator = new ArticleDisplayDateComparator(orderByAsc);
521                    }
522                    else if (orderByCol.equals("id")) {
523                            orderByComparator = new ArticleIDComparator(orderByAsc);
524                    }
525                    else if (orderByCol.equals("modified-date")) {
526                            orderByComparator = new ArticleModifiedDateComparator(orderByAsc);
527                    }
528                    else if (orderByCol.equals("review-date")) {
529                            orderByComparator = new ArticleReviewDateComparator(orderByAsc);
530                    }
531                    else if (orderByCol.equals("title")) {
532                            orderByComparator = new ArticleTitleComparator(orderByAsc);
533                    }
534                    else if (orderByCol.equals("version")) {
535                            orderByComparator = new ArticleVersionComparator(orderByAsc);
536                    }
537    
538                    return orderByComparator;
539            }
540    
541            public static Tuple getArticles(Hits hits)
542                    throws PortalException, SystemException {
543    
544                    return getArticles(hits, false);
545            }
546    
547            public static Tuple getArticles(Hits hits, boolean includeScheduledArticles)
548                    throws PortalException, SystemException {
549    
550                    List<JournalArticle> articles = new ArrayList<JournalArticle>();
551                    boolean corruptIndex = false;
552    
553                    List<com.liferay.portal.kernel.search.Document> documents =
554                            hits.toList();
555    
556                    for (com.liferay.portal.kernel.search.Document document : documents) {
557                            long groupId = GetterUtil.getLong(document.get(Field.GROUP_ID));
558                            String articleId = document.get("articleId");
559                            boolean scheduledHead = GetterUtil.getBoolean(
560                                    document.get("scheduledHead"));
561    
562                            try {
563                                    JournalArticle article;
564    
565                                    if (includeScheduledArticles && scheduledHead) {
566                                            article = JournalArticleLocalServiceUtil.getLatestArticle(
567                                                    groupId, articleId, WorkflowConstants.STATUS_SCHEDULED);
568                                    }
569                                    else {
570                                            article = JournalArticleLocalServiceUtil.getArticle(
571                                                    groupId, articleId);
572                                    }
573    
574                                    articles.add(article);
575                            }
576                            catch (NoSuchArticleException nsae) {
577                                    corruptIndex = true;
578    
579                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
580                                            JournalArticle.class);
581    
582                                    long companyId = GetterUtil.getLong(
583                                            document.get(Field.COMPANY_ID));
584    
585                                    indexer.delete(companyId, document.getUID());
586                            }
587                    }
588    
589                    return new Tuple(articles, corruptIndex);
590            }
591    
592            public static String getEmailArticleAddedBody(
593                    PortletPreferences preferences) {
594    
595                    String emailArticleAddedBody = preferences.getValue(
596                            "emailArticleAddedBody", StringPool.BLANK);
597    
598                    if (Validator.isNotNull(emailArticleAddedBody)) {
599                            return emailArticleAddedBody;
600                    }
601                    else {
602                            return ContentUtil.get(
603                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_BODY));
604                    }
605            }
606    
607            public static boolean getEmailArticleAddedEnabled(
608                    PortletPreferences preferences) {
609    
610                    String emailArticleAddedEnabled = preferences.getValue(
611                            "emailArticleAddedEnabled", StringPool.BLANK);
612    
613                    if (Validator.isNotNull(emailArticleAddedEnabled)) {
614                            return GetterUtil.getBoolean(emailArticleAddedEnabled);
615                    }
616                    else {
617                            return GetterUtil.getBoolean(
618                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_ENABLED));
619                    }
620            }
621    
622            public static String getEmailArticleAddedSubject(
623                    PortletPreferences preferences) {
624    
625                    String emailArticleAddedSubject = preferences.getValue(
626                            "emailArticleAddedSubject", StringPool.BLANK);
627    
628                    if (Validator.isNotNull(emailArticleAddedSubject)) {
629                            return emailArticleAddedSubject;
630                    }
631                    else {
632                            return ContentUtil.get(
633                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_ADDED_SUBJECT));
634                    }
635            }
636    
637            public static String getEmailArticleApprovalDeniedBody(
638                    PortletPreferences preferences) {
639    
640                    String emailArticleApprovalDeniedBody = preferences.getValue(
641                            "emailArticleApprovalDeniedBody", StringPool.BLANK);
642    
643                    if (Validator.isNotNull(emailArticleApprovalDeniedBody)) {
644                            return emailArticleApprovalDeniedBody;
645                    }
646                    else {
647                            return ContentUtil.get(
648                                    PropsUtil.get(
649                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_BODY));
650                    }
651            }
652    
653            public static boolean getEmailArticleApprovalDeniedEnabled(
654                    PortletPreferences preferences) {
655    
656                    String emailArticleApprovalDeniedEnabled = preferences.getValue(
657                            "emailArticleApprovalDeniedEnabled", StringPool.BLANK);
658    
659                    if (Validator.isNotNull(emailArticleApprovalDeniedEnabled)) {
660                            return GetterUtil.getBoolean(emailArticleApprovalDeniedEnabled);
661                    }
662                    else {
663                            return GetterUtil.getBoolean(
664                                    PropsUtil.get(
665                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_ENABLED));
666                    }
667            }
668    
669            public static String getEmailArticleApprovalDeniedSubject(
670                    PortletPreferences preferences) {
671    
672                    String emailArticleApprovalDeniedSubject = preferences.getValue(
673                            "emailArticleApprovalDeniedSubject", StringPool.BLANK);
674    
675                    if (Validator.isNotNull(emailArticleApprovalDeniedSubject)) {
676                            return emailArticleApprovalDeniedSubject;
677                    }
678                    else {
679                            return ContentUtil.get(
680                                    PropsUtil.get(
681                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_DENIED_SUBJECT));
682                    }
683            }
684    
685            public static String getEmailArticleApprovalGrantedBody(
686                    PortletPreferences preferences) {
687    
688                    String emailArticleApprovalGrantedBody = preferences.getValue(
689                            "emailArticleApprovalGrantedBody", StringPool.BLANK);
690    
691                    if (Validator.isNotNull(emailArticleApprovalGrantedBody)) {
692                            return emailArticleApprovalGrantedBody;
693                    }
694                    else {
695                            return ContentUtil.get(
696                                    PropsUtil.get(
697                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_BODY));
698                    }
699            }
700    
701            public static boolean getEmailArticleApprovalGrantedEnabled(
702                    PortletPreferences preferences) {
703    
704                    String emailArticleApprovalGrantedEnabled = preferences.getValue(
705                            "emailArticleApprovalGrantedEnabled", StringPool.BLANK);
706    
707                    if (Validator.isNotNull(emailArticleApprovalGrantedEnabled)) {
708                            return GetterUtil.getBoolean(emailArticleApprovalGrantedEnabled);
709                    }
710                    else {
711                            return GetterUtil.getBoolean(
712                                    PropsUtil.get(
713                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_ENABLED));
714                    }
715            }
716    
717            public static String getEmailArticleApprovalGrantedSubject(
718                    PortletPreferences preferences) {
719    
720                    String emailArticleApprovalGrantedSubject = preferences.getValue(
721                            "emailArticleApprovalGrantedSubject", StringPool.BLANK);
722    
723                    if (Validator.isNotNull(emailArticleApprovalGrantedSubject)) {
724                            return emailArticleApprovalGrantedSubject;
725                    }
726                    else {
727                            return ContentUtil.get(
728                                    PropsUtil.get(
729                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_GRANTED_SUBJECT));
730                    }
731            }
732    
733            public static String getEmailArticleApprovalRequestedBody(
734                    PortletPreferences preferences) {
735    
736                    String emailArticleApprovalRequestedBody = preferences.getValue(
737                            "emailArticleApprovalRequestedBody", StringPool.BLANK);
738    
739                    if (Validator.isNotNull(emailArticleApprovalRequestedBody)) {
740                            return emailArticleApprovalRequestedBody;
741                    }
742                    else {
743                            return ContentUtil.get(
744                                    PropsUtil.get(
745                                            PropsKeys.JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_BODY));
746                    }
747            }
748    
749            public static boolean getEmailArticleApprovalRequestedEnabled(
750                    PortletPreferences preferences) {
751    
752                    String emailArticleApprovalRequestedEnabled = preferences.getValue(
753                            "emailArticleApprovalRequestedEnabled", StringPool.BLANK);
754    
755                    if (Validator.isNotNull(emailArticleApprovalRequestedEnabled)) {
756                            return GetterUtil.getBoolean(emailArticleApprovalRequestedEnabled);
757                    }
758                    else {
759                            return GetterUtil.getBoolean(
760                                    PropsUtil.get(
761                                            PropsKeys.
762                                                    JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_ENABLED));
763                    }
764            }
765    
766            public static String getEmailArticleApprovalRequestedSubject(
767                    PortletPreferences preferences) {
768    
769                    String emailArticleApprovalRequestedSubject = preferences.getValue(
770                            "emailArticleApprovalRequestedSubject", StringPool.BLANK);
771    
772                    if (Validator.isNotNull(emailArticleApprovalRequestedSubject)) {
773                            return emailArticleApprovalRequestedSubject;
774                    }
775                    else {
776                            return ContentUtil.get(
777                                    PropsUtil.get(
778                                            PropsKeys.
779                                                    JOURNAL_EMAIL_ARTICLE_APPROVAL_REQUESTED_SUBJECT));
780                    }
781            }
782    
783            public static String getEmailArticleReviewBody(
784                    PortletPreferences preferences) {
785    
786                    String emailArticleReviewBody = preferences.getValue(
787                            "emailArticleReviewBody", StringPool.BLANK);
788    
789                    if (Validator.isNotNull(emailArticleReviewBody)) {
790                            return emailArticleReviewBody;
791                    }
792                    else {
793                            return ContentUtil.get(
794                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_BODY));
795                    }
796            }
797    
798            public static boolean getEmailArticleReviewEnabled(
799                    PortletPreferences preferences) {
800    
801                    String emailArticleReviewEnabled = preferences.getValue(
802                            "emailArticleReviewEnabled", StringPool.BLANK);
803    
804                    if (Validator.isNotNull(emailArticleReviewEnabled)) {
805                            return GetterUtil.getBoolean(emailArticleReviewEnabled);
806                    }
807                    else {
808                            return GetterUtil.getBoolean(
809                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_ENABLED));
810                    }
811            }
812    
813            public static String getEmailArticleReviewSubject(
814                    PortletPreferences preferences) {
815    
816                    String emailArticleReviewSubject = preferences.getValue(
817                            "emailArticleReviewSubject", StringPool.BLANK);
818    
819                    if (Validator.isNotNull(emailArticleReviewSubject)) {
820                            return emailArticleReviewSubject;
821                    }
822                    else {
823                            return ContentUtil.get(
824                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_REVIEW_SUBJECT));
825                    }
826            }
827    
828            public static String getEmailArticleUpdatedBody(
829                    PortletPreferences preferences) {
830    
831                    String emailArticleUpdatedBody = preferences.getValue(
832                            "emailArticleUpdatedBody", StringPool.BLANK);
833    
834                    if (Validator.isNotNull(emailArticleUpdatedBody)) {
835                            return emailArticleUpdatedBody;
836                    }
837                    else {
838                            return ContentUtil.get(
839                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_BODY));
840                    }
841            }
842    
843            public static boolean getEmailArticleUpdatedEnabled(
844                    PortletPreferences preferences) {
845    
846                    String emailArticleUpdatedEnabled = preferences.getValue(
847                            "emailArticleUpdatedEnabled", StringPool.BLANK);
848    
849                    if (Validator.isNotNull(emailArticleUpdatedEnabled)) {
850                            return GetterUtil.getBoolean(emailArticleUpdatedEnabled);
851                    }
852                    else {
853                            return GetterUtil.getBoolean(
854                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_ENABLED));
855                    }
856            }
857    
858            public static String getEmailArticleUpdatedSubject(
859                    PortletPreferences preferences) {
860    
861                    String emailArticleUpdatedSubject = preferences.getValue(
862                            "emailArticleUpdatedSubject", StringPool.BLANK);
863    
864                    if (Validator.isNotNull(emailArticleUpdatedSubject)) {
865                            return emailArticleUpdatedSubject;
866                    }
867                    else {
868                            return ContentUtil.get(
869                                    PropsUtil.get(PropsKeys.JOURNAL_EMAIL_ARTICLE_UPDATED_SUBJECT));
870                    }
871            }
872    
873            public static String getEmailFromAddress(
874                            PortletPreferences preferences, long companyId)
875                    throws SystemException {
876    
877                    return PortalUtil.getEmailFromAddress(
878                            preferences, companyId, PropsValues.JOURNAL_EMAIL_FROM_ADDRESS);
879            }
880    
881            public static String getEmailFromName(
882                            PortletPreferences preferences, long companyId)
883                    throws SystemException {
884    
885                    return PortalUtil.getEmailFromName(
886                            preferences, companyId, PropsValues.JOURNAL_EMAIL_FROM_NAME);
887            }
888    
889            public static String getJournalControlPanelLink(
890                            PortletRequest portletRequest, long folderId)
891                    throws PortalException, SystemException {
892    
893                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
894                            WebKeys.THEME_DISPLAY);
895    
896                    PortletURL portletURL = PortletURLFactoryUtil.create(
897                            portletRequest, PortletKeys.JOURNAL,
898                            PortalUtil.getControlPanelPlid(themeDisplay.getCompanyId()),
899                            PortletRequest.RENDER_PHASE);
900    
901                    portletURL.setParameter("struts_action", "/journal/view");
902                    portletURL.setParameter("folderId", String.valueOf(folderId));
903    
904                    return portletURL.toString();
905            }
906    
907            public static long getPreviewPlid(
908                            JournalArticle article, ThemeDisplay themeDisplay)
909                    throws Exception {
910    
911                    if ((article != null) && Validator.isNotNull(article.getLayoutUuid())) {
912                            Layout layout =
913                                    LayoutLocalServiceUtil.getLayoutByUuidAndCompanyId(
914                                            article.getLayoutUuid(), themeDisplay.getCompanyId());
915    
916                            return layout.getPlid();
917                    }
918    
919                    Layout layout = LayoutLocalServiceUtil.fetchFirstLayout(
920                            themeDisplay.getScopeGroupId(), false,
921                            LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
922    
923                    if (layout == null) {
924                            layout = LayoutLocalServiceUtil.fetchFirstLayout(
925                                    themeDisplay.getScopeGroupId(), true,
926                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
927                    }
928    
929                    if (layout != null) {
930                            return layout.getPlid();
931                    }
932    
933                    return themeDisplay.getPlid();
934            }
935    
936            public static Stack<JournalArticle> getRecentArticles(
937                    PortletRequest portletRequest) {
938    
939                    PortletSession portletSession = portletRequest.getPortletSession();
940    
941                    Stack<JournalArticle> recentArticles =
942                            (Stack<JournalArticle>)portletSession.getAttribute(
943                                    WebKeys.JOURNAL_RECENT_ARTICLES);
944    
945                    if (recentArticles == null) {
946                            recentArticles = new FiniteUniqueStack<JournalArticle>(
947                                    MAX_STACK_SIZE);
948    
949                            portletSession.setAttribute(
950                                    WebKeys.JOURNAL_RECENT_ARTICLES, recentArticles);
951                    }
952    
953                    return recentArticles;
954            }
955    
956            public static Stack<DDMStructure> getRecentDDMStructures(
957                    PortletRequest portletRequest) {
958    
959                    PortletSession portletSession = portletRequest.getPortletSession();
960    
961                    Stack<DDMStructure> recentDDMStructures =
962                            (Stack<DDMStructure>)portletSession.getAttribute(
963                                    WebKeys.JOURNAL_RECENT_DYNAMIC_DATA_MAPPING_STRUCTURES);
964    
965                    if (recentDDMStructures == null) {
966                            recentDDMStructures = new FiniteUniqueStack<DDMStructure>(
967                                    MAX_STACK_SIZE);
968    
969                            portletSession.setAttribute(
970                                    WebKeys.JOURNAL_RECENT_DYNAMIC_DATA_MAPPING_STRUCTURES,
971                                    recentDDMStructures);
972                    }
973    
974                    return recentDDMStructures;
975            }
976    
977            public static Stack<DDMTemplate> getRecentDDMTemplates(
978                    PortletRequest portletRequest) {
979    
980                    PortletSession portletSession = portletRequest.getPortletSession();
981    
982                    Stack<DDMTemplate> recentDDMTemplates =
983                            (Stack<DDMTemplate>)portletSession.getAttribute(
984                                    WebKeys.JOURNAL_RECENT_DYNAMIC_DATA_MAPPING_TEMPLATES);
985    
986                    if (recentDDMTemplates == null) {
987                            recentDDMTemplates = new FiniteUniqueStack<DDMTemplate>(
988                                    MAX_STACK_SIZE);
989    
990                            portletSession.setAttribute(
991                                    WebKeys.JOURNAL_RECENT_DYNAMIC_DATA_MAPPING_TEMPLATES,
992                                    recentDDMTemplates);
993                    }
994    
995                    return recentDDMTemplates;
996            }
997    
998            public static long[] getStructureClassPKs(
999                            long[] groupIds, String structureId)
1000                    throws SystemException {
1001    
1002                    List<Long> classPKs = new ArrayList<Long>();
1003    
1004                    for (long groupId : groupIds) {
1005                            @SuppressWarnings("deprecation")
1006                            JournalStructure structure =
1007                                    com.liferay.portlet.journal.service.
1008                                            JournalStructureLocalServiceUtil.fetchStructure(
1009                                                    groupId, structureId);
1010    
1011                            if (structure != null) {
1012                                    classPKs.add(structure.getId());
1013                            }
1014                    }
1015    
1016                    return ArrayUtil.toLongArray(classPKs);
1017            }
1018    
1019            public static String getTemplateScript(
1020                    DDMTemplate ddmTemplate, Map<String, String> tokens, String languageId,
1021                    boolean transform) {
1022    
1023                    String script = ddmTemplate.getScript();
1024    
1025                    if (!transform) {
1026                            return script;
1027                    }
1028    
1029                    String[] transformerListenerClassNames = PropsUtil.getArray(
1030                            PropsKeys.JOURNAL_TRANSFORMER_LISTENER);
1031    
1032                    for (String transformerListenerClassName :
1033                                    transformerListenerClassNames) {
1034    
1035                            TransformerListener transformerListener = null;
1036    
1037                            try {
1038                                    transformerListener =
1039                                            (TransformerListener)InstanceFactory.newInstance(
1040                                                    transformerListenerClassName);
1041    
1042                                    continue;
1043                            }
1044                            catch (Exception e) {
1045                                    _log.error(e, e);
1046                            }
1047    
1048                            script = transformerListener.onScript(
1049                                    script, null, languageId, tokens);
1050                    }
1051    
1052                    return script;
1053            }
1054    
1055            public static String getTemplateScript(
1056                            long groupId, String ddmTemplateKey, Map<String, String> tokens,
1057                            String languageId)
1058                    throws PortalException, SystemException {
1059    
1060                    return getTemplateScript(
1061                            groupId, ddmTemplateKey, tokens, languageId, true);
1062            }
1063    
1064            public static String getTemplateScript(
1065                            long groupId, String ddmTemplateKey, Map<String, String> tokens,
1066                            String languageId, boolean transform)
1067                    throws PortalException, SystemException {
1068    
1069                    DDMTemplate ddmTemplate = DDMTemplateLocalServiceUtil.getTemplate(
1070                            groupId, PortalUtil.getClassNameId(DDMStructure.class),
1071                            ddmTemplateKey, true);
1072    
1073                    return getTemplateScript(ddmTemplate, tokens, languageId, transform);
1074            }
1075    
1076            public static Map<String, String> getTokens(
1077                            long articleGroupId, ThemeDisplay themeDisplay)
1078                    throws PortalException, SystemException {
1079    
1080                    return getTokens(articleGroupId, themeDisplay, null);
1081            }
1082    
1083            public static Map<String, String> getTokens(
1084                            long articleGroupId, ThemeDisplay themeDisplay, String xmlRequest)
1085                    throws PortalException, SystemException {
1086    
1087                    Map<String, String> tokens = new HashMap<String, String>();
1088    
1089                    if (themeDisplay != null) {
1090                            _populateTokens(tokens, articleGroupId, themeDisplay);
1091                    }
1092                    else if (Validator.isNotNull(xmlRequest)) {
1093                            try {
1094                                    _populateTokens(tokens, articleGroupId, xmlRequest);
1095                            }
1096                            catch (Exception e) {
1097                                    if (_log.isWarnEnabled()) {
1098                                            _log.warn(e, e);
1099                                    }
1100                            }
1101                    }
1102    
1103                    return tokens;
1104            }
1105    
1106            public static String getUrlTitle(long id, String title) {
1107                    if (title == null) {
1108                            return String.valueOf(id);
1109                    }
1110    
1111                    title = StringUtil.toLowerCase(title.trim());
1112    
1113                    if (Validator.isNull(title) || Validator.isNumber(title) ||
1114                            title.equals("rss")) {
1115    
1116                            title = String.valueOf(id);
1117                    }
1118                    else {
1119                            title = FriendlyURLNormalizerUtil.normalize(
1120                                    title, _friendlyURLPattern);
1121                    }
1122    
1123                    return ModelHintsUtil.trimString(
1124                            JournalArticle.class.getName(), "urlTitle", title);
1125            }
1126    
1127            public static String mergeArticleContent(
1128                    String curContent, String newContent, boolean removeNullElements) {
1129    
1130                    try {
1131                            Document curDocument = SAXReaderUtil.read(curContent);
1132                            Document newDocument = SAXReaderUtil.read(newContent);
1133    
1134                            Element curRootElement = curDocument.getRootElement();
1135                            Element newRootElement = newDocument.getRootElement();
1136    
1137                            curRootElement.addAttribute(
1138                                    "default-locale",
1139                                    newRootElement.attributeValue("default-locale"));
1140                            curRootElement.addAttribute(
1141                                    "available-locales",
1142                                    newRootElement.attributeValue("available-locales"));
1143    
1144                            _mergeArticleContentUpdate(
1145                                    curDocument, newRootElement,
1146                                    LocaleUtil.toLanguageId(LocaleUtil.getSiteDefault()));
1147    
1148                            if (removeNullElements) {
1149                                    _mergeArticleContentDelete(curRootElement, newDocument);
1150                            }
1151    
1152                            curContent = DDMXMLUtil.formatXML(curDocument);
1153                    }
1154                    catch (Exception e) {
1155                            _log.error(e, e);
1156                    }
1157    
1158                    return curContent;
1159            }
1160    
1161            public static void removeArticleLocale(Element element, String languageId)
1162                    throws PortalException, SystemException {
1163    
1164                    for (Element dynamicElementElement :
1165                                    element.elements("dynamic-element")) {
1166    
1167                            for (Element dynamicContentElement :
1168                                            dynamicElementElement.elements("dynamic-content")) {
1169    
1170                                    String curLanguageId = GetterUtil.getString(
1171                                            dynamicContentElement.attributeValue("language-id"));
1172    
1173                                    if (curLanguageId.equals(languageId)) {
1174                                            long id = GetterUtil.getLong(
1175                                                    dynamicContentElement.attributeValue("id"));
1176    
1177                                            if (id > 0) {
1178                                                    ImageLocalServiceUtil.deleteImage(id);
1179                                            }
1180    
1181                                            dynamicContentElement.detach();
1182                                    }
1183                            }
1184    
1185                            removeArticleLocale(dynamicElementElement, languageId);
1186                    }
1187            }
1188    
1189            public static String removeArticleLocale(
1190                    String content, String languageId) {
1191    
1192                    try {
1193                            Document document = SAXReaderUtil.read(content);
1194    
1195                            Element rootElement = document.getRootElement();
1196    
1197                            String availableLocales = rootElement.attributeValue(
1198                                    "available-locales");
1199    
1200                            if (availableLocales == null) {
1201                                    return content;
1202                            }
1203    
1204                            availableLocales = StringUtil.remove(availableLocales, languageId);
1205    
1206                            if (availableLocales.endsWith(",")) {
1207                                    availableLocales = availableLocales.substring(
1208                                            0, availableLocales.length() - 1);
1209                            }
1210    
1211                            rootElement.addAttribute("available-locales", availableLocales);
1212    
1213                            removeArticleLocale(rootElement, languageId);
1214    
1215                            content = DDMXMLUtil.formatXML(document);
1216                    }
1217                    catch (Exception e) {
1218                            _log.error(e, e);
1219                    }
1220    
1221                    return content;
1222            }
1223    
1224            public static String removeOldContent(String content, String xsd) {
1225                    try {
1226                            Document contentDoc = SAXReaderUtil.read(content);
1227                            Document xsdDoc = SAXReaderUtil.read(xsd);
1228    
1229                            Element contentRoot = contentDoc.getRootElement();
1230    
1231                            Stack<String> path = new Stack<String>();
1232    
1233                            path.push(contentRoot.getName());
1234    
1235                            _removeOldContent(path, contentRoot, xsdDoc);
1236    
1237                            content = DDMXMLUtil.formatXML(contentDoc);
1238                    }
1239                    catch (Exception e) {
1240                            _log.error(e, e);
1241                    }
1242    
1243                    return content;
1244            }
1245    
1246            public static void removeRecentArticle(
1247                    PortletRequest portletRequest, String articleId) {
1248    
1249                    removeRecentArticle(portletRequest, articleId, 0);
1250            }
1251    
1252            public static void removeRecentArticle(
1253                    PortletRequest portletRequest, String articleId, double version) {
1254    
1255                    Stack<JournalArticle> stack = getRecentArticles(portletRequest);
1256    
1257                    Iterator<JournalArticle> itr = stack.iterator();
1258    
1259                    while (itr.hasNext()) {
1260                            JournalArticle journalArticle = itr.next();
1261    
1262                            if (journalArticle.getArticleId().equals(articleId) &&
1263                                    ((journalArticle.getVersion() == version) ||
1264                                     (version == 0))) {
1265    
1266                                    itr.remove();
1267                            }
1268                    }
1269            }
1270    
1271            public static void removeRecentDDMStructure(
1272                    PortletRequest portletRequest, String ddmStructureKey) {
1273    
1274                    Stack<DDMStructure> stack = getRecentDDMStructures(portletRequest);
1275    
1276                    Iterator<DDMStructure> itr = stack.iterator();
1277    
1278                    while (itr.hasNext()) {
1279                            DDMStructure ddmStructure = itr.next();
1280    
1281                            if (ddmStructureKey.equals(ddmStructure.getStructureKey())) {
1282                                    itr.remove();
1283    
1284                                    break;
1285                            }
1286                    }
1287            }
1288    
1289            public static void removeRecentDDMTemplate(
1290                    PortletRequest portletRequest, String ddmTemplateKey) {
1291    
1292                    Stack<DDMTemplate> stack = getRecentDDMTemplates(portletRequest);
1293    
1294                    Iterator<DDMTemplate> itr = stack.iterator();
1295    
1296                    while (itr.hasNext()) {
1297                            DDMTemplate ddmTemplate = itr.next();
1298    
1299                            if (ddmTemplateKey.equals(ddmTemplate.getTemplateKey())) {
1300                                    itr.remove();
1301    
1302                                    break;
1303                            }
1304                    }
1305            }
1306    
1307            public static List<JournalStructure> toJournalStructures(
1308                            List<DDMStructure> ddmStructures)
1309                    throws SystemException {
1310    
1311                    List<JournalStructure> structures = new ArrayList<JournalStructure>();
1312    
1313                    for (DDMStructure ddmStructure : ddmStructures) {
1314                            JournalStructure structure = new JournalStructureAdapter(
1315                                    ddmStructure);
1316    
1317                            structures.add(structure);
1318                    }
1319    
1320                    return new UnmodifiableList<JournalStructure>(structures);
1321            }
1322    
1323            public static List<JournalTemplate> toJournalTemplates(
1324                    List<DDMTemplate> ddmTemplates) {
1325    
1326                    List<JournalTemplate> templates = new ArrayList<JournalTemplate>();
1327    
1328                    for (DDMTemplate ddmTemplate : ddmTemplates) {
1329                            JournalTemplate template = new JournalTemplateAdapter(ddmTemplate);
1330    
1331                            templates.add(template);
1332                    }
1333    
1334                    return new UnmodifiableList<JournalTemplate>(templates);
1335            }
1336    
1337            public static String transform(
1338                            ThemeDisplay themeDisplay, Map<String, String> tokens,
1339                            String viewMode, String languageId, String xml, String script,
1340                            String langType)
1341                    throws Exception {
1342    
1343                    return _transformer.transform(
1344                            themeDisplay, tokens, viewMode, languageId, xml, script, langType);
1345            }
1346    
1347            private static void _addElementOptions(
1348                    Element curContentElement, Element newContentElement) {
1349    
1350                    List<Element> newElementOptions = newContentElement.elements("option");
1351    
1352                    for (Element newElementOption : newElementOptions) {
1353                            Element curElementOption = SAXReaderUtil.createElement("option");
1354    
1355                            curElementOption.addCDATA(newElementOption.getText());
1356    
1357                            curContentElement.add(curElementOption);
1358                    }
1359            }
1360    
1361            private static Element _getElementByInstanceId(
1362                    Document document, String instanceId) {
1363    
1364                    XPath xPathSelector = SAXReaderUtil.createXPath(
1365                            "//dynamic-element[@instance-id=" +
1366                                    HtmlUtil.escapeXPathAttribute(instanceId) + "]");
1367    
1368                    List<Node> nodes = xPathSelector.selectNodes(document);
1369    
1370                    if (nodes.size() == 1) {
1371                            return (Element)nodes.get(0);
1372                    }
1373                    else {
1374                            return null;
1375                    }
1376            }
1377    
1378            private static void _mergeArticleContentDelete(
1379                            Element curParentElement, Document newDocument)
1380                    throws Exception {
1381    
1382                    List<Element> curElements = curParentElement.elements(
1383                            "dynamic-element");
1384    
1385                    for (int i = 0; i < curElements.size(); i++) {
1386                            Element curElement = curElements.get(i);
1387    
1388                            _mergeArticleContentDelete(curElement, newDocument);
1389    
1390                            String instanceId = curElement.attributeValue("instance-id");
1391    
1392                            Element newElement = _getElementByInstanceId(
1393                                    newDocument, instanceId);
1394    
1395                            if (newElement == null) {
1396                                    curElement.detach();
1397                            }
1398                    }
1399            }
1400    
1401            private static void _mergeArticleContentUpdate(
1402                            Document curDocument, Element newParentElement, Element newElement,
1403                            int pos, String defaultLocale)
1404                    throws Exception {
1405    
1406                    _mergeArticleContentUpdate(curDocument, newElement, defaultLocale);
1407    
1408                    String instanceId = newElement.attributeValue("instance-id");
1409    
1410                    Element curElement = _getElementByInstanceId(curDocument, instanceId);
1411    
1412                    if (curElement != null) {
1413                            _mergeArticleContentUpdate(curElement, newElement, defaultLocale);
1414                    }
1415                    else {
1416                            String parentInstanceId = newParentElement.attributeValue(
1417                                    "instance-id");
1418    
1419                            if (Validator.isNull(parentInstanceId)) {
1420                                    Element curRoot = curDocument.getRootElement();
1421    
1422                                    List<Element> curRootElements = curRoot.elements();
1423    
1424                                    curRootElements.add(pos, newElement.createCopy());
1425                            }
1426                            else {
1427                                    Element curParentElement = _getElementByInstanceId(
1428                                            curDocument, parentInstanceId);
1429    
1430                                    if (curParentElement != null) {
1431                                            List<Element> curParentElements =
1432                                                    curParentElement.elements();
1433    
1434                                            curParentElements.add(pos, newElement.createCopy());
1435                                    }
1436                            }
1437                    }
1438            }
1439    
1440            private static void _mergeArticleContentUpdate(
1441                            Document curDocument, Element newParentElement,
1442                            String defaultLocale)
1443                    throws Exception {
1444    
1445                    List<Element> newElements = newParentElement.elements(
1446                            "dynamic-element");
1447    
1448                    for (int i = 0; i < newElements.size(); i++) {
1449                            Element newElement = newElements.get(i);
1450    
1451                            _mergeArticleContentUpdate(
1452                                    curDocument, newParentElement, newElement, i, defaultLocale);
1453                    }
1454            }
1455    
1456            private static void _mergeArticleContentUpdate(
1457                    Element curElement, Element newElement, String defaultLocale) {
1458    
1459                    Attribute curTypeAttribute = curElement.attribute("type");
1460                    Attribute newTypeAttribute = newElement.attribute("type");
1461    
1462                    curTypeAttribute.setValue(newTypeAttribute.getValue());
1463    
1464                    Attribute curIndexTypeAttribute = curElement.attribute("index-type");
1465                    Attribute newIndexTypeAttribute = newElement.attribute("index-type");
1466    
1467                    if (newIndexTypeAttribute != null) {
1468                            if (curIndexTypeAttribute == null) {
1469                                    curElement.addAttribute(
1470                                            "index-type", newIndexTypeAttribute.getValue());
1471                            }
1472                            else {
1473                                    curIndexTypeAttribute.setValue(
1474                                            newIndexTypeAttribute.getValue());
1475                            }
1476                    }
1477    
1478                    List<Element> elements = newElement.elements("dynamic-content");
1479    
1480                    Element newContentElement = elements.get(0);
1481    
1482                    String newLanguageId = newContentElement.attributeValue("language-id");
1483                    String newValue = newContentElement.getText();
1484    
1485                    String indexType = newElement.attributeValue("index-type");
1486    
1487                    if (Validator.isNotNull(indexType)) {
1488                            curElement.addAttribute("index-type", indexType);
1489                    }
1490    
1491                    List<Element> curContentElements = curElement.elements(
1492                            "dynamic-content");
1493    
1494                    if (Validator.isNull(newLanguageId)) {
1495                            for (Element curContentElement : curContentElements) {
1496                                    curContentElement.detach();
1497                            }
1498    
1499                            Element curContentElement = SAXReaderUtil.createElement(
1500                                    "dynamic-content");
1501    
1502                            if (newContentElement.element("option") != null) {
1503                                    _addElementOptions(curContentElement, newContentElement);
1504                            }
1505                            else {
1506                                    curContentElement.addCDATA(newValue);
1507                            }
1508    
1509                            curElement.add(curContentElement);
1510                    }
1511                    else {
1512                            boolean alreadyExists = false;
1513    
1514                            for (Element curContentElement : curContentElements) {
1515                                    String curLanguageId = curContentElement.attributeValue(
1516                                            "language-id");
1517    
1518                                    if (newLanguageId.equals(curLanguageId)) {
1519                                            alreadyExists = true;
1520    
1521                                            curContentElement.clearContent();
1522    
1523                                            if (newContentElement.element("option") != null) {
1524                                                    _addElementOptions(
1525                                                            curContentElement, newContentElement);
1526                                            }
1527                                            else {
1528                                                    curContentElement.addCDATA(newValue);
1529                                            }
1530    
1531                                            break;
1532                                    }
1533                            }
1534    
1535                            if (!alreadyExists) {
1536                                    Element curContentElement = curContentElements.get(0);
1537    
1538                                    String curLanguageId = curContentElement.attributeValue(
1539                                            "language-id");
1540    
1541                                    if (Validator.isNull(curLanguageId)) {
1542                                            if (newLanguageId.equals(defaultLocale)) {
1543                                                    curContentElement.clearContent();
1544    
1545                                                    if (newContentElement.element("option") != null) {
1546                                                            _addElementOptions(
1547                                                                    curContentElement, newContentElement);
1548                                                    }
1549                                                    else {
1550                                                            curContentElement.addCDATA(newValue);
1551                                                    }
1552                                            }
1553                                            else {
1554                                                    curElement.add(newContentElement.createCopy());
1555                                            }
1556    
1557                                            curContentElement.addAttribute(
1558                                                    "language-id", defaultLocale);
1559                                    }
1560                                    else {
1561                                            curElement.add(newContentElement.createCopy());
1562                                    }
1563                            }
1564                    }
1565            }
1566    
1567            private static void _populateCustomTokens(Map<String, String> tokens) {
1568                    if (_customTokens == null) {
1569                            synchronized (JournalUtil.class) {
1570                                    _customTokens = new HashMap<String, String>();
1571    
1572                                    for (String customToken :
1573                                                    PropsValues.JOURNAL_ARTICLE_CUSTOM_TOKENS) {
1574    
1575                                            String value = PropsUtil.get(
1576                                                    PropsKeys.JOURNAL_ARTICLE_CUSTOM_TOKEN_VALUE,
1577                                                    new Filter(customToken));
1578    
1579                                            _customTokens.put(customToken, value);
1580                                    }
1581                            }
1582                    }
1583    
1584                    if (!_customTokens.isEmpty()) {
1585                            tokens.putAll(_customTokens);
1586                    }
1587            }
1588    
1589            private static void _populateTokens(
1590                            Map<String, String> tokens, long articleGroupId, String xmlRequest)
1591                    throws Exception {
1592    
1593                    Document requestDocument = SAXReaderUtil.read(xmlRequest);
1594    
1595                    Element rootElement = requestDocument.getRootElement();
1596    
1597                    Element themeDisplayElement = rootElement.element("theme-display");
1598    
1599                    Layout layout = LayoutLocalServiceUtil.getLayout(
1600                            GetterUtil.getLong(themeDisplayElement.elementText("plid")));
1601    
1602                    Group group = layout.getGroup();
1603    
1604                    LayoutSet layoutSet = layout.getLayoutSet();
1605    
1606                    String friendlyUrlCurrent = null;
1607    
1608                    if (layout.isPublicLayout()) {
1609                            friendlyUrlCurrent = themeDisplayElement.elementText(
1610                                    "path-friendly-url-public");
1611                    }
1612                    else if (group.isUserGroup()) {
1613                            friendlyUrlCurrent = themeDisplayElement.elementText(
1614                                    "path-friendly-url-private-user");
1615                    }
1616                    else {
1617                            friendlyUrlCurrent = themeDisplayElement.elementText(
1618                                    "path-friendly-url-private-group");
1619                    }
1620    
1621                    String layoutSetFriendlyUrl = themeDisplayElement.elementText(
1622                            "i18n-path");
1623    
1624                    String virtualHostname = layoutSet.getVirtualHostname();
1625    
1626                    if (Validator.isNull(virtualHostname) ||
1627                            !virtualHostname.equals(
1628                                    themeDisplayElement.elementText("server-name"))) {
1629    
1630                            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1631                    }
1632    
1633                    tokens.put("article_group_id", String.valueOf(articleGroupId));
1634                    tokens.put("cdn_host", themeDisplayElement.elementText("cdn-host"));
1635                    tokens.put("company_id", themeDisplayElement.elementText("company-id"));
1636                    tokens.put("friendly_url_current", friendlyUrlCurrent);
1637                    tokens.put(
1638                            "friendly_url_private_group",
1639                            themeDisplayElement.elementText("path-friendly-url-private-group"));
1640                    tokens.put(
1641                            "friendly_url_private_user",
1642                            themeDisplayElement.elementText("path-friendly-url-private-user"));
1643                    tokens.put(
1644                            "friendly_url_public",
1645                            themeDisplayElement.elementText("path-friendly-url-public"));
1646                    tokens.put("group_friendly_url", group.getFriendlyURL());
1647                    tokens.put("image_path", themeDisplayElement.elementText("path-image"));
1648                    tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1649                    tokens.put("main_path", themeDisplayElement.elementText("path-main"));
1650                    tokens.put(
1651                            "portal_ctx", themeDisplayElement.elementText("path-context"));
1652                    tokens.put(
1653                            "portal_url",
1654                            HttpUtil.removeProtocol(
1655                                    themeDisplayElement.elementText("url-portal")));
1656                    tokens.put(
1657                            "protocol",
1658                            HttpUtil.getProtocol(
1659                                    themeDisplayElement.elementText("url-portal")));
1660                    tokens.put(
1661                            "root_path", themeDisplayElement.elementText("path-context"));
1662                    tokens.put(
1663                            "scope_group_id",
1664                            themeDisplayElement.elementText("scope-group-id"));
1665                    tokens.put(
1666                            "site_group_id", themeDisplayElement.elementText("site-group-id"));
1667                    tokens.put(
1668                            "theme_image_path",
1669                            themeDisplayElement.elementText("path-theme-images"));
1670    
1671                    _populateCustomTokens(tokens);
1672    
1673                    // Deprecated tokens
1674    
1675                    tokens.put(
1676                            "friendly_url",
1677                            themeDisplayElement.elementText("path-friendly-url-public"));
1678                    tokens.put(
1679                            "friendly_url_private",
1680                            themeDisplayElement.elementText("path-friendly-url-private-group"));
1681                    tokens.put("group_id", String.valueOf(articleGroupId));
1682                    tokens.put(
1683                            "page_url",
1684                            themeDisplayElement.elementText("path-friendly-url-public"));
1685            }
1686    
1687            private static void _populateTokens(
1688                            Map<String, String> tokens, long articleGroupId,
1689                            ThemeDisplay themeDisplay)
1690                    throws PortalException, SystemException {
1691    
1692                    Layout layout = themeDisplay.getLayout();
1693    
1694                    Group group = layout.getGroup();
1695    
1696                    LayoutSet layoutSet = layout.getLayoutSet();
1697    
1698                    String friendlyUrlCurrent = null;
1699    
1700                    if (layout.isPublicLayout()) {
1701                            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPublic();
1702                    }
1703                    else if (group.isUserGroup()) {
1704                            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateUser();
1705                    }
1706                    else {
1707                            friendlyUrlCurrent = themeDisplay.getPathFriendlyURLPrivateGroup();
1708                    }
1709    
1710                    String layoutSetFriendlyUrl = themeDisplay.getI18nPath();
1711    
1712                    String virtualHostname = layoutSet.getVirtualHostname();
1713    
1714                    if (Validator.isNull(virtualHostname) ||
1715                            !virtualHostname.equals(themeDisplay.getServerName())) {
1716    
1717                            layoutSetFriendlyUrl = friendlyUrlCurrent + group.getFriendlyURL();
1718                    }
1719    
1720                    tokens.put("article_group_id", String.valueOf(articleGroupId));
1721                    tokens.put("cdn_host", themeDisplay.getCDNHost());
1722                    tokens.put("company_id", String.valueOf(themeDisplay.getCompanyId()));
1723                    tokens.put("friendly_url_current", friendlyUrlCurrent);
1724                    tokens.put(
1725                            "friendly_url_private_group",
1726                            themeDisplay.getPathFriendlyURLPrivateGroup());
1727                    tokens.put(
1728                            "friendly_url_private_user",
1729                            themeDisplay.getPathFriendlyURLPrivateUser());
1730                    tokens.put(
1731                            "friendly_url_public", themeDisplay.getPathFriendlyURLPublic());
1732                    tokens.put("group_friendly_url", group.getFriendlyURL());
1733                    tokens.put("image_path", themeDisplay.getPathImage());
1734                    tokens.put("layout_set_friendly_url", layoutSetFriendlyUrl);
1735                    tokens.put("main_path", themeDisplay.getPathMain());
1736                    tokens.put("portal_ctx", themeDisplay.getPathContext());
1737                    tokens.put(
1738                            "portal_url", HttpUtil.removeProtocol(themeDisplay.getURLPortal()));
1739                    tokens.put(
1740                            "protocol", HttpUtil.getProtocol(themeDisplay.getURLPortal()));
1741                    tokens.put("root_path", themeDisplay.getPathContext());
1742                    tokens.put(
1743                            "site_group_id", String.valueOf(themeDisplay.getSiteGroupId()));
1744                    tokens.put(
1745                            "scope_group_id", String.valueOf(themeDisplay.getScopeGroupId()));
1746                    tokens.put("theme_image_path", themeDisplay.getPathThemeImages());
1747    
1748                    _populateCustomTokens(tokens);
1749    
1750                    // Deprecated tokens
1751    
1752                    tokens.put("friendly_url", themeDisplay.getPathFriendlyURLPublic());
1753                    tokens.put(
1754                            "friendly_url_private",
1755                            themeDisplay.getPathFriendlyURLPrivateGroup());
1756                    tokens.put("group_id", String.valueOf(articleGroupId));
1757                    tokens.put("page_url", themeDisplay.getPathFriendlyURLPublic());
1758            }
1759    
1760            private static void _removeOldContent(
1761                            Stack<String> path, Element contentElement, Document xsdDocument)
1762                    throws SystemException {
1763    
1764                    String elementPath = "";
1765    
1766                    for (int i = 0; i < path.size(); i++) {
1767                            elementPath += "/" + path.elementAt(i);
1768                    }
1769    
1770                    for (int i = 0; i < contentElement.nodeCount(); i++) {
1771                            Node contentNode = contentElement.node(i);
1772    
1773                            if (contentNode instanceof Element) {
1774                                    _removeOldContent(
1775                                            path, (Element)contentNode, xsdDocument, elementPath);
1776                            }
1777                    }
1778            }
1779    
1780            private static void _removeOldContent(
1781                            Stack<String> path, Element contentElement, Document xsdDocument,
1782                            String elementPath)
1783                    throws SystemException {
1784    
1785                    String name = contentElement.attributeValue("name");
1786    
1787                    if (Validator.isNull(name)) {
1788                            return;
1789                    }
1790    
1791                    String localPath =
1792                            "dynamic-element[@name=" + HtmlUtil.escapeXPathAttribute(name) +
1793                                    "]";
1794    
1795                    String fullPath = elementPath + "/" + localPath;
1796    
1797                    XPath xPathSelector = SAXReaderUtil.createXPath(fullPath);
1798    
1799                    List<Node> curNodes = xPathSelector.selectNodes(xsdDocument);
1800    
1801                    if (curNodes.size() == 0) {
1802                            contentElement.detach();
1803                    }
1804    
1805                    path.push(localPath);
1806    
1807                    _removeOldContent(path, contentElement, xsdDocument);
1808    
1809                    path.pop();
1810            }
1811    
1812            private static Log _log = LogFactoryUtil.getLog(JournalUtil.class);
1813    
1814            private static Map<String, String> _customTokens;
1815            private static Pattern _friendlyURLPattern = Pattern.compile("[^a-z0-9_-]");
1816            private static Transformer _transformer = new Transformer(
1817                    PropsKeys.JOURNAL_TRANSFORMER_LISTENER,
1818                    PropsKeys.JOURNAL_ERROR_TEMPLATE, true);
1819    
1820    }