<?php namespace BookStack\Auth;
use Activity;
+use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
* Remove the given user from storage, Delete all related content.
* @throws Exception
*/
- public function destroy(User $user)
+ public function destroy(User $user, ?int $newOwnerId = null)
{
$user->socialAccounts()->delete();
$user->apiTokens()->delete();
foreach ($profileImages as $image) {
Images::destroy($image);
}
+
+ if (!empty($newOwnerId)) {
+ $newOwner = User::query()->find($newOwnerId);
+ if (!is_null($newOwner)) {
+ $this->migrateOwnership($user, $newOwner);
+ }
+ }
+ }
+
+ /**
+ * Migrate ownership of items in the system from one user to another.
+ */
+ protected function migrateOwnership(User $fromUser, User $toUser)
+ {
+ $entities = (new EntityProvider)->all();
+ foreach ($entities as $instance) {
+ $instance->newQuery()->where('owned_by', '=', $fromUser->id)
+ ->update(['owned_by' => $toUser->id]);
+ }
}
/**
/**
* Fetch all core entity types as an associated array
* with their basic names as the keys.
- * @return [string => Entity]
+ * @return array<Entity>
*/
public function all(): array
{
* Remove the specified user from storage.
* @throws \Exception
*/
- public function destroy(int $id)
+ public function destroy(Request $request, int $id)
{
$this->preventAccessInDemoMode();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$user = $this->userRepo->getById($id);
+ $newOwnerId = $request->get('new_owner_id', null);
if ($this->userRepo->isOnlyAdmin($user)) {
$this->showErrorNotification(trans('errors.users_cannot_delete_only_admin'));
return redirect($user->getEditUrl());
}
- $this->userRepo->destroy($user);
+ $this->userRepo->destroy($user, $newOwnerId);
$this->showSuccessNotification(trans('settings.users_delete_success'));
$this->logActivity(ActivityType::USER_DELETE, $user);
'users_delete_named' => 'Delete user :userName',
'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
'users_delete_confirm' => 'Are you sure you want to delete this user?',
- 'users_delete_success' => 'Users successfully removed',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Edit User',
'users_edit_profile' => 'Edit Profile',
'users_edit_success' => 'User successfully updated',
<div class="dropdown-search custom-select-input" components="dropdown dropdown-search user-select"
option:dropdown-search:url="/search/users/select"
>
- <input refs="user-select@input" type="hidden" name="{{ $name }}" value="{{ $user->id }}">
+ <input refs="user-select@input" type="hidden" name="{{ $name }}" value="{{ $user->id ?? '' }}">
<div refs="dropdown@toggle"
class="dropdown-search-toggle flex-container-row items-center"
aria-haspopup="true" aria-expanded="false" tabindex="0">
<div refs="user-select@user-info" class="flex-container-row items-center px-s">
- <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
- <span>{{ $user->name }}</span>
+ @if($user)
+ <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+ <span>{{ $user->name }}</span>
+ @else
+ <span>{{ trans('settings.users_none_selected') }}</span>
+ @endif
</div>
<span style="font-size: 1.5rem; margin-left: auto;">
@icon('caret-down')
<p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
+ <hr class="my-l">
+
+ <div class="grid half gap-xl v-center">
+ <div>
+ <label class="setting-list-label">{{ trans('settings.users_migrate_ownership') }}</label>
+ <p class="small">{{ trans('settings.users_migrate_ownership_desc') }}</p>
+ </div>
+ <div>
+ @include('components.user-select', ['name' => 'new_owner_id', 'user' => null])
+ </div>
+ </div>
+
+ <hr class="my-l">
+
<div class="grid half">
<p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
<div>