aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/App.tsx19
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/Header.tsx97
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/__tests__/App-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap20
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
5 files changed, 79 insertions, 62 deletions
diff --git a/server/sonar-web/src/main/js/apps/groups/components/App.tsx b/server/sonar-web/src/main/js/apps/groups/components/App.tsx
index e282044653b..31cd060f6c9 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/App.tsx
@@ -20,13 +20,14 @@
import { omit } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
+import { getSystemInfo } from '../../../api/system';
import { createGroup, deleteGroup, searchUsersGroups, updateGroup } from '../../../api/user_groups';
import ListFooter from '../../../components/controls/ListFooter';
import SearchBox from '../../../components/controls/SearchBox';
import Suggestions from '../../../components/embed-docs-modal/Suggestions';
import { translate } from '../../../helpers/l10n';
import { omitNil } from '../../../helpers/request';
-import { Group, Paging } from '../../../types/types';
+import { Group, Paging, SysInfoCluster } from '../../../types/types';
import DeleteForm from './DeleteForm';
import Form from './Form';
import Header from './Header';
@@ -39,6 +40,7 @@ interface State {
loading: boolean;
paging?: Paging;
query: string;
+ manageProvider?: string;
}
export default class App extends React.PureComponent<{}, State> {
@@ -48,6 +50,7 @@ export default class App extends React.PureComponent<{}, State> {
componentDidMount() {
this.mounted = true;
this.fetchGroups();
+ this.fetchManageInstance();
}
componentWillUnmount() {
@@ -62,6 +65,15 @@ export default class App extends React.PureComponent<{}, State> {
});
};
+ async fetchManageInstance() {
+ const info = (await getSystemInfo()) as SysInfoCluster;
+ if (this.mounted) {
+ this.setState({
+ manageProvider: info.System['External Users and Groups Provisioning'],
+ });
+ }
+ }
+
stopLoading = () => {
if (this.mounted) {
this.setState({ loading: false });
@@ -188,7 +200,8 @@ export default class App extends React.PureComponent<{}, State> {
};
render() {
- const { editedGroup, groupToBeDeleted, groups, loading, paging, query } = this.state;
+ const { editedGroup, groupToBeDeleted, groups, loading, paging, query, manageProvider } =
+ this.state;
const showAnyone = 'anyone'.includes(query.toLowerCase());
@@ -197,7 +210,7 @@ export default class App extends React.PureComponent<{}, State> {
<Suggestions suggestions="user_groups" />
<Helmet defer={false} title={translate('user_groups.page')} />
<main className="page page-limited" id="groups-page">
- <Header onCreate={this.handleCreate} />
+ <Header onCreate={this.handleCreate} manageProvider={manageProvider} />
<SearchBox
className="big-spacer-bottom"
diff --git a/server/sonar-web/src/main/js/apps/groups/components/Header.tsx b/server/sonar-web/src/main/js/apps/groups/components/Header.tsx
index 678446455bd..81e71d6cd8d 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/Header.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/Header.tsx
@@ -18,67 +18,64 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import DocLink from '../../../components/common/DocLink';
import { Button } from '../../../components/controls/buttons';
+import { Alert } from '../../../components/ui/Alert';
import { translate } from '../../../helpers/l10n';
import Form from './Form';
interface Props {
onCreate: (data: { description: string; name: string }) => Promise<void>;
+ manageProvider?: string;
}
-interface State {
- createModal: boolean;
-}
-
-export default class Header extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { createModal: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleCreateClick = () => {
- this.setState({ createModal: true });
- };
-
- handleClose = () => {
- if (this.mounted) {
- this.setState({ createModal: false });
- }
- };
-
- handleSubmit = (data: { name: string; description: string }) => {
- return this.props.onCreate(data);
- };
+export default function Header(props: Props) {
+ const { manageProvider } = props;
+ const [createModal, setCreateModal] = React.useState(false);
- render() {
- return (
- <>
- <header className="page-header" id="groups-header">
- <h1 className="page-title">{translate('user_groups.page')}</h1>
+ return (
+ <>
+ <div className="page-header" id="groups-header">
+ <h2 className="page-title">{translate('user_groups.page')}</h2>
- <div className="page-actions">
- <Button id="groups-create" onClick={this.handleCreateClick}>
- {translate('groups.create_group')}
- </Button>
- </div>
+ <div className="page-actions">
+ <Button
+ id="groups-create"
+ disabled={manageProvider !== undefined}
+ onClick={() => setCreateModal(true)}
+ >
+ {translate('groups.create_group')}
+ </Button>
+ </div>
+ {manageProvider === undefined ? (
<p className="page-description">{translate('user_groups.page.description')}</p>
- </header>
- {this.state.createModal && (
- <Form
- confirmButtonText={translate('create')}
- header={translate('groups.create_group')}
- onClose={this.handleClose}
- onSubmit={this.handleSubmit}
- />
+ ) : (
+ <Alert className="page-description max-width-100 width-100" variant="info">
+ <FormattedMessage
+ defaultMessage={translate('user_groups.page.managed_description')}
+ id="user_groups.page.managed_description"
+ values={{
+ provider: manageProvider,
+ link: (
+ <DocLink to="/instance-administration/authentication/overview/">
+ {translate('documentation')}
+ </DocLink>
+ ),
+ }}
+ />
+ </Alert>
)}
- </>
- );
- }
+ </div>
+ {createModal && (
+ <Form
+ confirmButtonText={translate('create')}
+ header={translate('groups.create_group')}
+ onClose={() => setCreateModal(false)}
+ onSubmit={props.onCreate}
+ />
+ )}
+ </>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/groups/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/groups/components/__tests__/App-test.tsx
index 3c64bcf3a03..8858be2fd5e 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/__tests__/App-test.tsx
@@ -57,6 +57,10 @@ jest.mock('../../../../api/user_groups', () => ({
updateGroup: jest.fn().mockResolvedValue({}),
}));
+jest.mock('../../../../api/system', () => ({
+ getSystemInfo: jest.fn().mockResolvedValue({ System: {} }),
+}));
+
beforeEach(() => {
jest.clearAllMocks();
});
diff --git a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap
index 3a12724c7c9..ba792ca89fc 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/Header-test.tsx.snap
@@ -2,19 +2,20 @@
exports[`should create new group 1`] = `
<Fragment>
- <header
+ <div
className="page-header"
id="groups-header"
>
- <h1
+ <h2
className="page-title"
>
user_groups.page
- </h1>
+ </h2>
<div
className="page-actions"
>
<Button
+ disabled={false}
id="groups-create"
onClick={[Function]}
>
@@ -26,25 +27,26 @@ exports[`should create new group 1`] = `
>
user_groups.page.description
</p>
- </header>
+ </div>
</Fragment>
`;
exports[`should create new group 2`] = `
<Fragment>
- <header
+ <div
className="page-header"
id="groups-header"
>
- <h1
+ <h2
className="page-title"
>
user_groups.page
- </h1>
+ </h2>
<div
className="page-actions"
>
<Button
+ disabled={false}
id="groups-create"
onClick={[Function]}
>
@@ -56,12 +58,12 @@ exports[`should create new group 2`] = `
>
user_groups.page.description
</p>
- </header>
+ </div>
<Form
confirmButtonText="create"
header="groups.create_group"
onClose={[Function]}
- onSubmit={[Function]}
+ onSubmit={[MockFunction]}
/>
</Fragment>
`;
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index f0c58f35917..5ea15bc32c3 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4392,6 +4392,7 @@ users.change_admin_password.form.continue_to_app=Continue to SonarQube
#------------------------------------------------------------------------------
user_groups.page=Groups
user_groups.page.description=Create and administer groups of users.
+user_groups.page.managed_description=Your instance is managed by {provider}. No modification is allowed. You can still delete local groups. All other operations should be done on your identity provider. See {link} for help managing groups.
user_groups.anyone.description=Anybody who browses the application belongs to this group. If authentication is not enforced, assigned permissions also apply to non-authenticated users.
groups.delete_group=Delete Group
groups.delete_group.confirmation=Are you sure you want to delete "{0}"?