2022-09-29 11:49:25 -04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Tests\Helpers;
|
|
|
|
|
|
|
|
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 BookStack\Entities\Repos\BookRepo;
|
|
|
|
use BookStack\Entities\Repos\BookshelfRepo;
|
|
|
|
use BookStack\Entities\Repos\ChapterRepo;
|
|
|
|
use BookStack\Entities\Repos\PageRepo;
|
2023-09-03 09:19:43 -04:00
|
|
|
use BookStack\Entities\Tools\TrashCan;
|
2023-05-17 12:56:55 -04:00
|
|
|
use BookStack\Users\Models\User;
|
2022-09-29 17:11:16 -04:00
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
2022-09-29 11:49:25 -04:00
|
|
|
|
2022-09-29 17:11:16 -04:00
|
|
|
/**
|
|
|
|
* Class to provider and action entity models for common test case
|
|
|
|
* operations. Tracks handled models and only returns fresh models.
|
|
|
|
* Does not dedupe against nested/child/parent models.
|
|
|
|
*/
|
2022-09-29 11:49:25 -04:00
|
|
|
class EntityProvider
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var array<string, int[]>
|
|
|
|
*/
|
|
|
|
protected array $fetchCache = [
|
|
|
|
'book' => [],
|
|
|
|
'page' => [],
|
|
|
|
'bookshelf' => [],
|
|
|
|
'chapter' => [],
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an un-fetched page from the system.
|
|
|
|
*/
|
2022-09-29 17:11:16 -04:00
|
|
|
public function page(callable $queryFilter = null): Page
|
2022-09-29 11:49:25 -04:00
|
|
|
{
|
|
|
|
/** @var Page $page */
|
2022-09-29 17:11:16 -04:00
|
|
|
$page = Page::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['page'])->first();
|
2022-09-29 11:49:25 -04:00
|
|
|
$this->addToCache($page);
|
|
|
|
return $page;
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:11:16 -04:00
|
|
|
public function pageWithinChapter(): Page
|
|
|
|
{
|
|
|
|
return $this->page(fn(Builder $query) => $query->whereHas('chapter')->with('chapter'));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function pageNotWithinChapter(): Page
|
|
|
|
{
|
|
|
|
return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
|
|
|
|
}
|
|
|
|
|
2023-12-12 07:14:00 -05:00
|
|
|
public function templatePage(): Page
|
|
|
|
{
|
|
|
|
$page = $this->page();
|
|
|
|
$page->template = true;
|
|
|
|
$page->save();
|
|
|
|
|
|
|
|
return $page;
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:49:25 -04:00
|
|
|
/**
|
|
|
|
* Get an un-fetched chapter from the system.
|
|
|
|
*/
|
2022-09-29 17:11:16 -04:00
|
|
|
public function chapter(callable $queryFilter = null): Chapter
|
2022-09-29 11:49:25 -04:00
|
|
|
{
|
|
|
|
/** @var Chapter $chapter */
|
2022-09-29 17:11:16 -04:00
|
|
|
$chapter = Chapter::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['chapter'])->first();
|
2022-09-29 11:49:25 -04:00
|
|
|
$this->addToCache($chapter);
|
|
|
|
return $chapter;
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:11:16 -04:00
|
|
|
public function chapterHasPages(): Chapter
|
|
|
|
{
|
|
|
|
return $this->chapter(fn(Builder $query) => $query->whereHas('pages'));
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:49:25 -04:00
|
|
|
/**
|
|
|
|
* Get an un-fetched book from the system.
|
|
|
|
*/
|
2022-09-29 17:11:16 -04:00
|
|
|
public function book(callable $queryFilter = null): Book
|
2022-09-29 11:49:25 -04:00
|
|
|
{
|
|
|
|
/** @var Book $book */
|
2022-09-29 17:11:16 -04:00
|
|
|
$book = Book::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['book'])->first();
|
2022-09-29 11:49:25 -04:00
|
|
|
$this->addToCache($book);
|
|
|
|
return $book;
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:11:16 -04:00
|
|
|
/**
|
|
|
|
* Get a book that has chapters and pages assigned.
|
|
|
|
*/
|
|
|
|
public function bookHasChaptersAndPages(): Book
|
|
|
|
{
|
|
|
|
return $this->book(function (Builder $query) {
|
|
|
|
$query->has('chapters')->has('pages')->with(['chapters', 'pages']);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:49:25 -04:00
|
|
|
/**
|
|
|
|
* Get an un-fetched shelf from the system.
|
|
|
|
*/
|
2022-09-29 17:11:16 -04:00
|
|
|
public function shelf(callable $queryFilter = null): Bookshelf
|
2022-09-29 11:49:25 -04:00
|
|
|
{
|
|
|
|
/** @var Bookshelf $shelf */
|
2022-09-29 17:11:16 -04:00
|
|
|
$shelf = Bookshelf::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['bookshelf'])->first();
|
2022-09-29 11:49:25 -04:00
|
|
|
$this->addToCache($shelf);
|
|
|
|
return $shelf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all entity types from the system.
|
|
|
|
* @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf}
|
|
|
|
*/
|
|
|
|
public function all(): array
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'page' => $this->page(),
|
|
|
|
'chapter' => $this->chapter(),
|
|
|
|
'book' => $this->book(),
|
|
|
|
'bookshelf' => $this->shelf(),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2022-09-29 17:11:16 -04:00
|
|
|
public function updatePage(Page $page, array $data): Page
|
|
|
|
{
|
|
|
|
$this->addToCache($page);
|
|
|
|
return app()->make(PageRepo::class)->update($page, $data);
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:49:25 -04:00
|
|
|
/**
|
|
|
|
* Create a book to page chain of entities that belong to a specific user.
|
|
|
|
* @return array{book: Book, chapter: Chapter, page: Page}
|
|
|
|
*/
|
|
|
|
public function createChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
|
|
|
|
{
|
|
|
|
if (empty($updaterUser)) {
|
|
|
|
$updaterUser = $creatorUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
$userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
|
|
|
|
/** @var Book $book */
|
|
|
|
$book = Book::factory()->create($userAttrs);
|
|
|
|
$chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
|
|
|
|
$page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
|
|
|
|
|
|
|
|
$book->rebuildPermissions();
|
|
|
|
$this->addToCache([$page, $chapter, $book]);
|
|
|
|
|
|
|
|
return compact('book', 'chapter', 'page');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and return a new bookshelf.
|
|
|
|
*/
|
|
|
|
public function newShelf(array $input = ['name' => 'test shelf', 'description' => 'My new test shelf']): Bookshelf
|
|
|
|
{
|
|
|
|
$shelf = app(BookshelfRepo::class)->create($input, []);
|
|
|
|
$this->addToCache($shelf);
|
|
|
|
return $shelf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and return a new book.
|
|
|
|
*/
|
|
|
|
public function newBook(array $input = ['name' => 'test book', 'description' => 'My new test book']): Book
|
|
|
|
{
|
|
|
|
$book = app(BookRepo::class)->create($input);
|
|
|
|
$this->addToCache($book);
|
|
|
|
return $book;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and return a new test chapter.
|
|
|
|
*/
|
|
|
|
public function newChapter(array $input, Book $book): Chapter
|
|
|
|
{
|
|
|
|
$chapter = app(ChapterRepo::class)->create($input, $book);
|
|
|
|
$this->addToCache($chapter);
|
|
|
|
return $chapter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and return a new test page.
|
|
|
|
*/
|
|
|
|
public function newPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
|
|
|
|
{
|
2022-09-29 12:31:38 -04:00
|
|
|
$book = $this->book();
|
2022-09-29 11:49:25 -04:00
|
|
|
$pageRepo = app(PageRepo::class);
|
|
|
|
$draftPage = $pageRepo->getNewDraftPage($book);
|
|
|
|
$this->addToCache($draftPage);
|
|
|
|
return $pageRepo->publishDraft($draftPage, $input);
|
|
|
|
}
|
|
|
|
|
2023-02-16 12:57:34 -05:00
|
|
|
/**
|
|
|
|
* Create and return a new test draft page.
|
|
|
|
*/
|
|
|
|
public function newDraftPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
|
|
|
|
{
|
|
|
|
$book = $this->book();
|
|
|
|
$pageRepo = app(PageRepo::class);
|
|
|
|
$draftPage = $pageRepo->getNewDraftPage($book);
|
|
|
|
$pageRepo->updatePageDraft($draftPage, $input);
|
|
|
|
$this->addToCache($draftPage);
|
|
|
|
return $draftPage;
|
|
|
|
}
|
|
|
|
|
2024-04-15 13:44:59 -04:00
|
|
|
/**
|
|
|
|
* Send an entity to the recycle bin.
|
|
|
|
*/
|
|
|
|
public function sendToRecycleBin(Entity $entity)
|
|
|
|
{
|
|
|
|
$trash = app()->make(TrashCan::class);
|
|
|
|
|
|
|
|
if ($entity instanceof Page) {
|
|
|
|
$trash->softDestroyPage($entity);
|
|
|
|
} elseif ($entity instanceof Chapter) {
|
|
|
|
$trash->softDestroyChapter($entity);
|
|
|
|
} elseif ($entity instanceof Book) {
|
|
|
|
$trash->softDestroyBook($entity);
|
|
|
|
} elseif ($entity instanceof Bookshelf) {
|
|
|
|
$trash->softDestroyBookshelf($entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
$entity->refresh();
|
|
|
|
if (is_null($entity->deleted_at)) {
|
|
|
|
throw new \Exception("Could not send entity type [{$entity->getMorphClass()}] to the recycle bin");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-03 09:19:43 -04:00
|
|
|
/**
|
|
|
|
* Fully destroy the given entity from the system, bypassing the recycle bin
|
|
|
|
* stage. Still runs through main app deletion logic.
|
|
|
|
*/
|
|
|
|
public function destroy(Entity $entity)
|
|
|
|
{
|
|
|
|
$trash = app()->make(TrashCan::class);
|
|
|
|
$trash->destroyEntity($entity);
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:49:25 -04:00
|
|
|
/**
|
|
|
|
* @param Entity|Entity[] $entities
|
|
|
|
*/
|
|
|
|
protected function addToCache($entities): void
|
|
|
|
{
|
|
|
|
if (!is_array($entities)) {
|
|
|
|
$entities = [$entities];
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($entities as $entity) {
|
|
|
|
$this->fetchCache[$entity->getType()][] = $entity->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|