From d198332d3c7866d5eb807b9e07a4b95fd67b97c2 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Fri, 19 Aug 2022 22:40:44 +0100 Subject: [PATCH] Rolled out reference pages to all entities, added testing Including testing to check permissions applied to listed references. --- app/Http/Controllers/ReferenceController.php | 68 +++++++++++++++++-- resources/views/books/references.blade.php | 20 ++++++ resources/views/chapters/references.blade.php | 21 ++++++ resources/views/entities/references.blade.php | 13 ++++ resources/views/pages/references.blade.php | 14 +--- resources/views/shelves/references.blade.php | 20 ++++++ routes/web.php | 3 + tests/References/ReferencesTest.php | 40 +++++++++++ tests/TestCase.php | 2 +- 9 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 resources/views/books/references.blade.php create mode 100644 resources/views/chapters/references.blade.php create mode 100644 resources/views/entities/references.blade.php create mode 100644 resources/views/shelves/references.blade.php diff --git a/app/Http/Controllers/ReferenceController.php b/app/Http/Controllers/ReferenceController.php index bed2c5f30..3af4feb06 100644 --- a/app/Http/Controllers/ReferenceController.php +++ b/app/Http/Controllers/ReferenceController.php @@ -3,7 +3,12 @@ namespace BookStack\Http\Controllers; use BookStack\Auth\Permissions\PermissionApplicator; +use BookStack\Entities\Models\Book; +use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Models\Chapter; +use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Relations\Relation; class ReferenceController extends Controller @@ -23,8 +28,64 @@ class ReferenceController extends Controller { /** @var Page $page */ $page = Page::visible()->whereSlugs($bookSlug, $pageSlug)->firstOrFail(); + $references = $this->getEntityReferences($page); - $baseQuery = $page->referencesTo() + return view('pages.references', [ + 'page' => $page, + 'references' => $references, + ]); + } + + /** + * Display the references to a given chapter. + */ + public function chapter(string $bookSlug, string $chapterSlug) + { + /** @var Chapter $chapter */ + $chapter = Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->firstOrFail(); + $references = $this->getEntityReferences($chapter); + + return view('chapters.references', [ + 'chapter' => $chapter, + 'references' => $references, + ]); + } + + /** + * Display the references to a given book. + */ + public function book(string $slug) + { + $book = Book::visible()->where('slug', '=', $slug)->firstOrFail(); + $references = $this->getEntityReferences($book); + + return view('books.references', [ + 'book' => $book, + 'references' => $references, + ]); + } + + /** + * Display the references to a given shelf. + */ + public function shelf(string $slug) + { + $shelf = Bookshelf::visible()->where('slug', '=', $slug)->firstOrFail(); + $references = $this->getEntityReferences($shelf); + + return view('shelves.references', [ + 'shelf' => $shelf, + 'references' => $references, + ]); + } + + /** + * Query the references for the given entities. + * Loads the commonly required relations while taking permissions into account. + */ + protected function getEntityReferences(Entity $entity): Collection + { + $baseQuery = $entity->referencesTo() ->where('from_type', '=', (new Page())->getMorphClass()) ->with([ 'from' => fn(Relation $query) => $query->select(Page::$listAttributes), @@ -39,9 +100,6 @@ class ReferenceController extends Controller 'from_type' )->get(); - return view('pages.references', [ - 'page' => $page, - 'references' => $references, - ]); + return $references; } } diff --git a/resources/views/books/references.blade.php b/resources/views/books/references.blade.php new file mode 100644 index 000000000..2468ed111 --- /dev/null +++ b/resources/views/books/references.blade.php @@ -0,0 +1,20 @@ +@extends('layouts.simple') + +@section('body') + +
+ +
+ @include('entities.breadcrumbs', ['crumbs' => [ + $book, + $book->getUrl('/references') => [ + 'text' => trans('entities.references'), + 'icon' => 'reference', + ] + ]]) +
+ + @include('entities.references', ['references' => $references]) +
+ +@stop diff --git a/resources/views/chapters/references.blade.php b/resources/views/chapters/references.blade.php new file mode 100644 index 000000000..7241c2b55 --- /dev/null +++ b/resources/views/chapters/references.blade.php @@ -0,0 +1,21 @@ +@extends('layouts.simple') + +@section('body') + +
+ +
+ @include('entities.breadcrumbs', ['crumbs' => [ + $chapter->book, + $chapter, + $chapter->getUrl('/references') => [ + 'text' => trans('entities.references'), + 'icon' => 'reference', + ] + ]]) +
+ + @include('entities.references', ['references' => $references]) +
+ +@stop diff --git a/resources/views/entities/references.blade.php b/resources/views/entities/references.blade.php new file mode 100644 index 000000000..db9e167aa --- /dev/null +++ b/resources/views/entities/references.blade.php @@ -0,0 +1,13 @@ +
+

{{ trans('entities.references') }}

+

{{ trans('entities.references_to_desc') }}

+ + @if(count($references) > 0) +
+ @include('entities.list', ['entities' => $references->pluck('from'), 'showPath' => true]) +
+ @else +

{{ trans('entities.references_none') }}

