Updated various classes to take EntityProvider instead of separate entities

This commit is contained in:
Dan Brown 2018-09-25 18:00:40 +01:00
parent 257a5a23ec
commit 495d18814a
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
9 changed files with 189 additions and 181 deletions

View File

@ -6,6 +6,7 @@ use BookStack\Entities\Book;
use BookStack\Entities\Bookshelf; use BookStack\Entities\Bookshelf;
use BookStack\Entities\Chapter; use BookStack\Entities\Chapter;
use BookStack\Entities\Entity; use BookStack\Entities\Entity;
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Page; use BookStack\Entities\Page;
use BookStack\Ownable; use BookStack\Ownable;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
@ -21,17 +22,31 @@ class PermissionService
protected $userRoles = false; protected $userRoles = false;
protected $currentUserModel = false; protected $currentUserModel = false;
public $book; /**
public $chapter; * @var Connection
public $page; */
public $bookshelf;
protected $db; protected $db;
/**
* @var JointPermission
*/
protected $jointPermission; protected $jointPermission;
/**
* @var Role
*/
protected $role; protected $role;
/**
* @var EntityPermission
*/
protected $entityPermission; protected $entityPermission;
/**
* @var EntityProvider
*/
protected $entityProvider;
protected $entityCache; protected $entityCache;
/** /**
@ -40,29 +55,20 @@ class PermissionService
* @param EntityPermission $entityPermission * @param EntityPermission $entityPermission
* @param Role $role * @param Role $role
* @param Connection $db * @param Connection $db
* @param Bookshelf $bookshelf * @param EntityProvider $entityProvider
* @param Book $book
* @param \BookStack\Entities\Chapter $chapter
* @param \BookStack\Entities\Page $page
*/ */
public function __construct( public function __construct(
JointPermission $jointPermission, JointPermission $jointPermission,
Permissions\EntityPermission $entityPermission, Permissions\EntityPermission $entityPermission,
Role $role, Role $role,
Connection $db, Connection $db,
Bookshelf $bookshelf, EntityProvider $entityProvider
Book $book,
Chapter $chapter,
Page $page
) { ) {
$this->db = $db; $this->db = $db;
$this->jointPermission = $jointPermission; $this->jointPermission = $jointPermission;
$this->entityPermission = $entityPermission; $this->entityPermission = $entityPermission;
$this->role = $role; $this->role = $role;
$this->bookshelf = $bookshelf; $this->entityProvider = $entityProvider;
$this->book = $book;
$this->chapter = $chapter;
$this->page = $page;
} }
/** /**
@ -102,7 +108,7 @@ class PermissionService
return $this->entityCache['book']->get($bookId); return $this->entityCache['book']->get($bookId);
} }
$book = $this->book->find($bookId); $book = $this->entityProvider->book->find($bookId);
if ($book === null) { if ($book === null) {
$book = false; $book = false;
} }
@ -121,7 +127,7 @@ class PermissionService
return $this->entityCache['chapter']->get($chapterId); return $this->entityCache['chapter']->get($chapterId);
} }
$chapter = $this->chapter->find($chapterId); $chapter = $this->entityProvider->chapter->find($chapterId);
if ($chapter === null) { if ($chapter === null) {
$chapter = false; $chapter = false;
} }
@ -170,7 +176,7 @@ class PermissionService
}); });
// Chunk through all bookshelves // Chunk through all bookshelves
$this->bookshelf->newQuery()->select(['id', 'restricted', 'created_by']) $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
->chunk(50, function ($shelves) use ($roles) { ->chunk(50, function ($shelves) use ($roles) {
$this->buildJointPermissionsForShelves($shelves, $roles); $this->buildJointPermissionsForShelves($shelves, $roles);
}); });
@ -182,7 +188,8 @@ class PermissionService
*/ */
protected function bookFetchQuery() protected function bookFetchQuery()
{ {
return $this->book->newQuery()->select(['id', 'restricted', 'created_by'])->with(['chapters' => function ($query) { return $this->entityProvider->book->newQuery()
->select(['id', 'restricted', 'created_by'])->with(['chapters' => function ($query) {
$query->select(['id', 'restricted', 'created_by', 'book_id']); $query->select(['id', 'restricted', 'created_by', 'book_id']);
}, 'pages' => function ($query) { }, 'pages' => function ($query) {
$query->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']); $query->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']);
@ -288,7 +295,7 @@ class PermissionService
}); });
// Chunk through all bookshelves // Chunk through all bookshelves
$this->bookshelf->newQuery()->select(['id', 'restricted', 'created_by']) $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
->chunk(50, function ($shelves) use ($roles) { ->chunk(50, function ($shelves) use ($roles) {
$this->buildJointPermissionsForShelves($shelves, $roles); $this->buildJointPermissionsForShelves($shelves, $roles);
}); });
@ -602,7 +609,9 @@ class PermissionService
*/ */
public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false)
{ {
$pageSelect = $this->db->table('pages')->selectRaw($this->page->entityRawQuery($fetchPageContent))->where('book_id', '=', $book_id)->where(function ($query) use ($filterDrafts) { $entities = $this->entityProvider;
$pageSelect = $this->db->table('pages')->selectRaw($entities->page->entityRawQuery($fetchPageContent))
->where('book_id', '=', $book_id)->where(function ($query) use ($filterDrafts) {
$query->where('draft', '=', 0); $query->where('draft', '=', 0);
if (!$filterDrafts) { if (!$filterDrafts) {
$query->orWhere(function ($query) { $query->orWhere(function ($query) {
@ -610,7 +619,7 @@ class PermissionService
}); });
} }
}); });
$chapterSelect = $this->db->table('chapters')->selectRaw($this->chapter->entityRawQuery())->where('book_id', '=', $book_id); $chapterSelect = $this->db->table('chapters')->selectRaw($entities->chapter->entityRawQuery())->where('book_id', '=', $book_id);
$query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U")) $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U"))
->mergeBindings($pageSelect)->mergeBindings($chapterSelect); ->mergeBindings($pageSelect)->mergeBindings($chapterSelect);
@ -701,7 +710,7 @@ class PermissionService
$this->currentAction = 'view'; $this->currentAction = 'view';
$tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn]; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
$pageMorphClass = $this->page->getMorphClass(); $pageMorphClass = $this->entityProvider->page->getMorphClass();
$q = $query->where(function ($query) use ($tableDetails, $pageMorphClass) { $q = $query->where(function ($query) use ($tableDetails, $pageMorphClass) {
$query->where(function ($query) use (&$tableDetails, $pageMorphClass) { $query->where(function ($query) use (&$tableDetails, $pageMorphClass) {
$query->whereExists(function ($permissionQuery) use (&$tableDetails, $pageMorphClass) { $query->whereExists(function ($permissionQuery) use (&$tableDetails, $pageMorphClass) {

View File

@ -212,9 +212,9 @@ class UserRepo
public function getAssetCounts(User $user) public function getAssetCounts(User $user)
{ {
return [ return [
'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->count(), 'pages' => $this->entityRepo->getUserTotalCreated('page', $user),
'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->count(), 'chapters' => $this->entityRepo->getUserTotalCreated('chapter', $user),
'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->count(), 'books' => $this->entityRepo->getUserTotalCreated('book', $user),
]; ];
} }

View File

@ -7,8 +7,25 @@ use BookStack\Actions\View;
use BookStack\Auth\Permissions\EntityPermission; use BookStack\Auth\Permissions\EntityPermission;
use BookStack\Auth\Permissions\JointPermission; use BookStack\Auth\Permissions\JointPermission;
use BookStack\Ownable; use BookStack\Ownable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphMany;
/**
* Class Entity
* The base class for book-like items such as pages, chapters & books.
* This is not a database model in itself but extended.
*
* @property integer $id
* @property string $name
* @property string $slug
* @property Carbon $created_at
* @property Carbon $updated_at
* @property int $created_by
* @property int $updated_by
* @property boolean $restricted
*
* @package BookStack\Entities
*/
class Entity extends Ownable class Entity extends Ownable
{ {

View File

@ -15,27 +15,27 @@ class EntityProvider
/** /**
* @var Bookshelf * @var Bookshelf
*/ */
protected $bookshelf; public $bookshelf;
/** /**
* @var Book * @var Book
*/ */
protected $book; public $book;
/** /**
* @var Chapter * @var Chapter
*/ */
protected $chapter; public $chapter;
/** /**
* @var Page * @var Page
*/ */
protected $page; public $page;
/** /**
* @var PageRevision * @var PageRevision
*/ */
protected $pageRevision; public $pageRevision;
/** /**
* EntityProvider constructor. * EntityProvider constructor.
@ -74,5 +74,16 @@ class EntityProvider
]; ];
} }
/**
* Get an entity instance by it's basic name.
* @param string $type
* @return Entity
*/
public function get(string $type)
{
$type = strtolower($type);
return $this->all()[$type];
}
} }

View File

@ -3,46 +3,23 @@
use BookStack\Actions\TagRepo; use BookStack\Actions\TagRepo;
use BookStack\Actions\ViewService; use BookStack\Actions\ViewService;
use BookStack\Auth\Permissions\PermissionService; use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User;
use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\NotifyException;
use BookStack\Uploads\AttachmentService; use BookStack\Uploads\AttachmentService;
use Carbon\Carbon; use Carbon\Carbon;
use DOMDocument; use DOMDocument;
use DOMXPath; use DOMXPath;
use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
class EntityRepo class EntityRepo
{ {
/**
* @var \BookStack\Entities\Bookshelf
*/
public $bookshelf;
/** /**
* @var \BookStack\Entities\Book $book * @var EntityProvider
*/ */
public $book; protected $entityProvider;
/**
* @var Chapter
*/
public $chapter;
/**
* @var Page
*/
public $page;
/**
* @var PageRevision
*/
protected $pageRevision;
/**
* Base entity instances keyed by type
* @var []Entity
*/
protected $entities;
/** /**
* @var PermissionService * @var PermissionService
@ -55,7 +32,7 @@ class EntityRepo
protected $viewService; protected $viewService;
/** /**
* @var \BookStack\Actions\TagRepo * @var TagRepo
*/ */
protected $tagRepo; protected $tagRepo;
@ -66,63 +43,36 @@ class EntityRepo
/** /**
* EntityRepo constructor. * EntityRepo constructor.
* @param \BookStack\Entities\Bookshelf $bookshelf * @param EntityProvider $entityProvider
* @param \BookStack\Entities\Book $book
* @param Chapter $chapter
* @param \BookStack\Entities\Page $page
* @param \BookStack\Entities\PageRevision $pageRevision
* @param ViewService $viewService * @param ViewService $viewService
* @param PermissionService $permissionService * @param PermissionService $permissionService
* @param \BookStack\Actions\TagRepo $tagRepo * @param TagRepo $tagRepo
* @param SearchService $searchService * @param SearchService $searchService
*/ */
public function __construct( public function __construct(
Bookshelf $bookshelf, EntityProvider $entityProvider,
Book $book,
Chapter $chapter,
Page $page,
PageRevision $pageRevision,
ViewService $viewService, ViewService $viewService,
PermissionService $permissionService, PermissionService $permissionService,
TagRepo $tagRepo, TagRepo $tagRepo,
SearchService $searchService SearchService $searchService
) { ) {
$this->bookshelf = $bookshelf; $this->entityProvider = $entityProvider;
$this->book = $book;
$this->chapter = $chapter;
$this->page = $page;
$this->pageRevision = $pageRevision;
$this->entities = [
'bookshelf' => $this->bookshelf,
'page' => $this->page,
'chapter' => $this->chapter,
'book' => $this->book
];
$this->viewService = $viewService; $this->viewService = $viewService;
$this->permissionService = $permissionService; $this->permissionService = $permissionService;
$this->tagRepo = $tagRepo; $this->tagRepo = $tagRepo;
$this->searchService = $searchService; $this->searchService = $searchService;
} }
/**
* Get an entity instance via type.
* @param $type
* @return \BookStack\Entities\Entity
*/
protected function getEntity($type)
{
return $this->entities[strtolower($type)];
}
/** /**
* Base query for searching entities via permission system * Base query for searching entities via permission system
* @param string $type * @param string $type
* @param bool $allowDrafts * @param bool $allowDrafts
* @param string $permission
* @return \Illuminate\Database\Query\Builder * @return \Illuminate\Database\Query\Builder
*/ */
protected function entityQuery($type, $allowDrafts = false, $permission = 'view') protected function entityQuery($type, $allowDrafts = false, $permission = 'view')
{ {
$q = $this->permissionService->enforceEntityRestrictions($type, $this->getEntity($type), $permission); $q = $this->permissionService->enforceEntityRestrictions($type, $this->entityProvider->get($type), $permission);
if (strtolower($type) === 'page' && !$allowDrafts) { if (strtolower($type) === 'page' && !$allowDrafts) {
$q = $q->where('draft', '=', false); $q = $q->where('draft', '=', false);
} }
@ -150,11 +100,31 @@ class EntityRepo
*/ */
public function getById($type, $id, $allowDrafts = false, $ignorePermissions = false) public function getById($type, $id, $allowDrafts = false, $ignorePermissions = false)
{ {
$query = $this->entityQuery($type, $allowDrafts);
if ($ignorePermissions) { if ($ignorePermissions) {
$entity = $this->getEntity($type); $query = $this->entityProvider->get($type)->newQuery();
return $entity->newQuery()->find($id);
} }
return $this->entityQuery($type, $allowDrafts)->find($id);
return $query->find($id);
}
/**
* @param string $type
* @param []int $ids
* @param bool $allowDrafts
* @param bool $ignorePermissions
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|Collection
*/
public function getManyById($type, $ids, $allowDrafts = false, $ignorePermissions = false)
{
$query = $this->entityQuery($type, $allowDrafts);
if ($ignorePermissions) {
$query = $this->entityProvider->get($type)->newQuery();
}
return $query->whereIn('id', $ids)->get();
} }
/** /**
@ -172,7 +142,7 @@ class EntityRepo
if (strtolower($type) === 'chapter' || strtolower($type) === 'page') { if (strtolower($type) === 'chapter' || strtolower($type) === 'page') {
$q = $q->where('book_id', '=', function ($query) use ($bookSlug) { $q = $q->where('book_id', '=', function ($query) use ($bookSlug) {
$query->select('id') $query->select('id')
->from($this->book->getTable()) ->from($this->entityProvider->book->getTable())
->where('slug', '=', $bookSlug)->limit(1); ->where('slug', '=', $bookSlug)->limit(1);
}); });
} }
@ -193,7 +163,7 @@ class EntityRepo
*/ */
public function getPageByOldSlug($pageSlug, $bookSlug) public function getPageByOldSlug($pageSlug, $bookSlug)
{ {
$revision = $this->pageRevision->where('slug', '=', $pageSlug) $revision = $this->entityProvider->pageRevision->where('slug', '=', $pageSlug)
->whereHas('page', function ($query) { ->whereHas('page', function ($query) {
$this->permissionService->enforceEntityRestrictions('page', $query); $this->permissionService->enforceEntityRestrictions('page', $query);
}) })
@ -241,7 +211,7 @@ class EntityRepo
*/ */
public function getRecentlyCreated($type, $count = 20, $page = 0, $additionalQuery = false) public function getRecentlyCreated($type, $count = 20, $page = 0, $additionalQuery = false)
{ {
$query = $this->permissionService->enforceEntityRestrictions($type, $this->getEntity($type)) $query = $this->permissionService->enforceEntityRestrictions($type, $this->entityProvider->get($type))
->orderBy('created_at', 'desc'); ->orderBy('created_at', 'desc');
if (strtolower($type) === 'page') { if (strtolower($type) === 'page') {
$query = $query->where('draft', '=', false); $query = $query->where('draft', '=', false);
@ -262,7 +232,7 @@ class EntityRepo
*/ */
public function getRecentlyUpdated($type, $count = 20, $page = 0, $additionalQuery = false) public function getRecentlyUpdated($type, $count = 20, $page = 0, $additionalQuery = false)
{ {
$query = $this->permissionService->enforceEntityRestrictions($type, $this->getEntity($type)) $query = $this->permissionService->enforceEntityRestrictions($type, $this->entityProvider->get($type))
->orderBy('updated_at', 'desc'); ->orderBy('updated_at', 'desc');
if (strtolower($type) === 'page') { if (strtolower($type) === 'page') {
$query = $query->where('draft', '=', false); $query = $query->where('draft', '=', false);
@ -282,7 +252,7 @@ class EntityRepo
*/ */
public function getRecentlyViewed($type, $count = 10, $page = 0) public function getRecentlyViewed($type, $count = 10, $page = 0)
{ {
$filter = is_bool($type) ? false : $this->getEntity($type); $filter = is_bool($type) ? false : $this->entityProvider->get($type);
return $this->viewService->getUserRecentlyViewed($count, $page, $filter); return $this->viewService->getUserRecentlyViewed($count, $page, $filter);
} }
@ -317,7 +287,7 @@ class EntityRepo
*/ */
public function getPopular($type, $count = 10, $page = 0) public function getPopular($type, $count = 10, $page = 0)
{ {
$filter = is_bool($type) ? false : $this->getEntity($type); $filter = is_bool($type) ? false : $this->entityProvider->get($type);
return $this->viewService->getPopular($count, $page, $filter); return $this->viewService->getPopular($count, $page, $filter);
} }
@ -325,15 +295,28 @@ class EntityRepo
* Get draft pages owned by the current user. * Get draft pages owned by the current user.
* @param int $count * @param int $count
* @param int $page * @param int $page
* @return Collection
*/ */
public function getUserDraftPages($count = 20, $page = 0) public function getUserDraftPages($count = 20, $page = 0)
{ {
return $this->page->where('draft', '=', true) return $this->entityProvider->page->where('draft', '=', true)
->where('created_by', '=', user()->id) ->where('created_by', '=', user()->id)
->orderBy('updated_at', 'desc') ->orderBy('updated_at', 'desc')
->skip($count * $page)->take($count)->get(); ->skip($count * $page)->take($count)->get();
} }
/**
* Get the number of entities the given user has created.
* @param string $type
* @param User $user
* @return int
*/
public function getUserTotalCreated(string $type, User $user)
{
return $this->entityProvider->get($type)
->where('created_by', '=', $user->id)->count();
}
/** /**
* Get the child items for a chapter sorted by priority but * Get the child items for a chapter sorted by priority but
* with draft items floated to the top. * with draft items floated to the top.
@ -362,14 +345,14 @@ class EntityRepo
$tree = []; $tree = [];
foreach ($q as $index => $rawEntity) { foreach ($q as $index => $rawEntity) {
if ($rawEntity->entity_type === $this->page->getMorphClass()) { if ($rawEntity->entity_type === $this->entityProvider->page->getMorphClass()) {
$entities[$index] = $this->page->newFromBuilder($rawEntity); $entities[$index] = $this->entityProvider->page->newFromBuilder($rawEntity);
if ($renderPages) { if ($renderPages) {
$entities[$index]->html = $rawEntity->html; $entities[$index]->html = $rawEntity->html;
$entities[$index]->html = $this->renderPage($entities[$index]); $entities[$index]->html = $this->renderPage($entities[$index]);
}; };
} else if ($rawEntity->entity_type === $this->chapter->getMorphClass()) { } else if ($rawEntity->entity_type === $this->entityProvider->chapter->getMorphClass()) {
$entities[$index] = $this->chapter->newFromBuilder($rawEntity); $entities[$index] = $this->entityProvider->chapter->newFromBuilder($rawEntity);
$key = $entities[$index]->entity_type . ':' . $entities[$index]->id; $key = $entities[$index]->entity_type . ':' . $entities[$index]->id;
$parents[$key] = $entities[$index]; $parents[$key] = $entities[$index];
$parents[$key]->setAttribute('pages', collect()); $parents[$key]->setAttribute('pages', collect());
@ -384,7 +367,7 @@ class EntityRepo
if ($entity->chapter_id === 0 || $entity->chapter_id === '0') { if ($entity->chapter_id === 0 || $entity->chapter_id === '0') {
continue; continue;
} }
$parentKey = $this->chapter->getMorphClass() . ':' . $entity->chapter_id; $parentKey = $this->entityProvider->chapter->getMorphClass() . ':' . $entity->chapter_id;
if (!isset($parents[$parentKey])) { if (!isset($parents[$parentKey])) {
$tree[] = $entity; $tree[] = $entity;
continue; continue;
@ -458,7 +441,7 @@ class EntityRepo
*/ */
protected function slugExists($type, $slug, $currentId = false, $bookId = false) protected function slugExists($type, $slug, $currentId = false, $bookId = false)
{ {
$query = $this->getEntity($type)->where('slug', '=', $slug); $query = $this->entityProvider->get($type)->where('slug', '=', $slug);
if (strtolower($type) === 'page' || strtolower($type) === 'chapter') { if (strtolower($type) === 'page' || strtolower($type) === 'chapter') {
$query = $query->where('book_id', '=', $bookId); $query = $query->where('book_id', '=', $bookId);
} }
@ -470,10 +453,11 @@ class EntityRepo
/** /**
* Updates entity restrictions from a request * Updates entity restrictions from a request
* @param $request * @param Request $request
* @param \BookStack\Entities\Entity $entity * @param \BookStack\Entities\Entity $entity
* @throws \Throwable
*/ */
public function updateEntityPermissionsFromRequest($request, Entity $entity) public function updateEntityPermissionsFromRequest(Request $request, Entity $entity)
{ {
$entity->restricted = $request->get('restricted', '') === 'true'; $entity->restricted = $request->get('restricted', '') === 'true';
$entity->permissions()->delete(); $entity->permissions()->delete();
@ -506,7 +490,7 @@ class EntityRepo
public function createFromInput($type, $input = [], $book = false) public function createFromInput($type, $input = [], $book = false)
{ {
$isChapter = strtolower($type) === 'chapter'; $isChapter = strtolower($type) === 'chapter';
$entityModel = $this->getEntity($type)->newInstance($input); $entityModel = $this->entityProvider->get($type)->newInstance($input);
$entityModel->slug = $this->findSuitableSlug($type, $entityModel->name, false, $isChapter ? $book->id : false); $entityModel->slug = $this->findSuitableSlug($type, $entityModel->name, false, $isChapter ? $book->id : false);
$entityModel->created_by = user()->id; $entityModel->created_by = user()->id;
$entityModel->updated_by = user()->id; $entityModel->updated_by = user()->id;
@ -637,7 +621,7 @@ class EntityRepo
*/ */
public function getDraftPage(Book $book, $chapter = false) public function getDraftPage(Book $book, $chapter = false)
{ {
$page = $this->page->newInstance(); $page = $this->entityProvider->page->newInstance();
$page->name = trans('entities.pages_initial_name'); $page->name = trans('entities.pages_initial_name');
$page->created_by = user()->id; $page->created_by = user()->id;
$page->updated_by = user()->id; $page->updated_by = user()->id;
@ -648,7 +632,7 @@ class EntityRepo
} }
$book->pages()->save($page); $book->pages()->save($page);
$page = $this->page->find($page->id); $page = $this->entityProvider->page->find($page->id);
$this->permissionService->buildJointPermissionsForEntity($page); $this->permissionService->buildJointPermissionsForEntity($page);
return $page; return $page;
} }
@ -726,7 +710,7 @@ class EntityRepo
*/ */
public function savePageRevision(Page $page, $summary = null) public function savePageRevision(Page $page, $summary = null)
{ {
$revision = $this->pageRevision->newInstance($page->toArray()); $revision = $this->entityProvider->pageRevision->newInstance($page->toArray());
if (setting('app-editor') !== 'markdown') { if (setting('app-editor') !== 'markdown') {
$revision->markdown = ''; $revision->markdown = '';
} }
@ -742,10 +726,10 @@ class EntityRepo
$revisionLimit = config('app.revision_limit'); $revisionLimit = config('app.revision_limit');
if ($revisionLimit !== false) { if ($revisionLimit !== false) {
$revisionsToDelete = $this->pageRevision->where('page_id', '=', $page->id) $revisionsToDelete = $this->entityProvider->pageRevision->where('page_id', '=', $page->id)
->orderBy('created_at', 'desc')->skip(intval($revisionLimit))->take(10)->get(['id']); ->orderBy('created_at', 'desc')->skip(intval($revisionLimit))->take(10)->get(['id']);
if ($revisionsToDelete->count() > 0) { if ($revisionsToDelete->count() > 0) {
$this->pageRevision->whereIn('id', $revisionsToDelete->pluck('id'))->delete(); $this->entityProvider->pageRevision->whereIn('id', $revisionsToDelete->pluck('id'))->delete();
} }
} }
@ -1019,7 +1003,7 @@ class EntityRepo
*/ */
protected function userUpdatePageDraftsQuery(Page $page, $userId) protected function userUpdatePageDraftsQuery(Page $page, $userId)
{ {
return $this->pageRevision->where('created_by', '=', $userId) return $this->entityProvider->pageRevision->where('created_by', '=', $userId)
->where('type', 'update_draft') ->where('type', 'update_draft')
->where('page_id', '=', $page->id) ->where('page_id', '=', $page->id)
->orderBy('created_at', 'desc'); ->orderBy('created_at', 'desc');
@ -1084,7 +1068,7 @@ class EntityRepo
*/ */
protected function activePageEditingQuery(Page $page, $minRange = null) protected function activePageEditingQuery(Page $page, $minRange = null)
{ {
$query = $this->pageRevision->where('type', '=', 'update_draft') $query = $this->entityProvider->pageRevision->where('type', '=', 'update_draft')
->where('page_id', '=', $page->id) ->where('page_id', '=', $page->id)
->where('updated_at', '>', $page->updated_at) ->where('updated_at', '>', $page->updated_at)
->where('created_by', '!=', user()->id) ->where('created_by', '!=', user()->id)
@ -1144,7 +1128,7 @@ class EntityRepo
if ($drafts->count() > 0) { if ($drafts->count() > 0) {
$draft = $drafts->first(); $draft = $drafts->first();
} else { } else {
$draft = $this->pageRevision->newInstance(); $draft = $this->entityProvider->pageRevision->newInstance();
$draft->page_id = $page->id; $draft->page_id = $page->id;
$draft->slug = $page->slug; $draft->slug = $page->slug;
$draft->book_slug = $page->book->slug; $draft->book_slug = $page->book->slug;

View File

@ -2,24 +2,33 @@
use BookStack\Auth\Permissions\PermissionService; use BookStack\Auth\Permissions\PermissionService;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
class SearchService class SearchService
{ {
/**
* @var SearchTerm
*/
protected $searchTerm; protected $searchTerm;
protected $bookshelf;
protected $book;
protected $chapter;
protected $page;
protected $db;
protected $permissionService;
/** /**
* @var Entity[] * @var EntityProvider
*/ */
protected $entities; protected $entityProvider;
/**
* @var Connection
*/
protected $db;
/**
* @var PermissionService
*/
protected $permissionService;
/** /**
* Acceptable operators to be used in a query * Acceptable operators to be used in a query
@ -30,27 +39,15 @@ class SearchService
/** /**
* SearchService constructor. * SearchService constructor.
* @param SearchTerm $searchTerm * @param SearchTerm $searchTerm
* @param Bookshelf $bookshelf * @param EntityProvider $entityProvider
* @param \BookStack\Entities\Book $book
* @param \BookStack\Entities\Chapter $chapter
* @param Page $page
* @param Connection $db * @param Connection $db
* @param PermissionService $permissionService * @param PermissionService $permissionService
*/ */
public function __construct(SearchTerm $searchTerm, Bookshelf $bookshelf, Book $book, Chapter $chapter, Page $page, Connection $db, PermissionService $permissionService) public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
{ {
$this->searchTerm = $searchTerm; $this->searchTerm = $searchTerm;
$this->bookshelf = $bookshelf; $this->entityProvider = $entityProvider;
$this->book = $book;
$this->chapter = $chapter;
$this->page = $page;
$this->db = $db; $this->db = $db;
$this->entities = [
'bookshelf' => $this->bookshelf,
'page' => $this->page,
'chapter' => $this->chapter,
'book' => $this->book
];
$this->permissionService = $permissionService; $this->permissionService = $permissionService;
} }
@ -75,7 +72,7 @@ class SearchService
public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view') public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view')
{ {
$terms = $this->parseSearchString($searchString); $terms = $this->parseSearchString($searchString);
$entityTypes = array_keys($this->entities); $entityTypes = array_keys($this->entityProvider->all());
$entityTypesToSearch = $entityTypes; $entityTypesToSearch = $entityTypes;
if ($entityType !== 'all') { if ($entityType !== 'all') {
@ -172,17 +169,17 @@ class SearchService
* @param array $terms * @param array $terms
* @param string $entityType * @param string $entityType
* @param string $action * @param string $action
* @return \Illuminate\Database\Eloquent\Builder * @return EloquentBuilder
*/ */
protected function buildEntitySearchQuery($terms, $entityType = 'page', $action = 'view') protected function buildEntitySearchQuery($terms, $entityType = 'page', $action = 'view')
{ {
$entity = $this->getEntity($entityType); $entity = $this->entityProvider->get($entityType);
$entitySelect = $entity->newQuery(); $entitySelect = $entity->newQuery();
// Handle normal search terms // Handle normal search terms
if (count($terms['search']) > 0) { if (count($terms['search']) > 0) {
$subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score')); $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
$subQuery->where('entity_type', '=', 'BookStack\\' . ucfirst($entityType)); $subQuery->where('entity_type', '=', $entity->getMorphClass());
$subQuery->where(function (Builder $query) use ($terms) { $subQuery->where(function (Builder $query) use ($terms) {
foreach ($terms['search'] as $inputTerm) { foreach ($terms['search'] as $inputTerm) {
$query->orWhere('term', 'like', $inputTerm .'%'); $query->orWhere('term', 'like', $inputTerm .'%');
@ -196,9 +193,9 @@ class SearchService
// Handle exact term matching // Handle exact term matching
if (count($terms['exact']) > 0) { if (count($terms['exact']) > 0) {
$entitySelect->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($terms, $entity) { $entitySelect->where(function (EloquentBuilder $query) use ($terms, $entity) {
foreach ($terms['exact'] as $inputTerm) { foreach ($terms['exact'] as $inputTerm) {
$query->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($inputTerm, $entity) { $query->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
$query->where('name', 'like', '%'.$inputTerm .'%') $query->where('name', 'like', '%'.$inputTerm .'%')
->orWhere($entity->textField, 'like', '%'.$inputTerm .'%'); ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
}); });
@ -286,14 +283,14 @@ class SearchService
/** /**
* Apply a tag search term onto a entity query. * Apply a tag search term onto a entity query.
* @param \Illuminate\Database\Eloquent\Builder $query * @param EloquentBuilder $query
* @param string $tagTerm * @param string $tagTerm
* @return mixed * @return mixed
*/ */
protected function applyTagSearch(\Illuminate\Database\Eloquent\Builder $query, $tagTerm) protected function applyTagSearch(EloquentBuilder $query, $tagTerm)
{ {
preg_match("/^(.*?)((".$this->getRegexEscapedOperators().")(.*?))?$/", $tagTerm, $tagSplit); preg_match("/^(.*?)((".$this->getRegexEscapedOperators().")(.*?))?$/", $tagTerm, $tagSplit);
$query->whereHas('tags', function (\Illuminate\Database\Eloquent\Builder $query) use ($tagSplit) { $query->whereHas('tags', function (EloquentBuilder $query) use ($tagSplit) {
$tagName = $tagSplit[1]; $tagName = $tagSplit[1];
$tagOperator = count($tagSplit) > 2 ? $tagSplit[3] : ''; $tagOperator = count($tagSplit) > 2 ? $tagSplit[3] : '';
$tagValue = count($tagSplit) > 3 ? $tagSplit[4] : ''; $tagValue = count($tagSplit) > 3 ? $tagSplit[4] : '';
@ -318,16 +315,6 @@ class SearchService
return $query; return $query;
} }
/**
* Get an entity instance via type.
* @param $type
* @return Entity
*/
protected function getEntity($type)
{
return $this->entities[strtolower($type)];
}
/** /**
* Index the given entity. * Index the given entity.
* @param Entity $entity * @param Entity $entity
@ -375,7 +362,7 @@ class SearchService
{ {
$this->searchTerm->truncate(); $this->searchTerm->truncate();
foreach ($this->entities as $entityModel) { foreach ($this->entityProvider->all() as $entityModel) {
$selectFields = ['id', 'name', $entityModel->textField]; $selectFields = ['id', 'name', $entityModel->textField];
$entityModel->newQuery()->select($selectFields)->chunk(1000, function ($entities) { $entityModel->newQuery()->select($selectFields)->chunk(1000, function ($entities) {
$this->indexEntities($entities); $this->indexEntities($entities);
@ -429,7 +416,7 @@ class SearchService
* Custom entity search filters * Custom entity search filters
*/ */
protected function filterUpdatedAfter(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterUpdatedAfter(EloquentBuilder $query, Entity $model, $input)
{ {
try { try {
$date = date_create($input); $date = date_create($input);
@ -439,7 +426,7 @@ class SearchService
$query->where('updated_at', '>=', $date); $query->where('updated_at', '>=', $date);
} }
protected function filterUpdatedBefore(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterUpdatedBefore(EloquentBuilder $query, Entity $model, $input)
{ {
try { try {
$date = date_create($input); $date = date_create($input);
@ -449,7 +436,7 @@ class SearchService
$query->where('updated_at', '<', $date); $query->where('updated_at', '<', $date);
} }
protected function filterCreatedAfter(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterCreatedAfter(EloquentBuilder $query, Entity $model, $input)
{ {
try { try {
$date = date_create($input); $date = date_create($input);
@ -459,7 +446,7 @@ class SearchService
$query->where('created_at', '>=', $date); $query->where('created_at', '>=', $date);
} }
protected function filterCreatedBefore(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, $input)
{ {
try { try {
$date = date_create($input); $date = date_create($input);
@ -469,7 +456,7 @@ class SearchService
$query->where('created_at', '<', $date); $query->where('created_at', '<', $date);
} }
protected function filterCreatedBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterCreatedBy(EloquentBuilder $query, Entity $model, $input)
{ {
if (!is_numeric($input) && $input !== 'me') { if (!is_numeric($input) && $input !== 'me') {
return; return;
@ -480,7 +467,7 @@ class SearchService
$query->where('created_by', '=', $input); $query->where('created_by', '=', $input);
} }
protected function filterUpdatedBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, $input)
{ {
if (!is_numeric($input) && $input !== 'me') { if (!is_numeric($input) && $input !== 'me') {
return; return;
@ -491,41 +478,41 @@ class SearchService
$query->where('updated_by', '=', $input); $query->where('updated_by', '=', $input);
} }
protected function filterInName(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterInName(EloquentBuilder $query, Entity $model, $input)
{ {
$query->where('name', 'like', '%' .$input. '%'); $query->where('name', 'like', '%' .$input. '%');
} }
protected function filterInTitle(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterInTitle(EloquentBuilder $query, Entity $model, $input)
{ {
$this->filterInName($query, $model, $input); $this->filterInName($query, $model, $input);
} }
protected function filterInBody(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterInBody(EloquentBuilder $query, Entity $model, $input)
{ {
$query->where($model->textField, 'like', '%' .$input. '%'); $query->where($model->textField, 'like', '%' .$input. '%');
} }
protected function filterIsRestricted(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterIsRestricted(EloquentBuilder $query, Entity $model, $input)
{ {
$query->where('restricted', '=', true); $query->where('restricted', '=', true);
} }
protected function filterViewedByMe(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterViewedByMe(EloquentBuilder $query, Entity $model, $input)
{ {
$query->whereHas('views', function ($query) { $query->whereHas('views', function ($query) {
$query->where('user_id', '=', user()->id); $query->where('user_id', '=', user()->id);
}); });
} }
protected function filterNotViewedByMe(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterNotViewedByMe(EloquentBuilder $query, Entity $model, $input)
{ {
$query->whereDoesntHave('views', function ($query) { $query->whereDoesntHave('views', function ($query) {
$query->where('user_id', '=', user()->id); $query->where('user_id', '=', user()->id);
}); });
} }
protected function filterSortBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) protected function filterSortBy(EloquentBuilder $query, Entity $model, $input)
{ {
$functionName = camel_case('sort_by_' . $input); $functionName = camel_case('sort_by_' . $input);
if (method_exists($this, $functionName)) { if (method_exists($this, $functionName)) {
@ -538,7 +525,7 @@ class SearchService
* Sorting filter options * Sorting filter options
*/ */
protected function sortByLastCommented(\Illuminate\Database\Eloquent\Builder $query, Entity $model) protected function sortByLastCommented(EloquentBuilder $query, Entity $model)
{ {
$commentsTable = $this->db->getTablePrefix() . 'comments'; $commentsTable = $this->db->getTablePrefix() . 'comments';
$morphClass = str_replace('\\', '\\\\', $model->getMorphClass()); $morphClass = str_replace('\\', '\\\\', $model->getMorphClass());

View File

@ -204,7 +204,7 @@ class BookController extends Controller
// Get the books involved in the sort // Get the books involved in the sort
$bookIdsInvolved = $bookIdsInvolved->unique()->toArray(); $bookIdsInvolved = $bookIdsInvolved->unique()->toArray();
$booksInvolved = $this->entityRepo->book->newQuery()->whereIn('id', $bookIdsInvolved)->get(); $booksInvolved = $this->entityRepo->getManyById('book', $bookIdsInvolved, false, true);
// Throw permission error if invalid ids or inaccessible books given. // Throw permission error if invalid ids or inaccessible books given.
if (count($bookIdsInvolved) !== count($booksInvolved)) { if (count($bookIdsInvolved) !== count($booksInvolved)) {
$this->showPermissionError(); $this->showPermissionError();

View File

@ -21,7 +21,7 @@ class Attachment extends Ownable
/** /**
* Get the page this file was uploaded to. * Get the page this file was uploaded to.
* @return Page * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/ */
public function page() public function page()
{ {

View File

@ -16,7 +16,7 @@ class ImageRepo
* ImageRepo constructor. * ImageRepo constructor.
* @param Image $image * @param Image $image
* @param ImageService $imageService * @param ImageService $imageService
* @param \BookStack\Auth\\BookStack\Auth\Permissions\PermissionService $permissionService * @param \BookStack\Auth\Permissions\PermissionService $permissionService
* @param \BookStack\Entities\Page $page * @param \BookStack\Entities\Page $page
*/ */
public function __construct(Image $image, ImageService $imageService, PermissionService $permissionService, Page $page) public function __construct(Image $image, ImageService $imageService, PermissionService $permissionService, Page $page)