Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / user-profile / user-profile-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
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";
18
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';
25
26 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
27
28 export const getCurrentUserProfilePanelUuid = getProperty<string>(USER_PROFILE_PANEL_ID);
29 export const getUserProfileIsInaccessible = getProperty<boolean>(IS_PROFILE_INACCESSIBLE);
30
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;
37         if (uuid) {
38             await dispatch(propertiesActions.SET_PROPERTY({ key: USER_PROFILE_PANEL_ID, value: uuid }));
39             try {
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());
44             } catch (e) {
45                 if (e.status === 404) {
46                     await dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: true }));
47                     dispatch(reset(USER_PROFILE_FORM));
48                 } else {
49                     dispatch(snackbarActions.OPEN_SNACKBAR({
50                         message: 'Could not load user profile',
51                         kind: SnackbarKind.ERROR
52                     }));
53                 }
54             }
55         }
56     }
57
58 export const saveEditedUser = (resource: any) =>
59     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
60         try {
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 }));
65         } catch (e) {
66             dispatch(snackbarActions.OPEN_SNACKBAR({
67                 message: "Could not update profile",
68                 kind: SnackbarKind.ERROR,
69             }));
70         }
71     };
72
73 export const openSetupDialog = (uuid: string) =>
74     (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
75         dispatch(dialogActions.OPEN_DIALOG({
76             id: SETUP_DIALOG,
77             data: {
78                 title: 'Setup user',
79                 text: 'Are you sure you want to setup this user?',
80                 confirmButtonLabel: 'Confirm',
81                 uuid
82             }
83         }));
84     };
85
86 export const openActivateDialog = (uuid: string) =>
87     (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
88         dispatch(dialogActions.OPEN_DIALOG({
89             id: ACTIVATE_DIALOG,
90             data: {
91                 title: 'Activate user',
92                 text: 'Are you sure you want to activate this user?',
93                 confirmButtonLabel: 'Confirm',
94                 uuid
95             }
96         }));
97     };
98
99 export const openDeactivateDialog = (uuid: string) =>
100     (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
101         dispatch(dialogActions.OPEN_DIALOG({
102             id: DEACTIVATE_DIALOG,
103             data: {
104                 title: 'Deactivate user',
105                 text: 'Are you sure you want to deactivate this user?',
106                 confirmButtonLabel: 'Confirm',
107                 uuid
108             }
109         }));
110     };
111
112 export const setup = (uuid: string) =>
113     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
114         try {
115             const resources = await services.userService.setup(uuid);
116             dispatch(updateResources(resources.items));
117
118             // Refresh data explorer
119             dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
120
121             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been setup", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
122         } catch (e) {
123             dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
124         } finally {
125             dispatch(dialogActions.CLOSE_DIALOG({ id: SETUP_DIALOG }));
126         }
127     };
128
129 export const activate = (uuid: string) =>
130     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
131         try {
132             const user = await services.userService.activate(uuid);
133             dispatch(updateResources([user]));
134
135             // Refresh data explorer
136             dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
137
138             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been activated", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
139         } catch (e) {
140             dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
141         }
142     };
143
144 export const deactivate = (uuid: string) =>
145     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
146         try {
147             const { resources, auth } = getState();
148             // Call unsetup
149             const user = await services.userService.unsetup(uuid);
150             dispatch(updateResources([user]));
151
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
159             )(resources);
160             // Remove all users membership locally
161             dispatch<any>(deleteResources(memberships.map(link => link.uuid)));
162
163             // Refresh data explorer
164             dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
165
166             dispatch(snackbarActions.OPEN_SNACKBAR({
167                 message: "User has been deactivated.",
168                 hideDuration: 2000,
169                 kind: SnackbarKind.SUCCESS
170             }));
171         } catch (e) {
172             dispatch(snackbarActions.OPEN_SNACKBAR({
173                 message: "Could not deactivate user",
174                 kind: SnackbarKind.ERROR,
175             }));
176         }
177     };