001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.apidoc.documentation;
018
019import java.util.LinkedList;
020
021import org.apache.commons.lang.StringUtils;
022import org.nuxeo.ecm.platform.htmlsanitizer.HtmlSanitizerService;
023import org.nuxeo.runtime.api.Framework;
024
025/**
026 * Helper to generate HTML for documentation strings.
027 */
028public class DocumentationHelper {
029
030    private static final String BR = "<br/>";
031
032    private static final String BR2 = "<br />";
033
034    private static final String BR3 = "<br>";
035
036    private static final String P = "<p/>";
037
038    private static final String P2 = "<p />";
039
040    // private static final String CODE_START = "<div class=\"code\"><pre>";
041    private static final String CODE_START = "<code><pre>";
042
043    // private static final String CODE_END = "</pre></div>";
044    private static final String CODE_END = "</pre></code>";
045
046    private static final String AUTHOR = "@author";
047
048    // utility class
049    private DocumentationHelper() {
050    }
051
052    /**
053     * Transforms Nuxeo extension point {@code <documentation>} content into HTML.
054     * <p>
055     * <ul>
056     * <li>standalone newlines are turned into {@code <br/>}</li>
057     * <li>{@code <code>} blocks are turned into a {@code <div class="code">} with a {@code <pre>}</li>
058     * <li>{@code @author} blocks are removed</li>
059     * </ul>
060     */
061    public static String getHtml(String doc) {
062        if (doc == null) {
063            return "";
064        }
065        HtmlSanitizerService sanitizer = Framework.getService(HtmlSanitizerService.class);
066        if (sanitizer == null && !Framework.isTestModeSet()) {
067            throw new RuntimeException("Cannot find HtmlSanitizerService");
068        }
069
070        LinkedList<String> lines = new LinkedList<String>();
071        lines.add(P);
072        boolean newline = true;
073        boolean firstcode = false;
074        boolean code = false;
075        for (String line : doc.split("\n")) {
076            if (!code) {
077                line = line.trim();
078                if ("".equals(line) || BR.equals(line) || BR2.equals(line) || BR3.equals(line) || P.equals(line)
079                        || P2.equals(line)) {
080                    if (!newline) {
081                        lines.add(P);
082                        newline = true;
083                    }
084                } else {
085                    if ("<code>".equals(line)) {
086                        code = true;
087                        firstcode = true;
088                        line = CODE_START;
089                        if (!newline) {
090                            line = P + line;
091                        }
092                        lines.add(line);
093                        newline = false;
094                    } else if (line.startsWith(AUTHOR)) {
095                        if (!newline) {
096                            lines.add(P);
097                        }
098                        newline = true;
099                    } else {
100                        lines.add(line);
101                        newline = false;
102                    }
103                }
104            } else { // code
105                if ("</code>".equals(line.trim())) {
106                    code = false;
107                    line = CODE_END + P;
108                    newline = true;
109                } else {
110                    line = line.replace("&", "&amp;").replace("<", "&lt;");
111                }
112                if (firstcode) {
113                    // don't add a \n at the start of the code
114                    firstcode = false;
115                    line = lines.removeLast() + line;
116                }
117                lines.add(line);
118            }
119        }
120        if (code) {
121            lines.add(CODE_END);
122        }
123        String html = StringUtils.join(lines, "\n");
124        if (sanitizer != null) {
125            html = sanitizer.sanitizeString(html, null);
126        }
127        return secureXML(html);
128    }
129
130    /**
131     * Makes sure no passwords are embedded in the XML.
132     */
133    public static String secureXML(String xml) {
134        if (xml == null || !xml.contains("assword")) {
135            return xml;
136        }
137        xml = xml.replaceAll("<([a-zA-Z]*[pP])assword>[^<]*</([a-zA-Z]*)assword>", "<$1assword>********</$2assword>");
138        // attributes: nuxeo-core-auth
139        xml = xml.replaceAll("([a-zA-Z]*[pP])assword=\"[^\"]*\"", "$1assword=\"********\"");
140        // property: default-repository-config
141        xml = xml.replaceAll("([a-zA-Z]*[pP])assword\">[^<]*<", "$1assword\">********<");
142        return xml;
143    }
144
145}