001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2012-2014 ForgeRock AS 015 */ 016 017package org.forgerock.doc.maven.post; 018 019import org.apache.commons.io.FileUtils; 020import org.apache.commons.io.FilenameUtils; 021import org.apache.commons.io.IOUtils; 022import org.apache.maven.plugin.MojoExecutionException; 023import org.forgerock.doc.maven.AbstractDocbkxMojo; 024import org.forgerock.doc.maven.utils.HtmlUtils; 025import org.forgerock.doc.maven.utils.SyntaxHighlighterCopier; 026 027import java.io.File; 028import java.io.IOException; 029import java.net.URL; 030import java.util.HashMap; 031 032/** 033 * HTML post-processor for both single-page and chunked HTML formats. 034 */ 035public class Html { 036 037 /** 038 * The Mojo that holds configuration and related methods. 039 */ 040 private AbstractDocbkxMojo m; 041 042 /** 043 * Constructor setting the Mojo that holds the configuration. 044 * 045 * @param mojo The Mojo that holds the configuration. 046 */ 047 public Html(final AbstractDocbkxMojo mojo) { 048 m = mojo; 049 050 outputDirectories = new String[2]; 051 outputDirectories[0] = ""; 052 outputDirectories[1] = File.separator + FilenameUtils.getBaseName(m.getDocumentSrcName()); 053 } 054 055 /** 056 * Post-processes HTML formats. 057 * 058 * @throws MojoExecutionException Failed to post-process HTML. 059 */ 060 public void execute() throws MojoExecutionException { 061 // Add JavaScript for manipulating HTML content. 062 addScript(); 063 064 065 // Add SyntaxHighlighter files. 066 final File htmlDir = new File(m.getDocbkxOutputDirectory(), "html"); 067 final String chunkDirName = FilenameUtils.getBaseName(m.getDocumentSrcName()); 068 069 String[] outputDirectories = new String[2 * m.getDocNames().size()]; 070 071 int i = 0; 072 for (final String docName : m.getDocNames()) { 073 074 final File docDir = new File(htmlDir, docName); 075 076 // Examples: 077 // ${project.build.directory}/docbkx/html/my-book 078 outputDirectories[i] = docDir.getPath(); 079 ++i; 080 081 // ${project.build.directory}/docbkx/html/my-book/index 082 outputDirectories[i] = new File(docDir, chunkDirName).getPath(); 083 ++i; 084 } 085 086 SyntaxHighlighterCopier copier = 087 new SyntaxHighlighterCopier(outputDirectories); 088 try { 089 copier.copy(); 090 } catch (IOException e) { 091 throw new MojoExecutionException( 092 "Failed to copy files: " + e.getMessage(), e); 093 } 094 095 096 // Edit the HTML for publication. 097 editBuiltHtml(htmlDir.getPath()); 098 099 100 // Optionally fix links to arbitrary resources in chunked HTML. 101 if (m.doCopyResourceFiles() && m.getResourcesDirectory().exists()) { 102 103 final String baseName = FilenameUtils.getBaseName(m.getResourcesDirectory().getPath()); 104 105 for (final String docName : m.getDocNames()) { 106 107 final File docDir = new File(htmlDir, docName); 108 109 try { 110 HtmlUtils.fixResourceLinks(new File(docDir, chunkDirName).getPath(), baseName); 111 } catch (IOException e) { 112 throw new MojoExecutionException("Failed to update resource links", e); 113 } 114 } 115 } 116 } 117 118 /** 119 * Directories where scripts and CSS are to be added. 120 */ 121 private String[] outputDirectories; 122 123 /** 124 * Add JavaScript to include in HTML in each document source directory. 125 * See <a href="http://docbook.sourceforge.net/release/xsl/current/doc/html/html.script.html" 126 * >html.script</a> for details. 127 * 128 * @throws MojoExecutionException Failed to add script. 129 */ 130 private void addScript() throws MojoExecutionException { 131 132 final URL scriptUrl = getClass().getResource("/js/" + m.getJavaScriptFileName()); 133 String scriptString; 134 try { 135 scriptString = IOUtils.toString(scriptUrl); 136 } catch (IOException ie) { 137 throw new MojoExecutionException("Failed to read " + scriptUrl, ie); 138 } 139 140 if (scriptString != null) { 141 scriptString = scriptString.replace("PROJECT_NAME", m.getProjectName().toLowerCase()); 142 scriptString = scriptString.replace("PROJECT_VERSION", m.getProjectVersion()); 143 scriptString = scriptString.replace("LATEST_JSON", m.getLatestJson()); 144 scriptString = scriptString.replace("DOCS_SITE", m.getDocsSite()); 145 scriptString = scriptString.replace("EOSL_JSON", m.getEoslJson()); 146 } else { 147 throw new MojoExecutionException(scriptUrl + " was empty"); 148 } 149 150 // The html.script parameter should probably take URLs. 151 // When local files are referenced, 152 // the DocBook XSL stylesheets do not copy the .js files. 153 // Instead the files must be copied to the output directories. 154 155 for (final String outputDirectory : outputDirectories) { 156 157 for (final String docName : m.getDocNames()) { 158 159 final File parent = FileUtils.getFile( 160 m.getDocbkxOutputDirectory(), "html", docName + outputDirectory); 161 final File scriptFile = new File(parent, m.getJavaScriptFileName()); 162 163 try { 164 FileUtils.writeStringToFile(scriptFile, scriptString, "UTF-8"); 165 } catch (IOException ie) { 166 throw new MojoExecutionException( 167 "Failed to write to " + scriptFile.getPath(), ie); 168 } 169 } 170 } 171 } 172 173 /** 174 * Edit build single-page and chunked HTML. 175 * 176 * <p> 177 * 178 * The HTML built by docbkx-tools does not currently include the following, 179 * which this method adds. 180 * 181 * <ul> 182 * <li>A DOCTYPE declaration (needed by Internet Explorer to interpret CSS</li> 183 * <li>A meta tag for controlling crawling and indexing</li> 184 * <li>JavaScript to call the SyntaxHighlighter brushes</li> 185 * <li>A favicon link</li> 186 * <li>A paragraph about logging issues with a link to JIRA</li> 187 * <li>JavaScript used by Google Analytics</li> 188 * </ul> 189 * 190 * @param htmlDir Directory under which to find HTML output 191 * @throws MojoExecutionException Something went wrong when updating HTML. 192 */ 193 final void editBuiltHtml(final String htmlDir) throws MojoExecutionException { 194 try { 195 HashMap<String, String> replacements = new HashMap<String, String>(); 196 197 String doctype = IOUtils.toString( 198 getClass().getResourceAsStream("/starthtml-doctype.txt"), "UTF-8"); 199 replacements.put("<html>", doctype); 200 201 // See https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag 202 String robots = "<head>" + System.getProperty("line.separator") 203 + IOUtils.toString(getClass().getResourceAsStream("/robots.txt"), "UTF-8"); 204 replacements.put("<head>", robots); 205 206 String favicon = IOUtils.toString( 207 getClass().getResourceAsStream("/endhead-favicon.txt"), "UTF-8"); 208 favicon = favicon.replace("FAVICON-LINK", m.getFaviconLink()); 209 replacements.put("</head>", favicon); 210 211 String linkToJira = getLinkToJira(); 212 213 String gascript = IOUtils.toString( 214 getClass().getResourceAsStream("/endbody-ga.txt"), "UTF-8"); 215 gascript = gascript.replace("ANALYTICS-ID", m.getGoogleAnalyticsId()); 216 replacements.put("</body>", linkToJira + "\n" + gascript); 217 218 HtmlUtils.updateHtml(htmlDir, replacements); 219 } catch (IOException e) { 220 throw new MojoExecutionException( 221 "Failed to update output HTML correctly: " + e.getMessage()); 222 } 223 } 224 225 /** 226 * Return a <p> containing a link to log a bug in JIRA, depending on the project. 227 * The string is not localized. 228 * 229 * @return <p> containing a link to log a bug in JIRA. 230 */ 231 private String getLinkToJira() { 232 String link = "<p> </p><div id=\"footer\"><p>Something wrong on this page? " 233 + "<a href=\"JIRA-URL\">Log a documentation bug.</a></p></div>"; 234 235 // https://confluence.atlassian.com/display/JIRA/Creating+Issues+via+direct+HTML+links 236 String jiraURL = "https://bugster.forgerock.org/jira/secure/CreateIssueDetails!init.jspa"; 237 238 if (m.getProjectName().equalsIgnoreCase("OpenAM")) { 239 jiraURL += "?pid=10000&components=10007&issuetype=1"; 240 } 241 if (m.getProjectName().equalsIgnoreCase("OpenDJ")) { 242 jiraURL += "?pid=10040&components=10132&issuetype=1"; 243 } 244 if (m.getProjectName().equalsIgnoreCase("OpenICF")) { 245 jiraURL += "?pid=10041&components=10170&issuetype=1"; 246 } 247 if (m.getProjectName().equalsIgnoreCase("OpenIDM")) { 248 jiraURL += "?pid=10020&components=10164&issuetype=1"; 249 } 250 if (m.getProjectName().equalsIgnoreCase("OpenIG")) { 251 jiraURL += "?pid=10060&components=10220&issuetype=1"; 252 } 253 if (m.getProjectName().equalsIgnoreCase("ForgeRock")) { // Just testing 254 jiraURL += "?pid=10010&issuetype=1"; 255 } 256 257 if (!jiraURL.contains("pid")) { 258 link = ""; 259 } else { 260 link = link.replaceFirst("JIRA-URL", jiraURL); 261 } 262 return link; 263 } 264}