From a7317acec01cde855a9f9f3d2da3dcc49d89aa86 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Fri, 25 Oct 2013 08:39:56 -0400 Subject: [PATCH] Support for intra-Markdown linking using [[WikiLinks]] syntax (issue-324) --- src/main/java/com/gitblit/wicket/pages/DocsPage.java | 69 ++++++++++++++--- src/main/java/com/gitblit/wicket/pages/RepositoryPage.java | 31 +++++++ src/main/java/com/gitblit/wicket/pages/MarkdownPage.java | 32 +++++++- src/main/java/com/gitblit/wicket/pages/SummaryPage.java | 4 src/main/resources/gitblit.css | 17 ++-- releases.moxie | 1 src/main/java/com/gitblit/utils/MarkdownUtils.java | 14 +++ src/main/java/com/gitblit/wicket/GitBlitWebApp.properties | 3 src/main/java/com/gitblit/wicket/pages/DocsPage.html | 25 +++++- 9 files changed, 163 insertions(+), 33 deletions(-) diff --git a/releases.moxie b/releases.moxie index b2968a1..e9ec642 100644 --- a/releases.moxie +++ b/releases.moxie @@ -31,6 +31,7 @@ - Added option to render Markdown commit messages (issue-203) - Added setting to control creating a repository as --shared on Unix servers (issue-263) - Added raw links to the commit, commitdiff, and compare pages (issue-319) + - Support intradocument linking in Markdown content using [[WikiLinks]] syntax (issue-324) - Added setting to globally disable anonymous pushes in the receive pack - Added a normalized diffstat display to the commit, commitdiff, and compare pages dependencyChanges: diff --git a/src/main/java/com/gitblit/utils/MarkdownUtils.java b/src/main/java/com/gitblit/utils/MarkdownUtils.java index f9c07fb..1595f65 100644 --- a/src/main/java/com/gitblit/utils/MarkdownUtils.java +++ b/src/main/java/com/gitblit/utils/MarkdownUtils.java @@ -22,6 +22,7 @@ import java.io.StringWriter; import org.apache.commons.io.IOUtils; +import org.pegdown.LinkRenderer; import org.pegdown.PegDownProcessor; /** @@ -55,8 +56,19 @@ * @throws java.text.ParseException */ public static String transformMarkdown(String markdown) { + return transformMarkdown(markdown, null); + } + + /** + * Returns the html version of the markdown source text. + * + * @param markdown + * @return html version of markdown text + * @throws java.text.ParseException + */ + public static String transformMarkdown(String markdown, LinkRenderer linkRenderer) { PegDownProcessor pd = new PegDownProcessor(ALL); - String html = pd.markdownToHtml(markdown); + String html = pd.markdownToHtml(markdown, linkRenderer == null ? new LinkRenderer() : linkRenderer); return html; } diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 4b19f9b..526093a 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -503,4 +503,5 @@ gb.noActivityToday = there has been no activity today gb.anonymousUser= anonymous gb.commitMessageRenderer = commit message renderer -gb.diffStat = {0} insertions & {1} deletions \ No newline at end of file +gb.diffStat = {0} insertions & {1} deletions +gb.home = home \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.html b/src/main/java/com/gitblit/wicket/pages/DocsPage.html index ad93000..7f1e64e 100644 --- a/src/main/java/com/gitblit/wicket/pages/DocsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.html @@ -6,10 +6,26 @@ <body> <wicket:extend> - - <!-- header --> + +<div wicket:id="docs"></div> + +<wicket:fragment wicket:id="indexFragment"> + <ul class="nav nav-tabs"> + <li class="active"><a data-toggle="tab" href="#home"><wicket:message key="gb.home">[home]</wicket:message></a></li> + <li><a data-toggle="tab" href="#pages"><wicket:message key="gb.pages">[pages]</wicket:message></a></li> + </ul> + <div class="tab-content"> + <div id="home" wicket:id="index" class="tab-pane active"></div> + <div id="pages" wicket:id="documents" class="tab-pane"></div> + </div> +</wicket:fragment> + +<wicket:fragment wicket:id="noIndexFragment"> <div style="margin-top:5px;" class="header"><i class="icon-book" style="vertical-align: middle;"></i> <b><span wicket:id="header">[header]</span></b></div> - + <div wicket:id="documents"></div> +</wicket:fragment> + +<wicket:fragment wicket:id="documentsFragment"> <!-- documents --> <table style="width:100%" class="pretty"> <tr wicket:id="document"> @@ -22,7 +38,8 @@ </span> </td> </tr> - </table> + </table> +</wicket:fragment> </wicket:extend> </body> </html> \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java index eea9595..58471ef 100644 --- a/src/main/java/com/gitblit/wicket/pages/DocsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java @@ -15,21 +15,27 @@ */ package com.gitblit.wicket.pages; +import java.util.Arrays; import java.util.List; +import org.apache.wicket.Component; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.GitBlit; import com.gitblit.Keys; import com.gitblit.models.PathModel; import com.gitblit.utils.ByteFormat; import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.MarkdownUtils; +import com.gitblit.utils.StringUtils; import com.gitblit.wicket.CacheControl; import com.gitblit.wicket.CacheControl.LastModified; import com.gitblit.wicket.WicketUtils; @@ -42,14 +48,51 @@ super(params); Repository r = getRepository(); + RevCommit head = JGitUtils.getCommit(r, null); List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions); List<PathModel> paths = JGitUtils.getDocuments(r, extensions); + String doc = null; + String markdown = null; + String html = null; + + List<String> roots = Arrays.asList("home"); + + // try to find a custom index/root page + for (PathModel path : paths) { + String name = path.name.toLowerCase(); + if (name.indexOf('.') > -1) { + name = name.substring(0, name.lastIndexOf('.')); + } + if (roots.contains(name)) { + doc = path.name; + break; + } + } + + if (!StringUtils.isEmpty(doc)) { + // load the document + String [] encodings = GitBlit.getEncodings(); + markdown = JGitUtils.getStringContent(r, head.getTree(), doc, encodings); + html = MarkdownUtils.transformMarkdown(markdown, getLinkRenderer()); + } + + Fragment fragment = null; + if (StringUtils.isEmpty(html)) { + // no custom index/root, use the standard document list + fragment = new Fragment("docs", "noIndexFragment", this); + fragment.add(new Label("header", getString("gb.docs"))); + } else { + // custom index/root, use tabbed ui of index/root and document list + fragment = new Fragment("docs", "indexFragment", this); + Component content = new Label("index", html).setEscapeModelStrings(false); + fragment.add(content); + } + + // document list + final String id = getBestCommitId(head); final ByteFormat byteFormat = new ByteFormat(); - - add(new Label("header", getString("gb.docs"))); - - // documents list + Fragment docs = new Fragment("documents", "documentsFragment", this); ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths); DataView<PathModel> pathsView = new DataView<PathModel>("document", pathsDp) { private static final long serialVersionUID = 1L; @@ -60,23 +103,25 @@ PathModel entry = item.getModelObject(); item.add(WicketUtils.newImage("docIcon", "file_world_16x16.png")); item.add(new Label("docSize", byteFormat.format(entry.size))); - item.add(new LinkPanel("docName", "list", entry.name, BlobPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path))); + item.add(new LinkPanel("docName", "list", entry.name, MarkdownPage.class, WicketUtils + .newPathParameter(repositoryName, id, entry.path))); // links - item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path))); + item.add(new BookmarkablePageLink<Void>("view", MarkdownPage.class, WicketUtils + .newPathParameter(repositoryName, id, entry.path))); item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path))); + .newPathParameter(repositoryName, id, entry.path))); item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path))); + .newPathParameter(repositoryName, id, entry.path))); item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path))); + .newPathParameter(repositoryName, id, entry.path))); WicketUtils.setAlternatingBackground(item, counter); counter++; } }; - add(pathsView); + docs.add(pathsView); + fragment.add(docs); + add(fragment); } @Override diff --git a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java index 188a5b4..e0c85cf 100644 --- a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java +++ b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java @@ -16,6 +16,7 @@ package com.gitblit.wicket.pages; import java.text.MessageFormat; +import java.util.List; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; @@ -25,6 +26,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.GitBlit; +import com.gitblit.Keys; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; @@ -38,11 +40,32 @@ public MarkdownPage(PageParameters params) { super(params); - final String markdownPath = WicketUtils.getPath(params); + final String path = WicketUtils.getPath(params).replace("%2f", "/").replace("%2F", "/"); Repository r = getRepository(); RevCommit commit = JGitUtils.getCommit(r, objectId); String [] encodings = GitBlit.getEncodings(); + List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions); + + // Read raw markdown content and transform it to html + String markdownPath = path; + String markdownText = JGitUtils.getStringContent(r, commit.getTree(), path, encodings); + if (StringUtils.isEmpty(markdownText)) { + String name = path; + if (path.indexOf('.') > -1) { + name = path.substring(0, path.lastIndexOf('.')); + } + + for (String ext : extensions) { + String checkName = name + "." + ext; + markdownText = JGitUtils.getStringContent(r, commit.getTree(), checkName, encodings); + if (!StringUtils.isEmpty(markdownText)) { + // found it + markdownPath = path; + break; + } + } + } // markdown page links add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class, @@ -54,13 +77,14 @@ add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath))); - // Read raw markdown content and transform it to html - String markdownText = JGitUtils.getStringContent(r, commit.getTree(), markdownPath, encodings); String htmlText; try { - htmlText = MarkdownUtils.transformMarkdown(markdownText); + htmlText = MarkdownUtils.transformMarkdown(markdownText, getLinkRenderer()); } catch (Exception e) { logger.error("failed to transform markdown", e); + if (markdownText == null) { + markdownText = String.format("Markdown document <b>%1$s</b> not found in <em>%2$s</em>", markdownPath, repositoryName); + } markdownText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", getString("gb.error"), getString("gb.markdownFailure"), markdownText); htmlText = StringUtils.breakLinesForHtml(markdownText); } diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java index 2df0a0e..3b1d296 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java @@ -16,6 +16,8 @@ package com.gitblit.wicket.pages; import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; @@ -43,6 +45,8 @@ import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.pegdown.LinkRenderer; +import org.pegdown.ast.WikiLinkNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -644,6 +648,33 @@ return isOwner; } + /** + * Returns a Pegdown/Markdown link renderer which renders WikiLinks. + * + * @return a link renderer + */ + protected LinkRenderer getLinkRenderer() { + RevCommit head = JGitUtils.getCommit(r, "HEAD"); + final String id = getBestCommitId(head); + LinkRenderer renderer = new LinkRenderer() { + @Override + public Rendering render(WikiLinkNode node) { + try { + String path = URLEncoder.encode(node.getText().replace(' ', '-'), "UTF-8"); + String name = node.getText(); + if (name.indexOf('/') > -1) { + name = name.substring(name.lastIndexOf('/') + 1); + } + String url = urlFor(MarkdownPage.class, WicketUtils.newPathParameter(repositoryName, id, path)).toString(); + return new Rendering(url, name); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(); + } + } + }; + return renderer; + } + private class SearchForm extends SessionlessForm<Void> implements Serializable { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java index 0a13837..827e079 100644 --- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java @@ -167,7 +167,7 @@ String [] encodings = GitBlit.getEncodings(); markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings); if (isMarkdown) { - htmlText = MarkdownUtils.transformMarkdown(markdownText); + htmlText = MarkdownUtils.transformMarkdown(markdownText, getLinkRenderer()); } else { htmlText = MarkdownUtils.transformPlainText(markdownText); } @@ -181,7 +181,7 @@ if (StringUtils.isEmpty(htmlText)) { add(new Label("readme").setVisible(false)); } else { - Fragment fragment = new Fragment("readme", isMarkdown ? "markdownPanel" : "plaintextPanel"); + Fragment fragment = new Fragment("readme", isMarkdown ? "markdownPanel" : "plaintextPanel", this); fragment.add(new Label("readmeFile", readme)); // Add the html to the page Component content = new Label("readmeContent", htmlText).setEscapeModelStrings(false); diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css index 23f4312..53113ac 100644 --- a/src/main/resources/gitblit.css +++ b/src/main/resources/gitblit.css @@ -490,7 +490,6 @@ border:0px; padding: 0; line-height: 1.35em; - vertical-align:top; } table { @@ -821,7 +820,6 @@ padding: 3px; border: 1px solid #ddd; border-bottom: 0; - border-radius: 3px 3px 0 0; font-weight: bold; font-family: Helvetica,arial,freesans,clean,sans-serif; } @@ -839,9 +837,6 @@ margin:0 0 2px; padding:7px 14px; border:1px solid #ddd; - border-radius: 3px; - -webkit-border-radius:3px; - -moz-border-radius:3px;border-radius:3px; } div.header a, div.commitHeader a { @@ -1512,9 +1507,13 @@ li.L7, li.L9 { background: #fafafa !important; } +div.markdown { + max-width: 900px; +} + div.markdown pre { - background-color: #F5F5F5; - border: 1px solid rgba(0, 0, 0, 0.15); + background-color: rgb(251, 251, 251); + border: 1px solid rgb(221, 221, 221); border-radius: 4px 4px 4px 4px; display: block; font-size: 12px; @@ -1531,8 +1530,8 @@ } div.markdown code { - background-color: #ffffe0; - border: 1px solid orange; + background-color: rgb(251, 251, 251); + border: 1px solid rgb(221, 221, 221); border-radius: 3px; padding: 0 0.2em; } -- Gitblit v1.9.1