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