]> BookStack Code Mirror - bookstack/commitdiff
Added entity meta link to reference page
authorDan Brown <redacted>
Sat, 20 Aug 2022 11:07:38 +0000 (12:07 +0100)
committerDan Brown <redacted>
Sat, 20 Aug 2022 11:07:38 +0000 (12:07 +0100)
Not totally happy with implementation as is requires extra service to be
injected to core controllers, but does the job.
Included test to cover.
Updated some controller properties to be typed while there.

12 files changed:
app/Console/Commands/RegenerateReferences.php
app/Entities/Repos/PageRepo.php
app/Http/Controllers/BookController.php
app/Http/Controllers/BookshelfController.php
app/Http/Controllers/ChapterController.php
app/Http/Controllers/PageController.php
app/Http/Controllers/ReferenceController.php
app/References/ReferenceFetcher.php [new file with mode: 0644]
app/References/ReferenceStore.php [moved from app/References/ReferenceService.php with 98% similarity]
resources/lang/en/entities.php
resources/views/entities/meta.blade.php
tests/References/ReferencesTest.php

index 93450c5ea68c5aa6637f7beccf136a4b63f13d0e..805db2207c30dcf09d6e15f1bec9c5de3647eb13 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace BookStack\Console\Commands;
 
-use BookStack\References\ReferenceService;
+use BookStack\References\ReferenceStore;
 use Illuminate\Console\Command;
 use Illuminate\Support\Facades\DB;
 
@@ -22,14 +22,14 @@ class RegenerateReferences extends Command
      */
     protected $description = 'Regenerate all the cross-item model reference index';
 
-    protected ReferenceService $references;
+    protected ReferenceStore $references;
 
     /**
      * Create a new command instance.
      *
      * @return void
      */
