PolyGerrit: Implement support for New Agreement screen Bug: Issue 6866 Bug: Issue 6783 Change-Id: I3e69d317255f7318e225ff54cf192d9812fe0a70
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js index af2b9e6..43bf0ff 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -21,7 +21,8 @@ CUSTOM_DASHBOARD: /^\/dashboard\/?$/, PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/, - AGREEMENTS: /^\/settings\/(agreements|new-agreement)/, + AGREEMENTS: /^\/settings\/agreements\/?/, + NEW_AGREEMENTS: /^\/settings\/new-agreement\/?/, REGISTER: /^\/register(\/.*)?$/, // Pattern for login and logout URLs intended to be passed-through. May @@ -771,6 +772,9 @@ this._mapRoute(RoutePattern.AGREEMENTS, '_handleAgreementsRoute', true); + this._mapRoute(RoutePattern.NEW_AGREEMENTS, '_handleNewAgreementsRoute', + true); + this._mapRoute(RoutePattern.SETTINGS_LEGACY, '_handleSettingsLegacyRoute', true); @@ -1272,7 +1276,13 @@ } }, + // TODO fix this so it properly redirects + // to /settings#Agreements (Scrolls down) _handleAgreementsRoute(data) { + this._redirect('/settings/#Agreements'); + }, + + _handleNewAgreementsRoute(data) { data.params.view = Gerrit.Nav.View.AGREEMENTS; this._setParams(data.params); },
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html index ae002af..1b35443 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -134,6 +134,7 @@ '_handleGroupListOffsetRoute', '_handleGroupMembersRoute', '_handleGroupRoute', + '_handleNewAgreementsRoute', '_handlePluginListFilterOffsetRoute', '_handlePluginListFilterRoute', '_handlePluginListOffsetRoute', @@ -532,7 +533,14 @@ }); test('_handleAgreementsRoute', () => { - element._handleAgreementsRoute({params: {}}); + const data = {params: {}}; + element._handleAgreementsRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/settings/#Agreements'); + }); + + test('_handleNewAgreementsRoute', () => { + element._handleNewAgreementsRoute({params: {}}); assert.isTrue(setParamsStub.calledOnce); assert.equal(setParamsStub.lastCall.args[0].view, Gerrit.Nav.View.AGREEMENTS);
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html index 95cddab..7b279c7 100644 --- a/polygerrit-ui/app/elements/gr-app.html +++ b/polygerrit-ui/app/elements/gr-app.html
@@ -179,7 +179,7 @@ </gr-endpoint-decorator> </template> <template is="dom-if" if="[[_showCLAView]]" restamp="true"> - <gr-cla-view path="[[_path]]"></gr-cla-view> + <gr-cla-view></gr-cla-view> </template> <div id="errorView" class="errorView"> <div class="errorEmoji">[[_lastError.emoji]]</div>
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html index c665df4..307a2b4 100644 --- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html +++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
@@ -53,8 +53,7 @@ </template> </tbody> </table> - <!-- TODO: Renable this when supported in polygerrit --> - <!-- <a href$="[[getUrl()]]">New Contributor Agreement</a> --> + <a href$="[[getUrl()]]">New Contributor Agreement</a> </div> <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> </template>
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html index b667d66..a1f5dc5 100644 --- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html +++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
@@ -1,5 +1,5 @@ <!-- -Copyright (C) 2017 The Android Open Source Project +Copyright (C) 2018 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. @@ -14,12 +14,94 @@ limitations under the License. --> +<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html"> +<link rel="import" href="../../../bower_components/iron-input/iron-input.html"> <link rel="import" href="../../../bower_components/polymer/polymer.html"> -<link rel="import" href="../../shared/gr-placeholder/gr-placeholder.html"> +<link rel="import" href="../../../styles/gr-form-styles.html"> +<link rel="import" href="../../../styles/shared-styles.html"> +<link rel="import" href="../../shared/gr-button/gr-button.html"> +<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> <dom-module id="gr-cla-view"> <template> - <gr-placeholder title="Agreements" path="[[path]]"></gr-placeholder> + <style include="shared-styles"> + h1 { + margin-bottom: .6em; + } + h3 { + margin-bottom: .5em; + } + .agreementsUrl { + border: 0.1em solid #b0bdcc; + margin-bottom: 1.25em; + margin-left: 1.25em; + margin-right: 1.25em; + padding: 0.3em; + } + #claNewAgreementsLabel { + font-family: var(--font-family-bold); + } + #claNewAgreement { + display: none; + } + #claNewAgreement.show { + display: block; + } + .contributorAgreementButton { + font-family: var(--font-family-bold); + } + .contributorAgreementAlreadySubmitted { + color: red; + margin: 0 2em; + padding: .5em; + } + .agreementsSubmitted, + .hideAgreementsTextBox { + display: none; + } + main { + margin: 2em auto; + max-width: 50em; + } + </style> + <style include="gr-form-styles"></style> + <main> + <h1>New Contributor Agreement</h1> + <h3>Select an agreement type:</h3> + <template is="dom-repeat" items="[[_serverConfig.auth.contributor_agreements]]"> + <span class="contributorAgreementButton"> + <input id$="claNewAgreementsInput[[item.name]]" + name="claNewAgreementsRadio" + type="radio" + data-name$="[[item.name]]" + data-url$="[[item.url]]" + on-tap="_handleShowAgreement" + disabled$="[[_disableAggreements(item, _groups)]]"> + <label id="claNewAgreementsLabel">[[item.name]]</label> + </span> + <div class$="contributorAgreementAlreadySubmitted [[_hideAggreements(item, _groups)]]"> + Agreement already submitted. + </div> + <div class="agreementsUrl"> + [[item.description]] + </div> + </template> + <div id="claNewAgreement" class$="[[_computeShowAgreementsClass(_showAgreements)]]"> + <h3 class="smallHeading">Review the agreement:</h3> + <div id="agreementsUrl" class="agreementsUrl"> + <a href$="[[_agreementsUrl]]" target="blank" rel="noopener"> + Please review the agreement.</a> + </div> + <div class$="agreementsTextBox [[_computeHideAgreementClass(_agreementName, _serverConfig.auth.contributor_agreements)]]"> + <h3 class="smallHeading">Complete the agreement:</h3> + <input id="input-agreements" is="iron-input" bind-value="{{_agreementsText}}" placeholder="Enter 'I agree' here" /> + <gr-button on-tap="_handleSaveAgreements" disabled="[[_disableAgreementsText(_agreementsText)]]"> + Submit + </gr-button> + </div> + </div> + </main> + <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> </template> <script src="gr-cla-view.js"></script> -</dom-module> +</dom-module> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js index 71dc71b..39400c64 100644 --- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js +++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
@@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Android Open Source Project +// Copyright (C) 2018 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. @@ -18,7 +18,121 @@ is: 'gr-cla-view', properties: { - path: String, + _groups: Object, + /** @type {?} */ + _serverConfig: Object, + _agreementsText: String, + _agreementName: String, + _showAgreements: { + type: Boolean, + value: false, + }, + _agreementsUrl: String, + }, + + behaviors: [ + Gerrit.BaseUrlBehavior, + ], + + attached() { + this.loadData(); + + this.fire('title-change', {title: 'New Contributor Agreement'}); + }, + + loadData() { + const promises = []; + promises.push(this.$.restAPI.getConfig(true).then(config => { + this._serverConfig = config; + })); + + promises.push(this.$.restAPI.getAccountGroups().then(groups => { + this._groups = groups.sort((a, b) => { + return a.name.localeCompare(b.name); + }); + })); + + return Promise.all(promises); + }, + + _getAgreementsUrl(configUrl) { + let url; + if (!configUrl) { return ''; } + if (configUrl.startsWith('http:') || configUrl.startsWith('https:')) { + url = configUrl; + } else { + url = this.getBaseUrl() + '/' + configUrl; + } + + return url; + }, + + _handleShowAgreement(e) { + this._agreementName = e.target.getAttribute('data-name'); + this._agreementsUrl = + this._getAgreementsUrl(e.target.getAttribute('data-url')); + this._showAgreements = true; + }, + + _handleSaveAgreements(e) { + this._createToast('Agreement saving...'); + + const name = this._agreementName; + return this.$.restAPI.saveAccountAgreement({name}).then(res => { + let message = 'Agreement failed to be submitted, please try again'; + if (res.status === 200) { + message = 'Agreement has been successfully submited.'; + } + this._createToast(message); + this.loadData(); + this._agreementsText = ''; + this._showAgreements = false; + }); + }, + + _createToast(message) { + this.dispatchEvent(new CustomEvent('show-alert', + {detail: {message}, bubbles: true})); + }, + + _computeShowAgreementsClass(agreements) { + return agreements ? 'show' : ''; + }, + + _disableAggreements(item, groups) { + for (const value of groups) { + if (item && item.auto_verify_group && + item.auto_verify_group.name === value.name) { + return true; + } + } + + return false; + }, + + _hideAggreements(item, groups) { + return this._disableAggreements(item, groups) ? + '' : 'agreementsSubmitted'; + }, + + _disableAgreementsText(text) { + return text.toLowerCase() === 'i agree' ? false : true; + }, + + // This checks for auto_verify_group, + // if specified it returns 'hideAgreementsTextBox' which + // then hides the text box and submit button. + _computeHideAgreementClass(name, config) { + for (const key in config) { + if (!config.hasOwnProperty(key)) { return; } + for (const prop in config[key]) { + if (!config[key].hasOwnProperty(prop)) { return; } + if (name === config[key].name && + !config[key].auto_verify_group) { + return 'hideAgreementsTextBox'; + } + } + } }, }); })();
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html new file mode 100644 index 0000000..985fbfa --- /dev/null +++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
@@ -0,0 +1,183 @@ +<!DOCTYPE html> +<!-- +Copyright (C) 2018 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. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-cla-view</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> +<link rel="import" href="../../../test/common-test-setup.html"/> +<link rel="import" href="gr-cla-view.html"> + +<script>void(0);</script> + +<test-fixture id="basic"> + <template> + <gr-cla-view></gr-cla-view> + </template> +</test-fixture> + +<script> + suite('gr-cla-view tests', () => { + let element; + let agreements; + const auth = { + name: 'Individual', + description: 'test-description', + url: 'static/cla_individual.html', + auto_verify_group: { + url: '#/admin/groups/uuid-bc53f2738ef8ad0b3a4f53846ff59b05822caecb', + options: { + visible_to_all: true, + }, + group_id: 20, + owner: 'CLA Accepted - Individual', + owner_id: 'bc53f2738ef8ad0b3a4f53846ff59b05822caecb', + created_on: '2017-07-31 15:11:04.000000000', + id: 'bc53f2738ef8ad0b3a4f53846ff59b05822caecb', + name: 'CLA Accepted - Individual', + }, + }; + const auth2 = { + name: 'Individual2', + description: 'test-description2', + url: 'static/cla_individual2.html', + auto_verify_group: { + url: '#/admin/groups/uuid-e9aaddc47f305be7661ad4db9b66f9b707bd19a0', + options: {}, + group_id: 21, + owner: 'CLA Accepted - Individual2', + owner_id: 'e9aaddc47f305be7661ad4db9b66f9b707bd19a0', + created_on: '2017-07-31 15:25:42.000000000', + id: 'e9aaddc47f305be7661ad4db9b66f9b707bd19a0', + name: 'CLA Accepted - Individual2', + }, + }; + const config = { + auth: { + use_contributor_agreements: true, + contributor_agreements: [ + { + name: 'Individual', + description: 'test-description', + url: 'static/cla_individual.html', + }, + ], + }, + }; + const config2 = { + auth: { + use_contributor_agreements: true, + contributor_agreements: [ + { + name: 'Individual2', + description: 'test-description2', + url: 'static/cla_individual2.html', + }, + ], + }, + }; + const groups = [ + { + url: 'some url', + options: {}, + description: 'Group 1 description', + group_id: 1, + owner: 'Administrators', + owner_id: '123', + id: 'abc', + name: 'Individual', + }, + { + options: {visible_to_all: true}, + id: '456', + group_id: 2, + name: 'Individual 2', + }, + { + options: {visible_to_all: true}, + id: '457', + group_id: 3, + name: 'CLA Accepted - Individual', + }, + ]; + + setup(done => { + agreements = [{ + url: 'test-agreements.html', + description: 'Agreements 1 description', + name: 'Agreements 1', + }]; + + stub('gr-rest-api-interface', { + getAccountGroups() { return Promise.resolve(agreements); }, + }); + + element = fixture('basic'); + + element.loadData().then(() => { flush(done); }); + }); + + test('_disableAggreements equals true', () => { + assert.isTrue(element._disableAggreements(auth, groups)); + }); + + test('_disableAggreements equals false', () => { + assert.isFalse(element._disableAggreements(auth2, groups)); + }); + + test('_hideAggreements equals string', () => { + assert.equal(element._hideAggreements(auth, groups), ''); + }); + + test('_hideAggreements equals agreementsSubmitted', () => { + assert.equal(element._hideAggreements(auth2, groups), + 'agreementsSubmitted'); + }); + + test('_disableAgreementsText equals true', () => { + assert.isFalse(element._disableAgreementsText('I AGREE')); + }); + + test('_disableAgreementsText equals true', () => { + assert.isTrue(element._disableAgreementsText('I DO NOT AGREE')); + }); + + test('_computeHideAgreementClass returns true', () => { + assert.equal( + element._computeHideAgreementClass( + auth.name, config.auth.contributor_agreements), + 'hideAgreementsTextBox'); + }); + + test('_computeHideAgreementClass returns undefined', () => { + assert.isUndefined( + element._computeHideAgreementClass( + auth.name, config2.auth.contributor_agreements)); + }); + + test('_getAgreementsUrl has http', () => { + assert.equal(element._getAgreementsUrl( + 'http://test.org/test.html'), 'http://test.org/test.html'); + }); + + test('_getAgreementsUrl does not have http://', () => { + assert.equal(element._getAgreementsUrl( + 'test_cla.html'), '/test_cla.html'); + }); + }); +</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html deleted file mode 100644 index 15f44cf..0000000 --- a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html +++ /dev/null
@@ -1,55 +0,0 @@ -<!-- -Copyright (C) 2017 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="../../../behaviors/base-url-behavior/base-url-behavior.html"> -<link rel="import" href="../../../bower_components/polymer/polymer.html"> -<link rel="import" href="../../../styles/shared-styles.html"> - -<dom-module id="gr-placeholder"> - <template> - <style include="shared-styles"> - main { - margin: 2em auto; - max-width: 46em; - } - h1 { - margin-bottom: .1em; - } - @media only screen and (max-width: 67em) { - main { - margin: 2em 0 2em 15em; - } - } - @media only screen and (max-width: 53em) { - .loading { - padding: 0 var(--default-horizontal-margin); - } - main { - margin: 2em 1em; - } - } - </style> - <main> - <h1>[[title]]</h1> - <section> - This page is not yet implemented in PolyGerrit. View it in the - <a id="gwtLink" href$="[[computeGwtUrl(path)]]" rel="external"> - Old UI</a> - </section> - </main> - </template> - <script src="gr-placeholder.js"></script> -</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js deleted file mode 100644 index 9b60061..0000000 --- a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright (C) 2017 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() { - 'use strict'; - - Polymer({ - is: 'gr-placeholder', - - properties: { - path: String, - title: String, - }, - - behaviors: [ - Gerrit.BaseUrlBehavior, - ], - }); -})();
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js index a3d3d90..b5bb35a 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -230,8 +230,12 @@ return JSON.parse(source.substring(JSON_PREFIX.length)); }, - getConfig() { - return this._fetchSharedCacheURL('/config/server/info'); + getConfig(noCache) { + if (!noCache) { + return this._fetchSharedCacheURL('/config/server/info'); + } + + return this.fetchJSON('/config/server/info'); }, getRepo(repo) { @@ -675,13 +679,17 @@ }, getAccountGroups() { - return this._fetchSharedCacheURL('/accounts/self/groups'); + return this.fetchJSON('/accounts/self/groups'); }, getAccountAgreements() { return this._fetchSharedCacheURL('/accounts/self/agreements'); }, + saveAccountAgreement(name) { + return this.send('PUT', '/accounts/self/agreements', name); + }, + /** * @param {string=} opt_params */
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index a21890c..44a9296 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html
@@ -119,6 +119,7 @@ 'plugins/gr-settings-api/gr-settings-api_test.html', 'settings/gr-account-info/gr-account-info_test.html', 'settings/gr-change-table-editor/gr-change-table-editor_test.html', + 'settings/gr-cla-view/gr-cla-view_test.html', 'settings/gr-edit-preferences/gr-edit-preferences_test.html', 'settings/gr-email-editor/gr-email-editor_test.html', 'settings/gr-group-list/gr-group-list_test.html',