bc8088eddb
Also, documentation and refactoring. Feature: Issue 8791 Change-Id: I9ac64773d207739052bac6060c83212307d344d8
460 lines
11 KiB
Plaintext
460 lines
11 KiB
Plaintext
= Gerrit Code Review - PolyGerrit Plugin Development
|
|
|
|
CAUTION: Work in progress. Hard hat area. Please
|
|
link:https://bugs.chromium.org/p/gerrit/issues/entry?template=PolyGerrit%20plugins[send
|
|
feedback] if something's not right.
|
|
|
|
For migrating existing GWT UI plugins, please check out the
|
|
link:pg-plugin-migration.html#migration[migration guide].
|
|
|
|
[[loading]]
|
|
== Plugin loading and initialization
|
|
|
|
link:js-api.html#_entry_point[Entry point] for the plugin and the loading method
|
|
is based on link:http://w3c.github.io/webcomponents/spec/imports/[HTML Imports]
|
|
spec.
|
|
|
|
* The plugin provides pluginname.html, and can be a standalone file or a static
|
|
asset in a jar as a link:dev-plugins.html#deployment[Web UI plugin].
|
|
* pluginname.html contains a `dom-module` tag with a script that uses
|
|
`Gerrit.install()`. There should only be single `Gerrit.install()` per file.
|
|
* PolyGerrit imports pluginname.html along with all required resources defined in it
|
|
(fonts, styles, etc).
|
|
* For standalone plugins, the entry point file is a `pluginname.html` file
|
|
located in `gerrit-site/plugins` folder, where `pluginname` is an alphanumeric
|
|
plugin name.
|
|
|
|
Note: Code examples target modern browsers (Chrome, Firefox, Safari, Edge).
|
|
|
|
Here's a recommended starter `myplugin.html`:
|
|
|
|
``` html
|
|
<dom-module id="my-plugin">
|
|
<script>
|
|
Gerrit.install(plugin => {
|
|
'use strict';
|
|
|
|
// Your code here.
|
|
});
|
|
</script>
|
|
</dom-module>
|
|
```
|
|
|
|
[[low-level-api-concepts]]
|
|
== Low-level DOM API concepts
|
|
|
|
Basically, the DOM is the API surface. Low-level API provides methods for
|
|
decorating, replacing, and styling DOM elements exposed through a set of
|
|
link:pg-plugin-endpoints.html[endpoints].
|
|
|
|
PolyGerrit provides a simple way for accessing the DOM via DOM hooks API. A DOM
|
|
hook is a custom element that is instantiated for the plugin endpoint. In the
|
|
decoration case, a hook is set with a `content` attribute that points to the DOM
|
|
element.
|
|
|
|
1. Get the DOM hook API instance via `plugin.hook(endpointName)`
|
|
2. Set up an `onAttached` callback
|
|
3. Callback is called when the hook element is created and inserted into DOM
|
|
4. Use element.content to get UI element
|
|
|
|
``` js
|
|
Gerrit.install(plugin => {
|
|
const domHook = plugin.hook('reply-text');
|
|
domHook.onAttached(element => {
|
|
if (!element.content) { return; }
|
|
// element.content is a reply dialog text area.
|
|
});
|
|
});
|
|
```
|
|
|
|
[[low-level-decorating]]
|
|
=== Decorating DOM Elements
|
|
|
|
For each endpoint, PolyGerrit provides a list of DOM properties (such as
|
|
attributes and events) that are supported in the long-term.
|
|
|
|
``` js
|
|
Gerrit.install(plugin => {
|
|
const domHook = plugin.hook('reply-text');
|
|
domHook.onAttached(element => {
|
|
if (!element.content) { return; }
|
|
element.content.style.border = '1px red dashed';
|
|
});
|
|
});
|
|
```
|
|
|
|
[[low-level-replacing]]
|
|
=== Replacing DOM Elements
|
|
|
|
An endpoint's contents can be replaced by passing the replace attribute as an
|
|
option.
|
|
|
|
``` js
|
|
Gerrit.install(plugin => {
|
|
const domHook = plugin.hook('header-title', {replace: true});
|
|
domHook.onAttached(element => {
|
|
element.appendChild(document.createElement('my-site-header'));
|
|
});
|
|
});
|
|
```
|
|
|
|
[[low-level-style]]
|
|
=== Styling DOM Elements
|
|
|
|
A plugin may provide Polymer's
|
|
https://www.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-modules[style
|
|
modules] to style individual endpoints using
|
|
`plugin.registerStyleModule(endpointName, moduleName)`. A style must be defined
|
|
as a standalone `<dom-module>` defined in the same .html file.
|
|
|
|
Note: TODO: Insert link to the full styling API.
|
|
|
|
``` html
|
|
<dom-module id="my-plugin">
|
|
<script>
|
|
Gerrit.install(plugin => {
|
|
plugin.registerStyleModule('change-metadata', 'some-style-module');
|
|
});
|
|
</script>
|
|
</dom-module>
|
|
|
|
<dom-module id="some-style-module">
|
|
<style>
|
|
html {
|
|
--change-metadata-label-status: {
|
|
display: none;
|
|
}
|
|
--change-metadata-strategy: {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|
|
</dom-module>
|
|
```
|
|
|
|
[[high-level-api-concepts]]
|
|
== High-level DOM API concepts
|
|
|
|
High level API is based on low-level DOM API and is essentially a standardized
|
|
way for doing common tasks. It's less flexible, but will be a bit more stable.
|
|
|
|
The common way to access high-level API is through `plugin` instance passed
|
|
into setup callback parameter of `Gerrit.install()`, also sometimes referred to
|
|
as `self`.
|
|
|
|
[[low-level-api]]
|
|
== Low-level DOM API
|
|
|
|
The low-level DOM API methods are the base of all UI customization.
|
|
|
|
=== attributeHelper
|
|
`plugin.attributeHelper(element)`
|
|
|
|
Alternative for
|
|
link:https://www.polymer-project.org/1.0/docs/devguide/data-binding[Polymer data
|
|
binding] for plugins that don't use Polymer. Can be used to bind element
|
|
attribute changes to callbacks.
|
|
|
|
See `samples/bind-parameters.html` for examples on both Polymer data bindings
|
|
and `attibuteHelper` usage.
|
|
|
|
=== eventHelper
|
|
`plugin.eventHelper(element)`
|
|
|
|
Note: TODO
|
|
|
|
=== hook
|
|
`plugin.hook(endpointName, opt_options)`
|
|
|
|
See list of supported link:pg-plugin-endpoints.html[endpoints].
|
|
|
|
Note: TODO
|
|
|
|
=== registerCustomComponent
|
|
`plugin.registerCustomComponent(endpointName, opt_moduleName, opt_options)`
|
|
|
|
See list of supported link:pg-plugin-endpoints.html[endpoints].
|
|
|
|
Note: TODO
|
|
|
|
=== registerStyleModule
|
|
`plugin.registerStyleModule(endpointName, moduleName)`
|
|
|
|
Note: TODO
|
|
|
|
[[high-level-api]]
|
|
== High-level API
|
|
|
|
Plugin instance provides access to number of more specific APIs and methods
|
|
to be used by plugin authors.
|
|
|
|
=== admin
|
|
`plugin.admin()`
|
|
|
|
.Params:
|
|
- none
|
|
|
|
.Returns:
|
|
- Instance of link:pg-plugin-admin-api.html[GrAdminApi].
|
|
|
|
=== changeReply
|
|
`plugin.changeReply()`
|
|
|
|
Note: TODO
|
|
|
|
=== changeView
|
|
`plugin.changeView()`
|
|
|
|
Note: TODO
|
|
|
|
=== delete
|
|
`plugin.delete(url, opt_callback)`
|
|
|
|
Note: TODO
|
|
|
|
=== get
|
|
`plugin.get(url, opt_callback)`
|
|
|
|
Note: TODO
|
|
|
|
=== getPluginName
|
|
`plugin.getPluginName()`
|
|
|
|
Note: TODO
|
|
|
|
=== getServerInfo
|
|
`plugin.getServerInfo()`
|
|
|
|
Note: TODO
|
|
|
|
=== on
|
|
`plugin.on(eventName, callback)`
|
|
|
|
Note: TODO
|
|
|
|
=== panel
|
|
`plugin.panel(extensionpoint, callback)`
|
|
|
|
Deprecated. Use `plugin.registerCustomComponent()` instead.
|
|
|
|
``` js
|
|
Gerrit.install(function(self) {
|
|
self.panel('CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK', function(context) {
|
|
context.body.innerHTML =
|
|
'Sample link: <a href="http://some.com/foo">Foo</a>';
|
|
context.show();
|
|
});
|
|
});
|
|
```
|
|
|
|
Here's the recommended approach that uses Polymer for generating custom elements:
|
|
|
|
``` html
|
|
<dom-module id="some-plugin">
|
|
<script>
|
|
Gerrit.install(plugin => {
|
|
plugin.registerCustomComponent(
|
|
'change-view-integration', 'some-ci-module');
|
|
});
|
|
</script>
|
|
</dom-module>
|
|
|
|
<dom-module id="some-ci-module">
|
|
<template>
|
|
Sample link: <a href="http://some.com/foo">Foo</a>
|
|
</template>
|
|
<script>
|
|
Polymer({is: 'some-ci-module'});
|
|
</script>
|
|
</dom-module>
|
|
```
|
|
|
|
Here's a minimal example that uses low-level DOM Hooks API for the same purpose:
|
|
|
|
``` js
|
|
Gerrit.install(plugin => {
|
|
plugin.hook('change-view-integration', el => {
|
|
el.innerHTML = 'Sample link: <a href="http://some.com/foo">Foo</a>';
|
|
});
|
|
});
|
|
```
|
|
|
|
=== popup
|
|
`plugin.popup(moduleName)`
|
|
|
|
Note: TODO
|
|
|
|
=== post
|
|
`plugin.post(url, payload, opt_callback)`
|
|
|
|
Note: TODO
|
|
|
|
[[plugin-rest-api]]
|
|
=== restApi
|
|
`plugin.restApi(opt_prefix)`
|
|
|
|
.Params:
|
|
- (optional) URL prefix, for easy switching into plugin URL space,
|
|
e.g. `changes/1/revisions/1/cookbook~say-hello`
|
|
|
|
.Returns:
|
|
- Instance of link:pg-plugin-rest-api.html[GrPluginRestApi].
|
|
|
|
[[plugin-repo]]
|
|
=== repo
|
|
`plugin.repo()`
|
|
|
|
.Params:
|
|
- none
|
|
|
|
.Returns:
|
|
- Instance of link:pg-plugin-repo-api.html[GrRepoApi].
|
|
|
|
=== put
|
|
`plugin.put(url, payload, opt_callback)`
|
|
|
|
Note: TODO
|
|
|
|
=== screen
|
|
`plugin.screen(screenName, opt_moduleName)`
|
|
|
|
.Params:
|
|
- `*string* screenName` URL path fragment of the screen, e.g.
|
|
`/x/pluginname/*screenname*`
|
|
- `*string* opt_moduleName` (Optional) Web component to be instantiated for this
|
|
screen.
|
|
|
|
.Returns:
|
|
- Instance of GrDomHook.
|
|
|
|
=== screenUrl
|
|
`plugin.url(opt_screenName)`
|
|
|
|
.Params:
|
|
- `*string* screenName` (optional) URL path fragment of the screen, e.g.
|
|
`/x/pluginname/*screenname*`
|
|
|
|
.Returns:
|
|
- Absolute URL for the screen, e.g. `http://localhost/base/x/pluginname/screenname`
|
|
|
|
[[plugin-settings]]
|
|
=== settings
|
|
`plugin.settings()`
|
|
|
|
.Params:
|
|
- none
|
|
|
|
.Returns:
|
|
- Instance of link:pg-plugin-settings-api.html[GrSettingsApi].
|
|
|
|
=== settingsScreen
|
|
`plugin.settingsScreen(path, menu, callback)`
|
|
|
|
Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
|
|
|
|
=== changeMetadata
|
|
`plugin.changeMetadata()`
|
|
|
|
.Params:
|
|
- none
|
|
|
|
.Returns:
|
|
- Instance of link:pg-plugin-change-metadata-api.html[GrChangeMetadataApi].
|
|
|
|
=== theme
|
|
`plugin.theme()`
|
|
|
|
Note: TODO
|
|
|
|
=== url
|
|
`plugin.url(opt_path)`
|
|
|
|
Note: TODO
|
|
|
|
[[deprecated-api]]
|
|
== Deprecated APIs
|
|
|
|
Some of the deprecated APIs have limited implementation in PolyGerrit to serve
|
|
as a "stepping stone" to allow gradual migration.
|
|
|
|
=== install
|
|
`plugin.deprecated.install()`
|
|
|
|
.Params:
|
|
- none
|
|
|
|
Replaces plugin APIs with a deprecated version. This allows use of deprecated
|
|
APIs without changing JS code. For example, `onAction` is not available by
|
|
default, and after `plugin.deprecated.install()` it's accessible via
|
|
`self.onAction()`.
|
|
|
|
=== onAction
|
|
`plugin.deprecated.onAction(type, view_name, callback)`
|
|
|
|
.Params:
|
|
- `*string* type` Action type.
|
|
- `*string* view_name` REST API action.
|
|
- `*function(actionContext)* callback` Callback invoked on action button click.
|
|
|
|
Adds a button to the UI with a click callback. Exact button location depends on
|
|
parameters. Callback is triggered with an instance of
|
|
link:#deprecated-action-context[action context].
|
|
|
|
Support is limited:
|
|
|
|
- type is either `change` or `revision`.
|
|
|
|
See link:js-api.html#self_onAction[self.onAction] for more info.
|
|
|
|
=== panel
|
|
`plugin.deprecated.panel(extensionpoint, callback)`
|
|
|
|
.Params:
|
|
- `*string* extensionpoint`
|
|
- `*function(screenContext)* callback`
|
|
|
|
Adds a UI DOM element and triggers a callback with context to allow direct DOM
|
|
access.
|
|
|
|
Support is limited:
|
|
|
|
- extensionpoint is one of the following:
|
|
* CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK
|
|
* CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK
|
|
|
|
See link:js-api.html#self_panel[self.panel] for more info.
|
|
|
|
=== settingsScreen
|
|
`plugin.deprecated.settingsScreent(path, menu, callback)`
|
|
|
|
.Params:
|
|
- `*string* path` URL path fragment of the screen for direct link.
|
|
- `*string* menu` Menu item title.
|
|
- `*function(settingsScreenContext)* callback`
|
|
|
|
Adds a settings menu item and a section in the settings screen that is provided
|
|
to plugin for setup.
|
|
|
|
See link:js-api.html#self_settingsScreen[self.settingsScreen] for more info.
|
|
|
|
[[deprecated-action-context]]
|
|
=== Action Context (deprecated)
|
|
Instance of Action Context is passed to `onAction()` callback.
|
|
|
|
Support is limited:
|
|
|
|
- `popup()`
|
|
- `hide()`
|
|
- `refresh()`
|
|
- `textfield()`
|
|
- `br()`
|
|
- `msg()`
|
|
- `div()`
|
|
- `button()`
|
|
- `checkbox()`
|
|
- `label()`
|
|
- `prependLabel()`
|
|
- `call()`
|
|
|
|
See link:js-api.html#ActionContext[Action Context] for more info.
|