]> BookStack Code Mirror - bookstack/blobdiff - app/Entities/Controllers/PageController.php
Permissions: Cleanup after review of enum implementation PR
[bookstack] / app / Entities / Controllers / PageController.php
index ad75448b34a30fb3fc04a10e435d3607644432e7..67ecb0bb3773bfdb5681b6272d899774b19f0d98 100644 (file)
@@ -6,7 +6,9 @@ use BookStack\Activity\Models\View;
 use BookStack\Activity\Tools\CommentTree;
 use BookStack\Activity\Tools\UserEntityWatchOptions;
 use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Queries\EntityQueries;
+use BookStack\Entities\Queries\PageQueries;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Tools\Cloner;
@@ -15,8 +17,10 @@ use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Tools\PageEditActivity;
 use BookStack\Entities\Tools\PageEditorData;
 use BookStack\Exceptions\NotFoundException;
+use BookStack\Exceptions\NotifyException;
 use BookStack\Exceptions\PermissionsException;
 use BookStack\Http\Controller;
+use BookStack\Permissions\Permission;
 use BookStack\References\ReferenceFetcher;
 use Exception;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -28,6 +32,8 @@ class PageController extends Controller
 {
     public function __construct(
         protected PageRepo $pageRepo,
+        protected PageQueries $queries,
+        protected EntityQueries $entityQueries,
         protected ReferenceFetcher $referenceFetcher
     ) {
     }
@@ -37,10 +43,15 @@ class PageController extends Controller
      *
      * @throws Throwable
      */
-    public function create(string $bookSlug, string $chapterSlug = null)
+    public function create(string $bookSlug, ?string $chapterSlug = null)
     {
-        $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug);
-        $this->checkOwnablePermission('page-create', $parent);
+        if ($chapterSlug) {
+            $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
+        } else {
+            $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug);
+        }
+
+        $this->checkOwnablePermission(Permission::PageCreate, $parent);
 
         // Redirect to draft edit screen if signed in
         if ($this->isSignedIn()) {
@@ -60,14 +71,19 @@ class PageController extends Controller
      *
      * @throws ValidationException
      */
-    public function createAsGuest(Request $request, string $bookSlug, string $chapterSlug = null)
+    public function createAsGuest(Request $request, string $bookSlug, ?string $chapterSlug = null)
     {
         $this->validate($request, [
             'name' => ['required', 'string', 'max:255'],
         ]);
 
-        $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug);
-        $this->checkOwnablePermission('page-create', $parent);
+        if ($chapterSlug) {
+            $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
+        } else {
+            $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug);
+        }
+
+        $this->checkOwnablePermission(Permission::PageCreate, $parent);
 
         $page = $this->pageRepo->getNewDraftPage($parent);
         $this->pageRepo->publishDraft($page, [
@@ -84,10 +100,10 @@ class PageController extends Controller
      */
     public function editDraft(Request $request, string $bookSlug, int $pageId)
     {
-        $draft = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-create', $draft->getParent());
+        $draft = $this->queries->findVisibleByIdOrFail($pageId);
+        $this->checkOwnablePermission(Permission::PageCreate, $draft->getParent());
 
-        $editorData = new PageEditorData($draft, $this->pageRepo, $request->query('editor', ''));
+        $editorData = new PageEditorData($draft, $this->entityQueries, $request->query('editor', ''));
         $this->setPageTitle(trans('entities.pages_edit_draft'));
 
         return view('pages.edit', $editorData->getViewData());
@@ -104,8 +120,8 @@ class PageController extends Controller
         $this->validate($request, [
             'name' => ['required', 'string', 'max:255'],
         ]);
-        $draftPage = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-create', $draftPage->getParent());
+        $draftPage = $this->queries->findVisibleByIdOrFail($pageId);
+        $this->checkOwnablePermission(Permission::PageCreate, $draftPage->getParent());
 
         $page = $this->pageRepo->publishDraft($draftPage, $request->all());
 
@@ -121,19 +137,18 @@ class PageController extends Controller
     public function show(string $bookSlug, string $pageSlug)
     {
         try {
-            $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
+            $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
         } catch (NotFoundException $e) {
-            $page = $this->pageRepo->getByOldSlug($bookSlug, $pageSlug);
+            $revision = $this->entityQueries->revisions->findLatestVersionBySlugs($bookSlug, $pageSlug);
+            $page = $revision->page ?? null;
 
-            if ($page === null) {
+            if (is_null($page)) {
                 throw $e;
             }
 
             return redirect($page->getUrl());
         }
 
-        $this->checkOwnablePermission('page-view', $page);
-
         $pageContent = (new PageContent($page));
         $page->html = $pageContent->render();
         $pageNav = $pageContent->getNavigation($page->html);
@@ -155,7 +170,7 @@ class PageController extends Controller
             'watchOptions'    => new UserEntityWatchOptions(user(), $page),
             'next'            => $nextPreviousLocator->getNext(),
             'previous'        => $nextPreviousLocator->getPrevious(),
-            'referenceCount'  => $this->referenceFetcher->getPageReferenceCountToEntity($page),
+            'referenceCount'  => $this->referenceFetcher->getReferenceCountToEntity($page),
         ]);
     }
 
@@ -166,7 +181,7 @@ class PageController extends Controller
      */
     public function getPageAjax(int $pageId)
     {
-        $page = $this->pageRepo->getById($pageId);
+        $page = $this->queries->findVisibleByIdOrFail($pageId);
         $page->setHidden(array_diff($page->getHidden(), ['html', 'markdown']));
         $page->makeHidden(['book']);
 
@@ -180,10 +195,10 @@ class PageController extends Controller
      */
     public function edit(Request $request, string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-update', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page, $page->getUrl());
 
-        $editorData = new PageEditorData($page, $this->pageRepo, $request->query('editor', ''));
+        $editorData = new PageEditorData($page, $this->entityQueries, $request->query('editor', ''));
         if ($editorData->getWarnings()) {
             $this->showWarningNotification(implode("\n", $editorData->getWarnings()));
         }
@@ -204,8 +219,8 @@ class PageController extends Controller
         $this->validate($request, [
             'name' => ['required', 'string', 'max:255'],
         ]);
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-update', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
 
         $this->pageRepo->update($page, $request->all());
 
@@ -219,8 +234,8 @@ class PageController extends Controller
      */
     public function saveDraft(Request $request, int $pageId)
     {
-        $page = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-update', $page);
+        $page = $this->queries->findVisibleByIdOrFail($pageId);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
 
         if (!$this->isSignedIn()) {
             return $this->jsonError(trans('errors.guests_cannot_save_drafts'), 500);
@@ -244,7 +259,7 @@ class PageController extends Controller
      */
     public function redirectFromLink(int $pageId)
     {
-        $page = $this->pageRepo->getById($pageId);
+        $page = $this->queries->findVisibleByIdOrFail($pageId);
 
         return redirect($page->getUrl());
     }
@@ -256,16 +271,18 @@ class PageController extends Controller
      */
     public function showDelete(string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-delete', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageDelete, $page);
         $this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()]));
-        $times_used_as_template = Book::where('default_template', '=', $page->id)->count();
+        $usedAsTemplate =
+            $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 ||
+            $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0;
 
         return view('pages.delete', [
             'book'    => $page->book,
             'page'    => $page,
             'current' => $page,
-            'times_used_as_template' => $times_used_as_template,
+            'usedAsTemplate' => $usedAsTemplate,
         ]);
     }
 
@@ -276,14 +293,18 @@ class PageController extends Controller
      */
     public function showDeleteDraft(string $bookSlug, int $pageId)
     {
-        $page = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-update', $page);
+        $page = $this->queries->findVisibleByIdOrFail($pageId);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
         $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()]));
+        $usedAsTemplate =
+            $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 ||
+            $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0;
 
         return view('pages.delete', [
             'book'    => $page->book,
             'page'    => $page,
             'current' => $page,
+            'usedAsTemplate' => $usedAsTemplate,
         ]);
     }
 
