diff options
22 files changed, 470 insertions, 498 deletions
diff --git a/server/sonar-web/src/main/js/app/types.d.ts b/server/sonar-web/src/main/js/app/types.d.ts index d96c74fb7d3..e3019db6d73 100644 --- a/server/sonar-web/src/main/js/app/types.d.ts +++ b/server/sonar-web/src/main/js/app/types.d.ts @@ -538,6 +538,12 @@ declare namespace T { export type PeriodMode = 'days' | 'date' | 'version' | 'previous_analysis' | 'previous_version'; + export interface Permission { + description: string; + key: string; + name: string; + } + export interface PermissionDefinition { key: string; name: string; 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.tsx index d36592db6be..6a1fad79112 100644 --- 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.tsx @@ -17,8 +17,8 @@ * 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 PropTypes from 'prop-types'; +import * as React from 'react'; +import { Location } from 'history'; import Home from './Home'; import Template from './Template'; import OrganizationHelmet from '../../../components/common/OrganizationHelmet'; @@ -28,14 +28,21 @@ import { sortPermissions, mergePermissionsToTemplates, mergeDefaultsToTemplates import { translate } from '../../../helpers/l10n'; import '../../permissions/styles.css'; -export default class App extends React.PureComponent { - static propTypes = { - location: PropTypes.object.isRequired, - organization: PropTypes.object, - topQualifiers: PropTypes.array.isRequired - }; +interface Props { + location: Location; + organization: T.Organization | undefined; + topQualifiers: string[]; +} - state = { +interface State { + ready: boolean; + permissions: T.Permission[]; + permissionTemplates: T.PermissionTemplate[]; +} + +export default class App extends React.PureComponent<Props, State> { + mounted = false; + state: State = { ready: false, permissions: [], permissionTemplates: [] @@ -62,21 +69,21 @@ export default class App extends React.PureComponent { mergePermissionsToTemplates(r.permissionTemplates, permissions), r.defaultTemplates ); - this.setState({ - ready: true, - permissionTemplates, - permissions - }); + this.setState({ ready: true, permissionTemplates, permissions }); } }); }; - renderTemplate(id) { + renderTemplate(id: string) { if (!this.state.ready) { return null; } const template = this.state.permissionTemplates.find(t => t.id === id); + if (!template) { + return null; + } + return ( <Template organization={this.props.organization} @@ -91,8 +98,8 @@ export default class App extends React.PureComponent { return ( <Home organization={this.props.organization} - permissions={this.state.permissions} permissionTemplates={this.state.permissionTemplates} + permissions={this.state.permissions} ready={this.state.ready} refresh={this.requestPermissions} topQualifiers={this.props.topQualifiers} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.tsx index c66f1f0b31e..c585bd7d620 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.tsx @@ -20,8 +20,8 @@ import { connect } from 'react-redux'; import App from './App'; import forSingleOrganization from '../../organizations/forSingleOrganization'; -import { getAppState } from '../../../store/rootReducer'; +import { getAppState, Store } from '../../../store/rootReducer'; -const mapStateToProps = state => ({ topQualifiers: getAppState(state).qualifiers }); +const mapStateToProps = (state: Store) => ({ topQualifiers: getAppState(state).qualifiers }); export default forSingleOrganization(connect(mapStateToProps)(App)); 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.tsx index d1055d0217b..84bffc6f27e 100644 --- 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.tsx @@ -17,34 +17,28 @@ * 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 PropTypes from 'prop-types'; +import * as React from 'react'; import { sortBy } from 'lodash'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { PermissionTemplateType } from '../propTypes'; -export default class Defaults extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissionTemplate: PermissionTemplateType.isRequired - }; +interface Props { + organization: T.Organization | undefined; + template: T.PermissionTemplate; +} - render() { - const qualifiersToDisplay = - this.props.organization && !this.props.organization.isDefault - ? ['TRK'] - : this.props.permissionTemplate.defaultFor; +export default function Defaults({ organization, template }: Props) { + const qualifiersToDisplay = + organization && !organization.isDefault ? ['TRK'] : template.defaultFor; - const qualifiers = sortBy(qualifiersToDisplay) - .map(qualifier => translate('qualifiers', qualifier)) - .join(', '); + const qualifiers = sortBy(qualifiersToDisplay) + .map(qualifier => translate('qualifiers', qualifier)) + .join(', '); - return ( - <div> - <span className="badge spacer-right"> - {translateWithParameters('permission_template.default_for', qualifiers)} - </span> - </div> - ); - } + return ( + <div> + <span className="badge spacer-right"> + {translateWithParameters('permission_template.default_for', qualifiers)} + </span> + </div> + ); } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx index 3cf1b539834..45bd94bf645 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx @@ -17,42 +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 React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import Helmet from 'react-helmet'; import Header from './Header'; import List from './List'; import { translate } from '../../../helpers/l10n'; -export default class Home extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - topQualifiers: PropTypes.array.isRequired, - permissions: PropTypes.array.isRequired, - permissionTemplates: PropTypes.array.isRequired, - ready: PropTypes.bool.isRequired, - refresh: PropTypes.func.isRequired - }; +interface Props { + organization: T.Organization | undefined; + permissionTemplates: T.PermissionTemplate[]; + permissions: T.Permission[]; + ready: boolean; + refresh: () => Promise<void>; + topQualifiers: string[]; +} - render() { - return ( - <div className="page page-limited"> - <Helmet title={translate('permission_templates.page')} /> +export default function Home(props: Props) { + return ( + <div className="page page-limited"> + <Helmet title={translate('permission_templates.page')} /> - <Header - organization={this.props.organization} - ready={this.props.ready} - refresh={this.props.refresh} - /> + <Header organization={props.organization} ready={props.ready} refresh={props.refresh} /> - <List - organization={this.props.organization} - permissions={this.props.permissions} - permissionTemplates={this.props.permissionTemplates} - refresh={this.props.refresh} - topQualifiers={this.props.topQualifiers} - /> - </div> - ); - } + <List + organization={props.organization} + permissionTemplates={props.permissionTemplates} + permissions={props.permissions} + refresh={props.refresh} + topQualifiers={props.topQualifiers} + /> + </div> + ); } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js deleted file mode 100644 index fdc2ef38b3d..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import ListHeader from './ListHeader'; -import ListItem from './ListItem'; -import { PermissionTemplateType, CallbackType } from '../propTypes'; - -export default class List extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissionTemplates: PropTypes.arrayOf(PermissionTemplateType).isRequired, - permissions: PropTypes.array.isRequired, - topQualifiers: PropTypes.array.isRequired, - refresh: CallbackType - }; - - render() { - const permissionTemplates = this.props.permissionTemplates.map(p => ( - <ListItem - key={p.id} - organization={this.props.organization} - permissionTemplate={p} - refresh={this.props.refresh} - topQualifiers={this.props.topQualifiers} - /> - )); - - return ( - <div className="boxed-group boxed-group-inner"> - <table className="data zebra permissions-table" id="permission-templates"> - <ListHeader organization={this.props.organization} permissions={this.props.permissions} /> - <tbody>{permissionTemplates}</tbody> - </table> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx new file mode 100644 index 00000000000..860d669a28e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import ListHeader from './ListHeader'; +import ListItem from './ListItem'; + +interface Props { + organization: T.Organization | undefined; + permissionTemplates: T.PermissionTemplate[]; + permissions: T.Permission[]; + refresh: () => Promise<void>; + topQualifiers: string[]; +} + +export default function List(props: Props) { + const permissionTemplates = props.permissionTemplates.map(p => ( + <ListItem + key={p.id} + organization={props.organization} + refresh={props.refresh} + template={p} + topQualifiers={props.topQualifiers} + /> + )); + + return ( + <div className="boxed-group boxed-group-inner"> + <table className="data zebra permissions-table" id="permission-templates"> + <ListHeader organization={props.organization} permissions={props.permissions} /> + <tbody>{permissionTemplates}</tbody> + </table> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx index 50807d7d2de..51b77c9dc73 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx @@ -17,21 +17,20 @@ * 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 PropTypes from 'prop-types'; +import * as React from 'react'; import HelpTooltip from '../../../components/controls/HelpTooltip'; import { translate } from '../../../helpers/l10n'; import InstanceMessage from '../../../components/common/InstanceMessage'; import { Alert } from '../../../components/ui/Alert'; -export default class ListHeader extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissions: PropTypes.array.isRequired - }; +interface Props { + organization: T.Organization | undefined; + permissions: T.Permission[]; +} - renderTooltip = permission => - permission.key === 'user' || permission.key === 'codeviewer' ? ( +export default class ListHeader extends React.PureComponent<Props> { + renderTooltip(permission: T.Permission) { + return permission.key === 'user' || permission.key === 'codeviewer' ? ( <div> <InstanceMessage message={translate('projects_role', permission.key, 'desc')} /> <Alert className="spacer-top" variant="warning"> @@ -41,6 +40,7 @@ export default class ListHeader extends React.PureComponent { ) : ( <InstanceMessage message={translate('projects_role', permission.key, 'desc')} /> ); + } render() { const cells = this.props.permissions.map(permission => ( 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 deleted file mode 100644 index 96ac8ffe556..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import NameCell from './NameCell'; -import PermissionCell from './PermissionCell'; -import ActionsCell from './ActionsCell'; -import { PermissionTemplateType, CallbackType } from '../propTypes'; - -export default class ListItem extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissionTemplate: PermissionTemplateType.isRequired, - topQualifiers: PropTypes.array.isRequired, - refresh: CallbackType - }; - - render() { - const permissions = this.props.permissionTemplate.permissions.map(p => ( - <PermissionCell key={p.key} permission={p} /> - )); - - return ( - <tr data-id={this.props.permissionTemplate.id} data-name={this.props.permissionTemplate.name}> - <NameCell - organization={this.props.organization} - permissionTemplate={this.props.permissionTemplate} - topQualifiers={this.props.topQualifiers} - /> - - {permissions} - - <td className="nowrap thin text-right"> - <ActionsCell - organization={this.props.organization} - permissionTemplate={this.props.permissionTemplate} - refresh={this.props.refresh} - topQualifiers={this.props.topQualifiers} - /> - </td> - </tr> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx new file mode 100644 index 00000000000..9769bfbdea5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import NameCell from './NameCell'; +import PermissionCell from './PermissionCell'; +import ActionsCell from './ActionsCell'; + +interface Props { + organization: T.Organization | undefined; + refresh: () => Promise<void>; + template: T.PermissionTemplate; + topQualifiers: string[]; +} + +export default function ListItem(props: Props) { + const permissions = props.template.permissions.map(p => ( + <PermissionCell key={p.key} permission={p} /> + )); + + return ( + <tr data-id={props.template.id} data-name={props.template.name}> + <NameCell organization={props.organization} template={props.template} /> + + {permissions} + + <td className="nowrap thin text-right"> + <ActionsCell + organization={props.organization} + permissionTemplate={props.template} + refresh={props.refresh} + topQualifiers={props.topQualifiers} + /> + </td> + </tr> + ); +} 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 deleted file mode 100644 index afeae1c6a09..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; -import Defaults from './Defaults'; -import { PermissionTemplateType } from '../propTypes'; - -export default class NameCell extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissionTemplate: PermissionTemplateType.isRequired - }; - - render() { - const { permissionTemplate: t, organization } = this.props; - - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; - - return ( - <td> - <Link to={{ pathname, query: { id: t.id } }}> - <strong className="js-name">{t.name}</strong> - </Link> - - {t.defaultFor.length > 0 && ( - <div className="spacer-top js-defaults"> - <Defaults - organization={organization} - permissionTemplate={this.props.permissionTemplate} - /> - </div> - )} - - {!!t.description && <div className="spacer-top js-description">{t.description}</div>} - - {!!t.projectKeyPattern && ( - <div className="spacer-top js-project-key-pattern"> - Project Key Pattern: <code>{t.projectKeyPattern}</code> - </div> - )} - </td> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx new file mode 100644 index 00000000000..e295ec13074 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import { Link } from 'react-router'; +import Defaults from './Defaults'; + +interface Props { + organization: T.Organization | undefined; + template: T.PermissionTemplate; +} + +export default function NameCell({ template, organization }: Props) { + const pathname = organization + ? `/organizations/${organization.key}/permission_templates` + : '/permission_templates'; + + return ( + <td> + <Link to={{ pathname, query: { id: template.id } }}> + <strong className="js-name">{template.name}</strong> + </Link> + + {template.defaultFor.length > 0 && ( + <div className="spacer-top js-defaults"> + <Defaults organization={organization} template={template} /> + </div> + )} + + {!!template.description && ( + <div className="spacer-top js-description">{template.description}</div> + )} + + {!!template.projectKeyPattern && ( + <div className="spacer-top js-project-key-pattern"> + Project Key Pattern: <code>{template.projectKeyPattern}</code> + </div> + )} + </td> + ); +} 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 deleted file mode 100644 index aa447f2aaf7..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import { PermissionType } from '../propTypes'; -import HelpTooltip from '../../../components/controls/HelpTooltip'; -import { translate } from '../../../helpers/l10n'; -import { isSonarCloud } from '../../../helpers/system'; - -export default class PermissionCell extends React.PureComponent { - static propTypes = { - permission: PermissionType.isRequired - }; - - render() { - const { permission: p } = this.props; - - return ( - <td className="permission-column" data-permission={p.key}> - <div className="permission-column-inner"> - <ul> - {p.withProjectCreator && ( - <li className="little-spacer-bottom display-flex-center"> - {translate('permission_templates.project_creators')} - <HelpTooltip - className="little-spacer-left" - overlay={translate( - isSonarCloud() - ? 'permission_templates.project_creators.explanation.sonarcloud' - : 'permission_templates.project_creators.explanation' - )} - /> - </li> - )} - <li className="little-spacer-bottom"> - <strong>{p.usersCount}</strong> - {' user(s)'} - </li> - <li> - <strong>{p.groupsCount}</strong> - {' group(s)'} - </li> - </ul> - </div> - </td> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.tsx new file mode 100644 index 00000000000..4ba69a9f80f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/PermissionCell.tsx @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import HelpTooltip from '../../../components/controls/HelpTooltip'; +import { translate } from '../../../helpers/l10n'; +import { isSonarCloud } from '../../../helpers/system'; + +interface Props { + permission: { + key: string; + usersCount: number; + groupsCount: number; + withProjectCreator?: boolean; + }; +} + +export default function PermissionCell({ permission: p }: Props) { + return ( + <td className="permission-column" data-permission={p.key}> + <div className="permission-column-inner"> + <ul> + {p.withProjectCreator && ( + <li className="little-spacer-bottom display-flex-center"> + {translate('permission_templates.project_creators')} + <HelpTooltip + className="little-spacer-left" + overlay={translate( + isSonarCloud() + ? 'permission_templates.project_creators.explanation.sonarcloud' + : 'permission_templates.project_creators.explanation' + )} + /> + </li> + )} + <li className="little-spacer-bottom"> + <strong>{p.usersCount}</strong> + {' user(s)'} + </li> + <li> + <strong>{p.groupsCount}</strong> + {' group(s)'} + </li> + </ul> + </div> + </td> + ); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx index 9b1aee495cd..ba5a6f091a7 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx @@ -17,8 +17,7 @@ * 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 PropTypes from 'prop-types'; +import * as React from 'react'; import Helmet from 'react-helmet'; import TemplateHeader from './TemplateHeader'; import TemplateDetails from './TemplateDetails'; @@ -31,21 +30,30 @@ import { import * as api from '../../../api/permissions'; import { translate } from '../../../helpers/l10n'; -export default class Template extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - template: PropTypes.object.isRequired, - refresh: PropTypes.func.isRequired, - topQualifiers: PropTypes.array.isRequired - }; +interface Props { + organization: T.Organization | undefined; + refresh: () => void; + template: T.PermissionTemplate; + topQualifiers: string[]; +} - state = { - loading: false, - users: [], +interface State { + filter: string; + groups: T.PermissionGroup[]; + loading: boolean; + query: string; + selectedPermission?: string; + users: T.PermissionUser[]; +} + +export default class Template extends React.PureComponent<Props, State> { + mounted = false; + state: State = { + filter: 'all', groups: [], + loading: false, query: '', - filter: 'all', - selectedPermission: null + users: [] }; componentDidMount() { @@ -57,7 +65,7 @@ export default class Template extends React.PureComponent { this.mounted = false; } - requestHolders = realQuery => { + requestHolders = (realQuery?: string) => { this.setState({ loading: true }); const { template } = this.props; @@ -89,13 +97,13 @@ export default class Template extends React.PureComponent { }); }; - handleToggleUser = (user, permission) => { + handleToggleUser = (user: T.PermissionUser, permission: string) => { if (user.login === '<creator>') { return this.handleToggleProjectCreator(user, permission); } const { template, organization } = this.props; const hasPermission = user.permissions.includes(permission); - const data = { + const data: { templateId: string; login: string; permission: string; organization?: string } = { templateId: template.id, login: user.login, permission @@ -109,7 +117,7 @@ export default class Template extends React.PureComponent { return request.then(() => this.requestHolders()).then(this.props.refresh); }; - handleToggleProjectCreator = (user, permission) => { + handleToggleProjectCreator = (user: T.PermissionUser, permission: string) => { const { template } = this.props; const hasPermission = user.permissions.includes(permission); const request = hasPermission @@ -118,7 +126,7 @@ export default class Template extends React.PureComponent { return request.then(() => this.requestHolders()).then(this.props.refresh); }; - handleToggleGroup = (group, permission) => { + handleToggleGroup = (group: T.PermissionGroup, permission: string) => { const { template, organization } = this.props; const hasPermission = group.permissions.includes(permission); const data = { @@ -135,24 +143,24 @@ export default class Template extends React.PureComponent { return request.then(() => this.requestHolders()).then(this.props.refresh); }; - handleSearch = query => { + handleSearch = (query: string) => { this.setState({ query }); this.requestHolders(query); }; - handleFilter = filter => { + handleFilter = (filter: string) => { this.setState({ filter }, this.requestHolders); }; - handleSelectPermission = selectedPermission => { + handleSelectPermission = (selectedPermission: string) => { if (selectedPermission === this.state.selectedPermission) { - this.setState({ selectedPermission: null }, this.requestHolders); + this.setState({ selectedPermission: undefined }, this.requestHolders); } else { this.setState({ selectedPermission }, this.requestHolders); } }; - shouldDisplayCreator = creatorPermissions => { + shouldDisplayCreator = (creatorPermissions: string[]) => { const { filter, query, selectedPermission } = this.state; const CREATOR_NAME = translate('permission_templates.project_creators'); @@ -161,7 +169,7 @@ export default class Template extends React.PureComponent { const matchQuery = !query || CREATOR_NAME.toLocaleLowerCase().includes(query.toLowerCase()); const matchPermission = - selectedPermission == null || creatorPermissions.includes(selectedPermission); + selectedPermission === undefined || creatorPermissions.includes(selectedPermission); return !isFiltered && matchQuery && matchPermission; }; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.js b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.js deleted file mode 100644 index 8370a72b902..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import Defaults from './Defaults'; - -export default class TemplateDetails extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - template: PropTypes.object.isRequired - }; - - render() { - const { template } = this.props; - - return ( - <div className="big-spacer-bottom"> - {template.defaultFor.length > 0 && ( - <div className="spacer-top js-defaults"> - <Defaults organization={this.props.organization} permissionTemplate={template} /> - </div> - )} - - {!!template.description && ( - <div className="spacer-top js-description">{template.description}</div> - )} - - {!!template.projectKeyPattern && ( - <div className="spacer-top js-project-key-pattern"> - Project Key Pattern: <code>{template.projectKeyPattern}</code> - </div> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx new file mode 100644 index 00000000000..592c0cf4dfe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import Defaults from './Defaults'; + +interface Props { + organization: T.Organization | undefined; + template: T.PermissionTemplate; +} + +export default function TemplateDetails({ organization, template }: Props) { + return ( + <div className="big-spacer-bottom"> + {template.defaultFor.length > 0 && ( + <div className="spacer-top js-defaults"> + <Defaults organization={organization} template={template} /> + </div> + )} + + {!!template.description && ( + <div className="spacer-top js-description">{template.description}</div> + )} + + {!!template.projectKeyPattern && ( + <div className="spacer-top js-project-key-pattern"> + Project Key Pattern: <code>{template.projectKeyPattern}</code> + </div> + )} + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js deleted file mode 100644 index 127600700a4..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; -import ActionsCell from './ActionsCell'; -import { translate } from '../../../helpers/l10n'; - -export default class TemplateHeader extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - template: PropTypes.object.isRequired, - loading: PropTypes.bool.isRequired, - refresh: PropTypes.func.isRequired, - topQualifiers: PropTypes.array.isRequired - }; - - render() { - const { template, organization } = this.props; - - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; - - return ( - <header className="page-header" id="project-permissions-header"> - <div className="note spacer-bottom"> - <Link className="text-muted" to={pathname}> - {translate('permission_templates.page')} - </Link> - </div> - - <h1 className="page-title">{template.name}</h1> - - {this.props.loading && <i className="spinner" />} - - <div className="pull-right"> - <ActionsCell - fromDetails={true} - organization={this.props.organization} - permissionTemplate={this.props.template} - refresh={this.props.refresh} - topQualifiers={this.props.topQualifiers} - /> - </div> - </header> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx new file mode 100644 index 00000000000..ced9a28213b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import { Link } from 'react-router'; +import ActionsCell from './ActionsCell'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + loading: boolean; + organization: T.Organization | undefined; + refresh: () => void; + template: T.PermissionTemplate; + topQualifiers: string[]; +} + +export default function TemplateHeader(props: Props) { + const { template, organization } = props; + + const pathname = organization + ? `/organizations/${organization.key}/permission_templates` + : '/permission_templates'; + + return ( + <header className="page-header" id="project-permissions-header"> + <div className="note spacer-bottom"> + <Link className="text-muted" to={pathname}> + {translate('permission_templates.page')} + </Link> + </div> + + <h1 className="page-title">{template.name}</h1> + + {props.loading && <i className="spinner" />} + + <div className="pull-right"> + <ActionsCell + fromDetails={true} + organization={organization} + permissionTemplate={template} + refresh={props.refresh} + topQualifiers={props.topQualifiers} + /> + </div> + </header> + ); +} 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.tsx index ed819e648b3..ffcb41027a3 100644 --- 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.tsx @@ -17,11 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import * as React from 'react'; import { shallow } from 'enzyme'; -import React from 'react'; import Defaults from '../Defaults'; -const SAMPLE = { +const SAMPLE: T.PermissionTemplate = { + createdAt: '2018-01-01', + defaultFor: [], id: 'id', name: 'name', permissions: [] @@ -29,26 +31,26 @@ const SAMPLE = { it('should render one qualifier', () => { const sample = { ...SAMPLE, defaultFor: ['DEV'] }; - const output = shallow(<Defaults permissionTemplate={sample} />); + const output = shallow(<Defaults organization={undefined} template={sample} />); expect(output).toMatchSnapshot(); }); it('should render several qualifiers', () => { const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const output = shallow(<Defaults permissionTemplate={sample} />); + const output = shallow(<Defaults organization={undefined} template={sample} />); expect(output).toMatchSnapshot(); }); it('should render several qualifiers for default organization', () => { const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const organization = { isDefault: true }; - const output = shallow(<Defaults organization={organization} permissionTemplate={sample} />); + const organization: T.Organization = { isDefault: true, key: 'org', name: 'org' }; + const output = shallow(<Defaults organization={organization} template={sample} />); expect(output).toMatchSnapshot(); }); it('should render only projects for custom organization', () => { const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const organization = { isDefault: false }; - const output = shallow(<Defaults organization={organization} permissionTemplate={sample} />); + const organization: T.Organization = { isDefault: false, key: 'org', name: 'org' }; + const output = shallow(<Defaults organization={organization} template={sample} />); expect(output).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.js.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap index 9319abafef2..9319abafef2 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.js.snap +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/permission-templates/utils.js b/server/sonar-web/src/main/js/apps/permission-templates/utils.ts index e7afc8ed47e..52b92460684 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/utils.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/utils.ts @@ -28,22 +28,14 @@ export const PERMISSIONS_ORDER = [ 'scan' ]; -/** - * Sort list of permissions based on predefined order - * @param {Array} permissions - * @returns {Array} - */ -export function sortPermissions(permissions) { +export function sortPermissions(permissions: T.Permission[]) { return sortBy(permissions, p => PERMISSIONS_ORDER.indexOf(p.key)); } -/** - * Populate permissions' details in the list of permission templates - * @param {Array} permissionTemplates - * @param {Array} basePermissions - * @returns {Array} - */ -export function mergePermissionsToTemplates(permissionTemplates, basePermissions) { +export function mergePermissionsToTemplates( + permissionTemplates: T.PermissionTemplate[], + basePermissions: T.Permission[] +): T.PermissionTemplate[] { 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 @@ -58,15 +50,12 @@ export function mergePermissionsToTemplates(permissionTemplates, basePermissions }); } -/** - * Mark default templates - * @param {Array} permissionTemplates - * @param {Array} defaultTemplates - * @returns {Array} - */ -export function mergeDefaultsToTemplates(permissionTemplates, defaultTemplates = []) { +export function mergeDefaultsToTemplates( + permissionTemplates: T.PermissionTemplate[], + defaultTemplates: Array<{ templateId: string; qualifier: string }> = [] +): T.PermissionTemplate[] { return permissionTemplates.map(permissionTemplate => { - const defaultFor = []; + const defaultFor: string[] = []; defaultTemplates.forEach(defaultTemplate => { if (defaultTemplate.templateId === permissionTemplate.id) { |