Browse Source

Support for intra-Markdown linking using [[WikiLinks]] syntax (issue-324)

All WikiLinks must be specified relative to the root of the repository.
The displayed link text is stripped to just the document name. Spaces in
the document name are replaced with '-' characters; this is consistent with
wiki syntax and Github.

Change-Id: Id3fb1b5441352d9bacc4993a5b85882db113693b
tags/v1.4.0
James Moger 10 years ago
parent
commit
a7317acec0

+ 1
- 0
releases.moxie View File

- Added option to render Markdown commit messages (issue-203) - Added option to render Markdown commit messages (issue-203)
- Added setting to control creating a repository as --shared on Unix servers (issue-263) - 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) - 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 setting to globally disable anonymous pushes in the receive pack
- Added a normalized diffstat display to the commit, commitdiff, and compare pages - Added a normalized diffstat display to the commit, commitdiff, and compare pages
dependencyChanges: dependencyChanges:

+ 13
- 1
src/main/java/com/gitblit/utils/MarkdownUtils.java View File

import java.io.StringWriter; import java.io.StringWriter;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.pegdown.LinkRenderer;
import org.pegdown.PegDownProcessor; import org.pegdown.PegDownProcessor;
/** /**
* @throws java.text.ParseException * @throws java.text.ParseException
*/ */
public static String transformMarkdown(String markdown) { 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); PegDownProcessor pd = new PegDownProcessor(ALL);
String html = pd.markdownToHtml(markdown);
String html = pd.markdownToHtml(markdown, linkRenderer == null ? new LinkRenderer() : linkRenderer);
return html; return html;
} }

+ 2
- 1
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties View File

gb.noActivityToday = there has been no activity today gb.noActivityToday = there has been no activity today
gb.anonymousUser= anonymous gb.anonymousUser= anonymous
gb.commitMessageRenderer = commit message renderer gb.commitMessageRenderer = commit message renderer
gb.diffStat = {0} insertions & {1} deletions
gb.diffStat = {0} insertions & {1} deletions
gb.home = home

+ 21
- 4
src/main/java/com/gitblit/wicket/pages/DocsPage.html View File

<body> <body>
<wicket:extend> <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 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 --> <!-- documents -->
<table style="width:100%" class="pretty"> <table style="width:100%" class="pretty">
<tr wicket:id="document"> <tr wicket:id="document">
</span> </span>
</td> </td>
</tr> </tr>
</table>
</table>
</wicket:fragment>
</wicket:extend> </wicket:extend>
</body> </body>
</html> </html>

+ 56
- 11
src/main/java/com/gitblit/wicket/pages/DocsPage.java View File

*/ */
package com.gitblit.wicket.pages; package com.gitblit.wicket.pages;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters; import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink; 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.Item;
import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider; import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit; import com.gitblit.GitBlit;
import com.gitblit.Keys; import com.gitblit.Keys;
import com.gitblit.models.PathModel; import com.gitblit.models.PathModel;
import com.gitblit.utils.ByteFormat; import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils; 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;
import com.gitblit.wicket.CacheControl.LastModified; import com.gitblit.wicket.CacheControl.LastModified;
import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.WicketUtils;
super(params); super(params);
Repository r = getRepository(); Repository r = getRepository();
RevCommit head = JGitUtils.getCommit(r, null);
List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions); List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions);
List<PathModel> paths = JGitUtils.getDocuments(r, extensions); List<PathModel> paths = JGitUtils.getDocuments(r, extensions);
final ByteFormat byteFormat = new ByteFormat();
String doc = null;
String markdown = null;
String html = null;
List<String> roots = Arrays.asList("home");
add(new Label("header", getString("gb.docs")));
// 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;
}
}
// documents list
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();
Fragment docs = new Fragment("documents", "documentsFragment", this);
ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths); ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
DataView<PathModel> pathsView = new DataView<PathModel>("document", pathsDp) { DataView<PathModel> pathsView = new DataView<PathModel>("document", pathsDp) {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
PathModel entry = item.getModelObject(); PathModel entry = item.getModelObject();
item.add(WicketUtils.newImage("docIcon", "file_world_16x16.png")); item.add(WicketUtils.newImage("docIcon", "file_world_16x16.png"));
item.add(new Label("docSize", byteFormat.format(entry.size))); 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 // 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 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 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 item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path)));
.newPathParameter(repositoryName, id, entry.path)));
WicketUtils.setAlternatingBackground(item, counter); WicketUtils.setAlternatingBackground(item, counter);
counter++; counter++;
} }
}; };
add(pathsView);
docs.add(pathsView);
fragment.add(docs);
add(fragment);
} }
@Override @Override

