Merge "Allow editing of commit message from UI."
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 03be009..93549f8 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -478,11 +478,15 @@
 
 cache `"changes"`::
 +
-Each item caches all changes tied to one project. Default size
-is 1024, which means all changes for up to 1024 projects can be held
-in the cache. This cache should probably be disabled in a multi-master
-setup where change updates are not communicated between servers. The
-cache should be flushed whenever the database changes table is modified
+The size determines the number of projects that will have all its changes
+cached. If the cache is set to 1024, this means all changes for up to
+1024 projects can be held in the cache.
++
+Default size is 0 (disabled). It is disabled by default due to the fact
+that change updates are not communicated between Gerrit servers.
+Hence this cache should be disabled in an multi-master/multi-slave setup.
++
+The cache should be flushed whenever the database changes table is modified
 outside of gerrit.
 
 cache `"diff"`::
@@ -2350,14 +2354,15 @@
 open changes table or the account dashboard. The value must be a
 valid HTML hex color code, or standard color name.
 +
-By default `FCFEEF` (a creme color) for signed-out theme and white
-(`FFFFFF`) for signed-in theme.
+By default white, `FFFFFF`.
 
 [[theme.topMenuColor]]theme.topMenuColor::
 +
 This is the color of the main menu bar at the top of the page.
 The value must be a valid HTML hex color code, or standard color
-name.  The value defaults to <<theme.trimColor,trimColor>>.
+name.
++
+By default white, `FFFFFF`.
 
 [[theme.textColor]]theme.textColor::
 +
@@ -2365,7 +2370,7 @@
 open changes table or the account dashboard. The value must be a
 valid HTML hex color code, or standard color name.
 +
-By default black, `000000`.
+By default dark grey, `353535`.
 
 [[theme.trimColor]]theme.trimColor::
 +
@@ -2375,7 +2380,7 @@
 of the page.  The value must be a valid HTML hex color code, or
 standard color name.
 +
-By default a shade of green, `D4E9A9`.
+By default a light grey, `EEEEEE`.
 
 [[theme.selectionColor]]theme.selectionColor::
 +
@@ -2384,7 +2389,7 @@
 currently selected row.  The value must be a valid HTML hex color
 code, or standard color name.
 +
-By default a shade of yellow, `FFFFCC`.
+By default a pale blue, `D8EDF9`.
 
 [[theme.changeTableOutdatedColor]]theme.changeTableOutdatedColor::
 +
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 4c2335f..ca8be97 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -19,7 +19,7 @@
 * link:access-control.html[Access Controls]
 * link:error-messages.html[Error Messages]
 * link:rest-api.html[REST API]
-* link:user-custom-dashboards.html[Custom Dashboards]
+* link:user-dashboards.html[Dashboards]
 * link:user-notify.html[Subscribing to Email Notifications]
 * link:user-submodules.html[Subscribing to Git Submodules]
 * link:refs-notes-review.html[The `refs/notes/review` namespace]
diff --git a/Documentation/user-custom-dashboards.txt b/Documentation/user-dashboards.txt
similarity index 74%
rename from Documentation/user-custom-dashboards.txt
rename to Documentation/user-dashboards.txt
index 3f57a0b..019e978 100644
--- a/Documentation/user-custom-dashboards.txt
+++ b/Documentation/user-dashboards.txt
@@ -1,8 +1,8 @@
-Gerrit Code Review - Custom Dashboards
-======================================
+Gerrit Code Review - Dashboards
+===============================
 
-Description
------------
+Custom Dashboards
+-----------------
 
 A custom dashboard is shown in a layout similar to the per-user
 dashboard, but the sections are entirely configured from the URL.
@@ -48,13 +48,14 @@
 
 It is possible to share custom dashboards at a project level. To do
 this define the dashboards in a `refs/meta/dashboards/*` branch of the
-project. For each dashboard create a config file. The file name will be
-used as name for the dashboard.
+project. For each dashboard create a config file. The file path/name
+will be used as name (equivalent to a title in a custom dashboard) for
+the dashboard.
 
 Example dashboard config file `MyProject Dashboard`:
 
 ----
-[main]
+[dashboard]
   description = Most recent open and merged changes.
 [section "Open Changes"]
   query = status:open project:myProject limit:15
@@ -62,18 +63,37 @@
   query = status:merged project:myProject limit:15
 ----
 
+Once defined, project dashboards are accessible using stable URLs by
+using the project name, refname and pathname of the dashboard via URLs
+like:
+----
+  /#/projects/All-Projects,dashboards/Site:Main
+----
+
 Project dashboards are inherited from ancestor projects unless
-overridden by dashboards with the same ref and name.
+overridden by dashboards with the same ref and name.  This makes
+it easy to define common dashboards for every project by simply
+defining project dashboards on the All-Projects project.
 
 Project dashboard queries may contain the special `${project}` token
 which will be replaced with the project name to which the dashboard is
 being applied.  This is useful for defining dashboards designed to be
-inherited.
+inherited.  With this token, it is possible to cause a project
+dashboard to be restricted to only changes for the project in which
+an inherited dashboard is being applied by simply adding
+`project:${project}` to the queries in the dashboard.
 
-Section main
-~~~~~~~~~~~~
+Section dashboard
+~~~~~~~~~~~~~~~~~
 
-main.description::
+dashboard.title::
++
+The title of the dashboard.
++
+If not specified the path of the dashboard config file is used as
+title.
+
+dashboard.description::
 +
 The description of the dashboard.
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index 7c7dc4a..605fe51 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.http.client.URL;
 import com.google.gwtorm.client.KeyUtil;
 
 public class PageLinks {
@@ -78,14 +77,13 @@
     return "/q/" + KeyUtil.encode(query) + "," + page;
   }
 
