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