3 namespace BookStack\Entities\Repos;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Page;
8 use BookStack\Entities\Models\Chapter;
9 use BookStack\Entities\Models\Entity;
10 use BookStack\Entities\Tools\BookContents;
11 use BookStack\Entities\Tools\TrashCan;
12 use BookStack\Exceptions\MoveOperationException;
13 use BookStack\Exceptions\NotFoundException;
14 use BookStack\Exceptions\PermissionsException;
15 use BookStack\Facades\Activity;
20 public function __construct(
21 protected BaseRepo $baseRepo
26 * Get a chapter via the slug.
28 * @throws NotFoundException
30 public function getBySlug(string $bookSlug, string $chapterSlug): Chapter
32 $chapter = Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->first();
34 if ($chapter === null) {
35 throw new NotFoundException(trans('errors.chapter_not_found'));
42 * Create a new chapter in the system.
44 public function create(array $input, Book $parentBook): Chapter
46 $chapter = new Chapter();
47 $chapter->book_id = $parentBook->id;
48 $chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
49 $this->baseRepo->create($chapter, $input);
50 $this->updateChapterDefaultTemplate($chapter, intval($input['default_template_id'] ?? null));
51 Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
57 * Update the given chapter.
59 public function update(Chapter $chapter, array $input): Chapter
61 $this->baseRepo->update($chapter, $input);
63 if (array_key_exists('default_template_id', $input)) {
64 $this->updateChapterDefaultTemplate($chapter, intval($input['default_template_id']));
67 Activity::add(ActivityType::CHAPTER_UPDATE, $chapter);
73 * Remove a chapter from the system.
77 public function destroy(Chapter $chapter)
79 $trashCan = new TrashCan();
80 $trashCan->softDestroyChapter($chapter);
81 Activity::add(ActivityType::CHAPTER_DELETE, $chapter);
82 $trashCan->autoClearOld();
86 * Move the given chapter into a new parent book.
87 * The $parentIdentifier must be a string of the following format:
88 * 'book:<id>' (book:5).
90 * @throws MoveOperationException
91 * @throws PermissionsException
93 public function move(Chapter $chapter, string $parentIdentifier): Book
95 $parent = $this->findParentByIdentifier($parentIdentifier);
96 if (is_null($parent)) {
97 throw new MoveOperationException('Book to move chapter into not found');
100 if (!userCan('chapter-create', $parent)) {
101 throw new PermissionsException('User does not have permission to create a chapter within the chosen book');
104 $chapter->changeBook($parent->id);
105 $chapter->rebuildPermissions();
106 Activity::add(ActivityType::CHAPTER_MOVE, $chapter);
112 * Update the default page template used for this chapter.
113 * Checks that, if changing, the provided value is a valid template and the user
114 * has visibility of the provided page template id.
116 protected function updateChapterDefaultTemplate(Chapter $chapter, int $templateId): void
118 $changing = $templateId !== intval($chapter->default_template_id);
123 if ($templateId === 0) {
124 $chapter->default_template_id = null;
129 $templateExists = Page::query()->visible()
130 ->where('template', '=', true)
131 ->where('id', '=', $templateId)
134 $chapter->default_template_id = $templateExists ? $templateId : null;
139 * Find a page parent entity via an identifier string in the format:
143 * @throws MoveOperationException
145 public function findParentByIdentifier(string $identifier): ?Book
147 $stringExploded = explode(':', $identifier);
148 $entityType = $stringExploded[0];
149 $entityId = intval($stringExploded[1]);
151 if ($entityType !== 'book') {
152 throw new MoveOperationException('Chapters can only be in books');
155 return Book::visible()->where('id', '=', $entityId)->first();