]> BookStack Code Mirror - bookstack/blobdiff - app/Repos/EntityRepo.php
Merge branch 'master' into translations
[bookstack] / app / Repos / EntityRepo.php
index 28942d94a081fc532e62d50eb33f29b772b90b0b..7ecfb758c9e31364414987cc1121fa1df18f377d 100644 (file)
@@ -1,38 +1,69 @@
 <?php namespace BookStack\Repos;
 
-
 use BookStack\Book;
 use BookStack\Chapter;
+use BookStack\Entity;
 use BookStack\Page;
+use BookStack\Services\PermissionService;
+use BookStack\User;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Log;
 
 class EntityRepo
 {
 
+    /**
+     * @var Book $book
+     */
     public $book;
+
+    /**
+     * @var Chapter
+     */
     public $chapter;
+
+    /**
+     * @var Page
+     */
     public $page;
 
+    /**
+     * @var PermissionService
+     */
+    protected $permissionService;
+
+    /**
+     * Acceptable operators to be used in a query
+     * @var array
+     */
+    protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
+
     /**
      * EntityService constructor.
-     * @param $book
-     * @param $chapter
-     * @param $page
      */
-    public function __construct(Book $book, Chapter $chapter, Page $page)
+    public function __construct()
     {
-        $this->book = $book;
-        $this->chapter = $chapter;
-        $this->page = $page;
+        $this->book = app(Book::class);
+        $this->chapter = app(Chapter::class);
+        $this->page = app(Page::class);
+        $this->permissionService = app(PermissionService::class);
     }
 
     /**
      * Get the latest books added to the system.
-     * @param $count
-     * @param $page
+     * @param int $count
+     * @param int $page
+     * @param bool $additionalQuery
+     * @return
      */
-    public function getRecentlyCreatedBooks($count = 20, $page = 0)
+    public function getRecentlyCreatedBooks($count = 20, $page = 0, $additionalQuery = false)
     {
-        return $this->book->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get();
+        $query = $this->permissionService->enforceBookRestrictions($this->book)
+            ->orderBy('created_at', 'desc');
+        if ($additionalQuery !== false && is_callable($additionalQuery)) {
+            $additionalQuery($query);
+        }
+        return $query->skip($page * $count)->take($count)->get();
     }
 
     /**
@@ -43,17 +74,42 @@ class EntityRepo
      */
     public function getRecentlyUpdatedBooks($count = 20, $page = 0)
     {
-        return $this->book->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get();
+        return $this->permissionService->enforceBookRestrictions($this->book)
+            ->orderBy('updated_at', 'desc')->skip($page * $count)->take($count)->get();
     }
 
     /**
      * Get the latest pages added to the system.
-     * @param $count
-     * @param $page
+     * @param int $count
+     * @param int $page
+     * @param bool $additionalQuery
+     * @return
      */
-    public function getRecentlyCreatedPages($count = 20, $page = 0)
+    public function getRecentlyCreatedPages($count = 20, $page = 0, $additionalQuery = false)
     {
-        return $this->page->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get();
+        $query = $this->permissionService->enforcePageRestrictions($this->page)
+            ->orderBy('created_at', 'desc')->where('draft', '=', false);
+        if ($additionalQuery !== false && is_callable($additionalQuery)) {
+            $additionalQuery($query);
+        }
+        return $query->with('book')->skip($page * $count)->take($count)->get();
+    }
+
+    /**
+     * Get the latest chapters added to the system.
+     * @param int $count
+     * @param int $page
+     * @param bool $additionalQuery
+     * @return
+     */
+    public function getRecentlyCreatedChapters($count = 20, $page = 0, $additionalQuery = false)
+    {
+        $query = $this->permissionService->enforceChapterRestrictions($this->chapter)
+            ->orderBy('created_at', 'desc');
+        if ($additionalQuery !== false && is_callable($additionalQuery)) {
+            $additionalQuery($query);
+        }
+        return $query->skip($page * $count)->take($count)->get();
     }
 
     /**
@@ -64,8 +120,178 @@ class EntityRepo
      */
     public function getRecentlyUpdatedPages($count = 20, $page = 0)
     {
-        return $this->page->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get();
+        return $this->permissionService->enforcePageRestrictions($this->page)
+            ->where('draft', '=', false)
+            ->orderBy('updated_at', 'desc')->with('book')->skip($page * $count)->take($count)->get();
+    }
+
+    /**
+     * Get draft pages owned by the current user.
+     * @param int $count
+     * @param int $page
+     */
+    public function getUserDraftPages($count = 20, $page = 0)
+    {
+        return $this->page->where('draft', '=', true)
+            ->where('created_by', '=', user()->id)
+            ->orderBy('updated_at', 'desc')
+            ->skip($count * $page)->take($count)->get();
+    }
+
+    /**
+     * Updates entity restrictions from a request
+     * @param $request
+     * @param Entity $entity
+     */
+    public function updateEntityPermissionsFromRequest($request, Entity $entity)
+    {
+        $entity->restricted = $request->has('restricted') && $request->get('restricted') === 'true';
+        $entity->permissions()->delete();
+        if ($request->has('restrictions')) {
+            foreach ($request->get('restrictions') as $roleId => $restrictions) {
+                foreach ($restrictions as $action => $value) {
+                    $entity->permissions()->create([
+                        'role_id' => $roleId,
+                        'action'  => strtolower($action)
+                    ]);
+                }
+            }
+        }
+        $entity->save();
+        $this->permissionService->buildJointPermissionsForEntity($entity);
+    }
+
+    /**
+     * Prepare a string of search terms by turning
+     * it into an array of terms.
+     * Keeps quoted terms together.
+     * @param $termString
+     * @return array
+     */
+    public function prepareSearchTerms($termString)
+    {
+        $termString = $this->cleanSearchTermString($termString);
+        preg_match_all('/(".*?")/', $termString, $matches);
+        $terms = [];
+        if (count($matches[1]) > 0) {
+            foreach ($matches[1] as $match) {
+                $terms[] = $match;
+            }
+            $termString = trim(preg_replace('/"(.*?)"/', '', $termString));
+        }
+        if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString));
+        return $terms;
+    }
+
+    /**
+     * Removes any special search notation that should not
+     * be used in a full-text search.
+     * @param $termString
+     * @return mixed
+     */
+    protected function cleanSearchTermString($termString)
+    {
+        // Strip tag searches
+        $termString = preg_replace('/\[.*?\]/', '', $termString);
+        // Reduced multiple spacing into single spacing
+        $termString = preg_replace("/\s{2,}/", " ", $termString);
+        return $termString;
+    }
+
+    /**
+     * Get the available query operators as a regex escaped list.
+     * @return mixed
+     */
+    protected function getRegexEscapedOperators()
+    {
+        $escapedOperators = [];
+        foreach ($this->queryOperators as $operator) {
+            $escapedOperators[] = preg_quote($operator);
+        }
+        return join('|', $escapedOperators);
+    }
+
+    /**
+     * Parses advanced search notations and adds them to the db query.
+     * @param $query
+     * @param $termString
+     * @return mixed
+     */
+    protected function addAdvancedSearchQueries($query, $termString)
+    {
+        $escapedOperators = $this->getRegexEscapedOperators();
+        // Look for tag searches
+        preg_match_all("/\[(.*?)((${escapedOperators})(.*?))?\]/", $termString, $tags);
+        if (count($tags[0]) > 0) {
+            $this->applyTagSearches($query, $tags);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Apply extracted tag search terms onto a entity query.
+     * @param $query
+     * @param $tags
+     * @return mixed
+     */
+    protected function applyTagSearches($query, $tags) {
+        $query->where(function($query) use ($tags) {
+            foreach ($tags[1] as $index => $tagName) {
+                $query->whereHas('tags', function($query) use ($tags, $index, $tagName) {
+                    $tagOperator = $tags[3][$index];
+                    $tagValue = $tags[4][$index];
+                    if (!empty($tagOperator) && !empty($tagValue) && in_array($tagOperator, $this->queryOperators)) {
+                        if (is_numeric($tagValue) && $tagOperator !== 'like') {
+                            // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
+                            // search the value as a string which prevents being able to do number-based operations
+                            // on the tag values. We ensure it has a numeric value and then cast it just to be sure.
+                            $tagValue = (float) trim($query->getConnection()->getPdo()->quote($tagValue), "'");
+                            $query->where('name', '=', $tagName)->whereRaw("value ${tagOperator} ${tagValue}");
+                        } else {
+                            $query->where('name', '=', $tagName)->where('value', $tagOperator, $tagValue);
+                        }
+                    } else {
+                        $query->where('name', '=', $tagName);
+                    }
+                });
+            }
+        });
+        return $query;
     }
 
+    /**
+     * Alias method to update the book jointPermissions in the PermissionService.
+     * @param Collection $collection collection on entities
+     */
+    public function buildJointPermissions(Collection $collection)
+    {
+        $this->permissionService->buildJointPermissionsForEntities($collection);
+    }
+
+    /**
+     * Format a name as a url slug.
+     * @param $name
+     * @return string
+     */
+    protected function nameToSlug($name)
+    {
+        $slug = str_replace(' ', '-', strtolower($name));
+        $slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', $slug);
+        if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
+        return $slug;
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
 
-}
\ No newline at end of file