1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portlet.wiki.util;
16  
17  import com.liferay.portal.SystemException;
18  import com.liferay.portal.kernel.configuration.Filter;
19  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
20  import com.liferay.portal.kernel.portlet.LiferayPortletURL;
21  import com.liferay.portal.kernel.util.ArrayUtil;
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.Collections;
51  import java.util.Iterator;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.concurrent.ConcurrentHashMap;
55  import java.util.regex.Matcher;
56  import java.util.regex.Pattern;
57  
58  import javax.portlet.PortletPreferences;
59  import javax.portlet.PortletRequest;
60  import javax.portlet.PortletURL;
61  
62  /**
63   * <a href="WikiUtil.java.html"><b><i>View Source</i></b></a>
64   *
65   * @author Brian Wing Shun Chan
66   * @author Jorge Ferrer
67   */
68  public class WikiUtil {
69  
70      public static final String POP_PORTLET_PREFIX = "wiki.";
71  
72      public static String convert(
73              WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
74              String attachmentURLPrefix)
75          throws PageContentException, WikiFormatException {
76  
77          return _instance._convert(
78              page, viewPageURL, editPageURL, attachmentURLPrefix);
79      }
80  
81      public static String diffHtml (
82              WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
83              PortletURL editPageURL, String attachmentURLPrefix)
84          throws Exception {
85  
86          String sourceContent = StringPool.BLANK;
87          String targetContent = StringPool.BLANK;
88  
89          if (sourcePage != null) {
90              sourceContent = WikiUtil.convert(
91                  sourcePage, viewPageURL, editPageURL,attachmentURLPrefix);
92          }
93  
94          if (targetPage != null) {
95              targetContent = WikiUtil.convert(
96                  targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
97          }
98  
99          return DiffHtmlUtil.diff(
100             new UnsyncStringReader(sourceContent),
101             new UnsyncStringReader(targetContent));
102     }
103 
104     public static String getEditPage(String format) {
105         return _instance._getEditPage(format);
106     }
107 
108     public static String getEmailFromAddress(PortletPreferences preferences) {
109         String emailFromAddress = PropsUtil.get(
110             PropsKeys.WIKI_EMAIL_FROM_ADDRESS);
111 
112         return preferences.getValue("email-from-address", emailFromAddress);
113     }
114 
115     public static String getEmailFromName(PortletPreferences preferences) {
116         String emailFromName = PropsUtil.get(PropsKeys.WIKI_EMAIL_FROM_NAME);
117 
118         return preferences.getValue("email-from-name", emailFromName);
119     }
120 
121     public static String getEmailPageAddedBody(PortletPreferences preferences) {
122         String emailPageAddedBody = preferences.getValue(
123             "email-page-added-body", StringPool.BLANK);
124 
125         if (Validator.isNotNull(emailPageAddedBody)) {
126             return emailPageAddedBody;
127         }
128         else {
129             return ContentUtil.get(PropsUtil.get(
130                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_BODY));
131         }
132     }
133 
134     public static boolean getEmailPageAddedEnabled(
135         PortletPreferences preferences) {
136 
137         String emailPageAddedEnabled = preferences.getValue(
138             "email-page-added-enabled", StringPool.BLANK);
139 
140         if (Validator.isNotNull(emailPageAddedEnabled)) {
141             return GetterUtil.getBoolean(emailPageAddedEnabled);
142         }
143         else {
144             return GetterUtil.getBoolean(PropsUtil.get(
145                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_ENABLED));
146         }
147     }
148 
149     public static String getEmailPageAddedSignature(
150         PortletPreferences preferences) {
151 
152         String emailPageAddedSignature = preferences.getValue(
153             "email-page-added-signature", StringPool.BLANK);
154 
155         if (Validator.isNotNull(emailPageAddedSignature)) {
156             return emailPageAddedSignature;
157         }
158         else {
159             return ContentUtil.get(PropsUtil.get(
160                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SIGNATURE));
161         }
162     }
163 
164     public static String getEmailPageAddedSubjectPrefix(
165         PortletPreferences preferences) {
166 
167         String emailPageAddedSubjectPrefix = preferences.getValue(
168             "email-page-added-subject-prefix", StringPool.BLANK);
169 
170         if (Validator.isNotNull(emailPageAddedSubjectPrefix)) {
171             return emailPageAddedSubjectPrefix;
172         }
173         else {
174             return ContentUtil.get(PropsUtil.get(
175                 PropsKeys.WIKI_EMAIL_PAGE_ADDED_SUBJECT_PREFIX));
176         }
177     }
178 
179     public static String getEmailPageUpdatedBody(
180         PortletPreferences preferences) {
181 
182         String emailPageUpdatedBody = preferences.getValue(
183             "email-page-updated-body", StringPool.BLANK);
184 
185         if (Validator.isNotNull(emailPageUpdatedBody)) {
186             return emailPageUpdatedBody;
187         }
188         else {
189             return ContentUtil.get(PropsUtil.get(
190                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_BODY));
191         }
192     }
193 
194     public static boolean getEmailPageUpdatedEnabled(
195         PortletPreferences preferences) {
196 
197         String emailPageUpdatedEnabled = preferences.getValue(
198             "email-page-updated-enabled", StringPool.BLANK);
199 
200         if (Validator.isNotNull(emailPageUpdatedEnabled)) {
201             return GetterUtil.getBoolean(emailPageUpdatedEnabled);
202         }
203         else {
204             return GetterUtil.getBoolean(PropsUtil.get(
205                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_ENABLED));
206         }
207     }
208 
209     public static String getEmailPageUpdatedSignature(
210         PortletPreferences preferences) {
211 
212         String emailPageUpdatedSignature = preferences.getValue(
213             "email-page-updated-signature", StringPool.BLANK);
214 
215         if (Validator.isNotNull(emailPageUpdatedSignature)) {
216             return emailPageUpdatedSignature;
217         }
218         else {
219             return ContentUtil.get(PropsUtil.get(
220                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SIGNATURE));
221         }
222     }
223 
224     public static String getEmailPageUpdatedSubjectPrefix(
225         PortletPreferences preferences) {
226 
227         String emailPageUpdatedSubject = preferences.getValue(
228             "email-page-updated-subject-prefix", StringPool.BLANK);
229 
230         if (Validator.isNotNull(emailPageUpdatedSubject)) {
231             return emailPageUpdatedSubject;
232         }
233         else {
234             return ContentUtil.get(PropsUtil.get(
235                 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SUBJECT_PREFIX));
236         }
237     }
238 
239     public static String getHelpPage(String format) {
240         return _instance._getHelpPage(format);
241     }
242 
243     public static String getHelpURL(String format) {
244         return _instance._getHelpURL(format);
245     }
246 
247     public static Map<String, Boolean> getLinks(WikiPage page)
248         throws PageContentException {
249 
250         return _instance._getLinks(page);
251     }
252 
253     public static String getMailId(String mx, long nodeId, long pageId) {
254         StringBundler sb = new StringBundler(10);
255 
256         sb.append(StringPool.LESS_THAN);
257         sb.append(POP_PORTLET_PREFIX);
258         sb.append(nodeId);
259         sb.append(StringPool.PERIOD);
260         sb.append(pageId);
261         sb.append(StringPool.AT);
262         sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
263         sb.append(StringPool.PERIOD);
264         sb.append(mx);
265         sb.append(StringPool.GREATER_THAN);
266 
267         return sb.toString();
268     }
269 
270     public static List<WikiNode> getNodes(PortletRequest portletRequest)
271         throws SystemException {
272 
273         ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
274             WebKeys.THEME_DISPLAY);
275 
276         long groupId = themeDisplay.getScopeGroupId();
277 
278         String allNodes = ListUtil.toString(
279             WikiNodeLocalServiceUtil.getNodes(groupId), "name");
280 
281         PortletPreferences preferences = portletRequest.getPreferences();
282 
283         String[] visibleNodes = StringUtil.split(
284             preferences.getValue("visible-nodes", allNodes));
285         String[] hiddenNodes = StringUtil.split(
286             preferences.getValue("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 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, 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(
367             page, viewPageURL, editPageURL, attachmentURLPrefix);
368 
369         String editPageURLString = StringPool.BLANK;
370 
371         if (editPageURL != null) {
372             liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
373 
374             editPageURLString = editPageURL.toString();
375 
376             editPageURLString = StringUtil.replace(
377                 editPageURLString, "__REPLACEMENT__", "$1");
378         }
379 
380         Matcher matcher = _editPageURLPattern.matcher(content);
381 
382         content = _convertURLs(editPageURLString, matcher);
383 
384         String viewPageURLString = StringPool.BLANK;
385 
386         if (viewPageURL != null) {
387             liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
388 
389             viewPageURLString = viewPageURL.toString();
390 
391             viewPageURLString = StringUtil.replace(
392                 viewPageURLString, "__REPLACEMENT__", "$1");
393         }
394 
395         matcher = _viewPageURLPattern.matcher(content);
396 
397         content = _convertURLs(viewPageURLString, matcher);
398 
399         content = _replaceAttachments(
400             content, page.getTitle(), attachmentURLPrefix);
401 
402         return content;
403     }
404 
405     private String _convertURLs(String url, Matcher matcher) {
406         StringBuffer sb = new StringBuffer();
407 
408         while (matcher.find()) {
409             String replacement = null;
410 
411             if (matcher.groupCount() >= 1) {
412                 String encodedTitle = HttpUtil.encodeURL(matcher.group(1));
413 
414                 replacement = url.replace("$1", encodedTitle);
415             }
416             else {
417                 replacement = url;
418             }
419 
420             matcher.appendReplacement(sb, replacement);
421         }
422 
423         return matcher.appendTail(sb).toString();
424     }
425 
426     private String _getEditPage(String format) {
427         return PropsUtil.get(
428             PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
429     }
430 
431     private WikiEngine _getEngine(String format) throws WikiFormatException {
432         WikiEngine engine = _engines.get(format);
433 
434         if (engine != null) {
435             return engine;
436         }
437 
438         synchronized (_engines) {
439             engine = _engines.get(format);
440 
441             if (engine != null) {
442                 return engine;
443             }
444 
445             try {
446                 String engineClass = PropsUtil.get(
447                     PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
448 
449                 if (engineClass == null) {
450                     throw new WikiFormatException(format);
451                 }
452 
453                 if (!InstancePool.contains(engineClass)) {
454                     engine = (WikiEngine)InstancePool.get(engineClass);
455 
456                     engine.setMainConfiguration(
457                         _readConfigurationFile(
458                             PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN, format));
459 
460                     engine.setInterWikiConfiguration(
461                         _readConfigurationFile(
462                             PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
463                             format));
464                 }
465                 else {
466                     engine = (WikiEngine)InstancePool.get(engineClass);
467                 }
468 
469                 _engines.put(format, engine);
470 
471                 return engine;
472             }
473             catch (Exception e) {
474                 throw new WikiFormatException(e);
475             }
476         }
477     }
478 
479     private String _getHelpPage(String format) {
480         return PropsUtil.get(
481             PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
482     }
483 
484     private String _getHelpURL(String format) {
485         return PropsUtil.get(
486             PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
487     }
488 
489     private Map<String, Boolean> _getLinks(WikiPage page)
490         throws PageContentException {
491 
492         try {
493             return _getEngine(page.getFormat()).getOutgoingLinks(page);
494         }
495         catch (WikiFormatException wfe) {
496             return Collections.EMPTY_MAP;
497         }
498     }
499 
500     private String _readConfigurationFile(String propertyName, String format)
501         throws IOException {
502 
503         ClassLoader classLoader = getClass().getClassLoader();
504 
505         String configurationFile = PropsUtil.get(
506             propertyName, new Filter(format));
507 
508         if (Validator.isNotNull(configurationFile)) {
509             return HttpUtil.URLtoString(
510                 classLoader.getResource(configurationFile));
511         }
512         else {
513             return StringPool.BLANK;
514         }
515     }
516 
517     private String _replaceAttachments(
518         String content, String title, String attachmentURLPrefix) {
519 
520         content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
521 
522         content = StringUtil.replace(
523             content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
524 
525         return content;
526     }
527 
528     private boolean _validate(long nodeId, String content, String format)
529         throws WikiFormatException {
530 
531         return _getEngine(format).validate(nodeId, content);
532     }
533 
534     private static WikiUtil _instance = new WikiUtil();
535 
536     private static Pattern _editPageURLPattern = Pattern.compile(
537         "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)" +
538             "\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
539     private static Pattern _viewPageURLPattern = Pattern.compile(
540         "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
541 
542     private Map<String, WikiEngine> _engines =
543         new ConcurrentHashMap<String, WikiEngine>();
544 
545 }