Rewrote book children query

This commit is contained in:
Dan Brown 2017-01-01 21:21:11 +00:00
parent bab27462ab
commit 65796cfc7b
No known key found for this signature in database
GPG key ID: 46D9F943C24A2EF9
7 changed files with 94 additions and 43 deletions

View file

@ -22,6 +22,7 @@ class BookController extends Controller
/**
* BookController constructor.
* @param EntityRepo $entityRepo
* @param BookRepo $bookRepo
* @param PageRepo $pageRepo
* @param ChapterRepo $chapterRepo

View file

@ -93,47 +93,32 @@ class BookRepo extends EntityRepo
*/
public function getChildren(Book $book, $filterDrafts = false)
{
$pageQuery = $book->pages()->where('chapter_id', '=', 0);
$pageQuery = $this->permissionService->enforcePageRestrictions($pageQuery, 'view');
$q = $this->permissionService->bookChildrenQuery($book->id, $filterDrafts);
$entities = [];
$parents = [];
$tree = [];
if ($filterDrafts) {
$pageQuery = $pageQuery->where('draft', '=', false);
}
$pages = $pageQuery->get();
$chapterQuery = $book->chapters()->with(['pages' => function ($query) use ($filterDrafts) {
$this->permissionService->enforcePageRestrictions($query, 'view');
if ($filterDrafts) $query->where('draft', '=', false);
}]);
$chapterQuery = $this->permissionService->enforceChapterRestrictions($chapterQuery, 'view');
$chapters = $chapterQuery->get();
$children = $pages->values();
foreach ($chapters as $chapter) {
$children->push($chapter);
}
$bookSlug = $book->slug;
$children->each(function ($child) use ($bookSlug) {
$child->setAttribute('bookSlug', $bookSlug);
if ($child->isA('chapter')) {
$child->pages->each(function ($page) use ($bookSlug) {
$page->setAttribute('bookSlug', $bookSlug);
});
$child->pages = $child->pages->sortBy(function ($child, $key) {
$score = $child->priority;
if ($child->draft) $score -= 100;
return $score;
});
foreach ($q as $index => $rawEntity) {
if ($rawEntity->entity_type === 'Bookstack\\Page') {
$entities[$index] = $this->page->newFromBuilder($rawEntity);
} else if ($rawEntity->entity_type === 'Bookstack\\Chapter') {
$entities[$index] = $this->chapter->newFromBuilder($rawEntity);
$key = $entities[$index]->entity_type . ':' . $entities[$index]->id;
$parents[$key] = $entities[$index];
$parents[$key]->setAttribute('pages', collect());
}
});
if ($entities[$index]->chapter_id === 0) $tree[] = $entities[$index];
$entities[$index]->book = $book;
}
// Sort items with drafts first then by priority.
return $children->sortBy(function ($child, $key) {
$score = $child->priority;
if ($child->isA('page') && $child->draft) $score -= 100;
return $score;
});
foreach ($entities as $entity) {
if ($entity->chapter_id === 0) continue;
$parentKey = 'Bookstack\\Chapter:' . $entity->chapter_id;
$chapter = $parents[$parentKey];
$chapter->pages->push($entity);
}
return collect($tree);
}
}

View file

