diff --git a/app/Actions/Comment.php b/app/Actions/Comment.php
index 655d45221..f5269e253 100644
--- a/app/Actions/Comment.php
+++ b/app/Actions/Comment.php
@@ -1,6 +1,8 @@
 <?php namespace BookStack\Actions;
 
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
 
 /**
  * @property string text
@@ -8,25 +10,25 @@ use BookStack\Ownable;
  * @property int|null parent_id
  * @property int local_id
  */
-class Comment extends Ownable
+class Comment extends Model
 {
+    use HasCreatorAndUpdater;
+
     protected $fillable = ['text', 'parent_id'];
     protected $appends = ['created', 'updated'];
 
     /**
      * Get the entity that this comment belongs to
-     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
      */
-    public function entity()
+    public function entity(): MorphTo
     {
         return $this->morphTo('entity');
     }
 
     /**
      * Check if a comment has been updated since creation.
-     * @return bool
      */
-    public function isUpdated()
+    public function isUpdated(): bool
     {
         return $this->updated_at->timestamp > $this->created_at->timestamp;
     }
diff --git a/app/Auth/Permissions/PermissionService.php b/app/Auth/Permissions/PermissionService.php
index 5f4648d58..d858a7c18 100644
--- a/app/Auth/Permissions/PermissionService.php
+++ b/app/Auth/Permissions/PermissionService.php
@@ -5,7 +5,9 @@ use BookStack\Auth\Role;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\EntityProvider;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
 use Illuminate\Database\Connection;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Query\Builder as QueryBuilder;
@@ -168,7 +170,7 @@ class PermissionService
         });
 
         // Chunk through all bookshelves
-        $this->entityProvider->bookshelf->newQuery()->withTrashed()->select(['id', 'restricted', 'created_by'])
+        $this->entityProvider->bookshelf->newQuery()->withTrashed()->select(['id', 'restricted', 'owned_by'])
             ->chunk(50, function ($shelves) use ($roles) {
                 $this->buildJointPermissionsForShelves($shelves, $roles);
             });
@@ -181,10 +183,10 @@ class PermissionService
     protected function bookFetchQuery()
     {
         return $this->entityProvider->book->withTrashed()->newQuery()
-            ->select(['id', 'restricted', 'created_by'])->with(['chapters' => function ($query) {
-                $query->withTrashed()->select(['id', 'restricted', 'created_by', 'book_id']);
+            ->select(['id', 'restricted', 'owned_by'])->with(['chapters' => function ($query) {
+                $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id']);
             }, 'pages'  => function ($query) {
-                $query->withTrashed()->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']);
+                $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id', 'chapter_id']);
             }]);
     }
 
@@ -286,7 +288,7 @@ class PermissionService
         });
 
         // Chunk through all bookshelves
-        $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
+        $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'owned_by'])
             ->chunk(50, function ($shelves) use ($roles) {
                 $this->buildJointPermissionsForShelves($shelves, $roles);
             });
@@ -508,21 +510,19 @@ class PermissionService
             'action'             => $action,
             'has_permission'     => $permissionAll,
             'has_permission_own' => $permissionOwn,
-            'created_by'         => $entity->getRawAttribute('created_by')
+            'owned_by'         => $entity->getRawAttribute('owned_by')
         ];
     }
 
     /**
      * Checks if an entity has a restriction set upon it.
-     * @param Ownable $ownable
-     * @param $permission
-     * @return bool
+     * @param HasCreatorAndUpdater|HasOwner $ownable
      */
-    public function checkOwnableUserAccess(Ownable $ownable, $permission)
+    public function checkOwnableUserAccess(Model $ownable, string $permission): bool
     {
         $explodedPermission = explode('-', $permission);
 
-        $baseQuery = $ownable->where('id', '=', $ownable->id);
+        $baseQuery = $ownable->newQuery()->where('id', '=', $ownable->id);
         $action = end($explodedPermission);
         $this->currentAction = $action;
 
@@ -566,7 +566,7 @@ class PermissionService
                 $query->where('has_permission', '=', 1)
                     ->orWhere(function ($query2) use ($userId) {
                         $query2->where('has_permission_own', '=', 1)
-                            ->where('created_by', '=', $userId);
+                            ->where('owned_by', '=', $userId);
                     });
             });
 
@@ -615,7 +615,7 @@ class PermissionService
                         $query->where('has_permission', '=', true)
                             ->orWhere(function ($query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                     });
             });
@@ -639,7 +639,7 @@ class PermissionService
                         $query->where('has_permission', '=', true)
                             ->orWhere(function (Builder $query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                     });
             });
@@ -656,7 +656,7 @@ class PermissionService
             $query->where('draft', '=', false)
                 ->orWhere(function (Builder $query) {
                     $query->where('draft', '=', true)
-                        ->where('created_by', '=', $this->currentUser()->id);
+                        ->where('owned_by', '=', $this->currentUser()->id);
                 });
         });
     }
@@ -676,7 +676,7 @@ class PermissionService
                 $query->where('draft', '=', false)
                     ->orWhere(function ($query) {
                         $query->where('draft', '=', true)
-                            ->where('created_by', '=', $this->currentUser()->id);
+                            ->where('owned_by', '=', $this->currentUser()->id);
                     });
             });
         }
@@ -710,7 +710,7 @@ class PermissionService
                     ->where(function ($query) {
                         $query->where('has_permission', '=', true)->orWhere(function ($query) {
                             $query->where('has_permission_own', '=', true)
-                                ->where('created_by', '=', $this->currentUser()->id);
+                                ->where('owned_by', '=', $this->currentUser()->id);
                         });
                     });
             });
@@ -746,7 +746,7 @@ class PermissionService
                         ->where(function ($query) {
                             $query->where('has_permission', '=', true)->orWhere(function ($query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                         });
                 });
diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php
index 6b7de3259..6fb5dfa0f 100644
--- a/app/Auth/UserRepo.php
+++ b/app/Auth/UserRepo.php
@@ -1,6 +1,7 @@
 <?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;
@@ -169,7 +170,7 @@ class UserRepo
      * 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();
@@ -183,6 +184,25 @@ class UserRepo
         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]);
