Change-Id: I6935fa45598da847936748b474a3da47e94efaactags/v1.4.0
@@ -43,6 +43,7 @@ r20: { | |||
- Added setting to globally disable anonymous pushes in the receive pack | |||
- Added a normalized diffstat display to the commit, commitdiff, and compare pages | |||
- Added GO setting to automatically redirect all http requests to the secure https connector | |||
- Automatically display common repository root documents as tabs on the docs page | |||
dependencyChanges: | |||
- updated to Jetty 7.6.13 | |||
- updated to JGit 3.1.0 | |||
@@ -55,6 +56,7 @@ r20: { | |||
- { name: 'git.defaultAccessRestriction', defaultValue: 'PUSH' } | |||
- { name: 'git.mirrorPeriod', defaultValue: '30 mins' } | |||
- { name: 'web.commitMessageRenderer', defaultValue: 'plain' } | |||
- { name: 'web.documents', defaultValue: 'readme home index changelog contributing submitting_patches copying license notice authors' } | |||
- { name: 'web.showBranchGraph', defaultValue: 'true' } | |||
- { name: 'web.summaryShowReadme', defaultValue: 'false' } | |||
- { name: 'server.redirectToHttpsPort', defaultValue: 'true' } |
@@ -21,7 +21,10 @@ import java.io.UnsupportedEncodingException; | |||
import java.net.URLEncoder; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.wicket.Page; | |||
import org.apache.wicket.RequestCycle; | |||
@@ -82,6 +85,21 @@ public class MarkupProcessor { | |||
return list; | |||
} | |||
public List<String> getAllExtensions() { | |||
List<String> list = getMarkupExtensions(); | |||
list.add("txt"); | |||
list.add("TXT"); | |||
return list; | |||
} | |||
private List<String> getRoots() { | |||
return settings.getStrings(Keys.web.documents); | |||
} | |||
private String [] getEncodings() { | |||
return settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]); | |||
} | |||
private MarkupSyntax determineSyntax(String documentPath) { | |||
String ext = StringUtils.getFileExtension(documentPath).toLowerCase(); | |||
if (StringUtils.isEmpty(ext)) { | |||
@@ -105,33 +123,67 @@ public class MarkupProcessor { | |||
return MarkupSyntax.PLAIN; | |||
} | |||
public MarkupDocument parseReadme(Repository r, String repositoryName, String commitId) { | |||
String readme = null; | |||
RevCommit commit = JGitUtils.getCommit(r, commitId); | |||
List<PathModel> paths = JGitUtils.getFilesInPath(r, null, commit); | |||
public boolean hasRootDocs(Repository r) { | |||
List<String> roots = getRoots(); | |||
List<String> extensions = getAllExtensions(); | |||
List<PathModel> paths = JGitUtils.getFilesInPath(r, null, null); | |||
for (PathModel path : paths) { | |||
if (!path.isTree()) { | |||
String name = path.name.toLowerCase(); | |||
if (name.equals("readme") || name.equals("readme.txt")) { | |||
readme = path.name; | |||
break; | |||
} else if (name.startsWith("readme.")) { | |||
String ext = StringUtils.getFileExtension(name).toLowerCase(); | |||
if (getMarkupExtensions().contains(ext)) { | |||
readme = path.name; | |||
break; | |||
String ext = StringUtils.getFileExtension(path.name).toLowerCase(); | |||
String name = StringUtils.stripFileExtension(path.name).toLowerCase(); | |||
if (roots.contains(name)) { | |||
if (StringUtils.isEmpty(ext) || extensions.contains(ext)) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
if (!StringUtils.isEmpty(readme)) { | |||
String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]); | |||
String markup = JGitUtils.getStringContent(r, commit.getTree(), readme, encodings); | |||
return parse(repositoryName, commitId, readme, markup); | |||
public List<MarkupDocument> getRootDocs(Repository r, String repositoryName, String commitId) { | |||
List<String> roots = getRoots(); | |||
List<MarkupDocument> list = getDocs(r, repositoryName, commitId, roots); | |||
return list; | |||
} | |||
public MarkupDocument getReadme(Repository r, String repositoryName, String commitId) { | |||
List<MarkupDocument> list = getDocs(r, repositoryName, commitId, Arrays.asList("readme")); | |||
if (list.isEmpty()) { | |||
return null; | |||
} | |||
return list.get(0); | |||
} | |||
return null; | |||
private List<MarkupDocument> getDocs(Repository r, String repositoryName, String commitId, List<String> names) { | |||
List<String> extensions = getAllExtensions(); | |||
String [] encodings = getEncodings(); | |||
Map<String, MarkupDocument> map = new HashMap<String, MarkupDocument>(); | |||
RevCommit commit = JGitUtils.getCommit(r, commitId); | |||
List<PathModel> paths = JGitUtils.getFilesInPath(r, null, commit); | |||
for (PathModel path : paths) { | |||
if (!path.isTree()) { | |||
String ext = StringUtils.getFileExtension(path.name).toLowerCase(); | |||
String name = StringUtils.stripFileExtension(path.name).toLowerCase(); | |||
if (names.contains(name)) { | |||
if (StringUtils.isEmpty(ext) || extensions.contains(ext)) { | |||
String markup = JGitUtils.getStringContent(r, commit.getTree(), path.name, encodings); | |||
MarkupDocument doc = parse(repositoryName, commitId, path.name, markup); | |||
map.put(name, doc); | |||
} | |||
} | |||
} | |||
} | |||
// return document list in requested order | |||
List<MarkupDocument> list = new ArrayList<MarkupDocument>(); | |||
for (String name : names) { | |||
if (map.containsKey(name)) { | |||
list.add(map.get(name)); | |||
} | |||
} | |||
return list; | |||
} | |||
public MarkupDocument parse(String repositoryName, String commitId, String documentPath, String markupText) { |
@@ -7,8 +7,8 @@ | |||
<body> | |||
<wicket:extend> | |||
<!-- doc nav links --> | |||
<div class="page_nav2"> | |||
<a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | |||
<div style="float: right;" class="page_nav2"> | |||
<a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | |||
</div> | |||
<!-- document content --> |
@@ -20,7 +20,6 @@ import java.util.List; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
@@ -54,7 +53,7 @@ public class DocPage extends RepositoryPage { | |||
if (StringUtils.isEmpty(markupText)) { | |||
String name = StringUtils.stripFileExtension(path); | |||
List<String> docExtensions = processor.getMarkupExtensions(); | |||
List<String> docExtensions = processor.getAllExtensions(); | |||
for (String ext : docExtensions) { | |||
String checkName = name + "." + ext; | |||
markupText = JGitUtils.getStringContent(r, commit.getTree(), checkName, encodings); | |||
@@ -73,8 +72,6 @@ public class DocPage extends RepositoryPage { | |||
WicketUtils.newPathParameter(repositoryName, objectId, documentPath))); | |||
add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter( | |||
repositoryName, objectId, documentPath))); | |||
add(new BookmarkablePageLink<Void>("headLink", DocPage.class, | |||
WicketUtils.newPathParameter(repositoryName, Constants.HEAD, documentPath))); | |||
MarkupDocument markupDoc = processor.parse(repositoryName, getBestCommitId(commit), documentPath, markupText); | |||
add(new Label("content", markupDoc.html).setEscapeModelStrings(false)); |
@@ -7,23 +7,30 @@ | |||
<body> | |||
<wicket:extend> | |||
<div wicket:id="docs"></div> | |||
<div class="docs" wicket:id="docs"></div> | |||
<wicket:fragment wicket:id="indexFragment"> | |||
<wicket:fragment wicket:id="tabsFragment"> | |||
<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 wicket:id="tabTitle"> | |||
<a data-toggle="tab" wicket:id="link"><span wicket:id="label">[label]</span></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" class="tab-pane active"> | |||
<div class="markdown" wicket:id="index"></div> | |||
<div wicket:id="tabContent" class="tab-pane"> | |||
<!-- doc nav links --> | |||
<div style="float: right;" class="docnav"> | |||
<a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | |||
</div> | |||
<div class="content" wicket:id="content"></div> | |||
</div> | |||
<div id="pages" class="tab-pane"> | |||
<div style="padding-top: 5px;" wicket:id="documents"></div> | |||
</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> | |||
@@ -15,13 +15,14 @@ | |||
*/ | |||
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.behavior.SimpleAttributeModifier; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.apache.wicket.markup.html.link.ExternalLink; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
@@ -38,6 +39,7 @@ import com.gitblit.wicket.CacheControl; | |||
import com.gitblit.wicket.CacheControl.LastModified; | |||
import com.gitblit.wicket.MarkupProcessor; | |||
import com.gitblit.wicket.MarkupProcessor.MarkupDocument; | |||
import com.gitblit.wicket.MarkupProcessor.MarkupSyntax; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.LinkPanel; | |||
@@ -51,45 +53,76 @@ public class DocsPage extends RepositoryPage { | |||
Repository r = getRepository(); | |||
RevCommit head = JGitUtils.getCommit(r, null); | |||
List<String> extensions = processor.getMarkupExtensions(); | |||
final String commitId = getBestCommitId(head); | |||
List<String> extensions = processor.getAllExtensions(); | |||
List<PathModel> paths = JGitUtils.getDocuments(r, extensions); | |||
String doc = null; | |||
String markup = 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(); | |||
name = StringUtils.stripFileExtension(name); | |||
if (roots.contains(name)) { | |||
doc = path.name; | |||
break; | |||
} | |||
} | |||
if (!StringUtils.isEmpty(doc)) { | |||
// load the document | |||
String [] encodings = GitBlit.getEncodings(); | |||
markup = JGitUtils.getStringContent(r, head.getTree(), doc, encodings); | |||
// parse document | |||
MarkupDocument markupDoc = processor.parse(repositoryName, getBestCommitId(head), doc, markup); | |||
html = markupDoc.html; | |||
} | |||
List<MarkupDocument> roots = processor.getRootDocs(r, repositoryName, commitId); | |||
Fragment fragment = null; | |||
if (StringUtils.isEmpty(html)) { | |||
// no custom index/root, use the standard document list | |||
if (roots.isEmpty()) { | |||
// no identified root documents just show 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); | |||
// root documents, use tabbed ui of index/root and document list | |||
fragment = new Fragment("docs", "tabsFragment", this); | |||
ListDataProvider<MarkupDocument> docDp = new ListDataProvider<MarkupDocument>(roots); | |||
// tab titles | |||
DataView<MarkupDocument> tabTitles = new DataView<MarkupDocument>("tabTitle", docDp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter; | |||
@Override | |||
public void populateItem(final Item<MarkupDocument> item) { | |||
MarkupDocument doc = item.getModelObject(); | |||
String file = StringUtils.getLastPathElement(doc.documentPath); | |||
file = StringUtils.stripFileExtension(file); | |||
String name = file.replace('_', ' ').replace('-', ' '); | |||
ExternalLink link = new ExternalLink("link", "#" + file); | |||
link.add(new Label("label", name.toUpperCase()).setRenderBodyOnly(true)); | |||
item.add(link); | |||
if (counter == 0) { | |||
counter++; | |||
item.add(new SimpleAttributeModifier("class", "active")); | |||
} | |||
} | |||
}; | |||
fragment.add(tabTitles); | |||
// tab content | |||
DataView<MarkupDocument> tabsView = new DataView<MarkupDocument>("tabContent", docDp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter; | |||
@Override | |||
public void populateItem(final Item<MarkupDocument> item) { | |||
MarkupDocument doc = item.getModelObject(); | |||
// document page links | |||
item.add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class, | |||
WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath))); | |||
item.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, | |||
WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath))); | |||
item.add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter( | |||
repositoryName, commitId, doc.documentPath))); | |||
// document content | |||
String file = StringUtils.getLastPathElement(doc.documentPath); | |||
file = StringUtils.stripFileExtension(file); | |||
Component content = new Label("content", doc.html) | |||
.setEscapeModelStrings(false); | |||
if (!MarkupSyntax.PLAIN.equals(doc.syntax)) { | |||
content.add(new SimpleAttributeModifier("class", "markdown")); | |||
} | |||
item.add(content); | |||
item.add(new SimpleAttributeModifier("id", file)); | |||
if (counter == 0) { | |||
counter++; | |||
item.add(new SimpleAttributeModifier("class", "tab-pane active")); | |||
} | |||
} | |||
}; | |||
fragment.add(tabsView); | |||
} | |||
// document list |
@@ -142,7 +142,7 @@ public class SummaryPage extends RepositoryPage { | |||
// show a readme on the summary page | |||
RevCommit head = JGitUtils.getCommit(r, null); | |||
MarkupProcessor processor = new MarkupProcessor(GitBlit.getSettings()); | |||
MarkupDocument markupDoc = processor.parseReadme(r, repositoryName, getBestCommitId(head)); | |||
MarkupDocument markupDoc = processor.getReadme(r, repositoryName, getBestCommitId(head)); | |||
if (markupDoc == null || markupDoc.markup == null) { | |||
add(new Label("readme").setVisible(false)); | |||
} else { |
@@ -103,6 +103,16 @@ hr { | |||
margin-bottom: -1px; | |||
} | |||
ul.nav { | |||
border-color: #ccc; | |||
} | |||
.nav-tabs > .active > a, | |||
.nav-tabs > .active > a:hover { | |||
border: 1px solid #ccc; | |||
border-bottom-color: transparent; | |||
} | |||
.navbar .active a { | |||
background-color: transparent !important; | |||
outline: 0; | |||
@@ -1507,8 +1517,57 @@ li.L5, | |||
li.L7, | |||
li.L9 { background: #fafafa !important; } | |||
div.markdown { | |||
max-width: 900px; | |||
div.docs { | |||
max-width: 880px; | |||
} | |||
div.docs ul.nav { | |||
margin-bottom: 0px !important; | |||
} | |||
div.docs div.docnav { | |||
display: inline-block; | |||
padding: 6px 5px 6px 5px; | |||
border-left: 1px solid #ccc; | |||
border-right: 1px solid #ccc; | |||
border-bottom: 1px solid #ccc; | |||
border-radius: 0px 0px 3px 3px; | |||
background-color: #ECF1F4; | |||
color: #666; | |||
text-align: left; | |||
margin-top: -10px; | |||
} | |||
div.docs .content { | |||
margin-top: 10px; | |||
} | |||
div.docs div.markdown { | |||
margin-top: 10px; | |||
} | |||
div.markdown { | |||
line-height: 1.4; | |||
} | |||
div.markdown h1 { | |||
padding: 0px 0px 4px; | |||
border-bottom: 1px solid rgb(221, 221, 221); | |||
margin: 4px 0px 8px; | |||
} | |||
div.markdown h2 { | |||
padding: 4px 0px; | |||
border-bottom: 1px solid rgb(238, 238, 238); | |||
margin: 4px 0px 8px; | |||
} | |||
div.markdown h3 { | |||
padding: 8px 0px 4px; | |||
} | |||
div.markdown li { | |||
line-height: 1.4; | |||
} | |||
div.markdown pre { | |||
@@ -1533,11 +1592,7 @@ div.markdown code { | |||
background-color: rgb(250, 250, 250); | |||
border: 1px solid rgb(221, 221, 221); | |||
border-radius: 3px; | |||
padding: 0 0.2em; | |||
} | |||
div.markdown a { | |||
text-decoration: underline; | |||
padding: 0 0.4em; | |||
} | |||
div.markdown table { |