]> BookStack Code Mirror - bookstack/blob - app/Repos/BookRepo.php
Added auto-suggestions to tag names and values
[bookstack] / app / Repos / BookRepo.php
1 <?php namespace BookStack\Repos;
2
3 use Alpha\B;
4 use BookStack\Exceptions\NotFoundException;
5 use Illuminate\Support\Str;
6 use BookStack\Book;
7 use Views;
8
9 class BookRepo extends EntityRepo
10 {
11     protected $pageRepo;
12     protected $chapterRepo;
13
14     /**
15      * BookRepo constructor.
16      * @param PageRepo $pageRepo
17      * @param ChapterRepo $chapterRepo
18      */
19     public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo)
20     {
21         $this->pageRepo = $pageRepo;
22         $this->chapterRepo = $chapterRepo;
23         parent::__construct();
24     }
25
26     /**
27      * Base query for getting books.
28      * Takes into account any restrictions.
29      * @return mixed
30      */
31     private function bookQuery()
32     {
33         return $this->permissionService->enforceBookRestrictions($this->book, 'view');
34     }
35
36     /**
37      * Get the book that has the given id.
38      * @param $id
39      * @return mixed
40      */
41     public function getById($id)
42     {
43         return $this->bookQuery()->findOrFail($id);
44     }
45
46     /**
47      * Get all books, Limited by count.
48      * @param int $count
49      * @return mixed
50      */
51     public function getAll($count = 10)
52     {
53         $bookQuery = $this->bookQuery()->orderBy('name', 'asc');
54         if (!$count) return $bookQuery->get();
55         return $bookQuery->take($count)->get();
56     }
57
58     /**
59      * Get all books paginated.
60      * @param int $count
61      * @return mixed
62      */
63     public function getAllPaginated($count = 10)
64     {
65         return $this->bookQuery()
66             ->orderBy('name', 'asc')->paginate($count);
67     }
68
69
70     /**
71      * Get the latest books.
72      * @param int $count
73      * @return mixed
74      */
75     public function getLatest($count = 10)
76     {
77         return $this->bookQuery()->orderBy('created_at', 'desc')->take($count)->get();
78     }
79
80     /**
81      * Gets the most recently viewed for a user.
82      * @param int $count
83      * @param int $page
84      * @return mixed
85      */
86     public function getRecentlyViewed($count = 10, $page = 0)
87     {
88         return Views::getUserRecentlyViewed($count, $page, $this->book);
89     }
90
91     /**
92      * Gets the most viewed books.
93      * @param int $count
94      * @param int $page
95      * @return mixed
96      */
97     public function getPopular($count = 10, $page = 0)
98     {
99         return Views::getPopular($count, $page, $this->book);
100     }
101
102     /**
103      * Get a book by slug
104      * @param $slug
105      * @return mixed
106      * @throws NotFoundException
107      */
108     public function getBySlug($slug)
109     {
110         $book = $this->bookQuery()->where('slug', '=', $slug)->first();
111         if ($book === null) throw new NotFoundException('Book not found');
112         return $book;
113     }
114
115     /**
116      * Checks if a book exists.
117      * @param $id
118      * @return bool
119      */
120     public function exists($id)
121     {
122         return $this->bookQuery()->where('id', '=', $id)->exists();
123     }
124
125     /**
126      * Get a new book instance from request input.
127      * @param array $input
128      * @return Book
129      */
130     public function createFromInput($input)
131     {
132         $book = $this->book->newInstance($input);
133         $book->slug = $this->findSuitableSlug($book->name);
134         $book->created_by = auth()->user()->id;
135         $book->updated_by = auth()->user()->id;
136         $book->save();
137         $this->permissionService->buildJointPermissionsForEntity($book);
138         return $book;
139     }
140
141     /**
142      * Update the given book from user input.
143      * @param Book $book
144      * @param $input
145      * @return Book
146      */
147     public function updateFromInput(Book $book, $input)
148     {
149         $book->fill($input);
150         $book->slug = $this->findSuitableSlug($book->name, $book->id);
151         $book->updated_by = auth()->user()->id;
152         $book->save();
153         $this->permissionService->buildJointPermissionsForEntity($book);
154         return $book;
155     }
156
157     /**
158      * Destroy the given book.
159      * @param Book $book
160      * @throws \Exception
161      */
162     public function destroy(Book $book)
163     {
164         foreach ($book->pages as $page) {
165             $this->pageRepo->destroy($page);
166         }
167         foreach ($book->chapters as $chapter) {
168             $this->chapterRepo->destroy($chapter);
169         }
170         $book->views()->delete();
171         $book->permissions()->delete();
172         $this->permissionService->deleteJointPermissionsForEntity($book);
173         $book->delete();
174     }
175
176     /**
177      * Alias method to update the book jointPermissions in the PermissionService.
178      * @param Book $book
179      */
180     public function updateBookPermissions(Book $book)
181     {
182         $this->permissionService->buildJointPermissionsForEntity($book);
183     }
184
185     /**
186      * Get the next child element priority.
187      * @param Book $book
188      * @return int
189      */
190     public function getNewPriority($book)
191     {
192         $lastElem = $this->getChildren($book)->pop();
193         return $lastElem ? $lastElem->priority + 1 : 0;
194     }
195
196     /**
197      * @param string $slug
198      * @param bool|false $currentId
199      * @return bool
200      */
201     public function doesSlugExist($slug, $currentId = false)
202     {
203         $query = $this->book->where('slug', '=', $slug);
204         if ($currentId) {
205             $query = $query->where('id', '!=', $currentId);
206         }
207         return $query->count() > 0;
208     }
209
210     /**
211      * Provides a suitable slug for the given book name.
212      * Ensures the returned slug is unique in the system.
213      * @param string $name
214      * @param bool|false $currentId
215      * @return string
216      */
217     public function findSuitableSlug($name, $currentId = false)
218     {
219         $originalSlug = Str::slug($name);
220         $slug = $originalSlug;
221         $count = 2;
222         while ($this->doesSlugExist($slug, $currentId)) {
223             $slug = $originalSlug . '-' . $count;
224             $count++;
225         }
226         return $slug;
227     }
228
229     /**
230      * Get all child objects of a book.
231      * Returns a sorted collection of Pages and Chapters.
232      * Loads the bookslug onto child elements to prevent access database access for getting the slug.
233      * @param Book $book
234      * @param bool $filterDrafts
235      * @return mixed
236      */
237     public function getChildren(Book $book, $filterDrafts = false)
238     {
239         $pageQuery = $book->pages()->where('chapter_id', '=', 0);
240         $pageQuery = $this->permissionService->enforcePageRestrictions($pageQuery, 'view');
241
242         if ($filterDrafts) {
243             $pageQuery = $pageQuery->where('draft', '=', false);
244         }
245
246         $pages = $pageQuery->get();
247
248         $chapterQuery = $book->chapters()->with(['pages' => function($query) use ($filterDrafts) {
249             $this->permissionService->enforcePageRestrictions($query, 'view');
250             if ($filterDrafts) $query->where('draft', '=', false);
251         }]);
252         $chapterQuery = $this->permissionService->enforceChapterRestrictions($chapterQuery, 'view');
253         $chapters = $chapterQuery->get();
254         $children = $pages->merge($chapters);
255         $bookSlug = $book->slug;
256
257         $children->each(function ($child) use ($bookSlug) {
258             $child->setAttribute('bookSlug', $bookSlug);
259             if ($child->isA('chapter')) {
260                 $child->pages->each(function ($page) use ($bookSlug) {
261                     $page->setAttribute('bookSlug', $bookSlug);
262                 });
263                 $child->pages = $child->pages->sortBy(function($child, $key) {
264                     $score = $child->priority;
265                     if ($child->draft) $score -= 100;
266                     return $score;
267                 });
268             }
269         });
270
271         // Sort items with drafts first then by priority.
272         return $children->sortBy(function($child, $key) {
273             $score = $child->priority;
274             if ($child->isA('page') && $child->draft) $score -= 100;
275             return $score;
276         });
277     }
278
279     /**
280      * Get books by search term.
281      * @param $term
282      * @param int $count
283      * @param array $paginationAppends
284      * @return mixed
285      */
286     public function getBySearch($term, $count = 20, $paginationAppends = [])
287     {
288         $terms = $this->prepareSearchTerms($term);
289         $bookQuery = $this->permissionService->enforceBookRestrictions($this->book->fullTextSearchQuery(['name', 'description'], $terms));
290         $bookQuery = $this->addAdvancedSearchQueries($bookQuery, $term);
291         $books = $bookQuery->paginate($count)->appends($paginationAppends);
292         $words = join('|', explode(' ', preg_quote(trim($term), '/')));
293         foreach ($books as $book) {
294             //highlight
295             $result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $book->getExcerpt(100));
296             $book->searchSnippet = $result;
297         }
298         return $books;
299     }
300
301 }