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