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