+        }
     }
 
     /**
diff --git a/app/Entities/EntityProvider.php b/app/Entities/EntityProvider.php
index ef1935a0f..c77a57d61 100644
--- a/app/Entities/EntityProvider.php
+++ b/app/Entities/EntityProvider.php
@@ -55,7 +55,7 @@ class EntityProvider
     /**
      * 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
     {
diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php
index e681a4e22..c6b2468b0 100644
--- a/app/Entities/Models/Entity.php
+++ b/app/Entities/Models/Entity.php
@@ -9,7 +9,9 @@ use BookStack\Auth\Permissions\JointPermission;
 use BookStack\Entities\Tools\SearchIndex;
 use BookStack\Entities\Tools\SlugGenerator;
 use BookStack\Facades\Permissions;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Collection;
@@ -35,9 +37,11 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static Builder withLastView()
  * @method static Builder withViewCount()
  */
-abstract class Entity extends Ownable
+abstract class Entity extends Model
 {
     use SoftDeletes;
+    use HasCreatorAndUpdater;
+    use HasOwner;
 
     /**
      * @var string - Name of property where the main text content is found
diff --git a/app/Entities/Repos/BaseRepo.php b/app/Entities/Repos/BaseRepo.php
index ff4fc635b..8b2e70074 100644
--- a/app/Entities/Repos/BaseRepo.php
+++ b/app/Entities/Repos/BaseRepo.php
@@ -4,6 +4,7 @@ namespace BookStack\Entities\Repos;
 
 use BookStack\Actions\ActivityType;
 use BookStack\Actions\TagRepo;
+use BookStack\Auth\User;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\Models\HasCoverImage;
 use BookStack\Exceptions\ImageUploadException;
@@ -34,6 +35,7 @@ class BaseRepo
         $entity->forceFill([
             'created_by' => user()->id,
             'updated_by' => user()->id,
+            'owned_by' => user()->id,
         ]);
         $entity->refreshSlug();
         $entity->save();
@@ -88,30 +90,4 @@ class BaseRepo
             $entity->save();
         }
     }
-
-    /**
-     * Update the permissions of an entity.
-     */
-    public function updatePermissions(Entity $entity, bool $restricted, Collection $permissions = null)
-    {
-        $entity->restricted = $restricted;
-        $entity->permissions()->delete();
-
-        if (!is_null($permissions)) {
-            $entityPermissionData = $permissions->flatMap(function ($restrictions, $roleId) {
-                return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
-                    return [
-                        'role_id' => $roleId,
-                        'action' => strtolower($action),
-                    ] ;
-                });
-            });
-
-            $entity->permissions()->createMany($entityPermissionData);
-        }
-
-        $entity->save();
-        $entity->rebuildPermissions();
-        Activity::addForEntity($entity, ActivityType::PERMISSIONS_UPDATE);
-    }
 }
diff --git a/app/Entities/Repos/BookRepo.php b/app/Entities/Repos/BookRepo.php
index d6dbe0b73..68d62887b 100644
--- a/app/Entities/Repos/BookRepo.php
+++ b/app/Entities/Repos/BookRepo.php
@@ -114,14 +114,6 @@ class BookRepo
         $this->baseRepo->updateCoverImage($book, $coverImage, $removeImage);
     }
 
-    /**
-     * Update the permissions of a book.
-     */
-    public function updatePermissions(Book $book, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($book, $restricted, $permissions);
-    }
-
     /**
      * Remove a book from the system.
      * @throws Exception
diff --git a/app/Entities/Repos/BookshelfRepo.php b/app/Entities/Repos/BookshelfRepo.php
index 075582cbf..b15241fb3 100644
--- a/app/Entities/Repos/BookshelfRepo.php
+++ b/app/Entities/Repos/BookshelfRepo.php
@@ -137,14 +137,6 @@ class BookshelfRepo
         $this->baseRepo->updateCoverImage($shelf, $coverImage, $removeImage);
     }
 
-    /**
-     * Update the permissions of a bookshelf.
-     */
-    public function updatePermissions(Bookshelf $shelf, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($shelf, $restricted, $permissions);
-    }
-
     /**
      * Copy down the permissions of the given shelf to all child books.
      */
diff --git a/app/Entities/Repos/ChapterRepo.php b/app/Entities/Repos/ChapterRepo.php
index 281cc2cab..d56874e0d 100644
--- a/app/Entities/Repos/ChapterRepo.php
+++ b/app/Entities/Repos/ChapterRepo.php
@@ -62,14 +62,6 @@ class ChapterRepo
         return $chapter;
     }
 
-    /**
-     * Update the permissions of a chapter.
-     */
-    public function updatePermissions(Chapter $chapter, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($chapter, $restricted, $permissions);
-    }
-
     /**
      * Remove a chapter from the system.
      * @throws Exception
diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php
index 153ef8575..8840c06db 100644
--- a/app/Entities/Repos/PageRepo.php
+++ b/app/Entities/Repos/PageRepo.php
@@ -130,6 +130,7 @@ class PageRepo
         $page = (new Page())->forceFill([
             'name' => trans('entities.pages_initial_name'),
             'created_by' => user()->id,
+            'owned_by' => user()->id,
             'updated_by' => user()->id,
             'draft' => true,
         ]);
@@ -382,14 +383,6 @@ class PageRepo
         return $parentClass::visible()->where('id', '=', $entityId)->first();
     }
 
-    /**
-     * Update the permissions of a page.
-     */
-    public function updatePermissions(Page $page, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($page, $restricted, $permissions);
-    }
-
     /**
      * Change the page's parent to the given entity.
      */
diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php
new file mode 100644
index 000000000..8a27ce75b
--- /dev/null
+++ b/app/Entities/Tools/PermissionsUpdater.php
@@ -0,0 +1,68 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Facades\Activity;
+use Illuminate\Http\Request;
+use Illuminate\Support\Collection;
+
+class PermissionsUpdater
+{
+
+    /**
+     * Update an entities permissions from a permission form submit request.
+     */
+    public function updateFromPermissionsForm(Entity $entity, Request $request)
+    {
+        $restricted = $request->get('restricted') === 'true';
+        $permissions = $request->get('restrictions', null);
+        $ownerId = $request->get('owned_by', null);
+
+        $entity->restricted = $restricted;
+        $entity->permissions()->delete();
+
+        if (!is_null($permissions)) {
+            $entityPermissionData = $this->formatPermissionsFromRequestToEntityPermissions($permissions);
+            $entity->permissions()->createMany($entityPermissionData);
+        }
+
+        if (!is_null($ownerId)) {
+            $this->updateOwnerFromId($entity, intval($ownerId));
+        }
+
+        $entity->save();
+        $entity->rebuildPermissions();
+
+        Activity::addForEntity($entity, ActivityType::PERMISSIONS_UPDATE);
+    }
+
+    /**
+     * Update the owner of the given entity.
+     * Checks the user exists in the system first.
+     * Does not save the model, just updates it.
+     */
+    protected function updateOwnerFromId(Entity $entity, int $newOwnerId)
+    {
+        $newOwner = User::query()->find($newOwnerId);
+        if (!is_null($newOwner)) {
+            $entity->owned_by = $newOwner->id;
+        }
+    }
+
+    /**
+     * Format permissions provided from a permission form to be
+     * EntityPermission data.
+     */
+    protected function formatPermissionsFromRequestToEntityPermissions(array $permissions): Collection
+    {
+        return collect($permissions)->flatMap(function ($restrictions, $roleId) {
+            return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
+                return [
+                    'role_id' => $roleId,
+                    'action' => strtolower($action),
+                ] ;
+            });
+        });
+    }
+}
diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index b63fe911f..3d695ba85 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -4,6 +4,7 @@ use Activity;
 use BookStack\Actions\ActivityType;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Exceptions\ImageUploadException;
