diff --git a/app/Entities/Tools/BookContents.php b/app/Entities/Tools/BookContents.php
index ff018eda9..bdbc4262d 100644
--- a/app/Entities/Tools/BookContents.php
+++ b/app/Entities/Tools/BookContents.php
@@ -116,8 +116,17 @@ class BookContents
         // Load models into map
         $modelMap = $this->loadModelsFromSortMap($sortMap);
 
+        // Sort our changes from our map to be chapters first
+        // Since they need to be process to ensure book alignment for child page changes.
+        $sortMapItems = $sortMap->all();
+        usort($sortMapItems, function(BookSortMapItem $itemA, BookSortMapItem $itemB) {
+            $aScore = $itemA->type === 'page' ? 2 : 1;
+            $bScore = $itemB->type === 'page' ? 2 : 1;
+            return $aScore - $bScore;
+        });
+
         // Perform the sort
-        foreach ($sortMap->all() as $item) {
+        foreach ($sortMapItems as $item) {
             $this->applySortUpdates($item, $modelMap);
         }
 
@@ -163,32 +172,23 @@ class BookContents
              $currentParentKey = 'chapter:' . $model->chapter_id;
         }
 
-        $currentParent = $modelMap[$currentParentKey];
+        $currentParent = $modelMap[$currentParentKey] ?? null;
         /** @var Book $newBook */
-        $newBook = $modelMap['book:' . $sortMapItem->parentBookId] ?? null;
+        $newBook = $modelMap['book:' . $sortMapItem->parentBookId];
         /** @var ?Chapter $newChapter */
         $newChapter = $sortMapItem->parentChapterId ? ($modelMap['chapter:' . $sortMapItem->parentChapterId] ?? null) : null;
 
-        // Check permissions of our changes to be made
-        if (!$currentParent || !$newBook) {
-            return;
-        } else if (!userCan('chapter-update', $currentParent) && !userCan('book-update', $currentParent)) {
-            return;
-        } else if ($bookChanged && !$newChapter && !userCan('book-update', $newBook)) {
-            return;
-        } else if ($newChapter && !userCan('chapter-update', $newChapter)) {
-            return;
-        } else if (($chapterChanged || $bookChanged) && $newChapter && $newBook->id !== $newChapter->book_id) {
+        if (!$this->isSortChangePermissible($sortMapItem, $model, $currentParent, $newBook, $newChapter)) {
             return;
         }
 
         // Action the required changes
         if ($bookChanged) {
-            $model->changeBook($sortMapItem->parentBookId);
+            $model->changeBook($newBook->id);
         }
 
         if ($chapterChanged) {
-            $model->chapter_id = $sortMapItem->parentChapterId ?? 0;
+            $model->chapter_id = $newChapter->id ?? 0;
         }
 
         if ($priorityChanged) {
@@ -200,6 +200,51 @@ class BookContents
         }
     }
 
