$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
$recents = $this->isSignedIn() ?
(new RecentlyViewed())->run(12 * $recentFactor, 1)
- : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
+ : $this->queries->books->visibleForList()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
$favourites = (new TopFavourites())->run(6);
$recentlyUpdatedPages = $this->queries->pages->visibleForList()
->where('draft', false)
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
use BookStack\Http\ApiController;
class BookApiController extends ApiController
{
public function __construct(
- protected BookRepo $bookRepo
+ protected BookRepo $bookRepo,
+ protected BookQueries $queries,
) {
}
*/
public function list()
{
- $books = Book::visible();
+ $books = $this->queries->visibleForList();
return $this->apiListingResponse($books, [
'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by',
*/
public function read(string $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail(intval($id));
$book = $this->forJsonDisplay($book);
$book->load(['createdBy', 'updatedBy', 'ownedBy']);
*/
public function update(Request $request, string $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail(intval($id));
$this->checkOwnablePermission('book-update', $book);
$requestData = $this->validate($request, $this->rules()['update']);
*/
public function delete(string $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail(intval($id));
$this->checkOwnablePermission('book-delete', $book);
$this->bookRepo->destroy($book);
namespace BookStack\Entities\Controllers;
-use BookStack\Entities\Models\Book;
+use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Http\ApiController;
use Throwable;
class BookExportApiController extends ApiController
{
- protected $exportFormatter;
-
- public function __construct(ExportFormatter $exportFormatter)
- {
- $this->exportFormatter = $exportFormatter;
+ public function __construct(
+ protected ExportFormatter $exportFormatter,
+ protected BookQueries $queries,
+ ) {
$this->middleware('can:content-export');
}
*/
public function exportPdf(int $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail($id);
$pdfContent = $this->exportFormatter->bookToPdf($book);
return $this->download()->directly($pdfContent, $book->slug . '.pdf');
*/
public function exportHtml(int $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail($id);
$htmlContent = $this->exportFormatter->bookToContainedHtml($book);
return $this->download()->directly($htmlContent, $book->slug . '.html');
*/
public function exportPlainText(int $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail($id);
$textContent = $this->exportFormatter->bookToPlainText($book);
return $this->download()->directly($textContent, $book->slug . '.txt');
*/
public function exportMarkdown(int $id)
{
- $book = Book::visible()->findOrFail($id);
+ $book = $this->queries->findVisibleByIdOrFail($id);
$markdown = $this->exportFormatter->bookToMarkdown($book);
return $this->download()->directly($markdown, $book->slug . '.md');
use BookStack\Activity\ActivityQueries;
use BookStack\Activity\Models\View;
-use BookStack\Entities\Models\Book;
+use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Queries\BookshelfQueries;
use BookStack\Entities\Repos\BookshelfRepo;
use BookStack\Entities\Tools\ShelfContext;
public function __construct(
protected BookshelfRepo $shelfRepo,
protected BookshelfQueries $queries,
+ protected BookQueries $bookQueries,
protected ShelfContext $shelfContext,
protected ReferenceFetcher $referenceFetcher,
) {
public function create()
{
$this->checkPermission('bookshelf-create-all');
- $books = Book::visible()->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']);
+ $books = $this->bookQueries->visibleForList()->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']);
$this->setPageTitle(trans('entities.shelves_create'));
return view('shelves.create', ['books' => $books]);
$this->checkOwnablePermission('bookshelf-update', $shelf);
$shelfBookIds = $shelf->books()->get(['id'])->pluck('id');
- $books = Book::visible()->whereNotIn('id', $shelfBookIds)->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']);
+ $books = $this->bookQueries->visibleForList()
+ ->whereNotIn('id', $shelfBookIds)
+ ->orderBy('name')
+ ->get(['name', 'id', 'slug', 'created_at', 'updated_at']);
$this->setPageTitle(trans('entities.shelves_edit_named', ['name' => $shelf->getShortName()]));
namespace BookStack\Entities\Controllers;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Queries\BookQueries;
+use BookStack\Entities\Queries\ChapterQueries;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Exceptions\PermissionsException;
use BookStack\Http\ApiController;
];
public function __construct(
- protected ChapterRepo $chapterRepo
+ protected ChapterRepo $chapterRepo,
+ protected ChapterQueries $queries,
+ protected BookQueries $bookQueries,
) {
}
*/
public function list()
{
- $chapters = Chapter::visible();
+ $chapters = $this->queries->visibleForList();
return $this->apiListingResponse($chapters, [
'id', 'book_id', 'name', 'slug', 'description', 'priority',
$requestData = $this->validate($request, $this->rules['create']);
$bookId = $request->get('book_id');
- $book = Book::visible()->findOrFail($bookId);
+ $book = $this->bookQueries->findVisibleByIdOrFail(intval($bookId));
$this->checkOwnablePermission('chapter-create', $book);
$chapter = $this->chapterRepo->create($requestData, $book);
*/
public function read(string $id)
{
- $chapter = Chapter::visible()->findOrFail($id);
+ $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
$chapter = $this->forJsonDisplay($chapter);
$chapter->load([
public function update(Request $request, string $id)
{
$requestData = $this->validate($request, $this->rules()['update']);
- $chapter = Chapter::visible()->findOrFail($id);
+ $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
$this->checkOwnablePermission('chapter-update', $chapter);
if ($request->has('book_id') && $chapter->book_id !== intval($requestData['book_id'])) {
*/
public function delete(string $id)
{
- $chapter = Chapter::visible()->findOrFail($id);
+ $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
$this->checkOwnablePermission('chapter-delete', $chapter);
$this->chapterRepo->destroy($chapter);
namespace BookStack\Entities\Controllers;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Queries\PageQueries;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Exceptions\PermissionsException;
public function __construct(
protected PageRepo $pageRepo,
protected PageQueries $queries,
+ protected EntityQueries $entityQueries,
) {
}
*/
public function list()
{
- $pages = Page::visible();
+ $pages = $this->queries->visibleForList();
return $this->apiListingResponse($pages, [
'id', 'book_id', 'chapter_id', 'name', 'slug', 'priority',
$this->validate($request, $this->rules['create']);
if ($request->has('chapter_id')) {
- $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+ $parent = $this->entityQueries->chapters->findVisibleByIdOrFail(intval($request->get('chapter_id')));
} else {
- $parent = Book::visible()->findOrFail($request->get('book_id'));
+ $parent = $this->entityQueries->books->findVisibleByIdOrFail(intval($request->get('book_id')));
}
$this->checkOwnablePermission('page-create', $parent);
$parent = null;
if ($request->has('chapter_id')) {
- $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+ $parent = $this->entityQueries->chapters->findVisibleByIdOrFail(intval($request->get('chapter_id')));
} elseif ($request->has('book_id')) {
- $parent = Book::visible()->findOrFail($request->get('book_id'));
+ $parent = $this->entityQueries->books->findVisibleByIdOrFail(intval($request->get('book_id')));
}
if ($parent && !$parent->matches($page->getParent())) {
$this->checkOwnablePermission('page-delete', $page);
$this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()]));
$usedAsTemplate =
- Book::query()->where('default_template_id', '=', $page->id)->count() > 0 ||
- Chapter::query()->where('default_template_id', '=', $page->id)->count() > 0;
+ $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 ||
+ $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0;
return view('pages.delete', [
'book' => $page->book,
$this->checkOwnablePermission('page-update', $page);
$this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()]));
$usedAsTemplate =
- Book::query()->where('default_template_id', '=', $page->id)->count() > 0 ||
- Chapter::query()->where('default_template_id', '=', $page->id)->count() > 0;
+ $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 ||
+ $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0;
return view('pages.delete', [
'book' => $page->book,
*
* @throws \Exception
*/
- public function empty()
+ public function empty(TrashCan $trash)
{
- $deleteCount = (new TrashCan())->empty();
+ $deleteCount = $trash->empty();
$this->logActivity(ActivityType::RECYCLE_BIN_EMPTY);
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
/**
* Get the direct child items within this book.
*/
- public function getDirectChildren(): Collection
+ public function getDirectVisibleChildren(): Collection
{
$pages = $this->directPages()->scopes('visible')->get();
$chapters = $this->chapters()->scopes('visible')->get();
return $pages->concat($chapters)->sortBy('priority')->sortByDesc('draft');
}
-
- /**
- * Get a visible book by its slug.
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- */
- public static function getBySlug(string $slug): self
- {
- return static::visible()->where('slug', '=', $slug)->firstOrFail();
- }
}
return $this->start()->scopes('visible')->find($id);
}
+ public function findVisibleByIdOrFail(int $id): Book
+ {
+ return $this->start()->scopes('visible')->findOrFail($id);
+ }
+
public function findVisibleBySlugOrFail(string $slug): Book
{
/** @var ?Book $book */
return $this->start()->scopes('visible')->find($id);
}
+ public function findVisibleByIdOrFail(int $id): Chapter
+ {
+ return $this->start()->scopes('visible')->findOrFail($id);
+ }
+
public function findVisibleBySlugsOrFail(string $bookSlug, string $chapterSlug): Chapter
{
/** @var ?Chapter $chapter */
- $chapter = $this->start()->with('book')
+ $chapter = $this->start()
+ ->scopes('visible')
+ ->with('book')
->whereHas('book', function (Builder $query) use ($bookSlug) {
$query->where('slug', '=', $bookSlug);
})
return $chapter;
}
+ public function usingSlugs(string $bookSlug, string $chapterSlug): Builder
+ {
+ return $this->start()
+ ->where('slug', '=', $chapterSlug)
+ ->whereHas('book', function (Builder $query) use ($bookSlug) {
+ $query->where('slug', '=', $bookSlug);
+ });
+ }
+
public function visibleForList(): Builder
{
return $this->start()
+ ->scopes('visible')
->select(array_merge(static::$listAttributes, ['book_slug' => function ($builder) {
$builder->select('slug')
->from('books')
{
/** @var ?Page $page */
$page = $this->start()->with('book')
+ ->scopes('visible')
->whereHas('book', function (Builder $query) use ($bookSlug) {
$query->where('slug', '=', $bookSlug);
})
return $page;
}
+ public function usingSlugs(string $bookSlug, string $pageSlug): Builder
+ {
+ return $this->start()
+ ->where('slug', '=', $pageSlug)
+ ->whereHas('book', function (Builder $query) use ($bookSlug) {
+ $query->where('slug', '=', $bookSlug);
+ });
+ }
+
public function visibleForList(): Builder
{
return $this->start()
+ ->scopes('visible')
->select(array_merge(Page::$listAttributes, ['book_slug' => function ($builder) {
$builder->select('slug')
->from('books')
}]));
}
+ public function visibleWithContents(): Builder
+ {
+ return $this->start()
+ ->scopes('visible')
+ ->select(array_merge(Page::$contentAttributes, ['book_slug' => function ($builder) {
+ $builder->select('slug')
+ ->from('books')
+ ->whereColumn('books.id', '=', 'pages.book_id');
+ }]));
+ }
+
public function currentUserDraftsForList(): Builder
{
return $this->visibleForList()
public function __construct(
protected BaseRepo $baseRepo,
protected TagRepo $tagRepo,
- protected ImageRepo $imageRepo
+ protected ImageRepo $imageRepo,
+ protected TrashCan $trashCan,
) {
}
*/
public function destroy(Book $book)
{
- $trashCan = new TrashCan();
- $trashCan->softDestroyBook($book);
+ $this->trashCan->softDestroyBook($book);
Activity::add(ActivityType::BOOK_DELETE, $book);
- $trashCan->autoClearOld();
+ $this->trashCan->autoClearOld();
}
}
namespace BookStack\Entities\Repos;
use BookStack\Activity\ActivityType;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Facades\Activity;
use Exception;
{
public function __construct(
protected BaseRepo $baseRepo,
+ protected BookQueries $bookQueries,
+ protected TrashCan $trashCan,
) {
}
return intval($id);
});
- $syncData = Book::visible()
+ $syncData = $this->bookQueries->visibleForList()
->whereIn('id', $bookIds)
->pluck('id')
->mapWithKeys(function ($bookId) use ($numericIDs) {
*/
public function destroy(Bookshelf $shelf)
{
- $trashCan = new TrashCan();
- $trashCan->softDestroyShelf($shelf);
+ $this->trashCan->softDestroyShelf($shelf);
Activity::add(ActivityType::BOOKSHELF_DELETE, $shelf);
- $trashCan->autoClearOld();
+ $this->trashCan->autoClearOld();
}
}
public function __construct(
protected BaseRepo $baseRepo,
protected EntityQueries $entityQueries,
+ protected TrashCan $trashCan,
) {
}
*/
public function destroy(Chapter $chapter)
{
- $trashCan = new TrashCan();
- $trashCan->softDestroyChapter($chapter);
+ $this->trashCan->softDestroyChapter($chapter);
Activity::add(ActivityType::CHAPTER_DELETE, $chapter);
- $trashCan->autoClearOld();
+ $this->trashCan->autoClearOld();
}
/**
protected RevisionRepo $revisionRepo,
protected EntityQueries $entityQueries,
protected ReferenceStore $referenceStore,
- protected ReferenceUpdater $referenceUpdater
+ protected ReferenceUpdater $referenceUpdater,
+ protected TrashCan $trashCan,
) {
}
*/
public function destroy(Page $page)
{
- $trashCan = new TrashCan();
- $trashCan->softDestroyPage($page);
+ $this->trashCan->softDestroyPage($page);
Activity::add(ActivityType::PAGE_DELETE, $page);
- $trashCan->autoClearOld();
+ $this->trashCan->autoClearOld();
}
/**
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use Illuminate\Support\Collection;
class BookContents
{
- protected Book $book;
+ protected EntityQueries $queries;
- public function __construct(Book $book)
- {
- $this->book = $book;
+ public function __construct(
+ protected Book $book,
+ ) {
+ $this->queries = app()->make(EntityQueries::class);
}
/**
*/
public function getLastPriority(): int
{
- $maxPage = Page::visible()->where('book_id', '=', $this->book->id)
+ $maxPage = $this->book->pages()
->where('draft', '=', false)
- ->where('chapter_id', '=', 0)->max('priority');
- $maxChapter = Chapter::visible()->where('book_id', '=', $this->book->id)
+ ->where('chapter_id', '=', 0)
+ ->max('priority');
+
+ $maxChapter = $this->book->chapters()
->max('priority');
return max($maxChapter, $maxPage, 1);
public function getTree(bool $showDrafts = false, bool $renderPages = false): Collection
{
$pages = $this->getPages($showDrafts, $renderPages);
- $chapters = Chapter::visible()->where('book_id', '=', $this->book->id)->get();
+ $chapters = $this->book->chapters()->scopes('visible')->get();
$all = collect()->concat($pages)->concat($chapters);
$chapterMap = $chapters->keyBy('id');
$lonePages = collect();
*/
protected function getPages(bool $showDrafts = false, bool $getPageContent = false): Collection
{
- $query = Page::visible()
- ->select($getPageContent ? Page::$contentAttributes : Page::$listAttributes)
- ->where('book_id', '=', $this->book->id);
+ if ($getPageContent) {
+ $query = $this->queries->pages->visibleWithContents();
+ } else {
+ $query = $this->queries->pages->visibleForList();
+ }
if (!$showDrafts) {
$query->where('draft', '=', false);
}
- return $query->get();
+ return $query->where('book_id', '=', $this->book->id)->get();
}
/**
/** @var Book[] $booksInvolved */
$booksInvolved = array_values(array_filter($modelMap, function (string $key) {
- return strpos($key, 'book:') === 0;
+ return str_starts_with($key, 'book:');
}, ARRAY_FILTER_USE_KEY));
// Update permissions of books involved
}
}
- $pages = Page::visible()->whereIn('id', array_unique($ids['page']))->get(Page::$listAttributes);
+ $pages = $this->queries->pages->visibleForList()->whereIn('id', array_unique($ids['page']))->get();
/** @var Page $page */
foreach ($pages as $page) {
$modelMap['page:' . $page->id] = $page;
}
}
- $chapters = Chapter::visible()->whereIn('id', array_unique($ids['chapter']))->get();
+ $chapters = $this->queries->chapters->visibleForList()->whereIn('id', array_unique($ids['chapter']))->get();
/** @var Chapter $chapter */
foreach ($chapters as $chapter) {
$modelMap['chapter:' . $chapter->id] = $chapter;
$ids['book'][] = $chapter->book_id;
}
- $books = Book::visible()->whereIn('id', array_unique($ids['book']))->get();
+ $books = $this->queries->books->visibleForList()->whereIn('id', array_unique($ids['book']))->get();
/** @var Book $book */
foreach ($books as $book) {
$modelMap['book:' . $book->id] = $book;
$copyBook = $this->bookRepo->create($bookDetails);
// Clone contents
- $directChildren = $original->getDirectChildren();
+ $directChildren = $original->getDirectVisibleChildren();
foreach ($directChildren as $child) {
if ($child instanceof Chapter && userCan('chapter-create', $copyBook)) {
$this->cloneChapter($child, $copyBook, $child->name);
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use Illuminate\Support\Collection;
class SiblingFetcher
{
+ public function __construct(
+ protected EntityQueries $queries,
+ ) {
+ }
+
/**
* Search among the siblings of the entity of given type and id.
*/
// Page in book or chapter
if (($entity instanceof Page && !$entity->chapter) || $entity instanceof Chapter) {
- $entities = $entity->book->getDirectChildren();
+ $entities = $entity->book->getDirectVisibleChildren();
}
// Book
if ($contextShelf) {
$entities = $contextShelf->visibleBooks()->get();
} else {
- $entities = Book::visible()->get();
+ $entities = $this->queries->books->visibleForList()->get();
}
}
// Shelf
if ($entity instanceof Bookshelf) {
- $entities = Bookshelf::visible()->get();
+ $entities = $this->queries->shelves->visibleForList()->get();
}
return $entities;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\HasCoverImage;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Exceptions\NotifyException;
use BookStack\Facades\Activity;
use BookStack\Uploads\AttachmentService;
class TrashCan
{
+ public function __construct(
+ protected EntityQueries $queries,
+ ) {
+ }
+
/**
* Send a shelf to the recycle bin.
*
}
// Remove book template usages
- Book::query()->where('default_template_id', '=', $page->id)
+ $this->queries->books->start()
+ ->where('default_template_id', '=', $page->id)
->update(['default_template_id' => null]);
// Remove chapter template usages
- Chapter::query()->where('default_template_id', '=', $page->id)
+ $this->queries->chapters->start()
+ ->where('default_template_id', '=', $page->id)
->update(['default_template_id' => null]);
$page->forceDelete();
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Permissions\Models\JointPermission;
use BookStack\Users\Models\Role;
use Illuminate\Database\Eloquent\Builder;
*/
class JointPermissionBuilder
{
+ public function __construct(
+ protected EntityQueries $queries,
+ ) {
+ }
+
+
/**
* Re-generate all entity permission from scratch.
*/
});
// Chunk through all bookshelves
- Bookshelf::query()->withTrashed()->select(['id', 'owned_by'])
+ $this->queries->shelves->start()->withTrashed()->select(['id', 'owned_by'])
->chunk(50, function (EloquentCollection $shelves) use ($roles) {
$this->createManyJointPermissions($shelves->all(), $roles);
});
});
// Chunk through all bookshelves
- Bookshelf::query()->select(['id', 'owned_by'])
+ $this->queries->shelves->start()->select(['id', 'owned_by'])
->chunk(100, function ($shelves) use ($roles) {
$this->createManyJointPermissions($shelves->all(), $roles);
});
*/
protected function bookFetchQuery(): Builder
{
- return Book::query()->withTrashed()
+ return $this->queries->books->start()->withTrashed()
->select(['id', 'owned_by'])->with([
'chapters' => function ($query) {
$query->withTrashed()->select(['id', 'owned_by', 'book_id']);
namespace BookStack\Permissions;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\PermissionsUpdater;
use BookStack\Http\Controller;
use BookStack\Permissions\Models\EntityPermission;
class PermissionsController extends Controller
{
- protected PermissionsUpdater $permissionsUpdater;
-
- public function __construct(PermissionsUpdater $permissionsUpdater)
- {
- $this->permissionsUpdater = $permissionsUpdater;
+ public function __construct(
+ protected PermissionsUpdater $permissionsUpdater,
+ protected EntityQueries $queries,
+ ) {
}
/**
- * Show the Permissions view for a page.
+ * Show the permissions view for a page.
*/
public function showForPage(string $bookSlug, string $pageSlug)
{
- $page = Page::getBySlugs($bookSlug, $pageSlug);
+ $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
$this->checkOwnablePermission('restrictions-manage', $page);
$this->setPageTitle(trans('entities.pages_permissions'));
*/
public function updateForPage(Request $request, string $bookSlug, string $pageSlug)
{
- $page = Page::getBySlugs($bookSlug, $pageSlug);
+ $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
$this->checkOwnablePermission('restrictions-manage', $page);
$this->permissionsUpdater->updateFromPermissionsForm($page, $request);
}
/**
- * Show the Restrictions view for a chapter.
+ * Show the permissions view for a chapter.
*/
public function showForChapter(string $bookSlug, string $chapterSlug)
{
- $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug);
+ $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
$this->checkOwnablePermission('restrictions-manage', $chapter);
$this->setPageTitle(trans('entities.chapters_permissions'));
}
/**
- * Set the restrictions for a chapter.
+ * Set the permissions for a chapter.
*/
public function updateForChapter(Request $request, string $bookSlug, string $chapterSlug)
{
- $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug);
+ $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
$this->checkOwnablePermission('restrictions-manage', $chapter);
$this->permissionsUpdater->updateFromPermissionsForm($chapter, $request);
*/
public function showForBook(string $slug)
{
- $book = Book::getBySlug($slug);
+ $book = $this->queries->books->findVisibleBySlugOrFail($slug);
$this->checkOwnablePermission('restrictions-manage', $book);
$this->setPageTitle(trans('entities.books_permissions'));
}
/**
- * Set the restrictions for a book.
+ * Set the permissions for a book.
*/
public function updateForBook(Request $request, string $slug)
{
- $book = Book::getBySlug($slug);
+ $book = $this->queries->books->findVisibleBySlugOrFail($slug);
$this->checkOwnablePermission('restrictions-manage', $book);
$this->permissionsUpdater->updateFromPermissionsForm($book, $request);
*/
public function showForShelf(string $slug)
{
- $shelf = Bookshelf::getBySlug($slug);
+ $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);
$this->setPageTitle(trans('entities.shelves_permissions'));
*/
public function updateForShelf(Request $request, string $slug)
{
- $shelf = Bookshelf::getBySlug($slug);
+ $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);
$this->permissionsUpdater->updateFromPermissionsForm($shelf, $request);
*/
public function copyShelfPermissionsToBooks(string $slug)
{
- $shelf = Bookshelf::getBySlug($slug);
+ $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);
$updateCount = $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf);
namespace BookStack\References;
use BookStack\App\Model;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\References\ModelResolvers\BookLinkModelResolver;
use BookStack\References\ModelResolvers\BookshelfLinkModelResolver;
use BookStack\References\ModelResolvers\ChapterLinkModelResolver;
*/
public static function createWithEntityResolvers(): self
{
+ $queries = app()->make(EntityQueries::class);
+
return new self([
- new PagePermalinkModelResolver(),
- new PageLinkModelResolver(),
- new ChapterLinkModelResolver(),
- new BookLinkModelResolver(),
- new BookshelfLinkModelResolver(),
+ new PagePermalinkModelResolver($queries->pages),
+ new PageLinkModelResolver($queries->pages),
+ new ChapterLinkModelResolver($queries->chapters),
+ new BookLinkModelResolver($queries->books),
+ new BookshelfLinkModelResolver($queries->shelves),
]);
}
}
use BookStack\App\Model;
use BookStack\Entities\Models\Book;
+use BookStack\Entities\Queries\BookQueries;
class BookLinkModelResolver implements CrossLinkModelResolver
{
+ public function __construct(
+ protected BookQueries $queries
+ ) {
+ }
+
public function resolve(string $link): ?Model
{
$pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '([#?\/]|$)/';
$bookSlug = $matches[1];
/** @var ?Book $model */
- $model = Book::query()->where('slug', '=', $bookSlug)->first(['id']);
+ $model = $this->queries->start()->where('slug', '=', $bookSlug)->first(['id']);
return $model;
}
use BookStack\App\Model;
use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Queries\BookshelfQueries;
class BookshelfLinkModelResolver implements CrossLinkModelResolver
{
+ public function __construct(
+ protected BookshelfQueries $queries
+ ) {
+ }
public function resolve(string $link): ?Model
{
$pattern = '/^' . preg_quote(url('/shelves'), '/') . '\/([\w-]+)' . '([#?\/]|$)/';
$shelfSlug = $matches[1];
/** @var ?Bookshelf $model */
- $model = Bookshelf::query()->where('slug', '=', $shelfSlug)->first(['id']);
+ $model = $this->queries->start()->where('slug', '=', $shelfSlug)->first(['id']);
return $model;
}
use BookStack\App\Model;
use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Queries\ChapterQueries;
class ChapterLinkModelResolver implements CrossLinkModelResolver
{
+ public function __construct(
+ protected ChapterQueries $queries
+ ) {
+ }
+
public function resolve(string $link): ?Model
{
$pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '\/chapter\/' . '([\w-]+)' . '([#?\/]|$)/';
$chapterSlug = $matches[2];
/** @var ?Chapter $model */
- $model = Chapter::query()->whereSlugs($bookSlug, $chapterSlug)->first(['id']);
+ $model = $this->queries->usingSlugs($bookSlug, $chapterSlug)->first(['id']);
return $model;
}
use BookStack\App\Model;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\PageQueries;
class PageLinkModelResolver implements CrossLinkModelResolver
{
+ public function __construct(
+ protected PageQueries $queries
+ ) {
+ }
+
public function resolve(string $link): ?Model
{
$pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '\/page\/' . '([\w-]+)' . '([#?\/]|$)/';
$pageSlug = $matches[2];
/** @var ?Page $model */
- $model = Page::query()->whereSlugs($bookSlug, $pageSlug)->first(['id']);
+ $model = $this->queries->usingSlugs($bookSlug, $pageSlug)->first(['id']);
return $model;
}
use BookStack\App\Model;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\PageQueries;
class PagePermalinkModelResolver implements CrossLinkModelResolver
{
+ public function __construct(
+ protected PageQueries $queries
+ ) {
+ }
+
public function resolve(string $link): ?Model
{
$pattern = '/^' . preg_quote(url('/link'), '/') . '\/(\d+)/';
$id = intval($matches[1]);
/** @var ?Page $model */
- $model = Page::query()->find($id, ['id']);
+ $model = $this->queries->start()->find($id, ['id']);
return $model;
}
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Http\Controller;
class ReferenceController extends Controller
{
public function __construct(
- protected ReferenceFetcher $referenceFetcher
+ protected ReferenceFetcher $referenceFetcher,
+ protected EntityQueries $queries,
) {
}
*/
public function page(string $bookSlug, string $pageSlug)
{
- $page = Page::getBySlugs($bookSlug, $pageSlug);
+ $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
$references = $this->referenceFetcher->getReferencesToEntity($page);
return view('pages.references', [
*/
public function chapter(string $bookSlug, string $chapterSlug)
{
- $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug);
+ $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
$references = $this->referenceFetcher->getReferencesToEntity($chapter);
return view('chapters.references', [
*/
public function book(string $slug)
{
- $book = Book::getBySlug($slug);
+ $book = $this->queries->books->findVisibleBySlugOrFail($slug);
$references = $this->referenceFetcher->getReferencesToEntity($book);
return view('books.references', [
*/
public function shelf(string $slug)
{
- $shelf = Bookshelf::getBySlug($slug);
+ $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
$references = $this->referenceFetcher->getReferencesToEntity($shelf);
return view('shelves.references', [
/**
* Search siblings items in the system.
*/
- public function searchSiblings(Request $request)
+ public function searchSiblings(Request $request, SiblingFetcher $siblingFetcher)
{
$type = $request->get('entity_type', null);
$id = $request->get('entity_id', null);
- $entities = (new SiblingFetcher())->fetch($type, $id);
+ $entities = $siblingFetcher->fetch($type, $id);
return view('entities.list-basic', ['entities' => $entities, 'style' => 'compact']);
}
/**
* Show the page for application maintenance.
*/
- public function index()
+ public function index(TrashCan $trashCan)
{
$this->checkPermission('settings-manage');
$this->setPageTitle(trans('settings.maint'));
$version = trim(file_get_contents(base_path('version')));
// Recycle bin details
- $recycleStats = (new TrashCan())->getTrashedCounts();
+ $recycleStats = $trashCan->getTrashedCounts();
return view('settings.maintenance', [
'version' => $version,
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Exceptions\ImageUploadException;
use Exception;
use Illuminate\Support\Facades\DB;
public function __construct(
protected ImageStorage $storage,
protected ImageResizer $resizer,
+ protected EntityQueries $queries,
) {
}
}
if ($imageType === 'gallery' || $imageType === 'drawio') {
- return Page::visible()->where('id', '=', $image->uploaded_to)->exists();
+ return $this->queries->pages->visibleForList()->where('id', '=', $image->uploaded_to)->exists();
}
if ($imageType === 'cover_book') {
- return Book::visible()->where('id', '=', $image->uploaded_to)->exists();
+ return $this->queries->books->visibleForList()->where('id', '=', $image->uploaded_to)->exists();
}
if ($imageType === 'cover_bookshelf') {
- return Bookshelf::visible()->where('id', '=', $image->uploaded_to)->exists();
+ return $this->queries->shelves->visibleForList()->where('id', '=', $image->uploaded_to)->exists();
}
return false;
class UserProfileController extends Controller
{
+ public function __construct(
+ protected UserRepo $userRepo,
+ protected ActivityQueries $activityQueries,
+ protected UserContentCounts $contentCounts,
+ protected UserRecentlyCreatedContent $recentlyCreatedContent
+ ) {
+ }
+
+
/**
* Show the user profile page.
*/
- public function show(UserRepo $repo, ActivityQueries $activities, string $slug)
+ public function show(string $slug)
{
- $user = $repo->getBySlug($slug);
+ $user = $this->userRepo->getBySlug($slug);
- $userActivity = $activities->userActivity($user);
- $recentlyCreated = (new UserRecentlyCreatedContent())->run($user, 5);
- $assetCounts = (new UserContentCounts())->run($user);
+ $userActivity = $this->activityQueries->userActivity($user);
+ $recentlyCreated = $this->recentlyCreatedContent->run($user, 5);
+ $assetCounts = $this->contentCounts->run($user);
$this->setPageTitle($user->name);
namespace BookStack\Users\Queries;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Users\Models\User;
/**
*/
class UserContentCounts
{
+ public function __construct(
+ protected EntityQueries $queries,
+ ) {
+ }
+
+
/**
* @return array{pages: int, chapters: int, books: int, shelves: int}
*/
$createdBy = ['created_by' => $user->id];
return [
- 'pages' => Page::visible()->where($createdBy)->count(),
- 'chapters' => Chapter::visible()->where($createdBy)->count(),
- 'books' => Book::visible()->where($createdBy)->count(),
- 'shelves' => Bookshelf::visible()->where($createdBy)->count(),
+ 'pages' => $this->queries->pages->visibleForList()->where($createdBy)->count(),
+ 'chapters' => $this->queries->chapters->visibleForList()->where($createdBy)->count(),
+ 'books' => $this->queries->books->visibleForList()->where($createdBy)->count(),
+ 'shelves' => $this->queries->shelves->visibleForList()->where($createdBy)->count(),
];
}
}
namespace BookStack\Users\Queries;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
+use BookStack\Entities\Queries\EntityQueries;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
*/
class UserRecentlyCreatedContent
{
+ public function __construct(
+ protected EntityQueries $queries,
+ ) {
+ }
+
/**
* @return array{pages: Collection, chapters: Collection, books: Collection, shelves: Collection}
*/
};
return [
- 'pages' => $query(Page::visible()->where('draft', '=', false)),
- 'chapters' => $query(Chapter::visible()),
- 'books' => $query(Book::visible()),
- 'shelves' => $query(Bookshelf::visible()),
+ 'pages' => $query($this->queries->pages->visibleForList()->where('draft', '=', false)),
+ 'chapters' => $query($this->queries->chapters->visibleForList()),
+ 'books' => $query($this->queries->books->visibleForList()),
+ 'shelves' => $query($this->queries->shelves->visibleForList()),
];
}
}
$copy = Book::query()->where('name', '=', 'My copy book')->first();
$resp->assertRedirect($copy->getUrl());
- $this->assertEquals($book->getDirectChildren()->count(), $copy->getDirectChildren()->count());
+ $this->assertEquals($book->getDirectVisibleChildren()->count(), $copy->getDirectVisibleChildren()->count());
$this->get($copy->getUrl())->assertSee($book->description_html, false);
}
// Hide child content
/** @var BookChild $page */
- foreach ($book->getDirectChildren() as $child) {
+ foreach ($book->getDirectVisibleChildren() as $child) {
$this->permissions->setEntityPermissions($child, [], []);
}
/** @var Book $copy */
$copy = Book::query()->where('name', '=', 'My copy book')->first();
- $this->assertEquals(0, $copy->getDirectChildren()->count());
+ $this->assertEquals(0, $copy->getDirectVisibleChildren()->count());
}
public function test_copy_does_not_copy_pages_or_chapters_if_user_cant_create()
public function test_sibling_search_for_pages_without_chapter()
{
$page = $this->entities->pageNotWithinChapter();
- $bookChildren = $page->book->getDirectChildren();
+ $bookChildren = $page->book->getDirectVisibleChildren();
$this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
$search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
public function test_sibling_search_for_chapters()
{
$chapter = $this->entities->chapter();
- $bookChildren = $chapter->book->getDirectChildren();
+ $bookChildren = $chapter->book->getDirectVisibleChildren();
$this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
$search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter");