Make it configurable which mime types should be handled as project doc

At the moment it is hard-coded that "*.md" files are handled as
Markdown project documentation. Instead one can now define in the
global plugin configuration which mime types should be handled by the
Markdown formatter:

[formatter "MARKDOWN"]
  mimeType = text/x-markdown

A mapping from file extensions to mime types already exists and
doesn't need to be reimplemented, instead just rely on the mime type.

Multiple mime types may be specified for a formatter. A formatter for
which no mime type is specified is disabled.

An init step creates the basic plugin configuration so that the
default mime type for a formatter is automatically set.

At the moment there is only a single formatter, the formatter for
Markdown, but follow-up changes will add formatters for other
syntaxes, such as Asciidoc.

Change-Id: I0f89463f89baaac962876a166e72ff8e26865280
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/BUCK b/BUCK
index 7a43f6f..339a27c 100644
--- a/BUCK
+++ b/BUCK
@@ -10,6 +10,7 @@
     'Gerrit-ApiVersion: 2.11-SNAPSHOT',
     'Gerrit-HttpModule: com.googlesource.gerrit.plugins.xdocs.HttpModule',
     'Gerrit-Module: com.googlesource.gerrit.plugins.xdocs.Module',
+    'Gerrit-InitStep: com.googlesource.gerrit.plugins.xdocs.XDocInit',
   ],
 )
 
diff --git a/pom.xml b/pom.xml
index 9d7d24c..6776eb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,7 @@
               <Gerrit-PluginName>xdocs</Gerrit-PluginName>
               <Gerrit-HttpModule>com.googlesource.gerrit.plugins.xdocs.HttpModule</Gerrit-HttpModule>
               <Gerrit-Module>com.googlesource.gerrit.plugins.xdocs.Module</Gerrit-Module>
+              <Gerrit-InitStep>com.googlesource.gerrit.plugins.xdocs.XDocInit</Gerrit-InitStep>
 
               <Implementation-Vendor>Gerrit Code Review</Implementation-Vendor>
               <Implementation-URL>http://code.google.com/p/gerrit/</Implementation-URL>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocFileWebLink.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocFileWebLink.java
index 32fb4e0..cd5d676 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocFileWebLink.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocFileWebLink.java
@@ -18,6 +18,8 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.webui.FileWebLink;
 import com.google.gerrit.httpd.resources.Resource;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.inject.Inject;
@@ -31,8 +33,11 @@
       GitRepositoryManager repoManager,
       @Named(XDocLoader.Module.X_DOC_RESOURCES) LoadingCache<String, Resource> cache,
       XDocProjectConfig.Factory cfgFactory,
