Allow admins to define the default CSS for formatters

At the moment the default CSS is hard-coded and projects can override
it. Now administrators can override the default CSS for all projects.

Change-Id: I04ae0f55e1f8988d050cc8eb6b5adb61c8ddc84f
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/AsciidoctorFormatter.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/AsciidoctorFormatter.java
index 367dc7c..eb3d4ae 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/AsciidoctorFormatter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/AsciidoctorFormatter.java
@@ -91,12 +91,15 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         ByteStreams.copy(input, out);
         String html = out.toString(UTF_8.name());
+        String globalCss = util.getGlobalCss("asciidoctor");
         String projectCss = util.getCss(projectName, "asciidoctor");
         if (projectCfg.getBoolean(KEY_APPEND_CSS, true)) {
-          return util.insertCss(html, defaultCss, projectCss);
+          return util.insertCss(html,
+              MoreObjects.firstNonNull(globalCss, defaultCss), projectCss);
         } else {
           return util.insertCss(html,
-              MoreObjects.firstNonNull(projectCss, defaultCss));
+              MoreObjects.firstNonNull(projectCss,
+                  MoreObjects.firstNonNull(globalCss, defaultCss)));
         }
       }
     } finally {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/FormatterUtil.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/FormatterUtil.java
index 6447ea9..255876e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/FormatterUtil.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/FormatterUtil.java
@@ -17,6 +17,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
 
+import com.google.gerrit.extensions.annotations.PluginData;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -33,17 +34,24 @@
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 
+import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
 @Singleton
 public class FormatterUtil {
-  private final GitRepositoryManager repoManager;
   private final String pluginName;
+  private final File baseDir;
+  private final GitRepositoryManager repoManager;
 
   @Inject
   FormatterUtil(@PluginName String pluginName,
+      @PluginData File baseDir,
       GitRepositoryManager repoManager) {
     this.pluginName = pluginName;
+    this.baseDir = baseDir;
     this.repoManager = repoManager;
   }
 
@@ -61,6 +69,25 @@
   }
 
   /**
+   * Returns the CSS from the file
+   * "<review-site>/data/<plugin-name>/css/<name>.css".
+   *
+   * @param name the name of the CSS file without the ".css" file extension
+   * @return the CSS from the file; HTML characters are escaped;
+   *         <code>null</code> if the file doesn't exist
+   * @throws IOException thrown in case of an I/O Error while reading the CSS
+   *         file
+   */
+  public String getGlobalCss(String name) throws IOException {
+    Path p = Paths.get(baseDir.getAbsolutePath(), "css", name + ".css");
+    if (Files.exists(p)) {
+      byte[] css = Files.readAllBytes(p);
+      return escapeHtml(new String(css, UTF_8));
+    }
+    return null;
+  }
+
+  /**
    * Inserts the given CSS into the given HTML.
    *
    * @param html the HTML
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/MarkdownFormatter.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/MarkdownFormatter.java
index 1863d54..4f23ad9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/MarkdownFormatter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/formatter/MarkdownFormatter.java
@@ -22,7 +22,7 @@
 
 import com.googlesource.gerrit.plugins.xdocs.ConfigSection;
 
-import java.io.UnsupportedEncodingException;
+import java.io.IOException;
 
 public class MarkdownFormatter implements Formatter {
   public final static String NAME = "MARKDOWN";
@@ -40,7 +40,7 @@
 
   @Override
   public String format(String projectName, String revision,
-      ConfigSection globalCfg, String raw) throws UnsupportedEncodingException {
+      ConfigSection globalCfg, String raw) throws IOException {
     ConfigSection projectCfg =
         formatters.getFormatterConfig(globalCfg.getSubsection(), projectName);
     com.google.gerrit.server.documentation.MarkdownFormatter f =
@@ -48,18 +48,24 @@
     if (!globalCfg.getBoolean(KEY_ALLOW_HTML, false)) {
       f.suppressHtml();
     }
+    String globalCss = util.getGlobalCss("markdown");
     String projectCss = util.getCss(projectName, "markdown");
     if (projectCfg.getBoolean(KEY_APPEND_CSS, true)) {
-      // if f.setCss(css) is not invoked
+      // if there is no global CSS and f.setCss(null) is invoked
       // com.google.gerrit.server.documentation.MarkdownFormatter applies the
       // default CSS
+      f.setCss(globalCss);
       byte[] b = f.markdownToDocHtml(raw, UTF_8.name());
       return util.insertCss(new String(b, UTF_8), projectCss);
     } else {
-      // if there is no project-specific CSS and f.setCss(null) is invoked
-      // com.google.gerrit.server.documentation.MarkdownFormatter applies the
-      // default CSS
-      f.setCss(projectCss);
+      if (projectCss != null) {
+        f.setCss(projectCss);
+      } else {
+        // if there is no global CSS and f.setCss(null) is invoked
+        // com.google.gerrit.server.documentation.MarkdownFormatter applies the
+        // default CSS
+        f.setCss(globalCss);
+      }
       byte[] b = f.markdownToDocHtml(raw, UTF_8.name());
       return new String(b, UTF_8);
     }
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 3c7e004..aef00c5 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -240,3 +240,13 @@
 	*CANNOT* be overridden on project-level.
 
 	Default: `PLAIN_TEXT`
+
+<a id="globalDefaultCss">
+Global Default CSS
+------------------
+
+Gerrit administrators can override the built-in default CSS by
+providing CSS files in `<review-site>/data/@PLUGIN@/css/`:
+
+* `ASCIIDOCTOR`: `asciidoctor.css`
+* `MARKDOWN`: `markdown.css`