001
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.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.portlet.LiferayPortletURL;
024 import com.liferay.portal.kernel.search.Document;
025 import com.liferay.portal.kernel.search.Field;
026 import com.liferay.portal.kernel.search.Hits;
027 import com.liferay.portal.kernel.util.DiffHtmlUtil;
028 import com.liferay.portal.kernel.util.GetterUtil;
029 import com.liferay.portal.kernel.util.HtmlUtil;
030 import com.liferay.portal.kernel.util.HttpUtil;
031 import com.liferay.portal.kernel.util.InstancePool;
032 import com.liferay.portal.kernel.util.ListUtil;
033 import com.liferay.portal.kernel.util.OrderByComparator;
034 import com.liferay.portal.kernel.util.ParamUtil;
035 import com.liferay.portal.kernel.util.PropsKeys;
036 import com.liferay.portal.kernel.util.StringBundler;
037 import com.liferay.portal.kernel.util.StringPool;
038 import com.liferay.portal.kernel.util.StringUtil;
039 import com.liferay.portal.kernel.util.Validator;
040 import com.liferay.portal.security.permission.ActionKeys;
041 import com.liferay.portal.security.permission.PermissionChecker;
042 import com.liferay.portal.theme.ThemeDisplay;
043 import com.liferay.portal.util.PortalUtil;
044 import com.liferay.portal.util.PropsUtil;
045 import com.liferay.portal.util.PropsValues;
046 import com.liferay.portal.util.WebKeys;
047 import com.liferay.portlet.PortletURLUtil;
048 import com.liferay.portlet.documentlibrary.model.DLFileEntry;
049 import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
050 import com.liferay.portlet.messageboards.model.MBMessage;
051 import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
052 import com.liferay.portlet.wiki.PageContentException;
053 import com.liferay.portlet.wiki.WikiFormatException;
054 import com.liferay.portlet.wiki.engines.WikiEngine;
055 import com.liferay.portlet.wiki.model.WikiNode;
056 import com.liferay.portlet.wiki.model.WikiPage;
057 import com.liferay.portlet.wiki.model.WikiPageDisplay;
058 import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
059 import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
060 import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
061 import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
062 import com.liferay.portlet.wiki.util.comparator.PageTitleComparator;
063 import com.liferay.portlet.wiki.util.comparator.PageVersionComparator;
064 import com.liferay.util.ContentUtil;
065
066 import java.io.IOException;
067
068 import java.util.ArrayList;
069 import java.util.Arrays;
070 import java.util.Collections;
071 import java.util.HashSet;
072 import java.util.Iterator;
073 import java.util.List;
074 import java.util.Map;
075 import java.util.Set;
076 import java.util.concurrent.ConcurrentHashMap;
077 import java.util.regex.Matcher;
078 import java.util.regex.Pattern;
079
080 import javax.portlet.PortletPreferences;
081 import javax.portlet.PortletRequest;
082 import javax.portlet.PortletURL;
083 import javax.portlet.RenderRequest;
084 import javax.portlet.RenderResponse;
085
086
090 public class WikiUtil {
091
092 public static String convert(
093 WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
094 String attachmentURLPrefix)
095 throws PageContentException, WikiFormatException {
096
097 return _instance._convert(
098 page, viewPageURL, editPageURL, attachmentURLPrefix);
099 }
100
101 public static String diffHtml(
102 WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
103 PortletURL editPageURL, String attachmentURLPrefix)
104 throws Exception {
105
106 String sourceContent = StringPool.BLANK;
107 String targetContent = StringPool.BLANK;
108
109 if (sourcePage != null) {
110 sourceContent = WikiUtil.convert(
111 sourcePage, viewPageURL, editPageURL, attachmentURLPrefix);
112 }
113
114 if (targetPage != null) {
115 targetContent = WikiUtil.convert(
116 targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
117 }
118
119 return DiffHtmlUtil.diff(
120 new UnsyncStringReader(sourceContent),
121 new UnsyncStringReader(targetContent));
122 }
123
124 public static List<WikiPage> filterOrphans(List<WikiPage> pages)
125 throws PortalException {
126
127 List<Map<String, Boolean>> pageTitles =
128 new ArrayList<Map<String, Boolean>>();
129
130 for (WikiPage page : pages) {
131 pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
132 }
133
134 Set<WikiPage> notOrphans = new HashSet<WikiPage>();
135
136 for (WikiPage page : pages) {
137 for (Map<String, Boolean> pageTitle : pageTitles) {
138 String pageTitleLowerCase = page.getTitle();
139
140 if (pageTitle.get(pageTitleLowerCase) != null) {
141 notOrphans.add(page);
142
143 break;
144 }
145 }
146 }
147
148 List<WikiPage> orphans = new ArrayList<WikiPage>();
149
150 for (WikiPage page : pages) {
151 if (!notOrphans.contains(page)) {
152 orphans.add(page);
153 }
154 }
155
156 orphans = ListUtil.sort(orphans);
157
158 return orphans;
159 }
160
161 public static String getEditPage(String format) {
162 return _instance._getEditPage(format);
163 }
164
165 public static String getEmailFromAddress(
166 PortletPreferences preferences, long companyId)
167 throws SystemException {
168
169 return PortalUtil.getEmailFromAddress(
170 preferences, companyId, PropsValues.WIKI_EMAIL_FROM_ADDRESS);
171 }
172
173 public static String getEmailFromName(
174 PortletPreferences preferences, long companyId)
175 throws SystemException {
176
177 return PortalUtil.getEmailFromName(
178 preferences, companyId, PropsValues.WIKI_EMAIL_FROM_NAME);
179 }
180
181 public static String getEmailPageAddedBody(PortletPreferences preferences) {
182 String emailPageAddedBody = preferences.getValue(
183 "emailPageAddedBody", StringPool.BLANK);
184
185 if (Validator.isNotNull(emailPageAddedBody)) {
186 return emailPageAddedBody;
187 }
188 else {
189 return ContentUtil.get(
190 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_BODY));
191 }
192 }
193
194 public static boolean getEmailPageAddedEnabled(
195 PortletPreferences preferences) {
196
197 String emailPageAddedEnabled = preferences.getValue(
198 "emailPageAddedEnabled", StringPool.BLANK);
199
200 if (Validator.isNotNull(emailPageAddedEnabled)) {
201 return GetterUtil.getBoolean(emailPageAddedEnabled);
202 }
203 else {
204 return GetterUtil.getBoolean(
205 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_ENABLED));
206 }
207 }
208
209 public static String getEmailPageAddedSignature(
210 PortletPreferences preferences) {
211
212 String emailPageAddedSignature = preferences.getValue(
213 "emailPageAddedSignature", StringPool.BLANK);
214
215 if (Validator.isNotNull(emailPageAddedSignature)) {
216 return emailPageAddedSignature;
217 }
218 else {
219 return ContentUtil.get(
220 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_SIGNATURE));
221 }
222 }
223
224 public static String getEmailPageAddedSubjectPrefix(
225 PortletPreferences preferences) {
226
227 String emailPageAddedSubjectPrefix = preferences.getValue(
228 "emailPageAddedSubjectPrefix", StringPool.BLANK);
229
230 if (Validator.isNotNull(emailPageAddedSubjectPrefix)) {
231 return emailPageAddedSubjectPrefix;
232 }
233 else {
234 return ContentUtil.get(
235 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_ADDED_SUBJECT_PREFIX));
236 }
237 }
238
239 public static String getEmailPageUpdatedBody(
240 PortletPreferences preferences) {
241
242 String emailPageUpdatedBody = preferences.getValue(
243 "emailPageUpdatedBody", StringPool.BLANK);
244
245 if (Validator.isNotNull(emailPageUpdatedBody)) {
246 return emailPageUpdatedBody;
247 }
248 else {
249 return ContentUtil.get(
250 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_BODY));
251 }
252 }
253
254 public static boolean getEmailPageUpdatedEnabled(
255 PortletPreferences preferences) {
256
257 String emailPageUpdatedEnabled = preferences.getValue(
258 "emailPageUpdatedEnabled", StringPool.BLANK);
259
260 if (Validator.isNotNull(emailPageUpdatedEnabled)) {
261 return GetterUtil.getBoolean(emailPageUpdatedEnabled);
262 }
263 else {
264 return GetterUtil.getBoolean(
265 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_ENABLED));
266 }
267 }
268
269 public static String getEmailPageUpdatedSignature(
270 PortletPreferences preferences) {
271
272 String emailPageUpdatedSignature = preferences.getValue(
273 "emailPageUpdatedSignature", StringPool.BLANK);
274
275 if (Validator.isNotNull(emailPageUpdatedSignature)) {
276 return emailPageUpdatedSignature;
277 }
278 else {
279 return ContentUtil.get(
280 PropsUtil.get(PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SIGNATURE));
281 }
282 }
283
284 public static String getEmailPageUpdatedSubjectPrefix(
285 PortletPreferences preferences) {
286
287 String emailPageUpdatedSubject = preferences.getValue(
288 "emailPageUpdatedSubjectPrefix", StringPool.BLANK);
289
290 if (Validator.isNotNull(emailPageUpdatedSubject)) {
291 return emailPageUpdatedSubject;
292 }
293 else {
294 return ContentUtil.get(
295 PropsUtil.get(
296 PropsKeys.WIKI_EMAIL_PAGE_UPDATED_SUBJECT_PREFIX));
297 }
298 }
299
300 public static List<Object> getEntries(Hits hits) {
301 List<Object> entries = new ArrayList<Object>();
302
303 for (Document document : hits.getDocs()) {
304 String entryClassName = GetterUtil.getString(
305 document.get(Field.ENTRY_CLASS_NAME));
306 long entryClassPK = GetterUtil.getLong(
307 document.get(Field.ENTRY_CLASS_PK));
308
309 try {
310 Object obj = null;
311
312 if (entryClassName.equals(DLFileEntry.class.getName())) {
313 long classPK = GetterUtil.getLong(
314 document.get(Field.CLASS_PK));
315
316 WikiPageLocalServiceUtil.getPage(classPK);
317
318 obj = DLFileEntryLocalServiceUtil.getDLFileEntry(
319 entryClassPK);
320 }
321 else if (entryClassName.equals(MBMessage.class.getName())) {
322 long classPK = GetterUtil.getLong(
323 document.get(Field.CLASS_PK));
324
325 WikiPageLocalServiceUtil.getPage(classPK);
326
327 obj = MBMessageLocalServiceUtil.getMessage(entryClassPK);
328 }
329 else if (entryClassName.equals(WikiPage.class.getName())) {
330 obj = WikiPageLocalServiceUtil.getPage(entryClassPK);
331 }
332
333 entries.add(obj);
334 }
335 catch (Exception e) {
336 if (_log.isWarnEnabled()) {
337 _log.warn(
338 "Wiki search index is stale and contains entry " +
339 "{className=" + entryClassName + ", classPK=" +
340 entryClassPK + "}");
341 }
342 }
343 }
344
345 return entries;
346 }
347
348 public static WikiNode getFirstNode(PortletRequest portletRequest)
349 throws PortalException, SystemException {
350
351 ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
352 WebKeys.THEME_DISPLAY);
353 long groupId = themeDisplay.getScopeGroupId();
354 PermissionChecker permissionChecker =
355 themeDisplay.getPermissionChecker();
356
357 List<WikiNode> nodes = WikiNodeLocalServiceUtil.getNodes(groupId);
358
359 PortletPreferences preferences = portletRequest.getPreferences();
360 String[] visibleNodeNames = StringUtil.split(
361 preferences.getValue("visibleNodes", null));
362 nodes = orderNodes(nodes, visibleNodeNames);
363
364 String[] hiddenNodes = StringUtil.split(
365 preferences.getValue("hiddenNodes", StringPool.BLANK));
366 Arrays.sort(hiddenNodes);
367
368 for (WikiNode node : nodes) {
369 if ((Arrays.binarySearch(hiddenNodes, node.getName()) < 0) &&
370 (WikiNodePermission.contains(
371 permissionChecker, node, ActionKeys.VIEW))) {
372
373 return node;
374 }
375 }
376
377 return null;
378 }
379
380 public static String getFormattedContent(
381 RenderRequest renderRequest, RenderResponse renderResponse,
382 WikiPage wikiPage, PortletURL viewPageURL, PortletURL editPageURL,
383 String title, boolean preview)
384 throws Exception {
385
386 ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
387 WebKeys.THEME_DISPLAY);
388
389 double version = ParamUtil.getDouble(renderRequest, "version");
390
391 PortletURL curViewPageURL = PortletURLUtil.clone(
392 viewPageURL, renderResponse);
393 PortletURL curEditPageURL = PortletURLUtil.clone(
394 editPageURL, renderResponse);
395
396 StringBundler sb = new StringBundler();
397
398 sb.append(themeDisplay.getPathMain());
399 sb.append("/wiki/get_page_attachment?p_l_id=");
400 sb.append(themeDisplay.getPlid());
401 sb.append("&nodeId=");
402 sb.append(wikiPage.getNodeId());
403 sb.append("&title=");
404 sb.append(HttpUtil.encodeURL(wikiPage.getTitle()));
405 sb.append("&fileName=");
406
407 String attachmentURLPrefix = sb.toString();
408
409 if (!preview && (version == 0)) {
410 WikiPageDisplay pageDisplay = WikiCacheUtil.getDisplay(
411 wikiPage.getNodeId(), title, curViewPageURL, curEditPageURL,
412 attachmentURLPrefix);
413
414 if (pageDisplay != null) {
415 return pageDisplay.getFormattedContent();
416 }
417 }
418
419 return WikiUtil.convert(
420 wikiPage, curViewPageURL, curEditPageURL, attachmentURLPrefix);
421 }
422
423 public static String getHelpPage(String format) {
424 return _instance._getHelpPage(format);
425 }
426
427 public static String getHelpURL(String format) {
428 return _instance._getHelpURL(format);
429 }
430
431 public static Map<String, Boolean> getLinks(WikiPage page)
432 throws PageContentException {
433
434 return _instance._getLinks(page);
435 }
436
437 public static List<String> getNodeNames(List<WikiNode> nodes) {
438 List<String> nodeNames = new ArrayList<String>(nodes.size());
439
440 for (WikiNode node : nodes) {
441 nodeNames.add(node.getName());
442 }
443
444 return nodeNames;
445 }
446
447 public static List<WikiNode> getNodes(
448 List<WikiNode> nodes, String[] hiddenNodes,
449 PermissionChecker permissionChecker) {
450
451 nodes = ListUtil.copy(nodes);
452
453 Arrays.sort(hiddenNodes);
454
455 Iterator<WikiNode> itr = nodes.iterator();
456
457 while (itr.hasNext()) {
458 WikiNode node = itr.next();
459
460 if (!(Arrays.binarySearch(hiddenNodes, node.getName()) < 0) ||
461 !WikiNodePermission.contains(
462 permissionChecker, node, ActionKeys.VIEW)) {
463
464 itr.remove();
465 }
466 }
467
468 return nodes;
469 }
470
471 public static OrderByComparator getPageOrderByComparator(
472 String orderByCol, String orderByType) {
473
474 boolean orderByAsc = false;
475
476 if (orderByType.equals("asc")) {
477 orderByAsc = true;
478 }
479
480 OrderByComparator orderByComparator = null;
481
482 if (orderByCol.equals("modifiedDate")) {
483 orderByComparator = new PageCreateDateComparator(orderByAsc);
484 }
485 else if (orderByCol.equals("title")) {
486 orderByComparator = new PageTitleComparator(orderByAsc);
487 }
488 else if (orderByCol.equals("version")) {
489 orderByComparator = new PageVersionComparator(orderByAsc);
490 }
491
492 return orderByComparator;
493 }
494
495 public static List<WikiNode> orderNodes(
496 List<WikiNode> nodes, String[] visibleNodeNames) {
497
498 if ((visibleNodeNames == null) || (visibleNodeNames.length == 0)) {
499 return nodes;
500 }
501
502 nodes = ListUtil.copy(nodes);
503
504 List<WikiNode> orderedNodes = new ArrayList<WikiNode>(nodes.size());
505
506 for (String visibleNodeName : visibleNodeNames) {
507 for (WikiNode node : nodes) {
508 if (node.getName().equals(visibleNodeName)) {
509 orderedNodes.add(node);
510
511 nodes.remove(node);
512
513 break;
514 }
515 }
516 }
517
518 orderedNodes.addAll(nodes);
519
520 return orderedNodes;
521 }
522
523 public static String processContent(String content) {
524 content = content.replaceAll("</p>", "</p>\n");
525 content = content.replaceAll("</br>", "</br>\n");
526 content = content.replaceAll("</div>", "</div>\n");
527
528 return content;
529 }
530
531 public static boolean validate(long nodeId, String content, String format)
532 throws WikiFormatException {
533
534 return _instance._validate(nodeId, content, format);
535 }
536
537 private String _convert(
538 WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
539 String attachmentURLPrefix)
540 throws PageContentException, WikiFormatException {
541
542 LiferayPortletURL liferayViewPageURL = (LiferayPortletURL)viewPageURL;
543 LiferayPortletURL liferayEditPageURL = (LiferayPortletURL)editPageURL;
544
545 WikiEngine engine = _getEngine(page.getFormat());
546
547 String content = engine.convert(
548 page, viewPageURL, editPageURL, attachmentURLPrefix);
549
550 String editPageURLString = StringPool.BLANK;
551
552 if (editPageURL != null) {
553 liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
554
555 editPageURLString = editPageURL.toString();
556
557 editPageURLString = StringUtil.replace(
558 editPageURLString, "__REPLACEMENT__", "$1");
559 }
560
561 Matcher matcher = _editPageURLPattern.matcher(content);
562
563 content = _convertURLs(editPageURLString, matcher);
564
565 String viewPageURLString = StringPool.BLANK;
566
567 if (viewPageURL != null) {
568 liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
569
570 viewPageURLString = viewPageURL.toString();
571
572 viewPageURLString = StringUtil.replace(
573 viewPageURLString, "__REPLACEMENT__", "$1");
574 }
575
576 matcher = _viewPageURLPattern.matcher(content);
577
578 content = _convertURLs(viewPageURLString, matcher);
579
580 content = _replaceAttachments(
581 content, page.getTitle(), attachmentURLPrefix);
582
583 return content;
584 }
585
586 private String _convertURLs(String url, Matcher matcher) {
587 StringBuffer sb = new StringBuffer();
588
589 while (matcher.find()) {
590 String replacement = null;
591
592 if (matcher.groupCount() >= 1) {
593 String encodedTitle = HttpUtil.encodeURL(
594 HtmlUtil.unescape(matcher.group(1)));
595
596 replacement = url.replace("$1", encodedTitle);
597 }
598 else {
599 replacement = url;
600 }
601
602 matcher.appendReplacement(sb, replacement);
603 }
604
605 return matcher.appendTail(sb).toString();
606 }
607
608 private String _getEditPage(String format) {
609 return PropsUtil.get(
610 PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
611 }
612
613 private WikiEngine _getEngine(String format) throws WikiFormatException {
614 WikiEngine engine = _engines.get(format);
615
616 if (engine != null) {
617 return engine;
618 }
619
620 synchronized (_engines) {
621 engine = _engines.get(format);
622
623 if (engine != null) {
624 return engine;
625 }
626
627 try {
628 String engineClass = PropsUtil.get(
629 PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
630
631 if (engineClass == null) {
632 throw new WikiFormatException(format);
633 }
634
635 if (!InstancePool.contains(engineClass)) {
636 engine = (WikiEngine)InstancePool.get(engineClass);
637
638 engine.setMainConfiguration(
639 _readConfigurationFile(
640 PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN, format));
641
642 engine.setInterWikiConfiguration(
643 _readConfigurationFile(
644 PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
645 format));
646 }
647 else {
648 engine = (WikiEngine)InstancePool.get(engineClass);
649 }
650
651 _engines.put(format, engine);
652
653 return engine;
654 }
655 catch (Exception e) {
656 throw new WikiFormatException(e);
657 }
658 }
659 }
660
661 private String _getHelpPage(String format) {
662 return PropsUtil.get(
663 PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
664 }
665
666 private String _getHelpURL(String format) {
667 return PropsUtil.get(
668 PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
669 }
670
671 private Map<String, Boolean> _getLinks(WikiPage page)
672 throws PageContentException {
673
674 try {
675 return _getEngine(page.getFormat()).getOutgoingLinks(page);
676 }
677 catch (WikiFormatException wfe) {
678 return Collections.emptyMap();
679 }
680 }
681
682 private String _readConfigurationFile(String propertyName, String format)
683 throws IOException {
684
685 ClassLoader classLoader = getClass().getClassLoader();
686
687 String configurationFile = PropsUtil.get(
688 propertyName, new Filter(format));
689
690 if (Validator.isNotNull(configurationFile)) {
691 return HttpUtil.URLtoString(
692 classLoader.getResource(configurationFile));
693 }
694 else {
695 return StringPool.BLANK;
696 }
697 }
698
699 private String _replaceAttachments(
700 String content, String title, String attachmentURLPrefix) {
701
702 content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
703
704 content = StringUtil.replace(
705 content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
706
707 return content;
708 }
709
710 private boolean _validate(long nodeId, String content, String format)
711 throws WikiFormatException {
712
713 return _getEngine(format).validate(nodeId, content);
714 }
715
716 private static Log _log = LogFactoryUtil.getLog(WikiUtil.class);
717
718 private static WikiUtil _instance = new WikiUtil();
719
720 private static Pattern _editPageURLPattern = Pattern.compile(
721 "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)" +
722 "\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
723 private static Pattern _viewPageURLPattern = Pattern.compile(
724 "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
725
726 private Map<String, WikiEngine> _engines =
727 new ConcurrentHashMap<String, WikiEngine>();
728
729 }