-  public static String toProjectDashboard(Project.NameKey projectName,
-      String dashboardId) {
-    return PROJECTS + projectName.get() + DASHBOARDS + dashboardId;
+  public static String toProjectDashboard(Project.NameKey name, String id) {
+    return PROJECTS + name.get() + DASHBOARDS + id;
   }
 
   public static String projectQuery(Project.NameKey proj) {
     return op("project", proj.get());
-}
+  }
 
   public static String projectQuery(Project.NameKey proj, Status status) {
       return status(status) + " " + op("project", proj.get());
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java
index e874a52..56ff555 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java
@@ -67,8 +67,9 @@
    *
    * @return view to list the collection.
    * @throws ResourceNotFoundException if the collection cannot be listed.
+   * @throws AuthException if the collection requires authentication.
    */
-  RestView<P> list() throws ResourceNotFoundException;
+  RestView<P> list() throws ResourceNotFoundException, AuthException;
 
   /**
    * Parse a path component into a resource handle.
diff --git a/gerrit-gwtdebug/pom.xml b/gerrit-gwtdebug/pom.xml
index 9db434a..942f507 100644
--- a/gerrit-gwtdebug/pom.xml
+++ b/gerrit-gwtdebug/pom.xml
@@ -34,11 +34,6 @@
 
   <dependencies>
     <dependency>
-      <groupId>com.google.gwt</groupId>
-      <artifactId>gwt-dev</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>com.google.gerrit</groupId>
       <artifactId>gerrit-gwtui</artifactId>
       <version>${project.version}</version>
@@ -94,5 +89,12 @@
       <classifier>sources</classifier>
       <scope>provided</scope>
     </dependency>
+
+     <!-- Workaround for overwriting our dependencies (like args4j) by additional
+      classes put in gwt-dev.jar -->
+    <dependency>
+      <groupId>com.google.gwt</groupId>
+      <artifactId>gwt-dev</artifactId>
+    </dependency>
   </dependencies>
 </project>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 7f589aa..dd9e481 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -88,6 +88,7 @@
 import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.http.client.URL;
 import com.google.gwt.user.client.Window;
+import com.google.gwtjsonrpc.client.RemoteJsonException;
 import com.google.gwtorm.client.KeyUtil;
 
 public class Dispatcher {
@@ -401,20 +402,36 @@
       rest = rest.substring(c);
       if (matchPrefix(DASHBOARDS, rest)) {
         final String dashboardId = skip(rest);
+        GerritCallback<DashboardInfo> cb = new GerritCallback<DashboardInfo>() {
+          @Override
+          public void onSuccess(DashboardInfo result) {
+            if (matchPrefix("/dashboard/", result.url())) {
+              String rest = skip(result.url());
+              Gerrit.display(token, new CustomDashboardScreen(rest.substring(1)));
+            }
+          }
+
+          @Override
+          public void onFailure(Throwable caught) {
+            if ("default".equals(dashboardId)
+                && caught instanceof RemoteJsonException
+                && ((RemoteJsonException) caught).getCode() == 404) {
+              Gerrit.display(PageLinks.toChangeQuery(
+                  PageLinks.projectQuery(new Project.NameKey(project))));
+            } else {
+              super.onFailure(caught);
+            }
+          }
+        };
+        if ("default".equals(dashboardId)) {
+          DashboardList.getDefault(new Project.NameKey(project), cb);
+          return;
+        }
         c = dashboardId.indexOf(":");
         if (0 <= c) {
           final String ref = URL.decodePathSegment(dashboardId.substring(0, c));
           final String path = URL.decodePathSegment(dashboardId.substring(c + 1));
-          DashboardList.get(new Project.NameKey(project), ref + ":" + path,
-              new GerritCallback<DashboardInfo>() {
-                @Override
-                public void onSuccess(DashboardInfo result) {
-                  if (matchPrefix("/dashboard/", result.url())) {
-                    String rest = skip(result.url());
-                    Gerrit.display(token, new CustomDashboardScreen(rest.substring(1)));
-                  }
-                }
-              });
+          DashboardList.get(new Project.NameKey(project), ref + ":" + path, cb);
           return;
         }
       }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 70f01b1..b169a96 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -42,6 +42,7 @@
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.core.client.Callback;
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.core.client.GWT;
@@ -58,7 +59,6 @@
 import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Grid;
@@ -634,9 +634,9 @@
       m = new LinkMenuBar();
       addLink(m, C.menuMyChanges(), PageLinks.MINE);
       addLink(m, C.menuMyDrafts(), PageLinks.toChangeQuery("is:draft"));
+      addLink(m, C.menuMyDraftComments(), PageLinks.toChangeQuery("has:draft"));
       addLink(m, C.menuMyWatchedChanges(), PageLinks.toChangeQuery("is:watched status:open"));
       addLink(m, C.menuMyStarredChanges(), PageLinks.toChangeQuery("is:starred"));
-      addLink(m, C.menuMyDraftComments(), PageLinks.toChangeQuery("has:draft"));
       menuLeft.add(m, C.menuMine());
       menuLeft.selectTab(1);
     } else {
@@ -769,7 +769,7 @@
   private static Anchor anchor(final String text, final String to) {
     final Anchor a = new Anchor(text, to);
     a.setStyleName(RESOURCES.css().menuItem());
-    Accessibility.setRole(a.getElement(), Accessibility.ROLE_MENUITEM);
+    Roles.getMenuitemRole().set(a.getElement());
     return a;
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
index 7e7b927..46b7d4d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
@@ -42,8 +42,6 @@
     setStyleName(Gerrit.RESOURCES.css().searchPanel());
 
     searchBox = new HintTextBox();
-    searchBox.setVisibleLength(70);
-    searchBox.setHintText(Gerrit.C.searchHint());
     final MySuggestionDisplay suggestionDisplay = new MySuggestionDisplay();
     searchBox.addKeyPressHandler(new KeyPressHandler() {
       @Override
@@ -59,6 +57,8 @@
     final SuggestBox suggestBox =
         new SuggestBox(new SearchSuggestOracle(), searchBox, suggestionDisplay);
     searchBox.setStyleName("gwt-TextBox");
+    searchBox.setVisibleLength(70);
+    searchBox.setHintText(Gerrit.C.searchHint());
 
     final Button searchButton = new Button(Gerrit.C.searchButton());
     searchButton.addClickHandler(new ClickHandler() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index a2fcb50..b250446 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -98,14 +98,8 @@
 
       private Widget createSearchLink(final ProjectInfo projectInfo) {
         Image image = new Image(Gerrit.RESOURCES.queryProjectLink());
-        InlineHyperlink h;
-        if (projectInfo.defaultDashboard() != null) {
-          h = new InlineHyperlink(" ", PageLinks.toProjectDashboard(
-                  projectInfo.name_key(), projectInfo.defaultDashboard()));
-        } else {
-          h = new InlineHyperlink(" ", PageLinks.toChangeQuery(PageLinks
-                  .projectQuery(projectInfo.name_key())));
-        }
+        InlineHyperlink h = new InlineHyperlink(" ",
+            PageLinks.toProjectDashboard(projectInfo.name_key(), "default"));
         h.setTitle(Util.C.projectListQueryLink());
         DOM.insertBefore(h.getElement(), image.getElement(),
             DOM.getFirstChild(h.getElement()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
index 34e1bf2..c1510cc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
@@ -27,8 +27,6 @@
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.ui.Composite;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
index c095d4d..e732ba4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
@@ -16,10 +16,10 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwtjsonrpc.common.VoidResult;
@@ -32,7 +32,7 @@
     super(text);
     this.cmdType = cmdType;
     setStyleName(Gerrit.RESOURCES.css().downloadLink());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_TAB);
+    Roles.getTabRole().set(getElement());
     addClickHandler(this);
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
index 1516da1..fe9229d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
-import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
 
@@ -27,7 +27,7 @@
 
   DownloadCommandPanel() {
     setStyleName(Gerrit.RESOURCES.css().downloadLinkList());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_TABLIST);
+    Roles.getTablistRole().set(getElement());
   }
 
   boolean isEmpty() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
index 2afafaa..b59ebbc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
@@ -16,10 +16,10 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwtjsonrpc.common.VoidResult;
@@ -34,7 +34,7 @@
     this.urlType = urlType;
     this.urlData = urlData;
     setStyleName(Gerrit.RESOURCES.css().downloadLink());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_TAB);
+    Roles.getTabRole().set(getElement());
     addClickHandler(this);
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
index b3c9ebc..4e53e56 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
-import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
 
@@ -26,7 +26,7 @@
   DownloadUrlPanel(final DownloadCommandPanel commandPanel) {
     this.commandPanel = commandPanel;
     setStyleName(Gerrit.RESOURCES.css().downloadLinkList());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_TABLIST);
+    Roles.getTablistRole().set(getElement());
   }
 
   boolean isEmpty() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
index 229791b..02ee2b1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
@@ -29,7 +29,7 @@
       .get(callback);
   }
 
-  public static void defaultDashboard(Project.NameKey project,
+  public static void getDefault(Project.NameKey project,
       AsyncCallback<DashboardInfo> callback) {
     new RestApi(base(project) + "default")
       .addParameterTrue("inherited")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 66edb2c..8654723 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -36,20 +36,13 @@
   gwt-image: "greenCheck";
 }
 
+/** Override various GWT defaults */
 .gerritTopMenu {
   font-size: 9pt;
   padding-top: 5px;
   padding-left: 5px;
   padding-right: 5px;
-  background: topMenuColor;
-}
-.gerritTopMenu .gwt-TabBar .gwt-TabBarItem,
-.gerritTopMenu .gwt-TabBar .gwt-TabBarRest,
-.gerritTopMenu .gwt-TabBar .gwt-TabPanelBottom {
-  background: topMenuColor;
-}
-.gerritTopMenu .gwt-TabBar .gwt-TabBarItem-selected {
-  background: selectionColor;
+  background: transparent;
 }
 
 .gerritBody {
@@ -58,6 +51,17 @@
   padding-right: 5px;
 }
 
+a,
+a:visited {
+  color: #0654ac;
+  text-decoration: none;
+}
+
+a:hover {
+  color: #0654ac;
+  text-decoration: underline;
+}
+
 .version,
 .keyhelp {
   color: #a0adcc;
@@ -237,33 +241,46 @@
 .topmenuTDglue {
   width: 100%;
 }
+
 .topmenuMenuLeft {
   width: 300px;
-  border-left: 1px solid topMenuColor;
-  border-right: 1px solid topMenuColor;
-  border-bottom: 1px solid topMenuColor;
+  font-size: 9pt;
+  padding-top: 5px;
+  padding-left: 5px;
+  padding-right: 5px;
+  background: none;
+  position: relative;
+  top: 0;
+}
+.topmenuMenuLeft tbody tr td table {
+  border: 0;
+}
+.topmenuMenuLeft tbody tr td table.gwt-TabBar {
+  border-bottom: 1px solid #DDD;
+}
+.topmenuMenuLeft .gwt-TextBox {
+  width: 250px;
+}
+.topmenuMenuLeft .gwt-Button {
+  padding: 3px 6px;
 }
 .topmenuMenuLeft .gwt-TabBarFirst {
   display: none;
 }
 .topmenuMenuLeft .gwt-TabBarItem {
   margin: 0px;
-  background: topMenuColor;
+  background: transparent;
   padding-top: 0px;
   padding-bottom: 1px;
   padding-left: 1em;
   padding-right: 1em;
-  border-right: 1px solid black;
-}
-.topmenuMenuLeft .gwt-TabBarItem-selected {
-  background: selectionColor;
 }
 .topmenuMenuLeft .gwt-TabBarRest {
-  background: topMenuColor;
+  background: transparent;
   padding-top: 0px;
 }
 .topmenuMenuLeft .gwt-TabPanelBottom {
-  background: topMenuColor;
+  background: transparent;
   border-top: none;
   border-left: none;
   border-right: none;
@@ -290,12 +307,12 @@
   white-space: nowrap;
 }
 .searchPanel .gwt-TextBox {
-  font-size: 7pt;
+  font-size: 9pt;
 }
 .searchPanel .gwt-Button {
-  font-size: 7pt;
+  font-size: 9pt;
   margin-left: 2px;
-  padding: 1px;
+  padding: 3px 6px;
 }
 
 /** RPC Status **/
@@ -476,6 +493,10 @@
   border-bottom: 1px solid trimColor;
 }
 
+.changeTable a.gwt-InlineHyperlink {
+  color: #222 !important;
+}
+
 .accountDashboard.changeTable tr {
   color: #444444;
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css
index ed0b32c..f9a8cc0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css
@@ -42,6 +42,12 @@
   white-space: nowrap;
 }
 
+.gwt-TabBar .gwt-TabBarItem,
+.gwt-TabBar .gwt-TabBarRest,
+.gwt-TabPanelBottom {
+  background: transparent;
+}
+
 .gwt-TabBar {
   border-bottom: 1px solid black;
 }
@@ -49,16 +55,20 @@
   display: none;
 }
 .gwt-TabBar .gwt-TabBarItem {
-  margin: 0px;
+  color: #353535;
+  margin: 0;
   background: trimColor;
   padding-top: 0.5em;
   padding-bottom: 1px;
   padding-left: 1em;
   padding-right: 1em;
-  border-right: 1px solid black;
+  border-bottom: 3px solid transparent;
+  border-right: 0;
 }
 .gwt-TabBar .gwt-TabBarItem-selected {
+  color: #990000;
   background: selectionColor;
+  border-bottom-color: #990000;
 }
 .gwt-TabBar .gwt-TabBarRest {
   background: trimColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
index 1d26178..18ab7e9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -48,7 +48,6 @@
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.Focusable;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
index 5df41a2..80c1feb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
@@ -27,7 +27,6 @@
 
   public final native String name() /*-{ return this.name; }-*/;
   public final native String description() /*-{ return this.description; }-*/;
-  public final native String defaultDashboard() /*-{ return this.default_dashboard; }-*/;
 
   @Override
   public final String getDisplayString() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
index 30eb64f..e7195c9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
@@ -78,12 +78,9 @@
       }
 
       String json = res.getText();