+ 28
- 4
src/main/java/com/gitblit/wicket/pages/MarkdownPage.java View File

package com.gitblit.wicket.pages; package com.gitblit.wicket.pages;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List;
import org.apache.wicket.PageParameters; import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit; import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils; import com.gitblit.utils.StringUtils;
public MarkdownPage(PageParameters params) { public MarkdownPage(PageParameters params) {
super(params); super(params);
final String markdownPath = WicketUtils.getPath(params);
final String path = WicketUtils.getPath(params).replace("%2f", "/").replace("%2F", "/");
Repository r = getRepository(); Repository r = getRepository();
RevCommit commit = JGitUtils.getCommit(r, objectId); RevCommit commit = JGitUtils.getCommit(r, objectId);
String [] encodings = GitBlit.getEncodings(); 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 // markdown page links
add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class, add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class, add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class,
WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath))); 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; String htmlText;
try { try {
htmlText = MarkdownUtils.transformMarkdown(markdownText);
htmlText = MarkdownUtils.transformMarkdown(markdownText, getLinkRenderer());
} catch (Exception e) { } catch (Exception e) {
logger.error("failed to transform markdown", 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); 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); htmlText = StringUtils.breakLinesForHtml(markdownText);
} }

+ 31
- 0
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java View File

package com.gitblit.wicket.pages; package com.gitblit.wicket.pages;
import java.io.Serializable; import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.pegdown.LinkRenderer;
import org.pegdown.ast.WikiLinkNode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
return isOwner; 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 class SearchForm extends SessionlessForm<Void> implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

+ 2
- 2
src/main/java/com/gitblit/wicket/pages/SummaryPage.java View File

String [] encodings = GitBlit.getEncodings(); String [] encodings = GitBlit.getEncodings();
markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings); markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings);
if (isMarkdown) { if (isMarkdown) {
htmlText = MarkdownUtils.transformMarkdown(markdownText);
htmlText = MarkdownUtils.transformMarkdown(markdownText, getLinkRenderer());
} else { } else {
htmlText = MarkdownUtils.transformPlainText(markdownText); htmlText = MarkdownUtils.transformPlainText(markdownText);
} }
if (StringUtils.isEmpty(htmlText)) { if (StringUtils.isEmpty(htmlText)) {
add(new Label("readme").setVisible(false)); add(new Label("readme").setVisible(false));
} else { } else {
Fragment fragment = new Fragment("readme", isMarkdown ? "markdownPanel" : "plaintextPanel");
Fragment fragment = new Fragment("readme", isMarkdown ? "markdownPanel" : "plaintextPanel", this);
fragment.add(new Label("readmeFile", readme)); fragment.add(new Label("readmeFile", readme));
// Add the html to the page // Add the html to the page
Component content = new Label("readmeContent", htmlText).setEscapeModelStrings(false); Component content = new Label("readmeContent", htmlText).setEscapeModelStrings(false);

+ 8
- 9
src/main/resources/gitblit.css View File

border:0px; border:0px;
padding: 0; padding: 0;
line-height: 1.35em; line-height: 1.35em;
vertical-align:top;
} }
table { table {
padding: 3px; padding: 3px;
border: 1px solid #ddd; border: 1px solid #ddd;
border-bottom: 0; border-bottom: 0;
border-radius: 3px 3px 0 0;
font-weight: bold; font-weight: bold;
font-family: Helvetica,arial,freesans,clean,sans-serif; font-family: Helvetica,arial,freesans,clean,sans-serif;
} }
margin:0 0 2px; margin:0 0 2px;
padding:7px 14px; padding:7px 14px;
border:1px solid #ddd; border:1px solid #ddd;
border-radius: 3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;border-radius:3px;
} }
div.header a, div.commitHeader a { div.header a, div.commitHeader a {
li.L7, li.L7,
li.L9 { background: #fafafa !important; } li.L9 { background: #fafafa !important; }
div.markdown {
max-width: 900px;
}
div.markdown pre { 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; border-radius: 4px 4px 4px 4px;
display: block; display: block;
font-size: 12px; font-size: 12px;
} }
div.markdown code { 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; border-radius: 3px;
padding: 0 0.2em; padding: 0 0.2em;
} }

Loading…
Cancel
Save