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.PortalException;
19  import com.liferay.portal.kernel.exception.SystemException;
20  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
21  import com.liferay.portal.kernel.portlet.LiferayPortletURL;
22  import com.liferay.portal.kernel.util.ArrayUtil;
23  import com.liferay.portal.kernel.util.DiffHtmlUtil;
24  import com.liferay.portal.kernel.util.GetterUtil;
25  import com.liferay.portal.kernel.util.HttpUtil;
26  import com.liferay.portal.kernel.util.InstancePool;
27  import com.liferay.portal.kernel.util.ListUtil;
28  import com.liferay.portal.kernel.util.PropsKeys;
29  import com.liferay.portal.kernel.util.StringBundler;
30  import com.liferay.portal.kernel.util.StringPool;
31  import com.liferay.portal.kernel.util.StringUtil;
32  import com.liferay.portal.kernel.util.Validator;
33  import com.liferay.portal.security.permission.ActionKeys;
34  import com.liferay.portal.security.permission.PermissionChecker;
35  import com.liferay.portal.theme.ThemeDisplay;
36  import com.liferay.portal.util.ContentUtil;
37  import com.liferay.portal.util.PropsUtil;
38  import com.liferay.portal.util.PropsValues;
39  import com.liferay.portal.util.WebKeys;
40  import com.liferay.portlet.wiki.PageContentException;
41  import com.liferay.portlet.wiki.WikiFormatException;
42  import com.liferay.portlet.wiki.engines.WikiEngine;
43  import com.liferay.portlet.wiki.model.WikiNode;
44  import com.liferay.portlet.wiki.model.WikiPage;
45  import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
46  import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
47  
48  import java.io.IOException;
49  
50  import java.util.ArrayList;
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.PortletURL;
61  import javax.portlet.RenderRequest;
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 diffHtml (
83              WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
84              PortletURL editPageURL, String attachmentURLPrefix)
85          throws Exception {
86  
87          String sourceContent = StringPool.BLANK;
88          String targetContent = StringPool.BLANK;
89  
90          if (sourcePage != null) {
91              sourceContent = WikiUtil.convert(
92                  sourcePage, viewPageURL, editPageURL,attachmentURLPrefix);
93          }
94  
95          if (targetPage != null) {
96              targetContent = WikiUtil.convert(
97                  targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
98          }
99  
100         return DiffHtmlUtil.diff(
101             new UnsyncStringReader(sourceContent),
102             new UnsyncStringReader(targetContent));
103     }
104 
105     public static String getEditPage(String format) {
106         return _instance._getEditPage(format);
107     }
108 
109     public static String getEmailFromAddress(PortletPreferences preferences) {
110         String emailFromAddress = PropsUtil.get(
111             PropsKeys.WIKI_EMAIL_FROM_ADDRESS);
112 
113         return preferences.getValue("email-from-address", emailFromAddress);
114     }
115 
116     public static String getEmailFromName(PortletPreferences preferences) {
117         String emailFromName = PropsUtil.get(PropsKeys.WIKI_EMAIL_FROM_NAME);
118 
119         return preferences.getValue("email-from-name", emailFromName);
120     }
121 
122     public static boolean getEmailPageAddedEnabled(
123         PortletPreferences preferences) {
124 
125         String emailPageAddedEnabled = preferences.getValue(
126             "email-page-added-enabled", StringPool.BLANK);
127 
128         if (Validator.isNotNull(emailPageAddedEnabled)) {
129             return GetterUtil.getBoolean(emailPageAddedEnabled);
130         }
131         else {
132             return GetterUtil.getBoolean(PropsUtil.get(
133                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_ENABLED));
134         }
135     }
136 
137     public static String getEmailPageAddedBody(PortletPreferences preferences) {
138         String emailPageAddedBody = preferences.getValue(
139             "email-page-added-body", StringPool.BLANK);
140 
141         if (Validator.isNotNull(emailPageAddedBody)) {
142             return emailPageAddedBody;
143         }
144         else {
145             return ContentUtil.get(PropsUtil.get(
146                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_BODY));
147         }
148     }
149 
150     public static String getEmailPageAddedSignature(
151         PortletPreferences preferences) {
152 
153         String emailPageAddedSignature = preferences.getValue(
154             "email-page-added-signature", StringPool.BLANK);
155 
156         if (Validator.isNotNull(emailPageAddedSignature)) {
157             return emailPageAddedSignature;
158         }
159         else {
160             return ContentUtil.get(PropsUtil.get(
161                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SIGNATURE));
162         }
163     }
164 
165     public static String getEmailPageAddedSubjectPrefix(
166         PortletPreferences preferences) {
167 
168         String emailPageAddedSubjectPrefix = preferences.getValue(
169             "email-page-added-subject-prefix", StringPool.BLANK);
170 
171         if (Validator.isNotNull(emailPageAddedSubjectPrefix)) {
172             return emailPageAddedSubjectPrefix;
173         }
174         else {
175             return ContentUtil.get(PropsUtil.get(
176                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SUBJECT_PREFIX));
177         }
178     }
179 
180     public static boolean getEmailPageUpdatedEnabled(
181         PortletPreferences preferences) {
182 
183         String emailPageUpdatedEnabled = preferences.getValue(
184             "email-page-updated-enabled", StringPool.BLANK);
185 
186         if (Validator.isNotNull(emailPageUpdatedEnabled)) {
187             return GetterUtil.getBoolean(emailPageUpdatedEnabled);
188         }
189         else {
190             return GetterUtil.getBoolean(PropsUtil.get(
191                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_ENABLED));
192         }
193     }
194 
195     public static String getEmailPageUpdatedBody(
196         PortletPreferences preferences) {
197 
198         String emailPageUpdatedBody = preferences.getValue(
199             "email-page-updated-body", StringPool.BLANK);
200 
201         if (Validator.isNotNull(emailPageUpdatedBody)) {
202             return emailPageUpdatedBody;
203         }
204         else {
205             return ContentUtil.get(PropsUtil.get(
206                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_BODY));
207         }
208     }
209 
210     public static String getEmailPageUpdatedSignature(
211         PortletPreferences preferences) {
212 
213         String emailPageUpdatedSignature = preferences.getValue(
214             "email-page-updated-signature", StringPool.BLANK);
215 
216         if (Validator.isNotNull(emailPageUpdatedSignature)) {
217             return emailPageUpdatedSignature;
218         }
219         else {
220             return ContentUtil.get(PropsUtil.get(
221                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SIGNATURE));
222         }
223     }
224 
225     public static String getEmailPageUpdatedSubjectPrefix(
226         PortletPreferences preferences) {
227 
228         String emailPageUpdatedSubject = preferences.getValue(
229             "email-page-updated-subject-prefix", StringPool.BLANK);
230 
231         if (Validator.isNotNull(emailPageUpdatedSubject)) {
232             return emailPageUpdatedSubject;
233         }
234         else {
235             return ContentUtil.get(PropsUtil.get(
236                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SUBJECT_PREFIX));
237         }
238     }
239 
240     public static String getHelpPage(String format) {
241         return _instance._getHelpPage(format);
242     }
243 
244     public static String getHelpURL(String format) {
245         return _instance._getHelpURL(format);
246     }
247 
248     public static Map<String, Boolean> getLinks(WikiPage page)
249         throws PageContentException {
250 
251         return _instance._getLinks(page);
252     }
253 
254     public static String getMailId(String mx, long nodeId, long pageId) {
255         StringBundler sb = new StringBundler(10);
256 
257         sb.append(StringPool.LESS_THAN);
258         sb.append(POP_PORTLET_PREFIX);
259         sb.append(nodeId);
260         sb.append(StringPool.PERIOD);
261         sb.append(pageId);
262         sb.append(StringPool.AT);
263         sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
264         sb.append(StringPool.PERIOD);
265         sb.append(mx);
266         sb.append(StringPool.GREATER_THAN);
267 
268         return sb.toString();
269     }
270 
271     public static List<WikiNode> getNodes(RenderRequest renderRequest)
272         throws PortalException, SystemException {
273 
274         ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
275             WebKeys.THEME_DISPLAY);
276 
277         long groupId = themeDisplay.getScopeGroupId();
278 
279         String allNodes = ListUtil.toString(
280             WikiNodeLocalServiceUtil.getNodes(groupId), "name");
281 
282         String[] visibleNodes = StringUtil.split(
283             renderRequest.getPreferences().getValue("visible-nodes", allNodes));
284         String[] hiddenNodes = StringUtil.split(
285             renderRequest.getPreferences().getValue(
286                 "hidden-nodes", StringPool.BLANK));
287 
288         return getNodes(
289             groupId, visibleNodes, hiddenNodes,
290             themeDisplay.getPermissionChecker());
291     }
292 
293     public static List<WikiNode> getNodes(
294             long groupId, String[] visibleNodes, String[] hiddenNodes,
295             PermissionChecker permissionChecker)
296         throws PortalException, SystemException {
297 
298         List<WikiNode> nodes = WikiNodeLocalServiceUtil.getNodes(groupId);
299 
300         nodes = ListUtil.copy(nodes);
301         nodes = _orderNodes(nodes, visibleNodes);
302 
303         Iterator<WikiNode> itr = nodes.iterator();
304 
305         while (itr.hasNext()) {
306             WikiNode node = itr.next();
307 
308             if (ArrayUtil.contains(hiddenNodes, node.getName()) ||
309                 !WikiNodePermission.contains(
310                     permissionChecker, node.getNodeId(), ActionKeys.VIEW)) {
311 
312                 itr.remove();
313             }
314         }
315 
316         return nodes;
317     }
318 
319     public static String processContent(String content) {
320         content = content.replaceAll("</p>", "</p>\n");
321         content = content.replaceAll("</br>", "</br>\n");
322         content = content.replaceAll("</div>", "</div>\n");
323 
324         return content;
325     }
326 
327     public static boolean validate(
328             long nodeId, String content, String format)
329         throws WikiFormatException {
330 
331         return _instance._validate(nodeId, content, format);
332     }
333 
334     private static List<WikiNode> _orderNodes(
335         List<WikiNode> nodes, String[] nodeNames) {
336 
337         List<WikiNode> orderedNodes = new ArrayList<WikiNode>();
338 
339         for (String nodeName : nodeNames) {
340             for (WikiNode node : nodes) {
341                 if (node.getName().equals(nodeName)) {
342                     orderedNodes.add(node);
343 
344                     nodes.remove(node);
345 
346                     break;
347                 }
348             }
349         }
350 
351         orderedNodes.addAll(nodes);
352 
353         return orderedNodes;
354     }
355 
356     private String _convert(
357             WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
358             String attachmentURLPrefix)
359         throws PageContentException, WikiFormatException {
360 
361         LiferayPortletURL liferayViewPageURL = (LiferayPortletURL)viewPageURL;
362         LiferayPortletURL liferayEditPageURL = (LiferayPortletURL)editPageURL;
363 
364         WikiEngine engine = _getEngine(page.getFormat());
365 
366         String content = engine.convert(page, editPageURL);
367 
368         String editPageURLString = StringPool.BLANK;
369 
370         if (editPageURL != null) {
371             liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
372 
373             editPageURLString = editPageURL.toString();
374 
375             editPageURLString = StringUtil.replace(
376                 editPageURLString, "__REPLACEMENT__", "$1");
377         }
378 
379         Matcher matcher = _editPageURLPattern.matcher(content);
380 
381         content = matcher.replaceAll(editPageURLString);
382 
383         String viewPageURLString = StringPool.BLANK;
384 
385         if (viewPageURL != null) {
386             liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
387 
388             viewPageURLString = viewPageURL.toString();
389 
390             viewPageURLString = StringUtil.replace(
391                 viewPageURLString, "__REPLACEMENT__", "$1");
392         }
393 
394         matcher = _viewPageURLPattern.matcher(content);
395 
396         content = matcher.replaceAll(viewPageURLString);
397 
398         content = _replaceAttachments(
399             content, page.getTitle(), attachmentURLPrefix);
400 
401         return content;
402     }
403 
404     private String _getEditPage(String format) {
405         return PropsUtil.get(
406             PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
407     }
408 
409     private WikiEngine _getEngine(String format) throws WikiFormatException {
410         WikiEngine engine = _engines.get(format);
411 
412         if (engine == null) {
413             try {
414                 String engineClass = PropsUtil.get(
415                     PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
416 
417                 if (engineClass != null) {
418                     if (!InstancePool.contains(engineClass)) {
419                         engine = (WikiEngine)InstancePool.get(engineClass);
420 
421                         engine.setMainConfiguration(
422                             _readConfigurationFile(
423                                 PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN,
424                                 format));
425 
426                         engine.setInterWikiConfiguration(
427                             _readConfigurationFile(
428                                 PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
429                                 format));
430                     }
431                     else {
432                         engine = (WikiEngine)InstancePool.get(engineClass);
433                     }
434 
435                     _engines.put(format, engine);
436                 }
437             }
438             catch (Exception e) {
439                 throw new WikiFormatException(e);
440             }
441 
442             if (engine == null) {
443                 throw new WikiFormatException(format);
444             }
445         }
446 
447         return engine;
448     }
449 
450     private String _getHelpPage(String format) {
451         return PropsUtil.get(
452             PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
453     }
454 
455     private String _getHelpURL(String format) {
456         return PropsUtil.get(
457             PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
458     }
459 
460     private Map<String, Boolean> _getLinks(WikiPage page)
461         throws PageContentException {
462 
463         try {
464             return _getEngine(page.getFormat()).getOutgoingLinks(page);
465         }
466         catch (WikiFormatException wfe) {
467             return Collections.EMPTY_MAP;
468         }
469     }
470 
471     private String _readConfigurationFile(String propertyName, String format)
472         throws IOException {
473 
474         ClassLoader classLoader = getClass().getClassLoader();
475 
476         String configurationFile = PropsUtil.get(
477             propertyName, new Filter(format));
478 
479         if (Validator.isNotNull(configurationFile)) {
480             return HttpUtil.URLtoString(
481                 classLoader.getResource(configurationFile));
482         }
483         else {
484             return StringPool.BLANK;
485         }
486     }
487 
488     private String _replaceAttachments(
489         String content, String title, String attachmentURLPrefix) {
490 
491         content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
492 
493         content = StringUtil.replace(
494             content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
495 
496         return content;
497     }
498 
499     private boolean _validate(long nodeId, String content, String format)
500         throws WikiFormatException {
501 
502         return _getEngine(format).validate(nodeId, content);
503     }
504 
505     private static WikiUtil _instance = new WikiUtil();
506 
507     private static Pattern _editPageURLPattern = Pattern.compile(
508         "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
509     private static Pattern _viewPageURLPattern = Pattern.compile(
510         "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
511 
512     private Map<String, WikiEngine> _engines =
513         new HashMap<String, WikiEngine>();
514 
515 }