Merge branch 'stable-2.16' into 'stable-3.0' * stable-2.16: Polygerrit UI Change-Id: Ieb92f98d48df99c14912a9d01b8fa7b69f61e496
diff --git a/BUILD b/BUILD index a26e3b7..32b4c8d 100644 --- a/BUILD +++ b/BUILD
@@ -6,6 +6,8 @@ "PLUGIN_TEST_DEPS", "gerrit_plugin", ) +load("//tools/bzl:genrule2.bzl", "genrule2") +load("//tools/bzl:js.bzl", "polygerrit_plugin") gerrit_plugin( name = "reviewers", @@ -15,6 +17,28 @@ "Gerrit-Module: com.googlesource.gerrit.plugins.reviewers.Module", ], resources = glob(["src/main/resources/**/*"]), + resource_jars = [":rv-reviewers-static"], +) + +genrule2( + name = "rv-reviewers-static", + srcs = [":rv-reviewers"], + outs = ["rv-reviewers-static.jar"], + cmd = " && ".join([ + "mkdir $$TMP/static", + "cp -r $(locations :rv-reviewers) $$TMP/static", + "cd $$TMP", + "zip -Drq $$ROOT/$@ -g .", + ]), +) + +polygerrit_plugin( + name = "rv-reviewers", + srcs = glob([ + "rv-reviewers/*.html", + "rv-reviewers/*.js", + ]), + app = "plugin.html", ) junit_tests(
diff --git a/plugin.html b/plugin.html new file mode 100644 index 0000000..29c10cb --- /dev/null +++ b/plugin.html
@@ -0,0 +1,27 @@ +<!-- +@license +Copyright (C) 2019 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="./rv-reviewers/rv-reviewers.html"> + +<dom-module id="reviewers"> + <script> + if (window.Polymer) { + Gerrit.install(plugin => { + plugin.registerCustomComponent('repo-command', 'rv-reviewers'); + }); + } + </script> +</dom-module>
diff --git a/rv-reviewers/rv-edit-screen.html b/rv-reviewers/rv-edit-screen.html new file mode 100644 index 0000000..ca32849 --- /dev/null +++ b/rv-reviewers/rv-edit-screen.html
@@ -0,0 +1,76 @@ +<!-- +@license +Copyright (C) 2019 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="./rv-filter-section.html"> + +<dom-module id="rv-edit-screen"> + <template> + <style include="shared-styles"></style> + <style include="gr-menu-page-styles"></style> + <style include="gr-subpage-styles"> + .bottomButtons { + display: flex; + } + #closeButton { + float: right; + } + #filterSections { + width: 100%; + } + header { + border-bottom: 1px solid var(--border-colo); + flex-shrink: 0; + font-weight: var(--font-weight-bold) + } + </style> + <div> + <header>Reviewers Config</header> + <table id="filterSections"> + <tr> + <th>Filter Sections</th> + </tr> + <tr id="loading" class$="loadingMsg [[_computeLoadingClass(loading)]]"> + <td>Loading...</td> + </tr> + <tbody class$="[[_computeLoadingClass(loading)]]"> + <tr> + <template + is="dom-repeat" + items="[[_filterSections]]" + as="section"> + <rv-filter-section + filter="[[section.filter]]" + reviewers="[[section.reviewers]]" + editing="[[section.editing]]" + reviewers-url="[[_getReviewersUrl(repoName)]]" + plugin-rest-api="[[pluginRestApi]]" + can-modify-config="[[canModifyConfig]]" + on-reviewer-changed="_handleReviewerChanged"></rv-filter-section> + </template> + </tr> + </tbody> + </table> + <div class="bottomButtons"> + <gr-button id="closeButton" on-tap="_handleCloseTap">Close</gr-button> + <gr-button + id="addFilterBtn" + on-tap="_handleCreateSection" + hidden="[[_computeAddFilterBtnHidden(canModifyConfig, _editingFilter)]]">Add New Filter</gr-button> + </div> + </div> + </template> + <script src="./rv-edit-screen.js"></script> +</dom-module>
diff --git a/rv-reviewers/rv-edit-screen.js b/rv-reviewers/rv-edit-screen.js new file mode 100644 index 0000000..f25f696 --- /dev/null +++ b/rv-reviewers/rv-edit-screen.js
@@ -0,0 +1,69 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + Polymer({ + is: 'rv-edit-screen', + + properties: { + pluginRestApi: Object, + repoName: String, + loading: Boolean, + canModifyConfig: Boolean, + _editingFilter: { + type: Boolean, + value: false, + }, + _filterSections: Array, + }, + + attached() { + this._loadFilterSections(); + }, + + _loadFilterSections() { + this.pluginRestApi.get(this._getReviewersUrl(this.repoName)) + .then(filterSections => { + this._filterSections = filterSections; + }); + }, + + _computeAddFilterBtnHidden(canModifyConfig, editingFilter) { + return !canModifyConfig || editingFilter; + }, + + _computeLoadingClass(loading) { + return loading ? 'loading' : ''; + }, + + _getReviewersUrl(repoName) { + return `/projects/${repoName}/reviewers`; + }, + + _handleCreateSection() { + const section = {filter: '', reviewers: [], editing: true}; + this._editingFilter = true; + this.push('_filterSections', section); + }, + + _handleCloseTap(e) { + e.preventDefault(); + this.fire('close', null, {bubbles: false}); + }, + + _handleReviewerChanged(e) { + this._filterSections = e.detail.result; + this._editingFilter = false; + }, + }); +})();
diff --git a/rv-reviewers/rv-filter-section.html b/rv-reviewers/rv-filter-section.html new file mode 100644 index 0000000..d4784cc --- /dev/null +++ b/rv-reviewers/rv-filter-section.html
@@ -0,0 +1,96 @@ +<!-- +@license +Copyright (C) 2019 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="./rv-reviewer.html"> + +<dom-module id="rv-filter-section"> + <template> + <style include="shared-styles"> + :host { + display: block; + margin-bottom: 1em; + } + fieldset { + border: 1px solid var(--border-color); + } + .name { + align-items: center; + display: flex; + } + .header { + align-items: center; + background: var(--table-header-background-color); + border-bottom: 1px dotted var(--border-color); + display: flex; + justify-content: space-between; + min-height: 3em; + padding: 0 .7em; + } + #addReviewer { + display: flex; + } + #editFilterInput { + width: 30vw; + max-width: 500px; + margin-left: 3px; + } + #mainContainer { + display: block; + } + </style> + <style include="gr-form-styles"></style> + <fieldset id="section" + class$="gr-form-styles"> + <div id="mainContainer"> + <div class="header"> + <div class="name"> + <h3>Filter:</h3> + <input + id="editFilterInput" + bind-value="{{filter}}" + is="iron-input" + type="text" + disabled="[[_computeFilterInputDisabled(canModifyConfig, _originalFilter)]]"> + <gr-button + id="cancelBtn" + on-tap="_handleCancel" + hidden$="[[_computeCancelHidden(filter, _originalFilter)]]">Cancel</gr-button> + </div><!-- name --> + </div><!-- header --> + <div class="reviewers"> + <template + is="dom-repeat" + items="{{reviewers}}"> + <rv-reviewer + reviewer="{{item}}" + can-modify-config="[[canModifyConfig]]" + on-reviewer-deleted="_handleReviewerDeleted" + on-reviewer-added="_handleReviewerAdded"> + </rv-reviewer> + </template> + <div id="addReviewer"> + <gr-button + link + id="addBtn" + on-tap="_handleAddReviewer" + hidden="[[_computeAddBtnHidden(canModifyConfig, _editingReviewer)]]">Add Reviewer</gr-button> + </div><!-- addReviewer --> + </div><!-- reviewers --> + </div> + </fieldset> + </template> + <script src="./rv-filter-section.js"></script> +</dom-module>
diff --git a/rv-reviewers/rv-filter-section.js b/rv-reviewers/rv-filter-section.js new file mode 100644 index 0000000..0ca36dc --- /dev/null +++ b/rv-reviewers/rv-filter-section.js
@@ -0,0 +1,98 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + Polymer({ + is: 'rv-filter-section', + + properties: { + pluginRestApi: Object, + reviewers: Array, + filter: String, + canModifyConfig: Boolean, + _originalFilter: String, + _editingReviewer: { + type: Boolean, + value: false, + }, + reviewersUrl: String, + }, + + attached() { + this._updateSection(); + }, + + _updateSection() { + this._originalFilter = this.filter; + }, + + _computeEditing(filter, _originalFilter) { + if (_originalFilter === '') { + return true; + } + return filter === ''; + }, + + _computeCancelHidden(filter, _originalFilter) { + return !this._computeEditing(filter, _originalFilter); + }, + + _computeAddBtnHidden(canModifyConfig, editingReviewer) { + return !(canModifyConfig && !editingReviewer); + }, + + _computeFilterInputDisabled(canModifyConfig, originalFilter) { + return !canModifyConfig || originalFilter !== ''; + }, + + _handleCancel() { + this.remove(); + }, + + _handleReviewerDeleted(e) { + if (e.detail.editing) { + this.reviewers.pop(); + this._editingReviewer = false; + } else { + const index = e.model.index; + const deleted = this.reviewers[index]; + this._putReviewer(deleted, 'DELETE'); + } + }, + + _handleReviewerAdded(e) { + this._editingReviewer = false; + this._putReviewer(e.detail.reviewer, 'ADD').catch(err => { + this.fire('show-alert', {message: err}); + throw err; + }); + }, + + _putReviewer(reviewer, action) { + return this.pluginRestApi.put(this.reviewersUrl, { + action, + reviewer, + filter: this.filter, + }).then(result => { + const detail = {result}; + this.dispatchEvent( + new CustomEvent('reviewer-changed', {detail, bubbles: true})); + }); + }, + + _handleAddReviewer() { + this.push('reviewers', ''); + this._editingReviewer = true; + }, + }); +})();
diff --git a/rv-reviewers/rv-reviewer.html b/rv-reviewers/rv-reviewer.html new file mode 100644 index 0000000..3609612 --- /dev/null +++ b/rv-reviewers/rv-reviewer.html
@@ -0,0 +1,52 @@ +<!-- +@license +Copyright (C) 2019 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<dom-module id="rv-reviewer"> + <template> + <style include="shared-styles"> + #editReviewerInput { + display: block; + } + .reviewerRow { + align-items: center; + display: flex; + } + #reviewerHeader, #editReviewerInput, #deleteCancelBtn, #addBtn { + margin-left: 3px; + } + </style> + <style include="gr-form-styles"></style> + <div class="reviewerRow"> + <h4 id="reviewerHeader">Reviewer:</h4> + <input + id="editReviewerInput" + bind-value="{{reviewer}}" + is="iron-input" + type="text" + disabled="[[_computeReviewerDisabled(reviewer, _originalReviewer)]]"> + <gr-button + id="deleteCancelBtn" + on-tap="_handleDeleteCancel" + hidden$="[[_computeHideDeleteButton(canModifyConfig)]]" + >[[_computeDeleteCancel(reviewer, _originalReviewer)]]</gr-button> + <gr-button + id="addBtn" + on-tap="_handleAddReviewer" + hidden$="[[_computeHideAddButton(reviewer, _originalReviewer)]]">Add</gr-button> + </div> <!-- reviewerRow --> + </template> + <script src="./rv-reviewer.js"></script> +</dom-module>
diff --git a/rv-reviewers/rv-reviewer.js b/rv-reviewers/rv-reviewer.js new file mode 100644 index 0000000..9698a22 --- /dev/null +++ b/rv-reviewers/rv-reviewer.js
@@ -0,0 +1,74 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + Polymer({ + is: 'rv-reviewer', + + properties: { + reviewer: String, + canModifyConfig: Boolean, + _originalReviewer: String, + _deleted: Boolean, + _editing: { + type: Boolean, + computed: '_computeEditing(reviewer, _originalReviewer)', + }, + }, + + attached() { + this._originalReviewer = this.reviewer; + }, + + _computeReviewerDisabled(reviewer, _originalReviewer) { + return !this._computeEditing(reviewer, _originalReviewer); + }, + + _computeEditing(reviewer, _originalReviewer) { + if (_originalReviewer === '') { + return true; + } + return reviewer === ''; + }, + + _computeDeleteCancel(reviewer, _originalReviewer) { + return this._computeEditing(reviewer, _originalReviewer) ? + 'Cancel' : 'Delete'; + }, + + _computeHideAddButton(reviewer, _originalReviewer) { + return !(this._computeEditing(reviewer, _originalReviewer) + && this.$.editReviewerInput.value); + }, + + _computeHideDeleteButton(canModifyConfig) { + return !canModifyConfig; + }, + + _handleDeleteCancel() { + const detail = {editing: this._editing}; + if (this._editing) { + this.remove(); + } + this.dispatchEvent( + new CustomEvent('reviewer-deleted', {detail, bubbles: true})); + }, + + _handleAddReviewer() { + const detail = {reviewer: this.reviewer}; + this._originalReviewer = this.reviewer; + this.dispatchEvent( + new CustomEvent('reviewer-added', {detail, bubbles: true})); + }, + }); +})();
diff --git a/rv-reviewers/rv-reviewers.html b/rv-reviewers/rv-reviewers.html new file mode 100644 index 0000000..dcf890d --- /dev/null +++ b/rv-reviewers/rv-reviewers.html
@@ -0,0 +1,40 @@ +<!-- +@license +Copyright (C) 2019 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="./rv-edit-screen.html"> + +<dom-module id="rv-reviewers"> + <template> + <style include="shared-styles"> + #rvScreenOverlay { + width: 50em; + } + </style> + <gr-repo-command + title="Reviewers Config" + on-command-tap="_handleCommandTap"> + </gr-repo-command> + <gr-overlay id="rvScreenOverlay" with-backdrop> + <rv-edit-screen + plugin-rest-api="[[pluginRestApi]]" + repo-name="[[repoName]]" + loading="[[_loading]]" + can-modify-config="[[_canModifyConfig]]" + on-close="_handleRvEditScreenClose"></rv-edit-screen> + </gr-overlay> + </template> + <script src="./rv-reviewers.js"></script> +</dom-module>
diff --git a/rv-reviewers/rv-reviewers.js b/rv-reviewers/rv-reviewers.js new file mode 100644 index 0000000..e40e03a --- /dev/null +++ b/rv-reviewers/rv-reviewers.js
@@ -0,0 +1,82 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + Polymer({ + is: 'rv-reviewers', + + properties: { + pluginRestApi: Object, + repoName: String, + _canModifyConfig: { + type: Boolean, + computed: '_computeCanModifyConfig(_isOwner, _hasModifyCapability)', + }, + _loading: { + type: Boolean, + value: true, + }, + _isOwner: { + type: Boolean, + value: false, + }, + _hasModifyCapability: { + type: Boolean, + value: false, + }, + }, + + attached() { + this.pluginRestApi = this.plugin.restApi(); + this._setCanModifyConfig(); + }, + + _handleCommandTap() { + this.$.rvScreenOverlay.open(); + }, + + _handleRvEditScreenClose() { + this.$.rvScreenOverlay.close(); + }, + + _setCanModifyConfig() { + const promises = []; + promises.push(this._getRepoAccess(this.repoName).then( access => { + if (access && access[this.repoName] && access[this.repoName].is_owner) { + this._isOwner = true; + } + })); + promises.push(this._getCapabilities().then(capabilities => { + if (capabilities['reviewers-modifyReviewersConfig']) { + this._hasModifyCapability = true; + } + })); + Promise.all(promises).then(() => { + this._loading = false; + }); + }, + + _computeCanModifyConfig(isOwner, hasModifyCapability) { + return isOwner || hasModifyCapability; + }, + + _getRepoAccess(repoName) { + return this.pluginRestApi.get( + '/access/?project=' + encodeURIComponent(repoName)); + }, + + _getCapabilities() { + return this.pluginRestApi.get('/accounts/self/capabilities'); + }, + }); +})();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java index 076040b..efc3614 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
@@ -25,6 +25,8 @@ import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.RestApiModule; +import com.google.gerrit.extensions.webui.JavaScriptPlugin; +import com.google.gerrit.extensions.webui.WebUiPlugin; import com.google.gerrit.server.change.ReviewerSuggestion; import com.google.inject.AbstractModule; import com.google.inject.Inject; @@ -78,5 +80,13 @@ } }); } + install( + new AbstractModule() { + @Override + protected void configure() { + DynamicSet.bind(binder(), WebUiPlugin.class) + .toInstance(new JavaScriptPlugin("rv-reviewers.html")); + } + }); } }