@ -7,7 +7,6 @@ use BookStack\Exceptions\NotFoundException;
use BookStack\Page;
use BookStack\Services\PermissionService;
use BookStack\Services\ViewService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class EntityRepo
@ -127,6 +126,7 @@ class EntityRepo
public function getBySlug($type, $slug, $bookSlug = false)
{
$q = $this->entityQuery($type)->where('slug', '=', $slug);
if (strtolower($type) === 'chapter' || strtolower($type) === 'page') {
$q = $q->where('book_id', '=', function($query) use ($bookSlug) {
$query->select('id')

View file

@ -114,7 +114,7 @@ class ActivityService
$activity = $this->permissionService
->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get();
->orderBy('created_at', 'desc')->with(['entity', 'user.avatar'])->skip($count * $page)->take($count)->get();
return $this->filterSimilar($activity);
}

View file

@ -8,6 +8,7 @@ use BookStack\Ownable;
use BookStack\Page;
use BookStack\Role;
use BookStack\User;
use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
@ -23,6 +24,8 @@ class PermissionService
public $chapter;
public $page;
protected $db;
protected $jointPermission;
protected $role;
@ -31,18 +34,21 @@ class PermissionService
/**
* PermissionService constructor.
* @param JointPermission $jointPermission
* @param Connection $db
* @param Book $book
* @param Chapter $chapter
* @param Page $page
* @param Role $role
*/
public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role)
public function __construct(JointPermission $jointPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role)
{
$this->db = $db;
$this->jointPermission = $jointPermission;
$this->role = $role;
$this->book = $book;
$this->chapter = $chapter;
$this->page = $page;
// TODO - Update so admin still goes through filters
}
/**
@ -302,6 +308,10 @@ class PermissionService
$explodedAction = explode('-', $action);
$restrictionAction = end($explodedAction);
if ($role->system_name === 'admin') {
return $this->createJointPermissionDataArray($entity, $role, $action, true, true);
}
if ($entity->isA('book')) {
if (!$entity->restricted) {
@ -461,6 +471,51 @@ class PermissionService
return $q;
}
public function bookChildrenQuery($book_id, $filterDrafts = false) {
// Draft setup
$params = [
'userId' => $this->currentUser()->id,
'bookIdPage' => $book_id,
'bookIdChapter' => $book_id
];
if (!$filterDrafts) {
$params['userIdDrafts'] = $this->currentUser()->id;
}
// Role setup
$userRoles = $this->getRoles();
$roleBindings = [];
$roleValues = [];
foreach ($userRoles as $index => $roleId) {
$roleBindings[':role'.$index] = $roleId;
$roleValues['role'.$index] = $roleId;
}
// TODO - Clean this up, Maybe extract into a nice class for doing these kind of manual things
// Something which will handle the above role crap in a nice clean way
$roleBindingString = implode(',', array_keys($roleBindings));
$query = "SELECT * from (
(SELECT 'Bookstack\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft FROM {$this->page->getTable()}
where book_id = :bookIdPage AND ". ($filterDrafts ? '(draft = 0)' : '(draft = 0 OR (draft = 1 AND created_by = :userIdDrafts))') .")
UNION
(SELECT 'Bookstack\\\Chapter' as entity_type, id, slug, name, '' as text, description, book_id, priority, 0 as chapter_id, 0 as draft FROM {$this->chapter->getTable()} WHERE book_id = :bookIdChapter)
) as U WHERE (
SELECT COUNT(*) FROM {$this->jointPermission->getTable()} jp
WHERE
jp.entity_id=U.id AND
jp.entity_type=U.entity_type AND
jp.action = 'view' AND
jp.role_id IN ({$roleBindingString}) AND
(
jp.has_permission = 1 OR
(jp.has_permission_own = 1 AND jp.created_by = :userId)
)
) > 0
ORDER BY draft desc, priority asc";
$this->clean();
return $this->db->select($query, array_replace($roleValues, $params));
}
/**
* Add restrictions for a page query
* @param $query
@ -608,7 +663,7 @@ class PermissionService
private function isAdmin()
{
if ($this->isAdminUser === null) {
$this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false;
$this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasSystemRole('admin') : false;
}
return $this->isAdminUser;

View file

@ -37,7 +37,7 @@ class ViewService
// Otherwise create new view count
$entity->views()->save($this->view->create([
'user_id' => user()->id,
'user_id' => $user->id,
'views' => 1
]));

View file

@ -74,6 +74,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->roles->pluck('name')->contains($role);
}
/**
* Check if the user has a role.
* @param $role
* @return mixed
*/
public function hasSystemRole($role)
{
return $this->roles->pluck('system_name')->contains('admin');
}
/**
* Get all permissions belonging to a the current user.
* @param bool $cache