diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-04-11 14:46:00 +0200 |
---|---|---|
committer | Grégoire Aubert <gregaubert@users.noreply.github.com> | 2017-04-12 16:36:29 +0200 |
commit | b3c2e0d524cd316326d642247972e12d00de0d12 (patch) | |
tree | d770c08f3af6a39c4b528060925f1847f9684aa2 /server/sonar-web | |
parent | d2aef03a3830823ae277ad935e01194bd99cb0df (diff) | |
download | sonarqube-b3c2e0d524cd316326d642247972e12d00de0d12.tar.gz sonarqube-b3c2e0d524cd316326d642247972e12d00de0d12.zip |
SONAR-9110 Add organization page extensions
Diffstat (limited to 'server/sonar-web')
5 files changed, 123 insertions, 16 deletions
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<GetOrganizationType> => { 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 + ? <Extension extension={extension} options={{ organization }} /> + : <ExtensionNotFound />; + } +} + +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 ( <li className={adminActive ? 'active' : ''}> @@ -58,6 +48,7 @@ export default class OrganizationNavigation extends React.Component { {translate('layout.settings')} <i className="icon-dropdown" /> </a> <ul className="dropdown-menu"> + {this.renderAdminExtensions()} <li> <Link to={`/organizations/${organization.key}/groups`} activeClassName="active"> {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 ( + <li key={extension.key}> + <Link to={pathname} activeClassName="active"> + {extension.name} + </Link> + </li> + ); + }; + + renderExtensions(moreActive: boolean) { + const extensions = this.props.organization.pages || []; + if (extensions.length > 0) { + return ( + <li className={moreActive ? 'active' : ''}> + <a + className="dropdown-toggle" + id="organization-navigation-more" + data-toggle="dropdown" + href="#"> + {translate('more')} <i className="icon-dropdown" /> + </a> + <ul className="dropdown-menu"> + {extensions.map(this.renderExtension)} + </ul> + </li> + ); + } 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 ( <nav className="navbar navbar-context page-container" id="context-navigation"> <div className="navbar-context-inner"> @@ -166,7 +201,8 @@ export default class OrganizationNavigation extends React.Component { {translate('coding_rules.page')} </Link> </li> - {organization.canAdmin && this.renderAdministration()} + {this.renderExtensions(moreActive)} + {organization.canAdmin && this.renderAdministration(adminActive)} </ul> </div> </div> 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'; @@ -65,6 +66,10 @@ const routes = [ childRoutes: qualityProfilesRoutes }, { + path: 'extension/:pluginKey/:extensionKey', + component: OrganizationPageExtension + }, + { component: OrganizationAdmin, childRoutes: [ { path: 'delete', component: OrganizationDelete }, 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 }; |