1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
4 import { RootState } from "store/store";
5 import { Dispatch } from 'redux';
6 import { initialize, reset } from "redux-form";
7 import { ServiceRepository } from "services/services";
8 import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action";
9 import { propertiesActions } from 'store/properties/properties-actions';
10 import { getProperty } from 'store/properties/properties';
11 import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
12 import { deleteResources, updateResources } from "store/resources/resources-actions";
13 import { dialogActions } from "store/dialog/dialog-actions";
14 import { filterResources } from "store/resources/resources";
15 import { ResourceKind } from "models/resource";
16 import { LinkClass, LinkResource } from "models/link";
17 import { BuiltinGroups, getBuiltinGroupUuid } from "models/group";
19 export const USER_PROFILE_PANEL_ID = 'userProfilePanel';
20 export const USER_PROFILE_FORM = 'userProfileForm';
21 export const DEACTIVATE_DIALOG = 'deactivateDialog';
22 export const SETUP_DIALOG = 'setupDialog';
23 export const ACTIVATE_DIALOG = 'activateDialog';
24 export const IS_PROFILE_INACCESSIBLE = 'isProfileInaccessible';
26 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
28 export const getCurrentUserProfilePanelUuid = getProperty<string>(USER_PROFILE_PANEL_ID);
29 export const getUserProfileIsInaccessible = getProperty<boolean>(IS_PROFILE_INACCESSIBLE);
31 export const loadUserProfilePanel = (userUuid?: string) =>
32 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
33 // Reset isInacessible to ensure error screen is hidden
34 dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: false }));
35 // Get user uuid from route or use current user uuid
36 const uuid = userUuid || getState().auth.user?.uuid;
38 await dispatch(propertiesActions.SET_PROPERTY({ key: USER_PROFILE_PANEL_ID, value: uuid }));
40 const user = await services.userService.get(uuid, false, ["uuid", "first_name", "last_name", "email", "username", "prefs", "is_admin", "is_active"]);
41 dispatch(initialize(USER_PROFILE_FORM, user));
42 dispatch(updateResources([user]));
43 dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
45 if (e.status === 404) {
46 await dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: true }));
47 dispatch(reset(USER_PROFILE_FORM));
49 dispatch(snackbarActions.OPEN_SNACKBAR({
50 message: 'Could not load user profile',
51 kind: SnackbarKind.ERROR
58 export const saveEditedUser = (resource: any) =>
59 async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
61 const user = await services.userService.update(resource.uuid, resource);
62 dispatch(updateResources([user]));
63 dispatch(initialize(USER_PROFILE_FORM, user));
64 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Profile has been updated.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
66 dispatch(snackbarActions.OPEN_SNACKBAR({
67 message: "Could not update profile",
68 kind: SnackbarKind.ERROR,
73 export const openSetupDialog = (uuid: string) =>
74 (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
75 dispatch(dialogActions.OPEN_DIALOG({
79 text: 'Are you sure you want to setup this user?',
80 confirmButtonLabel: 'Confirm',
86 export const openActivateDialog = (uuid: string) =>
87 (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
88 dispatch(dialogActions.OPEN_DIALOG({
91 title: 'Activate user',
92 text: 'Are you sure you want to activate this user?',
93 confirmButtonLabel: 'Confirm',
99 export const openDeactivateDialog = (uuid: string) =>
100 (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
101 dispatch(dialogActions.OPEN_DIALOG({
102 id: DEACTIVATE_DIALOG,
104 title: 'Deactivate user',
105 text: 'Are you sure you want to deactivate this user?',
106 confirmButtonLabel: 'Confirm',
112 export const setup = (uuid: string) =>
113 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
115 const resources = await services.userService.setup(uuid);
116 dispatch(updateResources(resources.items));
118 // Refresh data explorer
119 dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
121 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been setup", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
123 dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
125 dispatch(dialogActions.CLOSE_DIALOG({ id: SETUP_DIALOG }));
129 export const activate = (uuid: string) =>
130 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
132 const user = await services.userService.activate(uuid);
133 dispatch(updateResources([user]));
135 // Refresh data explorer
136 dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
138 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been activated", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
140 dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
144 export const deactivate = (uuid: string) =>
145 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
147 const { resources, auth } = getState();
149 const user = await services.userService.unsetup(uuid);
150 dispatch(updateResources([user]));
152 // Find and remove all users membership
153 const allUsersGroupUuid = getBuiltinGroupUuid(auth.localCluster, BuiltinGroups.ALL);
154 const memberships = filterResources((resource: LinkResource) =>
155 resource.kind === ResourceKind.LINK &&
156 resource.linkClass === LinkClass.PERMISSION &&
157 resource.headUuid === allUsersGroupUuid &&
158 resource.tailUuid === uuid
160 // Remove all users membership locally
161 dispatch<any>(deleteResources(memberships.map(link => link.uuid)));
163 // Refresh data explorer
164 dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
166 dispatch(snackbarActions.OPEN_SNACKBAR({
167 message: "User has been deactivated.",
169 kind: SnackbarKind.SUCCESS
172 dispatch(snackbarActions.OPEN_SNACKBAR({
173 message: "Could not deactivate user",
174 kind: SnackbarKind.ERROR,