From b3c2e0d524cd316326d642247972e12d00de0d12 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Tue, 11 Apr 2017 14:46:00 +0200 Subject: [PATCH] SONAR-9110 Add organization page extensions --- .../src/main/js/api/organizations.js | 6 +- .../extensions/OrganizationPageExtension.js | 60 +++++++++++++++++ .../navigation/OrganizationNavigation.js | 66 ++++++++++++++----- .../src/main/js/apps/organizations/routes.js | 5 ++ .../src/main/js/store/organizations/duck.js | 2 + 5 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.js diff --git a/server/sonar-web/src/main/js/api/organizations.js b/server/sonar-web/src/main/js/api/organizations.js index 41f3c014b9c..df7a1569d47 100644 --- a/server/sonar-web/src/main/js/api/organizations.js +++ b/server/sonar-web/src/main/js/api/organizations.js @@ -36,7 +36,11 @@ type GetOrganizationType = null | Organization; type GetOrganizationNavigation = { canAdmin: boolean, - isDefault: boolean + canDelete: boolean, + canProvisionProjects: boolean, + isDefault: boolean, + pages: Array<{ key: string, name: string }>, + adminPages: Array<{ key: string, name: string }> }; export const getOrganization = (key: string): Promise => { diff --git a/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.js b/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.js new file mode 100644 index 00000000000..6f37a031437 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.js @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info 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. + */ +// @flow +import React from 'react'; +import { connect } from 'react-redux'; +import Extension from './Extension'; +import ExtensionNotFound from './ExtensionNotFound'; +import { getOrganizationByKey } from '../../../store/rootReducer'; +import type { Organization } from '../../../store/organizations/duck'; + +type Props = { + organization: Organization, + params: { + extensionKey: string, + organizationKey: string, + pluginKey: string + } +}; + +class OrganizationPageExtension extends React.PureComponent { + props: Props; + + render() { + const { extensionKey, pluginKey } = this.props.params; + const { organization } = this.props; + + let pages = organization.pages || []; + if (organization.canAdmin && organization.adminPages) { + pages = pages.concat(organization.adminPages); + } + + const extension = pages.find(p => p.key === `${pluginKey}/${extensionKey}`); + return extension + ? + : ; + } +} + +const mapStateToProps = (state, ownProps: Props) => ({ + organization: getOrganizationByKey(state, ownProps.params.organizationKey) +}); + +export default connect(mapStateToProps)(OrganizationPageExtension); diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js index 0675c1872d8..3fc479d5b54 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js @@ -22,6 +22,7 @@ import React from 'react'; import { Link } from 'react-router'; import { translate } from '../../../helpers/l10n'; import OrganizationIcon from '../../../components/ui/OrganizationIcon'; +import type { Organization } from '../../../store/organizations/duck'; const ADMIN_PATHS = [ 'edit', @@ -35,22 +36,11 @@ const ADMIN_PATHS = [ export default class OrganizationNavigation extends React.Component { props: { location: { pathname: string }, - organization: { - avatar?: string, - description?: string, - key: string, - name: string, - canAdmin?: boolean, - canDelete?: boolean, - url?: string - } + organization: Organization }; - renderAdministration() { - const { organization, location } = this.props; - - const adminActive = ADMIN_PATHS.some(path => - location.pathname.endsWith(`organizations/${organization.key}/${path}`)); + renderAdministration(adminActive: boolean) { + const { organization } = this.props; return (
  • @@ -58,6 +48,7 @@ export default class OrganizationNavigation extends React.Component { {translate('layout.settings')} 
      + {this.renderAdminExtensions()}
    • {translate('user_groups.page')} @@ -98,12 +89,56 @@ export default class OrganizationNavigation extends React.Component { ); } + renderAdminExtensions() { + const extensions = this.props.organization.adminPages || []; + return extensions.map(this.renderExtension); + } + + renderExtension = (extension: { key: string, name: string }) => { + const { organization } = this.props; + const pathname = `/organizations/${organization.key}/extension/${extension.key}`; + return ( +
    • + + {extension.name} + +
    • + ); + }; + + renderExtensions(moreActive: boolean) { + const extensions = this.props.organization.pages || []; + if (extensions.length > 0) { + return ( +
    • + + {translate('more')}  + +
        + {extensions.map(this.renderExtension)} +
      +
    • + ); + } else { + return null; + } + } + render() { const { organization, location } = this.props; const isHomeActive = location.pathname === `organizations/${organization.key}/projects` || location.pathname === `organizations/${organization.key}/projects/favorite`; + const adminActive = ADMIN_PATHS.some(path => + location.pathname.endsWith(`organizations/${organization.key}/${path}`)); + + const moreActive = !adminActive && location.pathname.includes('/extension/'); + return (
    diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js index 7f91a2f344d..c01ca215da0 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.js +++ b/server/sonar-web/src/main/js/apps/organizations/routes.js @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import OrganizationPage from './components/OrganizationPage'; +import OrganizationPageExtension from '../../app/components/extensions/OrganizationPageExtension'; import OrganizationProjects from './components/OrganizationProjects'; import OrganizationFavoriteProjects from './components/OrganizationFavoriteProjects'; import OrganizationRules from './components/OrganizationRules'; @@ -64,6 +65,10 @@ const routes = [ path: 'quality_profiles', childRoutes: qualityProfilesRoutes }, + { + path: 'extension/:pluginKey/:extensionKey', + component: OrganizationPageExtension + }, { component: OrganizationAdmin, childRoutes: [ diff --git a/server/sonar-web/src/main/js/store/organizations/duck.js b/server/sonar-web/src/main/js/store/organizations/duck.js index 13671285423..b897af7845c 100644 --- a/server/sonar-web/src/main/js/store/organizations/duck.js +++ b/server/sonar-web/src/main/js/store/organizations/duck.js @@ -22,6 +22,7 @@ import { combineReducers } from 'redux'; import { omit, uniq, without } from 'lodash'; export type Organization = { + adminPages?: Array<{ key: string, name: string }>, avatar?: string, canAdmin?: boolean, canDelete?: boolean, @@ -29,6 +30,7 @@ export type Organization = { description?: string, key: string, name: string, + pages?: Array<{ key: string, name: string }>, url?: string }; -- 2.39.5