Allow navar.md to enable Markdown extensions A site's pages may require specific Gitiles extensions to Markdown. For example the Gitiles Markdown documentation uses its extensions to demo how those extensions are formatted. Use a named link in navbar.md as metadata to activate extensions that may have been disabled by the site's configuration, e.g.: [home]: / [logo]: /images/logo.png [extensions]: blocknote, multicolumn Change-Id: Id43e5d103bbd6b31ebfcb2565c0600d45075bc16
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 78d1cf8..001b3b2 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
@@ -129,8 +129,9 @@ .setRequestUri(req.getRequestURI()) .setReader(reader) .setRootTree(root); + Navbar navbar = createNavbar(cfg, fmt, navmd); res.setHeader(HttpHeaders.ETAG, curEtag); - showDoc(req, res, view, cfg, fmt, navmd, srcmd); + showDoc(req, res, view, fmt, navbar, srcmd); } } @@ -161,14 +162,14 @@ HttpServletRequest req, HttpServletResponse res, GitilesView view, - MarkdownConfig cfg, MarkdownToHtml.Builder fmt, - MarkdownFile navFile, + Navbar navbar, MarkdownFile srcFile) throws IOException { Map<String, Object> data = new HashMap<>(); - data.putAll(buildNavbar(cfg, fmt, navFile)); + data.putAll(navbar.toSoyData()); + MarkdownConfig cfg = navbar.getConfig(); Node doc = GitilesMarkdown.parse(cfg, srcFile.consumeContent()); data.put("pageTitle", pageTitle(doc, srcFile)); if (view.getType() != GitilesView.Type.ROOTED_DOC) { @@ -182,7 +183,10 @@ try (OutputStream out = startRenderCompressedStreamingHtml(req, res, SOY_TEMPLATE, data)) { Writer w = newWriter(out, res); - fmt.setFilePath(srcFile.path).build().renderToHtml(new StreamHtmlBuilder(w), doc); + fmt.setConfig(cfg) + .setFilePath(srcFile.path) + .build() + .renderToHtml(new StreamHtmlBuilder(w), doc); w.flush(); } catch (RuntimeIOException e) { Throwables.throwIfInstanceOf(e.getCause(), IOException.class); @@ -190,14 +194,15 @@ } } - private Map<String, Object> buildNavbar( - MarkdownConfig cfg, MarkdownToHtml.Builder fmt, MarkdownFile navFile) { - Navbar navbar = new Navbar(); + private static Navbar createNavbar( + MarkdownConfig cfg, MarkdownToHtml.Builder fmt, @Nullable MarkdownFile navFile) { + Navbar navbar = new Navbar().setConfig(cfg); if (navFile != null) { - navbar.setFormatter(fmt.setFilePath(navFile.path).build()); - navbar.setMarkdown(cfg, navFile.consumeContent()); + navbar + .setFormatter(fmt.setFilePath(navFile.path).build()) + .setMarkdown(navFile.consumeContent()); } - return navbar.toSoyData(); + return navbar; } private static String pageTitle(Node doc, MarkdownFile srcFile) {
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 1982e5a..b4add94 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
@@ -16,6 +16,7 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import java.util.Set; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.util.StringUtils; @@ -85,6 +86,31 @@ } } + private MarkdownConfig(MarkdownConfig p, Set<String> enable, Set<String> disable) { + render = p.render; + inputLimit = p.inputLimit; + imageLimit = p.imageLimit; + analyticsId = p.analyticsId; + + autoLink = on("autolink", p.autoLink, enable, disable); + blockNote = on("blocknote", p.blockNote, enable, disable); + ghThematicBreak = on("ghthematicbreak", p.ghThematicBreak, enable, disable); + multiColumn = on("multicolumn", p.multiColumn, enable, disable); + namedAnchor = on("namedanchor", p.namedAnchor, enable, disable); + safeHtml = on("safehtml", p.safeHtml, enable, disable); + smartQuote = on("smartquote", p.smartQuote, enable, disable); + strikethrough = on("strikethrough", p.strikethrough, enable, disable); + tables = on("tables", p.tables, enable, disable); + toc = on("toc", p.toc, enable, disable); + + allowAnyIFrame = safeHtml ? p.allowAnyIFrame : false; + allowIFrame = safeHtml ? p.allowIFrame : ImmutableList.of(); + } + + private static boolean on(String key, boolean val, Set<String> enable, Set<String> disable) { + return enable.contains(key) ? true : disable.contains(key) ? false : val; + } + boolean isIFrameAllowed(String src) { if (allowAnyIFrame) { return true; @@ -96,4 +122,8 @@ } return false; } + + MarkdownConfig copyWithExtensions(Set<String> enable, Set<String> disable) { + return new MarkdownConfig(this, enable, disable); + } }
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 a4581f1..a0f05bb 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
@@ -14,21 +14,27 @@ package com.google.gitiles.doc; +import com.google.common.base.CharMatcher; +import com.google.common.base.Splitter; import com.google.gitiles.doc.html.HtmlBuilder; import com.google.template.soy.shared.restricted.EscapingConventions.FilterImageDataUri; import com.google.template.soy.shared.restricted.Sanitizers; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.stream.Collectors.toSet; import org.commonmark.node.Heading; import org.commonmark.node.Node; import org.eclipse.jgit.util.RawParseUtils; class Navbar { - private static final Pattern REF_LINK = - Pattern.compile("^\\[(logo|home)\\]:\\s*(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + private static final Pattern META_LINK = + Pattern.compile( + "^\\[(logo|home|extensions)\\]:\\s*(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + private MarkdownConfig cfg; private MarkdownToHtml fmt; private Node node; private String siteTitle; @@ -37,14 +43,23 @@ Navbar() {} + MarkdownConfig getConfig() { + return cfg; + } + + Navbar setConfig(MarkdownConfig cfg) { + this.cfg = cfg; + return this; + } + Navbar setFormatter(MarkdownToHtml html) { this.fmt = html; return this; } - Navbar setMarkdown(MarkdownConfig cfg, byte[] md) { + Navbar setMarkdown(byte[] md) { if (md != null && md.length > 0) { - parse(cfg, RawParseUtils.decode(md)); + parse(RawParseUtils.decode(md)); } return this; } @@ -73,11 +88,10 @@ } } - private void parse(MarkdownConfig cfg, String markdown) { + private void parse(String markdown) { + extractMetadata(markdown); node = GitilesMarkdown.parse(cfg, markdown); - extractSiteTitle(); - extractRefLinks(markdown); } private void extractSiteTitle() { @@ -93,8 +107,8 @@ } } - private void extractRefLinks(String markdown) { - Matcher m = REF_LINK.matcher(markdown); + private void extractMetadata(String markdown) { + Matcher m = META_LINK.matcher(markdown); while (m.find()) { String key = m.group(1).toLowerCase(); String url = m.group(2).trim(); @@ -105,7 +119,29 @@ case "home": homeUrl = url; break; + case "extensions": + Set<String> names = splitExtensionNames(url); + cfg = cfg.copyWithExtensions(enabled(names), disabled(names)); + break; } } } + + private static Set<String> splitExtensionNames(String url) { + return Splitter.on(CharMatcher.whitespace().or(CharMatcher.is(','))) + .trimResults() + .omitEmptyStrings() + .splitToList(url) + .stream() + .map(String::toLowerCase) + .collect(toSet()); + } + + private static Set<String> enabled(Set<String> names) { + return names.stream().filter(n -> !n.startsWith("!")).collect(toSet()); + } + + private static Set<String> disabled(Set<String> names) { + return names.stream().filter(n -> n.startsWith("!")).map(n -> n.substring(1)).collect(toSet()); + } }
diff --git a/navbar.md b/navbar.md index 608531c..9f8ac26 100644 --- a/navbar.md +++ b/navbar.md
@@ -2,3 +2,5 @@ * [Markdown](/Documentation/markdown.md) * [Configuration](/Documentation/config.md) * [Developers](/Documentation/developer-guide.md) + +[extensions]: blocknote, multicolumn, namedanchor, smartquote, toc