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