X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/3acea12f1c0013be4f1e3994cae2ea662e43bb4e..refs/pull/1688/head:/app/Entities/Repos/EntityRepo.php diff --git a/app/Entities/Repos/EntityRepo.php b/app/Entities/Repos/EntityRepo.php index 7ca25b785..0dd0fbb0a 100644 --- a/app/Entities/Repos/EntityRepo.php +++ b/app/Entities/Repos/EntityRepo.php @@ -6,6 +6,7 @@ use BookStack\Actions\ViewService; use BookStack\Auth\Permissions\PermissionService; use BookStack\Auth\User; use BookStack\Entities\Book; +use BookStack\Entities\BookChild; use BookStack\Entities\Bookshelf; use BookStack\Entities\Chapter; use BookStack\Entities\Entity; @@ -16,10 +17,10 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotifyException; use BookStack\Uploads\AttachmentService; use DOMDocument; -use DOMNode; use DOMXPath; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Throwable; @@ -79,7 +80,7 @@ class EntityRepo * @param string $type * @param bool $allowDrafts * @param string $permission - * @return \Illuminate\Database\Query\Builder + * @return QueryBuilder */ protected function entityQuery($type, $allowDrafts = false, $permission = 'view') { @@ -142,25 +143,29 @@ class EntityRepo * Get an entity by its url slug. * @param string $type * @param string $slug - * @param string|bool $bookSlug + * @param string|null $bookSlug * @return Entity * @throws NotFoundException */ - public function getBySlug($type, $slug, $bookSlug = false) + public function getEntityBySlug(string $type, string $slug, string $bookSlug = null): Entity { - $q = $this->entityQuery($type)->where('slug', '=', $slug); + $type = strtolower($type); + $query = $this->entityQuery($type)->where('slug', '=', $slug); - if (strtolower($type) === 'chapter' || strtolower($type) === 'page') { - $q = $q->where('book_id', '=', function ($query) use ($bookSlug) { + if ($type === 'chapter' || $type === 'page') { + $query = $query->where('book_id', '=', function (QueryBuilder $query) use ($bookSlug) { $query->select('id') ->from($this->entityProvider->book->getTable()) ->where('slug', '=', $bookSlug)->limit(1); }); } - $entity = $q->first(); + + $entity = $query->first(); + if ($entity === null) { - throw new NotFoundException(trans('errors.' . strtolower($type) . '_not_found')); + throw new NotFoundException(trans('errors.' . $type . '_not_found')); } + return $entity; } @@ -408,6 +413,17 @@ class EntityRepo return collect($tree); } + + /** + * Get the bookshelves that a book is contained in. + * @param Book $book + * @return \Illuminate\Database\Eloquent\Collection|static[] + */ + public function getBookParentShelves(Book $book) + { + return $this->permissionService->enforceEntityRestrictions('shelf', $book->shelves())->get(); + } + /** * Get the child items for a chapter sorted by priority but * with draft items floated to the top. @@ -460,25 +476,6 @@ class EntityRepo return $slug; } - /** - * Check if a slug already exists in the database. - * @param string $type - * @param string $slug - * @param bool|integer $currentId - * @param bool|integer $bookId - * @return bool - */ - protected function slugExists($type, $slug, $currentId = false, $bookId = false) - { - $query = $this->entityProvider->get($type)->where('slug', '=', $slug); - if (strtolower($type) === 'page' || strtolower($type) === 'chapter') { - $query = $query->where('book_id', '=', $bookId); - } - if ($currentId) { - $query = $query->where('id', '!=', $currentId); - } - return $query->count() > 0; - } /** * Updates entity restrictions from a request @@ -492,70 +489,73 @@ class EntityRepo $entity->permissions()->delete(); if ($request->filled('restrictions')) { - foreach ($request->get('restrictions') as $roleId => $restrictions) { - foreach ($restrictions as $action => $value) { - $entity->permissions()->create([ + $entityPermissionData = collect($request->get('restrictions'))->flatMap(function($restrictions, $roleId) { + return collect($restrictions)->keys()->map(function($action) use ($roleId) { + return [ 'role_id' => $roleId, - 'action' => strtolower($action) - ]); - } - } + 'action' => strtolower($action), + ] ; + }); + }); + + $entity->permissions()->createMany($entityPermissionData); } $entity->save(); - $this->permissionService->buildJointPermissionsForEntity($entity); + $entity->rebuildPermissions(); } - /** * Create a new entity from request input. * Used for books and chapters. * @param string $type * @param array $input - * @param bool|Book $book + * @param Book|null $book * @return Entity */ - public function createFromInput($type, $input = [], $book = false) + public function createFromInput(string $type, array $input = [], Book $book = null) { - $isChapter = strtolower($type) === 'chapter'; $entityModel = $this->entityProvider->get($type)->newInstance($input); - $entityModel->slug = $this->findSuitableSlug($type, $entityModel->name, false, $isChapter ? $book->id : false); $entityModel->created_by = user()->id; $entityModel->updated_by = user()->id; - $isChapter ? $book->chapters()->save($entityModel) : $entityModel->save(); + + if ($book) { + $entityModel->book_id = $book->id; + } + + $entityModel->refreshSlug(); + $entityModel->save(); if (isset($input['tags'])) { $this->tagRepo->saveTagsToEntity($entityModel, $input['tags']); } - $this->permissionService->buildJointPermissionsForEntity($entityModel); + $entityModel->rebuildPermissions(); $this->searchService->indexEntity($entityModel); return $entityModel; } /** * Update entity details from request input. - * Used for books and chapters - * @param string $type - * @param Entity $entityModel - * @param array $input - * @return Entity + * Used for shelves, books and chapters. */ - public function updateFromInput($type, Entity $entityModel, $input = []) + public function updateFromInput(Entity $entityModel, array $input): Entity { - if ($entityModel->name !== $input['name']) { - $entityModel->slug = $this->findSuitableSlug($type, $input['name'], $entityModel->id); - } $entityModel->fill($input); $entityModel->updated_by = user()->id; + + if ($entityModel->isDirty('name')) { + $entityModel->refreshSlug(); + } + $entityModel->save(); if (isset($input['tags'])) { $this->tagRepo->saveTagsToEntity($entityModel, $input['tags']); } - $this->permissionService->buildJointPermissionsForEntity($entityModel); + $entityModel->rebuildPermissions(); $this->searchService->indexEntity($entityModel); return $entityModel; } @@ -582,79 +582,26 @@ class EntityRepo $shelf->books()->sync($syncData); } - /** - * Append a Book to a BookShelf. - * @param Bookshelf $shelf - * @param Book $book - */ - public function appendBookToShelf(Bookshelf $shelf, Book $book) - { - if ($shelf->contains($book)) { - return; - } - - $maxOrder = $shelf->books()->max('order'); - $shelf->books()->attach($book->id, ['order' => $maxOrder + 1]); - } - /** * Change the book that an entity belongs to. - * @param string $type - * @param integer $newBookId - * @param Entity $entity - * @param bool $rebuildPermissions - * @return Entity */ - public function changeBook($type, $newBookId, Entity $entity, $rebuildPermissions = false) + public function changeBook(BookChild $bookChild, int $newBookId): Entity { - $entity->book_id = $newBookId; + $bookChild->book_id = $newBookId; + $bookChild->refreshSlug(); + $bookChild->save(); + // Update related activity - foreach ($entity->activity as $activity) { - $activity->book_id = $newBookId; - $activity->save(); - } - $entity->slug = $this->findSuitableSlug($type, $entity->name, $entity->id, $newBookId); - $entity->save(); + $bookChild->activity()->update(['book_id' => $newBookId]); // Update all child pages if a chapter - if (strtolower($type) === 'chapter') { - foreach ($entity->pages as $page) { - $this->changeBook('page', $newBookId, $page, false); + if ($bookChild->isA('chapter')) { + foreach ($bookChild->pages as $page) { + $this->changeBook($page, $newBookId); } } - // Update permissions if applicable - if ($rebuildPermissions) { - $entity->load('book'); - $this->permissionService->buildJointPermissionsForEntity($entity->book); - } - - return $entity; - } - - /** - * Alias method to update the book jointPermissions in the PermissionService. - * @param Book $book - */ - public function buildJointPermissionsForBook(Book $book) - { - $this->permissionService->buildJointPermissionsForEntity($book); - } - - /** - * Format a name as a url slug. - * @param $name - * @return string - */ - protected function nameToSlug($name) - { - $slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', mb_strtolower($name)); - $slug = preg_replace('/\s{2,}/', ' ', $slug); - $slug = str_replace(' ', '-', $slug); - if ($slug === "") { - $slug = substr(md5(rand(1, 500)), 0, 5); - } - return $slug; + return $bookChild; } /** @@ -766,7 +713,7 @@ class EntityRepo } // Remove data or JavaScript iFrames - $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')]'); + $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]'); foreach ($badIframes as $badIframe) { $badIframe->parentNode->removeChild($badIframe); } @@ -815,24 +762,6 @@ class EntityRepo $shelf->delete(); } - /** - * Destroy the provided book and all its child entities. - * @param Book $book - * @throws NotifyException - * @throws Throwable - */ - public function destroyBook(Book $book) - { - foreach ($book->pages as $page) { - $this->destroyPage($page); - } - foreach ($book->chapters as $chapter) { - $this->destroyChapter($chapter); - } - $this->destroyEntityCommonRelations($book); - $book->delete(); - } - /** * Destroy a chapter and its relations. * @param Chapter $chapter @@ -907,6 +836,7 @@ class EntityRepo $shelfBooks = $bookshelf->books()->get(); $updatedBookCount = 0; + /** @var Book $book */ foreach ($shelfBooks as $book) { if (!userCan('restrictions-manage', $book)) { continue; @@ -915,7 +845,7 @@ class EntityRepo $book->restricted = $bookshelf->restricted; $book->permissions()->createMany($shelfPermissions); $book->save(); - $this->permissionService->buildJointPermissionsForEntity($book); + $book->rebuildPermissions(); $updatedBookCount++; }