From 9ddbcb00f8f3a6b6a85d4e1bf68d0edea0d45df5 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Thu, 9 Jun 2016 14:49:05 +0200 Subject: [PATCH] refactor permission templates page (#1025) --- .../PermissionTemplatesPageTest.java | 45 +++++ .../should_create.html | 99 +++++++++++ .../should_display_page.html | 89 ++++++++++ server/sonar-web/package.json | 1 + .../sonar-web/src/main/js/api/permissions.js | 29 ++-- .../__tests__/permission-templates-test.js | 114 ------------- .../main/js/apps/permission-templates/app.js | 8 +- .../components/ActionsCell.js | 159 ++++++++++++++++++ .../permission-templates/components/App.js | 92 ++++++++++ .../components/Defaults.js | 43 +++++ .../{header.js => components/Header.js} | 51 ++++-- .../List.js} | 45 ++--- .../ListHeader.js} | 34 ++-- .../components/ListItem.js | 101 +++++++++++ .../components/NameCell.js | 57 +++++++ .../components/PermissionCell.js | 86 ++++++++++ .../components/__tests__/ActionsCell-test.js | 103 ++++++++++++ .../components/__tests__/Defaults-test.js | 43 +++++ .../main/js/apps/permission-templates/main.js | 96 ----------- .../permission-template-defaults.js | 59 ------- .../permission-template-set-defaults.js | 85 ---------- .../permission-template.js | 140 --------------- .../js/apps/permission-templates/propTypes.js | 40 +++++ .../js/apps/permission-templates/styles.css | 17 ++ .../templates/permission-templates-form.hbs | 6 + .../js/apps/permission-templates/utils.js | 71 ++++++++ .../{create-view.js => views/CreateView.js} | 4 +- .../{delete-view.js => views/DeleteView.js} | 6 +- .../{form-view.js => views/FormView.js} | 4 +- .../{groups-view.js => views/GroupsView.js} | 6 +- .../{update-view.js => views/UpdateView.js} | 4 +- .../{users-view.js => views/UsersView.js} | 6 +- .../main/js/apps/project-permissions/main.js | 62 ++++--- .../project-permissions/permissions-header.js | 28 +-- .../apps/project-permissions/permissions.js | 4 +- .../sonar-web/src/main/less/init/icons.less | 2 +- .../resources/org/sonar/l10n/core.properties | 6 +- 37 files changed, 1220 insertions(+), 625 deletions(-) create mode 100644 it/it-tests/src/test/java/it/authorisation/PermissionTemplatesPageTest.java create mode 100644 it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_create.html create mode 100644 it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_display_page.html delete mode 100644 server/sonar-web/src/main/js/apps/permission-templates/__tests__/permission-templates-test.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/App.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.js rename server/sonar-web/src/main/js/apps/permission-templates/{header.js => components/Header.js} (56%) rename server/sonar-web/src/main/js/apps/permission-templates/{permission-templates.js => components/List.js} (56%) rename server/sonar-web/src/main/js/apps/permission-templates/{permissions-header.js => components/ListHeader.js} (64%) create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.js delete mode 100644 server/sonar-web/src/main/js/apps/permission-templates/main.js delete mode 100644 server/sonar-web/src/main/js/apps/permission-templates/permission-template-defaults.js delete mode 100644 server/sonar-web/src/main/js/apps/permission-templates/permission-template-set-defaults.js delete mode 100644 server/sonar-web/src/main/js/apps/permission-templates/permission-template.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/propTypes.js create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/styles.css create mode 100644 server/sonar-web/src/main/js/apps/permission-templates/utils.js rename server/sonar-web/src/main/js/apps/permission-templates/{create-view.js => views/CreateView.js} (93%) rename server/sonar-web/src/main/js/apps/permission-templates/{delete-view.js => views/DeleteView.js} (87%) rename server/sonar-web/src/main/js/apps/permission-templates/{form-view.js => views/FormView.js} (91%) rename server/sonar-web/src/main/js/apps/permission-templates/{groups-view.js => views/GroupsView.js} (93%) rename server/sonar-web/src/main/js/apps/permission-templates/{update-view.js => views/UpdateView.js} (93%) rename server/sonar-web/src/main/js/apps/permission-templates/{users-view.js => views/UsersView.js} (93%) diff --git a/it/it-tests/src/test/java/it/authorisation/PermissionTemplatesPageTest.java b/it/it-tests/src/test/java/it/authorisation/PermissionTemplatesPageTest.java new file mode 100644 index 00000000000..d7761ffd017 --- /dev/null +++ b/it/it-tests/src/test/java/it/authorisation/PermissionTemplatesPageTest.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package it.authorisation; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.selenium.Selenese; +import it.Category1Suite; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import util.QaOnly; +import util.selenium.SeleneseTest; + +@Category(QaOnly.class) +public class PermissionTemplatesPageTest { + + @ClassRule + public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR; + + @Test + public void should_display_page() throws Exception { + Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("should_display_page", + "/authorisation/PermissionTemplatesPageTest/should_display_page.html", + "/authorisation/PermissionTemplatesPageTest/should_create.html" + ).build(); + new SeleneseTest(selenese).runOn(orchestrator); + } +} diff --git a/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_create.html b/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_create.html new file mode 100644 index 00000000000..5ea5a7e5814 --- /dev/null +++ b/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_create.html @@ -0,0 +1,99 @@ + + + + + + should_create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
open/sessions/logout
open/sessions/new
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
open/permission_templates
waitForElementPresentcss=.page-actions button
clickcss=.page-actions button
waitForElementPresentcss=#permission-template-name
typecss=#permission-template-nameCustom
typecss=#permission-template-descriptionDescription
typecss=#permission-template-project-key-pattern.*
clickcss=#permission-template-submit
waitForElementPresentcss=tr[data-name="Custom"]
assertTextcss=tr[data-name="Custom"] .js-name*Custom*
assertTextcss=tr[data-name="Custom"] .js-description*Description*
assertTextcss=tr[data-name="Custom"] .js-project-key-pattern*.*
+ + diff --git a/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_display_page.html b/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_display_page.html new file mode 100644 index 00000000000..34ec0d429a0 --- /dev/null +++ b/it/it-tests/src/test/resources/authorisation/PermissionTemplatesPageTest/should_display_page.html @@ -0,0 +1,89 @@ + + + + + + should_display_page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
open/sessions/logout
open/sessions/new
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
open/permission_templates
waitForElementPresentcss=tr[data-id="default_template"]
assertTextcss=tr[data-id="default_template"] .js-name*Default template*
assertTextcss=tr[data-id="default_template"] .js-defaults*Projects*
assertTextcss=tr[data-id="default_template"] .js-description*This permission template will be used*
assertElementPresentcss=td[data-permission="user"]
assertElementPresentcss=td[data-permission="codeviewer"]
assertElementPresentcss=td[data-permission="issueadmin"]
assertElementPresentcss=td[data-permission="admin"]
assertElementPresentcss=td[data-permission="scan"]
+ + diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 98bc1baba05..3dd9cbad49b 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -59,6 +59,7 @@ "react-addons-shallow-compare": "15.0.1", "react-addons-test-utils": "15.0.1", "react-dom": "15.0.1", + "react-helmet": "3.1.0", "react-redux": "4.4.1", "react-router": "2.0.1", "react-router-redux": "4.0.2", diff --git a/server/sonar-web/src/main/js/api/permissions.js b/server/sonar-web/src/main/js/api/permissions.js index 03f723e56d1..4139c0f7c56 100644 --- a/server/sonar-web/src/main/js/api/permissions.js +++ b/server/sonar-web/src/main/js/api/permissions.js @@ -19,6 +19,7 @@ */ import $ from 'jquery'; import _ from 'underscore'; +import { getJSON, post } from '../helpers/request'; function request (options) { return $.ajax(options); @@ -102,13 +103,13 @@ export function revokeFromGroup (permission, group, project) { return request({ type: 'POST', url, data }); } -export function getPermissionTemplates (query) { +/** + * Get list of permission templates + * @returns {Promise} + */ +export function getPermissionTemplates () { const url = window.baseUrl + '/api/permissions/search_templates'; - const data = { }; - if (query) { - data.q = query; - } - return request({ type: 'GET', url, data }); + return getJSON(url); } export function createPermissionTemplate (options) { @@ -126,14 +127,16 @@ export function deletePermissionTemplate (options) { return request(_.extend({ type: 'POST', url }, options)); } -export function setDefaultPermissionTemplate (template, qualifier) { - if (typeof template !== 'string' || !template.length) { - return typeError('setDefaultPermissionTemplate', 'please provide permission template ID'); - } - +/** + * Set default permission template for a given qualifier + * @param {string} templateName + * @param {string} qualifier + * @returns {Promise} + */ +export function setDefaultPermissionTemplate (templateName, qualifier) { const url = window.baseUrl + '/api/permissions/set_default_template'; - const data = { templateId: template, qualifier }; - return request({ type: 'POST', url, data }); + const data = { templateName, qualifier }; + return post(url, data); } export function applyTemplateToProject (options) { diff --git a/server/sonar-web/src/main/js/apps/permission-templates/__tests__/permission-templates-test.js b/server/sonar-web/src/main/js/apps/permission-templates/__tests__/permission-templates-test.js deleted file mode 100644 index ade4d7b5872..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/__tests__/permission-templates-test.js +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -/* eslint no-unused-expressions: 0 */ -import React from 'react'; -import TestUtils from 'react-addons-test-utils'; -import { expect } from 'chai'; -import sinon from 'sinon'; - -import Defaults from '../permission-template-defaults'; -import SetDefaults from '../permission-template-set-defaults'; - -describe('Permission Templates', function () { - describe('Defaults', () => { - it('should display one qualifier', () => { - const permissionTemplate = { defaultFor: ['VW'] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-trk')).to.be.empty; - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-vw')).to.have.length(1); - }); - - it('should display two qualifiers', () => { - const permissionTemplate = { defaultFor: ['TRK', 'VW'] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-trk')).to.have.length(1); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-vw')).to.have.length(1); - }); - - it('should not display qualifiers', () => { - const permissionTemplate = { defaultFor: [] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-trk')).to.be.empty; - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-vw')).to.be.empty; - }); - - it('should omit "project" if there is only one qualifier', () => { - const permissionTemplate = { defaultFor: ['TRK'] }; - const topQualifiers = ['TRK']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'icon-qualifier-trk')).to.be.empty; - }); - }); - - describe('SetDefaults', () => { - const refresh = sinon.spy(); - - it('should display a dropdown with one option', () => { - const permissionTemplate = { defaultFor: ['VW'] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'dropdown')).to.have.length(1); - expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a')).to.have.length(1); - }); - - it('should display a dropdown with two options', () => { - const permissionTemplate = { defaultFor: [] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'dropdown')).to.have.length(1); - expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a')).to.have.length(2); - }); - - it('should not display a dropdown', () => { - const permissionTemplate = { defaultFor: ['TRK', 'VW'] }; - const topQualifiers = ['TRK', 'VW']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'dropdown')).to.be.empty; - expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a')).to.be.empty; - }); - - it('should omit dropdown if there is only one qualifier', () => { - const permissionTemplate = { defaultFor: [] }; - const topQualifiers = ['TRK']; - const result = TestUtils.renderIntoDocument( - - ); - expect(TestUtils.scryRenderedDOMComponentsWithClass(result, 'dropdown')).to.be.empty; - expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a')).to.have.length(1); - }); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/app.js b/server/sonar-web/src/main/js/apps/permission-templates/app.js index f257ec4a95c..d3747022dc1 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/app.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/app.js @@ -18,10 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ReactDOM from 'react-dom'; -import Main from './main'; +import { render } from 'react-dom'; +import App from './components/App'; window.sonarqube.appStarted.then(options => { const el = document.querySelector(options.el); - ReactDOM.render(
, el); + render(( + + ), el); }); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js new file mode 100644 index 00000000000..46a1e0e4f6c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js @@ -0,0 +1,159 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import difference from 'lodash/difference'; +import { PermissionTemplateType, CallbackType } from '../propTypes'; +import QualifierIcon from '../../../components/shared/qualifier-icon'; +import { translate } from '../../../helpers/l10n'; +import { setDefaultPermissionTemplate } from '../../../api/permissions'; + +export default class ActionsCell extends React.Component { + static propTypes = { + permissionTemplate: PermissionTemplateType.isRequired, + topQualifiers: React.PropTypes.array.isRequired, + onUpdate: CallbackType, + onDelete: CallbackType, + refresh: CallbackType + }; + + handleUpdateClick (e) { + e.preventDefault(); + this.props.onUpdate(); + } + + handleDeleteClick (e) { + e.preventDefault(); + this.props.onDelete(); + } + + setDefault (qualifier, e) { + e.preventDefault(); + setDefaultPermissionTemplate( + this.props.permissionTemplate.name, + qualifier + ).then(this.props.refresh); + } + + getAvailableQualifiers () { + return difference( + this.props.topQualifiers, + this.props.permissionTemplate.defaultFor); + } + + renderDropdownIcon (icon) { + const style = { + display: 'inline-block', + width: 16, + marginRight: 4, + textAlign: 'center' + }; + return ( +
{icon}
+ ); + } + + renderSetDefaultsControl () { + const availableQualifiers = this.getAvailableQualifiers(); + + if (availableQualifiers.length === 0) { + return null; + } + + return this.props.topQualifiers.length === 1 ? + this.renderIfSingleTopQualifier(availableQualifiers) : + this.renderIfMultipleTopQualifiers(availableQualifiers); + } + + renderSetDefaultLink (qualifier, child) { + return ( +
  • + + {this.renderDropdownIcon()} + {child} + +
  • + ); + } + + renderIfSingleTopQualifier (availableQualifiers) { + return availableQualifiers.map(qualifier => ( + this.renderSetDefaultLink(qualifier, ( + {translate('permission_templates.set_default')} + ))) + ); + } + + renderIfMultipleTopQualifiers (availableQualifiers) { + return availableQualifiers.map(qualifier => ( + this.renderSetDefaultLink(qualifier, ( + + {translate('permission_templates.set_default_for')} + {' '} + + {' '} + {translate('qualifiers', qualifier)} + + ))) + ); + } + + render () { + const { permissionTemplate: t } = this.props; + + return ( + +
    + + + +
    + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/App.js b/server/sonar-web/src/main/js/apps/permission-templates/components/App.js new file mode 100644 index 00000000000..bbb92be633c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/App.js @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Helmet from 'react-helmet'; +import Header from './Header'; +import List from './List'; +import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; +import { getPermissionTemplates } from '../../../api/permissions'; +import { sortPermissions, mergePermissionsToTemplates, mergeDefaultsToTemplates } from '../utils'; +import { translate } from '../../../helpers/l10n'; +import '../styles.css'; + +export default class App extends React.Component { + static propTypes = { + topQualifiers: React.PropTypes.array.isRequired + }; + + state = { + ready: false, + permissions: [], + permissionTemplates: [] + }; + + componentWillMount () { + this.requestPermissions = this.requestPermissions.bind(this); + } + + componentDidMount () { + this.mounted = true; + this.requestPermissions(); + } + + componentWillUnmount () { + this.mounted = false; + } + + requestPermissions () { + getPermissionTemplates().then(r => { + if (this.mounted) { + const permissions = sortPermissions(r.permissions); + const permissionTemplates = mergeDefaultsToTemplates( + mergePermissionsToTemplates(r.permissionTemplates, permissions), + r.defaultTemplates + ); + this.setState({ + ready: true, + permissionTemplates, + permissions + }); + } + }); + } + + render () { + return ( +
    + + +
    + + + + +
    + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.js new file mode 100644 index 00000000000..35776605f97 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.js @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import sortBy from 'lodash/sortBy'; +import { translate } from '../../../helpers/l10n'; +import { PermissionTemplateType } from '../propTypes'; + +export default class Defaults extends React.Component { + static propTypes = { + permissionTemplate: PermissionTemplateType.isRequired + }; + + render () { + const qualifiers = sortBy(this.props.permissionTemplate.defaultFor) + .map(qualifier => translate('qualifiers', qualifier)) + .join(', '); + + return ( +
    + + {translate('default')} for {qualifiers} + +
    + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/header.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.js similarity index 56% rename from server/sonar-web/src/main/js/apps/permission-templates/header.js rename to server/sonar-web/src/main/js/apps/permission-templates/components/Header.js index fb42ae354ef..b22b9387f3e 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/header.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.js @@ -18,34 +18,49 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import CreateView from './create-view'; -import { translate } from '../../helpers/l10n'; +import CreateView from '../views/CreateView'; +import { translate } from '../../../helpers/l10n'; +import { CallbackType } from '../propTypes'; -export default React.createClass({ - onCreate(e) { +export default class Header extends React.Component { + static propTypes = { + ready: React.PropTypes.bool.isRequired, + refresh: CallbackType + }; + + componentWillMount () { + this.handleCreateClick = this.handleCreateClick.bind(this); + } + + handleCreateClick (e) { e.preventDefault(); + new CreateView({ refresh: this.props.refresh }).render(); - }, - - renderSpinner () { - if (this.props.ready) { - return null; - } - return ; - }, + } - render() { + render () { return (
    -

    {translate('permission_templates.page')}

    - {this.renderSpinner()} +

    + {translate('permission_templates.page')} +

    + + {!this.props.ready && ( + + )} +
    - +
    -

    {translate('roles.page.description')}

    + +

    + {translate('permission_templates.page.description')} +

    ); } -}); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/permission-templates.js b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js similarity index 56% rename from server/sonar-web/src/main/js/apps/permission-templates/permission-templates.js rename to server/sonar-web/src/main/js/apps/permission-templates/components/List.js index 2a6aff49bd6..11ad4cd42a9 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/permission-templates.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js @@ -17,34 +17,35 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import classNames from 'classnames'; import React from 'react'; +import ListHeader from './ListHeader'; +import ListItem from './ListItem'; +import { PermissionTemplateType, CallbackType } from '../propTypes'; -import PermissionsHeader from './permissions-header'; -import PermissionTemplate from './permission-template'; - -export default React.createClass({ - propTypes: { - permissionTemplates: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - permissions: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, +export default class List extends React.Component { + static propTypes = { + permissionTemplates: React.PropTypes.arrayOf( + PermissionTemplateType).isRequired, + permissions: React.PropTypes.array.isRequired, topQualifiers: React.PropTypes.array.isRequired, - refresh: React.PropTypes.func.isRequired - }, + refresh: CallbackType + }; + + render () { + const permissionTemplates = this.props.permissionTemplates.map(p => ( + + )); - render() { - const permissionTemplates = this.props.permissionTemplates.map(p => { - return ; - }); - const className = classNames('data zebra', { 'new-loading': !this.props.ready }); return ( - - +
    + {permissionTemplates}
    ); } -}); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/permissions-header.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js similarity index 64% rename from server/sonar-web/src/main/js/apps/permission-templates/permissions-header.js rename to server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js index a459d2f60c1..5a820ef0d38 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/permissions-header.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js @@ -19,28 +19,30 @@ */ import React from 'react'; -export default React.createClass({ - propTypes: { - permissions: React.PropTypes.arrayOf(React.PropTypes.object).isRequired - }, +export default class ListHeader extends React.Component { + static propTypes = { + permissions: React.PropTypes.array.isRequired + }; + + render () { + const cells = this.props.permissions.map(p => ( + + {p.name} + + + )); - render() { - const cellWidth = (80 / this.props.permissions.length) + '%'; - const cells = this.props.permissions.map(p => { - return ( - - {p.name}
    {p.description} - - ); - }); return ( -   +   {cells} -   +   ); } -}); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.js new file mode 100644 index 00000000000..0e659a14f47 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.js @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import Backbone from 'backbone'; +import React from 'react'; +import NameCell from './NameCell'; +import PermissionCell from './PermissionCell'; +import ActionsCell from './ActionsCell'; +import UsersView from '../views/UsersView'; +import GroupsView from '../views/GroupsView'; +import UpdateView from '../views/UpdateView'; +import DeleteView from '../views/DeleteView'; +import { PermissionTemplateType, CallbackType } from '../propTypes'; + +export default class ListItem extends React.Component { + static propTypes = { + permissionTemplate: PermissionTemplateType.isRequired, + topQualifiers: React.PropTypes.array.isRequired, + refresh: CallbackType + }; + + componentWillMount () { + this.handleShowGroups = this.handleShowGroups.bind(this); + this.handleShowUsers = this.handleShowUsers.bind(this); + } + + handleShowGroups (permission) { + new GroupsView({ + permission, + permissionTemplate: this.props.permissionTemplate, + refresh: this.props.refresh + }).render(); + } + + handleShowUsers (permission) { + new UsersView({ + permission, + permissionTemplate: this.props.permissionTemplate, + refresh: this.props.refresh + }).render(); + } + + onUpdate () { + new UpdateView({ + model: new Backbone.Model(this.props.permissionTemplate), + refresh: this.props.refresh + }).render(); + } + + onDelete () { + new DeleteView({ + model: new Backbone.Model(this.props.permissionTemplate), + refresh: this.props.refresh + }).render(); + } + + render () { + const permissions = this.props.permissionTemplate.permissions.map(p => ( + + )); + + return ( + + + + {permissions} + + + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js new file mode 100644 index 00000000000..6591f1c7020 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Defaults from './Defaults'; +import { PermissionTemplateType } from '../propTypes'; + +export default class NameCell extends React.Component { + static propTypes = { + permissionTemplate: PermissionTemplateType.isRequired, + topQualifiers: React.PropTypes.array.isRequired + }; + + render () { + const { permissionTemplate: t } = this.props; + + return ( + + {t.name} + + {t.defaultFor.length > 0 && ( +
    + +
    + )} + + {!!t.description && ( +
    + {t.description} +
    + )} + + {!!t.projectKeyPattern && ( +
    + Project Key Pattern: {t.projectKeyPattern} +
    + )} + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.js new file mode 100644 index 00000000000..44bf5183445 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.js @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { PermissionType, CallbackType } from '../propTypes'; + +export default class PermissionCell extends React.Component { + static propTypes = { + permission: PermissionType.isRequired, + onShowUsers: CallbackType, + onShowGroups: CallbackType + }; + + handleShowUsers (e) { + e.preventDefault(); + this.props.onShowUsers(this.props.permission); + } + + handleShowGroups (e) { + e.preventDefault(); + this.props.onShowGroups(this.props.permission); + } + + render () { + const { permission: p } = this.props; + + return ( + + + + + + + + + + + + + + +
    + Users + + {p.usersCount} + + +
    + Groups + + {p.groupsCount} + + +
    + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.js b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.js new file mode 100644 index 00000000000..09f83fd29ce --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.js @@ -0,0 +1,103 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import chai, { expect } from 'chai'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import { shallow } from 'enzyme'; +import React from 'react'; +import ActionsCell from '../ActionsCell'; + +chai.use(sinonChai); + +const SAMPLE = { + id: 'id', + name: 'name', + permissions: [], + defaultFor: [] +}; + +function renderActionsCell (props) { + return shallow( + true} + onDelete={() => true} + refresh={() => true} + {...props}/> + ); +} + +function simulateClick (element) { + element.simulate('click', { + preventDefault() {} + }); +} + +describe('Permission Templates :: ActionsCell', () => { + it('should update', () => { + const onUpdate = sinon.spy(); + const updateButton = renderActionsCell({ onUpdate }).find('.js-update'); + + expect(updateButton).have.length(1); + expect(onUpdate).to.not.have.been.called; + + simulateClick(updateButton); + + expect(onUpdate).to.have.been.called; + }); + + it('should delete', () => { + const onDelete = sinon.spy(); + const deleteButton = renderActionsCell({ onDelete }).find('.js-delete'); + + expect(deleteButton).have.length(1); + expect(onDelete).to.not.have.been.called; + + simulateClick(deleteButton); + + expect(onDelete).to.have.been.called; + }); + + it('should not delete', () => { + const permissionTemplate = { ...SAMPLE, defaultFor: ['VW'] }; + const deleteButton = renderActionsCell({ permissionTemplate }) + .find('.js-delete'); + + expect(deleteButton).to.have.length(0); + }); + + it('should set default', () => { + const setDefault = renderActionsCell() + .find('.js-set-default'); + + expect(setDefault).to.have.length(2); + expect(setDefault.at(0).prop('data-qualifier')).to.equal('TRK'); + expect(setDefault.at(1).prop('data-qualifier')).to.equal('VW'); + }); + + it('should not set default', () => { + const permissionTemplate = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; + const setDefault = renderActionsCell({ permissionTemplate }) + .find('.js-set-default'); + + expect(setDefault).to.have.length(0); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.js b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.js new file mode 100644 index 00000000000..c74885f8ad4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.js @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { expect } from 'chai'; +import { shallow } from 'enzyme'; +import React from 'react'; +import Defaults from '../Defaults'; + +const SAMPLE = { + id: 'id', + name: 'name', + permissions: [] +}; + +describe('Permission Templates :: Defaults', () => { + it('should render one qualifier', () => { + const sample = { ...SAMPLE, defaultFor: ['DEV'] }; + const output = shallow(); + expect(output.text()).to.contain('DEV'); + }); + + it('should render several qualifiers', () => { + const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; + const output = shallow(); + expect(output.text()).to.contain('TRK'); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/main.js b/server/sonar-web/src/main/js/apps/permission-templates/main.js deleted file mode 100644 index d20b26aabcf..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/main.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import _ from 'underscore'; -import React from 'react'; -import Header from './header'; -import PermissionTemplates from './permission-templates'; -import { getPermissionTemplates } from '../../api/permissions'; - -const PERMISSIONS_ORDER = ['user', 'codeviewer', 'issueadmin', 'admin', 'scan']; - -export default React.createClass({ - propTypes: { - topQualifiers: React.PropTypes.array.isRequired - }, - - getInitialState() { - return { ready: false, permissions: [], permissionTemplates: [] }; - }, - - componentDidMount() { - this.requestPermissions(); - }, - - sortPermissions(permissions) { - return _.sortBy(permissions, p => PERMISSIONS_ORDER.indexOf(p.key)); - }, - - mergePermissionsToTemplates(permissionTemplates, basePermissions) { - return permissionTemplates.map(permissionTemplate => { - // it's important to keep the order of the permission template's permissions - // the same as the order of base permissions - const permissions = basePermissions.map(basePermission => { - const projectPermission = _.findWhere(permissionTemplate.permissions, { key: basePermission.key }); - return _.extend({ usersCount: 0, groupsCount: 0 }, basePermission, projectPermission); - }); - return _.extend({}, permissionTemplate, { permissions }); - }); - }, - - mergeDefaultsToTemplates(permissionTemplates, defaultTemplates = []) { - return permissionTemplates.map(permissionTemplate => { - const defaultFor = []; - defaultTemplates.forEach(defaultTemplate => { - if (defaultTemplate.templateId === permissionTemplate.id) { - defaultFor.push(defaultTemplate.qualifier); - } - }); - return _.extend({}, permissionTemplate, { defaultFor }); - }); - }, - - requestPermissions() { - getPermissionTemplates().done(r => { - const permissions = this.sortPermissions(r.permissions); - const permissionTemplates = this.mergePermissionsToTemplates(r.permissionTemplates, permissions); - const permissionTemplatesWithDefaults = this.mergeDefaultsToTemplates(permissionTemplates, r.defaultTemplates); - this.setState({ - ready: true, - permissionTemplates: permissionTemplatesWithDefaults, - permissions - }); - }); - }, - - render() { - return ( -
    -
    - - -
    - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/permission-template-defaults.js b/server/sonar-web/src/main/js/apps/permission-templates/permission-template-defaults.js deleted file mode 100644 index 4ca60b3d4db..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/permission-template-defaults.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import _ from 'underscore'; -import React from 'react'; -import QualifierIcon from '../../components/shared/qualifier-icon'; -import { translate } from '../../helpers/l10n'; - -export default React.createClass({ - propTypes: { - permissionTemplate: React.PropTypes.object.isRequired, - topQualifiers: React.PropTypes.array.isRequired - }, - - renderIfSingleTopQualifier() { - return ( -
      -
    • Default
    • -
    - ); - }, - - renderIfMultipleTopQualifiers() { - const defaults = this.props.permissionTemplate.defaultFor.map(qualifier => { - return
  •  {translate('qualifier', qualifier)}
  • ; - }); - return ( -
      -
    • Default for
    • - {defaults} -
    - ); - }, - - render() { - if (_.size(this.props.permissionTemplate.defaultFor) === 0) { - return null; - } - return this.props.topQualifiers.length === 1 ? - this.renderIfSingleTopQualifier() : - this.renderIfMultipleTopQualifiers(); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/permission-template-set-defaults.js b/server/sonar-web/src/main/js/apps/permission-templates/permission-template-set-defaults.js deleted file mode 100644 index 27be58a70e2..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/permission-template-set-defaults.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import _ from 'underscore'; -import React from 'react'; -import { setDefaultPermissionTemplate } from '../../api/permissions'; -import QualifierIcon from '../../components/shared/qualifier-icon'; -import { translate } from '../../helpers/l10n'; - -export default React.createClass({ - propTypes: { - permissionTemplate: React.PropTypes.object.isRequired, - topQualifiers: React.PropTypes.array.isRequired, - refresh: React.PropTypes.func.isRequired - }, - - getAvailableQualifiers() { - return _.difference(this.props.topQualifiers, this.props.permissionTemplate.defaultFor); - }, - - setDefault(qualifier, e) { - e.preventDefault(); - setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).done(() => this.props.refresh()); - }, - - renderIfSingleTopQualifier(availableQualifiers) { - const qualifiers = availableQualifiers.map(qualifier => { - return ( - - Set Default - - ); - }); - - return {qualifiers}; - }, - - renderIfMultipleTopQualifiers(availableQualifiers) { - const qualifiers = availableQualifiers.map(qualifier => { - return ( -
  • - - Set Default for {translate('qualifier', qualifier)} - -
  • - ); - }); - - return ( - - -
      {qualifiers}
    -
    - ); - }, - - render() { - const availableQualifiers = this.getAvailableQualifiers(); - if (availableQualifiers.length === 0) { - return null; - } - - return this.props.topQualifiers.length === 1 ? - this.renderIfSingleTopQualifier(availableQualifiers) : - this.renderIfMultipleTopQualifiers(availableQualifiers); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/permission-template.js b/server/sonar-web/src/main/js/apps/permission-templates/permission-template.js deleted file mode 100644 index c2a3e2cb9c3..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/permission-template.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import _ from 'underscore'; -import Backbone from 'backbone'; -import React from 'react'; -import Defaults from './permission-template-defaults'; -import SetDefaults from './permission-template-set-defaults'; -import UsersView from './users-view'; -import GroupsView from './groups-view'; -import UpdateView from './update-view'; -import DeleteView from './delete-view'; - -export default React.createClass({ - propTypes: { - permissionTemplate: React.PropTypes.object.isRequired, - topQualifiers: React.PropTypes.array.isRequired, - refresh: React.PropTypes.func.isRequired - }, - - showGroups(permission, e) { - e.preventDefault(); - new GroupsView({ - permission, - permissionTemplate: this.props.permissionTemplate, - refresh: this.props.refresh - }).render(); - }, - - showUsers(permission, e) { - e.preventDefault(); - new UsersView({ - permission, - permissionTemplate: this.props.permissionTemplate, - refresh: this.props.refresh - }).render(); - }, - - onUpdate(e) { - e.preventDefault(); - new UpdateView({ - model: new Backbone.Model(this.props.permissionTemplate), - refresh: this.props.refresh - }).render(); - }, - - onDelete(e) { - e.preventDefault(); - new DeleteView({ - model: new Backbone.Model(this.props.permissionTemplate), - refresh: this.props.refresh - }).render(); - }, - - renderAssociation() { - const projectKeyPattern = this.props.permissionTemplate.projectKeyPattern; - if (!projectKeyPattern) { - return null; - } - return
    Project Key Pattern: {projectKeyPattern}
    ; - }, - - renderDeleteButton() { - if (_.size(this.props.permissionTemplate.defaultFor) > 0) { - return null; - } - return ; - }, - - render() { - const permissions = this.props.permissionTemplate.permissions.map(p => { - return ( - - - - - - - - - - - - - - -
    Users{p.usersCount} - -
    Groups{p.groupsCount} - -
    - - ); - }); - return ( - - - {this.props.permissionTemplate.name} -

    {this.props.permissionTemplate.description}

    - - {permissions} - - {this.renderAssociation()} - -
    - - -
    - - {this.renderDeleteButton()} -
    -
    - - - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/propTypes.js b/server/sonar-web/src/main/js/apps/permission-templates/propTypes.js new file mode 100644 index 00000000000..5bc6119de09 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/propTypes.js @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { PropTypes } from 'react'; + +const { shape, arrayOf, string, number, func } = PropTypes; + +export const PermissionType = shape({ + key: string.isRequired, + name: string.isRequired, + description: string.isRequired, + usersCount: number.isRequired, + groupsCount: number.isRequired +}); + +export const PermissionTemplateType = shape({ + id: string.isRequired, + name: string.isRequired, + description: string, + permissions: arrayOf(PermissionType).isRequired, + defaultFor: arrayOf(string).isRequired +}); + +export const CallbackType = func.isRequired; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/styles.css b/server/sonar-web/src/main/js/apps/permission-templates/styles.css new file mode 100644 index 00000000000..a19119a6da3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/styles.css @@ -0,0 +1,17 @@ +.permissions-table { + table-layout: fixed; +} + +.permissions-table > tbody > tr > td { + border-bottom: 20px solid #fff !important; +} + +.permissions-table .permission-column { + width: 130px; + white-space: nowrap; +} + +.permissions-table .actions-column { + width: 130px; + text-align: right; +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs b/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs index 3478f988bdd..02ef4004747 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs +++ b/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs @@ -8,6 +8,9 @@