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