Change-Id: I6935fa45598da847936748b474a3da47e94efaactags/v1.4.0
- 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 | ||||
- Added GO setting to automatically redirect all http requests to the secure https connector | - 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: | dependencyChanges: | ||||
- updated to Jetty 7.6.13 | - updated to Jetty 7.6.13 | ||||
- updated to JGit 3.1.0 | - updated to JGit 3.1.0 | ||||
- { name: 'git.defaultAccessRestriction', defaultValue: 'PUSH' } | - { name: 'git.defaultAccessRestriction', defaultValue: 'PUSH' } | ||||
- { name: 'git.mirrorPeriod', defaultValue: '30 mins' } | - { name: 'git.mirrorPeriod', defaultValue: '30 mins' } | ||||
- { name: 'web.commitMessageRenderer', defaultValue: 'plain' } | - { 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.showBranchGraph', defaultValue: 'true' } | ||||
- { name: 'web.summaryShowReadme', defaultValue: 'false' } | - { name: 'web.summaryShowReadme', defaultValue: 'false' } | ||||
- { name: 'server.redirectToHttpsPort', defaultValue: 'true' } | - { name: 'server.redirectToHttpsPort', defaultValue: 'true' } |
import java.net.URLEncoder; | 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.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import org.apache.wicket.Page; | import org.apache.wicket.Page; | ||||
import org.apache.wicket.RequestCycle; | import org.apache.wicket.RequestCycle; | ||||
return list; | 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) { | private MarkupSyntax determineSyntax(String documentPath) { | ||||
String ext = StringUtils.getFileExtension(documentPath).toLowerCase(); | String ext = StringUtils.getFileExtension(documentPath).toLowerCase(); | ||||
if (StringUtils.isEmpty(ext)) { | if (StringUtils.isEmpty(ext)) { | ||||
return MarkupSyntax.PLAIN; | 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) { | for (PathModel path : paths) { | ||||
if (!path.isTree()) { | 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) { | public MarkupDocument parse(String repositoryName, String commitId, String documentPath, String markupText) { |
<body> | <body> | ||||
<wicket:extend> | <wicket:extend> | ||||
<!-- doc nav links --> | <!-- 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> | </div> | ||||
<!-- document content --> | <!-- document content --> |
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.eclipse.jgit.lib.Constants; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
if (StringUtils.isEmpty(markupText)) { | if (StringUtils.isEmpty(markupText)) { | ||||
String name = StringUtils.stripFileExtension(path); | String name = StringUtils.stripFileExtension(path); | ||||
List<String> docExtensions = processor.getMarkupExtensions(); | |||||
List<String> docExtensions = processor.getAllExtensions(); | |||||
for (String ext : docExtensions) { | for (String ext : docExtensions) { | ||||
String checkName = name + "." + ext; | String checkName = name + "." + ext; | ||||
markupText = JGitUtils.getStringContent(r, commit.getTree(), checkName, encodings); | markupText = JGitUtils.getStringContent(r, commit.getTree(), checkName, encodings); | ||||
WicketUtils.newPathParameter(repositoryName, objectId, documentPath))); | WicketUtils.newPathParameter(repositoryName, objectId, documentPath))); | ||||
add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter( | add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter( | ||||
repositoryName, objectId, documentPath))); | 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); | MarkupDocument markupDoc = processor.parse(repositoryName, getBestCommitId(commit), documentPath, markupText); | ||||
add(new Label("content", markupDoc.html).setEscapeModelStrings(false)); | add(new Label("content", markupDoc.html).setEscapeModelStrings(false)); |
<body> | <body> | ||||
<wicket:extend> | <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"> | <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> | <li><a data-toggle="tab" href="#pages"><wicket:message key="gb.pages">[pages]</wicket:message></a></li> | ||||
</ul> | </ul> | ||||
<div class="tab-content"> | <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> | ||||
<div id="pages" wicket:id="documents" class="tab-pane"></div> | |||||
</div> | </div> | ||||
</wicket:fragment> | </wicket:fragment> | ||||
<wicket:fragment wicket:id="noIndexFragment"> | <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> | <div wicket:id="documents"></div> | ||||
</wicket:fragment> | </wicket:fragment> | ||||
*/ | */ | ||||
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.Component; | ||||
import org.apache.wicket.PageParameters; | 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.basic.Label; | ||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | 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.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 com.gitblit.wicket.CacheControl.LastModified; | import com.gitblit.wicket.CacheControl.LastModified; | ||||
import com.gitblit.wicket.MarkupProcessor; | import com.gitblit.wicket.MarkupProcessor; | ||||
import com.gitblit.wicket.MarkupProcessor.MarkupDocument; | import com.gitblit.wicket.MarkupProcessor.MarkupDocument; | ||||
import com.gitblit.wicket.MarkupProcessor.MarkupSyntax; | |||||
import com.gitblit.wicket.WicketUtils; | import com.gitblit.wicket.WicketUtils; | ||||
import com.gitblit.wicket.panels.LinkPanel; | import com.gitblit.wicket.panels.LinkPanel; | ||||
Repository r = getRepository(); | Repository r = getRepository(); | ||||
RevCommit head = JGitUtils.getCommit(r, null); | 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); | 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; | 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 = new Fragment("docs", "noIndexFragment", this); | ||||
fragment.add(new Label("header", getString("gb.docs"))); | |||||
} else { | } 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 | // document list |
// show a readme on the summary page | // show a readme on the summary page | ||||
RevCommit head = JGitUtils.getCommit(r, null); | RevCommit head = JGitUtils.getCommit(r, null); | ||||
MarkupProcessor processor = new MarkupProcessor(GitBlit.getSettings()); | 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) { | if (markupDoc == null || markupDoc.markup == null) { | ||||
add(new Label("readme").setVisible(false)); | add(new Label("readme").setVisible(false)); | ||||
} else { | } else { |
margin-bottom: -1px; | 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 { | .navbar .active a { | ||||
background-color: transparent !important; | background-color: transparent !important; | ||||
outline: 0; | outline: 0; | ||||
li.L7, | li.L7, | ||||
li.L9 { background: #fafafa !important; } | 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 { | div.markdown pre { | ||||
background-color: rgb(250, 250, 250); | background-color: rgb(250, 250, 250); | ||||
border: 1px solid rgb(221, 221, 221); | border: 1px solid rgb(221, 221, 221); | ||||
border-radius: 3px; | border-radius: 3px; | ||||
padding: 0 0.2em; | |||||
} | |||||
div.markdown a { | |||||
text-decoration: underline; | |||||
padding: 0 0.4em; | |||||
} | } | ||||
div.markdown table { | div.markdown table { |