Introduce gr-admin-nav-behavior and use in gr-admin-view
gr-admin-nav-behavior will also be used by the main header. For this change, the behavior replaces work previously done in gr-admin-view. In a follow-up change, gr-admin-view will display the current section's options in a gr-dropdown-list and the gr-main-header will display top level admin links in a dropdown menu. Change-Id: Ief0ca439528929f35335309c292512579bb696c3
This commit is contained in:
@@ -0,0 +1,203 @@
|
|||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<script>
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ACCOUNT_CAPABILITIES = ['createProject', 'createGroup', 'viewPlugins'];
|
||||||
|
|
||||||
|
const ADMIN_LINKS = [{
|
||||||
|
name: 'Repositories',
|
||||||
|
noBaseUrl: true,
|
||||||
|
url: '/admin/repos',
|
||||||
|
view: 'gr-repo-list',
|
||||||
|
viewableToAll: true,
|
||||||
|
}, {
|
||||||
|
name: 'Groups',
|
||||||
|
section: 'Groups',
|
||||||
|
noBaseUrl: true,
|
||||||
|
url: '/admin/groups',
|
||||||
|
view: 'gr-admin-group-list',
|
||||||
|
}, {
|
||||||
|
name: 'Plugins',
|
||||||
|
capability: 'viewPlugins',
|
||||||
|
section: 'Plugins',
|
||||||
|
noBaseUrl: true,
|
||||||
|
url: '/admin/plugins',
|
||||||
|
view: 'gr-plugin-list',
|
||||||
|
}];
|
||||||
|
|
||||||
|
window.Gerrit = window.Gerrit || {};
|
||||||
|
|
||||||
|
/** @polymerBehavior Gerrit.AdminNavBehavior */
|
||||||
|
Gerrit.AdminNavBehavior = {
|
||||||
|
/**
|
||||||
|
* @param {!Object} account
|
||||||
|
* @param {!Function} getAccountCapabilities
|
||||||
|
* @param {!Function} getAdminMenuLinks
|
||||||
|
* Possible aguments in options:
|
||||||
|
* repoName?: string
|
||||||
|
* groupId?: string,
|
||||||
|
* groupName?: string,
|
||||||
|
* groupIsInternal?: boolean,
|
||||||
|
* isAdmin?: boolean,
|
||||||
|
* groupOwner?: boolean,
|
||||||
|
* @param {!Object=} opt_options
|
||||||
|
* @return {Promise<!Object>}
|
||||||
|
*/
|
||||||
|
getAdminLinks(account, getAccountCapabilities, getAdminMenuLinks,
|
||||||
|
opt_options) {
|
||||||
|
if (!account) {
|
||||||
|
return Promise.resolve(this._filterLinks(link => link.viewableToAll,
|
||||||
|
getAdminMenuLinks, opt_options));
|
||||||
|
}
|
||||||
|
return getAccountCapabilities(ACCOUNT_CAPABILITIES)
|
||||||
|
.then(capabilities => {
|
||||||
|
return this._filterLinks(link => {
|
||||||
|
return !link.capability ||
|
||||||
|
capabilities.hasOwnProperty(link.capability);
|
||||||
|
}, getAdminMenuLinks, opt_options);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Function} filterFn
|
||||||
|
* @param {!Function} getAdminMenuLinks
|
||||||
|
* Possible aguments in options:
|
||||||
|
* repoName?: string
|
||||||
|
* groupId?: string,
|
||||||
|
* groupName?: string,
|
||||||
|
* groupIsInternal?: boolean,
|
||||||
|
* isAdmin?: boolean,
|
||||||
|
* groupOwner?: boolean,
|
||||||
|
* @param {!Object|undefined} opt_options
|
||||||
|
* @return {Promise<!Object>}
|
||||||
|
*/
|
||||||
|
_filterLinks(filterFn, getAdminMenuLinks, opt_options) {
|
||||||
|
let links = ADMIN_LINKS.slice(0);
|
||||||
|
let expandedSection;
|
||||||
|
|
||||||
|
// Append top-level links that are defined by plugins.
|
||||||
|
links.push(...getAdminMenuLinks().map(link => ({
|
||||||
|
url: link.url,
|
||||||
|
name: link.text,
|
||||||
|
noBaseUrl: link.url[0] === '/',
|
||||||
|
view: null,
|
||||||
|
viewableToAll: true,
|
||||||
|
})));
|
||||||
|
|
||||||
|
links = links.filter(filterFn);
|
||||||
|
|
||||||
|
const filteredLinks = [];
|
||||||
|
const repoName = opt_options && opt_options.repoName;
|
||||||
|
const groupId = opt_options && opt_options.groupId;
|
||||||
|
const groupName = opt_options && opt_options.groupName;
|
||||||
|
const groupIsInternal = opt_options && opt_options.groupIsInternal;
|
||||||
|
const isAdmin = opt_options && opt_options.isAdmin;
|
||||||
|
const groupOwner = opt_options && opt_options.groupOwner;
|
||||||
|
|
||||||
|
// Don't bother to get sub-navigation items if only the top level links
|
||||||
|
// are needed. This is used by the main header dropdown.
|
||||||
|
if (!repoName && !groupId) { return {links, expandedSection}; }
|
||||||
|
|
||||||
|
// Otherwise determine the full set of links and return both the full
|
||||||
|
// set in addition to the subsection that should be displayed if it
|
||||||
|
// exists.
|
||||||
|
for (const link of links) {
|
||||||
|
const linkCopy = Object.assign({}, link);
|
||||||
|
if (linkCopy.name === 'Repositories' && repoName) {
|
||||||
|
linkCopy.subsection = this.getRepoSubsections(repoName);
|
||||||
|
expandedSection = linkCopy.subsection;
|
||||||
|
} else if (linkCopy.name === 'Groups' && groupId && groupName) {
|
||||||
|
linkCopy.subsection = this.getGroupSubsections(groupId, groupName,
|
||||||
|
groupIsInternal, isAdmin, groupOwner);
|
||||||
|
expandedSection = linkCopy.subsection;
|
||||||
|
}
|
||||||
|
filteredLinks.push(linkCopy);
|
||||||
|
}
|
||||||
|
return {links: filteredLinks, expandedSection};
|
||||||
|
},
|
||||||
|
|
||||||
|
getGroupSubsections(groupId, groupName, groupIsInternal, isAdmin,
|
||||||
|
groupOwner) {
|
||||||
|
const subsection = {
|
||||||
|
name: groupName,
|
||||||
|
view: Gerrit.Nav.View.GROUP,
|
||||||
|
url: Gerrit.Nav.getUrlForGroup(groupId),
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
if (groupIsInternal) {
|
||||||
|
subsection.children.push({
|
||||||
|
name: 'Members',
|
||||||
|
detailType: Gerrit.Nav.GroupDetailView.MEMBERS,
|
||||||
|
view: Gerrit.Nav.View.GROUP,
|
||||||
|
url: Gerrit.Nav.getUrlForGroupMembers(groupId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (groupIsInternal && (isAdmin || groupOwner)) {
|
||||||
|
subsection.children.push(
|
||||||
|
{
|
||||||
|
name: 'Audit Log',
|
||||||
|
detailType: Gerrit.Nav.GroupDetailView.LOG,
|
||||||
|
view: Gerrit.Nav.View.GROUP,
|
||||||
|
url: Gerrit.Nav.getUrlForGroupLog(groupId),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return subsection;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRepoSubsections(repoName) {
|
||||||
|
return {
|
||||||
|
name: repoName,
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
url: Gerrit.Nav.getUrlForRepo(repoName),
|
||||||
|
children: [{
|
||||||
|
name: 'Access',
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
detailType: Gerrit.Nav.RepoDetailView.ACCESS,
|
||||||
|
url: Gerrit.Nav.getUrlForRepoAccess(repoName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Commands',
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
|
||||||
|
url: Gerrit.Nav.getUrlForRepoCommands(repoName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Branches',
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
|
||||||
|
url: Gerrit.Nav.getUrlForRepoBranches(repoName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tags',
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
detailType: Gerrit.Nav.RepoDetailView.TAGS,
|
||||||
|
url: Gerrit.Nav.getUrlForRepoTags(repoName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dashboards',
|
||||||
|
view: Gerrit.Nav.View.REPO,
|
||||||
|
detailType: Gerrit.Nav.RepoDetailView.DASHBOARDS,
|
||||||
|
url: Gerrit.Nav.getUrlForRepoDashboards(repoName),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(window);
|
||||||
|
</script>
|
@@ -0,0 +1,303 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
|
<title>keyboard-shortcut-behavior</title>
|
||||||
|
|
||||||
|
<script src="../../bower_components/webcomponentsjs/webcomponents.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-admin-nav-behavior.html">
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<test-element></test-element>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-admin-nav-behavior tests', () => {
|
||||||
|
let element;
|
||||||
|
let sandbox;
|
||||||
|
let capabilityStub;
|
||||||
|
let menuLinkStub;
|
||||||
|
|
||||||
|
suiteSetup(() => {
|
||||||
|
// Define a Polymer element that uses this behavior.
|
||||||
|
Polymer({
|
||||||
|
is: 'test-element',
|
||||||
|
behaviors: [
|
||||||
|
Gerrit.AdminNavBehavior,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
element = fixture('basic');
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
capabilityStub = sinon.stub();
|
||||||
|
menuLinkStub = sinon.stub().returns([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
const testAdminLinks = (account, options, expected, done) => {
|
||||||
|
element.getAdminLinks(account,
|
||||||
|
capabilityStub,
|
||||||
|
menuLinkStub,
|
||||||
|
options)
|
||||||
|
.then(res => {
|
||||||
|
assert.equal(expected.totalLength, res.links.length);
|
||||||
|
assert.equal(res.links[0].name, 'Repositories');
|
||||||
|
// Repos
|
||||||
|
if (expected.groupListShown) {
|
||||||
|
assert.equal(res.links[1].name, 'Groups');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.pluginListShown) {
|
||||||
|
assert.equal(res.links[2].name, 'Plugins');
|
||||||
|
assert.isNotOk(res.links[2].subsection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.projectPageShown) {
|
||||||
|
assert.isOk(res.links[0].subsection);
|
||||||
|
assert.equal(res.links[0].subsection.children.length, 5);
|
||||||
|
} else {
|
||||||
|
assert.isNotOk(res.links[0].subsection);
|
||||||
|
}
|
||||||
|
// Groups
|
||||||
|
if (expected.groupPageShown) {
|
||||||
|
assert.isOk(res.links[1].subsection);
|
||||||
|
assert.equal(res.links[1].subsection.children.length,
|
||||||
|
expected.groupSubpageLength);
|
||||||
|
} else if ( expected.totalLength > 1) {
|
||||||
|
assert.isNotOk(res.links[1].subsection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.pluginGeneratedLinks) {
|
||||||
|
for (const link of expected.pluginGeneratedLinks) {
|
||||||
|
assert.isTrue(!!res.links.find(l => {
|
||||||
|
return (l.url === link.url && l.name === link.text);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current section
|
||||||
|
if (expected.projectPageShown || expected.groupPageShown) {
|
||||||
|
assert.isOk(res.expandedSection);
|
||||||
|
assert.isOk(res.expandedSection.children);
|
||||||
|
} else {
|
||||||
|
assert.isNotOk(res.expandedSection);
|
||||||
|
}
|
||||||
|
if (expected.projectPageShown) {
|
||||||
|
assert.equal(res.expandedSection.name, 'my-repo');
|
||||||
|
assert.equal(res.expandedSection.children.length, 5);
|
||||||
|
} else if (expected.groupPageShown) {
|
||||||
|
assert.equal(res.expandedSection.name, 'my-group');
|
||||||
|
assert.equal(res.expandedSection.children.length,
|
||||||
|
expected.groupSubpageLength);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
suite('logged out', () => {
|
||||||
|
let account;
|
||||||
|
let expected;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
expected = {
|
||||||
|
groupListShown: false,
|
||||||
|
groupPageShown: false,
|
||||||
|
pluginListShown: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('without a specific repo or group', done => {
|
||||||
|
let options;
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
totalLength: 1,
|
||||||
|
projectPageShown: false,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with a repo', done => {
|
||||||
|
const options = {repoName: 'my-repo'};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
totalLength: 1,
|
||||||
|
projectPageShown: true,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with plugin generated links', done => {
|
||||||
|
let options;
|
||||||
|
const generatedLinks = [
|
||||||
|
{text: 'internal link text', url: '/internal/link/url'},
|
||||||
|
{text: 'external link text', url: 'http://external/link/url'},
|
||||||
|
];
|
||||||
|
menuLinkStub.returns(generatedLinks);
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
totalLength: 3,
|
||||||
|
projectPageShown: false,
|
||||||
|
pluginGeneratedLinks: generatedLinks,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('no plugin capability logged in', () => {
|
||||||
|
const account = {
|
||||||
|
name: 'test-user',
|
||||||
|
};
|
||||||
|
let expected;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
expected = {
|
||||||
|
totalLength: 2,
|
||||||
|
pluginListShown: false,
|
||||||
|
};
|
||||||
|
capabilityStub.returns(Promise.resolve({}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('without a specific project or group', done => {
|
||||||
|
let options;
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupListShown: true,
|
||||||
|
groupPageShown: false,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with a repo', done => {
|
||||||
|
const account = {
|
||||||
|
name: 'test-user',
|
||||||
|
};
|
||||||
|
const options = {repoName: 'my-repo'};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: true,
|
||||||
|
groupListShown: true,
|
||||||
|
groupPageShown: false,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('view plugin capability logged in', () => {
|
||||||
|
const account = {
|
||||||
|
name: 'test-user',
|
||||||
|
};
|
||||||
|
let expected;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
capabilityStub.returns(Promise.resolve({viewPlugins: true}));
|
||||||
|
expected = {
|
||||||
|
totalLength: 3,
|
||||||
|
groupListShown: true,
|
||||||
|
pluginListShown: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('without a specific repo or group', done => {
|
||||||
|
let options;
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupPageShown: false,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with a repo', done => {
|
||||||
|
const options = {repoName: 'my-repo'};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: true,
|
||||||
|
groupPageShown: false,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin with internal group', done => {
|
||||||
|
const options = {
|
||||||
|
groupId: 'a15262',
|
||||||
|
groupName: 'my-group',
|
||||||
|
groupIsInternal: true,
|
||||||
|
isAdmin: true,
|
||||||
|
groupOwner: false,
|
||||||
|
};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupPageShown: true,
|
||||||
|
groupSubpageLength: 2,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('group owner with internal group', done => {
|
||||||
|
const options = {
|
||||||
|
groupId: 'a15262',
|
||||||
|
groupName: 'my-group',
|
||||||
|
groupIsInternal: true,
|
||||||
|
isAdmin: false,
|
||||||
|
groupOwner: true,
|
||||||
|
};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupPageShown: true,
|
||||||
|
groupSubpageLength: 2,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non owner or admin with internal group', done => {
|
||||||
|
const options = {
|
||||||
|
groupId: 'a15262',
|
||||||
|
groupName: 'my-group',
|
||||||
|
groupIsInternal: true,
|
||||||
|
isAdmin: false,
|
||||||
|
groupOwner: false,
|
||||||
|
};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupPageShown: true,
|
||||||
|
groupSubpageLength: 1,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin with external group', done => {
|
||||||
|
const options = {
|
||||||
|
groupId: 'a15262',
|
||||||
|
groupName: 'my-group',
|
||||||
|
groupIsInternal: false,
|
||||||
|
isAdmin: true,
|
||||||
|
groupOwner: true,
|
||||||
|
};
|
||||||
|
expected = Object.assign(expected, {
|
||||||
|
projectPageShown: false,
|
||||||
|
groupPageShown: true,
|
||||||
|
groupSubpageLength: 0,
|
||||||
|
});
|
||||||
|
testAdminLinks(account, options, expected, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@@ -18,6 +18,7 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||||
|
<link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
|
||||||
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
||||||
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
|
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
|
||||||
<link rel="import" href="../../../styles/gr-page-nav-styles.html">
|
<link rel="import" href="../../../styles/gr-page-nav-styles.html">
|
||||||
|
@@ -17,35 +17,9 @@
|
|||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Note: noBaseUrl: true is set on entries where the URL is not yet supported
|
|
||||||
// by router abstraction.
|
|
||||||
const ADMIN_LINKS = [{
|
|
||||||
name: 'Repositories',
|
|
||||||
noBaseUrl: true,
|
|
||||||
url: '/admin/repos',
|
|
||||||
view: 'gr-repo-list',
|
|
||||||
viewableToAll: true,
|
|
||||||
children: [],
|
|
||||||
}, {
|
|
||||||
name: 'Groups',
|
|
||||||
section: 'Groups',
|
|
||||||
noBaseUrl: true,
|
|
||||||
url: '/admin/groups',
|
|
||||||
view: 'gr-admin-group-list',
|
|
||||||
children: [],
|
|
||||||
}, {
|
|
||||||
name: 'Plugins',
|
|
||||||
capability: 'viewPlugins',
|
|
||||||
section: 'Plugins',
|
|
||||||
noBaseUrl: true,
|
|
||||||
url: '/admin/plugins',
|
|
||||||
view: 'gr-plugin-list',
|
|
||||||
}];
|
|
||||||
|
|
||||||
const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
|
const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
|
||||||
|
|
||||||
const ACCOUNT_CAPABILITIES = ['createProject', 'createGroup', 'viewPlugins'];
|
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'gr-admin-view',
|
is: 'gr-admin-view',
|
||||||
|
|
||||||
@@ -66,6 +40,7 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
_subsectionLinks: Array,
|
||||||
_filteredLinks: Array,
|
_filteredLinks: Array,
|
||||||
_showDownload: {
|
_showDownload: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -89,6 +64,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
|
Gerrit.AdminNavBehavior,
|
||||||
Gerrit.BaseUrlBehavior,
|
Gerrit.BaseUrlBehavior,
|
||||||
Gerrit.URLEncodingBehavior,
|
Gerrit.URLEncodingBehavior,
|
||||||
],
|
],
|
||||||
@@ -108,113 +84,54 @@
|
|||||||
];
|
];
|
||||||
return Promise.all(promises).then(result => {
|
return Promise.all(promises).then(result => {
|
||||||
this._account = result[0];
|
this._account = result[0];
|
||||||
if (!this._account) {
|
let options;
|
||||||
// Return so that account capabilities don't load with no account.
|
if (this._repoName) {
|
||||||
return this._filteredLinks = this._filterLinks(link => {
|
options = {repoName: this._repoName};
|
||||||
return link.viewableToAll;
|
} else if (this._groupId) {
|
||||||
});
|
options = {
|
||||||
}
|
groupId: this._groupId,
|
||||||
this._loadAccountCapabilities();
|
groupName: this._groupName,
|
||||||
});
|
groupIsInternal: this._groupIsInternal,
|
||||||
},
|
isAdmin: this._isAdmin,
|
||||||
|
groupOwner: this._groupOwner,
|
||||||
_filterLinks(filterFn) {
|
|
||||||
let links = ADMIN_LINKS.slice(0);
|
|
||||||
|
|
||||||
// Append top-level links that are defined by plugins.
|
|
||||||
links.push(...this.$.jsAPI.getAdminMenuLinks().map(link => ({
|
|
||||||
url: link.url,
|
|
||||||
name: link.text,
|
|
||||||
children: [],
|
|
||||||
noBaseUrl: link.url[0] === '/',
|
|
||||||
view: null,
|
|
||||||
viewableToAll: true,
|
|
||||||
})));
|
|
||||||
|
|
||||||
links = links.filter(filterFn);
|
|
||||||
|
|
||||||
const filteredLinks = [];
|
|
||||||
for (const link of links) {
|
|
||||||
const linkCopy = Object.assign({}, link);
|
|
||||||
linkCopy.children = linkCopy.children ?
|
|
||||||
linkCopy.children.filter(filterFn) : [];
|
|
||||||
if (linkCopy.name === 'Repositories' && this._repoName) {
|
|
||||||
linkCopy.subsection = {
|
|
||||||
name: this._repoName,
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
url: Gerrit.Nav.getUrlForRepo(this._repoName),
|
|
||||||
children: [{
|
|
||||||
name: 'Access',
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
detailType: Gerrit.Nav.RepoDetailView.ACCESS,
|
|
||||||
url: Gerrit.Nav.getUrlForRepoAccess(this._repoName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Commands',
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
|
|
||||||
url: Gerrit.Nav.getUrlForRepoCommands(this._repoName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Branches',
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
|
|
||||||
url: Gerrit.Nav.getUrlForRepoBranches(this._repoName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Tags',
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
detailType: Gerrit.Nav.RepoDetailView.TAGS,
|
|
||||||
url: Gerrit.Nav.getUrlForRepoTags(this._repoName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Dashboards',
|
|
||||||
view: Gerrit.Nav.View.REPO,
|
|
||||||
detailType: Gerrit.Nav.RepoDetailView.DASHBOARDS,
|
|
||||||
url: Gerrit.Nav.getUrlForRepoDashboards(this._repoName),
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (linkCopy.name === 'Groups' && this._groupId && this._groupName) {
|
|
||||||
linkCopy.subsection = {
|
|
||||||
name: this._groupName,
|
|
||||||
view: Gerrit.Nav.View.GROUP,
|
|
||||||
url: Gerrit.Nav.getUrlForGroup(this._groupId),
|
|
||||||
children: [],
|
|
||||||
};
|
|
||||||
if (this._groupIsInternal) {
|
|
||||||
linkCopy.subsection.children.push({
|
|
||||||
name: 'Members',
|
|
||||||
detailType: Gerrit.Nav.GroupDetailView.MEMBERS,
|
|
||||||
view: Gerrit.Nav.View.GROUP,
|
|
||||||
url: Gerrit.Nav.getUrlForGroupMembers(this._groupId),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this._groupIsInternal && (this._isAdmin || this._groupOwner)) {
|
|
||||||
linkCopy.subsection.children.push(
|
|
||||||
{
|
|
||||||
name: 'Audit Log',
|
|
||||||
detailType: Gerrit.Nav.GroupDetailView.LOG,
|
|
||||||
view: Gerrit.Nav.View.GROUP,
|
|
||||||
url: Gerrit.Nav.getUrlForGroupLog(this._groupId),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredLinks.push(linkCopy);
|
return this.getAdminLinks(this._account,
|
||||||
|
this.$.restAPI.getAccountCapabilities.bind(this.$.restAPI),
|
||||||
|
this.$.jsAPI.getAdminMenuLinks.bind(this.$.jsAPI),
|
||||||
|
options)
|
||||||
|
.then(res => {
|
||||||
|
this._filteredLinks = res.links;
|
||||||
|
|
||||||
|
if (!res.expandedSection) {
|
||||||
|
this._subsectionLinks = [];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return filteredLinks;
|
this._subsectionLinks = [res.expandedSection]
|
||||||
|
.concat(res.expandedSection.children).map(section => {
|
||||||
|
return {
|
||||||
|
text: !section.detailType ? 'Home' : section.name,
|
||||||
|
value: section.view + section.detailType,
|
||||||
|
view: section.view,
|
||||||
|
url: section.url,
|
||||||
|
detailType: section.detailType,
|
||||||
|
parent: this._groupId || this._repoName || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadAccountCapabilities() {
|
_handleSubsectionChange(e) {
|
||||||
return this.$.restAPI.getAccountCapabilities(ACCOUNT_CAPABILITIES)
|
const selected = this._subsectionLinks
|
||||||
.then(capabilities => {
|
.find(section => section.value === e.detail.value);
|
||||||
this._filteredLinks = this._filterLinks(link => {
|
|
||||||
return !link.capability ||
|
// This is when it gets set initially.
|
||||||
capabilities.hasOwnProperty(link.capability);
|
if (this._selectedIsCurrentPage(selected)) {
|
||||||
});
|
return;
|
||||||
});
|
}
|
||||||
|
Gerrit.Nav.navigateToRelativeUrl(selected.url);
|
||||||
},
|
},
|
||||||
|
|
||||||
_paramsChanged(params) {
|
_paramsChanged(params) {
|
||||||
@@ -260,6 +177,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_computeSelectedTitle(params) {
|
||||||
|
return this.getSelectedTitle(params.view);
|
||||||
|
},
|
||||||
|
|
||||||
// TODO (beckysiegel): Update these functions after router abstraction is
|
// TODO (beckysiegel): Update these functions after router abstraction is
|
||||||
// updated. They are currently copied from gr-dropdown (and should be
|
// updated. They are currently copied from gr-dropdown (and should be
|
||||||
// updated there as well once complete).
|
// updated there as well once complete).
|
||||||
|
@@ -95,6 +95,9 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('_filteredLinks admin', done => {
|
test('_filteredLinks admin', done => {
|
||||||
|
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
|
||||||
|
name: 'test-user',
|
||||||
|
}));
|
||||||
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
createGroup: true,
|
createGroup: true,
|
||||||
@@ -102,35 +105,36 @@ limitations under the License.
|
|||||||
viewPlugins: true,
|
viewPlugins: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
element._loadAccountCapabilities().then(() => {
|
element.reload().then(() => {
|
||||||
assert.equal(element._filteredLinks.length, 3);
|
assert.equal(element._filteredLinks.length, 3);
|
||||||
|
|
||||||
// Repos
|
// Repos
|
||||||
assert.equal(element._filteredLinks[0].children.length, 0);
|
|
||||||
assert.isNotOk(element._filteredLinks[0].subsection);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
assert.equal(element._filteredLinks[1].children.length, 0);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
assert.equal(element._filteredLinks[2].children.length, 0);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('_filteredLinks non admin authenticated', done => {
|
test('_filteredLinks non admin authenticated', done => {
|
||||||
|
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
|
||||||
|
name: 'test-user',
|
||||||
|
}));
|
||||||
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
});
|
});
|
||||||
element._loadAccountCapabilities().then(() => {
|
element.reload().then(() => {
|
||||||
assert.equal(element._filteredLinks.length, 2);
|
assert.equal(element._filteredLinks.length, 2);
|
||||||
|
|
||||||
// Repos
|
// Repos
|
||||||
assert.equal(element._filteredLinks[0].children.length, 0);
|
|
||||||
assert.isNotOk(element._filteredLinks[0].subsection);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
assert.equal(element._filteredLinks[1].children.length, 0);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -140,7 +144,6 @@ limitations under the License.
|
|||||||
assert.equal(element._filteredLinks.length, 1);
|
assert.equal(element._filteredLinks.length, 1);
|
||||||
|
|
||||||
// Repos
|
// Repos
|
||||||
assert.equal(element._filteredLinks[0].children.length, 0);
|
|
||||||
assert.isNotOk(element._filteredLinks[0].subsection);
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -156,7 +159,6 @@ limitations under the License.
|
|||||||
assert.deepEqual(element._filteredLinks[1], {
|
assert.deepEqual(element._filteredLinks[1], {
|
||||||
url: '/internal/link/url',
|
url: '/internal/link/url',
|
||||||
name: 'internal link text',
|
name: 'internal link text',
|
||||||
children: [],
|
|
||||||
noBaseUrl: true,
|
noBaseUrl: true,
|
||||||
view: null,
|
view: null,
|
||||||
viewableToAll: true,
|
viewableToAll: true,
|
||||||
@@ -164,7 +166,6 @@ limitations under the License.
|
|||||||
assert.deepEqual(element._filteredLinks[2], {
|
assert.deepEqual(element._filteredLinks[2], {
|
||||||
url: 'http://external/link/url',
|
url: 'http://external/link/url',
|
||||||
name: 'external link text',
|
name: 'external link text',
|
||||||
children: [],
|
|
||||||
noBaseUrl: false,
|
noBaseUrl: false,
|
||||||
view: null,
|
view: null,
|
||||||
viewableToAll: true,
|
viewableToAll: true,
|
||||||
@@ -174,6 +175,9 @@ limitations under the License.
|
|||||||
|
|
||||||
test('Repo shows up in nav', done => {
|
test('Repo shows up in nav', done => {
|
||||||
element._repoName = 'Test Repo';
|
element._repoName = 'Test Repo';
|
||||||
|
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
|
||||||
|
name: 'test-user',
|
||||||
|
}));
|
||||||
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
createGroup: true,
|
createGroup: true,
|
||||||
@@ -181,18 +185,52 @@ limitations under the License.
|
|||||||
viewPlugins: true,
|
viewPlugins: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
element._loadAccountCapabilities().then(() => {
|
element.reload().then(() => {
|
||||||
|
flushAsynchronousOperations();
|
||||||
assert.equal(element._filteredLinks.length, 3);
|
assert.equal(element._filteredLinks.length, 3);
|
||||||
|
|
||||||
// Repos
|
// Repos
|
||||||
assert.equal(element._filteredLinks[0].children.length, 0);
|
assert.equal(element._filteredLinks[0].subsection.children.length, 5);
|
||||||
assert.equal(element._filteredLinks[0].subsection.name, 'Test Repo');
|
assert.equal(element._filteredLinks[0].subsection.name, 'Test Repo');
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
assert.equal(element._filteredLinks[1].children.length, 0);
|
assert.isNotOk(element._filteredLinks[1].subsection);
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
assert.equal(element._filteredLinks[2].children.length, 0);
|
assert.isNotOk(element._filteredLinks[2].subsection);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Group shows up in nav', done => {
|
||||||
|
element._groupId = 'a15262';
|
||||||
|
element._groupName = 'my-group';
|
||||||
|
element._groupIsInternal = true;
|
||||||
|
element._isAdmin = true;
|
||||||
|
element._groupOwner = false;
|
||||||
|
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
|
||||||
|
name: 'test-user',
|
||||||
|
}));
|
||||||
|
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
|
||||||
|
return Promise.resolve({
|
||||||
|
createGroup: true,
|
||||||
|
createProject: true,
|
||||||
|
viewPlugins: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
element.reload().then(() => {
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
assert.equal(element._filteredLinks.length, 3);
|
||||||
|
|
||||||
|
// Repos
|
||||||
|
assert.isNotOk(element._filteredLinks[0].subsection);
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
assert.equal(element._filteredLinks[1].subsection.children.length, 2);
|
||||||
|
assert.equal(element._filteredLinks[1].subsection.name, 'my-group');
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
assert.isNotOk(element._filteredLinks[2].subsection);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -196,6 +196,7 @@ limitations under the License.
|
|||||||
'keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html',
|
'keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html',
|
||||||
'rest-client-behavior/rest-client-behavior_test.html',
|
'rest-client-behavior/rest-client-behavior_test.html',
|
||||||
'gr-access-behavior/gr-access-behavior_test.html',
|
'gr-access-behavior/gr-access-behavior_test.html',
|
||||||
|
'gr-admin-nav-behavior/gr-admin-nav-behavior_test.html',
|
||||||
'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
|
'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
|
||||||
'gr-change-table-behavior/gr-change-table-behavior_test.html',
|
'gr-change-table-behavior/gr-change-table-behavior_test.html',
|
||||||
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
|
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
|
||||||
|
Reference in New Issue
Block a user