diff --git a/app/Entities/Bookshelf.php b/app/Entities/Bookshelf.php index 8f2cdf8ae..08ce8d8cb 100644 --- a/app/Entities/Bookshelf.php +++ b/app/Entities/Bookshelf.php @@ -92,4 +92,14 @@ class Bookshelf extends Entity { return "'BookStack\\\\BookShelf' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text,'' as html, '0' as book_id, '0' as priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at"; } + + /** + * Check if this shelf contains the given book. + * @param Book $book + * @return bool + */ + public function contains(Book $book) + { + return $this->books()->where('id', '=', $book->id)->count() > 0; + } } diff --git a/app/Entities/BreadcrumbsViewComposer.php b/app/Entities/BreadcrumbsViewComposer.php new file mode 100644 index 000000000..7745d9aee --- /dev/null +++ b/app/Entities/BreadcrumbsViewComposer.php @@ -0,0 +1,34 @@ +entityContextManager = $entityContextManager; + } + + /** + * Modify data when the view is composed. + * @param View $view + */ + public function compose(View $view) + { + $crumbs = $view->getData()['crumbs']; + if (array_first($crumbs) instanceof Book) { + $shelf = $this->entityContextManager->getContextualShelfForBook(array_first($crumbs)); + if ($shelf) { + array_unshift($crumbs, $shelf); + $view->with('crumbs', $crumbs); + } + } + } +} \ No newline at end of file diff --git a/app/Entities/EntityContextManager.php b/app/Entities/EntityContextManager.php new file mode 100644 index 000000000..23ffed0d2 --- /dev/null +++ b/app/Entities/EntityContextManager.php @@ -0,0 +1,62 @@ +session = $session; + $this->entityRepo = $entityRepo; + } + + /** + * Get the current bookshelf context for the given book. + * @param Book $book + * @return Bookshelf|null + */ + public function getContextualShelfForBook(Book $book) + { + $contextBookshelfId = $this->session->get($this->KEY_SHELF_CONTEXT_ID, null); + if (is_int($contextBookshelfId)) { + + /** @var Bookshelf $shelf */ + $shelf = $this->entityRepo->getById('bookshelf', $contextBookshelfId); + + if ($shelf && $shelf->contains($book)) { + return $shelf; + } + + } + return null; + } + + /** + * Store the current contextual shelf ID. + * @param int $shelfId + */ + public function setShelfContext(int $shelfId) + { + $this->session->put($this->KEY_SHELF_CONTEXT_ID, $shelfId); + } + + /** + * Clear the session stored shelf context id. + */ + public function clearShelfContext() + { + $this->session->forget($this->KEY_SHELF_CONTEXT_ID); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index 431ff640d..9eb19ce65 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -3,6 +3,7 @@ use Activity; use BookStack\Auth\UserRepo; use BookStack\Entities\Book; +use BookStack\Entities\EntityContextManager; use BookStack\Entities\Repos\EntityRepo; use BookStack\Entities\ExportService; use Illuminate\Http\Request; @@ -15,18 +16,25 @@ class BookController extends Controller protected $entityRepo; protected $userRepo; protected $exportService; + protected $entityContextManager; /** * BookController constructor. * @param EntityRepo $entityRepo - * @param \BookStack\Auth\UserRepo $userRepo - * @param \BookStack\Entities\ExportService $exportService + * @param UserRepo $userRepo + * @param ExportService $exportService + * @param EntityContextManager $entityContextManager */ - public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService) - { + public function __construct( + EntityRepo $entityRepo, + UserRepo $userRepo, + ExportService $exportService, + EntityContextManager $entityContextManager + ) { $this->entityRepo = $entityRepo; $this->userRepo = $userRepo; $this->exportService = $exportService; + $this->entityContextManager = $entityContextManager; parent::__construct(); } @@ -50,6 +58,8 @@ class BookController extends Controller $popular = $this->entityRepo->getPopular('book', 4, 0); $new = $this->entityRepo->getRecentlyCreated('book', 4, 0); + $this->entityContextManager->clearShelfContext(); + $this->setPageTitle(trans('entities.books')); return view('books.index', [ 'books' => $books, @@ -95,14 +105,22 @@ class BookController extends Controller /** * Display the specified book. * @param $slug + * @param Request $request * @return Response + * @throws \BookStack\Exceptions\NotFoundException */ - public function show($slug) + public function show($slug, Request $request) { $book = $this->entityRepo->getBySlug('book', $slug); $this->checkOwnablePermission('book-view', $book); + $bookChildren = $this->entityRepo->getBookChildren($book); + Views::add($book); + if ($request->has('shelf')) { + $this->entityContextManager->setShelfContext(intval($request->get('shelf'))); + } + $this->setPageTitle($book->getShortName()); return view('books.show', [ 'book' => $book, diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php index 2fa9ca332..b86bc2e38 100644 --- a/app/Http/Controllers/BookshelfController.php +++ b/app/Http/Controllers/BookshelfController.php @@ -3,6 +3,7 @@ use Activity; use BookStack\Auth\UserRepo; use BookStack\Entities\Bookshelf; +use BookStack\Entities\EntityContextManager; use BookStack\Entities\Repos\EntityRepo; use Illuminate\Http\Request; use Illuminate\Http\Response; @@ -13,16 +14,19 @@ class BookshelfController extends Controller protected $entityRepo; protected $userRepo; + protected $entityContextManager; /** * BookController constructor. * @param EntityRepo $entityRepo * @param UserRepo $userRepo + * @param EntityContextManager $entityContextManager */ - public function __construct(EntityRepo $entityRepo, UserRepo $userRepo) + public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, EntityContextManager $entityContextManager) { $this->entityRepo = $entityRepo; $this->userRepo = $userRepo; + $this->entityContextManager = $entityContextManager; parent::__construct(); } @@ -32,9 +36,7 @@ class BookshelfController extends Controller */ public function index() { - $view = setting()->getUser($this->currentUser, 'bookshelves_view_type', config('app.views.bookshelves', 'grid')); - $sort = setting()->getUser($this->currentUser, 'bookshelves_sort', 'name'); $order = setting()->getUser($this->currentUser, 'bookshelves_sort_order', 'asc'); $sortOptions = [ @@ -43,14 +45,16 @@ class BookshelfController extends Controller 'updated_at' => trans('common.sort_updated_at'), ]; - $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18, $sort, $order, function($query) { - $query->with(['books']); - }); + $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18, $sort, $order); + foreach ($shelves as $shelf) { + $shelf->books = $this->entityRepo->getBookshelfChildren($shelf); + } + $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('bookshelf', 4, 0) : false; $popular = $this->entityRepo->getPopular('bookshelf', 4, 0); $new = $this->entityRepo->getRecentlyCreated('bookshelf', 4, 0); - + $this->entityContextManager->clearShelfContext(); $this->setPageTitle(trans('entities.shelves')); return view('shelves.index', [ 'shelves' => $shelves, @@ -105,11 +109,13 @@ class BookshelfController extends Controller */ public function show(string $slug) { - $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug); /** @var $bookshelf Bookshelf */ + /** @var Bookshelf $bookshelf */ + $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug); $this->checkOwnablePermission('book-view', $bookshelf); $books = $this->entityRepo->getBookshelfChildren($bookshelf); Views::add($bookshelf); + $this->entityContextManager->setShelfContext($bookshelf->id); $this->setPageTitle($bookshelf->getShortName()); return view('shelves.show', [ diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 7fc618ea9..4bcf7b40e 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -1,35 +1,45 @@ entityRepo = $entityRepo; $this->viewService = $viewService; $this->searchService = $searchService; + $this->entityContextManager = $entityContextManager; parent::__construct(); } /** * Searches all entities. * @param Request $request - * @return \Illuminate\View\View + * @return View * @internal param string $searchTerm */ public function search(Request $request) @@ -56,7 +66,7 @@ class SearchController extends Controller * Searches all entities within a book. * @param Request $request * @param integer $bookId - * @return \Illuminate\View\View + * @return View * @internal param string $searchTerm */ public function searchBook(Request $request, $bookId) @@ -70,7 +80,7 @@ class SearchController extends Controller * Searches all entities within a chapter. * @param Request $request * @param integer $chapterId - * @return \Illuminate\View\View + * @return View * @internal param string $searchTerm */ public function searchChapter(Request $request, $chapterId) @@ -106,7 +116,7 @@ class SearchController extends Controller /** * Search siblings items in the system. * @param Request $request - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|mixed + * @return Factory|View|mixed */ public function searchSiblings(Request $request) { @@ -130,16 +140,21 @@ class SearchController extends Controller $entities = $this->entityRepo->getBookDirectChildren($entity->book); } - // Book in shelf - // TODO - When shelve tracking added, Update below if criteria - // Book + // Gets just the books in a shelf if shelf is in context if ($entity->isA('book')) { - $entities = $this->entityRepo->getAll('book'); + $contextShelf = $this->entityContextManager->getContextualShelfForBook($entity); + if ($contextShelf) { + $entities = $this->entityRepo->getBookshelfChildren($contextShelf); + } else { + $entities = $this->entityRepo->getAll('book'); + } } // Shelve - // TODO - When shelve tracking added + if ($entity->isA('bookshelf')) { + $entities = $this->entityRepo->getAll('bookshelf'); + } return view('partials.entity-list-basic', ['entities' => $entities, 'style' => 'compact']); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3ca59dcb3..9b91ba126 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,12 +3,14 @@ use Blade; use BookStack\Entities\Book; use BookStack\Entities\Bookshelf; +use BookStack\Entities\BreadcrumbsViewComposer; use BookStack\Entities\Chapter; use BookStack\Entities\Page; use BookStack\Settings\Setting; use BookStack\Settings\SettingService; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Schema; use Validator; @@ -33,7 +35,6 @@ class AppServiceProvider extends ServiceProvider return substr_count($uploadName, '.') < 2; }); - // Custom blade view directives Blade::directive('icon', function ($expression) { return ""; @@ -49,6 +50,9 @@ class AppServiceProvider extends ServiceProvider 'BookStack\\Chapter' => Chapter::class, 'BookStack\\Page' => Page::class, ]); + + // View Composers + View::composer('partials.breadcrumbs', BreadcrumbsViewComposer::class); } /** diff --git a/resources/assets/js/components/breadcrumb-listing.js b/resources/assets/js/components/breadcrumb-listing.js index e4f4e5302..1e2fe9ea4 100644 --- a/resources/assets/js/components/breadcrumb-listing.js +++ b/resources/assets/js/components/breadcrumb-listing.js @@ -25,10 +25,8 @@ class BreadcrumbListing { onSearch() { const input = this.searchInput.value.toLowerCase().trim(); const listItems = this.entityListElem.querySelectorAll('.entity-list-item'); - console.log(listItems); for (let listItem of listItems) { const match = !input || listItem.textContent.toLowerCase().includes(input); - console.log(match); listItem.style.display = match ? 'flex' : 'none'; } } diff --git a/resources/views/shelves/list-item.blade.php b/resources/views/shelves/list-item.blade.php index a3bb2fd52..c9c9670c5 100644 --- a/resources/views/shelves/list-item.blade.php +++ b/resources/views/shelves/list-item.blade.php @@ -12,7 +12,7 @@