From 222c665018cd7fc231d2970307e3a7423e4a377f Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 5 Feb 2024 17:35:49 +0000 Subject: [PATCH] Queries: Extracted PageRepo queries to own class Started new class for PageRevisions too as part of these changes --- app/Entities/Controllers/BookController.php | 16 ++-- .../Controllers/BookExportController.php | 8 +- .../Controllers/BookSortController.php | 6 +- .../Controllers/BookshelfController.php | 10 +-- .../Controllers/ChapterController.php | 24 ++--- .../Controllers/ChapterExportController.php | 8 +- .../Controllers/PageApiController.php | 10 ++- app/Entities/Controllers/PageController.php | 57 +++++++----- .../Controllers/PageExportController.php | 24 ++--- .../Controllers/PageRevisionController.php | 14 +-- .../Controllers/PageTemplateController.php | 29 ++++--- app/Entities/Queries/BookQueries.php | 2 +- app/Entities/Queries/BookshelfQueries.php | 2 +- app/Entities/Queries/ChapterQueries.php | 4 +- app/Entities/Queries/EntityQueries.php | 1 + app/Entities/Queries/PageQueries.php | 35 ++++++++ app/Entities/Queries/PageRevisionQueries.php | 41 +++++++++ .../Queries/ProvidesEntityQueries.php | 11 ++- app/Entities/Repos/PageRepo.php | 87 ------------------- app/Entities/Tools/PageEditorData.php | 14 +-- .../Controllers/AttachmentController.php | 12 +-- 21 files changed, 219 insertions(+), 196 deletions(-) create mode 100644 app/Entities/Queries/PageRevisionQueries.php diff --git a/app/Entities/Controllers/BookController.php b/app/Entities/Controllers/BookController.php index a956a47d6..0e9346dbd 100644 --- a/app/Entities/Controllers/BookController.php +++ b/app/Entities/Controllers/BookController.php @@ -124,7 +124,7 @@ class BookController extends Controller */ public function show(Request $request, ActivityQueries $activities, string $slug) { - $book = $this->queries->findVisibleBySlug($slug); + $book = $this->queries->findVisibleBySlugOrFail($slug); $bookChildren = (new BookContents($book))->getTree(true); $bookParentShelves = $book->shelves()->scopes('visible')->get(); @@ -151,7 +151,7 @@ class BookController extends Controller */ public function edit(string $slug) { - $book = $this->queries->findVisibleBySlug($slug); + $book = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('book-update', $book); $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()])); @@ -167,7 +167,7 @@ class BookController extends Controller */ public function update(Request $request, string $slug) { - $book = $this->queries->findVisibleBySlug($slug); + $book = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('book-update', $book); $validated = $this->validate($request, [ @@ -194,7 +194,7 @@ class BookController extends Controller */ public function showDelete(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-delete', $book); $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()])); @@ -208,7 +208,7 @@ class BookController extends Controller */ public function destroy(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-delete', $book); $this->bookRepo->destroy($book); @@ -223,7 +223,7 @@ class BookController extends Controller */ public function showCopy(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-view', $book); session()->flashInput(['name' => $book->name]); @@ -240,7 +240,7 @@ class BookController extends Controller */ public function copy(Request $request, Cloner $cloner, string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-view', $book); $this->checkPermission('book-create-all'); @@ -256,7 +256,7 @@ class BookController extends Controller */ public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-update', $book); $this->checkOwnablePermission('book-delete', $book); $this->checkPermission('bookshelf-create-all'); diff --git a/app/Entities/Controllers/BookExportController.php b/app/Entities/Controllers/BookExportController.php index 6540df978..5c1a964c1 100644 --- a/app/Entities/Controllers/BookExportController.php +++ b/app/Entities/Controllers/BookExportController.php @@ -23,7 +23,7 @@ class BookExportController extends Controller */ public function pdf(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $pdfContent = $this->exportFormatter->bookToPdf($book); return $this->download()->directly($pdfContent, $bookSlug . '.pdf'); @@ -36,7 +36,7 @@ class BookExportController extends Controller */ public function html(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $htmlContent = $this->exportFormatter->bookToContainedHtml($book); return $this->download()->directly($htmlContent, $bookSlug . '.html'); @@ -47,7 +47,7 @@ class BookExportController extends Controller */ public function plainText(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $textContent = $this->exportFormatter->bookToPlainText($book); return $this->download()->directly($textContent, $bookSlug . '.txt'); @@ -58,7 +58,7 @@ class BookExportController extends Controller */ public function markdown(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $textContent = $this->exportFormatter->bookToMarkdown($book); return $this->download()->directly($textContent, $bookSlug . '.md'); diff --git a/app/Entities/Controllers/BookSortController.php b/app/Entities/Controllers/BookSortController.php index 5d7024952..5aefc5832 100644 --- a/app/Entities/Controllers/BookSortController.php +++ b/app/Entities/Controllers/BookSortController.php @@ -22,7 +22,7 @@ class BookSortController extends Controller */ public function show(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-update', $book); $bookChildren = (new BookContents($book))->getTree(false); @@ -38,7 +38,7 @@ class BookSortController extends Controller */ public function showItem(string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $bookChildren = (new BookContents($book))->getTree(); return view('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); @@ -49,7 +49,7 @@ class BookSortController extends Controller */ public function update(Request $request, string $bookSlug) { - $book = $this->queries->findVisibleBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('book-update', $book); // Return if no map sent diff --git a/app/Entities/Controllers/BookshelfController.php b/app/Entities/Controllers/BookshelfController.php index bc3fc2a6f..3118325da 100644 --- a/app/Entities/Controllers/BookshelfController.php +++ b/app/Entities/Controllers/BookshelfController.php @@ -103,7 +103,7 @@ class BookshelfController extends Controller */ public function show(Request $request, ActivityQueries $activities, string $slug) { - $shelf = $this->queries->findVisibleBySlug($slug); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('bookshelf-view', $shelf); $listOptions = SimpleListOptions::fromRequest($request, 'shelf_books')->withSortOptions([ @@ -141,7 +141,7 @@ class BookshelfController extends Controller */ public function edit(string $slug) { - $shelf = $this->queries->findVisibleBySlug($slug); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('bookshelf-update', $shelf); $shelfBookIds = $shelf->books()->get(['id'])->pluck('id'); @@ -164,7 +164,7 @@ class BookshelfController extends Controller */ public function update(Request $request, string $slug) { - $shelf = $this->queries->findVisibleBySlug($slug); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('bookshelf-update', $shelf); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], @@ -190,7 +190,7 @@ class BookshelfController extends Controller */ public function showDelete(string $slug) { - $shelf = $this->queries->findVisibleBySlug($slug); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('bookshelf-delete', $shelf); $this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $shelf->getShortName()])); @@ -205,7 +205,7 @@ class BookshelfController extends Controller */ public function destroy(string $slug) { - $shelf = $this->queries->findVisibleBySlug($slug); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); $this->checkOwnablePermission('bookshelf-delete', $shelf); $this->shelfRepo->destroy($shelf); diff --git a/app/Entities/Controllers/ChapterController.php b/app/Entities/Controllers/ChapterController.php index 4b0a525eb..2e36a84b9 100644 --- a/app/Entities/Controllers/ChapterController.php +++ b/app/Entities/Controllers/ChapterController.php @@ -37,7 +37,7 @@ class ChapterController extends Controller */ public function create(string $bookSlug) { - $book = $this->entityQueries->books->findVisibleBySlug($bookSlug); + $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('chapter-create', $book); $this->setPageTitle(trans('entities.chapters_create')); @@ -62,7 +62,7 @@ class ChapterController extends Controller 'default_template_id' => ['nullable', 'integer'], ]); - $book = $this->entityQueries->books->findVisibleBySlug($bookSlug); + $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); $this->checkOwnablePermission('chapter-create', $book); $chapter = $this->chapterRepo->create($validated, $book); @@ -75,7 +75,7 @@ class ChapterController extends Controller */ public function show(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-view', $chapter); $sidebarTree = (new BookContents($chapter->book))->getTree(); @@ -103,7 +103,7 @@ class ChapterController extends Controller */ public function edit(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-update', $chapter); $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()])); @@ -125,7 +125,7 @@ class ChapterController extends Controller 'default_template_id' => ['nullable', 'integer'], ]); - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-update', $chapter); $this->chapterRepo->update($chapter, $validated); @@ -140,7 +140,7 @@ class ChapterController extends Controller */ public function showDelete(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-delete', $chapter); $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()])); @@ -156,7 +156,7 @@ class ChapterController extends Controller */ public function destroy(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-delete', $chapter); $this->chapterRepo->destroy($chapter); @@ -171,7 +171,7 @@ class ChapterController extends Controller */ public function showMove(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()])); $this->checkOwnablePermission('chapter-update', $chapter); $this->checkOwnablePermission('chapter-delete', $chapter); @@ -189,7 +189,7 @@ class ChapterController extends Controller */ public function move(Request $request, string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-update', $chapter); $this->checkOwnablePermission('chapter-delete', $chapter); @@ -218,7 +218,7 @@ class ChapterController extends Controller */ public function showCopy(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-view', $chapter); session()->flashInput(['name' => $chapter->name]); @@ -237,7 +237,7 @@ class ChapterController extends Controller */ public function copy(Request $request, Cloner $cloner, string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-view', $chapter); $entitySelection = $request->get('entity_selection') ?: null; @@ -263,7 +263,7 @@ class ChapterController extends Controller */ public function convertToBook(HierarchyTransformer $transformer, string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->checkOwnablePermission('chapter-update', $chapter); $this->checkOwnablePermission('chapter-delete', $chapter); $this->checkPermission('book-create-all'); diff --git a/app/Entities/Controllers/ChapterExportController.php b/app/Entities/Controllers/ChapterExportController.php index 0e071fc82..ead601ab4 100644 --- a/app/Entities/Controllers/ChapterExportController.php +++ b/app/Entities/Controllers/ChapterExportController.php @@ -25,7 +25,7 @@ class ChapterExportController extends Controller */ public function pdf(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $pdfContent = $this->exportFormatter->chapterToPdf($chapter); return $this->download()->directly($pdfContent, $chapterSlug . '.pdf'); @@ -39,7 +39,7 @@ class ChapterExportController extends Controller */ public function html(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $containedHtml = $this->exportFormatter->chapterToContainedHtml($chapter); return $this->download()->directly($containedHtml, $chapterSlug . '.html'); @@ -52,7 +52,7 @@ class ChapterExportController extends Controller */ public function plainText(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $chapterText = $this->exportFormatter->chapterToPlainText($chapter); return $this->download()->directly($chapterText, $chapterSlug . '.txt'); @@ -65,7 +65,7 @@ class ChapterExportController extends Controller */ public function markdown(string $bookSlug, string $chapterSlug) { - $chapter = $this->queries->findVisibleBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $chapterText = $this->exportFormatter->chapterToMarkdown($chapter); return $this->download()->directly($chapterText, $chapterSlug . '.md'); diff --git a/app/Entities/Controllers/PageApiController.php b/app/Entities/Controllers/PageApiController.php index d2947f1bb..6e3880aed 100644 --- a/app/Entities/Controllers/PageApiController.php +++ b/app/Entities/Controllers/PageApiController.php @@ -5,6 +5,7 @@ namespace BookStack\Entities\Controllers; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\PermissionsException; use BookStack\Http\ApiController; @@ -35,7 +36,8 @@ class PageApiController extends ApiController ]; public function __construct( - protected PageRepo $pageRepo + protected PageRepo $pageRepo, + protected PageQueries $queries, ) { } @@ -97,7 +99,7 @@ class PageApiController extends ApiController */ public function read(string $id) { - $page = $this->pageRepo->getById($id, []); + $page = $this->queries->findVisibleByIdOrFail($id); return response()->json($page->forJsonDisplay()); } @@ -113,7 +115,7 @@ class PageApiController extends ApiController { $requestData = $this->validate($request, $this->rules['update']); - $page = $this->pageRepo->getById($id, []); + $page = $this->queries->findVisibleByIdOrFail($id); $this->checkOwnablePermission('page-update', $page); $parent = null; @@ -148,7 +150,7 @@ class PageApiController extends ApiController */ public function delete(string $id) { - $page = $this->pageRepo->getById($id, []); + $page = $this->queries->findVisibleByIdOrFail($id); $this->checkOwnablePermission('page-delete', $page); $this->pageRepo->destroy($page); diff --git a/app/Entities/Controllers/PageController.php b/app/Entities/Controllers/PageController.php index f2cd729c6..3a5bdbd0b 100644 --- a/app/Entities/Controllers/PageController.php +++ b/app/Entities/Controllers/PageController.php @@ -31,7 +31,7 @@ class PageController extends Controller { public function __construct( protected PageRepo $pageRepo, - protected PageQueries $pageQueries, + protected PageQueries $queries, protected EntityQueries $entityQueries, protected ReferenceFetcher $referenceFetcher ) { @@ -44,7 +44,12 @@ class PageController extends Controller */ public function create(string $bookSlug, string $chapterSlug = null) { - $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug); + if ($chapterSlug) { + $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + } else { + $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + } + $this->checkOwnablePermission('page-create', $parent); // Redirect to draft edit screen if signed in @@ -71,7 +76,12 @@ class PageController extends Controller 'name' => ['required', 'string', 'max:255'], ]); - $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug); + if ($chapterSlug) { + $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + } else { + $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + } + $this->checkOwnablePermission('page-create', $parent); $page = $this->pageRepo->getNewDraftPage($parent); @@ -89,10 +99,10 @@ class PageController extends Controller */ public function editDraft(Request $request, string $bookSlug, int $pageId) { - $draft = $this->pageRepo->getById($pageId); + $draft = $this->queries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-create', $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()); @@ -109,7 +119,7 @@ class PageController extends Controller $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], ]); - $draftPage = $this->pageRepo->getById($pageId); + $draftPage = $this->queries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-create', $draftPage->getParent()); $page = $this->pageRepo->publishDraft($draftPage, $request->all()); @@ -126,11 +136,12 @@ 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; } @@ -171,7 +182,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']); @@ -185,10 +196,10 @@ class PageController extends Controller */ public function edit(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-update', $page); - $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())); } @@ -209,7 +220,7 @@ class PageController extends Controller $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], ]); - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-update', $page); $this->pageRepo->update($page, $request->all()); @@ -224,7 +235,7 @@ class PageController extends Controller */ public function saveDraft(Request $request, int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-update', $page); if (!$this->isSignedIn()) { @@ -249,7 +260,7 @@ class PageController extends Controller */ public function redirectFromLink(int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); return redirect($page->getUrl()); } @@ -261,7 +272,7 @@ class PageController extends Controller */ public function showDelete(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-delete', $page); $this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()])); $usedAsTemplate = @@ -283,7 +294,7 @@ class PageController extends Controller */ public function showDeleteDraft(string $bookSlug, int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-update', $page); $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()])); $usedAsTemplate = @@ -306,7 +317,7 @@ class PageController extends Controller */ public function destroy(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-delete', $page); $parent = $page->getParent(); @@ -323,7 +334,7 @@ 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); @@ -370,7 +381,7 @@ class PageController extends Controller */ public function showMove(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-update', $page); $this->checkOwnablePermission('page-delete', $page); @@ -388,7 +399,7 @@ class PageController extends Controller */ public function move(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-update', $page); $this->checkOwnablePermission('page-delete', $page); @@ -417,7 +428,7 @@ class PageController extends Controller */ public function showCopy(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-view', $page); session()->flashInput(['name' => $page->name]); @@ -435,7 +446,7 @@ class PageController extends Controller */ public function copy(Request $request, Cloner $cloner, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-view', $page); $entitySelection = $request->get('entity_selection') ?: null; diff --git a/app/Entities/Controllers/PageExportController.php b/app/Entities/Controllers/PageExportController.php index 31862c8ac..be97f1930 100644 --- a/app/Entities/Controllers/PageExportController.php +++ b/app/Entities/Controllers/PageExportController.php @@ -2,7 +2,7 @@ namespace BookStack\Entities\Controllers; -use BookStack\Entities\Repos\PageRepo; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Tools\ExportFormatter; use BookStack\Entities\Tools\PageContent; use BookStack\Exceptions\NotFoundException; @@ -11,16 +11,10 @@ use Throwable; class PageExportController extends Controller { - protected $pageRepo; - protected $exportFormatter; - - /** - * PageExportController constructor. - */ - public function __construct(PageRepo $pageRepo, ExportFormatter $exportFormatter) - { - $this->pageRepo = $pageRepo; - $this->exportFormatter = $exportFormatter; + public function __construct( + protected PageQueries $queries, + protected ExportFormatter $exportFormatter, + ) { $this->middleware('can:content-export'); } @@ -33,7 +27,7 @@ class PageExportController extends Controller */ public function pdf(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $page->html = (new PageContent($page))->render(); $pdfContent = $this->exportFormatter->pageToPdf($page); @@ -48,7 +42,7 @@ class PageExportController extends Controller */ public function html(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $page->html = (new PageContent($page))->render(); $containedHtml = $this->exportFormatter->pageToContainedHtml($page); @@ -62,7 +56,7 @@ class PageExportController extends Controller */ public function plainText(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $pageText = $this->exportFormatter->pageToPlainText($page); return $this->download()->directly($pageText, $pageSlug . '.txt'); @@ -75,7 +69,7 @@ class PageExportController extends Controller */ public function markdown(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $pageText = $this->exportFormatter->pageToMarkdown($page); return $this->download()->directly($pageText, $pageSlug . '.md'); diff --git a/app/Entities/Controllers/PageRevisionController.php b/app/Entities/Controllers/PageRevisionController.php index a3190a0fc..232d40668 100644 --- a/app/Entities/Controllers/PageRevisionController.php +++ b/app/Entities/Controllers/PageRevisionController.php @@ -4,6 +4,7 @@ namespace BookStack\Entities\Controllers; use BookStack\Activity\ActivityType; use BookStack\Entities\Models\PageRevision; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Entities\Repos\RevisionRepo; use BookStack\Entities\Tools\PageContent; @@ -18,6 +19,7 @@ class PageRevisionController extends Controller { public function __construct( protected PageRepo $pageRepo, + protected PageQueries $pageQueries, protected RevisionRepo $revisionRepo, ) { } @@ -29,7 +31,7 @@ class PageRevisionController extends Controller */ public function index(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $listOptions = SimpleListOptions::fromRequest($request, 'page_revisions', true)->withSortOptions([ 'id' => trans('entities.pages_revisions_sort_number') ]); @@ -60,7 +62,7 @@ class PageRevisionController extends Controller */ public function show(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); /** @var ?PageRevision $revision */ $revision = $page->revisions()->where('id', '=', $revisionId)->first(); if ($revision === null) { @@ -89,7 +91,7 @@ class PageRevisionController extends Controller */ public function changes(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); /** @var ?PageRevision $revision */ $revision = $page->revisions()->where('id', '=', $revisionId)->first(); if ($revision === null) { @@ -121,7 +123,7 @@ class PageRevisionController extends Controller */ public function restore(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-update', $page); $page = $this->pageRepo->restoreRevision($page, $revisionId); @@ -136,7 +138,7 @@ class PageRevisionController extends Controller */ public function destroy(string $bookSlug, string $pageSlug, int $revId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $this->checkOwnablePermission('page-delete', $page); $revision = $page->revisions()->where('id', '=', $revId)->first(); @@ -162,7 +164,7 @@ class PageRevisionController extends Controller */ public function destroyUserDraft(string $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->revisionRepo->deleteDraftsForCurrentUser($page); return response('', 200); diff --git a/app/Entities/Controllers/PageTemplateController.php b/app/Entities/Controllers/PageTemplateController.php index e4e7b5680..c0b972148 100644 --- a/app/Entities/Controllers/PageTemplateController.php +++ b/app/Entities/Controllers/PageTemplateController.php @@ -2,6 +2,7 @@ namespace BookStack\Entities\Controllers; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; @@ -9,14 +10,10 @@ use Illuminate\Http\Request; class PageTemplateController extends Controller { - protected $pageRepo; - - /** - * PageTemplateController constructor. - */ - public function __construct(PageRepo $pageRepo) - { - $this->pageRepo = $pageRepo; + public function __construct( + protected PageRepo $pageRepo, + protected PageQueries $pageQueries, + ) { } /** @@ -26,7 +23,19 @@ class PageTemplateController extends Controller { $page = $request->get('page', 1); $search = $request->get('search', ''); - $templates = $this->pageRepo->getTemplates(10, $page, $search); + $count = 10; + + $query = $this->pageQueries->visibleTemplates() + ->orderBy('name', 'asc') + ->skip(($page - 1) * $count) + ->take($count); + + if ($search) { + $query->where('name', 'like', '%' . $search . '%'); + } + + $templates = $query->paginate($count, ['*'], 'page', $page); + $templates->withPath('/templates'); if ($search) { $templates->appends(['search' => $search]); @@ -44,7 +53,7 @@ class PageTemplateController extends Controller */ public function get(int $templateId) { - $page = $this->pageRepo->getById($templateId); + $page = $this->pageQueries->findVisibleByIdOrFail($templateId); if (!$page->template) { throw new NotFoundException(); diff --git a/app/Entities/Queries/BookQueries.php b/app/Entities/Queries/BookQueries.php index 5e85523cf..3d7474b3d 100644 --- a/app/Entities/Queries/BookQueries.php +++ b/app/Entities/Queries/BookQueries.php @@ -18,7 +18,7 @@ class BookQueries implements ProvidesEntityQueries return $this->start()->scopes('visible')->find($id); } - public function findVisibleBySlug(string $slug): Book + public function findVisibleBySlugOrFail(string $slug): Book { /** @var ?Book $book */ $book = $this->start() diff --git a/app/Entities/Queries/BookshelfQueries.php b/app/Entities/Queries/BookshelfQueries.php index 52e123087..d61607e7a 100644 --- a/app/Entities/Queries/BookshelfQueries.php +++ b/app/Entities/Queries/BookshelfQueries.php @@ -18,7 +18,7 @@ class BookshelfQueries implements ProvidesEntityQueries return $this->start()->scopes('visible')->find($id); } - public function findVisibleBySlug(string $slug): Bookshelf + public function findVisibleBySlugOrFail(string $slug): Bookshelf { /** @var ?Bookshelf $shelf */ $shelf = $this->start() diff --git a/app/Entities/Queries/ChapterQueries.php b/app/Entities/Queries/ChapterQueries.php index dcfc4aad3..200514932 100644 --- a/app/Entities/Queries/ChapterQueries.php +++ b/app/Entities/Queries/ChapterQueries.php @@ -24,7 +24,7 @@ class ChapterQueries implements ProvidesEntityQueries return $this->start()->scopes('visible')->find($id); } - public function findVisibleBySlugs(string $bookSlug, string $chapterSlug): Chapter + public function findVisibleBySlugsOrFail(string $bookSlug, string $chapterSlug): Chapter { /** @var ?Chapter $chapter */ $chapter = $this->start()->with('book') @@ -34,7 +34,7 @@ class ChapterQueries implements ProvidesEntityQueries ->where('slug', '=', $chapterSlug) ->first(); - if ($chapter === null) { + if (is_null($chapter)) { throw new NotFoundException(trans('errors.chapter_not_found')); } diff --git a/app/Entities/Queries/EntityQueries.php b/app/Entities/Queries/EntityQueries.php index 39a21c913..31e5b913a 100644 --- a/app/Entities/Queries/EntityQueries.php +++ b/app/Entities/Queries/EntityQueries.php @@ -11,6 +11,7 @@ class EntityQueries public BookQueries $books, public ChapterQueries $chapters, public PageQueries $pages, + public PageRevisionQueries $revisions, ) { } diff --git a/app/Entities/Queries/PageQueries.php b/app/Entities/Queries/PageQueries.php index f1991626f..1640dc2db 100644 --- a/app/Entities/Queries/PageQueries.php +++ b/app/Entities/Queries/PageQueries.php @@ -3,6 +3,7 @@ namespace BookStack\Entities\Queries; use BookStack\Entities\Models\Page; +use BookStack\Exceptions\NotFoundException; use Illuminate\Database\Eloquent\Builder; class PageQueries implements ProvidesEntityQueries @@ -17,6 +18,34 @@ class PageQueries implements ProvidesEntityQueries return $this->start()->scopes('visible')->find($id); } + public function findVisibleByIdOrFail(int $id): Page + { + $page = $this->findVisibleById($id); + + if (is_null($page)) { + throw new NotFoundException(trans('errors.page_not_found')); + } + + return $page; + } + + public function findVisibleBySlugsOrFail(string $bookSlug, string $pageSlug): Page + { + /** @var ?Page $page */ + $page = $this->start()->with('book') + ->whereHas('book', function (Builder $query) use ($bookSlug) { + $query->where('slug', '=', $bookSlug); + }) + ->where('slug', '=', $pageSlug) + ->first(); + + if (is_null($page)) { + throw new NotFoundException(trans('errors.chapter_not_found')); + } + + return $page; + } + public function visibleForList(): Builder { return $this->start() @@ -33,4 +62,10 @@ class PageQueries implements ProvidesEntityQueries ->where('draft', '=', true) ->where('created_by', '=', user()->id); } + + public function visibleTemplates(): Builder + { + return $this->visibleForList() + ->where('template', '=', true); + } } diff --git a/app/Entities/Queries/PageRevisionQueries.php b/app/Entities/Queries/PageRevisionQueries.php new file mode 100644 index 000000000..2dcd428f5 --- /dev/null +++ b/app/Entities/Queries/PageRevisionQueries.php @@ -0,0 +1,41 @@ +whereHas('page', function (Builder $query) { + $query->scopes('visible'); + }) + ->where('slug', '=', $pageSlug) + ->where('type', '=', 'version') + ->where('book_slug', '=', $bookSlug) + ->orderBy('created_at', 'desc') + ->first(); + } + + public function findLatestCurrentUserDraftsForPageId(int $pageId): ?PageRevision + { + return $this->latestCurrentUserDraftsForPageId($pageId)->first(); + } + + public function latestCurrentUserDraftsForPageId(int $pageId): Builder + { + return $this->start() + ->where('created_by', '=', user()->id) + ->where('type', 'update_draft') + ->where('page_id', '=', $pageId) + ->orderBy('created_at', 'desc'); + } +} diff --git a/app/Entities/Queries/ProvidesEntityQueries.php b/app/Entities/Queries/ProvidesEntityQueries.php index 5c37b02e4..ea83d6cdd 100644 --- a/app/Entities/Queries/ProvidesEntityQueries.php +++ b/app/Entities/Queries/ProvidesEntityQueries.php @@ -2,9 +2,18 @@ namespace BookStack\Entities\Queries; -use BookStack\Entities\Models\Entity; +use BookStack\App\Model; use Illuminate\Database\Eloquent\Builder; +/** + * Interface for our classes which provide common queries for our + * entity objects. Ideally all queries for entities should run through + * these classes. + * Any added methods should return a builder instances to allow extension + * via building on the query, unless the method starts with 'find' + * in which case an entity object should be returned. + * (nullable unless it's a *OrFail method). + */ interface ProvidesEntityQueries { public function start(): Builder; diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 929de528d..74aed072a 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -14,13 +14,11 @@ use BookStack\Entities\Tools\PageContent; use BookStack\Entities\Tools\PageEditorData; use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; -use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\PermissionsException; use BookStack\Facades\Activity; use BookStack\References\ReferenceStore; use BookStack\References\ReferenceUpdater; use Exception; -use Illuminate\Pagination\LengthAwarePaginator; class PageRepo { @@ -33,91 +31,6 @@ class PageRepo ) { } - /** - * Get a page by ID. - * - * @throws NotFoundException - */ - public function getById(int $id, array $relations = ['book']): Page - { - /** @var Page $page */ - $page = Page::visible()->with($relations)->find($id); - - if (!$page) { - throw new NotFoundException(trans('errors.page_not_found')); - } - - return $page; - } - - /** - * Get a page its book and own slug. - * - * @throws NotFoundException - */ - public function getBySlug(string $bookSlug, string $pageSlug): Page - { - $page = Page::visible()->whereSlugs($bookSlug, $pageSlug)->first(); - - if (!$page) { - throw new NotFoundException(trans('errors.page_not_found')); - } - - return $page; - } - - /** - * Get a page by its old slug but checking the revisions table - * for the last revision that matched the given page and book slug. - */ - public function getByOldSlug(string $bookSlug, string $pageSlug): ?Page - { - $revision = $this->revisionRepo->getBySlugs($bookSlug, $pageSlug); - - return $revision->page ?? null; - } - - /** - * Get pages that have been marked as a template. - */ - public function getTemplates(int $count = 10, int $page = 1, string $search = ''): LengthAwarePaginator - { - $query = Page::visible() - ->where('template', '=', true) - ->orderBy('name', 'asc') - ->skip(($page - 1) * $count) - ->take($count); - - if ($search) { - $query->where('name', 'like', '%' . $search . '%'); - } - - $paginator = $query->paginate($count, ['*'], 'page', $page); - $paginator->withPath('/templates'); - - return $paginator; - } - - /** - * Get a parent item via slugs. - */ - public function getParentFromSlugs(string $bookSlug, string $chapterSlug = null): Entity - { - if ($chapterSlug !== null) { - return Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->firstOrFail(); - } - - return Book::visible()->where('slug', '=', $bookSlug)->firstOrFail(); - } - - /** - * Get the draft copy of the given page for the current user. - */ - public function getUserDraft(Page $page): ?PageRevision - { - return $this->revisionRepo->getLatestDraftForCurrentUser($page); - } - /** * Get a new draft page belonging to the given parent entity. */ diff --git a/app/Entities/Tools/PageEditorData.php b/app/Entities/Tools/PageEditorData.php index 3c7c9e2ea..20bf19eb2 100644 --- a/app/Entities/Tools/PageEditorData.php +++ b/app/Entities/Tools/PageEditorData.php @@ -4,7 +4,7 @@ namespace BookStack\Entities\Tools; use BookStack\Activity\Tools\CommentTree; use BookStack\Entities\Models\Page; -use BookStack\Entities\Repos\PageRepo; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\Markdown\HtmlToMarkdown; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; @@ -15,7 +15,7 @@ class PageEditorData public function __construct( protected Page $page, - protected PageRepo $pageRepo, + protected EntityQueries $queries, protected string $requestedEditor ) { $this->viewData = $this->build(); @@ -35,7 +35,11 @@ class PageEditorData { $page = clone $this->page; $isDraft = boolval($this->page->draft); - $templates = $this->pageRepo->getTemplates(10); + $templates = $this->queries->pages->visibleTemplates() + ->orderBy('name', 'asc') + ->take(10) + ->get(); + $draftsEnabled = auth()->check(); $isDraftRevision = false; @@ -47,8 +51,8 @@ class PageEditorData } // Check for a current draft version for this user - $userDraft = $this->pageRepo->getUserDraft($page); - if ($userDraft !== null) { + $userDraft = $this->queries->revisions->findLatestCurrentUserDraftsForPageId($page->id)->first(); + if (!is_null($userDraft)) { $page->forceFill($userDraft->only(['name', 'html', 'markdown'])); $isDraftRevision = true; $this->warnings[] = $editActivity->getEditingActiveDraftMessage($userDraft); diff --git a/app/Uploads/Controllers/AttachmentController.php b/app/Uploads/Controllers/AttachmentController.php index e61c10338..809cdfa58 100644 --- a/app/Uploads/Controllers/AttachmentController.php +++ b/app/Uploads/Controllers/AttachmentController.php @@ -2,6 +2,7 @@ namespace BookStack\Uploads\Controllers; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\FileUploadException; use BookStack\Exceptions\NotFoundException; @@ -18,6 +19,7 @@ class AttachmentController extends Controller { public function __construct( protected AttachmentService $attachmentService, + protected PageQueries $pageQueries, protected PageRepo $pageRepo ) { } @@ -36,7 +38,7 @@ class AttachmentController extends Controller ]); $pageId = $request->get('uploaded_to'); - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->checkPermission('attachment-create-all'); $this->checkOwnablePermission('page-update', $page); @@ -152,7 +154,7 @@ class AttachmentController extends Controller ]), 422); } - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->checkPermission('attachment-create-all'); $this->checkOwnablePermission('page-update', $page); @@ -173,7 +175,7 @@ class AttachmentController extends Controller */ public function listForPage(int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-view', $page); return view('attachments.manager-list', [ @@ -192,7 +194,7 @@ class AttachmentController extends Controller $this->validate($request, [ 'order' => ['required', 'array'], ]); - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->checkOwnablePermission('page-update', $page); $attachmentOrder = $request->get('order'); @@ -213,7 +215,7 @@ class AttachmentController extends Controller $attachment = Attachment::query()->findOrFail($attachmentId); try { - $page = $this->pageRepo->getById($attachment->uploaded_to); + $page = $this->pageQueries->findVisibleByIdOrFail($attachment->uploaded_to); } catch (NotFoundException $exception) { throw new NotFoundException(trans('errors.attachment_not_found')); }