]> BookStack Code Mirror - bookstack/blob - app/Repos/EntityRepo.php
Fixed image tests after amends to url system
[bookstack] / app / Repos / EntityRepo.php
1 <?php namespace BookStack\Repos;
2
3 use BookStack\Book;
4 use BookStack\Chapter;
5 use BookStack\Entity;
6 use BookStack\Page;
7 use BookStack\Services\PermissionService;
8 use BookStack\User;
9 use Illuminate\Support\Collection;
10 use Illuminate\Support\Facades\Log;
11
12 class EntityRepo
13 {
14
15     /**
16      * @var Book $book
17      */
18     public $book;
19
20     /**
21      * @var Chapter
22      */
23     public $chapter;
24
25     /**
26      * @var Page
27      */
28     public $page;
29
30     /**
31      * @var PermissionService
32      */
33     protected $permissionService;
34
35     /**
36      * Acceptable operators to be used in a query
37      * @var array
38      */
39     protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
40
41     /**
42      * EntityService constructor.
43      */
44     public function __construct()
45     {
46         $this->book = app(Book::class);
47         $this->chapter = app(Chapter::class);
48         $this->page = app(Page::class);
49         $this->permissionService = app(PermissionService::class);
50     }
51
52     /**
53      * Get the latest books added to the system.
54      * @param int $count
55      * @param int $page
56      * @param bool $additionalQuery
57      * @return
58      */
59     public function getRecentlyCreatedBooks($count = 20, $page = 0, $additionalQuery = false)
60     {
61         $query = $this->permissionService->enforceBookRestrictions($this->book)
62             ->orderBy('created_at', 'desc');
63         if ($additionalQuery !== false && is_callable($additionalQuery)) {
64             $additionalQuery($query);
65         }
66         return $query->skip($page * $count)->take($count)->get();
67     }
68
69     /**
70      * Get the most recently updated books.
71      * @param $count
72      * @param int $page
73      * @return mixed
74      */
75     public function getRecentlyUpdatedBooks($count = 20, $page = 0)
76     {
77         return $this->permissionService->enforceBookRestrictions($this->book)
78             ->orderBy('updated_at', 'desc')->skip($page * $count)->take($count)->get();
79     }
80
81     /**
82      * Get the latest pages added to the system.
83      * @param int $count
84      * @param int $page
85      * @param bool $additionalQuery
86      * @return
87      */
88     public function getRecentlyCreatedPages($count = 20, $page = 0, $additionalQuery = false)
89     {
90         $query = $this->permissionService->enforcePageRestrictions($this->page)
91             ->orderBy('created_at', 'desc')->where('draft', '=', false);
92         if ($additionalQuery !== false && is_callable($additionalQuery)) {
93             $additionalQuery($query);
94         }
95         return $query->with('book')->skip($page * $count)->take($count)->get();
96     }
97
98     /**
99      * Get the latest chapters added to the system.
100      * @param int $count
101      * @param int $page
102      * @param bool $additionalQuery
103      * @return
104      */
105     public function getRecentlyCreatedChapters($count = 20, $page = 0, $additionalQuery = false)
106     {
107         $query = $this->permissionService->enforceChapterRestrictions($this->chapter)
108             ->orderBy('created_at', 'desc');
109         if ($additionalQuery !== false && is_callable($additionalQuery)) {
110             $additionalQuery($query);
111         }
112         return $query->skip($page * $count)->take($count)->get();
113     }
114
115     /**
116      * Get the most recently updated pages.
117      * @param $count
118      * @param int $page
119      * @return mixed
120      */
121     public function getRecentlyUpdatedPages($count = 20, $page = 0)
122     {
123         return $this->permissionService->enforcePageRestrictions($this->page)
124             ->where('draft', '=', false)
125             ->orderBy('updated_at', 'desc')->with('book')->skip($page * $count)->take($count)->get();
126     }
127
128     /**
129      * Get draft pages owned by the current user.
130      * @param int $count
131      * @param int $page
132      */
133     public function getUserDraftPages($count = 20, $page = 0)
134     {
135         $user = auth()->user();
136         return $this->page->where('draft', '=', true)
137             ->where('created_by', '=', $user->id)
138             ->orderBy('updated_at', 'desc')
139             ->skip($count * $page)->take($count)->get();
140     }
141
142     /**
143      * Updates entity restrictions from a request
144      * @param $request
145      * @param Entity $entity
146      */
147     public function updateEntityPermissionsFromRequest($request, Entity $entity)
148     {
149         $entity->restricted = $request->has('restricted') && $request->get('restricted') === 'true';
150         $entity->permissions()->delete();
151         if ($request->has('restrictions')) {
152             foreach ($request->get('restrictions') as $roleId => $restrictions) {
153                 foreach ($restrictions as $action => $value) {
154                     $entity->permissions()->create([
155                         'role_id' => $roleId,
156                         'action'  => strtolower($action)
157                     ]);
158                 }
159             }
160         }
161         $entity->save();
162         $this->permissionService->buildJointPermissionsForEntity($entity);
163     }
164
165     /**
166      * Prepare a string of search terms by turning
167      * it into an array of terms.
168      * Keeps quoted terms together.
169      * @param $termString
170      * @return array
171      */
172     public function prepareSearchTerms($termString)
173     {
174         $termString = $this->cleanSearchTermString($termString);
175         preg_match_all('/(".*?")/', $termString, $matches);
176         $terms = [];
177         if (count($matches[1]) > 0) {
178             foreach ($matches[1] as $match) {
179                 $terms[] = $match;
180             }
181             $termString = trim(preg_replace('/"(.*?)"/', '', $termString));
182         }
183         if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString));
184         return $terms;
185     }
186
187     /**
188      * Removes any special search notation that should not
189      * be used in a full-text search.
190      * @param $termString
191      * @return mixed
192      */
193     protected function cleanSearchTermString($termString)
194     {
195         // Strip tag searches
196         $termString = preg_replace('/\[.*?\]/', '', $termString);
197         // Reduced multiple spacing into single spacing
198         $termString = preg_replace("/\s{2,}/", " ", $termString);
199         return $termString;
200     }
201
202     /**
203      * Get the available query operators as a regex escaped list.
204      * @return mixed
205      */
206     protected function getRegexEscapedOperators()
207     {
208         $escapedOperators = [];
209         foreach ($this->queryOperators as $operator) {
210             $escapedOperators[] = preg_quote($operator);
211         }
212         return join('|', $escapedOperators);
213     }
214
215     /**
216      * Parses advanced search notations and adds them to the db query.
217      * @param $query
218      * @param $termString
219      * @return mixed
220      */
221     protected function addAdvancedSearchQueries($query, $termString)
222     {
223         $escapedOperators = $this->getRegexEscapedOperators();
224         // Look for tag searches
225         preg_match_all("/\[(.*?)((${escapedOperators})(.*?))?\]/", $termString, $tags);
226         if (count($tags[0]) > 0) {
227             $this->applyTagSearches($query, $tags);
228         }
229
230         return $query;
231     }
232
233     /**
234      * Apply extracted tag search terms onto a entity query.
235      * @param $query
236      * @param $tags
237      * @return mixed
238      */
239     protected function applyTagSearches($query, $tags) {
240         $query->where(function($query) use ($tags) {
241             foreach ($tags[1] as $index => $tagName) {
242                 $query->whereHas('tags', function($query) use ($tags, $index, $tagName) {
243                     $tagOperator = $tags[3][$index];
244                     $tagValue = $tags[4][$index];
245                     if (!empty($tagOperator) && !empty($tagValue) && in_array($tagOperator, $this->queryOperators)) {
246                         if (is_numeric($tagValue) && $tagOperator !== 'like') {
247                             // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
248                             // search the value as a string which prevents being able to do number-based operations
249                             // on the tag values. We ensure it has a numeric value and then cast it just to be sure.
250                             $tagValue = (float) trim($query->getConnection()->getPdo()->quote($tagValue), "'");
251                             $query->where('name', '=', $tagName)->whereRaw("value ${tagOperator} ${tagValue}");
252                         } else {
253                             $query->where('name', '=', $tagName)->where('value', $tagOperator, $tagValue);
254                         }
255                     } else {
256                         $query->where('name', '=', $tagName);
257                     }
258                 });
259             }
260         });
261         return $query;
262     }
263
264     /**
265      * Alias method to update the book jointPermissions in the PermissionService.
266      * @param Collection $collection collection on entities
267      */
268     public function buildJointPermissions(Collection $collection)
269     {
270         $this->permissionService->buildJointPermissionsForEntities($collection);
271     }
272
273 }
274
275
276
277
278
279
280
281
282
283
284
285