001    /**
002     * Copyright (c) 2000-2012 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.wiki.util;
016    
017    import com.liferay.portal.kernel.configuration.Filter;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
021    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
022    import com.liferay.portal.kernel.util.DiffHtmlUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.HtmlUtil;
025    import com.liferay.portal.kernel.util.HttpUtil;
026    import com.liferay.portal.kernel.util.InstancePool;
027    import com.liferay.portal.kernel.util.ListUtil;
028    import com.liferay.portal.kernel.util.OrderByComparator;
029    import com.liferay.portal.kernel.util.ParamUtil;
030    import com.liferay.portal.kernel.util.PropsKeys;
031    import com.liferay.portal.kernel.util.StringBundler;
032    import com.liferay.portal.kernel.util.StringPool;
033    import com.liferay.portal.kernel.util.StringUtil;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.security.permission.ActionKeys;
036    import com.liferay.portal.security.permission.PermissionChecker;
037    import com.liferay.portal.theme.ThemeDisplay;
038    import com.liferay.portal.util.PortalUtil;
039    import com.liferay.portal.util.PropsUtil;
040    import com.liferay.portal.util.PropsValues;
041    import com.liferay.portal.util.WebKeys;
042    import com.liferay.portlet.PortletURLUtil;
043    import com.liferay.portlet.wiki.PageContentException;
044    import com.liferay.portlet.wiki.WikiFormatException;
045    import com.liferay.portlet.wiki.engines.WikiEngine;
046    import com.liferay.portlet.wiki.model.WikiNode;
047    import com.liferay.portlet.wiki.model.WikiPage;
048    import com.liferay.portlet.wiki.model.WikiPageDisplay;
049    import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
050    import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
051    import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
052    import com.liferay.portlet.wiki.util.comparator.PageTitleComparator;
053    import com.liferay.portlet.wiki.util.comparator.PageVersionComparator;
054    import com.liferay.util.ContentUtil;
055    
056    import java.io.IOException;
057    
058    import java.util.ArrayList;
059    import java.util.Arrays;
060    import java.util.Collections;
061    import java.util.HashSet;
062    import java.util.Iterator;
063    import java.util.List;
064    import java.util.Map;
065    import java.util.Set;
066    import java.util.concurrent.ConcurrentHashMap;
067    import java.util.regex.Matcher;
068    import java.util.regex.Pattern;
069    
070    import javax.portlet.PortletPreferences;
071    import javax.portlet.PortletRequest;
072    import javax.portlet.PortletURL;
073    import javax.portlet.RenderRequest;
074    import javax.portlet.RenderResponse;
075    
076    /**
077     * @author Brian Wing Shun Chan
078     * @author Jorge Ferrer
079     */
080    public class WikiUtil {
081    
082            public static String convert(
083                            WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
084                            String attachmentURLPrefix)
085                    throws PageContentException, WikiFormatException {
086    
087                    return _instance._convert(
088                            page, viewPageURL, editPageURL, attachmentURLPrefix);
089            }
090    
091            public static String diffHtml(
092                            WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
093                            PortletURL editPageURL, String attachmentURLPrefix)
094                    throws Exception {
095    
096                    String sourceContent = StringPool.BLANK;
097                    String targetContent = StringPool.BLANK;
098    
099                    if (sourcePage != null) {
100                            sourceContent = WikiUtil.convert(
101                                    sourcePage, viewPageURL, editPageURL, attachmentURLPrefix);
102                    }
103    
104                    if (targetPage != null) {
105                            targetContent = WikiUtil.convert(
106                                    targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
107                    }
108    
109                    return DiffHtmlUtil.diff(
110                            new UnsyncStringReader(sourceContent),
111                            new UnsyncStringReader(targetContent));
112            }
113    
114            public static List<WikiPage> filterOrphans(List<WikiPage> pages)
115                    throws PortalException {
116    
117                    List<Map<String, Boolean>> pageTitles =
118                            new ArrayList<Map<String, Boolean>>();
119    
120                    for (WikiPage page : pages) {
121                            pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
122                    }
123    
124                    Set<WikiPage> notOrphans = new HashSet<WikiPage>();
125    
126                    for (WikiPage page : pages) {
127                            for (Map<String, Boolean> pageTitle : pageTitles) {
128                                    String pageTitleLowerCase = page.getTitle();
129    
130                                    if (pageTitle.get(pageTitleLowerCase) != null) {
131                                            notOrphans.add(page);
132    
133                                            break;
134                                    }
135                            }
136                    }
137    
138                    List<WikiPage> orphans = new ArrayList<WikiPage>();
139    
140                    for (WikiPage page : pages) {
141                            if (!notOrphans.contains(page)) {
142                                    orphans.add(page);
143                            }
144                    }
145    
146                    orphans = ListUtil.sort(orphans);
147    
148                    return orphans;
149            }
150    
151            public static String getEditPage(String format) {
152                    return _instance._getEditPage(format);
153            }
154    
155            public static String getEmailFromAddress(
156                            PortletPreferences preferences, long companyId)
157                    throws SystemException {
158    
159                    return PortalUtil.getEmailFromAddress(
160                            preferences, companyId, PropsValues.WIKI_EMAIL_FROM_ADDRESS);
161            }
162    
163            public static String getEmailFromName(
164                            PortletPreferences preferences, long companyId)
165                    throws SystemException {
166    
167                    return PortalUtil.getEmailFromName(
168                            preferences, companyId, PropsValues.WIKI_EMAIL_FROM_NAME);
169            }
170    
171            public static String getEmailPageAddedBody(PortletPreferences preferences) {
172                    String emailPageAddedBody = preferences.getValue(
173                            "emailPageAddedBody", StringPool.BLANK);
174    
175                    if (Validator.isNotNull(emailPageAddedBody)) {
176                            return emailPageAddedBody;
177                    }
178                    else {
179                            return ContentUtil.get(
180                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_BODY));
181                    }
182            }
183    
184            public static boolean getEmailPageAddedEnabled(
185                    PortletPreferences preferences) {
186    
187                    String emailPageAddedEnabled = preferences.getValue(
188                            "emailPageAddedEnabled", StringPool.BLANK);
189    
190                    if (Validator.isNotNull(emailPageAddedEnabled)) {
191                            return GetterUtil.getBoolean(emailPageAddedEnabled);
192                    }
193                    else {
194                            return GetterUtil.getBoolean(
195                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_ENABLED));
196                    }
197            }
198    
199            public static String getEmailPageAddedSignature(
200                    PortletPreferences preferences) {
201    
202                    String emailPageAddedSignature = preferences.getValue(
203                            "emailPageAddedSignature", StringPool.BLANK);
204    
205                    if (Validator.isNotNull(emailPageAddedSignature)) {
206                            return emailPageAddedSignature;
207                    }
208                    else {
209                            return ContentUtil.get(
210                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_SIGNATURE));
211                    }
212            }
213    
214            public static String getEmailPageAddedSubjectPrefix(
215                    PortletPreferences preferences) {
216    
217                    String emailPageAddedSubjectPrefix = preferences.getValue(
218                            "emailPageAddedSubjectPrefix", StringPool.BLANK);
219    
220                    if (Validator.isNotNull(emailPageAddedSubjectPrefix)) {
221                            return emailPageAddedSubjectPrefix;
222                    }
223                    else {
224                            return ContentUtil.get(
225                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_SUBJECT_PREFIX));
226                    }
227            }
228    
229            public static String getEmailPageUpdatedBody(
230                    PortletPreferences preferences) {
231    
232                    String emailPageUpdatedBody = preferences.getValue(
233                            "emailPageUpdatedBody", StringPool.BLANK);
234    
235                    if (Validator.isNotNull(emailPageUpdatedBody)) {
236                            return emailPageUpdatedBody;
237                    }
238                    else {
239                            return ContentUtil.get(
240                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_BODY));
241                    }
242            }
243    
244            public static boolean getEmailPageUpdatedEnabled(
245                    PortletPreferences preferences) {
246    
247                    String emailPageUpdatedEnabled = preferences.getValue(
248                            "emailPageUpdatedEnabled", StringPool.BLANK);
249    
250                    if (Validator.isNotNull(emailPageUpdatedEnabled)) {
251                            return GetterUtil.getBoolean(emailPageUpdatedEnabled);
252                    }
253                    else {
254                            return GetterUtil.getBoolean(
255                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_ENABLED));
256                    }
257            }
258    
259            public static String getEmailPageUpdatedSignature(
260                    PortletPreferences preferences) {
261    
262                    String emailPageUpdatedSignature = preferences.getValue(
263                            "emailPageUpdatedSignature", StringPool.BLANK);
264    
265                    if (Validator.isNotNull(emailPageUpdatedSignature)) {
266                            return emailPageUpdatedSignature;
267                    }
268                    else {
269                            return ContentUtil.get(
270                                    PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SIGNATURE));
271                    }
272            }
273    
274            public static String getEmailPageUpdatedSubjectPrefix(
275                    PortletPreferences preferences) {
276    
277                    String emailPageUpdatedSubject = preferences.getValue(
278                            "emailPageUpdatedSubjectPrefix", StringPool.BLANK);
279    
280                    if (Validator.isNotNull(emailPageUpdatedSubject)) {
281                            return emailPageUpdatedSubject;
282                    }
283                    else {
284                            return ContentUtil.get(
285                                    PropsUtil.get(
286                                            PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SUBJECT_PREFIX));
287                    }
288            }
289    
290            public static WikiNode getFirstNode(PortletRequest portletRequest)
291                    throws PortalException, SystemException {
292    
293                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
294                            WebKeys.THEME_DISPLAY);
295                    long groupId = themeDisplay.getScopeGroupId();
296                    PermissionChecker permissionChecker =
297                            themeDisplay.getPermissionChecker();
298    
299                    List<WikiNode> nodes = WikiNodeLocalServiceUtil.getNodes(groupId);
300    
301                    PortletPreferences preferences = portletRequest.getPreferences();
302                    String[] visibleNodeNames = StringUtil.split(
303                            preferences.getValue("visibleNodes", null));
304                    nodes = orderNodes(nodes, visibleNodeNames);
305    
306                    String[] hiddenNodes = StringUtil.split(
307                            preferences.getValue("hiddenNodes", StringPool.BLANK));
308                    Arrays.sort(hiddenNodes);
309    
310                    for (WikiNode node : nodes) {
311                            if ((Arrays.binarySearch(hiddenNodes, node.getName()) < 0) &&
312                                    (WikiNodePermission.contains(
313                                            permissionChecker, node, ActionKeys.VIEW))) {
314    
315                                    return node;
316                            }
317                    }
318    
319                    return null;
320            }
321    
322            public static String getFormattedContent(
323                            RenderRequest renderRequest, RenderResponse renderResponse,
324                            WikiPage wikiPage, PortletURL viewPageURL, PortletURL editPageURL,
325                            String title, boolean preview)
326                    throws Exception {
327    
328                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
329                            WebKeys.THEME_DISPLAY);
330    
331                    double version = ParamUtil.getDouble(renderRequest, "version");
332    
333                    PortletURL curViewPageURL = PortletURLUtil.clone(
334                            viewPageURL, renderResponse);
335                    PortletURL curEditPageURL = PortletURLUtil.clone(
336                            editPageURL, renderResponse);
337    
338                    StringBundler sb = new StringBundler();
339    
340                    sb.append(themeDisplay.getPathMain());
341                    sb.append("/wiki/get_page_attachment?p_l_id=");
342                    sb.append(themeDisplay.getPlid());
343                    sb.append("&nodeId=");
344                    sb.append(wikiPage.getNodeId());
345                    sb.append("&title=");
346                    sb.append(HttpUtil.encodeURL(wikiPage.getTitle()));
347                    sb.append("&fileName=");
348    
349                    String attachmentURLPrefix = sb.toString();
350    
351                    if (!preview && (version == 0)) {
352                            WikiPageDisplay pageDisplay = WikiCacheUtil.getDisplay(
353                                    wikiPage.getNodeId(), title, curViewPageURL, curEditPageURL,
354                                    attachmentURLPrefix);
355    
356                            if (pageDisplay != null) {
357                                    return pageDisplay.getFormattedContent();
358                            }
359                    }
360    
361                    return WikiUtil.convert(
362                            wikiPage, curViewPageURL, curEditPageURL, attachmentURLPrefix);
363            }
364    
365            public static String getHelpPage(String format) {
366                    return _instance._getHelpPage(format);
367            }
368    
369            public static String getHelpURL(String format) {
370                    return _instance._getHelpURL(format);
371            }
372    
373            public static Map<String, Boolean> getLinks(WikiPage page)
374                    throws PageContentException {
375    
376                    return _instance._getLinks(page);
377            }
378    
379            public static List<String> getNodeNames(List<WikiNode> nodes) {
380                    List<String> nodeNames = new ArrayList<String>(nodes.size());
381    
382                    for (WikiNode node : nodes) {
383                            nodeNames.add(node.getName());
384                    }
385    
386                    return nodeNames;
387            }
388    
389            public static List<WikiNode> getNodes(
390                    List<WikiNode> nodes, String[] hiddenNodes,
391                    PermissionChecker permissionChecker) {
392    
393                    nodes = ListUtil.copy(nodes);
394    
395                    Arrays.sort(hiddenNodes);
396    
397                    Iterator<WikiNode> itr = nodes.iterator();
398    
399                    while (itr.hasNext()) {
400                            WikiNode node = itr.next();
401    
402                            if (!(Arrays.binarySearch(hiddenNodes, node.getName()) < 0) ||
403                                    !WikiNodePermission.contains(
404                                            permissionChecker, node, ActionKeys.VIEW)) {
405    
406                                    itr.remove();
407                            }
408                    }
409    
410                    return nodes;
411            }
412    
413            public static OrderByComparator getPageOrderByComparator(
414                    String orderByCol, String orderByType) {
415    
416                    boolean orderByAsc = false;
417    
418                    if (orderByType.equals("asc")) {
419                            orderByAsc = true;
420                    }
421    
422                    OrderByComparator orderByComparator = null;
423    
424                    if (orderByCol.equals("modifiedDate")) {
425                            orderByComparator = new PageCreateDateComparator(orderByAsc);
426                    }
427                    else if (orderByCol.equals("title")) {
428                            orderByComparator = new PageTitleComparator(orderByAsc);
429                    }
430                    else if (orderByCol.equals("version")) {
431                            orderByComparator = new PageVersionComparator(orderByAsc);
432                    }
433    
434                    return orderByComparator;
435            }
436    
437            public static List<WikiNode> orderNodes(
438                    List<WikiNode> nodes, String[] visibleNodeNames) {
439    
440                    if ((visibleNodeNames == null) || (visibleNodeNames.length == 0)) {
441                            return nodes;
442                    }
443    
444                    nodes = ListUtil.copy(nodes);
445    
446                    List<WikiNode> orderedNodes = new ArrayList<WikiNode>(nodes.size());
447    
448                    for (String visibleNodeName : visibleNodeNames) {
449                            for (WikiNode node : nodes) {
450                                    if (node.getName().equals(visibleNodeName)) {
451                                            orderedNodes.add(node);
452    
453                                            nodes.remove(node);
454    
455                                            break;
456                                    }
457                            }
458                    }
459    
460                    orderedNodes.addAll(nodes);
461    
462                    return orderedNodes;
463            }
464    
465            public static String processContent(String content) {
466                    content = content.replaceAll("</p>", "</p>\n");
467                    content = content.replaceAll("</br>", "</br>\n");
468                    content = content.replaceAll("</div>", "</div>\n");
469    
470                    return content;
471            }
472    
473            public static boolean validate(long nodeId, String content, String format)
474                    throws WikiFormatException {
475    
476                    return _instance._validate(nodeId, content, format);
477            }
478    
479            private String _convert(
480                            WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
481                            String attachmentURLPrefix)
482                    throws PageContentException, WikiFormatException {
483    
484                    LiferayPortletURL liferayViewPageURL = (LiferayPortletURL)viewPageURL;
485                    LiferayPortletURL liferayEditPageURL = (LiferayPortletURL)editPageURL;
486    
487                    WikiEngine engine = _getEngine(page.getFormat());
488    
489                    String content = engine.convert(
490                            page, viewPageURL, editPageURL, attachmentURLPrefix);
491    
492                    String editPageURLString = StringPool.BLANK;
493    
494                    if (editPageURL != null) {
495                            liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
496    
497                            editPageURLString = editPageURL.toString();
498    
499                            editPageURLString = StringUtil.replace(
500                                    editPageURLString, "__REPLACEMENT__", "$1");
501                    }
502    
503                    Matcher matcher = _editPageURLPattern.matcher(content);
504    
505                    content = _convertURLs(editPageURLString, matcher);
506    
507                    String viewPageURLString = StringPool.BLANK;
508    
509                    if (viewPageURL != null) {
510                            liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
511    
512                            viewPageURLString = viewPageURL.toString();
513    
514                            viewPageURLString = StringUtil.replace(
515                                    viewPageURLString, "__REPLACEMENT__", "$1");
516                    }
517    
518                    matcher = _viewPageURLPattern.matcher(content);
519    
520                    content = _convertURLs(viewPageURLString, matcher);
521    
522                    content = _replaceAttachments(
523                            content, page.getTitle(), attachmentURLPrefix);
524    
525                    return content;
526            }
527    
528            private String _convertURLs(String url, Matcher matcher) {
529                    StringBuffer sb = new StringBuffer();
530    
531                    while (matcher.find()) {
532                            String replacement = null;
533    
534                            if (matcher.groupCount() >= 1) {
535                                    String encodedTitle = HttpUtil.encodeURL(
536                                            HtmlUtil.unescape(matcher.group(1)));
537    
538                                    replacement = url.replace("$1", encodedTitle);
539                            }
540                            else {
541                                    replacement = url;
542                            }
543    
544                            matcher.appendReplacement(sb, replacement);
545                    }
546    
547                    return matcher.appendTail(sb).toString();
548            }
549    
550            private String _getEditPage(String format) {
551                    return PropsUtil.get(
552                            PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
553            }
554    
555            private WikiEngine _getEngine(String format) throws WikiFormatException {
556                    WikiEngine engine = _engines.get(format);
557    
558                    if (engine != null) {
559                            return engine;
560                    }
561    
562                    synchronized (_engines) {
563                            engine = _engines.get(format);
564    
565                            if (engine != null) {
566                                    return engine;
567                            }
568    
569                            try {
570                                    String engineClass = PropsUtil.get(
571                                            PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
572    
573                                    if (engineClass == null) {
574                                            throw new WikiFormatException(format);
575                                    }
576    
577                                    if (!InstancePool.contains(engineClass)) {
578                                            engine = (WikiEngine)InstancePool.get(engineClass);
579    
580                                            engine.setMainConfiguration(
581                                                    _readConfigurationFile(
582                                                            PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN, format));
583    
584                                            engine.setInterWikiConfiguration(
585                                                    _readConfigurationFile(
586                                                            PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
587                                                            format));
588                                    }
589                                    else {
590                                            engine = (WikiEngine)InstancePool.get(engineClass);
591                                    }
592    
593                                    _engines.put(format, engine);
594    
595                                    return engine;
596                            }
597                            catch (Exception e) {
598                                    throw new WikiFormatException(e);
599                            }
600                    }
601            }
602    
603            private String _getHelpPage(String format) {
604                    return PropsUtil.get(
605                            PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
606            }
607    
608            private String _getHelpURL(String format) {
609                    return PropsUtil.get(
610                            PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
611            }
612    
613            private Map<String, Boolean> _getLinks(WikiPage page)
614                    throws PageContentException {
615    
616                    try {
617                            return _getEngine(page.getFormat()).getOutgoingLinks(page);
618                    }
619                    catch (WikiFormatException wfe) {
620                            return Collections.emptyMap();
621                    }
622            }
623    
624            private String _readConfigurationFile(String propertyName, String format)
625                    throws IOException {
626    
627                    ClassLoader classLoader = getClass().getClassLoader();
628    
629                    String configurationFile = PropsUtil.get(
630                            propertyName, new Filter(format));
631    
632                    if (Validator.isNotNull(configurationFile)) {
633                            return HttpUtil.URLtoString(
634                                    classLoader.getResource(configurationFile));
635                    }
636                    else {
637                            return StringPool.BLANK;
638                    }
639            }
640    
641            private String _replaceAttachments(
642                    String content, String title, String attachmentURLPrefix) {
643    
644                    content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
645    
646                    content = StringUtil.replace(
647                            content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
648    
649                    return content;
650            }
651    
652            private boolean _validate(long nodeId, String content, String format)
653                    throws WikiFormatException {
654    
655                    return _getEngine(format).validate(nodeId, content);
656            }
657    
658            private static WikiUtil _instance = new WikiUtil();
659    
660            private static Pattern _editPageURLPattern = Pattern.compile(
661                    "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)" +
662                            "\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
663            private static Pattern _viewPageURLPattern = Pattern.compile(
664                    "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
665    
666            private Map<String, WikiEngine> _engines =
667                    new ConcurrentHashMap<String, WikiEngine>();
668    
669    }