@@ -295,8 +316,8 @@ class PageController extends Controller
      */
     public function destroy(string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-delete', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageDelete, $page);
         $parent = $page->getParent();
 
         $this->pageRepo->destroy($page);
@@ -312,16 +333,16 @@ class PageController extends Controller
      */
     public function destroyDraft(string $bookSlug, int $pageId)
     {
-        $page = $this->pageRepo->getById($pageId);
+        $page = $this->queries->findVisibleByIdOrFail($pageId);
         $book = $page->book;
         $chapter = $page->chapter;
-        $this->checkOwnablePermission('page-update', $page);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
 
         $this->pageRepo->destroy($page);
 
         $this->showSuccessNotification(trans('entities.pages_delete_draft_success'));
 
-        if ($chapter && userCan('view', $chapter)) {
+        if ($chapter && userCan(Permission::ChapterView, $chapter)) {
             return redirect($chapter->getUrl());
         }
 
@@ -337,7 +358,9 @@ class PageController extends Controller
             $query->scopes('visible');
         };
 
-        $pages = Page::visible()->with(['updatedBy', 'book' => $visibleBelongsScope, 'chapter' => $visibleBelongsScope])
+        $pages = $this->queries->visibleForList()
+            ->addSelect('updated_by')
+            ->with(['updatedBy', 'book' => $visibleBelongsScope, 'chapter' => $visibleBelongsScope])
             ->orderBy('updated_at', 'desc')
             ->paginate(20)
             ->setPath(url('/pages/recently-updated'));
@@ -359,9 +382,9 @@ class PageController extends Controller
      */
     public function showMove(string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-update', $page);
-        $this->checkOwnablePermission('page-delete', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
+        $this->checkOwnablePermission(Permission::PageDelete, $page);
 
         return view('pages.move', [
             'book' => $page->book,
@@ -377,9 +400,9 @@ class PageController extends Controller
      */
     public function move(Request $request, string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-update', $page);
-        $this->checkOwnablePermission('page-delete', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageUpdate, $page);
+        $this->checkOwnablePermission(Permission::PageDelete, $page);
 
         $entitySelection = $request->get('entity_selection', null);
         if ($entitySelection === null || $entitySelection === '') {
@@ -406,8 +429,7 @@ class PageController extends Controller
      */
     public function showCopy(string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-view', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
         session()->flashInput(['name' => $page->name]);
 
         return view('pages.copy', [
@@ -424,19 +446,19 @@ class PageController extends Controller
      */
     public function copy(Request $request, Cloner $cloner, string $bookSlug, string $pageSlug)
     {
-        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->checkOwnablePermission('page-view', $page);
+        $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
+        $this->checkOwnablePermission(Permission::PageView, $page);
 
         $entitySelection = $request->get('entity_selection') ?: null;
-        $newParent = $entitySelection ? $this->pageRepo->findParentByIdentifier($entitySelection) : $page->getParent();
+        $newParent = $entitySelection ? $this->entityQueries->findVisibleByStringIdentifier($entitySelection) : $page->getParent();
 
-        if (is_null($newParent)) {
+        if (!$newParent instanceof Book && !$newParent instanceof Chapter) {
             $this->showErrorNotification(trans('errors.selected_book_chapter_not_found'));
 
             return redirect($page->getUrl('/copy'));
         }
 
-        $this->checkOwnablePermission('page-create', $newParent);
+        $this->checkOwnablePermission(Permission::PageCreate, $newParent);
 
         $newName = $request->get('name') ?: $page->name;
         $pageCopy = $cloner->clonePage($page, $newParent, $newName);