diff --git a/docu/index.md b/docu/index.md
index 641d8767..8885c768 100644
--- a/docu/index.md
+++ b/docu/index.md
@@ -5,7 +5,7 @@ with full SSO through Gerrit.
* License: [Apache Public License 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [Home page](https://github.com/tomaswolf/gerrit-gitblit-plugin)
-* Installed plugin version: 2.15.171.0-SNAPSHOT — Check for updates
+* Installed plugin version: 2.16.171.1-SNAPSHOT — Check for updates
For a list of contributors, see at [GitHub](https://github.com/tomaswolf/gerrit-gitblit-plugin/graphs/contributors).
@@ -15,30 +15,11 @@ This is a privately maintained fork of the official Gerrit-Gitblit plugin. Pleas
# Configuration
-There are two different configurations: one for Gerrit so it knows how to generate links that will be processed by the plugin, and
-an optional GitBlit configuration for the plugin itself.
+The plugin in configured in Gerrit's global configuration file `gerrit.config`.
+As of v2.16.171.0, this plugin does _not_ use the `[gitweb]` section anymore.
+Configuration is done in the `[plugin "gitblit"]` section.
-## Gerrit configuration
-
-In Gerrit's `gerrit.config`, define the `[gitweb]` section as follows:
-
- [gitweb]
- type = custom
- url = plugins/gitblit/
- linkname = browse
- project = summary/?r=${project}
- revision = commit/?r=${project}&h=${commit}
- branch = log/?r=${project}&h=${branch}
- filehistory = history/?f=${file}&r=${project}&h=${branch}
- file = blob/?r=${project}&h=${commit}&f=${file}
- roottree = tree/?r=${project}&h=${commit}
-
-This is normally done automatically if you add the plugin and run through `java -jar gerrit.war init -d site_path`, but you can also
-add this manually to Gerrit's config file. The `linkname` can be adapted to your taste.
-
-### Configuring the top menu
-
-This plugin adds a "GitBlit" top menu to Gerrit, and also a new sub-menu item to the "Projects" top menu. Since v2.11.162.2 of this plugin, the link
+This plugin adds a "GitBlit" top menu to Gerrit. Since v2.11.162.2 of this plugin, the link
texts for all sub-menu items can be configured to your taste in a `[plugin "gitblit"]` section in your `gerrit.config`. If the section is not present,
or some values in that section are not defined, the plugin uses built-in default texts. The default configuration would correspond to
@@ -47,14 +28,14 @@ or some values in that section are not defined, the plugin uses built-in default
activity = Activity
documentation = Documentation
search =
- browse = Browse
+ linkname = Gitblit
-The first four are sub-menu items of the "GitBlit" top menu, the last one is a new "browse" sub-menu item in Gerrit's "Projects" menu that is shown
-for Gerrit's "current" project (since v2.11.162.2).
+The first four are sub-menu items of the "GitBlit" top menu, the last one is the link text shown on links to Gitblit for individual files
+or in the project list.
The "search" sub-menu item is by default not set and will thus not be shown. Setting it makes only sense if you enable GitBlit indexing on some of
your projects. See the [GitBlit documentation](http://gitblit.com/setup_lucene.html) for more information on that.
-
+
## GitBlit configuration
The plugin includes a minimal default configuration to make GitBlit act only as a repository viewer. You can augment that with further
@@ -66,7 +47,7 @@ To see the built-in configuration, access it at [`gitblit.properties`](../src/ma
By default, the built-in configuration does allow anonymous browsing, subject to the repository and ref-level access restrictions defined in Gerrit.
If you want to lock the GitBlit plugin to allow only logged-in users to browse, set in `$GERRIT_SITE/etc/gitblit.properties` the key
-`web.authenticateViewPages = true`. This is the only key of the built-in configuration that you _can_ override.
+`web.authenticateViewPages = true`. This is the only key of the built-in configuration that you _can_ override.
GitBlit's ticket service, fan-out service, and its plugin mechanism are disabled in this plugin, as is ssh access through GitBlit since Gerrit
already provides that. Also disabled is Gitblit's LFS implementation.
@@ -95,7 +76,7 @@ The GitBlit `${baseFolder}` is the plugin's data directory provided by Gerrit at
-
+
# Issue tracking
@@ -111,6 +92,6 @@ Report bugs or make feature requests at the [GitHub issue tracker](https://githu
diff --git a/pom.xml b/pom.xml
index 12a298ac..391e38de 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@ limitations under the License.
gitblit-plugin
GitBlit for Gerrit integrated as a plugin
Gerrit - GitBlit Plugin
- 2.15.171.0-SNAPSHOT
+ 2.16.171.1-SNAPSHOT
Apache License 2.0
@@ -29,7 +29,7 @@ limitations under the License.
plugin
- 2.15
+ 2.16.7
1.7.1
1.4.22
restart
@@ -232,6 +232,8 @@ limitations under the License.
com/gitblit/models/RefModel$*.class
com/gitblit/models/RepositoryCommit.class
com/gitblit/models/RepositoryCommit$*.class
+ com/gitblit/wicket/MarkupProcessor.class
+ com/gitblit/wicket/MarkupProcessor$*.class
com/gitblit/wicket/SessionlessForm.class
com/gitblit/wicket/SessionlessForm$*.class
com/gitblit/wicket/pages/BasePage.class
@@ -258,6 +260,8 @@ limitations under the License.
com/gitblit/wicket/panels/LogPanel$*.class
com/gitblit/wicket/panels/SearchPanel.class
com/gitblit/wicket/panels/SearchPanel$*.class
+ com/gitblit/utils/MarkdownUtils.class
+ com/gitblit/utils/MarkdownUtils$*.class
com/gitblit/utils/MetricUtils.class
com/gitblit/utils/MetricUtils$*.class
com/gitblit/utils/GitBlitDiffFormatter.class
@@ -277,6 +281,8 @@ limitations under the License.
com/gitblit/utils/RefLogUtils$*.class
com/gitblit/wicket/pages/NewRepositoryPage.class
com/gitblit/wicket/pages/NewRepositoryPage$*.class
+ com/gitblit/wicket/panels/BranchesPanel.class
+ com/gitblit/wicket/panels/BranchesPanel$*.class
com/gitblit/wicket/panels/HistoryPanel.class
com/gitblit/wicket/panels/HistoryPanel$*.class
com/gitblit/tickets/BranchTicketService.class
diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java
index 708a3d29..b99912d1 100644
--- a/src/main/java/com/gitblit/service/LuceneService.java
+++ b/src/main/java/com/gitblit/service/LuceneService.java
@@ -97,9 +97,9 @@
/**
* The Lucene service handles indexing and searching repositories.
- *
+ *
* @author James Moger
- *
+ *
*/
public class LuceneService implements Runnable {
@@ -185,7 +185,7 @@ public void run() {
/**
* Synchronously indexes a repository. This may build a complete index of a repository or it may update an existing index.
- *
+ *
* @param displayName
* the name of the repository
* @param repository
@@ -226,7 +226,7 @@ private void index(RepositoryModel model, Repository repository) {
/**
* Close the writer/searcher objects for a repository.
- *
+ *
* @param repositoryName
*/
public synchronized void close(String repositoryName) {
@@ -251,7 +251,7 @@ public synchronized void close(String repositoryName) {
/**
* Close all Lucene indexers.
- *
+ *
*/
public synchronized void close() {
// close all writers
@@ -277,7 +277,7 @@ public synchronized void close() {
/**
* Deletes the Lucene index for the specified repository.
- *
+ *
* @param repositoryName
* @return true, if successful
*/
@@ -305,7 +305,7 @@ public boolean deleteIndex(String repositoryName) {
/**
* Returns the author for the commit, if this information is available.
- *
+ *
* @param commit
* @return an author or unknown
*/
@@ -323,7 +323,7 @@ private String getAuthor(RevCommit commit) {
/**
* Returns the committer for the commit, if this information is available.
- *
+ *
* @param commit
* @return an committer or unknown
*/
@@ -341,7 +341,7 @@ private String getCommitter(RevCommit commit) {
/**
* Get the tree associated with the given commit.
- *
+ *
* @param walk
* @param commit
* @return tree
@@ -358,7 +358,7 @@ private RevTree getTree(final RevWalk walk, final RevCommit commit) throws IOExc
/**
* Construct a keyname from the branch.
- *
+ *
* @param branchName
* @return a keyname appropriate for the Git config file format
*/
@@ -368,7 +368,7 @@ private String getBranchKey(String branchName) {
/**
* Returns the Lucene configuration for the specified repository.
- *
+ *
* @param repository
* @return a config object
*/
@@ -381,7 +381,7 @@ private FileBasedConfig getConfig(Repository repository) {
/**
* Reads the Lucene config file for the repository to check the index version. If the index version is different, then rebuild the repository
* index.
- *
+ *
* @param repository
* @return true of the on-disk index format is different than INDEX_VERSION
*/
@@ -399,7 +399,7 @@ private boolean shouldReindex(Repository repository) {
/**
* This completely indexes the repository and will destroy any existing index.
- *
+ *
* @param repositoryName
* @param repository
* @return IndexResult
@@ -613,7 +613,7 @@ public int compare(RefModel ref1, RefModel ref2) {
/**
* Incrementally update the index with the specified commit for the repository.
- *
+ *
* @param repositoryName
* @param repository
* @param branch
@@ -688,7 +688,7 @@ private IndexResult index(String repositoryName, Repository repository, String b
/**
* Delete a blob from the specified branch of the repository index.
- *
+ *
* @param repositoryName
* @param branch
* @param path
@@ -699,10 +699,9 @@ public boolean deleteBlob(String repositoryName, String branch, String path) thr
String pattern = MessageFormat.format("{0}:'{'0} AND {1}:\"'{'1'}'\" AND {2}:\"'{'2'}'\"", FIELD_OBJECT_TYPE, FIELD_BRANCH, FIELD_PATH);
String q = MessageFormat.format(pattern, SearchObjectType.blob.name(), branch, path);
- BooleanQuery query = new BooleanQuery();
StandardAnalyzer analyzer = new StandardAnalyzer();
QueryParser qp = new QueryParser(FIELD_SUMMARY, analyzer);
- query.add(qp.parse(q), Occur.MUST);
+ BooleanQuery query = (new BooleanQuery.Builder()).add(qp.parse(q), Occur.MUST).build();
IndexWriter writer = getIndexWriter(repositoryName);
int numDocsBefore = writer.numDocs();
@@ -720,7 +719,7 @@ public boolean deleteBlob(String repositoryName, String branch, String path) thr
/**
* Updates a repository index incrementally from the last indexed commits.
- *
+ *
* @param model
* @param repository
* @return IndexResult
@@ -849,7 +848,7 @@ public int compare(RefModel ref1, RefModel ref2) {
/**
* Creates a Lucene document for a commit
- *
+ *
* @param commit
* @param tags
* @return a Lucene document
@@ -871,7 +870,7 @@ private Document createDocument(RevCommit commit, List tags) {
/**
* Incrementally index an object for the repository.
- *
+ *
* @param repositoryName
* @param doc
* @return true, if successful
@@ -917,7 +916,7 @@ private synchronized void resetIndexSearcher(String repository) throws IOExcepti
/**
* Gets an index searcher for the repository.
- *
+ *
* @param repository
* @return
* @throws IOException
@@ -926,7 +925,7 @@ private IndexSearcher getIndexSearcher(String repository) throws IOException {
IndexSearcher searcher = searchers.get(repository);
if (searcher == null) {
IndexWriter writer = getIndexWriter(repository);
- searcher = new IndexSearcher(DirectoryReader.open(writer, true));
+ searcher = new IndexSearcher(DirectoryReader.open(writer, true, true));
searchers.put(repository, searcher);
}
return searcher;
@@ -934,7 +933,7 @@ private IndexSearcher getIndexSearcher(String repository) throws IOException {
/**
* Gets an index writer for the repository. The index will be created if it does not already exist or if forceCreate is specified.
- *
+ *
* @param repository
* @return an IndexWriter
* @throws IOException
@@ -960,7 +959,7 @@ private IndexWriter getIndexWriter(String repository) throws IOException {
/**
* Searches the specified repositories for the given text or query
- *
+ *
* @param text
* if the text is null or empty, null is returned
* @param page
@@ -970,7 +969,7 @@ private IndexWriter getIndexWriter(String repository) throws IOException {
* @param repositories
* a list of repositories to search. if no repositories are specified null is returned.
* @return a list of SearchResults in order from highest to the lowest score
- *
+ *
*/
public List search(String text, int page, int pageSize, List repositories) {
if (ArrayUtils.isEmpty(repositories)) {
@@ -981,7 +980,7 @@ public List search(String text, int page, int pageSize, List search(String text, int page, int pageSize, List search(String text, int page, int pageSize, String... repositories) {
if (StringUtils.isEmpty(text)) {
@@ -1004,15 +1003,15 @@ public List search(String text, int page, int pageSize, String...
StandardAnalyzer analyzer = new StandardAnalyzer();
try {
// default search checks summary and content
- BooleanQuery query = new BooleanQuery();
+ BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder();
QueryParser qp;
qp = new QueryParser(FIELD_SUMMARY, analyzer);
qp.setAllowLeadingWildcard(true);
- query.add(qp.parse(text), Occur.SHOULD);
+ queryBuilder.add(qp.parse(text), Occur.SHOULD);
qp = new QueryParser(FIELD_CONTENT, analyzer);
qp.setAllowLeadingWildcard(true);
- query.add(qp.parse(text), Occur.SHOULD);
+ queryBuilder.add(qp.parse(text), Occur.SHOULD);
IndexSearcher searcher;
if (repositories.length == 1) {
@@ -1030,6 +1029,7 @@ public List search(String text, int page, int pageSize, String...
searcher = new IndexSearcher(reader);
}
+ BooleanQuery query = queryBuilder.build();
Query rewrittenQuery = searcher.rewrite(query);
logger.debug(rewrittenQuery.toString());
@@ -1062,7 +1062,7 @@ public List search(String text, int page, int pageSize, String...
}
/**
- *
+ *
* @param analyzer
* @param query
* @param content
@@ -1192,7 +1192,7 @@ float duration() {
/**
* Custom subclass of MultiReader to identify the source index for a given doc id. This would not be necessary of there was a public method to
* obtain this information.
- *
+ *
*/
private class MultiSourceReader extends MultiReader {
diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java
index 428d40b3..d07c460f 100644
--- a/src/main/java/com/gitblit/servlet/RawServlet.java
+++ b/src/main/java/com/gitblit/servlet/RawServlet.java
@@ -414,6 +414,8 @@ protected boolean streamFromRepo(HttpServletRequest request, HttpServletResponse
String requestedPath) throws IOException {
boolean served = false;
+ // RevWalk is disposed in finally
+ @SuppressWarnings("resource")
RevWalk rw = new RevWalk(repository);
TreeWalk tw = new TreeWalk(repository);
try {
diff --git a/src/main/java/com/gitblit/tickets/TicketIndexer.java b/src/main/java/com/gitblit/tickets/TicketIndexer.java
index d1e88374..ced040ca 100644
--- a/src/main/java/com/gitblit/tickets/TicketIndexer.java
+++ b/src/main/java/com/gitblit/tickets/TicketIndexer.java
@@ -64,9 +64,9 @@
/**
* Indexes tickets in a Lucene database.
- *
+ *
* @author James Moger
- *
+ *
*/
public class TicketIndexer {
@@ -174,8 +174,7 @@ public boolean deleteAll(RepositoryModel repository) {
IndexWriter writer = getWriter();
StandardAnalyzer analyzer = new StandardAnalyzer();
QueryParser qp = new QueryParser(Lucene.rid.name(), analyzer);
- BooleanQuery query = new BooleanQuery();
- query.add(qp.parse(repository.getRID()), Occur.MUST);
+ BooleanQuery query = (new BooleanQuery.Builder()).add(qp.parse(repository.getRID()), Occur.MUST).build();
int numDocsBefore = writer.numDocs();
writer.deleteDocuments(query);
@@ -197,7 +196,7 @@ public boolean deleteAll(RepositoryModel repository) {
/**
* Bulk Add/Update tickets in the Lucene index
- *
+ *
* @param tickets
*/
public void index(List tickets) {
@@ -216,7 +215,7 @@ public void index(List tickets) {
/**
* Add/Update a ticket in the Lucene index
- *
+ *
* @param ticket
*/
public void index(TicketModel ticket) {
@@ -234,7 +233,7 @@ public void index(TicketModel ticket) {
/**
* Delete a ticket from the Lucene index.
- *
+ *
* @param ticket
* @throws Exception
* @return true, if deleted, false if no record was deleted
@@ -251,7 +250,7 @@ public boolean delete(TicketModel ticket) {
/**
* Delete a ticket from the Lucene index.
- *
+ *
* @param repository
* @param ticketId
* @throws Exception
@@ -260,8 +259,7 @@ public boolean delete(TicketModel ticket) {
private boolean delete(String repository, long ticketId, IndexWriter writer) throws Exception {
StandardAnalyzer analyzer = new StandardAnalyzer();
QueryParser qp = new QueryParser(Lucene.did.name(), analyzer);
- BooleanQuery query = new BooleanQuery();
- query.add(qp.parse(StringUtils.getSHA1(repository + ticketId)), Occur.MUST);
+ BooleanQuery query = (new BooleanQuery.Builder()).add(qp.parse(StringUtils.getSHA1(repository + ticketId)), Occur.MUST).build();
int numDocsBefore = writer.numDocs();
writer.deleteDocuments(query);
@@ -279,7 +277,7 @@ private boolean delete(String repository, long ticketId, IndexWriter writer) thr
/**
* Returns true if the repository has tickets in the index.
- *
+ *
* @param repository
* @return true if there are indexed tickets
*/
@@ -289,7 +287,7 @@ public boolean hasTickets(RepositoryModel repository) {
/**
* Search for tickets matching the query. The returned tickets are shadows of the real ticket, but suitable for a results list.
- *
+ *
* @param repository
* @param text
* @param page
@@ -304,22 +302,23 @@ public List searchFor(RepositoryModel repository, String text, int
StandardAnalyzer analyzer = new StandardAnalyzer();
try {
// search the title, description and content
- BooleanQuery query = new BooleanQuery();
+ BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder();
QueryParser qp;
qp = new QueryParser(Lucene.title.name(), analyzer);
qp.setAllowLeadingWildcard(true);
- query.add(qp.parse(text), Occur.SHOULD);
+ queryBuilder.add(qp.parse(text), Occur.SHOULD);
qp = new QueryParser(Lucene.body.name(), analyzer);
qp.setAllowLeadingWildcard(true);
- query.add(qp.parse(text), Occur.SHOULD);
+ queryBuilder.add(qp.parse(text), Occur.SHOULD);
qp = new QueryParser(Lucene.content.name(), analyzer);
qp.setAllowLeadingWildcard(true);
- query.add(qp.parse(text), Occur.SHOULD);
+ queryBuilder.add(qp.parse(text), Occur.SHOULD);
IndexSearcher searcher = getSearcher();
+ BooleanQuery query = queryBuilder.build();
Query rewrittenQuery = searcher.rewrite(query);
log.debug(rewrittenQuery.toString());
@@ -347,7 +346,7 @@ public List searchFor(RepositoryModel repository, String text, int
/**
* Search for tickets matching the query. The returned tickets are shadows of the real ticket, but suitable for a results list.
- *
+ *
* @param text
* @param page
* @param pageSize
@@ -378,7 +377,7 @@ public List queryFor(String queryText, int page, int pageSize, Stri
sort = new Sort(Lucene.fromString(sortBy).asSortField(desc));
}
int maxSize = 5000;
- TopFieldDocs docs = searcher.search(rewrittenQuery, null, maxSize, sort, false, false);
+ TopFieldDocs docs = searcher.search(rewrittenQuery, maxSize, sort, false, false);
int size = (pageSize <= 0) ? maxSize : pageSize;
int offset = Math.max(0, (page - 1) * size);
ScoreDoc[] hits = subset(docs.scoreDocs, offset, size);
@@ -440,7 +439,7 @@ private synchronized void closeWriter() {
private IndexSearcher getSearcher() throws IOException {
if (searcher == null) {
- searcher = new IndexSearcher(DirectoryReader.open(getWriter(), true));
+ searcher = new IndexSearcher(DirectoryReader.open(getWriter(), true, true));
}
return searcher;
}
@@ -459,7 +458,7 @@ private synchronized void closeSearcher() {
/**
* Creates a Lucene document from a ticket.
- *
+ *
* @param ticket
* @return a Lucene document
*/
@@ -536,7 +535,7 @@ private void toDocField(Document doc, Lucene lucene, String value) {
/**
* Creates a query result from the Lucene document. This result is not a high-fidelity representation of the real ticket, but it is suitable for
* display in a table of search results.
- *
+ *
* @param doc
* @return a query result
* @throws ParseException
diff --git a/src/main/java/com/gitblit/utils/MarkdownUtils.java b/src/main/java/com/gitblit/utils/MarkdownUtils.java
new file mode 100644
index 00000000..4d6728e6
--- /dev/null
+++ b/src/main/java/com/gitblit/utils/MarkdownUtils.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.utils;
+
+import static com.vladsch.flexmark.ext.wikilink.WikiLinkExtension.WIKI_LINK;
+import static com.vladsch.flexmark.profiles.pegdown.Extensions.ALL;
+import static com.vladsch.flexmark.profiles.pegdown.Extensions.ANCHORLINKS;
+import static com.vladsch.flexmark.profiles.pegdown.Extensions.SMARTYPANTS;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+
+import org.apache.commons.io.IOUtils;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.vladsch.flexmark.ast.Node;
+import com.vladsch.flexmark.ext.wikilink.WikiImage;
+import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkResolver;
+import com.vladsch.flexmark.html.HtmlRenderer;
+import com.vladsch.flexmark.html.IndependentLinkResolverFactory;
+import com.vladsch.flexmark.html.LinkResolver;
+import com.vladsch.flexmark.html.renderer.LinkResolverContext;
+import com.vladsch.flexmark.html.renderer.ResolvedLink;
+import com.vladsch.flexmark.parser.Parser;
+import com.vladsch.flexmark.profiles.pegdown.PegdownOptionsAdapter;
+import com.vladsch.flexmark.util.options.MutableDataHolder;
+
+/**
+ * Utility methods for transforming raw markdown text to html.
+ *
+ * @author James Moger
+ *
+ */
+public class MarkdownUtils {
+
+ /**
+ * Returns the html version of the plain source text.
+ *
+ * @param text
+ * @return html version of plain text
+ * @throws java.text.ParseException
+ */
+ public static String transformPlainText(String text) {
+ // url auto-linking
+ text = text.replaceAll("((http|https)://[0-9A-Za-z-_=\\?\\.\\$#&/]*)", "$1");
+ String html = "" + text + "
";
+ return html;
+ }
+
+
+ /**
+ * 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) {
+ return transformMarkdown(markdown, null);
+ }
+
+ public interface Linker {
+
+ ResolvedLink link(ResolvedLink original, boolean image);
+
+ }
+
+ /**
+ * 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, Linker linkRenderer) {
+ MutableDataHolder options;
+
+ if (linkRenderer == null) {
+ options = PegdownOptionsAdapter.flexmarkOptions(ALL & ~SMARTYPANTS & ~ANCHORLINKS).toMutable();
+ } else {
+ options = PegdownOptionsAdapter.flexmarkOptions(ALL & ~SMARTYPANTS & ~ANCHORLINKS, new CustomExtension(linkRenderer)).toMutable();
+ }
+ Node document = Parser.builder(options).build().parse(markdown);
+ return HtmlRenderer.builder(options).build().render(document);
+ }
+
+ private static class CustomExtension implements HtmlRenderer.HtmlRendererExtension {
+
+ private final Linker resolver;
+
+ public CustomExtension(Linker resolver) {
+ this.resolver = resolver;
+ }
+
+ @Override
+ public void rendererOptions(final MutableDataHolder options) {
+ // Nothing
+ }
+
+ @Override
+ public void extend(final HtmlRenderer.Builder rendererBuilder, final String rendererType) {
+ rendererBuilder.linkResolverFactory(new Factory());
+ }
+
+ private class Resolver extends WikiLinkLinkResolver {
+
+ public Resolver(LinkResolverContext context) {
+ super(context);
+ }
+
+ @Override
+ public ResolvedLink resolveLink(Node node, LinkResolverContext context, ResolvedLink link) {
+ if (link.getLinkType() == WIKI_LINK && link.getUrl().indexOf("://") < 0) {
+ ResolvedLink result = resolver.link(link, node instanceof WikiImage);
+ if (result != null) {
+ return result;
+ }
+ }
+ return super.resolveLink(node, context, link);
+ }
+ }
+
+ private class Factory extends IndependentLinkResolverFactory {
+
+ @Override
+ public LinkResolver create(final LinkResolverContext context) {
+ return new Resolver(context);
+ }
+ }
+ }
+ /**
+ * Returns the html version of the markdown source reader. The reader is
+ * closed regardless of success or failure.
+ *
+ * @param markdownReader
+ * @return html version of the markdown text
+ * @throws java.text.ParseException
+ */
+ public static String transformMarkdown(Reader markdownReader) throws IOException {
+ // Read raw markdown content and transform it to html
+ StringWriter writer = new StringWriter();
+ try {
+ IOUtils.copy(markdownReader, writer);
+ String markdown = writer.toString();
+ return transformMarkdown(markdown);
+ } finally {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // IGNORE
+ }
+ }
+ }
+
+
+ /**
+ * Transforms GFM (Github Flavored Markdown) to html.
+ * Gitblit does not support the complete GFM specification.
+ *
+ * @param input
+ * @param repositoryName
+ * @return html
+ */
+ public static String transformGFM(IStoredSettings settings, String input, String repositoryName) {
+ String text = input;
+
+ // strikethrough
+ text = text.replaceAll("~~(.*)~~", "$1");
+ text = text.replaceAll("\\{(?:-){2}(.*)(?:-){2}}", "$1");
+
+ // underline
+ text = text.replaceAll("\\{(?:\\+){2}(.*)(?:\\+){2}}", "$1");
+
+ // strikethrough, replacement
+ text = text.replaceAll("\\{~~(.*)~>(.*)~~}", "$1$2");
+
+ // highlight
+ text = text.replaceAll("\\{==(.*)==}", "$1");
+
+ String canonicalUrl = settings.getString(Keys.web.canonicalUrl, "https://localhost:8443");
+
+ // emphasize and link mentions
+ String mentionReplacement = String.format(" **[@$1](%1s/user/$1)**", canonicalUrl);
+ text = text.replaceAll("\\s@([A-Za-z0-9-_]+)", mentionReplacement);
+
+ // link ticket refs
+ String ticketReplacement = MessageFormat.format("$1[#$2]({0}/tickets?r={1}&h=$2)$3", canonicalUrl, repositoryName);
+ text = text.replaceAll("([\\s,]+)#(\\d+)([\\s,:\\.\\n])", ticketReplacement);
+
+ // link commit shas
+ int shaLen = settings.getInteger(Keys.web.shortCommitIdLength, 6);
+ String commitPattern = MessageFormat.format("\\s([A-Fa-f0-9]'{'{0}'}')([A-Fa-f0-9]'{'{1}'}')", shaLen, 40 - shaLen);
+ String commitReplacement = String.format(" [`$1`](%1$s/commit?r=%2$s&h=$1$2)", canonicalUrl, repositoryName);
+ text = text.replaceAll(commitPattern, commitReplacement);
+
+ String html = transformMarkdown(text);
+ return html;
+ }
+}
diff --git a/src/main/java/com/gitblit/utils/RefLogUtils.java b/src/main/java/com/gitblit/utils/RefLogUtils.java
index 67b4d550..bd710e5b 100644
--- a/src/main/java/com/gitblit/utils/RefLogUtils.java
+++ b/src/main/java/com/gitblit/utils/RefLogUtils.java
@@ -65,9 +65,9 @@
/**
* Utility class for maintaining a reflog within a git repository on an orphan branch.
- *
+ *
* @author James Moger
- *
+ *
*/
public class RefLogUtils {
@@ -77,7 +77,7 @@ public class RefLogUtils {
/**
* Log an error message and exception.
- *
+ *
* @param t
* @param repository
* if repository is not null it MUST be the {0} parameter in the pattern.
@@ -99,13 +99,13 @@ private static void error(Throwable t, Repository repository, String pattern, Ob
/**
* Returns true if the repository has a reflog branch.
- *
+ *
* @param repository
* @return true if the repository has a reflog branch
*/
public static boolean hasRefLogBranch(Repository repository) {
try {
- return repository.getRef(GB_REFLOG) != null;
+ return repository.exactRef(GB_REFLOG) != null;
} catch (Exception e) {
LOGGER.error("failed to determine hasRefLogBranch", e);
}
@@ -114,7 +114,7 @@ public static boolean hasRefLogBranch(Repository repository) {
/**
* Returns a RefModel for the reflog branch in the repository. If the branch can not be found, null is returned.
- *
+ *
* @param repository
* @return a refmodel for the reflog branch or null
*/
@@ -173,7 +173,7 @@ private static UserModel newUserModelFrom(PersonIdent ident) {
/**
* Logs a ref deletion.
- *
+ *
* @param user
* @param repository
* @param ref
@@ -204,7 +204,7 @@ public static boolean deleteRef(UserModel user, Repository repository, Ref ref)
/**
* Updates the reflog with the received commands.
- *
+ *
* @param user
* @param repository
* @param commands
@@ -293,7 +293,7 @@ public static boolean updateRefLog(UserModel user, Repository repository, Collec
/**
* Creates an in-memory index of the reflog entry.
- *
+ *
* @param repo
* @param headId
* @param commands
@@ -405,7 +405,7 @@ public static List getRefLog(String repositoryName, Repository repo
/**
* Returns the list of reflog entries as they were recorded by Gitblit. Each RefLogEntry may represent multiple ref updates.
- *
+ *
* @param repositoryName
* @param repository
* @param minimumDate
@@ -489,7 +489,7 @@ public static List getRefLog(String repositoryName, Repository repo
/**
* Returns the list of entries organized by ref (e.g. each ref has it's own RefLogEntry object).
- *
+ *
* @param repositoryName
* @param repository
* @param maxCount
@@ -501,7 +501,7 @@ public static List getLogByRef(String repositoryName, Repository re
/**
* Returns the list of entries organized by ref (e.g. each ref has it's own RefLogEntry object).
- *
+ *
* @param repositoryName
* @param repository
* @param offset
@@ -547,7 +547,7 @@ public static List getLogByRef(String repositoryName, Repository re
/**
* Returns the list of ref changes separated by ref (e.g. each ref has it's own RefLogEntry object).
- *
+ *
* @param repositoryName
* @param repository
* @param minimumDate
@@ -584,7 +584,7 @@ public static List getLogByRef(String repositoryName, Repository re
/**
* Returns a commit log grouped by day.
- *
+ *
* @param repositoryName
* @param repository
* @param minimumDate
@@ -679,7 +679,7 @@ public static List getDailyLog(String repositoryName, Repository
/**
* Returns the list of commits separated by ref (e.g. each ref has it's own RefLogEntry object for each day).
- *
+ *
* @param repositoryName
* @param repository
* @param minimumDate
diff --git a/src/main/java/com/gitblit/wicket/MarkupProcessor.java b/src/main/java/com/gitblit/wicket/MarkupProcessor.java
new file mode 100644
index 00000000..5d487432
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/MarkupProcessor.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket;
+
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.wicket.Page;
+import org.apache.wicket.RequestCycle;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.mylyn.wikitext.confluence.core.ConfluenceLanguage;
+import org.eclipse.mylyn.wikitext.core.parser.Attributes;
+import org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
+import org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder;
+import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage;
+import org.eclipse.mylyn.wikitext.mediawiki.core.MediaWikiLanguage;
+import org.eclipse.mylyn.wikitext.textile.core.TextileLanguage;
+import org.eclipse.mylyn.wikitext.tracwiki.core.TracWikiLanguage;
+import org.eclipse.mylyn.wikitext.twiki.core.TWikiLanguage;
+import org.pegdown.DefaultVerbatimSerializer;
+import org.pegdown.LinkRenderer;
+import org.pegdown.ToHtmlSerializer;
+import org.pegdown.VerbatimSerializer;
+import org.pegdown.plugins.ToHtmlSerializerPlugin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.models.PathModel;
+import com.gitblit.servlet.RawServlet;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.MarkdownUtils.Linker;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.XssFilter;
+import com.gitblit.wicket.pages.DocPage;
+import com.google.common.base.Joiner;
+import com.vladsch.flexmark.html.renderer.LinkStatus;
+import com.vladsch.flexmark.html.renderer.ResolvedLink;
+
+/**
+ * Processes markup content and generates html with repository-relative page and
+ * image linking.
+ *
+ * @author James Moger
+ *
+ */
+public class MarkupProcessor {
+
+ public enum MarkupSyntax {
+ PLAIN, MARKDOWN, TWIKI, TRACWIKI, TEXTILE, MEDIAWIKI, CONFLUENCE
+ }
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final IStoredSettings settings;
+
+ private final XssFilter xssFilter;
+
+ public static List getMarkupExtensions(IStoredSettings settings) {
+ List list = new ArrayList();
+ list.addAll(settings.getStrings(Keys.web.confluenceExtensions));
+ list.addAll(settings.getStrings(Keys.web.markdownExtensions));
+ list.addAll(settings.getStrings(Keys.web.mediawikiExtensions));
+ list.addAll(settings.getStrings(Keys.web.textileExtensions));
+ list.addAll(settings.getStrings(Keys.web.tracwikiExtensions));
+ list.addAll(settings.getStrings(Keys.web.twikiExtensions));
+ return list;
+ }
+
+ public MarkupProcessor(IStoredSettings settings, XssFilter xssFilter) {
+ this.settings = settings;
+ this.xssFilter = xssFilter;
+ }
+
+ public List getMarkupExtensions() {
+ return getMarkupExtensions(settings);
+ }
+
+ public List getAllExtensions() {
+ List list = getMarkupExtensions(settings);
+ list.add("txt");
+ list.add("TXT");
+ return list;
+ }
+
+ private List 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)) {
+ return MarkupSyntax.PLAIN;
+ }
+
+ if (settings.getStrings(Keys.web.confluenceExtensions).contains(ext)) {
+ return MarkupSyntax.CONFLUENCE;
+ } else if (settings.getStrings(Keys.web.markdownExtensions).contains(ext)) {
+ return MarkupSyntax.MARKDOWN;
+ } else if (settings.getStrings(Keys.web.mediawikiExtensions).contains(ext)) {
+ return MarkupSyntax.MEDIAWIKI;
+ } else if (settings.getStrings(Keys.web.textileExtensions).contains(ext)) {
+ return MarkupSyntax.TEXTILE;
+ } else if (settings.getStrings(Keys.web.tracwikiExtensions).contains(ext)) {
+ return MarkupSyntax.TRACWIKI;
+ } else if (settings.getStrings(Keys.web.twikiExtensions).contains(ext)) {
+ return MarkupSyntax.TWIKI;
+ }
+
+ return MarkupSyntax.PLAIN;
+ }
+
+ public boolean hasRootDocs(Repository r) {
+ List roots = getRoots();
+ List extensions = getAllExtensions();
+ List paths = JGitUtils.getFilesInPath(r, null, null);
+ for (PathModel path : paths) {
+ if (!path.isTree()) {
+ 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;
+ }
+
+ public List getRootDocs(Repository r, String repositoryName, String commitId) {
+ List roots = getRoots();
+ List list = getDocs(r, repositoryName, commitId, roots);
+ return list;
+ }
+
+ public MarkupDocument getReadme(Repository r, String repositoryName, String commitId) {
+ List list = getDocs(r, repositoryName, commitId, Arrays.asList("readme"));
+ if (list.isEmpty()) {
+ return null;
+ }
+ return list.get(0);
+ }
+
+ private List getDocs(Repository r, String repositoryName, String commitId, List names) {
+ List extensions = getAllExtensions();
+ String [] encodings = getEncodings();
+ Map map = new HashMap();
+ RevCommit commit = JGitUtils.getCommit(r, commitId);
+ List 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 list = new ArrayList();
+ 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) {
+ final MarkupSyntax syntax = determineSyntax(documentPath);
+ final MarkupDocument doc = new MarkupDocument(documentPath, markupText, syntax);
+
+ if (markupText != null) {
+ try {
+ switch (syntax){
+ case CONFLUENCE:
+ parse(doc, repositoryName, commitId, new ConfluenceLanguage());
+ break;
+ case MARKDOWN:
+ parse(doc, repositoryName, commitId);
+ break;
+ case MEDIAWIKI:
+ parse(doc, repositoryName, commitId, new MediaWikiLanguage());
+ break;
+ case TEXTILE:
+ parse(doc, repositoryName, commitId, new TextileLanguage());
+ break;
+ case TRACWIKI:
+ parse(doc, repositoryName, commitId, new TracWikiLanguage());
+ break;
+ case TWIKI:
+ parse(doc, repositoryName, commitId, new TWikiLanguage());
+ break;
+ default:
+ doc.html = MarkdownUtils.transformPlainText(markupText);
+ break;
+ }
+ } catch (Exception e) {
+ logger.error("failed to transform " + syntax, e);
+ }
+ }
+
+ if (doc.html == null) {
+ // failed to transform markup
+ if (markupText == null) {
+ markupText = String.format("Document %1$s not found in %2$s", documentPath, repositoryName);
+ }
+ markupText = MessageFormat.format("{0}: {1}
{2}", "Error", "failed to parse markup", markupText);
+ doc.html = StringUtils.breakLinesForHtml(markupText);
+ }
+
+ return doc;
+ }
+
+ /**
+ * Parses the markup using the specified markup language
+ *
+ * @param doc
+ * @param repositoryName
+ * @param commitId
+ * @param lang
+ */
+ private void parse(final MarkupDocument doc, final String repositoryName, final String commitId, MarkupLanguage lang) {
+ StringWriter writer = new StringWriter();
+ HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer) {
+
+ @Override
+ public void image(Attributes attributes, String imagePath) {
+ String url;
+ if (imagePath.indexOf("://") == -1) {
+ // relative image
+ String path = doc.getRelativePath(imagePath);
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
+ } else {
+ // absolute image
+ url = imagePath;
+ }
+ super.image(attributes, url);
+ }
+
+ @Override
+ public void link(Attributes attributes, String hrefOrHashName, String text) {
+ String url;
+ if (hrefOrHashName.charAt(0) != '#') {
+ if (hrefOrHashName.indexOf("://") == -1) {
+ // relative link
+ String path = doc.getRelativePath(hrefOrHashName);
+ url = getWicketUrl(DocPage.class, repositoryName, commitId, path);
+ } else {
+ // absolute link
+ url = hrefOrHashName;
+ }
+ } else {
+ // page-relative hash link
+ url = hrefOrHashName;
+ }
+ super.link(attributes, url, text);
+ }
+ };
+
+ // avoid the and tags
+ builder.setEmitAsDocument(false);
+
+ MarkupParser parser = new MarkupParser(lang);
+ parser.setBuilder(builder);
+ parser.parse(doc.markup);
+
+ final String content = writer.toString();
+ final String safeContent = xssFilter.relaxed(content);
+
+ doc.html = safeContent;
+ }
+
+ /**
+ * Parses the document as Markdown using Pegdown.
+ *
+ * @param doc
+ * @param repositoryName
+ * @param commitId
+ */
+ private void parse(final MarkupDocument doc, final String repositoryName, final String commitId) {
+ Linker renderer = new Linker() {
+
+ @Override
+ public ResolvedLink link(ResolvedLink original, boolean image) {
+ if (image) {
+ String path = doc.getRelativePath(original.getUrl());
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ String url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
+ return original.withStatus(LinkStatus.VALID).withUrl(url);
+ } else {
+ String path = doc.getRelativePath(original.getUrl());
+ String url = getWicketUrl(DocPage.class, repositoryName, commitId, path);
+ return original.withStatus(LinkStatus.VALID).withUrl(url);
+ }
+ }
+ };
+
+ final String content = MarkdownUtils.transformMarkdown(doc.markup, renderer);
+ final String safeContent = xssFilter.relaxed(content);
+
+ doc.html = safeContent;
+ }
+
+ private String getWicketUrl(Class extends Page> pageClass, final String repositoryName, final String commitId, final String document) {
+ String fsc = settings.getString(Keys.web.forwardSlashCharacter, "/");
+ String encodedPath = document.replace(' ', '-');
+ try {
+ encodedPath = URLEncoder.encode(encodedPath, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error(null, e);
+ }
+ encodedPath = encodedPath.replace("/", fsc).replace("%2F", fsc);
+
+ String url = RequestCycle.get().urlFor(pageClass, WicketUtils.newPathParameter(repositoryName, commitId, encodedPath)).toString();
+ return url;
+ }
+
+// private String getDocumentName(final String document) {
+// // extract document name
+// String name = StringUtils.stripFileExtension(document);
+// name = name.replace('_', ' ');
+// if (name.indexOf('/') > -1) {
+// name = name.substring(name.lastIndexOf('/') + 1);
+// }
+// return name;
+// }
+
+ public static class MarkupDocument implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String documentPath;
+ public final String markup;
+ public final MarkupSyntax syntax;
+ public String html;
+
+ MarkupDocument(String documentPath, String markup, MarkupSyntax syntax) {
+ this.documentPath = documentPath;
+ this.markup = markup;
+ this.syntax = syntax;
+ }
+
+ String getCurrentPath() {
+ String basePath = "";
+ if (documentPath.indexOf('/') > -1) {
+ basePath = documentPath.substring(0, documentPath.lastIndexOf('/') + 1);
+ if (basePath.charAt(0) == '/') {
+ return basePath.substring(1);
+ }
+ }
+ return basePath;
+ }
+
+ String getRelativePath(String ref) {
+ if (ref.charAt(0) == '/') {
+ // absolute path in repository
+ return ref.substring(1);
+ } else {
+ // resolve relative repository path
+ String cp = getCurrentPath();
+ if (StringUtils.isEmpty(cp)) {
+ return ref;
+ }
+ // this is a simple relative path resolver
+ List currPathStrings = new ArrayList(Arrays.asList(cp.split("/")));
+ String file = ref;
+ while (file.startsWith("../")) {
+ // strip ../ from the file reference
+ // drop the last path element
+ file = file.substring(3);
+ currPathStrings.remove(currPathStrings.size() - 1);
+ }
+ currPathStrings.add(file);
+ String path = Joiner.on("/").join(currPathStrings);
+ return path;
+ }
+ }
+ }
+
+ /**
+ * This class implements a workaround for a bug reported in issue-379.
+ * The bug was introduced by my own pegdown pull request #115.
+ *
+ * @author James Moger
+ *
+ */
+ public static class WorkaroundHtmlSerializer extends ToHtmlSerializer {
+
+ public WorkaroundHtmlSerializer(final LinkRenderer linkRenderer) {
+ super(linkRenderer,
+ Collections.singletonMap(VerbatimSerializer.DEFAULT, DefaultVerbatimSerializer.INSTANCE),
+ Collections.emptyList());
+ }
+ private void printAttribute(String name, String value) {
+ printer.print(' ').print(name).print('=').print('"').print(value).print('"');
+ }
+
+ /* Reimplement print image tag to eliminate a trailing double-quote */
+ @Override
+ protected void printImageTag(LinkRenderer.Rendering rendering) {
+ printer.print("");
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
index 96ebe7af..d53cee71 100644
--- a/src/main/java/com/gitblit/wicket/pages/RootPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -79,12 +79,13 @@
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.NavigationPanel;
import com.google.common.base.Strings;
+import com.googlesource.gerrit.plugins.gitblit.HttpUtils;
/**
* Root page is a topbar, navigable page like Repositories, Users, or Federation.
- *
+ *
* @author James Moger
- *
+ *
*/
public abstract class RootPage extends BasePage {
@@ -618,7 +619,7 @@ protected void onInitialize() {
UserModel user = session.getUser();
boolean editCredentials = app().authentication().supportsCredentialChanges(user);
HttpServletRequest request = ((WebRequest) getRequest()).getHttpServletRequest();
- AuthenticationType authenticationType = (AuthenticationType) request.getSession().getAttribute(Constants.ATTRIB_AUTHTYPE);
+ AuthenticationType authenticationType = HttpUtils.getAttribute(request.getSession(), Constants.ATTRIB_AUTHTYPE, AuthenticationType.class);
boolean standardLogin = (null != authenticationType) ? authenticationType.isStandard() : true;
if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {
@@ -681,7 +682,7 @@ protected void onInitialize() {
/**
* Creates a submenu. This is not actually submenu because we're using an older Twitter Bootstrap which is pre-submenu.
- *
+ *
* @param wicketId
* @param submenuTitle
* @param menuItems
diff --git a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java
new file mode 100644
index 00000000..410e0677
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.panels;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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.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;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.model.StringResourceModel;
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.Constants;
+import com.gitblit.models.RefModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.servlet.RawServlet;
+import com.gitblit.servlet.SyndicationServlet;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.BranchesPage;
+import com.gitblit.wicket.pages.CommitPage;
+import com.gitblit.wicket.pages.GitSearchPage;
+import com.gitblit.wicket.pages.LogPage;
+import com.gitblit.wicket.pages.MetricsPage;
+import com.gitblit.wicket.pages.TreePage;
+
+public class BranchesPanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private final boolean hasBranches;
+
+ public BranchesPanel(String wicketId, final RepositoryModel model, Repository r,
+ final int maxCount, final boolean showAdmin) {
+ super(wicketId);
+
+ // branches
+ List branches = new ArrayList();
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (user == null) {
+ user = UserModel.ANONYMOUS;
+ }
+
+ List localBranches = JGitUtils.getLocalBranches(r, false, -1);
+ for (RefModel refModel : localBranches) {
+ if (user.canView(model, refModel.reference.getName())) {
+ branches.add(refModel);
+ }
+ }
+ if (model.showRemoteBranches) {
+ List remoteBranches = JGitUtils.getRemoteBranches(r, false, -1);
+ for (RefModel refModel : remoteBranches) {
+ if (user.canView(model, refModel.reference.getName())) {
+ branches.add(refModel);
+ }
+ }
+ }
+ Collections.sort(branches);
+ Collections.reverse(branches);
+ if (maxCount > 0 && branches.size() > maxCount) {
+ branches = new ArrayList(branches.subList(0, maxCount));
+ }
+
+ if (maxCount > 0) {
+ // summary page
+ // show branches page link
+ add(new LinkPanel("branches", "title", new StringResourceModel("gb.branches", this,
+ null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
+ } else {
+ // branches page
+ add(new Label("branches", new StringResourceModel("gb.branches", this, null)));
+ }
+
+ // only allow delete if we have multiple branches
+ // final boolean showDelete = showAdmin && branches.size() > 1;
+
+ ListDataProvider branchesDp = new ListDataProvider(branches);
+ DataView branchesView = new DataView("branch", branchesDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ @Override
+ public void populateItem(final Item item) {
+ final RefModel entry = item.getModelObject();
+
+ PageParameters shortUniqRef = WicketUtils.newObjectParameter(model.name,
+ Repository.shortenRefName(entry.getName()));
+
+ item.add(WicketUtils.createDateLabel("branchDate", entry.getDate(), getTimeZone(), getTimeUtils()));
+
+ item.add(new LinkPanel("branchName", "list name", StringUtils.trimString(
+ entry.displayName, 28), LogPage.class, shortUniqRef));
+
+ String author = entry.getAuthorIdent().getName();
+ LinkPanel authorLink = new LinkPanel("branchAuthor", "list", author,
+ GitSearchPage.class, WicketUtils.newSearchParameter(model.name,
+ entry.getName(), author, Constants.SearchType.AUTHOR));
+ setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
+ item.add(authorLink);
+
+ // short message
+ String shortMessage = entry.getShortMessage();
+ String trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
+ LinkPanel shortlog = new LinkPanel("branchLog", "list subject", trimmedMessage,
+ CommitPage.class, shortUniqRef);
+ if (!shortMessage.equals(trimmedMessage)) {
+ shortlog.setTooltip(shortMessage);
+ }
+ item.add(shortlog);
+
+ if (maxCount <= 0) {
+ Fragment fragment = new Fragment("branchLinks", /* showDelete? "branchPageAdminLinks" : */ "branchPageLinks", this);
+ fragment.add(new BookmarkablePageLink("log", LogPage.class, shortUniqRef));
+ fragment.add(new BookmarkablePageLink("tree", TreePage.class, shortUniqRef));
+ String rawUrl = RawServlet.asLink(getContextUrl(), model.name, Repository.shortenRefName(entry.getName()), null);
+ fragment.add(new ExternalLink("raw", rawUrl));
+ fragment.add(new BookmarkablePageLink("metrics", MetricsPage.class, shortUniqRef));
+ fragment.add(new ExternalLink("syndication", SyndicationServlet.asLink(
+ getRequest().getRelativePathPrefixToContextRoot(), model.name,
+ Repository.shortenRefName(entry.getName()), 0)));
+// if (showDelete) {
+// fragment.add(createDeleteBranchLink(model, entry));
+// }
+ item.add(fragment);
+ } else {
+ Fragment fragment = new Fragment("branchLinks", "branchPanelLinks", this);
+ fragment.add(new BookmarkablePageLink("log", LogPage.class, shortUniqRef));
+ fragment.add(new BookmarkablePageLink("tree", TreePage.class, shortUniqRef));
+ String rawUrl = RawServlet.asLink(getContextUrl(), model.name, Repository.shortenRefName(entry.getName()), null);
+ fragment.add(new ExternalLink("raw", rawUrl));
+ item.add(fragment);
+ }
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(branchesView);
+ if (branches.size() < maxCount || maxCount <= 0) {
+ add(new Label("allBranches", "").setVisible(false));
+ } else {
+ add(new LinkPanel("allBranches", "link", new StringResourceModel("gb.allBranches",
+ this, null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
+ }
+ // We always have 1 branch
+ hasBranches = (branches.size() > 1)
+ || ((branches.size() == 1) && !branches.get(0).displayName
+ .equalsIgnoreCase("master"));
+ }
+
+ public BranchesPanel hideIfEmpty() {
+ setVisible(hasBranches);
+ return this;
+ }
+
+// private Link createDeleteBranchLink(final RepositoryModel repositoryModel, final RefModel entry)
+// {
+// Link deleteLink = new Link("deleteBranch") {
+// private static final long serialVersionUID = 1L;
+//
+// @Override
+// public void onClick() {
+// Repository r = app().repositories().getRepository(repositoryModel.name);
+// if (r == null) {
+// if (app().repositories().isCollectingGarbage(repositoryModel.name)) {
+// error(MessageFormat.format(getString("gb.busyCollectingGarbage"), repositoryModel.name));
+// } else {
+// error(MessageFormat.format("Failed to find repository {0}", repositoryModel.name));
+// }
+// return;
+// }
+// final String branch = entry.getName();
+// Ref ref = null;
+// try {
+// ref = r.findRef(branch);
+// if (ref == null && !branch.startsWith(Constants.R_HEADS)) {
+// ref = r.exactRef(Constants.R_HEADS + branch);
+// }
+// } catch (IOException e) {
+// }
+// if (ref != null) {
+// boolean success = JGitUtils.deleteBranchRef(r, ref.getName());
+// if (success) {
+// // clear commit cache
+// CommitCache.instance().clear(repositoryModel.name, branch);
+//
+// // optionally update reflog
+// if (RefLogUtils.hasRefLogBranch(r)) {
+// UserModel user = GitBlitWebSession.get().getUser();
+// RefLogUtils.deleteRef(user, r, ref);
+// }
+// }
+//
+// if (success) {
+// info(MessageFormat.format("Branch \"{0}\" deleted", branch));
+// } else {
+// error(MessageFormat.format("Failed to delete branch \"{0}\"", branch));
+// }
+// }
+// r.close();
+//
+// // redirect to the owning page
+// PageParameters params = WicketUtils.newRepositoryParameter(repositoryModel.name);
+// String relativeUrl = urlFor(getPage().getClass(), params).toString();
+// String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
+// getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
+// }
+// };
+//
+// deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
+// "Delete branch \"{0}\"?", entry.displayName )));
+// return deleteLink;
+// }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitInitStep.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitInitStep.java
index b7d5accb..5aeb126b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitInitStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitInitStep.java
@@ -51,18 +51,14 @@ public void run() throws Exception {
}
private void configureGitBlit() {
- Section gitWeb = sections.get("gitweb", null);
- gitWeb.set("type", "custom");
- gitWeb.set("url", "plugins/" + pluginName + '/');
- gitWeb.set("project", "summary/?r=${project}");
- gitWeb.set("revision", "commit/?r=${project}&h=${commit}");
- gitWeb.set("branch", "log/?r=${project}&h=${branch}");
- gitWeb.set("filehistory", "history/?f=${file}&r=${project}&h=${branch}");
- gitWeb.set("file", "blob/?r=${project}&h=${commit}&f=${file}");
- gitWeb.set("roottree", "tree/?r=${project}&h=${commit}");
- gitWeb.string("Link name", "linkname", "GitBlit");
+ String defaultLinkName = cfg.getString("gitweb", null, "linkname");
+ if (defaultLinkName == null || defaultLinkName.isEmpty()) {
+ defaultLinkName = "GitBlit";
+ }
+ cfg.unsetSection("gitweb", null);
Section pluginCfg = sections.get("plugin", pluginName);
// These values are displayed in the UI.
+ pluginCfg.string("Link name", "linkname", defaultLinkName, true);
pluginCfg.string("\"Repositories\" submenu title", "repositories", "Repositories", true);
pluginCfg.string("\"Activity\" submenu title", "activity", "Activity", true);
pluginCfg.string("\"Documentation\" submenu title", "documentation", "Documentation", true);
@@ -78,7 +74,7 @@ private void configureGitBlit() {
pluginCfg.set("search", newValue);
}
}
- pluginCfg.string("\"Browse\" submenu title for the \"Projects\" top-level menu", "browse", "Browse", true);
+ pluginCfg.unset("browse");
// If everything is at the default, then make sure we don't have the section at all.
if (cfg.getNames("plugin", pluginName).isEmpty()) {
cfg.unsetSection("plugin", pluginName);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitModule.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitModule.java
index 5417f559..0d29a2e3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitModule.java
@@ -14,6 +14,13 @@
package com.googlesource.gerrit.plugins.gitblit;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.BranchWebLink;
+import com.google.gerrit.extensions.webui.FileHistoryWebLink;
+import com.google.gerrit.extensions.webui.FileWebLink;
+import com.google.gerrit.extensions.webui.ParentWebLink;
+import com.google.gerrit.extensions.webui.PatchSetWebLink;
+import com.google.gerrit.extensions.webui.ProjectWebLink;
+import com.google.gerrit.extensions.webui.TagWebLink;
import com.google.gerrit.extensions.webui.TopMenu;
import com.google.inject.AbstractModule;
@@ -22,6 +29,13 @@ public class GitBlitModule extends AbstractModule {
@Override
protected void configure() {
DynamicSet.bind(binder(), TopMenu.class).to(GitBlitTopMenu.class);
+ DynamicSet.bind(binder(), BranchWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), FileHistoryWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), FileWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), ParentWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), PatchSetWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), ProjectWebLink.class).to(GitBlitWebUrls.class);
+ DynamicSet.bind(binder(), TagWebLink.class).to(GitBlitWebUrls.class);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitTopMenu.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitTopMenu.java
index 3523cdfa..c0e8612c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitTopMenu.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitTopMenu.java
@@ -19,7 +19,6 @@
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.extensions.client.GerritTopMenu;
import com.google.gerrit.extensions.client.MenuItem;
import com.google.gerrit.extensions.webui.TopMenu;
import com.google.gerrit.server.CurrentUser;
@@ -35,7 +34,6 @@ public class GitBlitTopMenu implements TopMenu {
private final MenuEntry fullMenuEntries;
private final MenuEntry restrictedMenuEntries;
- private final MenuEntry extraProjectEntries;
private final Provider userProvider;
@Inject
@@ -63,15 +61,10 @@ public GitBlitTopMenu(@PluginName String pluginName, @PluginCanonicalWebUrl Stri
}
fullMenuItems.add(documentation);
fullMenuEntries = new MenuEntry(GITBLIT_TOPMENU_NAME, fullMenuItems);
- // Actually, I'd like to give the project "browse" link only if the user has the right to see the contents of the repository. But how would
- // I know in getEntries() below which is the "current" project? Since I don't know how to do that, we show that link always, based on the
- // assumption that if a user can see a project at all, he can also see its contents. If not, gitblit will tell him so...
- extraProjectEntries = new MenuEntry(GerritTopMenu.PROJECTS, Arrays.asList(new MenuItem(cfg.getString("browse", "Browse"), gitBlitBaseUrl
- + "summary/?r=${projectName}", "")));
}
@Override
public List getEntries() {
- return Arrays.asList(userProvider.get().isIdentifiedUser() ? fullMenuEntries : restrictedMenuEntries, extraProjectEntries);
+ return Arrays.asList(userProvider.get().isIdentifiedUser() ? fullMenuEntries : restrictedMenuEntries);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitWebUrls.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitWebUrls.java
new file mode 100644
index 00000000..29de3cec
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitWebUrls.java
@@ -0,0 +1,104 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.googlesource.gerrit.plugins.gitblit;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.common.WebLinkInfo;
+import com.google.gerrit.extensions.webui.BranchWebLink;
+import com.google.gerrit.extensions.webui.FileHistoryWebLink;
+import com.google.gerrit.extensions.webui.FileWebLink;
+import com.google.gerrit.extensions.webui.ParentWebLink;
+import com.google.gerrit.extensions.webui.PatchSetWebLink;
+import com.google.gerrit.extensions.webui.ProjectWebLink;
+import com.google.gerrit.extensions.webui.TagWebLink;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class GitBlitWebUrls implements BranchWebLink, FileWebLink, PatchSetWebLink, ProjectWebLink, FileHistoryWebLink, ParentWebLink, TagWebLink {
+
+ private static final Logger log = LoggerFactory.getLogger(GitBlitWebUrls.class);
+
+ private final String name;
+ private final String baseUrl;
+
+ @Inject
+ public GitBlitWebUrls(@PluginName String pluginName, @PluginCanonicalWebUrl String pluginUrl, @GerritServerConfig Config config) {
+ String baseGerritUrl = "/plugins/" + pluginName + '/';
+ try {
+ if (pluginUrl != null) {
+ URL u = new URL(pluginUrl);
+ baseGerritUrl = u.getPath();
+ if (!baseGerritUrl.endsWith("/")) {
+ baseGerritUrl += '/';
+ }
+ }
+ } catch (MalformedURLException e) {
+ log.error("Canonical web URL is malformed: " + pluginUrl, e);
+ }
+ baseUrl = baseGerritUrl;
+ String linkName = config.getString("plugin", pluginName, "linkname");
+ if (linkName == null || linkName.isEmpty()) {
+ linkName = config.getString("gitweb", null, "linkname");
+ }
+ if (linkName == null || linkName.isEmpty()) {
+ linkName = "Gitblit";
+ }
+ name = linkName;
+ }
+
+ @Override
+ public WebLinkInfo getProjectWeblink(String projectName) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("summary/?r=%s", projectName), Target.BLANK);
+ }
+
+ @Override
+ public WebLinkInfo getPatchSetWebLink(String projectName, String commit) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("commit/?r=%s&h=%s", projectName, commit), Target.BLANK);
+ }
+
+ @Override
+ public WebLinkInfo getParentWebLink(String projectName, String commit) {
+ return getPatchSetWebLink(projectName, commit);
+ }
+
+ @Override
+ public WebLinkInfo getFileWebLink(String projectName, String revision, String fileName) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("blob/?r=%s&h=%s&f=%s", projectName, revision, fileName), Target.BLANK);
+ }
+
+ @Override
+ public WebLinkInfo getBranchWebLink(String projectName, String branchName) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("log/?r=%s&h=%s", projectName, branchName), Target.BLANK);
+ }
+
+ @Override
+ public WebLinkInfo getTagWebLink(String projectName, String tagName) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("log/?r=%s&h=%s", projectName, tagName), Target.BLANK);
+ }
+
+ @Override
+ public WebLinkInfo getFileHistoryWebLink(String projectName, String revision, String fileName) {
+ return new WebLinkInfo(name, null, baseUrl + String.format("history/?f=%s&r=%s&h=%s", fileName, projectName, revision), Target.BLANK);
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/HttpUtils.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/HttpUtils.java
new file mode 100644
index 00000000..e3ed1033
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/HttpUtils.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.googlesource.gerrit.plugins.gitblit;
+
+import java.util.Optional;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+public class HttpUtils {
+
+ // Can't use injection since used in Gitblit wicket pages
+
+ public static T getAttribute(final HttpServletRequest httpRequest, String attribute, Class clazz) {
+ Object obj = httpRequest.getAttribute(attribute);
+ if (obj instanceof Optional) {
+ obj = ((Optional>) obj).orElse(null);
+ }
+ if (clazz.isInstance(obj)) {
+ return clazz.cast(obj);
+ }
+ return null;
+ }
+
+ public static T getAttribute(final HttpSession session, String attribute, Class clazz) {
+ Object obj = session.getAttribute(attribute);
+ if (obj instanceof Optional) {
+ obj = ((Optional>) obj).orElse(null);
+ }
+ if (clazz.isInstance(obj)) {
+ return clazz.cast(obj);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlitWebApp.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlitWebApp.java
index ea7d2131..558ee585 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlitWebApp.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlitWebApp.java
@@ -56,6 +56,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.gitblit.HttpUtils;
@Singleton
public class GerritGitBlitWebApp extends GitBlitWebApp {
@@ -93,7 +94,7 @@ public IHeaderResponse decorate(IHeaderResponse response) {
/**
* Sets a unique key that is different for each plugin instance (i.e., different for each load of the plugin.)
- *
+ *
* @param key
* for this plugin instance.
*/
@@ -166,7 +167,7 @@ private void resetWicketSessionOnPluginReload(WebRequestCycle requestCycle) {
WebRequest request = requestCycle.getWebRequest();
HttpSession realSession = request.getHttpServletRequest().getSession();
if (realSession != null) {
- Object sessionPluginInstanceKey = realSession.getAttribute(INSTANCE_ATTRIBUTE);
+ Object sessionPluginInstanceKey = HttpUtils.getAttribute(realSession, INSTANCE_ATTRIBUTE, String.class);
String currentPluginInstanceKey = getPluginInstanceKey();
if (sessionPluginInstanceKey instanceof String) {
if (!sessionPluginInstanceKey.equals(currentPluginInstanceKey)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitAuthenticationManager.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitAuthenticationManager.java
index 253e77d9..7361d9ca 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitAuthenticationManager.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitAuthenticationManager.java
@@ -47,6 +47,7 @@
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.gitblit.HttpUtils;
@Singleton
public class GerritGitBlitAuthenticationManager implements IAuthenticationManager {
@@ -114,9 +115,9 @@ public IAuthenticationManager stop() {
@Override
public UserModel authenticate(final HttpServletRequest httpRequest) {
// Added by the GerritAuthenticationFilter.
- String username = (String) httpRequest.getAttribute("gerrit-username");
- String token = (String) httpRequest.getAttribute("gerrit-token");
- String password = (String) httpRequest.getAttribute("gerrit-password");
+ String username = HttpUtils.getAttribute(httpRequest, "gerrit-username", String.class);
+ String token = HttpUtils.getAttribute(httpRequest, "gerrit-token", String.class);
+ String password = HttpUtils.getAttribute(httpRequest, "gerrit-password", String.class);
if (!Strings.isNullOrEmpty(token)) {
return authenticateFromSession(httpRequest, username, token);
@@ -154,7 +155,7 @@ private UserModel authenticateFromSession(final HttpServletRequest httpRequest,
return null;
}
- String userName = session.getUser().getUserName();
+ String userName = session.getUser().getUserName().orElse(null);
// Gerrit users not necessarily have a username. Google OAuth returns users without user names.
UserModel user;
if (Strings.isNullOrEmpty(userName)) {
@@ -227,7 +228,7 @@ private boolean isStandardLogin(HttpServletRequest request) {
return false;
}
HttpSession session = request.getSession();
- AuthenticationType authenticationType = (AuthenticationType) session.getAttribute(Constants.ATTRIB_AUTHTYPE);
+ AuthenticationType authenticationType = HttpUtils.getAttribute(session, Constants.ATTRIB_AUTHTYPE, AuthenticationType.class);
return authenticationType != null && authenticationType.isStandard();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserManager.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserManager.java
index 99fff8a1..d618d3cf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserManager.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserManager.java
@@ -34,8 +34,8 @@
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GetDiffPreferences;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.restapi.account.GetDiffPreferences;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserModel.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserModel.java
index 6eb79cbf..82c6ca95 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserModel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritGitBlitUserModel.java
@@ -24,18 +24,18 @@
import com.gitblit.utils.StringUtils;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.GetDiffPreferences;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackend.ForProject;
import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.restapi.account.GetDiffPreferences;
import com.google.inject.Provider;
/**
@@ -81,7 +81,7 @@ public GerritGitBlitUserModel(String username, final PermissionBackend permissio
@Override
protected boolean canAccess(final RepositoryModel repository, final AccessRestrictionType ifRestriction, final AccessPermission requirePermission) {
- ForProject projectPermissions = permissions.user(userProvider).project(new NameKey(StringUtils.stripDotGit(repository.name)));
+ ForProject projectPermissions = permissions.user(userProvider.get()).project(new NameKey(StringUtils.stripDotGit(repository.name)));
if (projectPermissions == null) {
return false;
}
@@ -99,13 +99,13 @@ protected boolean canAccess(final RepositoryModel repository, final AccessRestri
@Override
public boolean hasRepositoryPermission(String name) {
- ForProject projectPermissions = permissions.user(userProvider).project(new NameKey(StringUtils.stripDotGit(name)));
+ ForProject projectPermissions = permissions.user(userProvider.get()).project(new NameKey(StringUtils.stripDotGit(name)));
return projectPermissions != null && projectPermissions.testOrFalse(ProjectPermission.ACCESS);
}
@Override
public boolean canView(RepositoryModel repository, String ref) {
- ForProject projectPermissions = permissions.user(userProvider).project(new NameKey(StringUtils.stripDotGit(repository.name)));
+ ForProject projectPermissions = permissions.user(userProvider.get()).project(new NameKey(StringUtils.stripDotGit(repository.name)));
if (projectPermissions != null) {
ForRef refPermissions = projectPermissions.ref(ref);
return refPermissions != null && refPermissions.testOrFalse(RefPermission.READ);
@@ -128,7 +128,7 @@ public int diffContext() {
if (diffPrefs != null) {
return diffPrefs.context;
}
- } catch (AuthException | ConfigInvalidException | PermissionBackendException | IOException e) {
+ } catch (RestApiException | ConfigInvalidException | PermissionBackendException | IOException e) {
// Ignore and return default below.
}
}
diff --git a/src/main/java/com/syntevo/bugtraq/BugtraqConfig.java b/src/main/java/com/syntevo/bugtraq/BugtraqConfig.java
index 3a5a6117..dcd6b228 100644
--- a/src/main/java/com/syntevo/bugtraq/BugtraqConfig.java
+++ b/src/main/java/com/syntevo/bugtraq/BugtraqConfig.java
@@ -178,7 +178,7 @@ private static Config getBaseConfig(@NotNull Repository repository, @NotNull Str
RevWalk rw = new RevWalk(repository);
try (TreeWalk tw = new TreeWalk(repository)) {
tw.setFilter(PathFilterGroup.createFromStrings(configFileName));
- Ref head = repository.getRef(Constants.HEAD);
+ Ref head = repository.exactRef(Constants.HEAD);
if (head == null) {
return null;
}
diff --git a/src/main/resources/Documentation/index.md b/src/main/resources/Documentation/index.md
index 481e87ea..787482e7 100644
--- a/src/main/resources/Documentation/index.md
+++ b/src/main/resources/Documentation/index.md
@@ -15,30 +15,11 @@ This is a privately maintained fork of the official Gerrit-Gitblit plugin. Pleas
# Configuration
-There are two different configurations: one for Gerrit so it knows how to generate links that will be processed by the plugin, and
-an optional GitBlit configuration for the plugin itself.
+The plugin in configured in Gerrit's global configuration file `gerrit.config`.
+As of v2.16.171.0, this plugin does _not_ use the `[gitweb]` section anymore.
+Configuration is done in the `[plugin "@PLUGIN@"]` section.
-## Gerrit configuration
-
-In Gerrit's `gerrit.config`, define the `[gitweb]` section as follows:
-
- [gitweb]
- type = custom
- url = plugins/@PLUGIN@/
- linkname = browse
- project = summary/?r=${project}
- revision = commit/?r=${project}&h=${commit}
- branch = log/?r=${project}&h=${branch}
- filehistory = history/?f=${file}&r=${project}&h=${branch}
- file = blob/?r=${project}&h=${commit}&f=${file}
- roottree = tree/?r=${project}&h=${commit}
-
-This is normally done automatically if you add the plugin and run through `java -jar gerrit.war init -d site_path`, but you can also
-add this manually to Gerrit's config file. The `linkname` can be adapted to your taste.
-
-### Configuring the top menu
-
-This plugin adds a "GitBlit" top menu to Gerrit, and also a new sub-menu item to the "Projects" top menu. Since v2.11.162.2 of this plugin, the link
+This plugin adds a "GitBlit" top menu to Gerrit. Since v2.11.162.2 of this plugin, the link
texts for all sub-menu items can be configured to your taste in a `[plugin "@PLUGIN@"]` section in your `gerrit.config`. If the section is not present,
or some values in that section are not defined, the plugin uses built-in default texts. The default configuration would correspond to
@@ -47,14 +28,14 @@ or some values in that section are not defined, the plugin uses built-in default
activity = Activity
documentation = Documentation
search =
- browse = Browse
+ linkname = Gitblit
-The first four are sub-menu items of the "GitBlit" top menu, the last one is a new "browse" sub-menu item in Gerrit's "Projects" menu that is shown
-for Gerrit's "current" project (since v2.11.162.2).
+The first four are sub-menu items of the "GitBlit" top menu, the last one is the link text shown on links to Gitblit for individual files
+or in the project list.
The "search" sub-menu item is by default not set and will thus not be shown. Setting it makes only sense if you enable GitBlit indexing on some of
your projects. See the [GitBlit documentation](http://gitblit.com/setup_lucene.html) for more information on that.
-
+
## GitBlit configuration
The plugin includes a minimal default configuration to make GitBlit act only as a repository viewer. You can augment that with further
@@ -66,7 +47,7 @@ To see the built-in configuration, access it at [`gitblit.properties`](@URL@plug
By default, the built-in configuration does allow anonymous browsing, subject to the repository and ref-level access restrictions defined in Gerrit.
If you want to lock the GitBlit plugin to allow only logged-in users to browse, set in `$GERRIT_SITE/etc/gitblit.properties` the key
-`web.authenticateViewPages = true`. This is the only key of the built-in configuration that you _can_ override.
+`web.authenticateViewPages = true`. This is the only key of the built-in configuration that you _can_ override.
GitBlit's ticket service, fan-out service, and its plugin mechanism are disabled in this plugin, as is ssh access through GitBlit since Gerrit
already provides that. Also disabled is Gitblit's LFS implementation.
@@ -95,7 +76,7 @@ The GitBlit `${baseFolder}` is the plugin's data directory provided by Gerrit at
-
+
# Issue tracking