-    public function __construct(ReferenceService $references)
+    public function __construct(ReferenceStore $references)
     {
         $this->references = $references;
         parent::__construct();
index 09c664edc6db888649d860193763377abcf9d7cc..40d1e6e53313ffea5d063ff91c99c2b872586975 100644 (file)
@@ -16,7 +16,7 @@ use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\PermissionsException;
 use BookStack\Facades\Activity;
-use BookStack\References\ReferenceService;
+use BookStack\References\ReferenceStore;
 use Exception;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Pagination\LengthAwarePaginator;
@@ -24,12 +24,12 @@ use Illuminate\Pagination\LengthAwarePaginator;
 class PageRepo
 {
     protected BaseRepo $baseRepo;
-    protected ReferenceService $references;
+    protected ReferenceStore $references;
 
     /**
      * PageRepo constructor.
      */
-    public function __construct(BaseRepo $baseRepo, ReferenceService $references)
+    public function __construct(BaseRepo $baseRepo, ReferenceStore $references)
     {
         $this->baseRepo = $baseRepo;
         $this->references = $references;
index c5b6d0bf6def5ee529769aba77de0c8e42194ad1..a041267bbdf87df17e4711a903200db2001ccb81 100644 (file)
@@ -15,19 +15,22 @@ use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Facades\Activity;
+use BookStack\References\ReferenceFetcher;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
 
 class BookController extends Controller
 {
-    protected $bookRepo;
-    protected $entityContextManager;
+    protected BookRepo $bookRepo;
+    protected ShelfContext $shelfContext;
+    protected ReferenceFetcher $referenceFetcher;
 
-    public function __construct(ShelfContext $entityContextManager, BookRepo $bookRepo)
+    public function __construct(ShelfContext $entityContextManager, BookRepo $bookRepo, ReferenceFetcher $referenceFetcher)
     {
         $this->bookRepo = $bookRepo;
-        $this->entityContextManager = $entityContextManager;
+        $this->shelfContext = $entityContextManager;
+        $this->referenceFetcher = $referenceFetcher;
     }
 
     /**
@@ -44,7 +47,7 @@ class BookController extends Controller
         $popular = $this->bookRepo->getPopular(4);
         $new = $this->bookRepo->getRecentlyCreated(4);
 
-        $this->entityContextManager->clearShelfContext();
+        $this->shelfContext->clearShelfContext();
 
         $this->setPageTitle(trans('entities.books'));
 
@@ -122,7 +125,7 @@ class BookController extends Controller
 
         View::incrementFor($book);
         if ($request->has('shelf')) {
-            $this->entityContextManager->setShelfContext(intval($request->get('shelf')));
+            $this->shelfContext->setShelfContext(intval($request->get('shelf')));
         }
 
         $this->setPageTitle($book->getShortName());
@@ -133,6 +136,7 @@ class BookController extends Controller
             'bookChildren'      => $bookChildren,
             'bookParentShelves' => $bookParentShelves,
             'activity'          => $activities->entityActivity($book, 20, 1),
+            'referenceCount'    => $this->referenceFetcher->getPageReferenceCountToEntity($book),
         ]);
     }
 
index ccbeb6484b0db0720f4d55cf872d74e810d1d6d6..2143b876a517dc10dd8df31f09101af8ef97e90e 100644 (file)
@@ -10,6 +10,7 @@ use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
+use BookStack\References\ReferenceFetcher;
 use Exception;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
@@ -18,11 +19,13 @@ class BookshelfController extends Controller
 {
     protected BookshelfRepo $shelfRepo;
     protected ShelfContext $shelfContext;
+    protected ReferenceFetcher $referenceFetcher;
 
-    public function __construct(BookshelfRepo $shelfRepo, ShelfContext $shelfContext)
+    public function __construct(BookshelfRepo $shelfRepo, ShelfContext $shelfContext, ReferenceFetcher $referenceFetcher)
     {
         $this->shelfRepo = $shelfRepo;
         $this->shelfContext = $shelfContext;
+        $this->referenceFetcher = $referenceFetcher;
     }
 
     /**
@@ -124,6 +127,7 @@ class BookshelfController extends Controller
             'activity'                => $activities->entityActivity($shelf, 20, 1),
             'order'                   => $order,
             'sort'                    => $sort,
+            'referenceCount'          => $this->referenceFetcher->getPageReferenceCountToEntity($shelf),
         ]);
     }
 
index 60eb523800fc369edb694db27e0bcdb065825e15..735c760be2d059f255709c9f22c071fd1d80811c 100644 (file)
@@ -13,20 +13,21 @@ use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\PermissionsException;
+use BookStack\References\ReferenceFetcher;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
 
 class ChapterController extends Controller
 {
-    protected $chapterRepo;
+    protected ChapterRepo $chapterRepo;
+    protected ReferenceFetcher $referenceFetcher;
 
-    /**
-     * ChapterController constructor.
-     */
-    public function __construct(ChapterRepo $chapterRepo)
+
+    public function __construct(ChapterRepo $chapterRepo, ReferenceFetcher $referenceFetcher)
     {
         $this->chapterRepo = $chapterRepo;
+        $this->referenceFetcher = $referenceFetcher;
     }
 
     /**
@@ -77,13 +78,14 @@ class ChapterController extends Controller
         $this->setPageTitle($chapter->getShortName());
 
         return view('chapters.show', [
-            'book'        => $chapter->book,
-            'chapter'     => $chapter,
-            'current'     => $chapter,
-            'sidebarTree' => $sidebarTree,
-            'pages'       => $pages,
-            'next'        => $nextPreviousLocator->getNext(),
-            'previous'    => $nextPreviousLocator->getPrevious(),
+            'book'           => $chapter->book,
+            'chapter'        => $chapter,
+            'current'        => $chapter,
+            'sidebarTree'    => $sidebarTree,
+            'pages'          => $pages,
+            'next'           => $nextPreviousLocator->getNext(),
+            'previous'       => $nextPreviousLocator->getPrevious(),
+            'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($chapter),
         ]);
     }
 
index 268dce0573a9c51a0beacb264c7dc9ca14435481..748468b211fd1939e8d211861936dec3afca1078 100644 (file)
@@ -14,6 +14,7 @@ use BookStack\Entities\Tools\PageEditorData;
 use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\PermissionsException;
+use BookStack\References\ReferenceFetcher;
 use Exception;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Http\Request;
@@ -23,13 +24,15 @@ use Throwable;
 class PageController extends Controller
 {
     protected PageRepo $pageRepo;
+    protected ReferenceFetcher $referenceFetcher;
 
     /**
      * PageController constructor.
      */
-    public function __construct(PageRepo $pageRepo)
+    public function __construct(PageRepo $pageRepo, ReferenceFetcher $referenceFetcher)
     {
         $this->pageRepo = $pageRepo;
+        $this->referenceFetcher = $referenceFetcher;
     }
 
     /**
@@ -160,6 +163,7 @@ class PageController extends Controller
             'pageNav'         => $pageNav,
             'next'            => $nextPreviousLocator->getNext(),
             'previous'        => $nextPreviousLocator->getPrevious(),
+            'referenceCount'  => $this->referenceFetcher->getPageReferenceCountToEntity($page),
         ]);
     }
 
index 3af4feb06108b9cd29afd5a131561ead8b5e9f09..07b143223587a271c2cd843f971886199ef8ebff 100644 (file)
@@ -2,23 +2,19 @@
 
 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;
+use BookStack\References\ReferenceFetcher;
 
 class ReferenceController extends Controller
 {
+    protected ReferenceFetcher $referenceFetcher;
 
-    protected PermissionApplicator $permissions;
-
-    public function __construct(PermissionApplicator $permissions)
+    public function __construct(ReferenceFetcher $referenceFetcher)
     {
-        $this->permissions = $permissions;
+        $this->referenceFetcher = $referenceFetcher;
     }
 
     /**
@@ -28,7 +24,7 @@ class ReferenceController extends Controller
     {
         /** @var Page $page */
         $page = Page::visible()->whereSlugs($bookSlug, $pageSlug)->firstOrFail();
-        $references = $this->getEntityReferences($page);
+        $references = $this->referenceFetcher->getPageReferencesToEntity($page);
 
         return view('pages.references', [
             'page' => $page,
@@ -43,7 +39,7 @@ class ReferenceController extends Controller
     {
         /** @var Chapter $chapter */
         $chapter = Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->firstOrFail();
-        $references = $this->getEntityReferences($chapter);
+        $references = $this->referenceFetcher->getPageReferencesToEntity($chapter);
 
         return view('chapters.references', [
             'chapter' => $chapter,
@@ -57,7 +53,7 @@ class ReferenceController extends Controller
     public function book(string $slug)
     {
         $book = Book::visible()->where('slug', '=', $slug)->firstOrFail();
-        $references = $this->getEntityReferences($book);
+        $references = $this->referenceFetcher->getPageReferencesToEntity($book);
 
         return view('books.references', [
             'book' => $book,
@@ -71,35 +67,11 @@ class ReferenceController extends Controller
     public function shelf(string $slug)
     {
         $shelf = Bookshelf::visible()->where('slug', '=', $slug)->firstOrFail();
-        $references = $this->getEntityReferences($shelf);
+        $references = $this->referenceFetcher->getPageReferencesToEntity($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),
-                'from.book' => fn(Relation $query) => $query->scopes('visible'),
-                'from.chapter' => fn(Relation $query) => $query->scopes('visible')
-            ]);
-
-        $references = $this->permissions->restrictEntityRelationQuery(
-            $baseQuery,
-            'references',
-            'from_id',
-            'from_type'
-        )->get();
-
-        return $references;
-    }
 }
diff --git a/app/References/ReferenceFetcher.php b/app/References/ReferenceFetcher.php
new file mode 100644 (file)
index 0000000..fef2744
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+namespace BookStack\References;
+
+use BookStack\Auth\Permissions\PermissionApplicator;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Relations\Relation;
+
+class ReferenceFetcher
+{
+    protected PermissionApplicator $permissions;
+
+    public function __construct(PermissionApplicator $permissions)
+    {
+        $this->permissions = $permissions;
+    }
+
+    /**
+     * Query and return the page references pointing to the given entity.
+     * Loads the commonly required relations while taking permissions into account.
+     */
+    public function getPageReferencesToEntity(Entity $entity): Collection
+    {
+        $baseQuery = $entity->referencesTo()
+            ->where('from_type', '=', (new Page())->getMorphClass())
+            ->with([
+                'from' => fn(Relation $query) => $query->select(Page::$listAttributes),
+                'from.book' => fn(Relation $query) => $query->scopes('visible'),
+                'from.chapter' => fn(Relation $query) => $query->scopes('visible')
+            ]);
+
+        $references = $this->permissions->restrictEntityRelationQuery(
+            $baseQuery,
+            'references',
+            'from_id',
+            'from_type'
+        )->get();
+
+        return $references;
+    }
+
+    /**
+     * Returns the count of page references pointing to the given entity.
+     * Takes permissions into account.
+     */
+    public function getPageReferenceCountToEntity(Entity $entity): int
+    {
+        $baseQuery = $entity->referencesTo()
+            ->where('from_type', '=', (new Page())->getMorphClass());
+
+        $count = $this->permissions->restrictEntityRelationQuery(
+            $baseQuery,
+            'references',
+            'from_id',
+            'from_type'
+        )->count();
+
+        return $count;
+    }
+}
\ No newline at end of file
similarity index 98%
rename from app/References/ReferenceService.php
rename to app/References/ReferenceStore.php
index fd7f74ae11f1330c9a985fc5a7a639ef18ad402d..f6e3c04a386f04d4facb21d75d2a5f5ab11a72db 100644 (file)
@@ -5,7 +5,7 @@ namespace BookStack\References;
 use BookStack\Entities\Models\Page;
 use Illuminate\Database\Eloquent\Collection;
 
-class ReferenceService
+class ReferenceStore
 {
 
     /**
index a92b465b8192f7d7566a908d83632902162018a2..527665f8875ba55b5ac9e4c69f2ff7b223d89fdb 100644 (file)
@@ -23,6 +23,7 @@ return [
     'meta_updated' => 'Updated :timeLength',
     'meta_updated_name' => 'Updated :timeLength by :user',
     'meta_owned_name' => 'Owned by :user',
+    'meta_reference_page_count' => 'Referenced on 1 page|Referenced on :count pages',
     'entity_select' => 'Entity Select',
     'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item',
     'images' => 'Images',
index 83ff2376220afe9dce33ff85904bc1ba5a515320..ac91eeed35f4c5b0a30432f70a031d684946630d 100644 (file)
             <span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
         </div>
     @endif
+
+    @if($referenceCount ?? 0)
+        <a href="{{ $entity->getUrl('/references') }}" class="entity-meta-item">
+            @icon('reference')
+            <div>
+                {!! trans_choice('entities.meta_reference_page_count', $referenceCount, ['count' => $referenceCount]) !!}
+            </div>
+        </a>
+    @endif
 </div>
\ No newline at end of file
index 20829b6b4ac695d4c39c5c458834a033b20cd199..9ae226bb71dd17a6c910833249fe5a67091e0deb 100644 (file)
@@ -54,6 +54,28 @@ class ReferencesTest extends TestCase
         $this->assertDatabaseMissing('references', ['to_id' => $pageA->id, 'to_type' => $pageA->getMorphClass()]);
     }
 
+    public function test_references_to_count_visible_on_entity_show_view()
+    {
+        $entities = $this->getEachEntityType();
+        /** @var Page $otherPage */
+        $otherPage = Page::query()->where('id', '!=', $entities['page']->id)->first();
+
+        $this->asEditor();
+        foreach ($entities as $entity) {
+            $this->createReference($entities['page'], $entity);
+        }
+
+        foreach ($entities as $entity) {
+            $resp = $this->get($entity->getUrl());
+            $resp->assertSee('Referenced on 1 page');
+            $resp->assertDontSee('Referenced on 1 pages');
+        }
+
+        $this->createReference($otherPage, $entities['page']);
+        $resp = $this->get($entities['page']->getUrl());
+        $resp->assertSee('Referenced on 2 pages');
+    }
+
     public function test_references_to_visible_on_references_page()
     {
         $entities = $this->getEachEntityType();