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 +* Installed plugin version: 2.16.171.1-SNAPSHOT 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
-GitBlit plugin 2.15.171.0-SNAPSHOT +GitBlit plugin 2.16.171.1-SNAPSHOT
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 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