Make all Markdown extensions configurable

Allow site admins to selectively enable or disable Markdown
extensions.  This can help a site avoid creating documentation
that isn't compatible with other CommonMark parsers.

Change-Id: I0466b03ef213a398d79f943af2ddf95a7e0853e7
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
index 468b26e..911c4ef 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
@@ -98,7 +98,7 @@
           .setReader(reader)
           .setRootTree(rootTree)
           .build()
-          .toSoyHtml(GitilesMarkdown.parse(raw));
+          .toSoyHtml(GitilesMarkdown.parse(config, raw));
     } catch (RuntimeException | IOException err) {
       log.error(
           String.format(
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
index bd5c481..78d1cf8 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
@@ -167,9 +167,9 @@
       MarkdownFile srcFile)
       throws IOException {
     Map<String, Object> data = new HashMap<>();
-    data.putAll(buildNavbar(fmt, navFile));
+    data.putAll(buildNavbar(cfg, fmt, navFile));
 
-    Node doc = GitilesMarkdown.parse(srcFile.consumeContent());
+    Node doc = GitilesMarkdown.parse(cfg, srcFile.consumeContent());
     data.put("pageTitle", pageTitle(doc, srcFile));
     if (view.getType() != GitilesView.Type.ROOTED_DOC) {
       data.put("sourceUrl", GitilesView.show().copyFrom(view).toUrl());
@@ -190,11 +190,12 @@
     }
   }
 
-  private Map<String, Object> buildNavbar(MarkdownToHtml.Builder fmt, MarkdownFile navFile) {
+  private Map<String, Object> buildNavbar(
+      MarkdownConfig cfg, MarkdownToHtml.Builder fmt, MarkdownFile navFile) {
     Navbar navbar = new Navbar();
     if (navFile != null) {
       navbar.setFormatter(fmt.setFilePath(navFile.path).build());
-      navbar.setMarkdown(navFile.consumeContent());
+      navbar.setMarkdown(cfg, navFile.consumeContent());
     }
     return navbar.toSoyData();
   }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
index cc50048..e094931 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
@@ -14,7 +14,9 @@
 
 package com.google.gitiles.doc;
 
-import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.commonmark.Extension;
 import org.commonmark.ext.autolink.AutolinkExtension;
 import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
 import org.commonmark.ext.gfm.tables.TablesExtension;
@@ -24,28 +26,43 @@
 
 /** Parses Gitiles style CommonMark Markdown. */
 public class GitilesMarkdown {
-  private static final Parser PARSER =
-      Parser.builder()
-          .extensions(
-              ImmutableList.of(
-                  AutolinkExtension.create(),
-                  BlockNoteExtension.create(),
-                  GitilesHtmlExtension.create(),
-                  GitHubThematicBreakExtension.create(),
-                  MultiColumnExtension.create(),
-                  NamedAnchorExtension.create(),
-                  SmartQuotedExtension.create(),
-                  StrikethroughExtension.create(),
-                  TablesExtension.create(),
-                  TocExtension.create()))
-          .build();
-
-  public static Node parse(byte[] md) {
-    return parse(RawParseUtils.decode(md));
+  public static Node parse(MarkdownConfig cfg, byte[] md) {
+    return parse(cfg, RawParseUtils.decode(md));
   }
 
-  public static Node parse(String md) {
-    return PARSER.parse(md);
+  public static Node parse(MarkdownConfig cfg, String md) {
+    List<Extension> ext = new ArrayList<>();
+    if (cfg.autoLink) {
+      ext.add(AutolinkExtension.create());
+    }
+    if (cfg.blockNote) {
+      ext.add(BlockNoteExtension.create());
+    }
+    if (cfg.safeHtml) {
+      ext.add(GitilesHtmlExtension.create());
+    }
+    if (cfg.ghThematicBreak) {
+      ext.add(GitHubThematicBreakExtension.create());
+    }
+    if (cfg.multiColumn) {
+      ext.add(MultiColumnExtension.create());
+    }
+    if (cfg.namedAnchor) {
+      ext.add(NamedAnchorExtension.create());
+    }
+    if (cfg.smartQuote) {
+      ext.add(SmartQuotedExtension.create());
+    }
+    if (cfg.strikethrough) {
+      ext.add(StrikethroughExtension.create());
+    }
+    if (cfg.tables) {
+      ext.add(TablesExtension.create());
+    }
+    if (cfg.toc) {
+      ext.add(TocExtension.create());
+    }
+    return Parser.builder().extensions(ext).build().parse(md);
   }
 
   private GitilesMarkdown() {}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownConfig.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownConfig.java
index 5737d85..79ddbb6 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownConfig.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownConfig.java
@@ -41,6 +41,17 @@
   final int imageLimit;
   final String analyticsId;
 
+  final boolean autoLink;
+  final boolean blockNote;
+  final boolean ghThematicBreak;
+  final boolean multiColumn;
+  final boolean namedAnchor;
+  final boolean safeHtml;
+  final boolean smartQuote;
+  final boolean strikethrough;
+  final boolean tables;
+  final boolean toc;
+
   private final boolean allowAnyIFrame;
   private final ImmutableList<String> allowIFrame;
 
@@ -50,7 +61,22 @@
     imageLimit = cfg.getInt("markdown", "imageLimit", IMAGE_LIMIT);
     analyticsId = Strings.emptyToNull(cfg.getString("google", null, "analyticsId"));
 
-    String[] f = cfg.getStringList("markdown", null, "allowiframe");
+    boolean githubFlavor = cfg.getBoolean("markdown", "githubFlavor", true);
+    autoLink = cfg.getBoolean("markdown", "autolink", githubFlavor);
+    blockNote = cfg.getBoolean("markdown", "blocknote", false);
+    ghThematicBreak = cfg.getBoolean("markdown", "ghthematicbreak", githubFlavor);
+    multiColumn = cfg.getBoolean("markdown", "multicolumn", false);
+    namedAnchor = cfg.getBoolean("markdown", "namedanchor", false);
+    safeHtml = cfg.getBoolean("markdown", "safehtml", githubFlavor);
+    smartQuote = cfg.getBoolean("markdown", "smartquote", false);
+    strikethrough = cfg.getBoolean("markdown", "strikethrough", githubFlavor);
+    tables = cfg.getBoolean("markdown", "tables", githubFlavor);
+    toc = cfg.getBoolean("markdown", "toc", true);
+
+    String[] f = {};
+    if (safeHtml) {
+      f = cfg.getStringList("markdown", null, "allowiframe");
+    }
     allowAnyIFrame = f.length == 1 && StringUtils.toBooleanOrNull(f[0]) == Boolean.TRUE;
     if (allowAnyIFrame) {
       allowIFrame = ImmutableList.of();
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java
index 50e0b61..a4581f1 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java
@@ -42,9 +42,9 @@
     return this;
   }
 
-  Navbar setMarkdown(byte[] md) {
+  Navbar setMarkdown(MarkdownConfig cfg, byte[] md) {
     if (md != null && md.length > 0) {
-      parse(RawParseUtils.decode(md));
+      parse(cfg, RawParseUtils.decode(md));
     }
     return this;
   }
@@ -73,8 +73,8 @@
     }
   }
 
-  private void parse(String markdown) {
-    node = GitilesMarkdown.parse(markdown);
+  private void parse(MarkdownConfig cfg, String markdown) {
+    node = GitilesMarkdown.parse(cfg, markdown);
 
     extractSiteTitle();
     extractRefLinks(markdown);
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
index 9046e0c..27e10cc 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/TestGitilesAccess.java
@@ -78,6 +78,10 @@
       @Override
       public Config getConfig() {
         Config config = new Config();
+        config.setBoolean("markdown", null, "blocknote", true);
+        config.setBoolean("markdown", null, "multicolumn", true);
+        config.setBoolean("markdown", null, "namedanchor", true);
+        config.setBoolean("markdown", null, "smartquote", true);
         config.setStringList(
             "gitiles", null, "allowOriginRegex", ImmutableList.of("http://localhost"));
         return config;