3 namespace Tests\Helpers;
5 use BookStack\Auth\Permissions\EntityPermission;
6 use BookStack\Auth\Role;
7 use BookStack\Auth\User;
8 use BookStack\Entities\Models\Book;
9 use BookStack\Entities\Models\Bookshelf;
10 use BookStack\Entities\Models\Chapter;
11 use BookStack\Entities\Models\Entity;
12 use BookStack\Entities\Models\Page;
13 use BookStack\Entities\Repos\BookRepo;
14 use BookStack\Entities\Repos\BookshelfRepo;
15 use BookStack\Entities\Repos\ChapterRepo;
16 use BookStack\Entities\Repos\PageRepo;
17 use Illuminate\Database\Eloquent\Builder;
20 * Class to provider and action entity models for common test case
21 * operations. Tracks handled models and only returns fresh models.
22 * Does not dedupe against nested/child/parent models.
27 * @var array<string, int[]>
29 protected array $fetchCache = [
37 * Get an un-fetched page from the system.
39 public function page(callable $queryFilter = null): Page
41 /** @var Page $page */
42 $page = Page::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['page'])->first();
43 $this->addToCache($page);
47 public function pageWithinChapter(): Page
49 return $this->page(fn(Builder $query) => $query->whereHas('chapter')->with('chapter'));
52 public function pageNotWithinChapter(): Page
54 return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
58 * Get an un-fetched chapter from the system.
60 public function chapter(callable $queryFilter = null): Chapter
62 /** @var Chapter $chapter */
63 $chapter = Chapter::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['chapter'])->first();
64 $this->addToCache($chapter);
68 public function chapterHasPages(): Chapter
70 return $this->chapter(fn(Builder $query) => $query->whereHas('pages'));
74 * Get an un-fetched book from the system.
76 public function book(callable $queryFilter = null): Book
78 /** @var Book $book */
79 $book = Book::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['book'])->first();
80 $this->addToCache($book);
85 * Get a book that has chapters and pages assigned.
87 public function bookHasChaptersAndPages(): Book
89 return $this->book(function (Builder $query) {
90 $query->has('chapters')->has('pages')->with(['chapters', 'pages']);
95 * Get an un-fetched shelf from the system.
97 public function shelf(callable $queryFilter = null): Bookshelf
99 /** @var Bookshelf $shelf */
100 $shelf = Bookshelf::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['bookshelf'])->first();
101 $this->addToCache($shelf);
106 * Get all entity types from the system.
107 * @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf}
109 public function all(): array
112 'page' => $this->page(),
113 'chapter' => $this->chapter(),
114 'book' => $this->book(),
115 'bookshelf' => $this->shelf(),
119 public function updatePage(Page $page, array $data): Page
121 $this->addToCache($page);
122 return app()->make(PageRepo::class)->update($page, $data);
126 * Create a book to page chain of entities that belong to a specific user.
127 * @return array{book: Book, chapter: Chapter, page: Page}
129 public function createChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
131 if (empty($updaterUser)) {
132 $updaterUser = $creatorUser;
135 $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
136 /** @var Book $book */
137 $book = Book::factory()->create($userAttrs);
138 $chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
139 $page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
141 $book->rebuildPermissions();
142 $this->addToCache([$page, $chapter, $book]);
144 return compact('book', 'chapter', 'page');
148 * Create and return a new bookshelf.
150 public function newShelf(array $input = ['name' => 'test shelf', 'description' => 'My new test shelf']): Bookshelf
152 $shelf = app(BookshelfRepo::class)->create($input, []);
153 $this->addToCache($shelf);
158 * Create and return a new book.
160 public function newBook(array $input = ['name' => 'test book', 'description' => 'My new test book']): Book
162 $book = app(BookRepo::class)->create($input);
163 $this->addToCache($book);
168 * Create and return a new test chapter.
170 public function newChapter(array $input, Book $book): Chapter
172 $chapter = app(ChapterRepo::class)->create($input, $book);
173 $this->addToCache($chapter);
178 * Create and return a new test page.
180 public function newPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
182 $book = $this->book();
183 $pageRepo = app(PageRepo::class);
184 $draftPage = $pageRepo->getNewDraftPage($book);
185 $this->addToCache($draftPage);
186 return $pageRepo->publishDraft($draftPage, $input);
190 * Regenerate the permission for an entity.
191 * Centralised to manage clearing of cached elements between requests.
193 public function regenPermissions(Entity $entity): void
195 $entity->rebuildPermissions();
196 $entity->load('jointPermissions');
200 * Set the given entity as having restricted permissions, and apply the given
201 * permissions for the given roles.
202 * @param string[] $actions
203 * @param Role[] $roles
205 public function setPermissions(Entity $entity, array $actions = [], array $roles = []): void
207 $entity->permissions()->delete();
210 // Set default permissions to not allow actions so that only the provided role permissions are at play.
211 ['role_id' => null, 'view' => false, 'create' => false, 'update' => false, 'delete' => false],
214 foreach ($roles as $role) {
215 $permission = ['role_id' => $role->id];
216 foreach (EntityPermission::PERMISSIONS as $possibleAction) {
217 $permission[$possibleAction] = in_array($possibleAction, $actions);
219 $permissions[] = $permission;
222 $entity->permissions()->createMany($permissions);
223 $entity->load('permissions');
224 $this->regenPermissions($entity);
228 * @param Entity|Entity[] $entities
230 protected function addToCache($entities): void
232 if (!is_array($entities)) {
233 $entities = [$entities];
236 foreach ($entities as $entity) {
237 $this->fetchCache[$entity->getType()][] = $entity->id;