PolyGerrit: Implement support for members tab in groups
Change-Id: I928dbeaa8ee1e652442e618380a4015a6b1230a1
This commit is contained in:
@@ -29,6 +29,7 @@ limitations under the License.
|
||||
<link rel="import" href="../gr-admin-group-list/gr-admin-group-list.html">
|
||||
<link rel="import" href="../gr-admin-project-list/gr-admin-project-list.html">
|
||||
<link rel="import" href="../gr-group/gr-group.html">
|
||||
<link rel="import" href="../gr-group-members/gr-group-members.html">
|
||||
<link rel="import" href="../gr-plugin-list/gr-plugin-list.html">
|
||||
<link rel="import" href="../gr-project/gr-project.html">
|
||||
<link rel="import" href="../gr-group-audit-log/gr-group-audit-log.html">
|
||||
@@ -88,6 +89,12 @@ limitations under the License.
|
||||
on-name-changed="_updateGroupName"></gr-group>
|
||||
</main>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_showGroupMembers]]" restamp="true">
|
||||
<main>
|
||||
<gr-group-members
|
||||
group-id="[[params.groupId]]"></gr-group-members>
|
||||
</main>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_showGroupList]]" restamp="true">
|
||||
<main class="table">
|
||||
<gr-admin-group-list class="table" params="[[params]]">
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
_showGroup: Boolean,
|
||||
_showGroupAuditLog: Boolean,
|
||||
_showGroupList: Boolean,
|
||||
_showGroupMembers: Boolean,
|
||||
_showProjectMain: Boolean,
|
||||
_showProjectList: Boolean,
|
||||
_showProjectDetailList: Boolean,
|
||||
@@ -128,7 +129,15 @@
|
||||
name: this._groupName,
|
||||
view: 'gr-group',
|
||||
url: `/admin/groups/${this.encodeURL(this._groupId + '', true)}`,
|
||||
children: [],
|
||||
children: [
|
||||
{
|
||||
name: 'Members',
|
||||
detailType: 'members',
|
||||
view: 'gr-group-members',
|
||||
url: `/admin/groups/${this.encodeURL(this._groupId, true)}` +
|
||||
',members',
|
||||
},
|
||||
],
|
||||
};
|
||||
if (this._groupOwner) {
|
||||
linkCopy.subsection.children.push(
|
||||
@@ -161,6 +170,7 @@
|
||||
this.set('_showGroup', params.adminView === 'gr-group');
|
||||
this.set('_showGroupAuditLog', params.adminView === 'gr-group-audit-log');
|
||||
this.set('_showGroupList', params.adminView === 'gr-admin-group-list');
|
||||
this.set('_showGroupMembers', params.adminView === 'gr-group-members');
|
||||
this.set('_showProjectMain', params.adminView === 'gr-project');
|
||||
this.set('_showProjectList',
|
||||
params.adminView === 'gr-admin-project-list');
|
||||
@@ -217,8 +227,8 @@
|
||||
this._groupName = group.name;
|
||||
this.reload();
|
||||
this.$.restAPI.getIsGroupOwner(group.name).then(
|
||||
configs => {
|
||||
if (configs.hasOwnProperty(group.name)) {
|
||||
isOwner => {
|
||||
if (isOwner) {
|
||||
this._groupOwner = true;
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
Gerrit.ListViewBehavior,
|
||||
],
|
||||
|
||||
attached() {
|
||||
this.fire('title-change', {title: 'Audit Log'});
|
||||
},
|
||||
|
||||
ready() {
|
||||
this._getAuditLogs();
|
||||
},
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
<!--
|
||||
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="../../../behaviors/gr-url-encoding-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
|
||||
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
|
||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
|
||||
|
||||
<dom-module id="gr-group-members">
|
||||
<template>
|
||||
<style include="gr-form-styles"></style>
|
||||
<style include="shared-styles">
|
||||
main {
|
||||
margin: 2em 1em;
|
||||
}
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
#loading.loading {
|
||||
display: block;
|
||||
}
|
||||
#loading:not(.loading) {
|
||||
display: none;
|
||||
}
|
||||
.input {
|
||||
width: 15em;
|
||||
}
|
||||
gr-autocomplete {
|
||||
width: 20em;
|
||||
--gr-autocomplete: {
|
||||
font-size: 1em;
|
||||
height: 2em;
|
||||
width: 20em;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: var(--default-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
th {
|
||||
border-bottom: 1px solid #eee;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
<style include="gr-form-styles"></style>
|
||||
<main class="gr-form-styles">
|
||||
<div id="loading" class$="[[_computeLoadingClass(_loading)]]">
|
||||
Loading...
|
||||
</div>
|
||||
<div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
|
||||
<h1 id="Title">[[_groupName]]</h1>
|
||||
<div id="form">
|
||||
<fieldset>
|
||||
<h3 id="members">Members</h3>
|
||||
<fieldset>
|
||||
<span class="value">
|
||||
<gr-autocomplete
|
||||
id="groupMemberSearchInput"
|
||||
text="{{_groupMemberSearch}}"
|
||||
query="[[_queryMembers]]"
|
||||
placeholder="Name Or Email"
|
||||
hidden$="[[!_groupOwner]]">
|
||||
</gr-autocomplete>
|
||||
</span>
|
||||
<gr-button
|
||||
id="saveGroupMember"
|
||||
on-tap="_handleSavingGroupMember"
|
||||
disabled="[[!_groupMemberSearch]]"
|
||||
hidden$="[[!_groupOwner]]">
|
||||
Add
|
||||
</gr-button>
|
||||
<div class="gr-form-styles">
|
||||
<table id="groupMembers" class="gr-form-styles">
|
||||
<tr class="headerRow">
|
||||
<th class="nameHeader">Name</th>
|
||||
<th class="emailAddressHeader">Email Address</th>
|
||||
<th class="deleteHeader" hidden$="[[!_groupOwner]]">
|
||||
Delete Member
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<template is="dom-repeat" items="[[_groupMembers]]">
|
||||
<tr>
|
||||
<td class="nameColumn">
|
||||
<a href$="[[_memberUrl(item)]]">[[item.name]]</a>
|
||||
</td>
|
||||
<td>[[item.email]]</td>
|
||||
<td hidden$="[[!_groupOwner]]">
|
||||
<gr-button
|
||||
class="deleteButton"
|
||||
on-tap="_handleDeleteMember">
|
||||
Delete
|
||||
</gr-button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<h3 id="includedGroups">Included Groups</h3>
|
||||
<fieldset>
|
||||
<span class="value">
|
||||
<gr-autocomplete
|
||||
id="includedGroupSearchInput"
|
||||
text="{{_includedGroupSearch}}"
|
||||
query="[[_queryIncludedGroup]]"
|
||||
placeholder="Group Name"
|
||||
hidden$="[[!_groupOwner]]">
|
||||
</gr-autocomplete>
|
||||
</span>
|
||||
<gr-button
|
||||
id="saveIncludedGroups"
|
||||
on-tap="_handleSavingIncludedGroups"
|
||||
disabled="[[!_includedGroupSearch]]"
|
||||
hidden$="[[!_groupOwner]]">
|
||||
Add
|
||||
</gr-button>
|
||||
<div class="gr-form-styles">
|
||||
<table id="includedGroups" class="gr-form-styles">
|
||||
<tr class="headerRow">
|
||||
<th class="groupNameHeader">Group Name</th>
|
||||
<th class="descriptionHeader">Description</th>
|
||||
<th class="deleteIncludedHeader" hidden$="[[!_groupOwner]]">
|
||||
Delete Included Group
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<template is="dom-repeat" items="[[_includedGroups]]">
|
||||
<tr>
|
||||
<td class="nameColumn">
|
||||
<a href$="[[_groupUrl(item.group_id)]]">
|
||||
[[item.name]]
|
||||
</a>
|
||||
</td>
|
||||
<td>[[item.description]]</td>
|
||||
<td hidden$="[[!_groupOwner]]">
|
||||
<gr-button
|
||||
class="deleteIncludedGroupButton"
|
||||
on-tap="_handleDeleteIncludedGroup">
|
||||
Delete
|
||||
</gr-button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<gr-overlay id="overlay" with-backdrop>
|
||||
<gr-confirm-delete-item-dialog
|
||||
class="confirmDialog"
|
||||
on-confirm="_handleDeleteConfirm"
|
||||
on-cancel="_handleConfirmDialogCancel"
|
||||
item="[[_itemName]]"
|
||||
item-type="[[_itemType]]"></gr-confirm-delete-item-dialog>
|
||||
</gr-overlay>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-group-members.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,239 @@
|
||||
// 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';
|
||||
|
||||
const SUGGESTIONS_LIMIT = 15;
|
||||
|
||||
Polymer({
|
||||
is: 'gr-group-members',
|
||||
|
||||
properties: {
|
||||
groupId: Number,
|
||||
_groupMemberSearch: String,
|
||||
_includedGroupSearch: 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,
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.BaseUrlBehavior,
|
||||
Gerrit.URLEncodingBehavior,
|
||||
],
|
||||
|
||||
attached() {
|
||||
this._loadGroupDetails();
|
||||
|
||||
this.fire('title-change', {title: 'Members'});
|
||||
},
|
||||
|
||||
_loadGroupDetails() {
|
||||
if (!this.groupId) { return; }
|
||||
|
||||
const promises = [];
|
||||
|
||||
return this.$.restAPI.getGroupConfig(this.groupId).then(
|
||||
config => {
|
||||
this._groupName = config.name;
|
||||
promises.push(this.$.restAPI.getIsGroupOwner(config.name)
|
||||
.then(isOwner => { this._groupOwner = isOwner; }));
|
||||
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;
|
||||
},
|
||||
|
||||
_memberUrl(item) {
|
||||
if (item.email) {
|
||||
item = item.email;
|
||||
} else if (item.username) {
|
||||
item = item.username;
|
||||
} else {
|
||||
item = item.name;
|
||||
}
|
||||
return this.getBaseUrl() + '/q/owner:' + this.encodeURL(item, true) +
|
||||
' status:open';
|
||||
},
|
||||
|
||||
_groupUrl(item) {
|
||||
return this.getBaseUrl() + '/admin/groups/' + this.encodeURL(item, true);
|
||||
},
|
||||
|
||||
_handleSavingGroupMember() {
|
||||
return this.$.restAPI.saveGroupMembers(this._groupName,
|
||||
this._groupMemberSearch).then(config => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
this.$.restAPI.getGroupMembers(this._groupName).then(members => {
|
||||
this._groupMembers = members;
|
||||
});
|
||||
this._groupMemberSearch = '';
|
||||
});
|
||||
},
|
||||
|
||||
_handleDeleteConfirm() {
|
||||
this.$.overlay.close();
|
||||
if (this._itemType === 'member') {
|
||||
return this.$.restAPI.deleteGroupMembers(this._groupName,
|
||||
this._itemName)
|
||||
.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._itemName)
|
||||
.then(itemDeleted => {
|
||||
if (itemDeleted.status === 204) {
|
||||
this.$.restAPI.getIncludedGroup(this._groupName)
|
||||
.then(includedGroup => {
|
||||
this._includedGroups = includedGroup;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_handleConfirmDialogCancel() {
|
||||
this.$.overlay.close();
|
||||
},
|
||||
|
||||
_handleDeleteMember(e) {
|
||||
let item;
|
||||
const name = e.model.get('item.name');
|
||||
const username = e.model.get('item.username');
|
||||
const email = e.model.get('item.email');
|
||||
if (username) {
|
||||
item = username;
|
||||
} else if (name) {
|
||||
item = name;
|
||||
} else if (email) {
|
||||
item = email;
|
||||
}
|
||||
if (!item) {
|
||||
return '';
|
||||
}
|
||||
this._itemName = item;
|
||||
this._itemType = 'member';
|
||||
this.$.overlay.open();
|
||||
},
|
||||
|
||||
_handleSavingIncludedGroups() {
|
||||
return this.$.restAPI.saveIncludedGroup(this._groupName,
|
||||
this._includedGroupSearch)
|
||||
.then(config => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
this.$.restAPI.getIncludedGroup(this._groupName)
|
||||
.then(includedGroup => {
|
||||
this._includedGroups = includedGroup;
|
||||
});
|
||||
this._includedGroupSearch = '';
|
||||
});
|
||||
},
|
||||
|
||||
_handleDeleteIncludedGroup(e) {
|
||||
const name = e.model.get('item.name');
|
||||
if (!name) {
|
||||
return '';
|
||||
}
|
||||
this._itemName = name;
|
||||
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,
|
||||
});
|
||||
}
|
||||
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: response[key],
|
||||
});
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,156 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-group-members</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-group-members.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-group-members></gr-group-members>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-group-members tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
let groups;
|
||||
let groupMembers;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
groups = {
|
||||
name: 'Administrators',
|
||||
owner: 'Administrators',
|
||||
group_id: 1,
|
||||
};
|
||||
|
||||
groupMembers = [
|
||||
{
|
||||
_account_id: 1000097,
|
||||
name: 'Jane Roe',
|
||||
email: 'jane.roe@example.com',
|
||||
username: 'jane',
|
||||
},
|
||||
{
|
||||
_account_id: 1000096,
|
||||
name: 'Test User',
|
||||
email: 'john.doe@example.com',
|
||||
username: 'john',
|
||||
},
|
||||
{
|
||||
_account_id: 1000095,
|
||||
name: 'Gerrit',
|
||||
email: 'gerrit@example.com',
|
||||
username: 'git',
|
||||
},
|
||||
];
|
||||
|
||||
stub('gr-rest-api-interface', {
|
||||
getSuggestedAccounts(input) {
|
||||
if (input.startsWith('test')) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
_account_id: 1000096,
|
||||
name: 'test-account',
|
||||
email: 'test.account@example.com',
|
||||
username: 'test123',
|
||||
},
|
||||
{
|
||||
_account_id: 1001439,
|
||||
name: 'test-admin',
|
||||
email: 'test.admin@example.com',
|
||||
username: 'test_admin',
|
||||
},
|
||||
{
|
||||
_account_id: 1001439,
|
||||
name: 'test-git',
|
||||
username: 'test_git',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
},
|
||||
getLoggedIn() { return Promise.resolve(true); },
|
||||
getGroupConfig() {
|
||||
return Promise.resolve(groups);
|
||||
},
|
||||
getGroupMembers() {
|
||||
return Promise.resolve(groupMembers);
|
||||
},
|
||||
getIsGroupOwner() {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
});
|
||||
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('save correctly', () => {
|
||||
element._groupOwner = true;
|
||||
|
||||
const memberName = 'test-admin';
|
||||
|
||||
sandbox.stub(element.$.restAPI, 'saveGroupMembers', () => {
|
||||
return Promise.resolve({});
|
||||
});
|
||||
|
||||
const button = Polymer.dom(element.root).querySelector('gr-button');
|
||||
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
|
||||
element.$.groupMemberSearchInput.text = memberName;
|
||||
|
||||
assert.isFalse(button.hasAttribute('disabled'));
|
||||
|
||||
element._handleSavingGroupMember().then(() => {
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
assert.isFalse(element.$.Title.classList.contains('edited'));
|
||||
});
|
||||
});
|
||||
|
||||
test('_getAccountSuggestions empty', done => {
|
||||
element._getAccountSuggestions('nonexistent').then(accounts => {
|
||||
assert.equal(accounts.length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('_getAccountSuggestions non-empty', done => {
|
||||
element._getAccountSuggestions('test-').then(accounts => {
|
||||
assert.equal(accounts.length, 3);
|
||||
assert.equal(accounts[0].name,
|
||||
'test-account <test.account@example.com>');
|
||||
assert.equal(accounts[1].name, 'test-admin <test.admin@example.com>');
|
||||
assert.equal(accounts[2].name, 'test-git');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -71,12 +71,12 @@ limitations under the License.
|
||||
<gr-autocomplete
|
||||
id="groupNameInput"
|
||||
text="{{_groupConfig.name}}"
|
||||
disabled$="[[_groupOwner]]"></gr-autocomplete>
|
||||
disabled="[[!_groupOwner]]"></gr-autocomplete>
|
||||
</span>
|
||||
<gr-button
|
||||
id="inputUpdateNameBtn"
|
||||
on-tap="_handleSaveName"
|
||||
disabled$="[[_computeButtonDisabled(_groupOwner, _rename)]]">
|
||||
disabled="[[_computeButtonDisabled(_groupOwner, _rename)]]">
|
||||
Rename Group</gr-button>
|
||||
</fieldset>
|
||||
<h3 class$="[[_computeHeaderClass(_owner)]]">
|
||||
@@ -87,12 +87,12 @@ limitations under the License.
|
||||
<gr-autocomplete
|
||||
text="{{_groupConfig.owner}}"
|
||||
query="[[_query]]"
|
||||
disabled$="[[_groupOwner]]">
|
||||
disabled$="[[!_groupOwner]]">
|
||||
</gr-autocomplete>
|
||||
</span>
|
||||
<gr-button
|
||||
on-tap="_handleSaveOwner"
|
||||
disabled$="[[_computeButtonDisabled(_groupOwner, _owner)]]">
|
||||
disabled="[[_computeButtonDisabled(_groupOwner, _owner)]]">
|
||||
Change Owners</gr-button>
|
||||
</fieldset>
|
||||
<h3 class$="[[_computeHeaderClass(_description)]]">
|
||||
@@ -104,11 +104,11 @@ limitations under the License.
|
||||
class="description"
|
||||
autocomplete="on"
|
||||
bind-value="{{_groupConfig.description}}"
|
||||
disabled$="[[_groupOwner]]"></iron-autogrow-textarea>
|
||||
disabled="[[!_groupOwner]]"></iron-autogrow-textarea>
|
||||
</div>
|
||||
<gr-button
|
||||
on-tap="_handleSaveDescription"
|
||||
disabled$=
|
||||
disabled=
|
||||
"[[_computeButtonDisabled(_groupOwner, _description)]]">
|
||||
Save Description
|
||||
</gr-button>
|
||||
@@ -124,7 +124,7 @@ limitations under the License.
|
||||
<span class="value">
|
||||
<gr-select
|
||||
bind-value="{{_groupConfig.options.visible_to_all}}">
|
||||
<select disabled$="[[_groupOwner]]">
|
||||
<select disabled$="[[!_groupOwner]]">
|
||||
<template is="dom-repeat" items="[[_submitTypes]]">
|
||||
<option value="[[item.value]]">[[item.label]]</option>
|
||||
</template>
|
||||
@@ -134,7 +134,7 @@ limitations under the License.
|
||||
</section>
|
||||
<gr-button
|
||||
on-tap="_handleSaveOptions"
|
||||
disabled$=
|
||||
disabled=
|
||||
"[[_computeButtonDisabled(_groupOwner, _options)]]">
|
||||
Save Group Options
|
||||
</gr-button>
|
||||
|
||||
@@ -100,14 +100,10 @@
|
||||
config => {
|
||||
this._groupConfig = config;
|
||||
this._groupName = config.name;
|
||||
this.fire('title-change', {title: config.name});
|
||||
this._loading = false;
|
||||
this.$.restAPI.getIsGroupOwner(config.name).then(
|
||||
configs => {
|
||||
if (Object.keys(configs).length === 0 &&
|
||||
configs.constructor === Object) {
|
||||
this._groupOwner = true;
|
||||
}
|
||||
});
|
||||
this.$.restAPI.getIsGroupOwner(config.name)
|
||||
.then(isOwner => { this._groupOwner = isOwner; });
|
||||
});
|
||||
},
|
||||
|
||||
@@ -187,7 +183,7 @@
|
||||
},
|
||||
|
||||
_computeButtonDisabled(options, option) {
|
||||
return options || !option;
|
||||
return !options || !option;
|
||||
},
|
||||
|
||||
_computeHeaderClass(configChanged) {
|
||||
|
||||
@@ -76,9 +76,10 @@ limitations under the License.
|
||||
name: groupName,
|
||||
};
|
||||
element._groupName = groupName;
|
||||
element._groupOwner = true;
|
||||
|
||||
sandbox.stub(element.$.restAPI, 'getIsGroupOwner', () => {
|
||||
return Promise.resolve({is_owner: true});
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
|
||||
sandbox.stub(element.$.restAPI, 'saveGroupName', () => {
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Matches /admin/groups/<group>,audit-log[/]
|
||||
// Matches /admin/groups/<group>,audit-log
|
||||
page(/^\/admin\/groups\/(.+),audit-log$/, loadUser, data => {
|
||||
restAPI.getLoggedIn().then(loggedIn => {
|
||||
if (loggedIn) {
|
||||
@@ -168,6 +168,16 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Matches /admin/groups/<group>,members
|
||||
page(/^\/admin\/groups\/(.+),members$/, loadUser, data => {
|
||||
app.params = {
|
||||
view: Gerrit.Nav.View.ADMIN,
|
||||
adminView: 'gr-group-members',
|
||||
detailType: 'members',
|
||||
groupId: data.params[0],
|
||||
};
|
||||
});
|
||||
|
||||
// Matches /admin/groups[,<offset>][/].
|
||||
page(/^\/admin\/groups(,(\d+))?(\/)?$/, loadUser, data => {
|
||||
restAPI.getLoggedIn().then(loggedIn => {
|
||||
|
||||
@@ -311,9 +311,26 @@
|
||||
revision, opt_errFn, opt_ctx);
|
||||
},
|
||||
|
||||
getIsGroupOwner(groupId) {
|
||||
const encodeId = encodeURIComponent(groupId);
|
||||
return this._fetchSharedCacheURL('/groups/?owned&q=' + encodeId);
|
||||
/**
|
||||
* @param {!string} groupName
|
||||
* @returns {!Promise<boolean>}
|
||||
*/
|
||||
getIsGroupOwner(groupName) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
return this._fetchSharedCacheURL('/groups/?owned&q=' + encodeName)
|
||||
.then(configs => configs.hasOwnProperty(encodeName));
|
||||
},
|
||||
|
||||
getGroupMembers(groupName) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
return this.send('GET', `/groups/${encodeName}/members/`)
|
||||
.then(response => this.getResponseObject(response));
|
||||
},
|
||||
|
||||
getIncludedGroup(groupName) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
return this.send('GET', `/groups/${encodeName}/groups/`)
|
||||
.then(response => this.getResponseObject(response));
|
||||
},
|
||||
|
||||
saveGroupName(groupId, name) {
|
||||
@@ -341,6 +358,35 @@
|
||||
return this._fetchSharedCacheURL('/groups/' + group + '/log.audit');
|
||||
},
|
||||
|
||||
saveGroupMembers(groupName, groupMembers) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
const encodeMember = encodeURIComponent(groupMembers);
|
||||
return this.send('PUT', `/groups/${encodeName}/members/${encodeMember}`)
|
||||
.then(response => this.getResponseObject(response));
|
||||
},
|
||||
|
||||
saveIncludedGroup(groupName, includedGroup) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
const encodeIncludedGroup = encodeURIComponent(includedGroup);
|
||||
return this.send('PUT',
|
||||
`/groups/${encodeName}/groups/${encodeIncludedGroup}`)
|
||||
.then(response => this.getResponseObject(response));
|
||||
},
|
||||
|
||||
deleteGroupMembers(groupName, groupMembers) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
const encodeMember = encodeURIComponent(groupMembers);
|
||||
return this.send('DELETE',
|
||||
`/groups/${encodeName}/members/${encodeMember}`);
|
||||
},
|
||||
|
||||
deleteIncludedGroup(groupName, includedGroup) {
|
||||
const encodeName = encodeURIComponent(groupName);
|
||||
const encodeIncludedGroup = encodeURIComponent(includedGroup);
|
||||
return this.send('DELETE',
|
||||
`/groups/${encodeName}/groups/${encodeIncludedGroup}`);
|
||||
},
|
||||
|
||||
getVersion() {
|
||||
return this._fetchSharedCacheURL('/config/server/version');
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ limitations under the License.
|
||||
'admin/gr-create-project-dialog/gr-create-project-dialog_test.html',
|
||||
'admin/gr-group/gr-group_test.html',
|
||||
'admin/gr-group-audit-log/gr-group-audit-log_test.html',
|
||||
'admin/gr-group-members/gr-group-members_test.html',
|
||||
'admin/gr-plugin-list/gr-plugin-list_test.html',
|
||||
'admin/gr-project/gr-project_test.html',
|
||||
'admin/gr-project-detail-list/gr-project-detail-list_test.html',
|
||||
|
||||
Reference in New Issue
Block a user