@@ -202,14 +203,12 @@ class BookController extends Controller
      * Set the restrictions for this book.
      * @throws Throwable
      */
-    public function permissions(Request $request, string $bookSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $book);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->bookRepo->updatePermissions($book, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($book, $request);
 
         $this->showSuccessNotification(trans('entities.books_permissions_updated'));
         return redirect($book->getUrl());
diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php
index 50dc97bab..32c22e185 100644
--- a/app/Http/Controllers/BookshelfController.php
+++ b/app/Http/Controllers/BookshelfController.php
@@ -2,6 +2,7 @@
 
 use Activity;
 use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Entities\Repos\BookshelfRepo;
 use BookStack\Exceptions\ImageUploadException;
@@ -19,9 +20,6 @@ class BookshelfController extends Controller
     protected $entityContextManager;
     protected $imageRepo;
 
-    /**
-     * BookController constructor.
-     */
     public function __construct(BookshelfRepo $bookshelfRepo, ShelfContext $entityContextManager, ImageRepo $imageRepo)
     {
         $this->bookshelfRepo = $bookshelfRepo;
@@ -200,14 +198,12 @@ class BookshelfController extends Controller
     /**
      * Set the permissions for this bookshelf.
      */
-    public function permissions(Request $request, string $slug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $slug)
     {
         $shelf = $this->bookshelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('restrictions-manage', $shelf);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->bookshelfRepo->updatePermissions($shelf, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($shelf, $request);
 
         $this->showSuccessNotification(trans('entities.shelves_permissions_updated'));
         return redirect($shelf->getUrl());
diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php
index 0059f202b..1d69df2a2 100644
--- a/app/Http/Controllers/ChapterController.php
+++ b/app/Http/Controllers/ChapterController.php
@@ -3,6 +3,7 @@
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
 use Illuminate\Http\Request;
@@ -190,14 +191,12 @@ class ChapterController extends Controller
      * Set the restrictions for this chapter.
      * @throws NotFoundException
      */
-    public function permissions(Request $request, string $bookSlug, string $chapterSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $chapterSlug)
     {
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
         $this->checkOwnablePermission('restrictions-manage', $chapter);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->chapterRepo->updatePermissions($chapter, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($chapter, $request);
 
         $this->showSuccessNotification(trans('entities.chapters_permissions_success'));
         return redirect($chapter->getUrl());
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index 758c85dda..479d5ac15 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -4,7 +4,8 @@ namespace BookStack\Http\Controllers;
 
 use BookStack\Facades\Activity;
 use BookStack\Interfaces\Loggable;
-use BookStack\Ownable;
+use BookStack\HasCreatorAndUpdater;
+use BookStack\Model;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Exceptions\HttpResponseException;
@@ -72,7 +73,7 @@ abstract class Controller extends BaseController
     /**
      * Check the current user's permissions against an ownable item otherwise throw an exception.
      */
-    protected function checkOwnablePermission(string $permission, Ownable $ownable): void
+    protected function checkOwnablePermission(string $permission, Model $ownable): void
     {
         if (!userCan($permission, $ownable)) {
             $this->showPermissionError();
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 67d28a78a..7d8e54382 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -5,6 +5,7 @@ use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Tools\PageEditActivity;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\NotifyException;
 use BookStack\Exceptions\PermissionsException;
@@ -453,14 +454,12 @@ class PageController extends Controller
      * @throws NotFoundException
      * @throws Throwable
      */
-    public function permissions(Request $request, string $bookSlug, string $pageSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $pageSlug)
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('restrictions-manage', $page);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->pageRepo->updatePermissions($page, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($page, $request);
 
         $this->showSuccessNotification(trans('entities.pages_permissions_success'));
         return redirect($page->getUrl());
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 8d688ed84..852d507c1 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -217,12 +217,13 @@ class UserController extends Controller
      * 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'));
@@ -234,7 +235,7 @@ class UserController extends Controller
             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);
 
diff --git a/app/Http/Controllers/UserSearchController.php b/app/Http/Controllers/UserSearchController.php
new file mode 100644
index 000000000..a0dfbd8d0
--- /dev/null
+++ b/app/Http/Controllers/UserSearchController.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+
+class UserSearchController extends Controller
+{
+    /**
+     * Search users in the system, with the response formatted
+     * for use in a select-style list.
+     */
+    public function forSelect(Request $request)
+    {
+        $search = $request->get('search', '');
+        $query = User::query()->orderBy('name', 'desc')
+            ->take(20);
+
+        if (!empty($search)) {
+            $query->where(function (Builder $query) use ($search) {
+                $query->where('email', 'like', '%' . $search . '%')
+                    ->orWhere('name', 'like', '%' . $search . '%');
+            });
+        }
+
+        $users = $query->get();
+        return view('components.user-select-list', compact('users'));
+    }
+}
diff --git a/app/Ownable.php b/app/Traits/HasCreatorAndUpdater.php
similarity index 59%
rename from app/Ownable.php
rename to app/Traits/HasCreatorAndUpdater.php
index b118bc742..ad6c3035f 100644
--- a/app/Ownable.php
+++ b/app/Traits/HasCreatorAndUpdater.php
@@ -1,27 +1,26 @@
-<?php namespace BookStack;
+<?php namespace BookStack\Traits;
 
 use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
  * @property int created_by
  * @property int updated_by
  */
-abstract class Ownable extends Model
+trait HasCreatorAndUpdater
 {
     /**
      * Relation for the user that created this entity.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function createdBy()
+    public function createdBy(): BelongsTo
     {
         return $this->belongsTo(User::class, 'created_by');
     }
 
     /**
      * Relation for the user that updated this entity.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function updatedBy()
+    public function updatedBy(): BelongsTo
     {
         return $this->belongsTo(User::class, 'updated_by');
     }
diff --git a/app/Traits/HasOwner.php b/app/Traits/HasOwner.php
new file mode 100644
index 000000000..9d1eb3df7
--- /dev/null
+++ b/app/Traits/HasOwner.php
@@ -0,0 +1,19 @@
+<?php namespace BookStack\Traits;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int owned_by
+ */
+trait HasOwner
+{
+    /**
+     * Relation for the user that owns this entity.
+     */
+    public function ownedBy(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'owned_by');
+    }
+
+}
diff --git a/app/Uploads/Attachment.php b/app/Uploads/Attachment.php
index 77c7925db..d1060477d 100644
--- a/app/Uploads/Attachment.php
+++ b/app/Uploads/Attachment.php
@@ -1,7 +1,8 @@
 <?php namespace BookStack\Uploads;
 
 use BookStack\Entities\Models\Page;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
 
 /**
  * @property int id
@@ -10,8 +11,10 @@ use BookStack\Ownable;
  * @property string extension
  * @property bool external
  */
-class Attachment extends Ownable
+class Attachment extends Model
 {
+    use HasCreatorAndUpdater;
+
     protected $fillable = ['name', 'order'];
 
     /**
diff --git a/app/Uploads/Image.php b/app/Uploads/Image.php
index 029fd3175..dc26af002 100644
--- a/app/Uploads/Image.php
+++ b/app/Uploads/Image.php
@@ -1,11 +1,13 @@
 <?php namespace BookStack\Uploads;
 
 use BookStack\Entities\Models\Page;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
 use Images;
 
-class Image extends Ownable
+class Image extends Model
 {
+    use HasCreatorAndUpdater;
 
     protected $fillable = ['name'];
     protected $hidden = [];
diff --git a/app/helpers.php b/app/helpers.php
index 935d4d8da..c090bfd05 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -2,7 +2,7 @@
 
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Auth\User;
-use BookStack\Ownable;
+use BookStack\Model;
 use BookStack\Settings\SettingService;
 
 /**
@@ -56,7 +56,7 @@ function hasAppAccess(): bool
  * Check if the current user has a permission. If an ownable element
  * is passed in the jointPermissions are checked against that particular item.
  */
-function userCan(string $permission, Ownable $ownable = null): bool
+function userCan(string $permission, Model $ownable = null): bool
 {
     if ($ownable === null) {
         return user() && user()->can($permission);
diff --git a/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php b/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php
new file mode 100644
index 000000000..bf8bf281f
--- /dev/null
+++ b/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php
@@ -0,0 +1,49 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class AddOwnedByFieldToEntities extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+        foreach ($tables as $table) {
+            Schema::table($table, function (Blueprint $table) {
+                $table->integer('owned_by')->unsigned()->index();
+            });
+
+            DB::table($table)->update(['owned_by' => DB::raw('`created_by`')]);
+        }
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->renameColumn('created_by', 'owned_by');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+        foreach ($tables as $table) {
+            Schema::table($table, function (Blueprint $table) {
+                $table->dropColumn('owned_by');
+            });
+        }
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->renameColumn('owned_by', 'created_by');
+        });
+    }
+}
diff --git a/database/seeds/DummyContentSeeder.php b/database/seeds/DummyContentSeeder.php
index 55e1f1075..611c05246 100644
--- a/database/seeds/DummyContentSeeder.php
+++ b/database/seeds/DummyContentSeeder.php
@@ -31,7 +31,7 @@ class DummyContentSeeder extends Seeder
         $role = Role::getRole('viewer');
         $viewerUser->attachRole($role);
 
-        $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id];
+        $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'owned_by' => $editorUser->id];
 
         factory(\BookStack\Entities\Models\Book::class, 5)->create($byData)
             ->each(function($book) use ($editorUser, $byData) {
diff --git a/resources/js/components/breadcrumb-listing.js b/resources/js/components/breadcrumb-listing.js
deleted file mode 100644
index 7f4344b17..000000000
--- a/resources/js/components/breadcrumb-listing.js
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-class BreadcrumbListing {
-
-    constructor(elem) {
-        this.elem = elem;
-        this.searchInput = elem.querySelector('input');
-        this.loadingElem = elem.querySelector('.loading-container');
-        this.entityListElem = elem.querySelector('.breadcrumb-listing-entity-list');
-
-        // this.loadingElem.style.display = 'none';
-        const entityDescriptor = elem.getAttribute('breadcrumb-listing').split(':');
-        this.entityType = entityDescriptor[0];
-        this.entityId = Number(entityDescriptor[1]);
-
-        this.elem.addEventListener('show', this.onShow.bind(this));
-        this.searchInput.addEventListener('input', this.onSearch.bind(this));
-    }
-
-    onShow() {
-        this.loadEntityView();
-    }
-
-    onSearch() {
-        const input = this.searchInput.value.toLowerCase().trim();
-        const listItems = this.entityListElem.querySelectorAll('.entity-list-item');
-        for (let listItem of listItems) {
-            const match = !input || listItem.textContent.toLowerCase().includes(input);
-            listItem.style.display = match ? 'flex' : 'none';
-            listItem.classList.toggle('hidden', !match);
-        }
-    }
-
-    loadEntityView() {
-        this.toggleLoading(true);
-
-        const params = {
-            'entity_id': this.entityId,
-            'entity_type': this.entityType,
-        };
-
-        window.$http.get('/search/entity/siblings', params).then(resp => {
-            this.entityListElem.innerHTML = resp.data;
-        }).catch(err => {
-            console.error(err);
-        }).then(() => {
-            this.toggleLoading(false);
-            this.onSearch();
-        });
-    }
-
-    toggleLoading(show = false) {
-        this.loadingElem.style.display = show ? 'block' : 'none';
-    }
-
-}
-
-export default BreadcrumbListing;
\ No newline at end of file
diff --git a/resources/js/components/dropdown-search.js b/resources/js/components/dropdown-search.js
new file mode 100644
index 000000000..8c81aae3c
--- /dev/null
+++ b/resources/js/components/dropdown-search.js
@@ -0,0 +1,79 @@
+import {debounce} from "../services/util";
+
+class DropdownSearch {
+
+    setup() {
+        this.elem = this.$el;
+        this.searchInput = this.$refs.searchInput;
+        this.loadingElem = this.$refs.loading;
+        this.listContainerElem = this.$refs.listContainer;
+
+        this.localSearchSelector = this.$opts.localSearchSelector;
+        this.url = this.$opts.url;
+
+        this.elem.addEventListener('show', this.onShow.bind(this));
+        this.searchInput.addEventListener('input', this.onSearch.bind(this));
+
+        this.runAjaxSearch = debounce(this.runAjaxSearch, 300, false);
+    }
+
+    onShow() {
+        this.loadList();
+    }
+
+    onSearch() {
+        const input = this.searchInput.value.toLowerCase().trim();
+        if (this.localSearchSelector) {
+            this.runLocalSearch(input);
+        } else {
+            this.toggleLoading(true);
+            this.runAjaxSearch(input);
+        }
+    }
+
+    runAjaxSearch(searchTerm) {
+        this.loadList(searchTerm);
+    }
+
+    runLocalSearch(searchTerm) {
+        const listItems = this.listContainerElem.querySelectorAll(this.localSearchSelector);
+        for (let listItem of listItems) {
+            const match = !searchTerm || listItem.textContent.toLowerCase().includes(searchTerm);
+            listItem.style.display = match ? 'flex' : 'none';
+            listItem.classList.toggle('hidden', !match);
+        }
+    }
+
+    async loadList(searchTerm = '') {
+        this.listContainerElem.innerHTML = '';
+        this.toggleLoading(true);
+
+        try {
+            const resp = await window.$http.get(this.getAjaxUrl(searchTerm));
+            this.listContainerElem.innerHTML = resp.data;
+        } catch (err) {
+            console.error(err);
+        }
+
+        this.toggleLoading(false);
+        if (this.localSearchSelector) {
+            this.onSearch();
+        }
+    }
+
+    getAjaxUrl(searchTerm = null) {
+        if (!searchTerm) {
+            return this.url;
+        }
+
+        const joiner = this.url.includes('?') ? '&' : '?';
+        return `${this.url}${joiner}search=${encodeURIComponent(searchTerm)}`;
+    }
+
+    toggleLoading(show = false) {
+        this.loadingElem.style.display = show ? 'block' : 'none';
+    }
+
+}
+
+export default DropdownSearch;
\ No newline at end of file
diff --git a/resources/js/components/dropdown.js b/resources/js/components/dropdown.js
index 7b1ce3055..22402d483 100644
--- a/resources/js/components/dropdown.js
+++ b/resources/js/components/dropdown.js
@@ -17,6 +17,7 @@ class DropDown {
         this.body = document.body;
         this.showing = false;
         this.setupListeners();
+        this.hide = this.hide.bind(this);
     }
 
     show(event = null) {
diff --git a/resources/js/components/index.js b/resources/js/components/index.js
index 87c496c91..91ccdaf3a 100644
--- a/resources/js/components/index.js
+++ b/resources/js/components/index.js
@@ -5,7 +5,6 @@ import attachments from "./attachments.js"
 import autoSuggest from "./auto-suggest.js"
 import backToTop from "./back-to-top.js"
 import bookSort from "./book-sort.js"
-import breadcrumbListing from "./breadcrumb-listing.js"
 import chapterToggle from "./chapter-toggle.js"
 import codeEditor from "./code-editor.js"
 import codeHighlighter from "./code-highlighter.js"
@@ -13,6 +12,7 @@ import collapsible from "./collapsible.js"
 import customCheckbox from "./custom-checkbox.js"
 import detailsHighlighter from "./details-highlighter.js"
 import dropdown from "./dropdown.js"
+import dropdownSearch from "./dropdown-search.js"
 import dropzone from "./dropzone.js"
 import editorToolbox from "./editor-toolbox.js"
 import entityPermissionsEditor from "./entity-permissions-editor.js"
@@ -48,6 +48,7 @@ import tagManager from "./tag-manager.js"
 import templateManager from "./template-manager.js"
 import toggleSwitch from "./toggle-switch.js"
 import triLayout from "./tri-layout.js"
+import userSelect from "./user-select.js"
 import wysiwygEditor from "./wysiwyg-editor.js"
 
 const componentMapping = {
@@ -58,7 +59,6 @@ const componentMapping = {
     "auto-suggest": autoSuggest,
     "back-to-top": backToTop,
     "book-sort": bookSort,
-    "breadcrumb-listing": breadcrumbListing,
     "chapter-toggle": chapterToggle,
     "code-editor": codeEditor,
     "code-highlighter": codeHighlighter,
@@ -66,6 +66,7 @@ const componentMapping = {
     "custom-checkbox": customCheckbox,
     "details-highlighter": detailsHighlighter,
     "dropdown": dropdown,
+    "dropdown-search": dropdownSearch,
     "dropzone": dropzone,
     "editor-toolbox": editorToolbox,
     "entity-permissions-editor": entityPermissionsEditor,
@@ -101,6 +102,7 @@ const componentMapping = {
     "template-manager": templateManager,
     "toggle-switch": toggleSwitch,
     "tri-layout": triLayout,
+    "user-select": userSelect,
     "wysiwyg-editor": wysiwygEditor,
 };
 
diff --git a/resources/js/components/user-select.js b/resources/js/components/user-select.js
new file mode 100644
index 000000000..477c11d6b
--- /dev/null
+++ b/resources/js/components/user-select.js
@@ -0,0 +1,24 @@
+import {onChildEvent} from "../services/dom";
+
+class UserSelect {
+
+    setup() {
+
+        this.input = this.$refs.input;
+        this.userInfoContainer = this.$refs.userInfo;
+
+        this.hide = this.$el.components.dropdown.hide;
+
+        onChildEvent(this.$el, 'a.dropdown-search-item', 'click', this.selectUser.bind(this));
+    }
+
+    selectUser(event, userEl) {
+        const id = userEl.getAttribute('data-id');
+        this.input.value = id;
+        this.userInfoContainer.innerHTML = userEl.innerHTML;
+        this.hide();
+    }
+
+}
+
+export default UserSelect;
\ No newline at end of file
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index 485ecb7bc..6b0153844 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Created :timeLength by :user',
     'meta_updated' => 'Updated :timeLength',
     'meta_updated_name' => 'Updated :timeLength by :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Entity Select',
     'images' => 'Images',
     'my_recent_drafts' => 'My Recent Drafts',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
     'permissions_enable' => 'Enable Custom Permissions',
     'permissions_save' => 'Save Permissions',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Search Results',
diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index 3e043e3c6..fe7ebc612 100755
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -175,7 +175,10 @@ return [
     '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',
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index eb40741d1..ede26c51c 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -724,4 +724,65 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   .template-item-actions button:first-child {
     border-top: 0;
   }
+}
+
+.dropdown-search-dropdown {
+  box-shadow: $bs-med;
+  overflow: hidden;
+  min-height: 100px;
+  width: 240px;
+  display: none;
+  position: absolute;
+  z-index: 80;
+  right: -$-m;
+  @include rtl {
+    right: auto;
+    left: -$-m;
+  }
+  .dropdown-search-search .svg-icon {
+    position: absolute;
+    left: $-s;
+    @include rtl {
+      right: $-s;
+      left: auto;
+    }
+    top: 11px;
+    fill: #888;
+    pointer-events: none;
+  }
+  .dropdown-search-list {
+    max-height: 400px;
+    overflow-y: scroll;
+    text-align: start;
+  }
+  .dropdown-search-item {
+    padding: $-s $-m;
+    &:hover,&:focus {
+      background-color: #F2F2F2;
+      text-decoration: none;
+    }
+  }
+  input {
+    padding-inline-start: $-xl;
+    border-radius: 0;
+    border: 0;
+    border-bottom: 1px solid #DDD;
+  }
+}
+
+@include smaller-than($m) {
+  .dropdown-search-dropdown {
+    position: fixed;
+    right: auto;
+    left: $-m;
+  }
+  .dropdown-search-dropdown .dropdown-search-list {
+    max-height: 240px;
+  }
+}
+
+.custom-select-input {
+  max-width: 280px;
+  border: 1px solid #DDD;
+  border-radius: 4px;
 }
\ No newline at end of file
diff --git a/resources/sass/_header.scss b/resources/sass/_header.scss
index e19bb4f61..246ef4b5b 100644
--- a/resources/sass/_header.scss
+++ b/resources/sass/_header.scss
@@ -269,9 +269,9 @@ header .search-box {
   }
 }
 
-.breadcrumb-listing {
+.dropdown-search {
   position: relative;
-  .breadcrumb-listing-toggle {
+  .dropdown-search-toggle {
     padding: 6px;
     border: 1px solid transparent;
     border-radius: 4px;
@@ -284,54 +284,6 @@ header .search-box {
   }
 }
 
-.breadcrumb-listing-dropdown {
-  box-shadow: $bs-med;
-  overflow: hidden;
-  min-height: 100px;
-  width: 240px;
-  display: none;
-  position: absolute;
-  z-index: 80;
-  right: -$-m;
-  @include rtl {
-    right: auto;
-    left: -$-m;
-  }
-  .breadcrumb-listing-search .svg-icon {
-    position: absolute;
-    left: $-s;
-    @include rtl {
-      right: $-s;
-      left: auto;
-    }
-    top: 11px;
-    fill: #888;
-    pointer-events: none;
-  }
-  .breadcrumb-listing-entity-list {
-    max-height: 400px;
-    overflow-y: scroll;
-    text-align: start;
-  }
-  input {
-    padding-inline-start: $-xl;
-    border-radius: 0;
-    border: 0;
-    border-bottom: 1px solid #DDD;
-  }
-}
-
-@include smaller-than($m) {
-  .breadcrumb-listing-dropdown {
-    position: fixed;
-    right: auto;
-    left: $-m;
-  }
-  .breadcrumb-listing-dropdown .breadcrumb-listing-entity-list {
-    max-height: 240px;
-  }
-}
-
 .faded {
   a, button, span, span > div {
     color: #666;
diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss
index c4e412f0e..e5ed608eb 100644
--- a/resources/sass/_layout.scss
+++ b/resources/sass/_layout.scss
@@ -153,6 +153,9 @@ body.flexbox {
 .justify-center {
   justify-content: center;
 }
+.items-center {
+  align-items: center;
+}
 
 
 /**
diff --git a/resources/views/components/user-select-list.blade.php b/resources/views/components/user-select-list.blade.php
new file mode 100644
index 000000000..2c49e965d
--- /dev/null
+++ b/resources/views/components/user-select-list.blade.php
@@ -0,0 +1,6 @@
+@foreach($users as $user)
+    <a href="#" class="flex-container-row items-center dropdown-search-item" data-id="{{ $user->id }}">
+        <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+        <span>{{ $user->name }}</span>
+    </a>
+@endforeach
\ No newline at end of file
diff --git a/resources/views/components/user-select.blade.php b/resources/views/components/user-select.blade.php
new file mode 100644
index 000000000..2a07f0bde
--- /dev/null
+++ b/resources/views/components/user-select.blade.php
@@ -0,0 +1,34 @@
+<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 ?? '' }}">
+    <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">
+            @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')
+        </span>
+    </div>
+    <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+        <div class="dropdown-search-search">
+            @icon('search')
+            <input refs="dropdown-search@searchInput"
+                   aria-label="{{ trans('common.search') }}"
+                   autocomplete="off"
+                   placeholder="{{ trans('common.search') }}"
+                   type="text">
+        </div>
+        <div refs="dropdown-search@loading" class="text-center">
+            @include('partials.loading-icon')
+        </div>
+        <div refs="dropdown-search@listContainer" class="dropdown-search-list"></div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/resources/views/form/entity-permissions.blade.php b/resources/views/form/entity-permissions.blade.php
index 3581a545b..35490bed9 100644
--- a/resources/views/form/entity-permissions.blade.php
+++ b/resources/views/form/entity-permissions.blade.php
@@ -2,15 +2,26 @@
     {!! csrf_field() !!}
     <input type="hidden" name="_method" value="PUT">
 
-    <p class="mb-none">{{ trans('entities.permissions_intro') }}</p>
-
-    <div class="form-group">
-        @include('form.checkbox', [
-            'name' => 'restricted',
-            'label' => trans('entities.permissions_enable'),
-        ])
+    <div class="grid half left-focus v-center">
+        <div>
+            <p class="mb-none mt-m">{{ trans('entities.permissions_intro') }}</p>
+            <div>
+                @include('form.checkbox', [
+                    'name' => 'restricted',
+                    'label' => trans('entities.permissions_enable'),
+                ])
+            </div>
+        </div>
+        <div>
+            <div class="form-group">
+                <label for="owner">{{ trans('entities.permissions_owner') }}</label>
+                @include('components.user-select', ['user' => $model->ownedBy, 'name' => 'owned_by'])
+            </div>
+        </div>
     </div>
 
+    <hr>
+
     <table permissions-table class="table permissions-table toggle-switch-list" style="{{ !$model->restricted ? 'display: none' : '' }}">
         <tr>
             <th>{{ trans('common.role') }}</th>
diff --git a/resources/views/partials/breadcrumb-listing.blade.php b/resources/views/partials/breadcrumb-listing.blade.php
index a1a33ae1c..2a559aa7d 100644
--- a/resources/views/partials/breadcrumb-listing.blade.php
+++ b/resources/views/partials/breadcrumb-listing.blade.php
@@ -1,14 +1,23 @@
-<div class="breadcrumb-listing" component="dropdown" breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
-    <div class="breadcrumb-listing-toggle" refs="dropdown@toggle"
+<div class="dropdown-search" components="dropdown dropdown-search"
+     option:dropdown-search:url="/search/entity/siblings?entity_type={{$entity->getType()}}&entity_id={{ $entity->id }}"
+     option:dropdown-search:local-search-selector=".entity-list-item"
+>
+    <div class="dropdown-search-toggle" refs="dropdown@toggle"
          aria-haspopup="true" aria-expanded="false" tabindex="0">
         <div class="separator">@icon('chevron-right')</div>
     </div>
-    <div refs="dropdown@menu" class="breadcrumb-listing-dropdown card" role="menu">
-        <div class="breadcrumb-listing-search">
+    <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+        <div class="dropdown-search-search">
             @icon('search')
-            <input autocomplete="off" type="text" name="entity-search" placeholder="{{ trans('common.search') }}" aria-label="{{ trans('common.search') }}">
+            <input refs="dropdown-search@searchInput"
+                   aria-label="{{ trans('common.search') }}"
+                   autocomplete="off"
+                   placeholder="{{ trans('common.search') }}"
+                   type="text">
         </div>
-        @include('partials.loading-icon')
-        <div class="breadcrumb-listing-entity-list px-m"></div>
+        <div refs="dropdown-search@loading">
+            @include('partials.loading-icon')
+        </div>
+        <div refs="dropdown-search@listContainer" class="dropdown-search-list px-m"></div>
     </div>
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-meta.blade.php b/resources/views/partials/entity-meta.blade.php
index f759ea25b..8996df9bb 100644
--- a/resources/views/partials/entity-meta.blade.php
+++ b/resources/views/partials/entity-meta.blade.php
@@ -1,34 +1,50 @@
 <div class="entity-meta">
     @if($entity->isA('revision'))
-        @icon('history'){{ trans('entities.pages_revision') }}
-        {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
-        <br>
+        <div>
+            @icon('history'){{ trans('entities.pages_revision') }}
+            {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
+        </div>
     @endif
 
     @if ($entity->isA('page'))
-        @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
-            @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br>
+        <div>
+            @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
+            @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }}
             @if (userCan('page-update', $entity))</a>@endif
+        </div>
     @endif
 
+    @if ($entity->ownedBy && $entity->ownedBy->id !== $entity->createdBy->id)
+        <div>
+            @icon('user'){!! trans('entities.meta_owned_name', [
+            'user' => "<a href='{$entity->ownedBy->getProfileUrl()}'>".e($entity->ownedBy->name). "</a>"
+        ]) !!}
+        </div>
+    @endif
 
     @if ($entity->createdBy)
-        @icon('star'){!! trans('entities.meta_created_name', [
+        <div>
+            @icon('star'){!! trans('entities.meta_created_name', [
             'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
-            'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
+            'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".e($entity->createdBy->name). "</a>"
             ]) !!}
+        </div>
     @else
-        @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+        <div>
+            @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+        </div>
     @endif
 
-    <br>
-
     @if ($entity->updatedBy)
-        @icon('edit'){!! trans('entities.meta_updated_name', [
+        <div>
+            @icon('edit'){!! trans('entities.meta_updated_name', [
                 'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
-                'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
+                'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".e($entity->updatedBy->name). "</a>"
             ]) !!}
+        </div>
     @elseif (!$entity->isA('revision'))
-        @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+        <div>
+            @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+        </div>
     @endif
 </div>
\ No newline at end of file
diff --git a/resources/views/users/delete.blade.php b/resources/views/users/delete.blade.php
index d3349c2f3..aba6f5cc1 100644
--- a/resources/views/users/delete.blade.php
+++ b/resources/views/users/delete.blade.php
@@ -12,6 +12,20 @@
 
             <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>
diff --git a/routes/web.php b/routes/web.php
index afefcb99e..e8f217862 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -148,6 +148,9 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
     Route::get('/search/entity/siblings', 'SearchController@searchSiblings');
 
+    // User Search
+    Route::get('/search/users/select', 'UserSearchController@forSelect');
+
     Route::get('/templates', 'PageTemplateController@list');
     Route::get('/templates/{templateId}', 'PageTemplateController@get');
 
diff --git a/tests/BrowserKitTest.php b/tests/BrowserKitTest.php
index bb5aaa031..6c332a984 100644
--- a/tests/BrowserKitTest.php
+++ b/tests/BrowserKitTest.php
@@ -1,10 +1,16 @@
 <?php namespace Tests;
 
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Models\Entity;
 use BookStack\Auth\Role;
 use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\Models\Page;
 use BookStack\Settings\SettingService;
+use DB;
 use Illuminate\Contracts\Console\Kernel;
+use Illuminate\Foundation\Application;
 use Illuminate\Foundation\Testing\DatabaseTransactions;
 use Laravel\BrowserKitTesting\TestCase;
 use Symfony\Component\DomCrawler\Crawler;
@@ -23,14 +29,14 @@ abstract class BrowserKitTest extends TestCase
 
     public function tearDown() : void
     {
-        \DB::disconnect();
+        DB::disconnect();
         parent::tearDown();
     }
 
     /**
      * Creates the application.
      *
-     * @return \Illuminate\Foundation\Application
+     * @return Application
      */
     public function createApplication()
     {
@@ -47,7 +53,7 @@ abstract class BrowserKitTest extends TestCase
      */
     public function getNormalUser()
     {
-        return \BookStack\Auth\User::where('system_name', '=', null)->get()->last();
+        return User::where('system_name', '=', null)->get()->last();
     }
 
     /**
@@ -64,23 +70,21 @@ abstract class BrowserKitTest extends TestCase
 
     /**
      * Create a group of entities that belong to a specific user.
-     * @param $creatorUser
-     * @param $updaterUser
-     * @return array
      */
-    protected function createEntityChainBelongingToUser($creatorUser, $updaterUser = false)
+    protected function createEntityChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
     {
-        if ($updaterUser === false) $updaterUser = $creatorUser;
-        $book = factory(\BookStack\Entities\Models\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
-        $chapter = factory(\BookStack\Entities\Models\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
-        $page = factory(\BookStack\Entities\Models\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id, 'chapter_id' => $chapter->id]);
+        if (empty($updaterUser)) {
+            $updaterUser = $creatorUser;
+        }
+
+        $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
+        $book = factory(Book::class)->create($userAttrs);
+        $chapter = factory(Chapter::class)->create(array_merge(['book_id' => $book->id], $userAttrs));
+        $page = factory(Page::class)->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
         $restrictionService = $this->app[PermissionService::class];
         $restrictionService->buildJointPermissionsForEntity($book);
-        return [
-            'book' => $book,
-            'chapter' => $chapter,
-            'page' => $page
-        ];
+
+        return compact('book', 'chapter', 'page');
     }
 
     /**
@@ -101,7 +105,7 @@ abstract class BrowserKitTest extends TestCase
      */
     protected function getNewBlankUser($attributes = [])
     {
-        $user = factory(\BookStack\Auth\User::class)->create($attributes);
+        $user = factory(User::class)->create($attributes);
         return $user;
     }
 
diff --git a/tests/Entity/SortTest.php b/tests/Entity/SortTest.php
index bb67bfc3e..01f764b7b 100644
--- a/tests/Entity/SortTest.php
+++ b/tests/Entity/SortTest.php
@@ -287,7 +287,7 @@ class SortTest extends TestCase
         $resp = $this->actingAs($viewer)->get($page->getUrl());
         $resp->assertDontSee($page->getUrl('/copy'));
 
-        $newBook->created_by = $viewer->id;
+        $newBook->owned_by = $viewer->id;
         $newBook->save();
         $this->giveUserPermissions($viewer, ['page-create-own']);
         $this->regenEntityPermissions($newBook);
diff --git a/tests/Permissions/EntityOwnerChangeTest.php b/tests/Permissions/EntityOwnerChangeTest.php
new file mode 100644
index 000000000..2f06bff2e
--- /dev/null
+++ b/tests/Permissions/EntityOwnerChangeTest.php
@@ -0,0 +1,50 @@
+<?php namespace Tests\Permissions;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Str;
+use Tests\TestCase;
+
+class EntityOwnerChangeTest extends TestCase
+{
+
+    public function test_changing_page_owner()
+    {
+        $page = Page::query()->first();
+        $user = User::query()->where('id', '!=', $page->owned_by)->first();
+
+        $this->asAdmin()->put($page->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('pages', ['owned_by' => $user->id, 'id' => $page->id]);
+    }
+
+    public function test_changing_chapter_owner()
+    {
+        $chapter = Chapter::query()->first();
+        $user = User::query()->where('id', '!=', $chapter->owned_by)->first();
+
+        $this->asAdmin()->put($chapter->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('chapters', ['owned_by' => $user->id, 'id' => $chapter->id]);
+    }
+
+    public function test_changing_book_owner()
+    {
+        $book = Book::query()->first();
+        $user = User::query()->where('id', '!=', $book->owned_by)->first();
+
+        $this->asAdmin()->put($book->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('books', ['owned_by' => $user->id, 'id' => $book->id]);
+    }
+
+    public function test_changing_shelf_owner()
+    {
+        $shelf = Bookshelf::query()->first();
+        $user = User::query()->where('id', '!=', $shelf->owned_by)->first();
+
+        $this->asAdmin()->put($shelf->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('bookshelves', ['owned_by' => $user->id, 'id' => $shelf->id]);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/Permissions/RolesTest.php b/tests/Permissions/RolesTest.php
index 9f32a9f49..3397ef429 100644
--- a/tests/Permissions/RolesTest.php
+++ b/tests/Permissions/RolesTest.php
@@ -289,7 +289,7 @@ class RolesTest extends BrowserKitTest
     {
         $otherShelf = Bookshelf::first();
         $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
-        $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+        $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
         $this->regenEntityPermissions($ownShelf);
 
         $this->checkAccessPermission('bookshelf-update-own', [
@@ -319,7 +319,7 @@ class RolesTest extends BrowserKitTest
         $this->giveUserPermissions($this->user, ['bookshelf-update-all']);
         $otherShelf = Bookshelf::first();
         $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
-        $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+        $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
         $this->regenEntityPermissions($ownShelf);
 
         $this->checkAccessPermission('bookshelf-delete-own', [
diff --git a/tests/User/UserManagementTest.php b/tests/User/UserManagementTest.php
new file mode 100644
index 000000000..d99d61401
--- /dev/null
+++ b/tests/User/UserManagementTest.php
@@ -0,0 +1,44 @@
+<?php namespace Tests\User;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class UserManagementTest extends TestCase
+{
+
+    public function test_delete()
+    {
+        $editor = $this->getEditor();
+        $resp = $this->asAdmin()->delete("settings/users/{$editor->id}");
+        $resp->assertRedirect("/settings/users");
+        $resp = $this->followRedirects($resp);
+
+        $resp->assertSee("User successfully removed");
+        $this->assertActivityExists(ActivityType::USER_DELETE);
+
+        $this->assertDatabaseMissing('users', ['id' => $editor->id]);
+    }
+
+    public function test_delete_offers_migrate_option()
+    {
+        $editor = $this->getEditor();
+        $resp = $this->asAdmin()->get("settings/users/{$editor->id}/delete");
+        $resp->assertSee("Migrate Ownership");
+        $resp->assertSee("new_owner_id");
+    }
+
+    public function test_delete_with_new_owner_id_changes_ownership()
+    {
+        $page = Page::query()->first();
+        $owner = $page->ownedBy;
+        $newOwner = User::query()->where('id', '!=' , $owner->id)->first();
+
+        $this->asAdmin()->delete("settings/users/{$owner->id}", ['new_owner_id' => $newOwner->id]);
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id,
+            'owned_by' => $newOwner->id,
+        ]);
+    }
+}
\ No newline at end of file