Document UI Extension feature
Change-Id: I12877b51c81bc473a0590e51c3d2be609ba7edfc
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index ab2fd5a..c6cab59 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -328,7 +328,7 @@
}
====
-If no Guice modules are declared in the manifest, UI commands may
+If no Guice modules are declared in the manifest, UI actions may
use auto-registration by providing an `@Export` annotation:
====
@@ -351,7 +351,7 @@
====
With a plugin-owned capability defined in this way, it is possible to restrict
-usage of an SSH command or UiAction to members of the group that were granted
+usage of an SSH command or `UiAction` to members of the group that were granted
this capability in the usual way, using the `RequiresCapability` annotation:
====
@@ -361,7 +361,7 @@
...
====
-Or with UiAction:
+Or with `UiAction`:
====
@RequiresCapability("printHello")
@@ -390,6 +390,186 @@
...
====
+[[ui_extension]]
+UI Extension
+------------
+
+Plugins can contribute their own UI commands on core Gerrit pages.
+This is useful for workflow customization or exposing plugin functionality
+through the UI in addition to SSH commands and the REST API.
+
+For instance a plugin to integrate Jira with Gerrit changes may contribute its
+own "File bug" button to allow filing a bug from the change page or plugins to
+integrate continuous integration systems may contribute a "Schedule" button to
+allow a CI build to be scheduled manually from the patch set panel.
+
+Two different places on core Gerrit pages are currently supported:
+
+* Change screen
+* Project info screen
+
+Plugins contribute UI actions by implementing the `UiAction` interface:
+
+====
+ @RequiresCapability("printHello")
+ class HelloWorldAction implements UiAction<RevisionResource>,
+ RestModifyView<RevisionResource, HelloWorldAction.Input> {
+ static class Input {
+ boolean french;
+ String message;
+ }
+
+ private Provider<CurrentUser> cu;
+
+ @Inject
+ HelloWorldAction(Provider<CurrentUser> user) {
+ this.user = user;
+ }
+
+ @Override
+ public String apply(RevisionResource rev, Input input) {
+ final String greeting = input.french
+ ? "Bonjour"
+ : "Hello";
+ return String.format("%s %s from change %s, patch set %d!",
+ greeting,
+ Strings.isNullOrEmpty(input.message)
+ ? Objects.firstNonNull(user.get().getUserName(), "world")
+ : input.message,
+ rev.getChange().getId().toString(),
+ rev.getPatchSet().getPatchSetId());
+ }
+
+ @Override
+ public Description getDescription(
+ RevisionResource resource) {
+ return new Description()
+ .setLabel("Say hello")
+ .setTitle("Say hello in different languages");
+ }
+ }
+====
+
+`UiAction` must be bound in a plugin module:
+
+====
+ public class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ install(new RestApiModule() {
+ @Override
+ protected void configure() {
+ post(REVISION_KIND, "say-hello")
+ .to(HelloWorldAction.class);
+ }
+ });
+ }
+ }
+====
+
+The module above must be declared in pom.xml for Maven driven plugins:
+
+====
+ <manifestEntries>
+ <Gerrit-Module>com.googlesource.gerrit.plugins.cookbook.Module</Gerrit-Module>
+ </manifestEntries>
+====
+
+or in the BUCK configuration file for Buck driven plugins:
+
+====
+ manifest_entries = [
+ 'Gerrit-Module: com.googlesource.gerrit.plugins.cookbook.Module',
+ ]
+====
+
+In some use cases more user input must be gathered, for that `UiAction` can be
+combined with the JavaScript API. This would display a small popup near the
+activation button to gather additional input from the user. The JS file is
+typically put in the `static` folder within the plugin's directory:
+
+====
+ Gerrit.install(function(self) {
+ function onSayHello(c) {
+ var f = c.textfield();
+ var t = c.checkbox();
+ var b = c.button('Say hello', {onclick: function(){
+ c.call(
+ {message: f.value, french: t.checked},
+ function(r) {
+ c.hide();
+ window.alert(r);
+ c.refresh();
+ });
+ }});
+ c.popup(c.div(
+ c.prependLabel('Greeting message', f),
+ c.br(),
+ c.label(t, 'french'),
+ c.br(),
+ b));
+ f.focus();
+ }
+ self.onAction('revision', 'say-hello', onSayHello);
+ });
+====
+
+The JS module must be exposed as a `WebUiPlugin` and bound as
+an HTTP Module:
+
+====
+ public class HttpModule extends HttpPluginModule {
+ @Override
+ protected void configureServlets() {
+ DynamicSet.bind(binder(), WebUiPlugin.class)
+ .toInstance(new JavaScriptPlugin("hello.js"));
+ }
+ }
+====
+
+The HTTP module above must be declared in pom.xml for Maven driven plugins:
+
+====
+ <manifestEntries>
+ <Gerrit-HttpModule>com.googlesource.gerrit.plugins.cookbook.HttpModule</Gerrit-HttpModule>
+ </manifestEntries>
+====
+
+or in the BUCK configuration file for Buck driven plugins
+
+====
+ manifest_entries = [
+ 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.cookbook.HttpModule',
+ ]
+====
+
+If `UiAction` is annotated with the `@RequiresCapability` annotation, then the
+capability check is done during the `UiAction` gathering, so the plugin author
+doesn't have to set `UiAction.Description.setVisible()` explicitly in this
+case.
+
+The following prerequisities must be met, to satisfy the capability check:
+
+* user is authenticated
+* user is a member of the Administrators group, or
+* user is a member of a group which has the required capability
+
+The `apply` method is called when the button is clicked. If `UiAction` is
+combined with JavaScript API (its own JavaScript function is provided),
+then a popup dialog is normally opened to gather additional user input.
+A new button is placed on the popup dialog to actually send the request.
+
+Every `UiAction` exposes a REST API endpoint. The endpoint from the example above
+can be accessed from any REST client, i. e.:
+
+====
+ curl -X POST -H "Content-Type: application/json" \
+ -d '{message: "François", french: true}' \
+ --digest --user joe:secret \
+ http://host:port/a/changes/1/revisions/1/cookbook~say-hello
+ "Bonjour François from change 1, patch set 1!"
+====
+
[[http]]
HTTP Servlets
-------------
@@ -573,6 +753,12 @@
Disabled plugins can be re-enabled using the
link:cmd-plugin-enable.html[plugin enable] command.
+SEE ALSO
+--------
+
+* link:js-api.html[JavaScript API]
+* link:dev-rest-api.html[REST API Developers' Notes]
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]