| /** |
| * @license |
| * 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. |
| */ |
| import '../../../scripts/bundled-polymer.js'; |
| import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js'; |
| import '../../../styles/gr-form-styles.js'; |
| import '../../../styles/gr-subpage-styles.js'; |
| import '../../../styles/gr-table-styles.js'; |
| import '../../../styles/shared-styles.js'; |
| import '../../shared/gr-account-link/gr-account-link.js'; |
| import '../../shared/gr-autocomplete/gr-autocomplete.js'; |
| import '../../shared/gr-button/gr-button.js'; |
| import '../../shared/gr-overlay/gr-overlay.js'; |
| import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; |
| import '../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js'; |
| import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js'; |
| import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; |
| import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; |
| import {PolymerElement} from '@polymer/polymer/polymer-element.js'; |
| import {htmlTemplate} from './gr-group-members_html.js'; |
| import {BaseUrlBehavior} from '../../../behaviors/base-url-behavior/base-url-behavior.js'; |
| import {URLEncodingBehavior} from '../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.js'; |
| |
| const SUGGESTIONS_LIMIT = 15; |
| const SAVING_ERROR_TEXT = 'Group may not exist, or you may not have '+ |
| 'permission to add it'; |
| |
| const URL_REGEX = '^(?:[a-z]+:)?//'; |
| |
| /** |
| * @extends Polymer.Element |
| */ |
| class GrGroupMembers extends mixinBehaviors( [ |
| BaseUrlBehavior, |
| URLEncodingBehavior, |
| ], GestureEventListeners( |
| LegacyElementMixin( |
| PolymerElement))) { |
| static get template() { return htmlTemplate; } |
| |
| static get is() { return 'gr-group-members'; } |
| |
| static get properties() { |
| return { |
| groupId: Number, |
| _groupMemberSearchId: String, |
| _groupMemberSearchName: String, |
| _includedGroupSearchId: String, |
| _includedGroupSearchName: String, |
| _loading: { |
| type: Boolean, |
| value: true, |
| }, |
| _groupName: String, |
| _groupMembers: Object, |
| _includedGroups: Object, |
| _itemName: String, |
| _itemType: String, |
| _queryMembers: { |
| type: Function, |
| value() { |
| return this._getAccountSuggestions.bind(this); |
| }, |
| }, |
| _queryIncludedGroup: { |
| type: Function, |
| value() { |
| return this._getGroupSuggestions.bind(this); |
| }, |
| }, |
| _groupOwner: { |
| type: Boolean, |
| value: false, |
| }, |
| _isAdmin: { |
| type: Boolean, |
| value: false, |
| }, |
| }; |
| } |
| |
| /** @override */ |
| attached() { |
| super.attached(); |
| this._loadGroupDetails(); |
| |
| this.dispatchEvent(new CustomEvent('title-change', { |
| detail: {title: 'Members'}, |
| composed: true, bubbles: true, |
| })); |
| } |
| |
| _loadGroupDetails() { |
| if (!this.groupId) { return; } |
| |
| const promises = []; |
| |
| const errFn = response => { |
| this.dispatchEvent(new CustomEvent('page-error', { |
| detail: {response}, |
| composed: true, bubbles: true, |
| })); |
| }; |
| |
| return this.$.restAPI.getGroupConfig(this.groupId, errFn) |
| .then(config => { |
| if (!config || !config.name) { return Promise.resolve(); } |
| |
| this._groupName = config.name; |
| |
| promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => { |
| this._isAdmin = isAdmin ? true : false; |
| })); |
| |
| promises.push(this.$.restAPI.getIsGroupOwner(config.name) |
| .then(isOwner => { |
| this._groupOwner = isOwner ? true : false; |
| })); |
| |
| promises.push(this.$.restAPI.getGroupMembers(config.name).then( |
| members => { |
| this._groupMembers = members; |
| })); |
| |
| promises.push(this.$.restAPI.getIncludedGroup(config.name) |
| .then(includedGroup => { |
| this._includedGroups = includedGroup; |
| })); |
| |
| return Promise.all(promises).then(() => { |
| this._loading = false; |
| }); |
| }); |
| } |
| |
| _computeLoadingClass(loading) { |
| return loading ? 'loading' : ''; |
| } |
| |
| _isLoading() { |
| return this._loading || this._loading === undefined; |
| } |
| |
| _computeGroupUrl(url) { |
| if (!url) { return; } |
| |
| const r = new RegExp(URL_REGEX, 'i'); |
| if (r.test(url)) { |
| return url; |
| } |
| |
| // For GWT compatibility |
| if (url.startsWith('#')) { |
| return this.getBaseUrl() + url.slice(1); |
| } |
| return this.getBaseUrl() + url; |
| } |
| |
| _handleSavingGroupMember() { |
| return this.$.restAPI.saveGroupMembers(this._groupName, |
| this._groupMemberSearchId).then(config => { |
| if (!config) { |
| return; |
| } |
| this.$.restAPI.getGroupMembers(this._groupName).then(members => { |
| this._groupMembers = members; |
| }); |
| this._groupMemberSearchName = ''; |
| this._groupMemberSearchId = ''; |
| }); |
| } |
| |
| _handleDeleteConfirm() { |
| this.$.overlay.close(); |
| if (this._itemType === 'member') { |
| return this.$.restAPI.deleteGroupMembers(this._groupName, |
| this._itemId) |
| .then(itemDeleted => { |
| if (itemDeleted.status === 204) { |
| this.$.restAPI.getGroupMembers(this._groupName) |
| .then(members => { |
| this._groupMembers = members; |
| }); |
| } |
| }); |
| } else if (this._itemType === 'includedGroup') { |
| return this.$.restAPI.deleteIncludedGroup(this._groupName, |
| this._itemId) |
| .then(itemDeleted => { |
| if (itemDeleted.status === 204 || itemDeleted.status === 205) { |
| this.$.restAPI.getIncludedGroup(this._groupName) |
| .then(includedGroup => { |
| this._includedGroups = includedGroup; |
| }); |
| } |
| }); |
| } |
| } |
| |
| _handleConfirmDialogCancel() { |
| this.$.overlay.close(); |
| } |
| |
| _handleDeleteMember(e) { |
| const id = e.model.get('item._account_id'); |
| const name = e.model.get('item.name'); |
| const username = e.model.get('item.username'); |
| const email = e.model.get('item.email'); |
| const item = username || name || email || id; |
| if (!item) { |
| return ''; |
| } |
| this._itemName = item; |
| this._itemId = id; |
| this._itemType = 'member'; |
| this.$.overlay.open(); |
| } |
| |
| _handleSavingIncludedGroups() { |
| return this.$.restAPI.saveIncludedGroup(this._groupName, |
| this._includedGroupSearchId.replace(/\+/g, ' '), err => { |
| if (err.status === 404) { |
| this.dispatchEvent(new CustomEvent('show-alert', { |
| detail: {message: SAVING_ERROR_TEXT}, |
| bubbles: true, |
| composed: true, |
| })); |
| return err; |
| } |
| throw Error(err.statusText); |
| }) |
| .then(config => { |
| if (!config) { |
| return; |
| } |
| this.$.restAPI.getIncludedGroup(this._groupName) |
| .then(includedGroup => { |
| this._includedGroups = includedGroup; |
| }); |
| this._includedGroupSearchName = ''; |
| this._includedGroupSearchId = ''; |
| }); |
| } |
| |
| _handleDeleteIncludedGroup(e) { |
| const id = decodeURIComponent(e.model.get('item.id')).replace(/\+/g, ' '); |
| const name = e.model.get('item.name'); |
| const item = name || id; |
| if (!item) { return ''; } |
| this._itemName = item; |
| this._itemId = id; |
| this._itemType = 'includedGroup'; |
| this.$.overlay.open(); |
| } |
| |
| _getAccountSuggestions(input) { |
| if (input.length === 0) { return Promise.resolve([]); } |
| return this.$.restAPI.getSuggestedAccounts( |
| input, SUGGESTIONS_LIMIT).then(accounts => { |
| const accountSuggestions = []; |
| let nameAndEmail; |
| if (!accounts) { return []; } |
| for (const key in accounts) { |
| if (!accounts.hasOwnProperty(key)) { continue; } |
| if (accounts[key].email !== undefined) { |
| nameAndEmail = accounts[key].name + |
| ' <' + accounts[key].email + '>'; |
| } else { |
| nameAndEmail = accounts[key].name; |
| } |
| accountSuggestions.push({ |
| name: nameAndEmail, |
| value: accounts[key]._account_id, |
| }); |
| } |
| return accountSuggestions; |
| }); |
| } |
| |
| _getGroupSuggestions(input) { |
| return this.$.restAPI.getSuggestedGroups(input) |
| .then(response => { |
| const groups = []; |
| for (const key in response) { |
| if (!response.hasOwnProperty(key)) { continue; } |
| groups.push({ |
| name: key, |
| value: decodeURIComponent(response[key].id), |
| }); |
| } |
| return groups; |
| }); |
| } |
| |
| _computeHideItemClass(owner, admin) { |
| return admin || owner ? '' : 'canModify'; |
| } |
| } |
| |
| customElements.define(GrGroupMembers.is, GrGroupMembers); |