3 namespace BookStack\Users\Controllers;
5 use BookStack\Access\SocialDriverManager;
6 use BookStack\Http\Controller;
7 use BookStack\Permissions\Permission;
8 use BookStack\Permissions\PermissionApplicator;
9 use BookStack\Settings\UserNotificationPreferences;
10 use BookStack\Settings\UserShortcutMap;
11 use BookStack\Uploads\ImageRepo;
12 use BookStack\Users\UserRepo;
14 use Illuminate\Http\Request;
15 use Illuminate\Validation\Rules\Password;
17 class UserAccountController extends Controller
19 public function __construct(
20 protected UserRepo $userRepo,
22 $this->middleware(function (Request $request, Closure $next) {
23 $this->preventGuestAccess();
24 return $next($request);
29 * Redirect the root my-account path to the main/first category.
30 * Required as a controller method, instead of the Route::redirect helper,
31 * to ensure the URL is generated correctly.
33 public function redirect()
35 return redirect('/my-account/profile');
39 * Show the profile form interface.
41 public function showProfile()
43 $this->setPageTitle(trans('preferences.profile'));
45 return view('users.account.profile', [
47 'category' => 'profile',
52 * Handle the submission of the user profile form.
54 public function updateProfile(Request $request, ImageRepo $imageRepo)
56 $this->preventAccessInDemoMode();
59 $validated = $this->validate($request, [
60 'name' => ['min:2', 'max:100'],
61 'email' => ['min:2', 'email', 'unique:users,email,' . $user->id],
62 'language' => ['string', 'max:15', 'alpha_dash'],
63 'profile_image' => array_merge(['nullable'], $this->getImageValidationRules()),
66 $this->userRepo->update($user, $validated, userCan(\BookStack\Permissions\Permission::UsersManage));
68 // Save profile image if in request
69 if ($request->hasFile('profile_image')) {
70 $imageUpload = $request->file('profile_image');
71 $imageRepo->destroyImage($user->avatar);
72 $image = $imageRepo->saveNew($imageUpload, 'user', $user->id);
73 $user->image_id = $image->id;
77 // Delete the profile image if reset option is in request
78 if ($request->has('profile_image_reset')) {
79 $imageRepo->destroyImage($user->avatar);
84 return redirect('/my-account/profile');
88 * Show the user-specific interface shortcuts.
90 public function showShortcuts()
92 $shortcuts = UserShortcutMap::fromUserPreferences();
93 $enabled = setting()->getForCurrentUser('ui-shortcuts-enabled', false);
95 $this->setPageTitle(trans('preferences.shortcuts_interface'));
97 return view('users.account.shortcuts', [
98 'category' => 'shortcuts',
99 'shortcuts' => $shortcuts,
100 'enabled' => $enabled,
105 * Update the user-specific interface shortcuts.
107 public function updateShortcuts(Request $request)
109 $enabled = $request->get('enabled') === 'true';
110 $providedShortcuts = $request->get('shortcut', []);
111 $shortcuts = new UserShortcutMap($providedShortcuts);
113 setting()->putForCurrentUser('ui-shortcuts', $shortcuts->toJson());
114 setting()->putForCurrentUser('ui-shortcuts-enabled', $enabled);
116 $this->showSuccessNotification(trans('preferences.shortcuts_update_success'));
118 return redirect('/my-account/shortcuts');
122 * Show the notification preferences for the current user.
124 public function showNotifications(PermissionApplicator $permissions)
126 $this->checkPermission(Permission::ReceiveNotifications);
128 $preferences = (new UserNotificationPreferences(user()));
130 $query = user()->watches()->getQuery();
131 $query = $permissions->restrictEntityRelationQuery($query, 'watches', 'watchable_id', 'watchable_type');
132 $query = $permissions->filterDeletedFromEntityRelationQuery($query, 'watches', 'watchable_id', 'watchable_type');
133 $watches = $query->with('watchable')->paginate(20);
135 $this->setPageTitle(trans('preferences.notifications'));
136 return view('users.account.notifications', [
137 'category' => 'notifications',
138 'preferences' => $preferences,
139 'watches' => $watches,
144 * Update the notification preferences for the current user.
146 public function updateNotifications(Request $request)
148 $this->preventAccessInDemoMode();
149 $this->checkPermission(Permission::ReceiveNotifications);
150 $data = $this->validate($request, [
151 'preferences' => ['required', 'array'],
152 'preferences.*' => ['required', 'string'],
155 $preferences = (new UserNotificationPreferences(user()));
156 $preferences->updateFromSettingsArray($data['preferences']);
157 $this->showSuccessNotification(trans('preferences.notifications_update_success'));
159 return redirect('/my-account/notifications');
163 * Show the view for the "Access & Security" account options.
165 public function showAuth(SocialDriverManager $socialDriverManager)
167 $mfaMethods = user()->mfaValues()->get()->groupBy('method');
169 $this->setPageTitle(trans('preferences.auth'));
171 return view('users.account.auth', [
172 'category' => 'auth',
173 'mfaMethods' => $mfaMethods,
174 'authMethod' => config('auth.method'),
175 'activeSocialDrivers' => $socialDriverManager->getActive(),
180 * Handle the submission for the auth change password form.
182 public function updatePassword(Request $request)
184 $this->preventAccessInDemoMode();
186 if (config('auth.method') !== 'standard') {
187 $this->showPermissionError();
190 $validated = $this->validate($request, [
191 'password' => ['required_with:password_confirm', Password::default()],
192 'password-confirm' => ['same:password', 'required_with:password'],
195 $this->userRepo->update(user(), $validated, false);
197 $this->showSuccessNotification(trans('preferences.auth_change_password_success'));
199 return redirect('/my-account/auth');
203 * Show the user self-delete page.
205 public function delete()
207 $this->setPageTitle(trans('preferences.delete_my_account'));
209 return view('users.account.delete', [
210 'category' => 'profile',
215 * Remove the current user from the system.
217 public function destroy(Request $request)
219 $this->preventAccessInDemoMode();
221 $requestNewOwnerId = intval($request->get('new_owner_id')) ?: null;
222 $newOwnerId = userCan(\BookStack\Permissions\Permission::UsersManage) ? $requestNewOwnerId : null;
224 $this->userRepo->destroy(user(), $newOwnerId);
226 return redirect('/');