diff --git a/Documentation/.gitignore b/Documentation/.gitignore
new file mode 100644
index 0000000000..8a3da24187
--- /dev/null
+++ b/Documentation/.gitignore
@@ -0,0 +1,2 @@
+*.html
+/.published
diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644
index 0000000000..59de209ab3
--- /dev/null
+++ b/Documentation/Makefile
@@ -0,0 +1,95 @@
+# Copyright (C) 2009 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.
+
+ASCIIDOC ?= asciidoc
+ASCIIDOC_EXTRA ?=
+ASCIIDOC_VER ?= 8.6.3
+SVN ?= svn
+PUB_ROOT ?= https://gerrit-documentation.googlecode.com/svn/Documentation
+
+all: html
+
+clean:
+ rm -f *.html
+ rm -rf $(LOCAL_ROOT)
+
+ASCIIDOC_EXE := $(shell which $(ASCIIDOC))
+ifeq ($(wildcard $(ASCIIDOC_EXE)),)
+ $(error $(ASCIIDOC) must be available)
+else
+ ASCIIDOC_OK := $(shell expr `asciidoc --version | cut -f2 -d' '` \>= $(ASCIIDOC_VER))
+ ifeq ($(ASCIIDOC_OK),0)
+ $(error $(ASCIIDOC) version $(ASCIIDOC_VER) or higher is required)
+ endif
+endif
+
+ifeq ($(origin VERSION), undefined)
+ VERSION := $(shell ./GEN-DOC-VERSION 2>/dev/null)
+endif
+
+DOC_HTML := $(patsubst %.txt,%.html,$(wildcard *.txt))
+LOCAL_ROOT := .published
+COMMIT := $(shell git describe HEAD | sed s/^v//)
+PUB_DIR := $(PUB_ROOT)/$(VERSION)
+PRIOR = PRIOR
+
+ifeq ($(VERSION),)
+ REVISION = $(COMMIT)
+else
+ ifeq ($(VERSION),$(COMMIT))
+ REVISION := $(VERSION)
+ else
+ REVISION := $(VERSION) (from v$(COMMIT))
+ endif
+endif
+
+html: $(DOC_HTML)
+
+update: html
+ifeq ($(VERSION),)
+ ./GEN-DOC-VERSION
+endif
+ @-rm -rf $(LOCAL_ROOT)
+ @echo "Checking out current $(VERSION)"
+ @if ! $(SVN) checkout $(PUB_DIR) $(LOCAL_ROOT) 2>/dev/null ; then \
+ echo "Copying $(PRIOR) to $(VERSION) ..." && \
+ $(SVN) cp -m "Create $(VERSION) documentation" $(PUB_ROOT)/$(PRIOR) $(PUB_DIR) && \
+ $(SVN) checkout $(PUB_DIR) $(LOCAL_ROOT) ; \
+ fi
+ @rm -f $(LOCAL_ROOT)/*.html
+ @cp *.html $(LOCAL_ROOT)
+ @cd $(LOCAL_ROOT) && \
+ r=`$(SVN) status | perl -ne 'print if s/^! *//' ` && \
+ if [ -n "$$r" ]; then $(SVN) rm $$r; fi && \
+ a=`$(SVN) status | perl -ne 'print if s/^\? *//' ` && \
+ if [ -n "$$a" ]; then \
+ $(SVN) add $$a && \
+ $(SVN) propset svn:mime-type text/html $$a ; \
+ fi && \
+ echo "Committing $(VERSION) at v$(COMMIT)" && \
+ $(SVN) commit -m "Updated $(VERSION) documentation to v$(COMMIT)"
+ @-rm -rf $(LOCAL_ROOT)
+
+$(DOC_HTML): %.html : %.txt
+ @echo "FORMAT $@"
+ @rm -f $@+ $@
+ @$(ASCIIDOC) -a toc \
+ -a data-uri \
+ -a 'revision=$(REVISION)' \
+ -a 'newline=\n' \
+ -b xhtml11 \
+ -f asciidoc.conf \
+ $(ASCIIDOC_EXTRA) \
+ -o $@+ $<
+ @mv $@+ $@
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
new file mode 100644
index 0000000000..2fe6213207
--- /dev/null
+++ b/Documentation/asciidoc.conf
@@ -0,0 +1,29 @@
+[attributes]
+asterisk=*
+plus=+
+caret=^
+startsb=[
+endsb=]
+tilde=~
+
+[specialsections]
+GERRIT=gerrituplink
+
+[gerrituplink]
+
+
+[macros]
+(?u)^(?Pget)::(?P\S*?)$=#
+
+[get-blockmacro]
+
+ GET {target} HTTP/1.0
+
diff --git a/ReleaseNotes/ReleaseNotes-2.9.txt b/ReleaseNotes/ReleaseNotes-2.9.txt
index e7bf4f51c2..2a249adbd1 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.txt
@@ -40,7 +40,8 @@ Release Highlights
------------------
-* The new change screen is now the default change screen.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2065[Issue 2065]:
+The new change screen is now the default change screen.
+
The
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-review-ui.html[
@@ -90,12 +91,17 @@ the change history.
+
The support for the old comment visibility strategy is discontinued.
-* Inline comments are shown in the change history.
+* link:http://code.google.com/p/gerrit/issues/detail?id=93[Issue 93]:
+Inline comments are shown in the change history.
-* A reply icon is shown on each change message.
+* link:http://code.google.com/p/gerrit/issues/detail?id=592[Issue 592]:
+A reply icon is shown on each change message.
* Quoting is possible when replying to a comment.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2313[Issue 2313]:
+Show whether a related change is merged or old.
+
* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-review-ui.html#related-changes[
Related Changes] tabs:
** `Cherry-Picks`
@@ -112,7 +118,8 @@ form: "current patch set/number of patch sets".
* Support `[`, `/` and `]` keys to navigate between files in a cycle.
-* Show a tooltip on reviewers indicating on which labels they can vote.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2078[Issue 2078]:
+Show a tooltip on reviewers indicating on which labels they can vote.
* The `Submit` button is enabled even if the change is not mergeable.
+
@@ -124,6 +131,32 @@ single merge commit and submit the changes in reverse order.
* If a merge commit is viewed this is highlighted by an icon. In this
case the parent commits are also shown.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2191[Issue 2191]:
+New copy-to-clipboard button for commit ID.
+
+
+New Side-by-Side Diff Screen
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=348[Issue 348]:
+The lines of a patch file are linkable.
++
+These links can be used to directly link to certain inline comments.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2395[Issue 2395]:
+The line length preference is used to draw a margin line at that many
+columns of text.
++
+This allows a user to configure their preferred width (e.g. 80 columns
+or 100 columns) and see the margin, making it easier to identify lines
+that run over that width.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2530[Issue 2530]:
+All diff preferences are honored.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=148[Issue 148]:
+The full file path is shown.
+
Change List / Dashboards
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -151,9 +184,11 @@ Project Screens
* The general project screen provides a copyable clone command that
automatically installs the `commit-msg` hook.
-* Project owners can change `HEAD` from the project branches screen.
+* link:http://code.google.com/p/gerrit/issues/detail?id=562[Issue 562]:
+Project owners can change `HEAD` from the project branches screen.
-* Administrators can change the parent project from the project access
+* link:http://code.google.com/p/gerrit/issues/detail?id=1298[Issue 1298]:
+Administrators can change the parent project from the project access
screen; other users can save changes to the parent project for review
and get the change approved by an administrator.
@@ -189,7 +224,8 @@ link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-r
review] command allowing to control when email notifications should be
sent.
-* New `--branch` option on the
+* link:http://code.google.com/p/gerrit/issues/detail?id=1752[Issue 1752]:
+New `--branch` option on the
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-review.html[
review] command.
@@ -201,7 +237,8 @@ reviewers added on the change.
* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-apropos.html[
apropos] command to search the Gerrit documentation.
-* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-create-branch.html[
+* link:http://code.google.com/p/gerrit/issues/detail?id=1156[Issue 1156]:
+New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/cmd-create-branch.html[
create-branch] command.
REST API
@@ -232,7 +269,8 @@ global capability for viewing all accounts].
* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/access-control.html#capability_viewPlugins[
global capability for viewing the list of installed plugins].
-* New `Change Owner` group that allows to assign label permissions to the change owner.
+* link:http://code.google.com/p/gerrit/issues/detail?id=1993[Issue 1993]:
+New `Change Owner` group that allows to assign label permissions to the change owner.
* Support link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/access-control.html#category_submit_on_behalf_of[
on behalf of for submit].
@@ -268,12 +306,14 @@ is:mergeable] search operator
Finds changes that have no merge conflicts and can be merged into the
destination branch.
-* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-search.html#parentproject[
+* link:http://code.google.com/p/gerrit/issues/detail?id=2163[Issue 2163]:
+New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-search.html#parentproject[
parentproject] search operator
+
Finds changes in the specified project or in one of its child projects.
-* New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-search.html#conflicts[
+* link:http://code.google.com/p/gerrit/issues/detail?id=2162[Issue 2162]:
+New link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/user-search.html#conflicts[
conflicts] search operator
+
Finds changes that conflict with the specified change.
@@ -330,7 +370,8 @@ Configuration
* New init step for installing the `Verified` label.
-* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/config-gerrit.html#repository.name.defaultSubmitType[
+* link:http://code.google.com/p/gerrit/issues/detail?id=2257[Issue 2257]:
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/config-gerrit.html#repository.name.defaultSubmitType[
Default submit type] for newly created projects can be configured.
* `sshd_log` and `httpd_log` can use log4j configuration.
@@ -341,6 +382,9 @@ Draft workflow can be disabled].
* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.9/config-project-config.html#receive.checkReceivedObjects[
Project configuration for checking of received objects].
+* link:http://code.google.com/p/gerrit/issues/detail?id=2318[Issue 2318]:
+Allow the text of the "Report Bug" link to be configured.
+
Misc
~~~~
@@ -348,7 +392,8 @@ Misc
* The removal of reviewers and their votes is recorded as a change
message.
-* The change URL is returned on push if the change is updated.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2229[Issue 2229]:
+The change URL is returned on push if the change is updated.
* The topic is included into merge commit messages if all merged
changes have the same topic.
@@ -427,6 +472,60 @@ Bug Fixes
---------
+Access Rights
+~~~~~~~~~~~~~
+
+
+* Fix possibility to overcome BLOCK permissions.
+
+
+Web UI
+~~~~~~
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2652[Issue 2652]:
+Copy label approvals when cherry-picking change to same branch.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2178[Issue 2178]:
+Fix background of reply box on new change screen getting transparent.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2362[Issue 2362]:
+Show quick approve button only for current patch set.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2405[Issue 2405]:
+Update `Patch Sets` drop-down panel when draft patch set is deleted.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2397[Issue 2397]:
+Fix linkifying of topics that are set to a URL.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2151[Issue 2151]:
+Fix overflowing of long lines in commit message block.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2401[Issue 2401]:
+Fix truncated long lines in new side-by-side diff screen.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2225[Issue 2225]:
+Display larger icons for Prev / Next and Up to Change links on new
+side-by-side diff screen.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2340[Issue 2340]:
+Fix selection in new side-by-side diff screen.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2417[Issue 2417]:
+Respect base diff revision for files REST call.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2654[Issue 2654]:
+Require the user to confirm setting the username.
++
+Once the username has been set, it cannot be edited. This can cause
+problems for users who accidentally set the wrong username. A
+confirmation dialog now warns the user that setting the username is
+permanent and the username is only set when the user confirms.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2635[Issue 2635]:
+Fix copying from copyable label in Safari.
+
+
Secondary Index
~~~~~~~~~~~~~~~
@@ -441,15 +540,68 @@ This produced bizarre results for queries like "message:1234".
Instead, use Lucene's QueryBuilder with an analyzer to convert a
full-text search word/phrase into a phrase query.
+* link:http://code.google.com/p/gerrit/issues/detail?id=2281[Issue 2281]:
+Reindex change after updating commit message.
+
+
+REST
+~~~~
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2568[Issue 2568]:
+Update description file during `PUT /projects/{name}/config`.
+
+
+SSH
+~~~
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2516[Issue 2516]:
+Fix parsing of label name on `review` command.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2440[Issue 2440]:
+Clarify for review command when `--verified` can be used
+
+
+Plugins
+~~~~~~~
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2551[Issue 2551]:
+Handle absolute URLs in the top level menu.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2391[Issue 2391]:
+Respect servlet context path in URL for top menu items.
+
+
+Other
+~~~~~
+
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=2382[Issue 2382]:
+Clean left over data migration after removal of TrackingIds table
+
Upgrades
--------
* Update JGit to 3.4.0.201405051725-m7
++
+This upgrade fixes the MissingObjectExceptions in Gerrit that are
+described in link:http://code.google.com/p/gerrit/issues/detail?id=2025[
+issue 2025].
+
* Update gwtjsonrpc to 1.5
* Update gwtorm to 1.8
* Update guava to 16.0
+
* Update H2 to 1.3.174
++
+This version includes a fix for an LOB deadlock between reading and
+updating LOB columns. This could lead to a deadlock between web and SSH
+clients as described in
+link:http://code.google.com/p/gerrit/issues/detail?id=2365[issue 2365].
+
* Update Jetty to 9.1.0.v20131115
* Update Servlet API to 3.1
* Update Lucene to 4.6.0
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
index 1a8c275a10..a0392f82ba 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
@@ -22,6 +22,8 @@ import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
@@ -189,9 +191,14 @@ public class CopyableLabel extends Composite implements HasText {
switch (event.getCharCode()) {
case 'c':
case 'x':
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- hideTextBox();
+ textBox.addKeyUpHandler(new KeyUpHandler() {
+ @Override
+ public void onKeyUp(final KeyUpEvent event) {
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ hideTextBox();
+ }
+ });
}
});
break;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java
new file mode 100644
index 0000000000..47ef52026c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2014 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.google.gerrit.httpd.plugins;
+
+package com.google.gerrit.httpd.plugins;
+
+import com.google.common.base.Strings;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+class ContextMapper {
+ private static final String PLUGINS_PREFIX = "/plugins/";
+ private static final String AUTHORIZED_PREFIX = "/a" + PLUGINS_PREFIX;
+ private final String base;
+ private final String authorizedBase;
+
+ public ContextMapper(String contextPath) {
+ base = Strings.nullToEmpty(contextPath) + PLUGINS_PREFIX;
+ authorizedBase = Strings.nullToEmpty(contextPath) + AUTHORIZED_PREFIX;
+ }
+
+ private static boolean isAuthorizedCall(HttpServletRequest req) {
+ return !Strings.isNullOrEmpty(req.getServletPath())
+ && req.getServletPath().startsWith(AUTHORIZED_PREFIX);
+ }
+
+ HttpServletRequest create(HttpServletRequest req, String name) {
+ String contextPath = (isAuthorizedCall(req) ? authorizedBase : base) + name;
+
+ return new WrappedRequest(req, contextPath);
+ }
+
+ public String getFullPath(String name) {
+ return base + name;
+ }
+
+ private class WrappedRequest extends HttpServletRequestWrapper {
+ private final String contextPath;
+ private final String pathInfo;
+
+ private WrappedRequest(HttpServletRequest req, String contextPath) {
+ super(req);
+ this.contextPath = contextPath;
+ this.pathInfo = getRequestURI().substring(contextPath.length());
+ }
+
+ @Override
+ public String getServletPath() {
+ return "";
+ }
+
+ @Override
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ @Override
+ public String getPathInfo() {
+ return pathInfo;
+ }
+ }
+
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 8f66b0558f..4dc6f1eb5c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -73,7 +73,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
@Singleton
@@ -83,8 +82,6 @@ class HttpPluginServlet extends HttpServlet
private static final long serialVersionUID = 1L;
private static final Logger log
= LoggerFactory.getLogger(HttpPluginServlet.class);
- private static final String PLUGINS_PREFIX = "/plugins/";
- private static final String AUTHORIZED_PREFIX = "/a" + PLUGINS_PREFIX;
private final MimeUtilFileTypeRegistry mimeUtil;
private final Provider webUrl;
@@ -94,8 +91,7 @@ class HttpPluginServlet extends HttpServlet
private final RestApiServlet managerApi;
private List pending = Lists.newArrayList();
- private String base;
- private String authorizedBase;
+ private ContextMapper wrapper;
private final ConcurrentMap plugins
= Maps.newConcurrentMap();
@@ -134,9 +130,7 @@ class HttpPluginServlet extends HttpServlet
public synchronized void init(ServletConfig config) throws ServletException {
super.init(config);
- String path = config.getServletContext().getContextPath();
- base = Strings.nullToEmpty(path) + PLUGINS_PREFIX;
- authorizedBase = Strings.nullToEmpty(path) + AUTHORIZED_PREFIX;
+ wrapper = new ContextMapper(config.getServletContext().getContextPath());
for (Plugin plugin : pending) {
install(plugin);
}
@@ -182,7 +176,8 @@ class HttpPluginServlet extends HttpServlet
}
try {
- ServletContext ctx = PluginServletContext.create(plugin, base + name);
+ ServletContext ctx =
+ PluginServletContext.create(plugin, wrapper.getFullPath(name));
filter.init(new WrappedFilterConfig(ctx));
} catch (ServletException e) {
log.warn(String.format("Plugin %s failed to initialize HTTP", name), e);
@@ -220,8 +215,7 @@ class HttpPluginServlet extends HttpServlet
return;
}
- WrappedRequest wr = new WrappedRequest(req,
- (isAuthorizedCall(req) ? authorizedBase : base) + name);
+ HttpServletRequest wr = wrapper.create(req, name);
FilterChain chain = new FilterChain() {
@Override
public void doFilter(ServletRequest req, ServletResponse res)
@@ -236,11 +230,6 @@ class HttpPluginServlet extends HttpServlet
}
}
- private boolean isAuthorizedCall(HttpServletRequest req) {
- return !Strings.isNullOrEmpty(req.getServletPath())
- && req.getServletPath().startsWith(AUTHORIZED_PREFIX);
- }
-
private static boolean isApiCall(HttpServletRequest req, List parts) {
String method = req.getMethod();
int cnt = parts.size();
@@ -258,14 +247,13 @@ class HttpPluginServlet extends HttpServlet
return;
}
- String uri = req.getRequestURI();
- String ctx = req.getContextPath();
- if (uri.length() <= ctx.length()) {
+ String pathInfo = req.getPathInfo();
+ if (pathInfo.length() < 1) {
Resource.NOT_FOUND.send(req, res);
return;
}
- String file = uri.substring(ctx.length() + 1);
+ String file = pathInfo.substring(1);
ResourceKey key = new ResourceKey(holder.plugin, file);
Resource rsc = resourceCache.getIfPresent(key);
if (rsc != null) {
@@ -273,6 +261,7 @@ class HttpPluginServlet extends HttpServlet
return;
}
+ String uri = req.getRequestURI();
if ("".equals(file)) {
res.sendRedirect(uri + holder.docPrefix + "index.html");
return;
@@ -675,32 +664,4 @@ class HttpPluginServlet extends HttpServlet
}
}
}
-
- private static class WrappedRequest extends HttpServletRequestWrapper {
- private final String contextPath;
-
- WrappedRequest(HttpServletRequest req, String contextPath) {
- super(req);
- this.contextPath = contextPath;
- }
-
- @Override
- public String getContextPath() {
- return contextPath;
- }
-
- @Override
- public String getServletPath() {
- return getRequestURI().substring(contextPath.length());
- }
-
- @Override
- public String getRequestURI() {
- String uri = super.getRequestURI();
- if (uri.startsWith("/a/")) {
- uri = uri.substring(2);
- }
- return uri;
- }
- }
}
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/ContextMapperTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/ContextMapperTest.java
new file mode 100644
index 0000000000..e032823cf1
--- /dev/null
+++ b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/ContextMapperTest.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2014 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.google.gerrit.httpd.plugins;
+
+package com.google.gerrit.httpd.plugins;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class ContextMapperTest {
+
+ private static final String CONTEXT = "/context";
+ private static final String PLUGIN_NAME = "my-plugin";
+ private static final String RESOURCE = "my-resource";
+
+ @Test
+ public void testUnauthorized() throws Exception {
+ ContextMapper classUnderTest = new ContextMapper(CONTEXT);
+
+ HttpServletRequest originalRequest =
+ createMockRequest("/plugins/", PLUGIN_NAME + "/" + RESOURCE);
+
+ HttpServletRequest result =
+ classUnderTest.create(originalRequest, PLUGIN_NAME);
+
+ assertEquals(CONTEXT + "/plugins/" + PLUGIN_NAME, result.getContextPath());
+ assertEquals("", result.getServletPath());
+ assertEquals("/" + RESOURCE, result.getPathInfo());
+ assertEquals(CONTEXT + "/plugins/" + PLUGIN_NAME + "/" + RESOURCE,
+ result.getRequestURI());
+ }
+
+ @Test
+ public void testAuthorized() throws Exception {
+ ContextMapper classUnderTest = new ContextMapper(CONTEXT);
+
+ HttpServletRequest originalRequest =
+ createMockRequest("/a/plugins/", PLUGIN_NAME + "/" + RESOURCE);
+
+ HttpServletRequest result =
+ classUnderTest.create(originalRequest, PLUGIN_NAME);
+
+ assertEquals(CONTEXT + "/a/plugins/" + PLUGIN_NAME,
+ result.getContextPath());
+ assertEquals("", result.getServletPath());
+ assertEquals("/" + RESOURCE, result.getPathInfo());
+ assertEquals(CONTEXT + "/a/plugins/" + PLUGIN_NAME + "/" + RESOURCE,
+ result.getRequestURI());
+ }
+
+ private static HttpServletRequest createMockRequest(String servletPath,
+ String pathInfo) {
+ HttpServletRequest req = createNiceMock(HttpServletRequest.class);
+ expect(req.getContextPath()).andStubReturn(CONTEXT);
+ expect(req.getServletPath()).andStubReturn(servletPath);
+ expect(req.getPathInfo()).andStubReturn(pathInfo);
+ String uri = CONTEXT + servletPath + pathInfo;
+ expect(req.getRequestURI()).andStubReturn(uri);
+ replay(req);
+
+ return req;
+ }
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
index 6d93dcd8a1..62896eacff 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
@@ -155,7 +155,7 @@ public class Reindex extends SiteProgram {
result = indexAll();
index.markReady(true);
} catch (Exception e) {
- throw die(e.getMessage());
+ throw die(e.getMessage(), e);
}
sysManager.stop();
dbManager.stop();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
index f3ad45f52a..63d2b35fa3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
@@ -109,8 +109,7 @@ public class PermissionCollection {
List sections = Lists.newArrayList(sectionToProject.keySet());
sorter.sort(ref, sections);
- Set seen = new HashSet<>();
- Set seenBlockingRules = new HashSet<>();
+ Set seen = new HashSet();
Set exclusiveGroupPermissions = new HashSet<>();
HashMap> permissions = new HashMap<>();
@@ -125,7 +124,7 @@ public class PermissionCollection {
SeenRule s = new SeenRule(section, permission, rule);
boolean addRule;
if (rule.isBlock()) {
- addRule = seenBlockingRules.add(s);
+ addRule = true;
} else {
addRule = seen.add(s) && !rule.isDeny() && !exclusivePermissionExists;
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 75063c8b99..54765ac558 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -299,6 +299,15 @@ public class RefControlTest {
public void testBlockRule_ParentBlocksChild() {
grant(local, PUSH, DEVS, "refs/tags/*");
grant(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/tags/*").setBlock();
+ ProjectControl u = util.user(local, DEVS);
+ assertFalse("u can't update tag", u.controlForRef("refs/tags/V10").canUpdate());
+ }
+
+ @Test
+ public void testBlockRule_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
+ grant(local, PUSH, DEVS, "refs/tags/*");
+ grant(local, PUSH, ANONYMOUS_USERS, "refs/tags/*").setBlock();
+ grant(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/tags/*").setBlock();
ProjectControl u = util.user(local, DEVS);
assertFalse("u can't update tag", u.controlForRef("refs/tags/V10").canUpdate());
@@ -318,6 +327,23 @@ public class RefControlTest {
assertFalse("u can't vote 2", range.contains(2));
}
+ @Test
+ public void testBlockLabelRange_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
+ grant(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*").setBlock();
+ grant(util.getParentConfig(), LABEL + "Code-Review", -2, +2, DEVS,
+ "refs/heads/*").setBlock();
+
+ ProjectControl u = util.user(local, DEVS);
+
+ PermissionRange range =
+ u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
+ assertTrue("u can vote -1", range.contains(-1));
+ assertTrue("u can vote +1", range.contains(1));
+ assertFalse("u can't vote -2", range.contains(-2));
+ assertFalse("u can't vote 2", range.contains(2));
+ }
+
@Test
public void testUnblockNoForce() {
grant(local, PUSH, ANONYMOUS_USERS, "refs/heads/*").setBlock();