001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.wiki.util;
016    
017    import com.liferay.portal.kernel.configuration.Filter;
018    import com.liferay.portal.kernel.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.diff.DiffHtmlUtil;
020    import com.liferay.portal.kernel.diff.DiffVersion;
021    import com.liferay.portal.kernel.diff.DiffVersionsInfo;
022    import com.liferay.portal.kernel.exception.PortalException;
023    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
024    import com.liferay.portal.kernel.language.LanguageUtil;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
028    import com.liferay.portal.kernel.search.Document;
029    import com.liferay.portal.kernel.search.Field;
030    import com.liferay.portal.kernel.search.Hits;
031    import com.liferay.portal.kernel.util.ArrayUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HtmlUtil;
034    import com.liferay.portal.kernel.util.HttpUtil;
035    import com.liferay.portal.kernel.util.InstanceFactory;
036    import com.liferay.portal.kernel.util.ListUtil;
037    import com.liferay.portal.kernel.util.OrderByComparator;
038    import com.liferay.portal.kernel.util.ParamUtil;
039    import com.liferay.portal.kernel.util.PropsKeys;
040    import com.liferay.portal.kernel.util.StringBundler;
041    import com.liferay.portal.kernel.util.StringPool;
042    import com.liferay.portal.kernel.util.StringUtil;
043    import com.liferay.portal.kernel.util.Validator;
044    import com.liferay.portal.model.Company;
045    import com.liferay.portal.security.permission.ActionKeys;
046    import com.liferay.portal.security.permission.PermissionChecker;
047    import com.liferay.portal.theme.PortletDisplay;
048    import com.liferay.portal.theme.ThemeDisplay;
049    import com.liferay.portal.util.PortalUtil;
050    import com.liferay.portal.util.PropsUtil;
051    import com.liferay.portal.util.WebKeys;
052    import com.liferay.portlet.PortletURLUtil;
053    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
054    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
055    import com.liferay.portlet.messageboards.model.MBMessage;
056    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
057    import com.liferay.portlet.wiki.PageContentException;
058    import com.liferay.portlet.wiki.WikiFormatException;
059    import com.liferay.portlet.wiki.WikiPortletInstanceSettings;
060    import com.liferay.portlet.wiki.engines.WikiEngine;
061    import com.liferay.portlet.wiki.model.WikiNode;
062    import com.liferay.portlet.wiki.model.WikiPage;
063    import com.liferay.portlet.wiki.model.WikiPageDisplay;
064    import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
065    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
066    import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
067    import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
068    import com.liferay.portlet.wiki.util.comparator.PageTitleComparator;
069    import com.liferay.portlet.wiki.util.comparator.PageVersionComparator;
070    
071    import java.io.IOException;
072    
073    import java.util.ArrayList;
074    import java.util.Arrays;
075    import java.util.Collections;
076    import java.util.HashSet;
077    import java.util.Iterator;
078    import java.util.LinkedHashMap;
079    import java.util.List;
080    import java.util.Map;
081    import java.util.Set;
082    import java.util.concurrent.ConcurrentHashMap;
083    import java.util.regex.Matcher;
084    import java.util.regex.Pattern;
085    
086    import javax.portlet.PortletRequest;
087    import javax.portlet.PortletURL;
088    import javax.portlet.RenderRequest;
089    import javax.portlet.RenderResponse;
090    
091    import javax.servlet.http.HttpServletRequest;
092    
093    /**
094     * @author Brian Wing Shun Chan
095     * @author Jorge Ferrer
096     */
097    public class WikiUtil {
098    
099            public static String convert(
100                            WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
101                            String attachmentURLPrefix)
102                    throws PageContentException, WikiFormatException {
103    
104                    return _instance._convert(
105                            page, viewPageURL, editPageURL, attachmentURLPrefix);
106            }
107    
108            public static String diffHtml(
109                            WikiPage sourcePage, WikiPage targetPage, PortletURL viewPageURL,
110                            PortletURL editPageURL, String attachmentURLPrefix)
111                    throws Exception {
112    
113                    String sourceContent = StringPool.BLANK;
114                    String targetContent = StringPool.BLANK;
115    
116                    if (sourcePage != null) {
117                            sourceContent = convert(
118                                    sourcePage, viewPageURL, editPageURL, attachmentURLPrefix);
119                    }
120    
121                    if (targetPage != null) {
122                            targetContent = convert(
123                                    targetPage, viewPageURL, editPageURL, attachmentURLPrefix);
124                    }
125    
126                    return DiffHtmlUtil.diff(
127                            new UnsyncStringReader(sourceContent),
128                            new UnsyncStringReader(targetContent));
129            }
130    
131            public static String escapeName(String name) {
132                    return StringUtil.replace(name, _UNESCAPED_CHARS, _ESCAPED_CHARS);
133            }
134    
135            public static List<WikiPage> filterOrphans(List<WikiPage> pages)
136                    throws PortalException {
137    
138                    List<Map<String, Boolean>> pageTitles =
139                            new ArrayList<Map<String, Boolean>>();
140    
141                    for (WikiPage page : pages) {
142                            pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
143                    }
144    
145                    Set<WikiPage> notOrphans = new HashSet<WikiPage>();
146    
147                    for (WikiPage page : pages) {
148                            for (Map<String, Boolean> pageTitle : pageTitles) {
149                                    String pageTitleLowerCase = page.getTitle();
150    
151                                    pageTitleLowerCase = StringUtil.toLowerCase(pageTitleLowerCase);
152    
153                                    if (pageTitle.get(pageTitleLowerCase) != null) {
154                                            notOrphans.add(page);
155    
156                                            break;
157                                    }
158                            }
159                    }
160    
161                    List<WikiPage> orphans = new ArrayList<WikiPage>();
162    
163                    for (WikiPage page : pages) {
164                            if (!notOrphans.contains(page)) {
165                                    orphans.add(page);
166                            }
167                    }
168    
169                    orphans = ListUtil.sort(orphans);
170    
171                    return orphans;
172            }
173    
174            public static String getAttachmentURLPrefix(
175                    String mainPath, long plid, long nodeId, String title) {
176    
177                    StringBundler sb = new StringBundler(8);
178    
179                    sb.append(mainPath);
180                    sb.append("/wiki/get_page_attachment?p_l_id=");
181                    sb.append(plid);
182                    sb.append("&nodeId=");
183                    sb.append(nodeId);
184                    sb.append("&title=");
185                    sb.append(HttpUtil.encodeURL(title));
186                    sb.append("&fileName=");
187    
188                    return sb.toString();
189            }
190    
191            public static DiffVersionsInfo getDiffVersionsInfo(
192                    long nodeId, String title, double sourceVersion, double targetVersion,
193                    HttpServletRequest request) {
194    
195                    double previousVersion = 0;
196                    double nextVersion = 0;
197    
198                    List<WikiPage> pages = WikiPageLocalServiceUtil.getPages(
199                            nodeId, title, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
200                            new PageVersionComparator(true));
201    
202                    for (WikiPage page : pages) {
203                            if ((page.getVersion() < sourceVersion) &&
204                                    (page.getVersion() > previousVersion)) {
205    
206                                    previousVersion = page.getVersion();
207                            }
208    
209                            if ((page.getVersion() > targetVersion) &&
210                                    ((page.getVersion() < nextVersion) || (nextVersion == 0))) {
211    
212                                    nextVersion = page.getVersion();
213                            }
214                    }
215    
216                    List<DiffVersion> diffVersions = new ArrayList<DiffVersion>();
217    
218                    for (WikiPage page : pages) {
219                            String extraInfo = StringPool.BLANK;
220    
221                            if (page.isMinorEdit()) {
222                                    extraInfo = LanguageUtil.get(request, "minor-edit");
223                            }
224    
225                            DiffVersion diffVersion = new DiffVersion(
226                                    page.getUserId(), page.getVersion(), page.getModifiedDate(),
227                                    page.getSummary(), extraInfo);
228    
229                            diffVersions.add(diffVersion);
230                    }
231    
232                    return new DiffVersionsInfo(diffVersions, nextVersion, previousVersion);
233            }
234    
235            public static String getEditPage(String format) {
236                    return _instance._getEditPage(format);
237            }
238    
239            public static Map<String, String> getEmailFromDefinitionTerms(
240                    RenderRequest request) {
241    
242                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
243                            WebKeys.THEME_DISPLAY);
244    
245                    Map<String, String> definitionTerms =
246                            new LinkedHashMap<String, String>();
247    
248                    definitionTerms.put(
249                            "[$COMPANY_ID$]",
250                            LanguageUtil.get(
251                                    themeDisplay.getLocale(),
252                                    "the-company-id-associated-with-the-wiki"));
253                    definitionTerms.put(
254                            "[$COMPANY_MX$]",
255                            LanguageUtil.get(
256                                    themeDisplay.getLocale(),
257                                    "the-company-mx-associated-with-the-wiki"));
258                    definitionTerms.put(
259                            "[$COMPANY_NAME$]",
260                            LanguageUtil.get(
261                                    themeDisplay.getLocale(),
262                                    "the-company-name-associated-with-the-wiki"));
263                    definitionTerms.put(
264                            "[$PAGE_USER_ADDRESS$]",
265                            LanguageUtil.get(
266                                    themeDisplay.getLocale(),
267                                    "the-email-address-of-the-user-who-added-the-page"));
268                    definitionTerms.put(
269                            "[$PAGE_USER_NAME$]",
270                            LanguageUtil.get(
271                                    themeDisplay.getLocale(), "the-user-who-added-the-page"));
272                    definitionTerms.put(
273                            "[$PORTLET_NAME$]", PortalUtil.getPortletTitle(request));
274                    definitionTerms.put(
275                            "[$SITE_NAME$]",
276                            LanguageUtil.get(
277                                    themeDisplay.getLocale(),
278                                    "the-site-name-associated-with-the-wiki"));
279    
280                    return definitionTerms;
281            }
282    
283            public static Map<String, String> getEmailNotificationDefinitionTerms(
284                    RenderRequest request, String emailFromAddress, String emailFromName) {
285    
286                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
287                            WebKeys.THEME_DISPLAY);
288    
289                    Map<String, String> definitionTerms =
290                            new LinkedHashMap<String, String>();
291    
292                    definitionTerms.put(
293                            "[$COMPANY_ID$]",
294                            LanguageUtil.get(
295                                    themeDisplay.getLocale(),
296                                    "the-company-id-associated-with-the-wiki"));
297                    definitionTerms.put(
298                            "[$COMPANY_MX$]",
299                            LanguageUtil.get(
300                                    themeDisplay.getLocale(),
301                                    "the-company-mx-associated-with-the-wiki"));
302                    definitionTerms.put(
303                            "[$COMPANY_NAME$]",
304                            LanguageUtil.get(
305                                    themeDisplay.getLocale(),
306                                    "the-company-name-associated-with-the-wiki"));
307                    definitionTerms.put(
308                            "[$DIFFS_URL$]",
309                            LanguageUtil.get(
310                                    themeDisplay.getLocale(),
311                                    "the-url-of-the-page-comparing-this-page-content-with-the-" +
312                                            "previous-version"));
313                    definitionTerms.put(
314                            "[$FROM_ADDRESS$]", HtmlUtil.escape(emailFromAddress));
315                    definitionTerms.put("[$FROM_NAME$]", HtmlUtil.escape(emailFromName));
316                    definitionTerms.put(
317                            "[$NODE_NAME$]",
318                            LanguageUtil.get(
319                                    themeDisplay.getLocale(),
320                                    "the-node-in-which-the-page-was-added"));
321                    definitionTerms.put(
322                            "[$PAGE_CONTENT$]",
323                            LanguageUtil.get(themeDisplay.getLocale(), "the-page-content"));
324                    definitionTerms.put(
325                            "[$PAGE_DATE_UPDATE$]",
326                            LanguageUtil.get(
327                                    themeDisplay.getLocale(), "the-date-of-the-modifications"));
328                    definitionTerms.put(
329                            "[$PAGE_DIFFS$]",
330                            LanguageUtil.get(
331                                    themeDisplay.getLocale(),
332                                    "the-page-content-compared-with-the-previous-version-page-" +
333                                            "content"));
334                    definitionTerms.put(
335                            "[$PAGE_ID$]",
336                            LanguageUtil.get(themeDisplay.getLocale(), "the-page-id"));
337                    definitionTerms.put(
338                            "[$PAGE_SUMMARY$]",
339                            LanguageUtil.get(
340                                    themeDisplay.getLocale(),
341                                    "the-summary-of-the-page-or-the-modifications"));
342                    definitionTerms.put(
343                            "[$PAGE_TITLE$]",
344                            LanguageUtil.get(themeDisplay.getLocale(), "the-page-title"));
345                    definitionTerms.put(
346                            "[$PAGE_URL$]",
347                            LanguageUtil.get(themeDisplay.getLocale(), "the-page-url"));
348                    definitionTerms.put(
349                            "[$PAGE_USER_ADDRESS$]",
350                            LanguageUtil.get(
351                                    themeDisplay.getLocale(),
352                                    "the-email-address-of-the-user-who-added-the-page"));
353                    definitionTerms.put(
354                            "[$PAGE_USER_NAME$]",
355                            LanguageUtil.get(
356                                    themeDisplay.getLocale(), "the-user-who-added-the-page"));
357    
358                    Company company = themeDisplay.getCompany();
359    
360                    definitionTerms.put("[$PORTAL_URL$]", company.getVirtualHostname());
361    
362                    definitionTerms.put(
363                            "[$PORTLET_NAME$]", PortalUtil.getPortletTitle(request));
364                    definitionTerms.put(
365                            "[$SITE_NAME$]",
366                            LanguageUtil.get(
367                                    themeDisplay.getLocale(),
368                                    "the-site-name-associated-with-the-wiki"));
369                    definitionTerms.put(
370                            "[$TO_ADDRESS$]",
371                            LanguageUtil.get(
372                                    themeDisplay.getLocale(),
373                                    "the-address-of-the-email-recipient"));
374                    definitionTerms.put(
375                            "[$TO_NAME$]",
376                            LanguageUtil.get(
377                                    themeDisplay.getLocale(), "the-name-of-the-email-recipient"));
378    
379                    return definitionTerms;
380            }
381    
382            public static List<Object> getEntries(Hits hits) {
383                    List<Object> entries = new ArrayList<Object>();
384    
385                    for (Document document : hits.getDocs()) {
386                            String entryClassName = GetterUtil.getString(
387                                    document.get(Field.ENTRY_CLASS_NAME));
388                            long entryClassPK = GetterUtil.getLong(
389                                    document.get(Field.ENTRY_CLASS_PK));
390    
391                            try {
392                                    Object obj = null;
393    
394                                    if (entryClassName.equals(DLFileEntry.class.getName())) {
395                                            long classPK = GetterUtil.getLong(
396                                                    document.get(Field.CLASS_PK));
397    
398                                            WikiPageLocalServiceUtil.getPage(classPK);
399    
400                                            obj = DLFileEntryLocalServiceUtil.getDLFileEntry(
401                                                    entryClassPK);
402                                    }
403                                    else if (entryClassName.equals(MBMessage.class.getName())) {
404                                            long classPK = GetterUtil.getLong(
405                                                    document.get(Field.CLASS_PK));
406    
407                                            WikiPageLocalServiceUtil.getPage(classPK);
408    
409                                            obj = MBMessageLocalServiceUtil.getMessage(entryClassPK);
410                                    }
411                                    else if (entryClassName.equals(WikiPage.class.getName())) {
412                                            obj = WikiPageLocalServiceUtil.getPage(entryClassPK);
413                                    }
414    
415                                    entries.add(obj);
416                            }
417                            catch (Exception e) {
418                                    if (_log.isWarnEnabled()) {
419                                            _log.warn(
420                                                    "Wiki search index is stale and contains entry " +
421                                                            "{className=" + entryClassName + ", classPK=" +
422                                                                    entryClassPK + "}");
423                                    }
424                            }
425                    }
426    
427                    return entries;
428            }
429    
430            public static WikiNode getFirstNode(PortletRequest portletRequest)
431                    throws PortalException {
432    
433                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
434                            WebKeys.THEME_DISPLAY);
435                    long groupId = themeDisplay.getScopeGroupId();
436                    PermissionChecker permissionChecker =
437                            themeDisplay.getPermissionChecker();
438    
439                    List<WikiNode> nodes = WikiNodeLocalServiceUtil.getNodes(groupId);
440    
441                    PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
442    
443                    WikiPortletInstanceSettings wikiPortletInstanceSettings =
444                            WikiPortletInstanceSettings.getInstance(
445                                    themeDisplay.getLayout(), portletDisplay.getId());
446    
447                    String[] visibleNodeNames =
448                            wikiPortletInstanceSettings.getVisibleNodes();
449    
450                    nodes = orderNodes(nodes, visibleNodeNames);
451    
452                    String[] hiddenNodes = wikiPortletInstanceSettings.getHiddenNodes();
453                    Arrays.sort(hiddenNodes);
454    
455                    for (WikiNode node : nodes) {
456                            if ((Arrays.binarySearch(hiddenNodes, node.getName()) < 0) &&
457                                    WikiNodePermission.contains(
458                                            permissionChecker, node, ActionKeys.VIEW)) {
459    
460                                    return node;
461                            }
462                    }
463    
464                    return null;
465            }
466    
467            public static String getFormattedContent(
468                            RenderRequest renderRequest, RenderResponse renderResponse,
469                            WikiPage wikiPage, PortletURL viewPageURL, PortletURL editPageURL,
470                            String title, boolean preview)
471                    throws Exception {
472    
473                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
474                            WebKeys.THEME_DISPLAY);
475    
476                    double version = ParamUtil.getDouble(renderRequest, "version");
477    
478                    PortletURL curViewPageURL = PortletURLUtil.clone(
479                            viewPageURL, renderResponse);
480                    PortletURL curEditPageURL = PortletURLUtil.clone(
481                            editPageURL, renderResponse);
482    
483                    StringBundler sb = new StringBundler(8);
484    
485                    sb.append(themeDisplay.getPathMain());
486                    sb.append("/wiki/get_page_attachment?p_l_id=");
487                    sb.append(themeDisplay.getPlid());
488                    sb.append("&nodeId=");
489                    sb.append(wikiPage.getNodeId());
490                    sb.append("&title=");
491                    sb.append(HttpUtil.encodeURL(wikiPage.getTitle()));
492                    sb.append("&fileName=");
493    
494                    String attachmentURLPrefix = sb.toString();
495    
496                    if (!preview && (version == 0)) {
497                            WikiPageDisplay pageDisplay = WikiCacheUtil.getDisplay(
498                                    wikiPage.getNodeId(), title, curViewPageURL, curEditPageURL,
499                                    attachmentURLPrefix);
500    
501                            if (pageDisplay != null) {
502                                    return pageDisplay.getFormattedContent();
503                            }
504                    }
505    
506                    return convert(
507                            wikiPage, curViewPageURL, curEditPageURL, attachmentURLPrefix);
508            }
509    
510            public static String getHelpPage(String format) {
511                    return _instance._getHelpPage(format);
512            }
513    
514            public static String getHelpURL(String format) {
515                    return _instance._getHelpURL(format);
516            }
517    
518            public static Map<String, Boolean> getLinks(WikiPage page)
519                    throws PageContentException {
520    
521                    return _instance._getLinks(page);
522            }
523    
524            public static List<String> getNodeNames(List<WikiNode> nodes) {
525                    List<String> nodeNames = new ArrayList<String>(nodes.size());
526    
527                    for (WikiNode node : nodes) {
528                            nodeNames.add(node.getName());
529                    }
530    
531                    return nodeNames;
532            }
533    
534            public static List<WikiNode> getNodes(
535                    List<WikiNode> nodes, String[] hiddenNodes,
536                    PermissionChecker permissionChecker) {
537    
538                    nodes = ListUtil.copy(nodes);
539    
540                    Arrays.sort(hiddenNodes);
541    
542                    Iterator<WikiNode> itr = nodes.iterator();
543    
544                    while (itr.hasNext()) {
545                            WikiNode node = itr.next();
546    
547                            if (!(Arrays.binarySearch(hiddenNodes, node.getName()) < 0) ||
548                                    !WikiNodePermission.contains(
549                                            permissionChecker, node, ActionKeys.VIEW)) {
550    
551                                    itr.remove();
552                            }
553                    }
554    
555                    return nodes;
556            }
557    
558            public static OrderByComparator<WikiPage> getPageOrderByComparator(
559                    String orderByCol, String orderByType) {
560    
561                    boolean orderByAsc = false;
562    
563                    if (orderByType.equals("asc")) {
564                            orderByAsc = true;
565                    }
566    
567                    OrderByComparator<WikiPage> orderByComparator = null;
568    
569                    if (orderByCol.equals("modifiedDate")) {
570                            orderByComparator = new PageCreateDateComparator(orderByAsc);
571                    }
572                    else if (orderByCol.equals("title")) {
573                            orderByComparator = new PageTitleComparator(orderByAsc);
574                    }
575                    else if (orderByCol.equals("version")) {
576                            orderByComparator = new PageVersionComparator(orderByAsc);
577                    }
578    
579                    return orderByComparator;
580            }
581    
582            public static List<WikiNode> orderNodes(
583                    List<WikiNode> nodes, String[] visibleNodeNames) {
584    
585                    if (ArrayUtil.isEmpty(visibleNodeNames)) {
586                            return nodes;
587                    }
588    
589                    nodes = ListUtil.copy(nodes);
590    
591                    List<WikiNode> orderedNodes = new ArrayList<WikiNode>(nodes.size());
592    
593                    for (String visibleNodeName : visibleNodeNames) {
594                            for (WikiNode node : nodes) {
595                                    if (node.getName().equals(visibleNodeName)) {
596                                            orderedNodes.add(node);
597    
598                                            nodes.remove(node);
599    
600                                            break;
601                                    }
602                            }
603                    }
604    
605                    orderedNodes.addAll(nodes);
606    
607                    return orderedNodes;
608            }
609    
610            public static String processContent(String content) {
611                    content = StringUtil.replace(content, "</p>", "</p>\n");
612                    content = StringUtil.replace(content, "</br>", "</br>\n");
613                    content = StringUtil.replace(content, "</div>", "</div>\n");
614    
615                    return content;
616            }
617    
618            public static String unescapeName(String name) {
619                    return StringUtil.replace(name, _ESCAPED_CHARS, _UNESCAPED_CHARS);
620            }
621    
622            public static boolean validate(long nodeId, String content, String format)
623                    throws WikiFormatException {
624    
625                    return _instance._validate(nodeId, content, format);
626            }
627    
628            private String _convert(
629                            WikiPage page, PortletURL viewPageURL, PortletURL editPageURL,
630                            String attachmentURLPrefix)
631                    throws PageContentException, WikiFormatException {
632    
633                    LiferayPortletURL liferayViewPageURL = (LiferayPortletURL)viewPageURL;
634                    LiferayPortletURL liferayEditPageURL = (LiferayPortletURL)editPageURL;
635    
636                    WikiEngine engine = _getEngine(page.getFormat());
637    
638                    String content = engine.convert(
639                            page, viewPageURL, editPageURL, attachmentURLPrefix);
640    
641                    String editPageURLString = StringPool.BLANK;
642    
643                    if (editPageURL != null) {
644                            liferayEditPageURL.setParameter("title", "__REPLACEMENT__", false);
645    
646                            editPageURLString = editPageURL.toString();
647    
648                            editPageURLString = StringUtil.replace(
649                                    editPageURLString, "__REPLACEMENT__", "$1");
650                    }
651    
652                    Matcher matcher = _editPageURLPattern.matcher(content);
653    
654                    content = _convertURLs(editPageURLString, matcher);
655    
656                    String viewPageURLString = StringPool.BLANK;
657    
658                    if (viewPageURL != null) {
659                            liferayViewPageURL.setParameter("title", "__REPLACEMENT__", false);
660    
661                            viewPageURLString = viewPageURL.toString();
662    
663                            viewPageURLString = StringUtil.replace(
664                                    viewPageURLString, "__REPLACEMENT__", "$1");
665                    }
666    
667                    matcher = _viewPageURLPattern.matcher(content);
668    
669                    content = _convertURLs(viewPageURLString, matcher);
670    
671                    content = _replaceAttachments(
672                            content, page.getTitle(), attachmentURLPrefix);
673    
674                    return content;
675            }
676    
677            private String _convertURLs(String url, Matcher matcher) {
678                    StringBuffer sb = new StringBuffer();
679    
680                    while (matcher.find()) {
681                            String replacement = null;
682    
683                            if (matcher.groupCount() >= 1) {
684                                    String encodedTitle = HttpUtil.encodeURL(
685                                            HtmlUtil.unescape(matcher.group(1)));
686    
687                                    replacement = url.replace("$1", encodedTitle);
688                            }
689                            else {
690                                    replacement = url;
691                            }
692    
693                            matcher.appendReplacement(sb, replacement);
694                    }
695    
696                    return matcher.appendTail(sb).toString();
697            }
698    
699            private String _getEditPage(String format) {
700                    return PropsUtil.get(
701                            PropsKeys.WIKI_FORMATS_EDIT_PAGE, new Filter(format));
702            }
703    
704            private WikiEngine _getEngine(String format) throws WikiFormatException {
705                    WikiEngine engine = _engines.get(format);
706    
707                    if (engine != null) {
708                            return engine;
709                    }
710    
711                    synchronized (_engines) {
712                            engine = _engines.get(format);
713    
714                            if (engine != null) {
715                                    return engine;
716                            }
717    
718                            try {
719                                    String engineClassName = PropsUtil.get(
720                                            PropsKeys.WIKI_FORMATS_ENGINE, new Filter(format));
721    
722                                    if (engineClassName == null) {
723                                            throw new WikiFormatException(format);
724                                    }
725    
726                                    Class<?> clazz = getClass();
727    
728                                    engine = (WikiEngine)InstanceFactory.newInstance(
729                                            clazz.getClassLoader(), engineClassName);
730    
731                                    engine.setInterWikiConfiguration(
732                                            _readConfigurationFile(
733                                                    PropsKeys.WIKI_FORMATS_CONFIGURATION_INTERWIKI,
734                                                    format));
735                                    engine.setMainConfiguration(
736                                            _readConfigurationFile(
737                                                    PropsKeys.WIKI_FORMATS_CONFIGURATION_MAIN, format));
738    
739                                    _engines.put(format, engine);
740    
741                                    return engine;
742                            }
743                            catch (Exception e) {
744                                    throw new WikiFormatException(e);
745                            }
746                    }
747            }
748    
749            private String _getHelpPage(String format) {
750                    return PropsUtil.get(
751                            PropsKeys.WIKI_FORMATS_HELP_PAGE, new Filter(format));
752            }
753    
754            private String _getHelpURL(String format) {
755                    return PropsUtil.get(
756                            PropsKeys.WIKI_FORMATS_HELP_URL, new Filter(format));
757            }
758    
759            private Map<String, Boolean> _getLinks(WikiPage page)
760                    throws PageContentException {
761    
762                    try {
763                            return _getEngine(page.getFormat()).getOutgoingLinks(page);
764                    }
765                    catch (WikiFormatException wfe) {
766                            return Collections.emptyMap();
767                    }
768            }
769    
770            private String _readConfigurationFile(String propertyName, String format)
771                    throws IOException {
772    
773                    ClassLoader classLoader = getClass().getClassLoader();
774    
775                    String configurationFile = PropsUtil.get(
776                            propertyName, new Filter(format));
777    
778                    if (Validator.isNotNull(configurationFile)) {
779                            return HttpUtil.URLtoString(
780                                    classLoader.getResource(configurationFile));
781                    }
782                    else {
783                            return StringPool.BLANK;
784                    }
785            }
786    
787            private String _replaceAttachments(
788                    String content, String title, String attachmentURLPrefix) {
789    
790                    content = StringUtil.replace(content, "[$WIKI_PAGE_NAME$]", title);
791    
792                    content = StringUtil.replace(
793                            content, "[$ATTACHMENT_URL_PREFIX$]", attachmentURLPrefix);
794    
795                    return content;
796            }
797    
798            private boolean _validate(long nodeId, String content, String format)
799                    throws WikiFormatException {
800    
801                    return _getEngine(format).validate(nodeId, content);
802            }
803    
804            private static final String[] _ESCAPED_CHARS = new String[] {
805                    "<PLUS>", "<QUESTION>", "<SLASH>"
806            };
807    
808            private static final String[] _UNESCAPED_CHARS = new String[] {
809                    StringPool.PLUS, StringPool.QUESTION, StringPool.SLASH
810            };
811    
812            private static final Log _log = LogFactoryUtil.getLog(WikiUtil.class);
813    
814            private static final WikiUtil _instance = new WikiUtil();
815    
816            private static final Pattern _editPageURLPattern = Pattern.compile(
817                    "\\[\\$BEGIN_PAGE_TITLE_EDIT\\$\\](.*?)" +
818                            "\\[\\$END_PAGE_TITLE_EDIT\\$\\]");
819            private static final Pattern _viewPageURLPattern = Pattern.compile(
820                    "\\[\\$BEGIN_PAGE_TITLE\\$\\](.*?)\\[\\$END_PAGE_TITLE\\$\\]");
821    
822            private final Map<String, WikiEngine> _engines =
823                    new ConcurrentHashMap<String, WikiEngine>();
824    
825    }