diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index ab2fd5a017..c6cab59a21 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -328,7 +328,7 @@ abstract class: } ==== -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 @@ Otherwise the capability must be bound in a plugin module: ==== 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 @@ this capability in the usual way, using the `RequiresCapability` annotation: ... ==== -Or with UiAction: +Or with `UiAction`: ==== @RequiresCapability("printHello") @@ -390,6 +390,186 @@ this can be specified by setting `scope = CapabilityScope.CORE`: ... ==== +[[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, + RestModifyView { + static class Input { + boolean french; + String message; + } + + private Provider cu; + + @Inject + HelloWorldAction(Provider 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: + +==== + + com.googlesource.gerrit.plugins.cookbook.Module + +==== + +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: + +==== + + com.googlesource.gerrit.plugins.cookbook.HttpModule + +==== + +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 @@ command can be used. 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]