-      if (!json.startsWith(JSON_MAGIC)) {
-        RpcStatus.INSTANCE.onRpcComplete();
-        cb.onFailure(new RemoteJsonException("Invalid JSON"));
-        return;
+      if (json.startsWith(JSON_MAGIC)) {
+        json = json.substring(JSON_MAGIC.length());
       }
-      json = json.substring(JSON_MAGIC.length());
 
       T data;
       try {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
index fdb5c56..72bf06c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
@@ -15,10 +15,10 @@
 package com.google.gerrit.client.ui;
 
 import com.google.gerrit.client.Gerrit;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.Anchor;
 
 public class CommandMenuItem extends Anchor implements ClickHandler {
@@ -27,7 +27,7 @@
   public CommandMenuItem(final String text, final Command cmd) {
     super(text);
     setStyleName(Gerrit.RESOURCES.css().menuItem());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUITEM);
+    Roles.getMenuitemRole().set(getElement());
     addClickHandler(this);
     command = cmd;
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
index 0353281..bde640d1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.client.ui;
 
 import com.google.gerrit.client.Gerrit;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -28,7 +28,7 @@
     body = new FlowPanel();
     initWidget(body);
     setStyleName(Gerrit.RESOURCES.css().linkMenuBar());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUBAR);
+    Roles.getMenubarRole().set(getElement());
   }
 
   public void addItem(final String text, final Command imp) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
index f076a2c..3173d72 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
@@ -15,14 +15,14 @@
 package com.google.gerrit.client.ui;
 
 import com.google.gerrit.client.Gerrit;
+import com.google.gwt.aria.client.Roles;
 import com.google.gwt.dom.client.AnchorElement;
-import com.google.gwt.user.client.ui.Accessibility;
 
 public class LinkMenuItem extends InlineHyperlink {
   public LinkMenuItem(final String text, final String targetHistoryToken) {
     super(text, targetHistoryToken);
     setStyleName(Gerrit.RESOURCES.css().menuItem());
-    Accessibility.setRole(getElement(), Accessibility.ROLE_MENUITEM);
+    Roles.getMenuitemRole().set(getElement());
   }
 
   @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
index 845a046..7629fe3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
@@ -51,6 +51,7 @@
     if (header == null) {
       onInitUI();
     }
+    Gerrit.setQueryString(null);
   }
 
   public void registerKeys() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
index 9723674..9e89eee 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
@@ -38,11 +38,11 @@
 
   private HostPageData.Theme getTheme(String name) {
     HostPageData.Theme theme = new HostPageData.Theme();
-    theme.backgroundColor = color(name, "backgroundColor", "#FCFEEF");
-    theme.textColor = color(name, "textColor", "#000000");
-    theme.trimColor = color(name, "trimColor", "#D4E9A9");
-    theme.selectionColor = color(name, "selectionColor", "#FFFFCC");
-    theme.topMenuColor = color(name, "topMenuColor", theme.trimColor);
+    theme.backgroundColor = color(name, "backgroundColor", "#FFFFFF");
+    theme.textColor = color(name, "textColor", "#353535");
+    theme.trimColor = color(name, "trimColor", "#EEEEEE");
+    theme.selectionColor = color(name, "selectionColor", "#D8EDF9");
+    theme.topMenuColor = color(name, "topMenuColor", "#FFFFFF");
     theme.changeTableOutdatedColor = color(name, "changeTableOutdatedColor", "#F08080");
     theme.tableOddRowColor = color(name, "tableOddRowColor", "transparent");
     theme.tableEvenRowColor = color(name, "tableEvenRowColor", "transparent");
@@ -54,11 +54,7 @@
     if (v == null || v.isEmpty()) {
       v = cfg.getString("theme", null, name);
       if (v == null || v.isEmpty()) {
-        if ("signed-in".equals(section) && "backgroundColor".equals(name)) {
-          v = "#FFFFFF";
-        } else {
-          v = defaultValue;
-        }
+        v = defaultValue;
       }
     }
     if (!v.startsWith("#") && v.matches("^[0-9a-fA-F]{2,6}$")) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
index f28d597..24f7bba 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
@@ -138,6 +138,10 @@
     return lineNbr;
   }
 
+  public void setLine(int line) {
+    lineNbr = line;
+  }
+
   public Account.Id getAuthor() {
     return author;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index ad1c479..928f597 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -23,6 +23,9 @@
 public class Module extends RestApiModule {
   @Override
   protected void configure() {
+    bind(AccountsCollection.class);
+    bind(Capabilities.class);
+
     DynamicMap.mapOf(binder(), ACCOUNT_KIND);
     DynamicMap.mapOf(binder(), CAPABILITY_KIND);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index 1485b65..aa688e0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -94,7 +94,12 @@
       }
       return Collections.emptyList();
     } else if (k.project == null && k.branch == null && k.changeId != null) {
-      return db.get().changes().byKey(new Change.Key(k.changeId)).toList();
+      Change.Key id = new Change.Key(k.changeId);
+      if (id.get().length() == 41) {
+        return db.get().changes().byKey(id).toList();
+      } else {
+        return db.get().changes().byKeyRange(id, id.max()).toList();
+      }
     }
     return db.get().changes().byBranchKey(
         k.branch(),
@@ -109,7 +114,7 @@
 
     ParsedId(String id) throws ResourceNotFoundException,
         UnsupportedEncodingException {
-      if (id.matches("^[0-9]+$")) {
+      if (id.matches("^[1-9][0-9]*$")) {
         legacyId = Change.Id.parse(id);
         return;
       }
@@ -117,7 +122,7 @@
       int t2 = id.lastIndexOf('~');
       int t1 = id.lastIndexOf('~', t2 - 1);
       if (t1 < 0 || t2 < 0) {
-        if (!id.matches("^I[0-9a-z]{40}$")) {
+        if (!id.matches("^I[0-9a-z]{4,40}$")) {
           throw new ResourceNotFoundException(id);
         }
         changeId = id;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
new file mode 100644
index 0000000..01e7e3b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
@@ -0,0 +1,70 @@
+// 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.google.gerrit.server.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.change.PutDraft.Input;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+
+class CreateDraft implements RestModifyView<RevisionResource, Input> {
+  private final Provider<ReviewDb> db;
+
+  @Inject
+  CreateDraft(Provider<ReviewDb> db) {
+    this.db = db;
+  }
+
+  @Override
+  public Class<Input> inputType() {
+    return Input.class;
+  }
+
+  @Override
+  public Object apply(RevisionResource rsrc, Input in) throws AuthException,
+      BadRequestException, ResourceConflictException, Exception {
+    if (Strings.isNullOrEmpty(in.path)) {
+      throw new BadRequestException("path must be non-empty");
+    } else if (in.message == null || in.message.trim().isEmpty()) {
+      throw new BadRequestException("message must be non-empty");
+    } else if (in.line != null && in.line <= 0) {
+      throw new BadRequestException("line must be > 0");
+    }
+
+    PatchLineComment c = new PatchLineComment(
+        new PatchLineComment.Key(
+            new Patch.Key(rsrc.getPatchSet().getId(), in.path),
+            ChangeUtil.messageUUID(db.get())),
+        in.line != null ? in.line : 0,
+        rsrc.getAuthorId(),
+        null);
+    c.setStatus(Status.DRAFT);
+    c.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
+    c.setMessage(in.message.trim());
+    db.get().patchComments().insert(Collections.singleton(c));
+    return new GetDraft.Comment(c);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java
new file mode 100644
index 0000000..af9b846
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.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.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.DeleteDraft.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+
+class DeleteDraft implements RestModifyView<DraftResource, Input> {
+  static class Input {
+  }
+
+  private final Provider<ReviewDb> db;
+
+  @Inject
+  DeleteDraft(Provider<ReviewDb> db) {
+    this.db = db;
+  }
+
+  @Override
+  public Class<Input> inputType() {
+    return Input.class;
+  }
+
+  @Override
+  public Object apply(DraftResource rsrc, Input input) throws OrmException {
+    db.get().patchComments().delete(Collections.singleton(rsrc.getComment()));
+    return new Object();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java
new file mode 100644
index 0000000..bcd8902
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java
@@ -0,0 +1,62 @@
+// 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.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.RestResource;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.inject.TypeLiteral;
+
+public class DraftResource implements RestResource {
+  public static final TypeLiteral<RestView<DraftResource>> DRAFT_KIND =
+      new TypeLiteral<RestView<DraftResource>>() {};
+
+  private final RevisionResource rev;
+  private final PatchLineComment comment;
+
+  DraftResource(RevisionResource rev, PatchLineComment c) {
+    this.rev = rev;
+    this.comment = c;
+  }
+
+  public ChangeControl getControl() {
+    return rev.getControl();
+  }
+
+  public Change getChange() {
+    return getControl().getChange();
+  }
+
+  public PatchSet getPatchSet() {
+    return rev.getPatchSet();
+  }
+
+  PatchLineComment getComment() {
+    return comment;
+  }
+
+  String getId() {
+    return comment.getKey().get();
+  }
+
+  Account.Id getAuthorId() {
+    return ((IdentifiedUser) getControl().getCurrentUser()).getAccountId();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java
new file mode 100644
index 0000000..83959a0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java
@@ -0,0 +1,83 @@
+// 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.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ChildCollection;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+class Drafts implements ChildCollection<RevisionResource, DraftResource> {
+  private final DynamicMap<RestView<DraftResource>> views;
+  private final Provider<CurrentUser> user;
+  private final Provider<ListDrafts> list;
+  private final Provider<ReviewDb> dbProvider;
+
+  @Inject
+  Drafts(DynamicMap<RestView<DraftResource>> views,
+      Provider<CurrentUser> user,
+      Provider<ListDrafts> list,
+      Provider<ReviewDb> dbProvider) {
+    this.views = views;
+    this.user = user;
+    this.list = list;
+    this.dbProvider = dbProvider;
+  }
+
+  @Override
+  public DynamicMap<RestView<DraftResource>> views() {
+    return views;
+  }
+
+  @Override
+  public RestView<RevisionResource> list() throws AuthException {
+    checkIdentifiedUser();
+    return list.get();
+  }
+
+  @Override
+  public DraftResource parse(RevisionResource rev, String id)
+      throws ResourceNotFoundException, UnsupportedEncodingException,
+      OrmException, AuthException {
+    checkIdentifiedUser();
+    String uuid = URLDecoder.decode(id, "UTF-8");
+    for (PatchLineComment c : dbProvider.get().patchComments()
+        .draftByPatchSetAuthor(
+            rev.getPatchSet().getId(),
+            rev.getAuthorId())) {
+      if (uuid.equals(c.getKey().get())) {
+        return new DraftResource(rev, c);
+      }
+    }
+    throw new ResourceNotFoundException(id);
+  }
+
+  private void checkIdentifiedUser() throws AuthException {
+    if (!(user.get() instanceof IdentifiedUser)) {
+      throw new AuthException("drafts only available to authenticated users");
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
new file mode 100644
index 0000000..fbfe1d5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
@@ -0,0 +1,65 @@
+// 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.google.gerrit.server.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.sql.Timestamp;
+
+class GetDraft implements RestReadView<DraftResource> {
+  @Override
+  public Object apply(DraftResource rsrc) throws AuthException,
+      BadRequestException, ResourceConflictException, Exception {
+    return new Comment(rsrc.getComment());
+  }
+
+  static enum Side {
+    PARENT, REVISION;
+  }
+
+  static class Comment {
+    final String kind = "gerritcodereview#comment";
+    String id;
+    String path;
+    Side side;
+    Integer line;
+    String message;
+    Timestamp updated;
+
+    Comment(PatchLineComment c) {
+      try {
+        id = URLEncoder.encode(c.getKey().get(), "UTF-8");
+      } catch (UnsupportedEncodingException e) {
+        throw new RuntimeException("UTF-8 encoding not supported", e);
+      }
+      path = c.getKey().getParentKey().getFileName();
+      if (c.getSide() == 0) {
+        side = Side.PARENT;
+      }
+      if (c.getLine() > 0) {
+        line = c.getLine();
+      }
+      message = Strings.emptyToNull(c.getMessage());
+      updated = c.getWrittenOn();
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
new file mode 100644
index 0000000..208f271
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
@@ -0,0 +1,79 @@
+// 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.google.gerrit.server.change;
+
+import static com.google.common.base.Objects.firstNonNull;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.GetDraft.Comment;
+import com.google.gerrit.server.change.GetDraft.Side;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+class ListDrafts implements RestReadView<RevisionResource> {
+  private final Provider<ReviewDb> db;
+
+  @Inject
+  ListDrafts(Provider<ReviewDb> db) {
+    this.db = db;
+  }
+
+  @Override
+  public Object apply(RevisionResource rsrc) throws AuthException,
+      BadRequestException, ResourceConflictException, Exception {
+    Map<String, List<Comment>> out = Maps.newTreeMap();
+    for (PatchLineComment c : db.get().patchComments()
+        .draftByPatchSetAuthor(
+            rsrc.getPatchSet().getId(),
+            rsrc.getAuthorId())) {
+      Comment o = new Comment(c);
+      List<Comment> list = out.get(o.path);
+      if (list == null) {
+        list = Lists.newArrayList();
+        out.put(o.path, list);
+      }
+      list.add(o);
+    }
+    for (List<Comment> list : out.values()) {
+      Collections.sort(list, new Comparator<Comment>() {
+        @Override
+        public int compare(Comment a, Comment b) {
+          int c = firstNonNull(a.side, Side.REVISION).ordinal()
+                - firstNonNull(b.side, Side.REVISION).ordinal();
+          if (c == 0) {
+            c = firstNonNull(a.line, 0) - firstNonNull(b.line, 0);
+          }
+          if (c == 0) {
+            c = a.id.compareTo(b.id);
+          }
+          return c;
+        }
+      });
+    }
+    return out;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index f0e7e46..3a25c15 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.change;
 
 import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND;
+import static com.google.gerrit.server.change.DraftResource.DRAFT_KIND;
 import static com.google.gerrit.server.change.ReviewerResource.REVIEWER_KIND;
 import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
 
@@ -25,7 +26,12 @@
 public class Module extends RestApiModule {
   @Override
   protected void configure() {
+    bind(Revisions.class);
+    bind(Reviewers.class);
+    bind(Drafts.class);
+
     DynamicMap.mapOf(binder(), CHANGE_KIND);
+    DynamicMap.mapOf(binder(), DRAFT_KIND);
     DynamicMap.mapOf(binder(), REVIEWER_KIND);
     DynamicMap.mapOf(binder(), REVISION_KIND);
 
@@ -41,6 +47,12 @@
     child(CHANGE_KIND, "revisions").to(Revisions.class);
     post(REVISION_KIND, "review").to(PostReview.class);
 
+    child(REVISION_KIND, "drafts").to(Drafts.class);
+    put(REVISION_KIND, "drafts").to(CreateDraft.class);
+    get(DRAFT_KIND).to(GetDraft.class);
+    put(DRAFT_KIND).to(PutDraft.class);
+    delete(DRAFT_KIND).to(DeleteDraft.class);
+
     install(new FactoryModule() {
       @Override
       protected void configure() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 59fe465..5011f31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.ApprovalCategory;
 import com.google.gerrit.reviewdb.client.ApprovalCategoryValue;
@@ -82,17 +81,17 @@
     DELETE, PUBLISH, KEEP;
   }
 
-  static enum Side {
-    PARENT, REVISION;
-  }
-
   static class Comment {
     String id;
-    Side side;
+    GetDraft.Side side;
     int line;
     String message;
   }
 
+  static class Output {
+    Map<String, Short> labels;
+  }
+
   private final ReviewDb db;
   private final ApprovalTypes approvalTypes;
   private final EmailReviewComments.Factory email;
@@ -124,8 +123,7 @@
 
   @Override
   public Object apply(RevisionResource revision, Input input)
-      throws AuthException, BadRequestException, ResourceConflictException,
-      Exception {
+      throws AuthException, BadRequestException, OrmException {
     if (input.labels != null) {
       checkLabels(revision, input.strictLabels, input.labels);
     }
@@ -161,7 +159,10 @@
         message,
         comments).sendAsync();
     fireCommentAddedHook(revision);
-    return input;
+
+    Output output = new Output();
+    output.labels = input.labels;
+    return output;
   }
 
   private void checkLabels(RevisionResource revision, boolean strict,
@@ -190,7 +191,7 @@
         continue;
       }
 
-      if (!at.getValuesAsList().contains(ent.getValue())) {
+      if (at.getValue(ent.getValue()) == null) {
         if (strict) {
           throw new BadRequestException(String.format(
               "label \"%s\": %d is not a valid value",
@@ -273,7 +274,7 @@
         }
         e.setStatus(PatchLineComment.Status.PUBLISHED);
         e.setWrittenOn(timestamp);
-        e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1);
+        e.setSide(c.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
         e.setMessage(c.message);
         (create ? ins : upd).add(e);
       }
@@ -422,7 +423,7 @@
   }
 
   @Deprecated
-  private void fireCommentAddedHook(RevisionResource rsrc) throws OrmException {
+  private void fireCommentAddedHook(RevisionResource rsrc) {
     IdentifiedUser user = (IdentifiedUser) rsrc.getControl().getCurrentUser();
     try {
       hooks.doCommentAddedHook(change,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
new file mode 100644
index 0000000..38b4da1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
@@ -0,0 +1,103 @@
+// 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.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.DefaultInput;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.GetDraft.Side;
+import com.google.gerrit.server.change.PutDraft.Input;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.Timestamp;
+import java.util.Collections;
+
+class PutDraft implements RestModifyView<DraftResource, Input> {
+  static class Input {
+    String kind;
+    String id;
+    String path;
+    Side side;
+    Integer line;
+    Timestamp updated; // Accepted but ignored.
+
+    @DefaultInput
+    String message;
+  }
+
+  private final Provider<ReviewDb> db;
+  private final Provider<DeleteDraft> delete;
+
+  @Inject
+  PutDraft(Provider<ReviewDb> db, Provider<DeleteDraft> delete) {
+    this.db = db;
+    this.delete = delete;
+  }
+
+  @Override
+  public Class<Input> inputType() {
+    return Input.class;
+  }
+
+  @Override
+  public Object apply(DraftResource rsrc, Input in)
+      throws AuthException, BadRequestException, ResourceConflictException,
+      Exception {
+    if (in == null || in.message == null || in.message.trim().isEmpty()) {
+      return delete.get().apply(rsrc, null);
+    } else if (in.kind != null && !"gerritcodereview#comment".equals(in.kind)) {
+      throw new BadRequestException("expected kind gerritcodereview#comment");
+    } else if (in.line != null && in.line < 0) {
+      throw new BadRequestException("line must be >= 0");
+    }
+
+    PatchLineComment c = rsrc.getComment();
+    if (in.path != null
+        && !in.path.equals(c.getKey().getParentKey().getFileName())) {
+      // Updating the path alters the primary key, which isn't possible.
+      // Delete then recreate the comment instead of an update.
+      db.get().patchComments().delete(Collections.singleton(c));
+      c = update(new PatchLineComment(
+          new PatchLineComment.Key(
+              new Patch.Key(rsrc.getPatchSet().getId(), in.path),
+              c.getKey().get()),
+          c.getLine(),
+          rsrc.getAuthorId(),
+          c.getParentUuid()), in);
+      db.get().patchComments().insert(Collections.singleton(c));
+    } else {
+      db.get().patchComments().update(Collections.singleton(update(c, in)));
+    }
+    return new GetDraft.Comment(c);
+  }
+
+  private PatchLineComment update(PatchLineComment e, Input in) {
+    if (in.side != null) {
+      e.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
+    }
+    if (in.line != null) {
+      e.setLine(in.line);
+    }
+    e.setMessage(in.message.trim());
+    e.updated();
+    return e;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
index 1bfb006..970230b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revisions.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.change;
 
+import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -26,6 +27,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+import java.util.Collections;
 import java.util.List;
 
 class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
@@ -52,36 +54,57 @@
   @Override
   public RevisionResource parse(ChangeResource change, String id)
       throws ResourceNotFoundException, Exception {
-    if (id.matches("^[1-9][0-9]{0,4}$")) {
+    List<PatchSet> match = Lists.newArrayListWithExpectedSize(2);
+    for (PatchSet ps : find(change, id)) {
+      Change.Id changeId = ps.getId().getParentKey();
+      if (changeId.equals(change.getChange().getId())
+          && change.getControl().isPatchVisible(ps, dbProvider.get())) {
+        match.add(ps);
+      }
+    }
+    if (match.size() != 1) {
+      throw new ResourceNotFoundException(id);
+    }
+    return new RevisionResource(change, match.get(0));
+  }
+
+  private List<PatchSet> find(ChangeResource change, String id)
+      throws OrmException {
+    ReviewDb db = dbProvider.get();
+
+    if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
+      // Legacy patch set number syntax.
       PatchSet ps = dbProvider.get().patchSets().get(new PatchSet.Id(
           change.getChange().getId(),
           Integer.parseInt(id)));
-      if (ps != null
-          && change.getControl().isPatchVisible(ps, dbProvider.get())) {
-        return new RevisionResource(change, ps);
+      if (ps != null) {
+        return Collections.singletonList(ps);
       }
-      throw new ResourceNotFoundException(id);
-    }
-
-    for (PatchSet ps : find(id)) {
-      Change.Id changeId = ps.getId().getParentKey();
-      if (changeId.equals(change.getChange().getId())) {
-        if (change.getControl().isPatchVisible(ps, dbProvider.get())) {
-          return new RevisionResource(change, ps);
-        }
-        break;
+      return Collections.emptyList();
+    } else if (id.length() < 4 || id.length() > RevId.LEN) {
+      // Require a minimum of 4 digits.
+      // Impossibly long identifier will never match.
+      return Collections.emptyList();
+    } else if (id.length() >= 8) {
+      // Commit names are rather unique. Query for the commit and later
+      // match to the change. This is most likely going to identify 1 or
+      // at most 2 patch sets to consider, which is smaller than looking
+      // for all patch sets in the change.
+      RevId revid = new RevId(id);
+      if (revid.isComplete()) {
+        return db.patchSets().byRevision(revid).toList();
+      } else {
+        return db.patchSets().byRevisionRange(revid, revid.max()).toList();
       }
-    }
-    throw new ResourceNotFoundException(id);
-  }
-
-  private List<PatchSet> find(String id) throws OrmException {
-    ReviewDb db = dbProvider.get();
-    RevId revid = new RevId(id);
-    if (revid.isComplete()) {
-      return db.patchSets().byRevision(revid).toList();
     } else {
-      return db.patchSets().byRevisionRange(revid, revid.max()).toList();
+      // Chance of collision rises; look at all patch sets on the change.
+      List<PatchSet> out = Lists.newArrayList();
+      for (PatchSet ps : db.patchSets().byChange(change.getChange().getId())) {
+        if (ps.getRevision() != null && ps.getRevision().get().startsWith(id)) {
+          out.add(ps);
+        }
+      }
+      return out;
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index dd39058..8e0658d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.git.NotesBranchUtil;
 import com.google.gerrit.server.git.SubmoduleOp;
 import com.google.gerrit.server.mail.AddReviewerSender;
-import com.google.gerrit.server.mail.CommentSender;
 import com.google.gerrit.server.mail.CreateChangeSender;
 import com.google.gerrit.server.mail.MergeFailSender;
 import com.google.gerrit.server.mail.MergedSender;
@@ -50,7 +49,6 @@
 import com.google.gerrit.server.patch.RemoveReviewer;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.CreateProject;
-import com.google.gerrit.server.project.ListProjects;
 import com.google.gerrit.server.project.PerRequestProjectControlCache;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.project.SuggestParentCandidates;
@@ -64,7 +62,6 @@
     bind(RequestScopedReviewDbProvider.class);
     bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
     bind(MetaDataUpdate.User.class).in(RequestScoped.class);
-    bind(ListProjects.class);
     bind(ApprovalsUtil.class);
 
     bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
index bf2a19f..6f71936 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
@@ -48,7 +48,7 @@
         cache(ID_CACHE,
             Project.NameKey.class,
             new TypeLiteral<List<Change>>() {})
-          .maximumWeight(1024)
+          .maximumWeight(0)
           .loader(Loader.class);
       }
     };
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
index e352598..2db3a26 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -17,6 +17,8 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -41,6 +43,7 @@
 import java.util.Map;
 
 /** List the installed plugins. */
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
 public class ListPlugins implements RestReadView<TopLevelResource> {
   private final PluginLoader pluginLoader;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
index e6871d8..4cdafb3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
@@ -28,6 +28,7 @@
     bind(ServerInformation.class).to(ServerInformationImpl.class);
 
     bind(PluginCleanerTask.class);
+    bind(PluginsCollection.class);
     bind(PluginGuiceEnvironment.class);
     bind(PluginLoader.class);
     bind(CopyConfigModule.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
index 083b696..4710698 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
@@ -163,7 +163,7 @@
     return query.replace("${project}", project);
   }
 
-  public static String defaultOf(Project proj) {
+  private static String defaultOf(Project proj) {
     final String defaultId = Objects.firstNonNull(
         proj.getLocalDefaultDashboard(),
         Strings.nullToEmpty(proj.getDefaultDashboard()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
index 0547185..b88a445c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -284,8 +284,6 @@
           if (showDescription) {
             info.description = Strings.emptyToNull(e.getProject().getDescription());
           }
-          info.defaultDashboard =
-              Strings.emptyToNull(DashboardsCollection.defaultOf(e.getProject()));
 
           try {
             if (showBranch != null) {
@@ -453,7 +451,6 @@
     String id;
     String parent;
     String description;
-    String defaultDashboard;
     Map<String, String> branches;
 
     void setName(String name) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
index 2fc9467..4507c17 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
@@ -23,6 +23,9 @@
 public class Module extends RestApiModule {
   @Override
   protected void configure() {
+    bind(ProjectsCollection.class);
+    bind(DashboardsCollection.class);
+
     DynamicMap.mapOf(binder(), PROJECT_KIND);
     DynamicMap.mapOf(binder(), DASHBOARD_KIND);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 67e5739..93f6f4c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -54,7 +54,7 @@
   private List<String> queries;
 
   @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "Maximum number of results to return")
-  void setLimit(int limit) {
+  public void setLimit(int limit) {
     imp.setLimit(limit);
   }
 
@@ -69,7 +69,7 @@
   }
 
   @Option(name = "-P", metaVar = "SORTKEY", usage = "Previous changes before SORTKEY")
-  void setSortKeyAfter(String key) {
+  public void setSortKeyAfter(String key) {
     // Querying for the prior page of changes requires sortkey_after predicate.
     // Changes are shown most recent->least recent. The previous page of
     // results contains changes that were updated after the given key.
@@ -78,7 +78,7 @@
   }
 
   @Option(name = "-N", metaVar = "SORTKEY", usage = "Next changes after SORTKEY")
-  void setSortKeyBefore(String key) {
+  public void setSortKeyBefore(String key) {
     // Querying for the next page of changes requires sortkey_before predicate.
     // Changes are shown most recent->least recent. The next page contains
     // changes that were updated before the given key.
@@ -98,6 +98,13 @@
     json.setChangeControlFactory(cf);
   }
 
+  public void addQuery(String query) {
+    if (queries == null) {
+      queries = Lists.newArrayList();
+    }
+    queries.add(query);
+  }
+
   @Override
   public Object apply(TopLevelResource rsrc)
       throws BadRequestException, AuthException, OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/RequestScopePropagator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/RequestScopePropagator.java
index 86d1565..748a879 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/RequestScopePropagator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/RequestScopePropagator.java
@@ -170,12 +170,12 @@
    */
   protected abstract <T> Callable<T> wrapImpl(final Callable<T> callable);
 
-  protected <T> Callable<T> context(RequestContext context,
+  protected <T> Callable<T> context(final RequestContext context,
       final Callable<T> callable) {
-    final CurrentUser user = context.getCurrentUser();
     return new Callable<T>() {
       @Override
       public T call() throws Exception {
+        final CurrentUser user = context.getCurrentUser();
         RequestContext old = local.setContext(new RequestContext() {
           @Override
           public CurrentUser getCurrentUser() {
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml
index 2461f51..9802b68 100644
--- a/gerrit-war/pom.xml
+++ b/gerrit-war/pom.xml
@@ -36,12 +36,6 @@
   <dependencies>
     <dependency>
       <groupId>org.apache.tomcat</groupId>
-      <artifactId>servlet-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>org.apache.tomcat</groupId>
       <artifactId>tomcat-servlet-api</artifactId>
       <scope>provided</scope>
     </dependency>
@@ -118,6 +112,12 @@
       <artifactId>jetty-servlet</artifactId>
       <scope>provided</scope>
     </dependency>
+
+    <dependency>
+      <groupId>org.apache.tomcat</groupId>
+      <artifactId>servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
   </dependencies>
 
   <build>