1 <?php namespace BookStack\Repos;
6 use BookStack\Exceptions\NotFoundException;
7 use Illuminate\Support\Str;
10 class ChapterRepo extends EntityRepo
15 * ChapterRepo constructor.
18 public function __construct(PageRepo $pageRepo)
20 $this->pageRepo = $pageRepo;
21 parent::__construct();
25 * Base query for getting chapters, Takes permissions into account.
28 private function chapterQuery()
30 return $this->permissionService->enforceChapterRestrictions($this->chapter, 'view');
34 * Check if an id exists.
38 public function idExists($id)
40 return $this->chapterQuery()->where('id', '=', $id)->count() > 0;
44 * Get a chapter by a specific id.
48 public function getById($id)
50 return $this->chapterQuery()->findOrFail($id);
55 * @return \Illuminate\Database\Eloquent\Collection|static[]
57 public function getAll()
59 return $this->chapterQuery()->all();
63 * Get a chapter that has the given slug within the given book.
67 * @throws NotFoundException
69 public function getBySlug($slug, $bookId)
71 $chapter = $this->chapterQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
72 if ($chapter === null) throw new NotFoundException('Chapter not found');
77 * Get the child items for a chapter
78 * @param Chapter $chapter
80 public function getChildren(Chapter $chapter)
82 $pages = $this->permissionService->enforcePageRestrictions($chapter->pages())->get();
83 // Sort items with drafts first then by priority.
84 return $pages->sortBy(function ($child, $key) {
85 $score = $child->priority;
86 if ($child->draft) $score -= 100;
92 * Create a new chapter from request input.
97 public function createFromInput($input, Book $book)
99 $chapter = $this->chapter->newInstance($input);
100 $chapter->slug = $this->findSuitableSlug($chapter->name, $book->id);
101 $chapter->created_by = auth()->user()->id;
102 $chapter->updated_by = auth()->user()->id;
103 $chapter = $book->chapters()->save($chapter);
104 $this->permissionService->buildJointPermissionsForEntity($chapter);
109 * Destroy a chapter and its relations by providing its slug.
110 * @param Chapter $chapter
112 public function destroy(Chapter $chapter)
114 if (count($chapter->pages) > 0) {
115 foreach ($chapter->pages as $page) {
116 $page->chapter_id = 0;
120 Activity::removeEntity($chapter);
121 $chapter->views()->delete();
122 $chapter->permissions()->delete();
123 $this->permissionService->deleteJointPermissionsForEntity($chapter);
128 * Check if a chapter's slug exists.
131 * @param bool|false $currentId
134 public function doesSlugExist($slug, $bookId, $currentId = false)
136 $query = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId);
138 $query = $query->where('id', '!=', $currentId);
140 return $query->count() > 0;
144 * Finds a suitable slug for the provided name.
145 * Checks database to prevent duplicate slugs.
148 * @param bool|false $currentId
151 public function findSuitableSlug($name, $bookId, $currentId = false)
153 $slug = Str::slug($name);
154 if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
155 while ($this->doesSlugExist($slug, $bookId, $currentId)) {
156 $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
162 * Get a new priority value for a new page to be added
163 * to the given chapter.
164 * @param Chapter $chapter
167 public function getNewPriority(Chapter $chapter)
169 $lastPage = $chapter->pages->last();
170 return $lastPage !== null ? $lastPage->priority + 1 : 0;
174 * Get chapters by the given search term.
175 * @param string $term
176 * @param array $whereTerms
178 * @param array $paginationAppends
181 public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
183 $terms = $this->prepareSearchTerms($term);
184 $chapterQuery = $this->permissionService->enforceChapterRestrictions($this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms));
185 $chapterQuery = $this->addAdvancedSearchQueries($chapterQuery, $term);
186 $chapters = $chapterQuery->paginate($count)->appends($paginationAppends);
187 $words = join('|', explode(' ', preg_quote(trim($term), '/')));
188 foreach ($chapters as $chapter) {
190 $result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $chapter->getExcerpt(100));
191 $chapter->searchSnippet = $result;
197 * Changes the book relation of this chapter.
199 * @param Chapter $chapter
200 * @param bool $rebuildPermissions
203 public function changeBook($bookId, Chapter $chapter, $rebuildPermissions = false)
205 $chapter->book_id = $bookId;
206 // Update related activity
207 foreach ($chapter->activity as $activity) {
208 $activity->book_id = $bookId;
211 $chapter->slug = $this->findSuitableSlug($chapter->name, $bookId, $chapter->id);
213 // Update all child pages
214 foreach ($chapter->pages as $page) {
215 $this->pageRepo->changeBook($bookId, $page);
218 // Update permissions if applicable
219 if ($rebuildPermissions) {
220 $chapter->load('book');
221 $this->permissionService->buildJointPermissionsForEntity($chapter->book);