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