Migrate UI to Polymer 3 Change-Id: I36b794caa40e76669c0cf2720206af75f8080737
diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.eslintignore
diff --git a/.eslintrc.json b/.eslintrc.json index 3a9c4b9..b586e29 100644 --- a/.eslintrc.json +++ b/.eslintrc.json
@@ -1,79 +1,166 @@ { - "extends": ["eslint:recommended", "google"], - "parserOptions": { - "ecmaVersion": 8 - }, - "env": { - "browser": true, - "es6": true - }, - "globals": { - "__dirname": false, - "app": false, - "page": false, - "Polymer": false, - "process": false, - "require": false, - "Gerrit": false, - "Promise": false, - "assert": false, - "test": false, - "flushAsynchronousOperations": false - }, - "rules": { - "arrow-parens": ["error", "as-needed"], - "block-spacing": ["error", "always"], - "brace-style": ["error", "1tbs", { "allowSingleLine": true }], - "camelcase": "off", - "comma-dangle": ["error", "always-multiline"], - "eol-last": "off", - "indent": "off", - "indent-legacy": ["error", 2, { - "MemberExpression": 2, - "FunctionDeclaration": {"body": 1, "parameters": 2}, - "FunctionExpression": {"body": 1, "parameters": 2}, - "CallExpression": {"arguments": 2}, - "ArrayExpression": 1, - "ObjectExpression": 1, - "SwitchCase": 1 - }], - "keyword-spacing": ["error", { "after": true, "before": true }], - "max-len": [ - "error", - 80, - 2, - {"ignoreComments": true} - ], - "new-cap": ["error", { "capIsNewExceptions": ["Polymer"] }], - "no-console": "off", - "no-restricted-syntax": [ - "error", - { - "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']", - "message": "Remove test.only." - }, - { - "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']", - "message": "Remove suite.only." - } - ], - "no-undef": "off", - "no-useless-escape": "off", - "no-var": "error", - "object-shorthand": ["error", "always"], - "prefer-arrow-callback": "error", - "prefer-const": "error", - "prefer-spread": "error", - "quote-props": ["error", "consistent-as-needed"], - "require-jsdoc": "off", - "semi": [2, "always"], - "template-curly-spacing": "error", - "valid-jsdoc": "off" - }, - "plugins": [ - "html" + "extends": ["eslint:recommended", "google"], + "parserOptions": { + "ecmaVersion": 8, + "sourceType": "module" + }, + "env": { + "browser": true, + "es6": true + }, + "globals": { + "__dirname": false, + "app": false, + "page": false, + "Polymer": false, + "process": false, + "require": false, + "Gerrit": false, + "Promise": false, + "assert": false, + "test": false, + "flushAsynchronousOperations": false + }, + "rules": { + "arrow-parens": ["error", "as-needed"], + "block-spacing": ["error", "always"], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "camelcase": "off", + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never" + }], + "eol-last": "off", + "indent": ["error", 2, { + "MemberExpression": 2, + "FunctionDeclaration": {"body": 1, "parameters": 2}, + "FunctionExpression": {"body": 1, "parameters": 2}, + "CallExpression": {"arguments": 2 }, + "ArrayExpression": 1, + "ObjectExpression": 1, + "SwitchCase": 1 + }], + "keyword-spacing": ["error", { "after": true, "before": true }], + "lines-between-class-members": ["error", "always"], + "max-len": [ + "error", + 80, + 2, + { + "ignoreComments": true, + "ignorePattern": "^import .*;$" + } ], - "settings": { - "html/report-bad-indent": "error" + "new-cap": ["error", { "capIsNewExceptions": ["Polymer", "LegacyElementMixin", "GestureEventListeners", "LegacyDataMixin"] }], + "no-console": "off", + "no-multiple-empty-lines": [ "error", { "max": 1 } ], + "no-prototype-builtins": "off", + "no-redeclare": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']", + "message": "Remove test.only." + }, + { + "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']", + "message": "Remove suite.only." + } + ], + "no-undef": "off", + "no-useless-escape": "off", + "no-var": "error", + "object-shorthand": ["error", "always"], + "padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": "class", + "next": "*" + }, + { + "blankLine": "always", + "prev": "*", + "next": "class" + } + ], + "prefer-arrow-callback": "error", + "prefer-const": "error", + "prefer-spread": "error", + "quote-props": ["error", "consistent-as-needed"], + "require-jsdoc": "off", + "semi": [2, "always"], + "template-curly-spacing": "error", + "valid-jsdoc": "off", + "require-jsdoc": 0, + "valid-jsdoc": 0, + "jsdoc/check-alignment": 2, + "jsdoc/check-examples": 0, + "jsdoc/check-indentation": 0, + "jsdoc/check-param-names": 0, + "jsdoc/check-syntax": 0, + "jsdoc/check-tag-names": 0, + "jsdoc/check-types": 0, + "jsdoc/implements-on-classes": 2, + "jsdoc/match-description": 0, + "jsdoc/newline-after-description": 2, + "jsdoc/no-types": 0, + "jsdoc/no-undefined-types": 0, + "jsdoc/require-description": 0, + "jsdoc/require-description-complete-sentence": 0, + "jsdoc/require-example": 0, + "jsdoc/require-hyphen-before-param-description": 0, + "jsdoc/require-jsdoc": 0, + "jsdoc/require-param": 0, + "jsdoc/require-param-description": 0, + "jsdoc/require-param-name": 2, + "jsdoc/require-param-type": 2, + "jsdoc/require-returns": 0, + "jsdoc/require-returns-check": 0, + "jsdoc/require-returns-description": 0, + "jsdoc/require-returns-type": 2, + "jsdoc/valid-types": 2, + "jsdoc/require-file-overview": ["error", { + "tags": { + "license": { + "mustExist": true, + "preventDuplicates": true + } + } + }], + "import/named": 2, + "import/no-unresolved": 2, + "import/no-self-import": 2, + // The no-cycle rule is slow, because it doesn't cache dependencies. + // Disable it. + "import/no-cycle": 0, + "import/no-useless-path-segments": 2, + "import/no-unused-modules": 2, + "import/no-default-export": 2 + }, + "plugins": [ + "html", + "jsdoc", + "import" + ], + "settings": { + "html/report-bad-indent": "error" + }, + "overrides": [ + { + "files": ["*_html.js", "*-styles.js"], + "rules": { + "max-len": "off" + } + }, + { + "files": ["*.html"], + "rules": { + "jsdoc/require-file-overview": "off" + } } - } + ] +}
diff --git a/BUILD b/BUILD index fcad0e5..3f1cc7a 100644 --- a/BUILD +++ b/BUILD
@@ -1,3 +1,7 @@ +load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") +load("//tools/bzl:js.bzl", "polygerrit_plugin") +load("//tools/bzl:genrule2.bzl", "genrule2") +load("//tools/js:eslint.bzl", "eslint") load("//tools/bzl:junit.bzl", "junit_tests") load("//tools/bzl:plugin.bzl", "PLUGIN_DEPS", "PLUGIN_TEST_DEPS", "gerrit_plugin") @@ -11,6 +15,7 @@ "Gerrit-SshModule: com.googlesource.gerrit.plugins.serviceuser.SshModule", ], resources = glob(["src/main/resources/**/*"]), + resource_jars = [":gr-serviceuser-static"], ) junit_tests( @@ -24,3 +29,53 @@ ":serviceuser__plugin", ], ) +genrule2( + name = "gr-serviceuser-static", + srcs = [":gr-serviceuser"], + outs = ["gr-serviceuser-static.jar"], + cmd = " && ".join([ + "mkdir $$TMP/static", + "cp -r $(locations :gr-serviceuser) $$TMP/static", + "cd $$TMP", + "zip -Drq $$ROOT/$@ -g .", + ]), +) + +rollup_bundle( + name = "serviceuser-bundle", + srcs = glob(["gr-serviceuser/*.js"]), + entry_point = "gr-serviceuser/gr-serviceuser.js", + rollup_bin = "//tools/node_tools:rollup-bin", + sourcemap = "hidden", + format = 'iife', + deps = [ + "@tools_npm//rollup-plugin-node-resolve", + ], +) + +polygerrit_plugin( + name = "gr-serviceuser", + app = "serviceuser-bundle.js", + plugin_name = "serviceuser", +) + +# Define the eslinter for the plugin +# The eslint macro creates 2 rules: lint_test and lint_bin +eslint( + name = "lint", + srcs = glob([ + "gr-serviceuser/**/*.js", + ]), + config = ".eslintrc.json", + data = [], + extensions = [ + ".js", + ], + ignore = ".eslintignore", + plugins = [ + "@npm//eslint-config-google", + "@npm//eslint-plugin-html", + "@npm//eslint-plugin-import", + "@npm//eslint-plugin-jsdoc", + ], +)
diff --git a/gr-serviceuser/gr-serviceuser-create.js b/gr-serviceuser/gr-serviceuser-create.js new file mode 100644 index 0000000..4ca98d9 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser-create.js
@@ -0,0 +1,171 @@ +/** + * @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. + */ + +import {htmlTemplate} from './gr-serviceuser-create_html.js'; + +export class GrServiceUserCreate extends Polymer.GestureEventListeners( + Polymer.Element) { + /** @returns {?} template for this component */ + static get template() { return htmlTemplate; } + + /** @returns {string} name of the component */ + static get is() { return 'gr-serviceuser-create'; } + + /** + * Defines properties of the component + * + * @returns {?} + */ + static get properties() { + return { + _infoMessageEnabled: { + type: Boolean, + value: false, + }, + _infoMessage: String, + _successMessageEnabled: { + type: Boolean, + value: false, + }, + _successMessage: String, + _newUsername: String, + _emailEnabled: { + type: Boolean, + value: false, + }, + _newEmail: String, + _newKey: String, + _dataValid: { + type: Boolean, + value: false, + }, + _isAdding: { + type: Boolean, + value: false, + }, + _enableButton: { + type: Boolean, + value: false, + }, + _accountId: String, + }; + } + + connectedCallback() { + super.connectedCallback(); + this._getConfig(); + } + + _forwardToDetails() { + page.show( + this.plugin.screenUrl() + + '/user/' + + this._accountId); + } + + _getConfig() { + return this.plugin.restApi('/config/server/serviceuser~config/').get('') + .then(config => { + if (!config) { + return; + } + + if (config.info && config.info != '') { + this._infoMessageEnabled = true; + this._infoMessage = config.info; + this.$.infoMessage.innerHTML = this._infoMessage; + } + + if (config.on_success && config.on_success != '') { + this._successMessageEnabled = true; + this._successMessage = config.on_success; + this.$.successMessage.innerHTML = this._successMessage; + } + + this._emailEnabled = config.allow_email; + }); + } + + _validateData() { + this._dataValid = this._validateName(this._newUsername) + && this._validateEmail(this._newEmail) + && this._validateKey(this._newKey); + this._computeButtonEnabled(); + } + + _validateName(username) { + if (username && username.trim().length > 0) { + return true; + } + + return false; + } + + _validateEmail(email) { + if (!email || email.trim().length == 0 || email.includes('@')) { + return true; + } + + return false; + } + + _validateKey(key) { + if (!key || !key.trim()) { + return false; + } + + return true; + } + + _computeButtonEnabled() { + this._enableButton = this._dataValid && !this._isAdding; + } + + _handleCreateServiceUser() { + this._isAdding = true; + this._computeButtonEnabled(); + const body = { + ssh_key: this._newKey.trim(), + email: this._newEmail ? this._newEmail.trim() : null, + }; + return this.plugin.restApi('/a/config/server/serviceuser~serviceusers/') + .post(this._newUsername, body) + .then(response => { + this._accountId = response._account_id; + if (this._successMessage) { + this.$.successDialogOverlay.open(); + } else { + this._forwardToDetails(); + } + }).catch(response => { + this.dispatchEvent( + new CustomEvent( + 'show-error', + { + detail: {message: response}, + bubbles: true, + composed: true, + } + ) + ); + this._isAdding = false; + this._computeButtonEnabled(); + }); + } +} + +customElements.define(GrServiceUserCreate.is, GrServiceUserCreate);
diff --git a/src/main/resources/static/gr-serviceuser-create.html b/gr-serviceuser/gr-serviceuser-create_html.js similarity index 75% rename from src/main/resources/static/gr-serviceuser-create.html rename to gr-serviceuser/gr-serviceuser-create_html.js index 2e70471..6cb9626 100644 --- a/src/main/resources/static/gr-serviceuser-create.html +++ b/gr-serviceuser/gr-serviceuser-create_html.js
@@ -1,22 +1,21 @@ -<!-- -@license -Copyright (C) 2019 The Android Open Source Project +/** + * @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. + */ -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="gr-serviceuser-create"> - <template> +export const htmlTemplate = Polymer.html` <style include="shared-styles"></style> <style include="gr-subpage-styles"></style> <style include="gr-form-styles"></style> @@ -88,6 +87,4 @@ </gr-dialog> </gr-overlay> </main> - </template> - <script src="gr-serviceuser-create.js"></script> -</dom-module> +`;
diff --git a/gr-serviceuser/gr-serviceuser-detail.js b/gr-serviceuser/gr-serviceuser-detail.js new file mode 100644 index 0000000..fa8fd32 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser-detail.js
@@ -0,0 +1,373 @@ +/** + * @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. + */ + +import './gr-serviceuser-ssh-panel.js'; +import './gr-serviceuser-http-password.js'; +import {htmlTemplate} from './gr-serviceuser-detail_html.js'; + +const NOT_FOUND_MESSAGE = 'Not Found'; + +export class GrServiceUserDetail extends Polymer.GestureEventListeners( + Polymer.Element) { + /** @returns {?} template for this component */ + static get template() { return htmlTemplate; } + + /** @returns {string} name of the component */ + static get is() { return 'gr-serviceuser-detail'; } + + /** + * Defines properties of the component + * + * @returns {?} + */ + static get properties() { + return { + _restApi: Object, + _serviceUserId: String, + _serviceUser: Object, + _loading: { + type: Boolean, + value: true, + }, + _statusButtonText: { + type: String, + value: 'Activate', + }, + _prefsChanged: { + type: Boolean, + value: false, + }, + _changingPrefs: { + type: Boolean, + value: false, + }, + _isAdmin: { + type: Boolean, + value: false, + }, + _allowEmail: { + type: Boolean, + value: false, + }, + _allowOwner: { + type: Boolean, + value: false, + }, + _allowHttpPassword: { + type: Boolean, + value: false, + }, + _newFullName: String, + _newEmail: String, + _availableOwners: Array, + _newOwner: String, + _ownerChangeWarning: String, + _query: { + type: Function, + value() { + return this._getGroupSuggestions.bind(this); + }, + }, + }; + } + + static get behaviors() { + return [ + Gerrit.ListViewBehavior, + ]; + } + + connectedCallback() { + super.connectedCallback(); + this._extractUserId(); + this._loadServiceUser(); + } + + _loadServiceUser() { + if (!this._serviceUserId) { return; } + + const promises = []; + + promises.push(this._getPluginConfig()); + promises.push(this._getServiceUser()); + + Promise.all(promises).then(() => { + this.$.sshEditor.loadData(this._restApi, this._serviceUser); + this.$.httpPass.loadData(this._restApi, this._serviceUser); + + this.dispatchEvent( + new CustomEvent( + 'title-change', + { + detail: {title: this._serviceUser.name}, + bubbles: true, + composed: true, + } + ) + ); + this._computeStatusButtonText(); + this._loading = false; + this._newFullName = this._serviceUser.name; + this._newEmail = this._serviceUser.email; + }); + } + + _computeLoadingClass(loading) { + return loading ? 'loading' : ''; + } + + _extractUserId() { + this._serviceUserId = this.baseURI.split('/').pop(); + } + + _getPermissions() { + return this.plugin.restApi().getAccountCapabilities(['administrateServer']) + .then(capabilities => { + this._isAdmin = capabilities && capabilities.administrateServer; + }); + } + + _getPluginConfig() { + return Promise.resolve(this._getPermissions()).then(() => { + this.plugin.restApi('/config/server/serviceuser~config/').get('') + .then(config => { + if (!config) { + return; + } + this._allowEmail = config.allow_email || this._isAdmin; + this._allowOwner = config.allow_owner || this._isAdmin; + this._allowHttpPassword = config.allow_http_password + || this._isAdmin; + }); + }); + } + + _getServiceUser() { + this._restApi = this.plugin.restApi( + '/a/config/server/serviceuser~serviceusers/'); + return this._restApi.get(this._serviceUserId) + .then(serviceUser => { + if (!serviceUser) { + this._serviceUser = {}; + return; + } + this._serviceUser = serviceUser; + }); + } + + _active(serviceUser) { + if (!serviceUser) { + return NOT_FOUND_MESSAGE; + } + + return serviceUser.inactive === true ? 'Inactive' : 'Active'; + } + + _computeStatusButtonText() { + if (!this._serviceUser) { + return; + } + + this._statusButtonText = this._serviceUser.inactive === true + ? 'Activate' + : 'Deactivate'; + } + + _toggleStatus() { + if (this._serviceUser.inactive === true) { + this._restApi.put(`${this._serviceUser._account_id}/active`) + .then(() => { + this._loadServiceUser(); + }); + } else { + this._restApi.delete(`${this._serviceUser._account_id}/active`) + .then(() => { + this._loadServiceUser(); + }); + } + } + + _getCreator(serviceUser) { + if (!serviceUser || !serviceUser.created_by) { + return NOT_FOUND_MESSAGE; + } + + if (serviceUser.created_by.username != undefined) { + return serviceUser.created_by.username; + } + + if (serviceUser.created_by._account_id != -1) { + return serviceUser.created_by._account_id; + } + + return NOT_FOUND_MESSAGE; + } + + _getOwnerGroup(serviceUser) { + return serviceUser && serviceUser.owner + ? serviceUser.owner.name + : NOT_FOUND_MESSAGE; + } + + _isEmailValid(email) { + if (!email) { + return false; + } + return email.includes('@'); + } + + _getGroupSuggestions(input) { + let query; + if (!input || input === this._getOwnerGroup(this._serviceUser)) { + query = ''; + } else { + query = `?suggest=${input}`; + } + + return this.plugin.restApi('/a/groups/').get(query) + .then(response => { + const groups = []; + for (const key in response) { + if (!response.hasOwnProperty(key)) { continue; } + groups.push({ + name: key, + value: decodeURIComponent(response[key].id), + }); + } + this._availableOwners = groups; + return groups; + }); + } + + _isOwnerValid(owner) { + if (!owner) { + return false; + } + + return this._getOwnerName(owner); + } + + _isNewOwner() { + return this._getOwnerName(this._newOwner) + === this._getOwnerGroup(this._serviceUser); + } + + _getOwnerName(id) { + return this._availableOwners.find(o => { return o.value === id; }).name; + } + + _computeOwnerWarning() { + let message = 'If '; + message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE + ? 'the owner group is changed' : 'an owner group is set'; + message += ' only members of the '; + message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE + ? 'new ' : ''; + message += 'owner group can see and administrate the service user.'; + message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE + ? '' : ' The creator of the service user can no' + + ' longer see and administrate the service user if she/he' + + ' is not member of the owner group.'; + this._ownerChangeWarning = message; + } + + _computePrefsChanged() { + if (this.loading || this._changingPrefs) { + return; + } + + if (!this._newOwner && !this._newEmail && !this._newFullName) { + this._prefsChanged = false; + return; + } + + if (this._newEmail && !this._isEmailValid(this._newEmail)) { + this._prefsChanged = false; + return; + } + + if (this._newOwner + && (this._isNewOwner() || !this._isOwnerValid(this._newOwner))) { + this._prefsChanged = false; + return; + } + + if (this._newOwner) { + this._computeOwnerWarning(); + } + + this._prefsChanged = true; + } + + _applyNewFullName() { + return this._restApi + .put(`${this._serviceUser._account_id}/name`, + {name: this._newFullName}) + .then(() => { + this.$.serviceUserFullNameInput.value = ''; + }); + } + + _applyNewEmail(email) { + if (!this._isEmailValid(email)) { + return; + } + return this._restApi + .put(`${this._serviceUser._account_id}/email`, {email}) + .then(() => { + this.$.serviceUserEmailInput.value = ''; + }); + } + + _applyNewOwner(owner) { + if (this._isNewOwner() || !this._isOwnerValid(this._newOwner)) { + return; + } + return this._restApi + .put(`${this._serviceUser._account_id}/owner`, {group: owner}) + .then(() => { + this.$.serviceUserOwnerInput.text = this._getOwnerGroup( + this._serviceUser); + }); + } + + _handleSavePreferences() { + const promises = []; + this._changingPrefs = true; + + if (this._newFullName) { + promises.push(this._applyNewFullName()); + } + + if (this._newEmail) { + promises.push(this._applyNewEmail(this._newEmail)); + } + + if (this._newOwner) { + promises.push(this._applyNewOwner(this._newOwner)); + } + + Promise.all(promises).then(() => { + this._changingPrefs = false; + this._prefsChanged = false; + this._ownerChangeWarning = ''; + this._loadServiceUser(); + }); + } +} + +customElements.define(GrServiceUserDetail.is, GrServiceUserDetail);
diff --git a/src/main/resources/static/gr-serviceuser-detail.html b/gr-serviceuser/gr-serviceuser-detail_html.js similarity index 86% rename from src/main/resources/static/gr-serviceuser-detail.html rename to gr-serviceuser/gr-serviceuser-detail_html.js index 41e4e8d..a6f2408 100644 --- a/src/main/resources/static/gr-serviceuser-detail.html +++ b/gr-serviceuser/gr-serviceuser-detail_html.js
@@ -1,25 +1,21 @@ -<!-- -@license -Copyright (C) 2019 The Android Open Source Project +/** + * @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. + */ -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="./gr-serviceuser-ssh-panel.html"> -<link rel="import" href="./gr-serviceuser-http-password.html"> - -<dom-module id="gr-serviceuser-detail"> - <template> +export const htmlTemplate = Polymer.html` <style include="shared-styles"></style> <style include="gr-subpage-styles"></style> <style include="gr-form-styles"></style> @@ -164,6 +160,4 @@ </div> </main> </div> - </template> - <script src="gr-serviceuser-detail.js"></script> -</dom-module> +`;
diff --git a/gr-serviceuser/gr-serviceuser-http-password.js b/gr-serviceuser/gr-serviceuser-http-password.js new file mode 100644 index 0000000..396eef7 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser-http-password.js
@@ -0,0 +1,71 @@ +/** + * @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. + */ + +import {htmlTemplate} from './gr-serviceuser-http-password_html.js'; + +class GrServiceUserHttpPassword extends Polymer.GestureEventListeners( + Polymer.Element) { + /** @returns {?} template for this component */ + static get template() { return htmlTemplate; } + + /** @returns {string} name of the component */ + static get is() { return 'gr-serviceuser-http-password'; } + + /** + * Defines properties of the component + * + * @returns {?} + */ + static get properties() { + return { + _restApi: Object, + _serviceUser: Object, + _generatedPassword: String, + _passwordUrl: String, + }; + } + + loadData(restApi, serviceUser) { + this._restApi = restApi; + this._serviceUser = serviceUser; + } + + _handleGenerateTap() { + this._generatedPassword = 'Generating...'; + this.$.generatedPasswordOverlay.open(); + this._restApi + .put(`${this._serviceUser._account_id}/password.http`, + {generate: true}) + .then(newPassword => { + this._generatedPassword = newPassword; + }); + } + + _closeOverlay() { + this.$.generatedPasswordOverlay.close(); + } + + _generatedPasswordOverlayClosed() { + this._generatedPassword = ''; + } + + _handleDelete() { + this._restApi.delete(`${this._serviceUser._account_id}/password.http`); + } +} + +customElements.define(GrServiceUserHttpPassword.is, GrServiceUserHttpPassword);
diff --git a/src/main/resources/static/gr-serviceuser-http-password.html b/gr-serviceuser/gr-serviceuser-http-password_html.js similarity index 69% rename from src/main/resources/static/gr-serviceuser-http-password.html rename to gr-serviceuser/gr-serviceuser-http-password_html.js index 8cebbe5..b48855c 100644 --- a/src/main/resources/static/gr-serviceuser-http-password.html +++ b/gr-serviceuser/gr-serviceuser-http-password_html.js
@@ -1,22 +1,21 @@ -<!-- -@license -Copyright (C) 2019 The Android Open Source Project +/** + * @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. + */ -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="gr-serviceuser-http-password"> - <template> +export const htmlTemplate = Polymer.html` <style include="shared-styles"> .password { font-family: var(--monospace-font-family); @@ -76,6 +75,4 @@ on-click="_closeOverlay">Close</gr-button> </div> </gr-overlay> - </template> - <script src="gr-serviceuser-http-password.js"></script> -</dom-module> +`;
diff --git a/gr-serviceuser/gr-serviceuser-list.js b/gr-serviceuser/gr-serviceuser-list.js new file mode 100644 index 0000000..a3c40c0 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser-list.js
@@ -0,0 +1,119 @@ +/** + * @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. + */ + +import {htmlTemplate} from './gr-serviceuser-list_html.js'; + +const NOT_FOUND_MESSAGE = 'Not Found'; + +export class GrServiceUserList extends Polymer.GestureEventListeners( + Polymer.Element) { + /** @returns {?} template for this component */ + static get template() { return htmlTemplate; } + + /** @returns {string} name of the component */ + static get is() { return 'gr-serviceuser-list'; } + + /** + * Defines properties of the component + * + * @returns {?} + */ + static get properties() { + return { + _serviceUsers: Array, + _loading: { + type: Boolean, + value: true, + }, + }; + } + + static get behaviors() { + return [ + Gerrit.ListViewBehavior, + ]; + } + + connectedCallback() { + super.connectedCallback(); + this.dispatchEvent( + new CustomEvent( + 'title-change', + {detail: {title: 'Service Users'}, bubbles: true, composed: true})); + this._getServiceUsers(); + } + + _getServiceUsers() { + return this.plugin.restApi('/a/config/server/serviceuser~serviceusers/') + .get('') + .then(serviceUsers => { + if (!serviceUsers) { + this._serviceUsers = []; + return; + } + this._serviceUsers = Object.keys(serviceUsers) + .map(key => { + const serviceUser = serviceUsers[key]; + serviceUser.username = key; + return serviceUser; + }); + this._loading = false; + }); + } + + _computeLoadingClass(loading) { + return loading ? 'loading' : ''; + } + + _active(item) { + if (!item) { + return NOT_FOUND_MESSAGE; + } + + return item.inactive === true ? 'Inactive' : 'Active'; + } + + _getCreator(item) { + if (!item || !item.created_by) { + return NOT_FOUND_MESSAGE; + } + + if (item.created_by.username != undefined) { + return item.created_by.username; + } + + if (item.created_by._account_id != -1) { + return item.created_by._account_id; + } + + return NOT_FOUND_MESSAGE; + } + + _getOwnerGroup(item) { + return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE; + } + + _computeServiceUserUrl(id) { + return `${this.plugin.screenUrl()}/user/${id}`; + } + + _createNewServiceUser() { + page.show(this.plugin.screenUrl() + '/create'); + } +} + +customElements.define(GrServiceUserList.is, GrServiceUserList);
diff --git a/src/main/resources/static/gr-serviceuser-list.html b/gr-serviceuser/gr-serviceuser-list_html.js similarity index 68% rename from src/main/resources/static/gr-serviceuser-list.html rename to gr-serviceuser/gr-serviceuser-list_html.js index e927d63..6bd9ecc 100644 --- a/src/main/resources/static/gr-serviceuser-list.html +++ b/gr-serviceuser/gr-serviceuser-list_html.js
@@ -1,22 +1,21 @@ -<!-- -@license -Copyright (C) 2019 The Android Open Source Project +/** + * @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. + */ -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="gr-serviceuser-list"> - <template> +export const htmlTemplate = Polymer.html` <style include="shared-styles"></style> <style include="gr-table-styles"></style> <style> @@ -59,10 +58,10 @@ <th class="accountState topHeader">Account State</th> </tr> <tr id="loading" - class$="loadingMsg [[computeLoadingClass(_loading)]]"> + class$="loadingMsg [[_computeLoadingClass(_loading)]]"> <td>Loading...</td> </tr> - <tbody class$="[[computeLoadingClass(_loading)]]"> + <tbody class$="[[_computeLoadingClass(_loading)]]"> <template is="dom-repeat" items="[[_serviceUsers]]"> <tr class="table"> @@ -79,6 +78,4 @@ </template> </tbody> </table> - </template> - <script src="gr-serviceuser-list.js"></script> -</dom-module> +`;
diff --git a/gr-serviceuser/gr-serviceuser-ssh-panel.js b/gr-serviceuser/gr-serviceuser-ssh-panel.js new file mode 100644 index 0000000..a2db9c0 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser-ssh-panel.js
@@ -0,0 +1,113 @@ +/** + * @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. + */ + +import {htmlTemplate} from './gr-serviceuser-ssh-panel_html.js'; + +class GrServiceUserSshPanel extends Polymer.GestureEventListeners( + Polymer.Element) { + /** @returns {?} template for this component */ + static get template() { return htmlTemplate; } + + /** @returns {string} name of the component */ + static get is() { return 'gr-serviceuser-ssh-panel'; } + + /** + * Defines properties of the component + * + * @returns {?} + */ + static get properties() { + return { + _restApi: Object, + _serviceUser: Object, + _keys: Array, + /** @type {?} */ + _keyToView: Object, + _newKey: { + type: String, + value: '', + }, + _keysToRemove: { + type: Array, + value() { return []; }, + }, + }; + } + + loadData(restApi, serviceUser) { + this._restApi = restApi; + this._serviceUser = serviceUser; + return this._restApi.get(`${this._serviceUser._account_id}/sshkeys`) + .then(keys => { + if (!keys) { + this._keys = []; + return; + } + this._keys = keys; + }); + } + + _getStatusLabel(isValid) { + return isValid ? 'Valid' : 'Invalid'; + } + + _showKey(e) { + const el = Polymer.dom(e).localTarget; + const index = parseInt(el.getAttribute('data-index'), 10); + this._keyToView = this._keys[index]; + this.$.viewKeyOverlay.open(); + } + + _closeOverlay() { + this.$.viewKeyOverlay.close(); + } + + _handleDeleteKey(e) { + const el = Polymer.dom(e).localTarget; + const index = parseInt(el.getAttribute('data-index'), 10); + this.push('_keysToRemove', this._keys[index]); + + const promises = this._keysToRemove.map(key => { + this._restApi.delete( + `${this._serviceUser._account_id}/sshkeys/${key.seq}`); + }); + + return Promise.all(promises).then(() => { + this.splice('_keys', index, 1); + this._keysToRemove = []; + }); + } + + _handleAddKey() { + this.$.addButton.disabled = true; + this.$.newKey.disabled = true; + return this._restApi.post(`${this._serviceUser._account_id}/sshkeys`, + this._newKey.trim(), null, 'plain/text') + .then(key => { + this.push('_keys', key); + }).catch(() => { + this.$.addButton.disabled = false; + this.$.newKey.disabled = false; + }); + } + + _computeAddButtonDisabled(newKey) { + return !newKey.length; + } +} + +customElements.define(GrServiceUserSshPanel.is, GrServiceUserSshPanel);
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.html b/gr-serviceuser/gr-serviceuser-ssh-panel_html.js similarity index 81% rename from src/main/resources/static/gr-serviceuser-ssh-panel.html rename to gr-serviceuser/gr-serviceuser-ssh-panel_html.js index 2906a10..a9fb7d9 100644 --- a/src/main/resources/static/gr-serviceuser-ssh-panel.html +++ b/gr-serviceuser/gr-serviceuser-ssh-panel_html.js
@@ -1,22 +1,21 @@ -<!-- -@license -Copyright (C) 2019 The Android Open Source Project +/** + * @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. + */ -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="gr-serviceuser-ssh-panel"> - <template> +export const htmlTemplate = Polymer.html` <style include="shared-styles"></style> <style include="gr-form-styles"> .statusHeader { @@ -127,6 +126,4 @@ </fieldset> </div> <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> - </template> - <script src="gr-serviceuser-ssh-panel.js"></script> -</dom-module> +`;
diff --git a/gr-serviceuser/gr-serviceuser.js b/gr-serviceuser/gr-serviceuser.js new file mode 100644 index 0000000..c6624a6 --- /dev/null +++ b/gr-serviceuser/gr-serviceuser.js
@@ -0,0 +1,41 @@ +/** + * @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. + */ + +import {GrServiceUserList} from './gr-serviceuser-list.js'; +import {GrServiceUserDetail} from './gr-serviceuser-detail.js'; +import {GrServiceUserCreate} from './gr-serviceuser-create.js'; + +Gerrit.install(plugin => { + plugin.restApi() + .getAccountCapabilities([ + 'administrateServer', + 'serviceuser-createServiceUser']) + .then(capabilities => { + if (capabilities + && (capabilities.administrateServer + || capabilities['serviceuser-createServiceUser'])) { + plugin.screen('list', GrServiceUserList.is); + plugin.screen('user', GrServiceUserDetail.is); + plugin.screen('create', GrServiceUserCreate.is); + } + plugin.admin() + .addMenuLink( + 'Service Users', + '/x/serviceuser/list', + 'serviceuser-createServiceUser'); + }); +});
diff --git a/package.json b/package.json new file mode 100644 index 0000000..aa7780e --- /dev/null +++ b/package.json
@@ -0,0 +1,13 @@ +{ + "name": "serviceuser", + "description": "Serviceuser plugin", + "browser": true, + "scripts": { + "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin", + "eslint": "npm run safe_bazelisk test :lint_test", + "eslintfix": "npm run safe_bazelisk run :lint_bin -- -- --fix $(pwd)" + }, + "devDependencies": {}, + "license": "Apache-2.0", + "private": true +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java index a99987b..650eba6 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
@@ -23,6 +23,6 @@ @Override protected void configure() { DynamicSet.bind(binder(), WebUiPlugin.class) - .toInstance(new JavaScriptPlugin("gr-serviceuser.html")); + .toInstance(new JavaScriptPlugin("serviceuser.js")); } }
diff --git a/src/main/resources/static/gr-serviceuser-create.js b/src/main/resources/static/gr-serviceuser-create.js deleted file mode 100644 index df5d9a6..0000000 --- a/src/main/resources/static/gr-serviceuser-create.js +++ /dev/null
@@ -1,150 +0,0 @@ -/** - * @license - * Copyright (C) 2016 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-serviceuser-create', - _legacyUndefinedCheck: true, - - properties: { - _infoMessageEnabled: { - type: Boolean, - value: false, - }, - _infoMessage: String, - _successMessageEnabled: { - type: Boolean, - value: false, - }, - _successMessage: String, - _newUsername: String, - _emailEnabled: { - type: Boolean, - value: false, - }, - _newEmail: String, - _newKey: String, - _dataValid: { - type: Boolean, - value: false, - }, - _isAdding: { - type: Boolean, - value: false, - }, - _enableButton: { - type: Boolean, - value: false, - }, - _accountId: String, - }, - - attached() { - this._getConfig(); - }, - - _forwardToDetails() { - page.show( - this.plugin.screenUrl() - + '/user/' - + this._accountId); - }, - - _getConfig() { - return this.plugin.restApi('/config/server/serviceuser~config/').get('') - .then(config => { - if (!config) { - return; - } - - if (config.info && config.info != '') { - this._infoMessageEnabled = true; - this._infoMessage = config.info; - this.$.infoMessage.innerHTML = this._infoMessage; - } - - if (config.on_success && config.on_success != '') { - this._successMessageEnabled = true; - this._successMessage = config.on_success; - this.$.successMessage.innerHTML = this._successMessage; - } - - this._emailEnabled = config.allow_email; - }); - }, - - _validateData() { - this._dataValid = this._validateName(this._newUsername) - && this._validateEmail(this._newEmail) - && this._validateKey(this._newKey); - this._computeButtonEnabled(); - }, - - _validateName(username) { - if (username && username.trim().length > 0) { - return true; - } - - return false; - }, - - _validateEmail(email) { - if (!email || email.trim().length == 0 || email.includes('@')) { - return true; - } - - return false; - }, - - _validateKey(key) { - if (!key || !key.trim()) { - return false; - } - - return true; - }, - - _computeButtonEnabled() { - this._enableButton = this._dataValid && !this._isAdding; - }, - - _handleCreateServiceUser() { - this._isAdding = true; - this._computeButtonEnabled(); - const body = { - ssh_key: this._newKey.trim(), - email: this._newEmail ? this._newEmail.trim() : null, - }; - return this.plugin.restApi('/config/server/serviceuser~serviceusers/') - .post(this._newUsername, body) - .then(response => { - this._accountId = response._account_id; - if (this._successMessage) { - this.$.successDialogOverlay.open(); - } else { - this._forwardToDetails(); - } - }).catch(response => { - this.fire('show-error', {message: response}); - this._isAdding = false; - this._computeButtonEnabled(); - }); - }, - }); -})();
diff --git a/src/main/resources/static/gr-serviceuser-detail.js b/src/main/resources/static/gr-serviceuser-detail.js deleted file mode 100644 index 95aded2..0000000 --- a/src/main/resources/static/gr-serviceuser-detail.js +++ /dev/null
@@ -1,345 +0,0 @@ -// 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() { - 'use strict'; - - const NOT_FOUND_MESSAGE = 'Not Found'; - - Polymer({ - is: 'gr-serviceuser-detail', - _legacyUndefinedCheck: true, - - properties: { - _restApi: Object, - _serviceUserId: String, - _serviceUser: Object, - _loading: { - type: Boolean, - value: true, - }, - _statusButtonText: { - type: String, - value: 'Activate', - }, - _prefsChanged: { - type: Boolean, - value: false, - }, - _changingPrefs: { - type: Boolean, - value: false, - }, - _isAdmin: { - type: Boolean, - value: false, - }, - _allowEmail: { - type: Boolean, - value: false, - }, - _allowOwner: { - type: Boolean, - value: false, - }, - _allowHttpPassword: { - type: Boolean, - value: false, - }, - _newFullName: String, - _newEmail: String, - _availableOwners: Array, - _newOwner: String, - _ownerChangeWarning: String, - _query: { - type: Function, - value() { - return this._getGroupSuggestions.bind(this); - }, - }, - }, - - behaviors: [ - Gerrit.ListViewBehavior, - ], - - attached() { - this._extractUserId(); - this._loadServiceUser(); - }, - - _loadServiceUser() { - if (!this._serviceUserId) { return; } - - const promises = []; - - promises.push(this._getPluginConfig()); - promises.push(this._getServiceUser()); - - Promise.all(promises).then(() => { - this.$.sshEditor.loadData(this._restApi, this._serviceUser); - this.$.httpPass.loadData(this._restApi, this._serviceUser); - - this.fire('title-change', {title: this._serviceUser.name}); - this._computeStatusButtonText(); - this._loading = false; - this._newFullName = this._serviceUser.name; - this._newEmail = this._serviceUser.email; - }); - }, - - _computeLoadingClass(loading) { - return loading ? 'loading' : ''; - }, - - _extractUserId() { - this._serviceUserId = this.baseURI.split('/').pop(); - }, - - _getPermissions() { - return this.plugin.restApi('/accounts/self/capabilities/').get('') - .then(capabilities => { - this._isAdmin = capabilities && capabilities.administrateServer; - }); - }, - - _getPluginConfig() { - return Promise.resolve(this._getPermissions()).then(() => { - this.plugin.restApi('/config/server/serviceuser~config/').get('') - .then(config => { - if (!config) { - return; - } - this._allowEmail = config.allow_email || this._isAdmin; - this._allowOwner = config.allow_owner || this._isAdmin; - this._allowHttpPassword = config.allow_http_password - || this._isAdmin; - }); - }); - }, - - _getServiceUser() { - this._restApi = this.plugin.restApi( - '/config/server/serviceuser~serviceusers/'); - return this._restApi.get(this._serviceUserId) - .then(serviceUser => { - if (!serviceUser) { - this._serviceUser = {}; - return; - } - this._serviceUser = serviceUser; - }); - }, - - _active(serviceUser) { - if (!serviceUser) { - return NOT_FOUND_MESSAGE; - } - - return serviceUser.inactive === true ? 'Inactive' : 'Active'; - }, - - _computeStatusButtonText() { - if (!this._serviceUser) { - return; - } - - this._statusButtonText = this._serviceUser.inactive === true - ? 'Activate' - : 'Deactivate'; - }, - - _toggleStatus() { - if (this._serviceUser.inactive === true) { - this._restApi.put(`${this._serviceUser._account_id}/active`) - .then(() => { - this._loadServiceUser(); - }); - } else { - this._restApi.delete(`${this._serviceUser._account_id}/active`) - .then(() => { - this._loadServiceUser(); - }); - } - }, - - _getCreator(serviceUser) { - if (!serviceUser || !serviceUser.created_by) { - return NOT_FOUND_MESSAGE; - } - - if (serviceUser.created_by.username != undefined) { - return serviceUser.created_by.username; - } - - if (serviceUser.created_by._account_id != -1) { - return serviceUser.created_by._account_id; - } - - return NOT_FOUND_MESSAGE; - }, - - _getOwnerGroup(serviceUser) { - return serviceUser && serviceUser.owner - ? serviceUser.owner.name - : NOT_FOUND_MESSAGE; - }, - - _isEmailValid(email) { - if (!email) { - return false; - } - return email.includes('@'); - }, - - _getGroupSuggestions(input) { - let query; - if (!input || input === this._getOwnerGroup(this._serviceUser)) { - query = ''; - } else { - query = `?suggest=${input}`; - } - - return this.plugin.restApi('/a/groups/').get(query) - .then(response => { - const groups = []; - for (const key in response) { - if (!response.hasOwnProperty(key)) { continue; } - groups.push({ - name: key, - value: decodeURIComponent(response[key].id), - }); - } - this._availableOwners = groups; - return groups; - }); - }, - - _isOwnerValid(owner) { - if (!owner) { - return false; - } - - return this._getOwnerName(owner); - }, - - _isNewOwner() { - return this._getOwnerName(this._newOwner) - === this._getOwnerGroup(this._serviceUser); - }, - - _getOwnerName(id) { - return this._availableOwners.find(o => { return o.value === id; }).name; - }, - - _computeOwnerWarning() { - let message = 'If '; - message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE - ? 'the owner group is changed' : 'an owner group is set'; - message += ' only members of the '; - message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE - ? 'new ' : ''; - message += 'owner group can see and administrate the service user.'; - message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE - ? '' : ' The creator of the service user can no' - + ' longer see and administrate the service user if she/he' - + ' is not member of the owner group.'; - this._ownerChangeWarning = message; - }, - - _computePrefsChanged() { - if (this.loading || this._changingPrefs) { - return; - } - - if (!this._newOwner && !this._newEmail && !this._newFullName) { - this._prefsChanged = false; - return; - } - - if (this._newEmail && !this._isEmailValid(this._newEmail)) { - this._prefsChanged = false; - return; - } - - if (this._newOwner - && (this._isNewOwner() || !this._isOwnerValid(this._newOwner))) { - this._prefsChanged = false; - return; - } - - if (this._newOwner) { - this._computeOwnerWarning(); - } - - this._prefsChanged = true; - }, - - _applyNewFullName() { - return this._restApi - .put(`${this._serviceUser._account_id}/name`, - {name: this._newFullName}) - .then(() => { - this.$.serviceUserFullNameInput.value = ''; - }); - }, - - _applyNewEmail(email) { - if (!this._isEmailValid(email)) { - return; - } - return this._restApi - .put(`${this._serviceUser._account_id}/email`, {email}) - .then(() => { - this.$.serviceUserEmailInput.value = ''; - }); - }, - - _applyNewOwner(owner) { - if (this._isNewOwner() || !this._isOwnerValid(this._newOwner)) { - return; - } - return this._restApi - .put(`${this._serviceUser._account_id}/owner`, {group: owner}) - .then(() => { - this.$.serviceUserOwnerInput.text = this._getOwnerGroup( - this._serviceUser); - }); - }, - - _handleSavePreferences() { - const promises = []; - this._changingPrefs = true; - - if (this._newFullName) { - promises.push(this._applyNewFullName()); - } - - if (this._newEmail) { - promises.push(this._applyNewEmail(this._newEmail)); - } - - if (this._newOwner) { - promises.push(this._applyNewOwner(this._newOwner)); - } - - Promise.all(promises).then(() => { - this._changingPrefs = false; - this._prefsChanged = false; - this._ownerChangeWarning = ''; - this._loadServiceUser(); - }); - }, - }); -})();
diff --git a/src/main/resources/static/gr-serviceuser-http-password.js b/src/main/resources/static/gr-serviceuser-http-password.js deleted file mode 100644 index 87e1543..0000000 --- a/src/main/resources/static/gr-serviceuser-http-password.js +++ /dev/null
@@ -1,60 +0,0 @@ -/** - * @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. - */ - -(function() { - 'use strict'; - - Polymer({ - is: 'gr-serviceuser-http-password', - _legacyUndefinedCheck: true, - - properties: { - _restApi: Object, - _serviceUser: Object, - _generatedPassword: String, - _passwordUrl: String, - }, - - loadData(restApi, serviceUser) { - this._restApi = restApi; - this._serviceUser = serviceUser; - }, - - _handleGenerateTap() { - this._generatedPassword = 'Generating...'; - this.$.generatedPasswordOverlay.open(); - this._restApi - .put(`${this._serviceUser._account_id}/password.http`, - {generate: true}) - .then(newPassword => { - this._generatedPassword = newPassword; - }); - }, - - _closeOverlay() { - this.$.generatedPasswordOverlay.close(); - }, - - _generatedPasswordOverlayClosed() { - this._generatedPassword = ''; - }, - - _handleDelete() { - this._restApi.delete(`${this._serviceUser._account_id}/password.http`); - }, - }); -})();
diff --git a/src/main/resources/static/gr-serviceuser-list.js b/src/main/resources/static/gr-serviceuser-list.js deleted file mode 100644 index 8f35dd4..0000000 --- a/src/main/resources/static/gr-serviceuser-list.js +++ /dev/null
@@ -1,95 +0,0 @@ -// 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() { - 'use strict'; - - const NOT_FOUND_MESSAGE = 'Not Found'; - - Polymer({ - is: 'gr-serviceuser-list', - _legacyUndefinedCheck: true, - - properties: { - _serviceUsers: Array, - _loading: { - type: Boolean, - value: true, - }, - }, - - behaviors: [ - Gerrit.ListViewBehavior, - ], - - attached() { - this.fire('title-change', {title: 'Service Users'}); - this._getServiceUsers(); - }, - - _getServiceUsers() { - return this.plugin.restApi('/config/server/serviceuser~serviceusers/') - .get('') - .then(serviceUsers => { - if (!serviceUsers) { - this._serviceUsers = []; - return; - } - this._serviceUsers = Object.keys(serviceUsers) - .map(key => { - const serviceUser = serviceUsers[key]; - serviceUser.username = key; - return serviceUser; - }); - this._loading = false; - }); - }, - - _active(item) { - if (!item) { - return NOT_FOUND_MESSAGE; - } - - return item.inactive === true ? 'Inactive' : 'Active'; - }, - - _getCreator(item) { - if (!item || !item.created_by) { - return NOT_FOUND_MESSAGE; - } - - if (item.created_by.username != undefined) { - return item.created_by.username; - } - - if (item.created_by._account_id != -1) { - return item.created_by._account_id; - } - - return NOT_FOUND_MESSAGE; - }, - - _getOwnerGroup(item) { - return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE; - }, - - _computeServiceUserUrl(id) { - return `${this.plugin.screenUrl()}/user/${id}`; - }, - - _createNewServiceUser() { - page.show(this.plugin.screenUrl() + '/create'); - }, - }); -})();
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.js b/src/main/resources/static/gr-serviceuser-ssh-panel.js deleted file mode 100644 index e1e8dd0..0000000 --- a/src/main/resources/static/gr-serviceuser-ssh-panel.js +++ /dev/null
@@ -1,101 +0,0 @@ -/** - * @license - * Copyright (C) 2016 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-serviceuser-ssh-panel', - _legacyUndefinedCheck: true, - - properties: { - _restApi: Object, - _serviceUser: Object, - _keys: Array, - /** @type {?} */ - _keyToView: Object, - _newKey: { - type: String, - value: '', - }, - _keysToRemove: { - type: Array, - value() { return []; }, - }, - }, - - loadData(restApi, serviceUser) { - this._restApi = restApi; - this._serviceUser = serviceUser; - return this._restApi.get(`${this._serviceUser._account_id}/sshkeys`) - .then(keys => { - if (!keys) { - this._keys = []; - return; - } - this._keys = keys; - }); - }, - - _getStatusLabel(isValid) { - return isValid ? 'Valid' : 'Invalid'; - }, - - _showKey(e) { - const el = Polymer.dom(e).localTarget; - const index = parseInt(el.getAttribute('data-index'), 10); - this._keyToView = this._keys[index]; - this.$.viewKeyOverlay.open(); - }, - - _closeOverlay() { - this.$.viewKeyOverlay.close(); - }, - - _handleDeleteKey(e) { - const el = Polymer.dom(e).localTarget; - const index = parseInt(el.getAttribute('data-index'), 10); - this.push('_keysToRemove', this._keys[index]); - - const promises = this._keysToRemove.map(key => { - this._restApi.delete(`${this._serviceUser._account_id}/sshkeys/${key.seq}`); - }); - - return Promise.all(promises).then(() => { - this.splice('_keys', index, 1); - this._keysToRemove = []; - }); - }, - - _handleAddKey() { - this.$.addButton.disabled = true; - this.$.newKey.disabled = true; - return this._restApi.post(`${this._serviceUser._account_id}/sshkeys`, - this._newKey.trim(), null, 'plain/text') - .then(key => { - this.push('_keys', key); - }).catch(() => { - this.$.addButton.disabled = false; - this.$.newKey.disabled = false; - }); - }, - - _computeAddButtonDisabled(newKey) { - return !newKey.length; - }, - }); -})();
diff --git a/src/main/resources/static/gr-serviceuser.html b/src/main/resources/static/gr-serviceuser.html deleted file mode 100644 index 92714a2..0000000 --- a/src/main/resources/static/gr-serviceuser.html +++ /dev/null
@@ -1,46 +0,0 @@ -<!-- -@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="./gr-serviceuser-list.html"> -<link rel="import" - href="./gr-serviceuser-detail.html"> -<link rel="import" - href="./gr-serviceuser-create.html"> - -<dom-module id="gr-serviceuser"> - <script> - Gerrit.install(plugin => { - plugin.restApi('/accounts/self/capabilities/').get('') - .then(capabilities => { - if (capabilities - && (capabilities.administrateServer - || capabilities['serviceuser-createServiceUser'])) { - plugin.screen('list', 'gr-serviceuser-list'); - plugin.screen('user', 'gr-serviceuser-detail'); - plugin.screen('create', 'gr-serviceuser-create'); - } - plugin.admin() - .addMenuLink( - 'Service Users', - '/x/serviceuser/list', - 'serviceuser-createServiceUser'); - }); - }); - </script> -</dom-module>