mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-25 09:42:10 +08:00
Rolled out reference pages to all entities, added testing
Including testing to check permissions applied to listed references.
This commit is contained in:
parent
d5465726e2
commit
d198332d3c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
20
resources/views/books/references.blade.php
Normal file
20
resources/views/books/references.blade.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="container small">
|
||||
|
||||
<div class="my-s">
|
||||
@include('entities.breadcrumbs', ['crumbs' => [
|
||||
$book,
|
||||
$book->getUrl('/references') => [
|
||||
'text' => trans('entities.references'),
|
||||
'icon' => 'reference',
|
||||
]
|
||||
]])
|
||||
</div>
|
||||
|
||||
@include('entities.references', ['references' => $references])
|
||||
</div>
|
||||
|
||||
@stop
|
21
resources/views/chapters/references.blade.php
Normal file
21
resources/views/chapters/references.blade.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="container small">
|
||||
|
||||
<div class="my-s">
|
||||
@include('entities.breadcrumbs', ['crumbs' => [
|
||||
$chapter->book,
|
||||
$chapter,
|
||||
$chapter->getUrl('/references') => [
|
||||
'text' => trans('entities.references'),
|
||||
'icon' => 'reference',
|
||||
]
|
||||
]])
|
||||
</div>
|
||||
|
||||
@include('entities.references', ['references' => $references])
|
||||
</div>
|
||||
|
||||
@stop
|
13
resources/views/entities/references.blade.php
Normal file
13
resources/views/entities/references.blade.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<main class="card content-wrap">
|
||||
<h1 class="list-heading">{{ trans('entities.references') }}</h1>
|
||||
<p>{{ trans('entities.references_to_desc') }}</p>
|
||||
|
||||
@if(count($references) > 0)
|
||||
<div class="book-contents">
|
||||
@include('entities.list', ['entities' => $references->pluck('from'), 'showPath' => true])
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted italic">{{ trans('entities.references_none') }}</p>
|
||||
@endif
|
||||
|
||||
</main>
|
|
@ -16,19 +16,7 @@
|
|||
]])
|
||||
</div>
|
||||
|
||||
<main class="card content-wrap">
|
||||
<h1 class="list-heading">{{ trans('entities.references') }}</h1>
|
||||
<p>{{ trans('entities.references_to_desc') }}</p>
|
||||
|
||||
@if(count($references) > 0)
|
||||
<div class="book-contents">
|
||||
@include('entities.list', ['entities' => $references->pluck('from'), 'showPath' => true])
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted italic">{{ trans('entities.references_none') }}</p>
|
||||
@endif
|
||||
|
||||
</main>
|
||||
@include('entities.references', ['references' => $references])
|
||||
</div>
|
||||
|
||||
@stop
|
||||
|
|
20
resources/views/shelves/references.blade.php
Normal file
20
resources/views/shelves/references.blade.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="container small">
|
||||
|
||||
<div class="my-s">
|
||||
@include('entities.breadcrumbs', ['crumbs' => [
|
||||
$shelf,
|
||||
$shelf->getUrl('/references') => [
|
||||
'text' => trans('entities.references'),
|
||||
'icon' => 'reference',
|
||||
]
|
||||
]])
|
||||
</div>
|
||||
|
||||
@include('entities.references', ['references' => $references])
|
||||
</div>
|
||||
|
||||
@stop
|
|
@ -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']);
|
||||
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user