blob: adb5d20ac5f318b89d2e0de895ee0d145af3d387 [file] [log] [blame]
Marian Harbachebeb1542019-12-13 10:42:46 +01001:linkattrs:
Viktar Donich2b2fdb72017-03-13 16:21:44 -07002= Gerrit Code Review - PolyGerrit Plugin Development
3
Viktar Donich5055e8d2017-11-09 13:02:42 -08004CAUTION: Work in progress. Hard hat area. Please
5link:https://bugs.chromium.org/p/gerrit/issues/entry?template=PolyGerrit%20plugins[send
Marian Harbach34253372019-12-10 18:01:31 +01006feedback,role=external,window=_blank] if something's not right.
Viktar Donich5055e8d2017-11-09 13:02:42 -08007
Viktar Donich15ef24c2017-12-07 16:04:38 -08008For migrating existing GWT UI plugins, please check out the
9link:pg-plugin-migration.html#migration[migration guide].
10
Viktar Donich9bd0ce62017-02-07 08:56:33 -080011[[loading]]
Viktar Donich2b2fdb72017-03-13 16:21:44 -070012== Plugin loading and initialization
13
Tao Zhoud2a8ded2020-06-05 16:08:54 +020014link:js-api.html#_entry_point[Entry point] for the plugin.
Viktar Donich2b2fdb72017-03-13 16:21:44 -070015
Tao Zhoud2a8ded2020-06-05 16:08:54 +020016* The plugin provides pluginname.js, and can be a standalone file or a static
Viktar Donich5055e8d2017-11-09 13:02:42 -080017 asset in a jar as a link:dev-plugins.html#deployment[Web UI plugin].
Tao Zhoud2a8ded2020-06-05 16:08:54 +020018* pluginname.js contains a call to `Gerrit.install()`. There should
19 only be single `Gerrit.install()` per file.
20* PolyGerrit imports pluginname.js.
21* For standalone plugins, the entry point file is a `pluginname.js` file
Viktar Donichcbc25672017-07-24 13:23:18 -070022 located in `gerrit-site/plugins` folder, where `pluginname` is an alphanumeric
23 plugin name.
Viktar Donich2b2fdb72017-03-13 16:21:44 -070024
Quinten Yearsley888e6382017-12-05 11:11:09 -080025Note: Code examples target modern browsers (Chrome, Firefox, Safari, Edge).
Viktar Donich5055e8d2017-11-09 13:02:42 -080026
Tao Zhoud2a8ded2020-06-05 16:08:54 +020027Here's a recommended starter `myplugin.js`:
Viktar Donich2b2fdb72017-03-13 16:21:44 -070028
Tao Zhoud2a8ded2020-06-05 16:08:54 +020029``` js
30Gerrit.install(plugin => {
31 'use strict';
Viktar Donich5055e8d2017-11-09 13:02:42 -080032
Tao Zhoud2a8ded2020-06-05 16:08:54 +020033 // Your code here.
34});
Viktar Donich2b2fdb72017-03-13 16:21:44 -070035```
Viktar Donichcbc25672017-07-24 13:23:18 -070036
Viktar Donich7610f782017-10-02 11:51:41 +010037[[low-level-api-concepts]]
38== Low-level DOM API concepts
Viktar Donichcbc25672017-07-24 13:23:18 -070039
40Basically, the DOM is the API surface. Low-level API provides methods for
41decorating, replacing, and styling DOM elements exposed through a set of
Viktar Donich6d10eca2017-11-13 17:57:43 -080042link:pg-plugin-endpoints.html[endpoints].
Viktar Donichcbc25672017-07-24 13:23:18 -070043
44PolyGerrit provides a simple way for accessing the DOM via DOM hooks API. A DOM
45hook is a custom element that is instantiated for the plugin endpoint. In the
46decoration case, a hook is set with a `content` attribute that points to the DOM
47element.
48
Viktar Donichab491f772017-08-15 08:02:58 -0700491. Get the DOM hook API instance via `plugin.hook(endpointName)`
Viktar Donichcbc25672017-07-24 13:23:18 -0700502. Set up an `onAttached` callback
513. Callback is called when the hook element is created and inserted into DOM
524. Use element.content to get UI element
53
54``` js
Viktar Donich5055e8d2017-11-09 13:02:42 -080055Gerrit.install(plugin => {
Viktar Donichab491f772017-08-15 08:02:58 -070056 const domHook = plugin.hook('reply-text');
Viktar Donichcbc25672017-07-24 13:23:18 -070057 domHook.onAttached(element => {
58 if (!element.content) { return; }
59 // element.content is a reply dialog text area.
60 });
61});
62```
63
64[[low-level-decorating]]
65=== Decorating DOM Elements
66
67For each endpoint, PolyGerrit provides a list of DOM properties (such as
68attributes and events) that are supported in the long-term.
69
Viktar Donichcbc25672017-07-24 13:23:18 -070070``` js
Viktar Donich5055e8d2017-11-09 13:02:42 -080071Gerrit.install(plugin => {
Viktar Donichab491f772017-08-15 08:02:58 -070072 const domHook = plugin.hook('reply-text');
Viktar Donichcbc25672017-07-24 13:23:18 -070073 domHook.onAttached(element => {
74 if (!element.content) { return; }
75 element.content.style.border = '1px red dashed';
76 });
77});
78```
79
80[[low-level-replacing]]
81=== Replacing DOM Elements
82
83An endpoint's contents can be replaced by passing the replace attribute as an
84option.
85
86``` js
Viktar Donich5055e8d2017-11-09 13:02:42 -080087Gerrit.install(plugin => {
Viktar Donichab491f772017-08-15 08:02:58 -070088 const domHook = plugin.hook('header-title', {replace: true});
Viktar Donichcbc25672017-07-24 13:23:18 -070089 domHook.onAttached(element => {
90 element.appendChild(document.createElement('my-site-header'));
91 });
92});
93```
94
95[[low-level-style]]
96=== Styling DOM Elements
97
98A plugin may provide Polymer's
Tao Zhoud2a8ded2020-06-05 16:08:54 +020099https://polymer-library.polymer-project.org/3.0/docs/devguide/style-shadow-dom[style
Marian Harbach34253372019-12-10 18:01:31 +0100100modules,role=external,window=_blank] to style individual endpoints using
Viktar Donichcbc25672017-07-24 13:23:18 -0700101`plugin.registerStyleModule(endpointName, moduleName)`. A style must be defined
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200102as a standalone `<dom-module>` defined in the same .js file.
103
104See `samples/theme-plugin.js` for examples.
Viktar Donichcbc25672017-07-24 13:23:18 -0700105
106Note: TODO: Insert link to the full styling API.
107
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200108``` js
109const styleElement = document.createElement('dom-module');
110styleElement.innerHTML =
111 `<template>
112 <style>
Viktar Donichcbc25672017-07-24 13:23:18 -0700113 html {
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200114 --primary-text-color: red;
Viktar Donichcbc25672017-07-24 13:23:18 -0700115 }
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200116 </style>
117 </template>`;
118
119styleElement.register('some-style-module');
120
121Gerrit.install(plugin => {
122 plugin.registerStyleModule('change-metadata', 'some-style-module');
123});
Viktar Donichcbc25672017-07-24 13:23:18 -0700124```
Viktar Donich7610f782017-10-02 11:51:41 +0100125
126[[high-level-api-concepts]]
127== High-level DOM API concepts
128
Quinten Yearsley888e6382017-12-05 11:11:09 -0800129High level API is based on low-level DOM API and is essentially a standardized
Viktar Donich7610f782017-10-02 11:51:41 +0100130way for doing common tasks. It's less flexible, but will be a bit more stable.
131
Quinten Yearsley888e6382017-12-05 11:11:09 -0800132The common way to access high-level API is through `plugin` instance passed
133into setup callback parameter of `Gerrit.install()`, also sometimes referred to
134as `self`.
Viktar Donich7610f782017-10-02 11:51:41 +0100135
136[[low-level-api]]
137== Low-level DOM API
138
Quinten Yearsley888e6382017-12-05 11:11:09 -0800139The low-level DOM API methods are the base of all UI customization.
Viktar Donich7610f782017-10-02 11:51:41 +0100140
141=== attributeHelper
142`plugin.attributeHelper(element)`
143
Viktar Donich6e1e69b2018-01-22 14:08:21 -0800144Alternative for
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200145link:https://polymer-library.polymer-project.org/3.0/docs/devguide/data-binding[Polymer data
Marian Harbach34253372019-12-10 18:01:31 +0100146binding,role=external,window=_blank] for plugins that don't use Polymer. Can be used to bind element
Viktar Donich6e1e69b2018-01-22 14:08:21 -0800147attribute changes to callbacks.
148
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200149See `samples/bind-parameters.js` for examples on both Polymer data bindings
Viktar Donich6e1e69b2018-01-22 14:08:21 -0800150and `attibuteHelper` usage.
Viktar Donich7610f782017-10-02 11:51:41 +0100151
152=== eventHelper
153`plugin.eventHelper(element)`
154
155Note: TODO
156
157=== hook
158`plugin.hook(endpointName, opt_options)`
159
Viktar Donich6d10eca2017-11-13 17:57:43 -0800160See list of supported link:pg-plugin-endpoints.html[endpoints].
Viktar Donich6ea8c8a2017-12-12 14:41:47 -0800161
Viktar Donich7610f782017-10-02 11:51:41 +0100162Note: TODO
163
164=== registerCustomComponent
165`plugin.registerCustomComponent(endpointName, opt_moduleName, opt_options)`
166
Viktar Donich6ea8c8a2017-12-12 14:41:47 -0800167See list of supported link:pg-plugin-endpoints.html[endpoints].
168
Viktar Donich7610f782017-10-02 11:51:41 +0100169Note: TODO
170
Thomas Shafer03275862019-02-26 15:39:16 -0800171=== registerDynamicCustomComponent
172`plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName,
173opt_options)`
174
175See list of supported link:pg-plugin-endpoints.html[endpoints].
176
177Note: TODO
178
Viktar Donich7610f782017-10-02 11:51:41 +0100179=== registerStyleModule
180`plugin.registerStyleModule(endpointName, moduleName)`
181
182Note: TODO
183
184[[high-level-api]]
185== High-level API
186
187Plugin instance provides access to number of more specific APIs and methods
188to be used by plugin authors.
189
Wyatt Allenc1485932018-03-30 10:53:36 -0700190=== admin
191`plugin.admin()`
192
193.Params:
194- none
195
196.Returns:
197- Instance of link:pg-plugin-admin-api.html[GrAdminApi].
198
Viktar Donich7610f782017-10-02 11:51:41 +0100199=== changeReply
200`plugin.changeReply()`
201
202Note: TODO
203
Viktar Donich7610f782017-10-02 11:51:41 +0100204=== delete
205`plugin.delete(url, opt_callback)`
206
207Note: TODO
208
209=== get
210`plugin.get(url, opt_callback)`
211
212Note: TODO
213
214=== getPluginName
215`plugin.getPluginName()`
216
217Note: TODO
218
219=== getServerInfo
220`plugin.getServerInfo()`
221
222Note: TODO
223
224=== on
225`plugin.on(eventName, callback)`
226
227Note: TODO
228
Viktar Donich9c7164a2017-12-19 15:03:58 -0800229=== panel
230`plugin.panel(extensionpoint, callback)`
231
232Deprecated. Use `plugin.registerCustomComponent()` instead.
233
234``` js
235Gerrit.install(function(self) {
236 self.panel('CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK', function(context) {
237 context.body.innerHTML =
238 'Sample link: <a href="http://some.com/foo">Foo</a>';
239 context.show();
240 });
241});
242```
243
244Here's the recommended approach that uses Polymer for generating custom elements:
245
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200246``` js
247class SomeCiModule extends Polymer.Element {
248 static get is() {
249 return "some-ci-module";
250 }
251 static get template() {
252 return Polymer.html`
253 Sample link: <a href="http://some.com/foo">Foo</a>
254 `;
255 }
256}
Viktar Donich9c7164a2017-12-19 15:03:58 -0800257
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200258// Register this element
259customElements.define(SomeCiModule.is, SomeCiModule);
260
261// Install the plugin
262Gerrit.install(plugin => {
263 plugin.registerCustomComponent('change-view-integration', 'some-ci-module');
264});
Viktar Donich9c7164a2017-12-19 15:03:58 -0800265```
266
Tao Zhoud2a8ded2020-06-05 16:08:54 +0200267See `samples/` for more examples.
268
Viktar Donich9c7164a2017-12-19 15:03:58 -0800269Here's a minimal example that uses low-level DOM Hooks API for the same purpose:
270
271``` js
272Gerrit.install(plugin => {
273 plugin.hook('change-view-integration', el => {
274 el.innerHTML = 'Sample link: <a href="http://some.com/foo">Foo</a>';
275 });
276});
277```
278
Viktar Donich7610f782017-10-02 11:51:41 +0100279=== popup
280`plugin.popup(moduleName)`
281
282Note: TODO
283
284=== post
285`plugin.post(url, payload, opt_callback)`
286
287Note: TODO
288
Viktar Donichbc8088e2018-04-23 15:29:57 -0700289[[plugin-rest-api]]
290=== restApi
291`plugin.restApi(opt_prefix)`
292
293.Params:
294- (optional) URL prefix, for easy switching into plugin URL space,
295 e.g. `changes/1/revisions/1/cookbook~say-hello`
296
297.Returns:
298- Instance of link:pg-plugin-rest-api.html[GrPluginRestApi].
299
300[[plugin-repo]]
Paladox none2bd5c212017-11-16 18:54:02 +0000301=== repo
302`plugin.repo()`
Viktar Donich7610f782017-10-02 11:51:41 +0100303
304.Params:
305- none
306
307.Returns:
Paladox none2bd5c212017-11-16 18:54:02 +0000308- Instance of link:pg-plugin-repo-api.html[GrRepoApi].
Viktar Donich7610f782017-10-02 11:51:41 +0100309
310=== put
311`plugin.put(url, payload, opt_callback)`
312
313Note: TODO
314
Viktar Donich6ea8c8a2017-12-12 14:41:47 -0800315=== screen
316`plugin.screen(screenName, opt_moduleName)`
317
318.Params:
319- `*string* screenName` URL path fragment of the screen, e.g.
320`/x/pluginname/*screenname*`
321- `*string* opt_moduleName` (Optional) Web component to be instantiated for this
322screen.
323
324.Returns:
325- Instance of GrDomHook.
326
327=== screenUrl
328`plugin.url(opt_screenName)`
329
330.Params:
331- `*string* screenName` (optional) URL path fragment of the screen, e.g.
332`/x/pluginname/*screenname*`
333
334.Returns:
335- Absolute URL for the screen, e.g. `http://localhost/base/x/pluginname/screenname`
336
Viktar Donich17c02262017-12-21 15:46:02 -0800337[[plugin-settings]]
338=== settings
339`plugin.settings()`
340
341.Params:
342- none
343
344.Returns:
345- Instance of link:pg-plugin-settings-api.html[GrSettingsApi].
346
347=== settingsScreen
348`plugin.settingsScreen(path, menu, callback)`
349
350Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
351
Dmitrii Filippov941ee632019-08-22 15:52:37 +0000352[[plugin-styles]]
353=== styles
354`plugin.styles()`
355
356.Params:
357- none
358
359.Returns:
360- Instance of link:pg-plugin-styles-api.html[GrStylesApi]
361
Viktar Donichb6db19e2018-04-03 15:32:54 -0700362=== changeMetadata
363`plugin.changeMetadata()`
364
365.Params:
366- none
367
368.Returns:
369- Instance of link:pg-plugin-change-metadata-api.html[GrChangeMetadataApi].
370
Viktar Donich7610f782017-10-02 11:51:41 +0100371=== theme
372`plugin.theme()`
373
Dmitrii Filippov941ee632019-08-22 15:52:37 +0000374
Viktar Donich7610f782017-10-02 11:51:41 +0100375Note: TODO
376
377=== url
378`plugin.url(opt_path)`
379
380Note: TODO
Viktar Donich9c7164a2017-12-19 15:03:58 -0800381
382[[deprecated-api]]
383== Deprecated APIs
384
385Some of the deprecated APIs have limited implementation in PolyGerrit to serve
386as a "stepping stone" to allow gradual migration.
387
388=== install
389`plugin.deprecated.install()`
390
391.Params:
392- none
393
394Replaces plugin APIs with a deprecated version. This allows use of deprecated
395APIs without changing JS code. For example, `onAction` is not available by
396default, and after `plugin.deprecated.install()` it's accessible via
397`self.onAction()`.
398
399=== onAction
400`plugin.deprecated.onAction(type, view_name, callback)`
401
402.Params:
403- `*string* type` Action type.
404- `*string* view_name` REST API action.
405- `*function(actionContext)* callback` Callback invoked on action button click.
406
407Adds a button to the UI with a click callback. Exact button location depends on
408parameters. Callback is triggered with an instance of
409link:#deprecated-action-context[action context].
410
411Support is limited:
412
413- type is either `change` or `revision`.
414
415See link:js-api.html#self_onAction[self.onAction] for more info.
416
417=== panel
418`plugin.deprecated.panel(extensionpoint, callback)`
419
420.Params:
421- `*string* extensionpoint`
422- `*function(screenContext)* callback`
423
424Adds a UI DOM element and triggers a callback with context to allow direct DOM
425access.
426
427Support is limited:
428
429- extensionpoint is one of the following:
430 * CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK
431 * CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK
432
433See link:js-api.html#self_panel[self.panel] for more info.
434
Viktar Donich17c02262017-12-21 15:46:02 -0800435=== settingsScreen
436`plugin.deprecated.settingsScreent(path, menu, callback)`
437
438.Params:
439- `*string* path` URL path fragment of the screen for direct link.
440- `*string* menu` Menu item title.
441- `*function(settingsScreenContext)* callback`
442
443Adds a settings menu item and a section in the settings screen that is provided
444to plugin for setup.
445
446See link:js-api.html#self_settingsScreen[self.settingsScreen] for more info.
447
Viktar Donich9c7164a2017-12-19 15:03:58 -0800448[[deprecated-action-context]]
449=== Action Context (deprecated)
450Instance of Action Context is passed to `onAction()` callback.
451
452Support is limited:
453
454- `popup()`
455- `hide()`
456- `refresh()`
457- `textfield()`
458- `br()`
459- `msg()`
460- `div()`
461- `button()`
462- `checkbox()`
463- `label()`
464- `prependLabel()`
465- `call()`
466
467See link:js-api.html#ActionContext[Action Context] for more info.