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