1 <?php namespace BookStack\Repos;
7 use BookStack\Services\PermissionService;
9 use Illuminate\Support\Collection;
10 use Illuminate\Support\Facades\Log;
31 * @var PermissionService
33 protected $permissionService;
36 * Acceptable operators to be used in a query
39 protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
42 * EntityService constructor.
44 public function __construct()
46 $this->book = app(Book::class);
47 $this->chapter = app(Chapter::class);
48 $this->page = app(Page::class);
49 $this->permissionService = app(PermissionService::class);
53 * Get the latest books added to the system.
56 * @param bool $additionalQuery
59 public function getRecentlyCreatedBooks($count = 20, $page = 0, $additionalQuery = false)
61 $query = $this->permissionService->enforceBookRestrictions($this->book)
62 ->orderBy('created_at', 'desc');
63 if ($additionalQuery !== false && is_callable($additionalQuery)) {
64 $additionalQuery($query);
66 return $query->skip($page * $count)->take($count)->get();
70 * Get the most recently updated books.
75 public function getRecentlyUpdatedBooks($count = 20, $page = 0)
77 return $this->permissionService->enforceBookRestrictions($this->book)
78 ->orderBy('updated_at', 'desc')->skip($page * $count)->take($count)->get();
82 * Get the latest pages added to the system.
85 * @param bool $additionalQuery
88 public function getRecentlyCreatedPages($count = 20, $page = 0, $additionalQuery = false)
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);
95 return $query->with('book')->skip($page * $count)->take($count)->get();
99 * Get the latest chapters added to the system.
102 * @param bool $additionalQuery
105 public function getRecentlyCreatedChapters($count = 20, $page = 0, $additionalQuery = false)
107 $query = $this->permissionService->enforceChapterRestrictions($this->chapter)
108 ->orderBy('created_at', 'desc');
109 if ($additionalQuery !== false && is_callable($additionalQuery)) {
110 $additionalQuery($query);
112 return $query->skip($page * $count)->take($count)->get();
116 * Get the most recently updated pages.
121 public function getRecentlyUpdatedPages($count = 20, $page = 0)
123 return $this->permissionService->enforcePageRestrictions($this->page)
124 ->where('draft', '=', false)
125 ->orderBy('updated_at', 'desc')->with('book')->skip($page * $count)->take($count)->get();
129 * Get draft pages owned by the current user.
133 public function getUserDraftPages($count = 20, $page = 0)
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();
143 * Updates entity restrictions from a request
145 * @param Entity $entity
147 public function updateEntityPermissionsFromRequest($request, Entity $entity)
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)
162 $this->permissionService->buildJointPermissionsForEntity($entity);
166 * Prepare a string of search terms by turning
167 * it into an array of terms.
168 * Keeps quoted terms together.
172 public function prepareSearchTerms($termString)
174 $termString = $this->cleanSearchTermString($termString);
175 preg_match_all('/(".*?")/', $termString, $matches);
177 if (count($matches[1]) > 0) {
178 foreach ($matches[1] as $match) {
181 $termString = trim(preg_replace('/"(.*?)"/', '', $termString));
183 if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString));
188 * Removes any special search notation that should not
189 * be used in a full-text search.
193 protected function cleanSearchTermString($termString)
195 // Strip tag searches
196 $termString = preg_replace('/\[.*?\]/', '', $termString);
197 // Reduced multiple spacing into single spacing
198 $termString = preg_replace("/\s{2,}/", " ", $termString);
203 * Get the available query operators as a regex escaped list.
206 protected function getRegexEscapedOperators()
208 $escapedOperators = [];
209 foreach ($this->queryOperators as $operator) {
210 $escapedOperators[] = preg_quote($operator);
212 return join('|', $escapedOperators);
216 * Parses advanced search notations and adds them to the db query.
221 protected function addAdvancedSearchQueries($query, $termString)
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);
234 * Apply extracted tag search terms onto a entity query.
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}");
253 $query->where('name', '=', $tagName)->where('value', $tagOperator, $tagValue);
256 $query->where('name', '=', $tagName);
265 * Alias method to update the book jointPermissions in the PermissionService.
266 * @param Collection $collection collection on entities
268 public function buildJointPermissions(Collection $collection)
270 $this->permissionService->buildJointPermissionsForEntities($collection);