 ebeb15460c
			
		
	
	ebeb15460c
	
	
	
		
			
			Asciidoc may fail to render link attributes for external links supposed to open in a new window correctly. This change adds :linkattrs: to the beginning of such files to force parsing link attributes correctly. Bug: Issue 12068 Change-Id: If18be60de646ff78f672239dd4fa435fd4fd92ab
		
			
				
	
	
		
			480 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			480 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| :linkattrs:
 | |
| = 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,role=external,window=_blank] 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,role=external,window=_blank]
 | |
| 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,role=external,window=_blank] 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,role=external,window=_blank] 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
 | |
| 
 | |
| === registerDynamicCustomComponent
 | |
| `plugin.registerDynamicCustomComponent(dynamicEndpointName, 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.
 | |
| 
 | |
| [[plugin-styles]]
 | |
| === styles
 | |
| `plugin.styles()`
 | |
| 
 | |
| .Params:
 | |
| - none
 | |
| 
 | |
| .Returns:
 | |
| - Instance of link:pg-plugin-styles-api.html[GrStylesApi]
 | |
| 
 | |
| === 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.
 |