+ @endif + +
\ No newline at end of file diff --git a/resources/views/pages/references.blade.php b/resources/views/pages/references.blade.php index 3f35a1629..42ae7076f 100644 --- a/resources/views/pages/references.blade.php +++ b/resources/views/pages/references.blade.php @@ -16,19 +16,7 @@ ]]) -
-

{{ trans('entities.references') }}

-

{{ trans('entities.references_to_desc') }}

- - @if(count($references) > 0) -
- @include('entities.list', ['entities' => $references->pluck('from'), 'showPath' => true]) -
- @else -

{{ trans('entities.references_none') }}

- @endif - -
+ @include('entities.references', ['references' => $references]) @stop diff --git a/resources/views/shelves/references.blade.php b/resources/views/shelves/references.blade.php new file mode 100644 index 000000000..7336c07af --- /dev/null +++ b/resources/views/shelves/references.blade.php @@ -0,0 +1,20 @@ +@extends('layouts.simple') + +@section('body') + +
+ +
+ @include('entities.breadcrumbs', ['crumbs' => [ + $shelf, + $shelf->getUrl('/references') => [ + 'text' => trans('entities.references'), + 'icon' => 'reference', + ] + ]]) +
+ + @include('entities.references', ['references' => $references]) +
+ +@stop diff --git a/routes/web.php b/routes/web.php index a16960283..dc46821cb 100644 --- a/routes/web.php +++ b/routes/web.php @@ -64,6 +64,7 @@ Route::middleware('auth')->group(function () { Route::get('/shelves/{slug}/permissions', [BookshelfController::class, 'showPermissions']); Route::put('/shelves/{slug}/permissions', [BookshelfController::class, 'permissions']); Route::post('/shelves/{slug}/copy-permissions', [BookshelfController::class, 'copyPermissions']); + Route::get('/shelves/{slug}/references', [ReferenceController::class, 'shelf']); // Book Creation Route::get('/shelves/{shelfSlug}/create-book', [BookController::class, 'create']); @@ -86,6 +87,7 @@ Route::middleware('auth')->group(function () { Route::post('/books/{bookSlug}/convert-to-shelf', [BookController::class, 'convertToShelf']); Route::get('/books/{bookSlug}/sort', [BookSortController::class, 'show']); Route::put('/books/{bookSlug}/sort', [BookSortController::class, 'update']); + Route::get('/books/{slug}/references', [ReferenceController::class, 'book']); Route::get('/books/{bookSlug}/export/html', [BookExportController::class, 'html']); Route::get('/books/{bookSlug}/export/pdf', [BookExportController::class, 'pdf']); Route::get('/books/{bookSlug}/export/markdown', [BookExportController::class, 'markdown']); @@ -142,6 +144,7 @@ Route::middleware('auth')->group(function () { Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/markdown', [ChapterExportController::class, 'markdown']); Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/plaintext', [ChapterExportController::class, 'plainText']); Route::put('/books/{bookSlug}/chapter/{chapterSlug}/permissions', [ChapterController::class, 'permissions']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/references', [ReferenceController::class, 'chapter']); Route::get('/books/{bookSlug}/chapter/{chapterSlug}/delete', [ChapterController::class, 'showDelete']); Route::delete('/books/{bookSlug}/chapter/{chapterSlug}', [ChapterController::class, 'destroy']); diff --git a/tests/References/ReferencesTest.php b/tests/References/ReferencesTest.php index 1285f5916..20829b6b4 100644 --- a/tests/References/ReferencesTest.php +++ b/tests/References/ReferencesTest.php @@ -54,6 +54,46 @@ class ReferencesTest extends TestCase $this->assertDatabaseMissing('references', ['to_id' => $pageA->id, 'to_type' => $pageA->getMorphClass()]); } + public function test_references_to_visible_on_references_page() + { + $entities = $this->getEachEntityType(); + $this->asEditor(); + foreach ($entities as $entity) { + $this->createReference($entities['page'], $entity); + } + + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl('/references')); + $resp->assertSee('References'); + $resp->assertSee($entities['page']->name); + $resp->assertDontSee('There are no tracked references'); + } + } + + public function test_reference_not_visible_if_view_permission_does_not_permit() + { + /** @var Page $page */ + /** @var Page $pageB */ + $page = Page::query()->first(); + $pageB = Page::query()->where('id', '!=', $page->id)->first(); + $this->createReference($pageB, $page); + + $this->setEntityRestrictions($pageB); + + $this->asEditor()->get($page->getUrl('/references'))->assertDontSee($pageB->name); + $this->asAdmin()->get($page->getUrl('/references'))->assertSee($pageB->name); + } + + public function test_reference_page_shows_empty_state_with_no_references() + { + /** @var Page $page */ + $page = Page::query()->first(); + + $this->asEditor() + ->get($page->getUrl('/references')) + ->assertSee('There are no tracked references'); + } + protected function createReference(Model $from, Model $to) { (new Reference())->forceFill([ diff --git a/tests/TestCase.php b/tests/TestCase.php index 92ae33a4e..3ca7638c8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -430,7 +430,7 @@ abstract class TestCase extends BaseTestCase } /** - * @return Entity[] + * @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf} */ protected function getEachEntityType(): array {