-      ProjectCache projectCache) {
-    super(pluginName, repoManager, cache, cfgFactory, projectCache);
+      ProjectCache projectCache,
+      FileTypeRegistry fileTypeRegistry,
+      PluginConfigFactory pluginCfgFactory) {
+    super(pluginName, repoManager, cache, cfgFactory, projectCache,
+        fileTypeRegistry, pluginCfgFactory);
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocGlobalConfig.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocGlobalConfig.java
index df6ff5a..062a683 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocGlobalConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocGlobalConfig.java
@@ -14,11 +14,17 @@
 
 package com.googlesource.gerrit.plugins.xdocs;
 
+import eu.medsea.mimeutil.MimeType;
+
 import org.eclipse.jgit.lib.Config;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class XDocGlobalConfig {
   private static final String SECTION_FORMATTER = "formatter";
   private static final String KEY_ALLOW_HTML = "allowHtml";
+  private static final String KEY_MIME_TYPE = "mimeType";
 
   enum Formatter {
     MARKDOWN;
@@ -34,4 +40,20 @@
     return cfg.getBoolean(SECTION_FORMATTER, formatter.name(),
         KEY_ALLOW_HTML, false);
   }
+
+  Map<MimeType, Formatter> getMimeTypes() {
+    Map<MimeType, Formatter> mimeTypes = new HashMap<>();
+    for (Formatter f : Formatter.values()) {
+      for (String mimeType :
+          cfg.getStringList(SECTION_FORMATTER, f.name(), KEY_MIME_TYPE)) {
+        mimeTypes.put(new MimeType(mimeType), f);
+      }
+    }
+    return mimeTypes;
+  }
+
+  static void initialize(Config cfg) {
+    cfg.setString(SECTION_FORMATTER, Formatter.MARKDOWN.name(), KEY_MIME_TYPE,
+        "text/x-markdown");
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocInit.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocInit.java
new file mode 100644
index 0000000..25cd2a2
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocInit.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.xdocs;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.pgm.init.api.ConsoleUI;
+import com.google.gerrit.pgm.init.api.InitStep;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+
+import java.io.File;
+
+@Singleton
+public class XDocInit implements InitStep {
+  private final String pluginName;
+  private final SitePaths sitePaths;
+  private final ConsoleUI ui;
+
+  @Inject
+  XDocInit(@PluginName String pluginName, SitePaths sitePaths,
+      ConsoleUI ui) {
+    this.pluginName = pluginName;
+    this.sitePaths = sitePaths;
+    this.ui = ui;
+  }
+
+  @Override
+  public void run() throws Exception {
+    File pluginConfig = new File(sitePaths.etc_dir, pluginName + ".config");
+    if (!pluginConfig.exists()) {
+      ui.message("\n");
+      ui.header("%s plugin", pluginName);
+
+      FileBasedConfig cfg = new FileBasedConfig(pluginConfig, FS.DETECTED);
+      XDocGlobalConfig.initialize(cfg);
+      cfg.save();
+
+      ui.message("Initialized %s plugin: %s", pluginName,
+          pluginConfig.getAbsolutePath());
+    }
+  }
+
+  @Override
+  public void postRun() throws Exception {
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
index ab56e70..c01cd8b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
@@ -22,6 +22,7 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.hash.Hashing;
 import com.google.common.net.HttpHeaders;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -30,6 +31,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.GetHead;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -43,6 +45,8 @@
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 
+import com.googlesource.gerrit.plugins.xdocs.XDocGlobalConfig.Formatter;
+
 import eu.medsea.mimeutil.MimeType;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -58,6 +62,7 @@
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 
 import java.io.IOException;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import javax.servlet.http.HttpServlet;
@@ -78,6 +83,8 @@
   private final LoadingCache<String, Resource> docCache;
   private final FileTypeRegistry fileTypeRegistry;
   private final XDocProjectConfig.Factory cfgFactory;
+  private final String pluginName;
+  private final PluginConfigFactory pluginCfgFactory;
 
   @Inject
   XDocServlet(
@@ -88,7 +95,9 @@
       GitRepositoryManager repoManager,
       @Named(XDocLoader.Module.X_DOC_RESOURCES) LoadingCache<String, Resource> cache,
       FileTypeRegistry fileTypeRegistry,
-      XDocProjectConfig.Factory cfgFactory) {
+      XDocProjectConfig.Factory cfgFactory,
+      @PluginName String pluginName,
+      PluginConfigFactory pluginCfgFactory) {
     this.db = db;
     this.projectControlFactory = projectControlFactory;
     this.projectCache = projectCache;
@@ -97,6 +106,8 @@
     this.docCache = cache;
     this.fileTypeRegistry = fileTypeRegistry;
     this.cfgFactory = cfgFactory;
+    this.pluginName = pluginName;
+    this.pluginCfgFactory = pluginCfgFactory;
   }
 
   @Override
@@ -119,8 +130,11 @@
       res.sendRedirect(getRedirectUrl(req, key, cfg));
       return;
     }
+    XDocGlobalConfig pluginCfg =
+        new XDocGlobalConfig(pluginCfgFactory.getGlobalPluginConfig(pluginName));
     MimeType mimeType = fileTypeRegistry.getMimeType(key.file, null);
-    if (!key.file.endsWith(".md")
+    Map<MimeType, Formatter> mimeTypes = pluginCfg.getMimeTypes();
+    if (!mimeTypes.keySet().contains(mimeType)
         && !("image".equals(mimeType.getMediaType())
             && fileTypeRegistry.isSafeInline(mimeType))) {
       Resource.NOT_FOUND.send(req, res);
@@ -177,7 +191,7 @@
         }
 
         Resource rsc;
-        if (key.file.endsWith(".md")) {
+        if (mimeTypes.get(mimeType) != null) {
           rsc = docCache.getUnchecked(
               (new XDocResourceKey(key.project, key.file, revId)).asString());
         } else if ("image".equals(mimeType.getMediaType())) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
index 1838b0f..e347516 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.webui.ProjectWebLink;
 import com.google.gerrit.httpd.resources.Resource;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
@@ -28,6 +30,8 @@
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 
+import eu.medsea.mimeutil.MimeType;
+
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -46,6 +50,8 @@
   private final LoadingCache<String, Resource> docCache;
   private final XDocProjectConfig.Factory cfgFactory;
   private final ProjectCache projectCache;
+  private final FileTypeRegistry fileTypeRegistry;
+  private final PluginConfigFactory pluginCfgFactory;
 
   @Inject
   XDocWebLink(
@@ -53,12 +59,16 @@
       GitRepositoryManager repoManager,
       @Named(XDocLoader.Module.X_DOC_RESOURCES) LoadingCache<String, Resource> cache,
       XDocProjectConfig.Factory cfgFactory,
-      ProjectCache projectCache) {
+      ProjectCache projectCache,
+      FileTypeRegistry fileTypeRegistry,
+      PluginConfigFactory pluginCfgFactory) {
     this.pluginName = pluginName;
     this.repoManager = repoManager;
     this.docCache = cache;
     this.cfgFactory = cfgFactory;
     this.projectCache = projectCache;
+    this.fileTypeRegistry = fileTypeRegistry;
+    this.pluginCfgFactory = pluginCfgFactory;
   }
 
   @Override
@@ -84,7 +94,10 @@
 
   public String getPatchUrl(String projectName, String revision,
       String fileName) {
-    if (!fileName.endsWith(".md")) {
+    XDocGlobalConfig pluginCfg =
+        new XDocGlobalConfig(pluginCfgFactory.getGlobalPluginConfig(pluginName));
+    MimeType mimeType = fileTypeRegistry.getMimeType(fileName, null);
+    if (!pluginCfg.getMimeTypes().keySet().contains(mimeType)) {
       return null;
     }
 
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 7b71bf4..3ec161f 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -30,11 +30,20 @@
 The global configuration of the @PLUGIN@ plugin is done in the
 `$site_path/etc/@PLUGIN@.config` file.
 
+The plugin contains an init step that creates the initial plugin
+configuration.
+
 ```
   [formatter "MARKDOWN"]
-    allowHtml = false
+    mimeType = text/x-markdown
 ```
 
+<a id="formatterMimeType">
+formatter.<formatter>.mimeType
+:	The mime type of files that you be rendered by this formatter.
+
+	Multiple mime types may be specified for a formatter.
+
 <a id="formatterAllowHtml">
 formatter.<formatter>.allowHtml
 :	Whether inline HTML blocks and inline HTML tags are allowed for