Merge "Document Maven archetype to create plugin projects"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 7027562..879d1ac 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -446,7 +446,7 @@
[[category_abandon]]
Abandon
-~~~~
+~~~~~~~
This category controls whether users are allowed to abandon changes
to projects in Gerrit. It can give permission to abandon a specific
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 8610dba..0f234f7 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2255,7 +2255,7 @@
Background color used for patch outdated messages. The value must be
a valid HTML hex color code, or standard color name.
+
-By default a shade of red, `FF0000`.
+By default a shade of red, `F08080`.
[[theme.tableOddRowColor]]theme.tableOddRowColor::
+
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 56ac759..68a873c 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -269,9 +269,10 @@
}
====
-If explicit registration is being used, a Guice ServletModule must
-be supplied to register the HTTP servlets, and the module must be
-declared in the manifest with the `Gerrit-HttpModule` attribute:
+The auto registration only works for standard servlet mappings like
+`/foo` or `/foo/*`. Regex style bindings must use a Guice ServletModule
+to register the HTTP servlets and declare it explicitly in the manifest
+with the `Gerrit-HttpModule` attribute:
====
import com.google.inject.servlet.ServletModule;
@@ -290,6 +291,22 @@
$ curl http://review.example.com/plugins/helloworld/print
----
+Data Directory
+--------------
+
+Plugins can request a data directory with a `@PluginData` File
+dependency. A data directory will be created automatically by the
+server in `$site_path/data/$plugin_name` and passed to the plugin.
+
+Plugins can use this to store any data they want.
+
+====
+ @Inject
+ MyType(@PluginData java.io.File myDir) {
+ new FileInputStream(new File(myDir, "my.config"));
+ }
+====
+
Documentation
-------------
diff --git a/Documentation/json.txt b/Documentation/json.txt
index 32bfed5..58e8e8f 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -124,6 +124,16 @@
refName:: Ref name within project.
+[[queryLimit]]
+queryLimit
+----------
+Information about the link:access-control.html#capability_queryLimit[queryLimit]
+of a user.
+
+min:: lower limit
+
+max:: upper limit
+
SEE ALSO
--------
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 1a359ac..cd5dd1e 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -58,8 +58,8 @@
[[accounts_self_capabilities]]
/accounts/self/capabilities (Account Capabilities)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Returns the global capabilities (such as createProject or
-createGroup) that are enabled for the calling user. This can be used
+Returns the global capabilities (such as `createProject` or
+`createGroup`) that are enabled for the calling user. This can be used
by UI tools to discover if administrative features are available
to the caller, so they can hide (or show) relevant UI actions.
@@ -99,6 +99,25 @@
}
----
+To filter the set of global capabilities the `q` parameter can be used.
+Filtering may decrease the response time by avoiding looking at every
+possible alternative for the caller.
+
+----
+ GET /a/accounts/self/capabilities?format=JSON&q=createAccount&q=createGroup HTTP/1.0
+ Authorization: Digest username="admin", realm="Gerrit Code Review", nonce="...
+
+ )]}'
+ {
+ "createAccount": true,
+ "createGroup": true
+ }
+----
+
+Most results are boolean, and a field is only present when its value
+is `true`. link:json.html#queryLimit[`queryLimit`] is a range and is
+presented as a nested JSON object with `min` and `max` members.
+
[[projects]]
/projects/ (List Projects)
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -128,6 +147,113 @@
}
----
+[[changes]]
+/changes/ (Query Changes)
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Queries changes visible to the caller. The query string must be
+provided by the `q` parameter. The `n` parameter can be used to limit
+the returned results.
+
+Query for open changes of watched projects:
+----
+ GET /changes/?format=JSON&q=status:open+is:watched&n=2 HTTP/1.0
+
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ {
+ "project": "demo",
+ "branch": "master",
+ "id": "Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
+ "subject": "One change",
+ "status": "NEW",
+ "created": "2012-07-17 07:18:30.854000000",
+ "updated": "2012-07-17 07:19:27.766000000",
+ "reviewed": true,
+ "_sortkey": "001e7057000006dc",
+ "_number": 1756,
+ "owner": {
+ "name": "John Doe"
+ },
+ "labels": {
+ "Verified": {},
+ "Code-Review": {}
+ }
+ },
+ {
+ "project": "demo",
+ "branch": "master",
+ "id": "I09c8041b5867d5b33170316e2abc34b79bbb8501",
+ "subject": "Another change",
+ "status": "NEW",
+ "created": "2012-07-17 07:18:30.884000000",
+ "updated": "2012-07-17 07:18:30.885000000",
+ "_sortkey": "001e7056000006dd",
+ "_number": 1757,
+ "owner": {
+ "name": "John Doe"
+ },
+ "labels": {
+ "Verified": {},
+ "Code-Review": {}
+ },
+ "_more_changes": true
+ }
+----
+
+The change output is sorted by the last update time, most recently
+updated to oldest update.
+
+If the `n` query parameter is supplied and additional changes exist
+that match the query beyond the end, the last change object has a
+`_more_changes: true` JSON field set. Callers can resume a query with
+the `n` query parameter, supplying the last change's `_sortkey` field
+as the value. When going in the reverse direction with the `p` query
+parameter a `_more_changes: true` is put in the first change object if
+there are results *before* the first change returned.
+
+Clients are allowed to specify more than one query by setting the `q`
+parameter multiple times. In this case the result is an array of
+arrays, one per query in the same order the queries were given in.
+
+Query that retrieves changes for a user's dashboard:
+----
+ GET /changes/?format=JSON&q=is:open+owner:self&q=is:open+reviewer:self+-owner:self&q=is:closed+owner:self+limit:5 HTTP/1.0
+
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json;charset=UTF-8
+
+ )]}'
+ [
+ [
+ {
+ "project": "demo",
+ "branch": "master",
+ "id": "Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
+ "subject": "One change",
+ "status": "NEW",
+ "created": "2012-07-17 07:18:30.854000000",
+ "updated": "2012-07-17 07:19:27.766000000",
+ "reviewed": true,
+ "_sortkey": "001e7057000006dc",
+ "_number": 1756,
+ "owner": {
+ "name": "John Doe"
+ },
+ "labels": {
+ "Verified": {},
+ "Code-Review": {}
+ }
+ }
+ ],
+ [],
+ []
+ ]
+----
+
GERRIT
------
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java
index 3dd54a7..85dd794 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java
@@ -16,10 +16,8 @@
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.SmallHeading;
import com.google.gerrit.common.auth.SignInMode;
import com.google.gerrit.common.auth.openid.DiscoveryResult;
-import com.google.gerrit.common.auth.openid.OpenIdUrls;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
index 234f937..5fde797 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -306,12 +306,12 @@
}
}
}
- if (detail.getNeededBy() != null) {
- for (final ChangeInfo ci : detail.getNeededBy()) {
- if ((ci.getStatus() == Change.Status.NEW) ||
- (ci.getStatus() == Change.Status.SUBMITTED)) {
- depsOpen = true;
- }
+ }
+ if (detail.getNeededBy() != null) {
+ for (final ChangeInfo ci : detail.getNeededBy()) {
+ if ((ci.getStatus() == Change.Status.NEW) ||
+ (ci.getStatus() == Change.Status.SUBMITTED)) {
+ depsOpen = true;
}
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index 9e69946..a7fde9b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -16,6 +16,7 @@
import static com.google.inject.Scopes.SINGLETON;
+import com.google.common.base.Strings;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.httpd.raw.CatServlet;
import com.google.gerrit.httpd.raw.HostPageServlet;
@@ -164,6 +165,11 @@
protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
String name = req.getPathInfo();
+ if (Strings.isNullOrEmpty(name)) {
+ toGerrit(PageLinks.ADMIN_PROJECTS, req, rsp);
+ return;
+ }
+
while (name.endsWith("/")) {
name = name.substring(0, name.length() - 1);
}
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 a2f4c99..9723674 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
@@ -43,7 +43,7 @@
theme.trimColor = color(name, "trimColor", "#D4E9A9");
theme.selectionColor = color(name, "selectionColor", "#FFFFCC");
theme.topMenuColor = color(name, "topMenuColor", theme.trimColor);
- theme.changeTableOutdatedColor = color(name, "changeTableOutdatedColor", "#FF0000");
+ theme.changeTableOutdatedColor = color(name, "changeTableOutdatedColor", "#F08080");
theme.tableOddRowColor = color(name, "tableOddRowColor", "transparent");
theme.tableEvenRowColor = color(name, "tableEvenRowColor", "transparent");
return theme;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
index da38573..c0e00aa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
@@ -16,7 +16,6 @@
import static com.google.gerrit.server.git.GitRepositoryManager.REF_REJECT_COMMITS;
-import com.google.common.base.Strings;
import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index fa47a2c..5b78c7b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -673,6 +673,12 @@
identifiedUserFactory.create(submitter.getAccountId());
Set<String> emails = new HashSet<String>();
for (RevCommit c : codeReviewCommits) {
+ try {
+ rw.parseBody(c);
+ } catch (IOException e) {
+ log.warn("Cannot parse commit " + c.name() + " in " + destBranch, e);
+ continue;
+ }
emails.add(c.getAuthorIdent().getEmailAddress());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 76cee02..91ac3ed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -288,8 +288,8 @@
}
try {
- runPlugin(name, jar, active);
- if (active == null) {
+ Plugin loadedPlugin = runPlugin(name, jar, active);
+ if (active == null && !loadedPlugin.isDisabled()) {
log.info(String.format("Loaded plugin %s", name));
}
} catch (PluginInstallException e) {
@@ -300,7 +300,7 @@
cleanInBackground();
}
- private void runPlugin(String name, File jar, Plugin oldPlugin)
+ private Plugin runPlugin(String name, File jar, Plugin oldPlugin)
throws PluginInstallException {
FileSnapshot snapshot = FileSnapshot.save(jar);
try {
@@ -327,6 +327,7 @@
disabled.put(name, newPlugin);
}
broken.remove(name);
+ return newPlugin;
} catch (Throwable err) {
broken.put(name, snapshot);
throw new PluginInstallException(err);