1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.wiki.util;
16  
17  import com.liferay.portal.kernel.configuration.Filter;
18  import com.liferay.portal.kernel.exception.SystemException;
19  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
20  import com.liferay.portal.kernel.portlet.LiferayPortletURL;
21  import com.liferay.portal.kernel.util.CharPool;
22  import com.liferay.portal.kernel.util.DiffHtmlUtil;
23  import com.liferay.portal.kernel.util.GetterUtil;
24  import com.liferay.portal.kernel.util.HttpUtil;
25  import com.liferay.portal.kernel.util.InstancePool;
26  import com.liferay.portal.kernel.util.ListUtil;
27  import com.liferay.portal.kernel.util.PropsKeys;
28  import com.liferay.portal.kernel.util.StringBundler;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.kernel.util.Validator;
32  import com.liferay.portal.security.permission.ActionKeys;
33  import com.liferay.portal.security.permission.PermissionChecker;
34  import com.liferay.portal.theme.ThemeDisplay;
35  import com.liferay.portal.util.ContentUtil;
36  import com.liferay.portal.util.PropsUtil;
37  import com.liferay.portal.util.PropsValues;
38  import com.liferay.portal.util.WebKeys;
39  import com.liferay.portlet.wiki.PageContentException;
40  import com.liferay.portlet.wiki.WikiFormatException;
41  import com.liferay.portlet.wiki.engines.WikiEngine;
42  import com.liferay.portlet.wiki.model.WikiNode;
43  import com.liferay.portlet.wiki.model.WikiPage;
44  import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
45  import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
46  
47  import java.io.IOException;
48  
49  import java.util.ArrayList;
50  import java.util.Arrays;
51  import java.util.Collections;
52  import java.util.HashMap;
53  import java.util.Iterator;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.regex.Matcher;
57  import java.util.regex.Pattern;
58  
59  import javax.portlet.PortletPreferences;
60  import javax.portlet.PortletRequest;
61  import javax.portlet.PortletURL;
62  
63  /**
64   * <a href="WikiUtil.java.html"><b><i>View Source</i></b></a>
65   *
66   * @author Brian Wing Shun Chan
67   * @author Jorge Ferrer
68   */
69  public class WikiUtil {
70  
71      public static final String POP_PORTLET_PREFIX = "wiki.";
72  
73      public static String convert(
74              WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
75              String attachmentURLPrefix)
76          throws PageContentException, WikiFormatException {
77  
78          return _instance._convert(
79              page, viewPageURL, editPageURL, attachmentURLPrefix);
80      }
81  
82      public static String decodeJSPWikiName(String jspWikiName) {
83          return StringUtil.replace(
84              jspWikiName, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
85      }
86  
87      public static String diffHtml (
88              WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
89              PortletURL editPageURL, String attachmentURLPrefix)
90          throws Exception {
91  
92          String sourceContent = StringPool.BLANK;
93          String targetContent = StringPool.BLANK;
94  
95          if (sourcePage != null) {
96              sourceContent = WikiUtil.convert(
97                  sourcePage, viewPageURL, editPageURL,attachmentURLPrefix);
98          }
99  
100         if (targetPage != null) {
101             targetContent = WikiUtil.convert(
102                 targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
103         }
104 
105         return DiffHtmlUtil.diff(
106             new UnsyncStringReader(sourceContent),
107             new UnsyncStringReader(targetContent));
108     }
109 
110     public static String encodeJSPWikiContent(String content) {
111         String newContent = content;
112 
113         Matcher matcher = _wikiLinkPattern.matcher(content);
114 
115         while (matcher.find()) {
116             String link = matcher.group();
117             String linkValues = matcher.group(1);
118 
119             int index = linkValues.indexOf(CharPool.PIPE);
120 
121             String name = linkValues;
122 
123             if (index != -1) {
124                 name = linkValues.substring(0, index);
125             }
126 
127             String newLink =
128                 "[[" + encodeJSPWikiName(name) + "|" + name + "]]";
129 
130             newContent = StringUtil.replace(newContent, link, newLink);
131         }
132 
133         return newContent;
134     }
135 
136     public static String encodeJSPWikiName(String name) {
137         if (name == null) {
138             return StringPool.BLANK;
139         }
140 
141         return StringUtil.replace(name, _JSP_WIKI_NAME_1, _JSP_WIKI_NAME_2);
142     }
143 
144     public static String getEditPage(String format) {
145         return _instance._getEditPage(format);
146     }
147 
148     public static String getEmailFromAddress(PortletPreferences preferences) {
149         String emailFromAddress = PropsUtil.get(
150             PropsKeys.WIKI_EMAIL_FROM_ADDRESS);
151 
152         return preferences.getValue("email-from-address", emailFromAddress);
153     }
154 
155     public static String getEmailFromName(PortletPreferences preferences) {
156         String emailFromName = PropsUtil.get(PropsKeys.WIKI_EMAIL_FROM_NAME);
157 
158         return preferences.getValue("email-from-name", emailFromName);
159     }
160 
161     public static boolean getEmailPageAddedEnabled(
162         PortletPreferences preferences) {
163 
164         String emailPageAddedEnabled = preferences.getValue(
165             "email-page-added-enabled", StringPool.BLANK);
166 
167         if (Validator.isNotNull(emailPageAddedEnabled)) {
168             return GetterUtil.getBoolean(emailPageAddedEnabled);
169         }
170         else {
171             return GetterUtil.getBoolean(PropsUtil.get(
172                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_ENABLED));
173         }
174     }
175 
176     public static String getEmailPageAddedBody(PortletPreferences preferences) {
177         String emailPageAddedBody = preferences.getValue(
178             "email-page-added-body", StringPool.BLANK);
179 
180         if (Validator.isNotNull(emailPageAddedBody)) {
181             return emailPageAddedBody;
182         }
183         else {
184             return ContentUtil.get(PropsUtil.get(
185                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_BODY));
186         }
187     }
188 
189     public static String getEmailPageAddedSignature(
190         PortletPreferences preferences) {
191 
192         String emailPageAddedSignature = preferences.getValue(
193             "email-page-added-signature", StringPool.BLANK);
194 
195         if (Validator.isNotNull(emailPageAddedSignature)) {
196             return emailPageAddedSignature;
197         }
198         else {
199             return ContentUtil.get(PropsUtil.get(
200                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SIGNATURE));
201         }
202     }
203 
204     public static String getEmailPageAddedSubjectPrefix(
205         PortletPreferences preferences) {
206 
207         String emailPageAddedSubjectPrefix = preferences.getValue(
208             "email-page-added-subject-prefix", StringPool.BLANK);
209 
210         if (Validator.isNotNull(emailPageAddedSubjectPrefix)) {
211             return emailPageAddedSubjectPrefix;
212         }
213         else {
214             return ContentUtil.get(PropsUtil.get(
215                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SUBJECT_PREFIX));
216         }
217     }
218 
219     public static boolean getEmailPageUpdatedEnabled(
220         PortletPreferences preferences) {
221 
222         String emailPageUpdatedEnabled = preferences.getValue(
223             "email-page-updated-enabled", StringPool.BLANK);
224 
225         if (Validator.isNotNull(emailPageUpdatedEnabled)) {
226             return GetterUtil.getBoolean(emailPageUpdatedEnabled);
227         }
228         else {
229             return GetterUtil.getBoolean(PropsUtil.get(
230                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_ENABLED));
231         }
232     }
233 
234     public static String getEmailPageUpdatedBody(
235         PortletPreferences preferences) {
236 
237         String emailPageUpdatedBody = preferences.getValue(
238             "email-page-updated-body", StringPool.BLANK);
239 
240         if (Validator.isNotNull(emailPageUpdatedBody)) {
241             return emailPageUpdatedBody;
242         }
243         else {
244             return ContentUtil.get(PropsUtil.get(
245                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_BODY));
246         }
247     }
248 
249     public static String getEmailPageUpdatedSignature(
250         PortletPreferences preferences) {
251 
252         String emailPageUpdatedSignature = preferences.getValue(
253             "email-page-updated-signature", StringPool.BLANK);
254 
255         if (Validator.isNotNull(emailPageUpdatedSignature)) {
256             return emailPageUpdatedSignature;
257         }
258         else {
259             return ContentUtil.get(PropsUtil.get(
260                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SIGNATURE));
261         }
262     }
263 
264     public static String getEmailPageUpdatedSubjectPrefix(
265         PortletPreferences preferences) {
266 
267         String emailPageUpdatedSubject = preferences.getValue(
268             "email-page-updated-subject-prefix", StringPool.BLANK);
269 
270         if (Validator.isNotNull(emailPageUpdatedSubject)) {
271             return emailPageUpdatedSubject;
272         }
273         else {
274             return ContentUtil.get(PropsUtil.get(
275                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SUBJECT_PREFIX));
276         }
277     }
278 
279     public static WikiNode getFirstNode(PortletRequest portletRequest)
280         throws SystemException {
281 
282         ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
283             WebKeys.THEME_DISPLAY);
284         long groupId = themeDisplay.getScopeGroupId();
285         PermissionChecker permissionChecker =
286             themeDisplay.getPermissionChecker();
287 
288         List<WikiNode> nodes = WikiNodeLocalServiceUtil.getNodes(groupId);
289 
290         PortletPreferences preferences = portletRequest.getPreferences();
291         String[] visibleNodeNames =
292             StringUtil.split(preferences.getValue("visible-nodes", null));
293         nodes = orderNodes(nodes, visibleNodeNames);
294 
295         String[] hiddenNodes = StringUtil.split(
296             preferences.getValue("hidden-nodes", StringPool.BLANK));
297         Arrays.sort(hiddenNodes);
298 
299         for(WikiNode node : nodes) {
300             if ((Arrays.binarySearch(hiddenNodes, node.getName()) < 0) &&
301                 (WikiNodePermission.contains(permissionChecker, node,
302                     ActionKeys.VIEW))) {
303                 return node;
304             }
305         }
306         return null;
307     }
308 
309     public static String getHelpPage(String format) {
310         return _instance._getHelpPage(format);
311     }
312 
313     public static String getHelpURL(String format) {
314         return _instance._getHelpURL(format);
315     }
316 
317     public static Map<String, Boolean> getLinks(WikiPage page)
318         throws PageContentException {
319 
320         return _instance._getLinks(page);
321     }
322 
323     public static String getMailId(String mx, long nodeId, long pageId) {
324         StringBundler sb = new StringBundler(10);
325 
326         sb.append(StringPool.LESS_THAN);
327         sb.append(POP_PORTLET_PREFIX);
328         sb.append(nodeId);
329         sb.append(StringPool.PERIOD);
330         sb.append(pageId);
331         sb.append(StringPool.AT);
332         sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
333         sb.append(StringPool.PERIOD);
334         sb.append(mx);
335         sb.append(StringPool.GREATER_THAN);
336 
337         return sb.toString();
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 List<WikiNode> orderNodes(
375         List<WikiNode> nodes, String[] visibleNodeNames) {
376 
377         if ((visibleNodeNames == null) || (visibleNodeNames.length == 0)) {
378             return nodes;
379         }
380 
381         nodes = ListUtil.copy(nodes);
382 
383         List<WikiNode> orderedNodes = new ArrayList<WikiNode>(nodes.size());
384 
385         for (String visibleNodeName : visibleNodeNames) {
386             for (WikiNode node : nodes) {
387                 if (node.getName().equals(visibleNodeName)) {
388                     orderedNodes.add(node);
389 
390                     nodes.remove(node);
391 
392                     break;
393                 }
394             }
395         }
396 
397         orderedNodes.addAll(nodes);
398 
399         return orderedNodes;
400     }
401 
402     public static String processContent(String content) {
403         content = content.replaceAll("</p>", "</p>\n");
404         content = content.replaceAll("</br>", "</br>\n");
405         content = content.replaceAll("</div>", "</div>\n");
406 
407         return content;
408     }
409 
410     public static boolean validate(
411             long nodeId, String content, String format)
412         throws WikiFormatException {
413 
414         return _instance._validate(nodeId, content, format);
415     }
416 
417     private static String _decodeJSPWikiContent(String jspWikiContent) {
418         return StringUtil.replace(
419             jspWikiContent, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
420     }
421 
422     private String _convert(
423             WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
424             String attachmentURLPrefix)
425         throws PageContentException, WikiFormatException {
426 
427         LiferayPortletURL liferayViewPageURL = (LiferayPortletURL)viewPageURL;
428         LiferayPortletURL liferayEditPageURL = (LiferayPortletURL)editPageURL;
429 
430         WikiEngine engine = _getEngine(page.getFormat());
431 
432         String content = engine.convert(page, editPageURL);
433 
434         String editPageURLString = StringPool.BLANK;
435 
436         if (editPageURL != null) {
437             liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
438 
439             editPageURLString = editPageURL.toString();
440 
441             editPageURLString = StringUtil.replace(
442                 editPageURLString, "__REPLACEMENT__", "$1");
443         }
444 
445         Matcher matcher = _editPageURLPattern.matcher(content);
446 
447         content = matcher.replaceAll(editPageURLString);
448 
449         String viewPageURLString = StringPool.BLANK;
450 
451         if (viewPageURL != null) {
452             liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
453 
454             viewPageURLString = viewPageURL.toString();
455 
456             viewPageURLString = StringUtil.replace(
457                 viewPageURLString, "__REPLACEMENT__", "$1");
458         }
459 
460         matcher = _viewPageURLPattern.matcher(content);
461 
462         content = matcher.replaceAll(viewPageURLString);
463 
464         content = _replaceAttachments(
465             content, page.getTitle(), attachmentURLPrefix);
466 
467         content = _decodeJSPWikiContent(content);
468 
469         return content;
470     }
471 
472     private String _getEditPage(String format) {
473         return PropsUtil.get(
474             PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
475     }
476 
477     private WikiEngine _getEngine(String format) throws WikiFormatException {
478         WikiEngine engine = _engines.get(format);
479 
480         if (engine == null) {
481             try {
482                 String engineClass = PropsUtil.get(
483                     PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
484 
485                 if (engineClass != null) {
486                     if (!InstancePool.contains(engineClass)) {
487                         engine = (WikiEngine)InstancePool.get(engineClass);
488 
489                         engine.setMainConfiguration(
490                             _readConfigurationFile(
491                                 PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN,
492                                 format));
493 
494                         engine.setInterWikiConfiguration(
495                             _readConfigurationFile(
496                                 PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
497                                 format));
498                     }
499                     else {
500                         engine = (WikiEngine)InstancePool.get(engineClass);
501                     }
502 
503                     _engines.put(format, engine);
504                 }
505             }
506             catch (Exception e) {
507                 throw new WikiFormatException(e);
508             }
509 
510             if (engine == null) {
511                 throw new WikiFormatException(format);
512             }
513         }
514 
515         return engine;
516     }
517 
518     private String _getHelpPage(String format) {
519         return PropsUtil.get(
520             PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
521     }
522 
523     private String _getHelpURL(String format) {
524         return PropsUtil.get(
525             PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
526     }
527 
528     private Map<String, Boolean> _getLinks(WikiPage page)
529         throws PageContentException {
530 
531         try {
532             return _getEngine(page.getFormat()).getOutgoingLinks(page);
533         }
534         catch (WikiFormatException wfe) {
535             return Collections.EMPTY_MAP;
536         }
537     }
538 
539     private String _readConfigurationFile(String propertyName, String format)
540         throws IOException {
541 
542         ClassLoader classLoader = getClass().getClassLoader();
543 
544         String configurationFile = PropsUtil.get(
545             propertyName, new Filter(format));
546 
547         if (Validator.isNotNull(configurationFile)) {
548             return HttpUtil.URLtoString(
549                 classLoader.getResource(configurationFile));
550         }
551         else {
552             return StringPool.BLANK;
553         }
554     }
555 
556     private String _replaceAttachments(
557         String content, String title, String attachmentURLPrefix) {
558 
559         content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
560 
561         content = StringUtil.replace(
562             content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
563 
564         return content;
565     }
566 
567     private boolean _validate(long nodeId, String content, String format)
568         throws WikiFormatException {
569 
570         return _getEngine(format).validate(nodeId, content);
571     }
572 
573     private static final String[] _JSP_WIKI_NAME_1 = {
574         StringPool.APOSTROPHE, StringPool.AT, StringPool.CARET,
575         StringPool.EXCLAMATION, StringPool.INVERTED_EXCLAMATION,
576         StringPool.INVERTED_QUESTION, StringPool.GRAVE_ACCENT,
577         StringPool.QUESTION, StringPool.SLASH, StringPool.STAR
578     };
579 
580     private static final String[] _JSP_WIKI_NAME_2 = {
581         "__APO__", "__AT__", "__CAR__", "__EXM__", "__INE__", "__INQ__",
582         "__GRA__", "__QUE__", "__SLA__", "__STA__"
583     };
584 
585     private static WikiUtil _instance = new WikiUtil();
586 
587     private static Pattern _editPageURLPattern = Pattern.compile(
588         "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
589     private static Pattern _viewPageURLPattern = Pattern.compile(
590         "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
591     private static Pattern _wikiLinkPattern = Pattern.compile(
592         "[\\[]{2,2}(.+?)[\\]]{2,2}");
593 
594     private Map<String, WikiEngine> _engines =
595         new HashMap<String, WikiEngine>();
596 
597 }