+    /**
+     * Check if the current user has permissions to apply the given sorting change.
+     */
+    protected function isSortChangePermissible(BookSortMapItem $sortMapItem, Entity $model, ?Entity $currentParent, ?Entity $newBook, ?Entity $newChapter): bool
+    {
+        // TODO - Move operations check for create permissions, Needs these also/instead?
+
+        // Stop if we can't see the current parent or new book.
+        if (!$currentParent || !$newBook) {
+            return false;
+        }
+
+        if ($model instanceof Chapter) {
+            $hasPermission = userCan('book-update', $currentParent)
+                && userCan('book-update', $newBook);
+            if (!$hasPermission) {
+                return false;
+            }
+        }
+
+        if ($model instanceof Page) {
+            $parentPermission = ($currentParent instanceof Chapter) ? 'chapter-update' : 'book-update';
+            $hasCurrentParentPermission = userCan($parentPermission, $currentParent);
+
+            // This needs to check if there was an intended chapter location in the original sort map
+            // rather than inferring from the $newChapter since that variable may be null
+            // due to other reasons (Visibility).
+            $newParent = $sortMapItem->parentChapterId ? $newChapter : $newBook;
+            if (!$newParent) {
+                return false;
+            }
+
+            $newParentInRightLocation = ($newParent instanceof Book || $newParent->book_id === $newBook->id);
+            $newParentPermission = ($newParent instanceof Chapter) ? 'chapter-update' : 'book-update';
+            $hasNewParentPermission = userCan($newParentPermission, $newParent);
+
+            $hasPermission = $hasCurrentParentPermission && $newParentInRightLocation && $hasNewParentPermission;
+            if (!$hasPermission) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     /**
      * Load models from the database into the given sort map.
      * @return array<string, Entity>
diff --git a/tests/Entity/SortTest.php b/tests/Entity/SortTest.php
index 89279bfcf..07e8b8ca8 100644
--- a/tests/Entity/SortTest.php
+++ b/tests/Entity/SortTest.php
@@ -239,20 +239,20 @@ class SortTest extends TestCase
         // Create request data
         $reqData = [
             [
-                'id'            => $chapterToMove->id,
-                'sort'          => 0,
+                'id' => $chapterToMove->id,
+                'sort' => 0,
                 'parentChapter' => false,
-                'type'          => 'chapter',
-                'book'          => $newBook->id,
+                'type' => 'chapter',
+                'book' => $newBook->id,
             ],
         ];
         foreach ($pagesToMove as $index => $page) {
             $reqData[] = [
-                'id'            => $page->id,
-                'sort'          => $index,
+                'id' => $page->id,
+                'sort' => $index,
                 'parentChapter' => $index === count($pagesToMove) - 1 ? $chapterToMove->id : false,
-                'type'          => 'page',
-                'book'          => $newBook->id,
+                'type' => 'page',
+                'book' => $newBook->id,
             ];
         }
 
@@ -260,18 +260,83 @@ class SortTest extends TestCase
         $sortResp->assertRedirect($newBook->getUrl());
         $sortResp->assertStatus(302);
         $this->assertDatabaseHas('chapters', [
-            'id'       => $chapterToMove->id,
-            'book_id'  => $newBook->id,
+            'id' => $chapterToMove->id,
+            'book_id' => $newBook->id,
             'priority' => 0,
         ]);
         $this->assertTrue($newBook->chapters()->count() === 1);
         $this->assertTrue($newBook->chapters()->first()->pages()->count() === 1);
 
         $checkPage = $pagesToMove[1];
-        $checkResp = $this->get(Page::find($checkPage->id)->getUrl());
+        $checkResp = $this->get($checkPage->refresh()->getUrl());
         $checkResp->assertSee($newBook->name);
     }
 
+    public function test_book_sort_makes_no_changes_if_new_chapter_does_not_align_with_new_book()
+    {
+        /** @var Page $page */
+        $page = Page::query()->where('chapter_id', '!=', 0)->first();
+        $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
+
+        $sortData = [
+            'id' => $page->id,
+            'sort' => 0,
+            'parentChapter' => $otherChapter->id,
+            'type' => 'page',
+            'book' => $page->book_id,
+        ];
+        $this->asEditor()->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
+        ]);
+    }
+
+    public function test_book_sort_makes_no_changes_if_no_view_permissions_on_new_chapter()
+    {
+        /** @var Page $page */
+        $page = Page::query()->where('chapter_id', '!=', 0)->first();
+        /** @var Chapter $otherChapter */
+        $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
+        $this->setEntityRestrictions($otherChapter);
+
+        $sortData = [
+            'id' => $page->id,
+            'sort' => 0,
+            'parentChapter' => $otherChapter->id,
+            'type' => 'page',
+            'book' => $otherChapter->book_id,
+        ];
+        $this->asEditor()->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
+        ]);
+    }
+
+    public function test_book_sort_makes_no_changes_if_no_update_permissions_on_new_chapter()
+    {
+        /** @var Page $page */
+        $page = Page::query()->where('chapter_id', '!=', 0)->first();
+        /** @var Chapter $otherChapter */
+        $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
+        $editor = $this->getEditor();
+        $this->setEntityRestrictions($otherChapter, ['view'], [$editor->roles()->first()]);
+
+        $sortData = [
+            'id' => $page->id,
+            'sort' => 0,
+            'parentChapter' => $otherChapter->id,
+            'type' => 'page',
+            'book' => $otherChapter->book_id,
+        ];
+        $this->actingAs($editor)->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
+        ]);
+    }
+
     public function test_book_sort_item_returns_book_content()
     {
         $books = Book::all();