Dan Brown

Rewrote book children query

...@@ -22,6 +22,7 @@ class BookController extends Controller ...@@ -22,6 +22,7 @@ class BookController extends Controller
22 22
23 /** 23 /**
24 * BookController constructor. 24 * BookController constructor.
25 + * @param EntityRepo $entityRepo
25 * @param BookRepo $bookRepo 26 * @param BookRepo $bookRepo
26 * @param PageRepo $pageRepo 27 * @param PageRepo $pageRepo
27 * @param ChapterRepo $chapterRepo 28 * @param ChapterRepo $chapterRepo
......
...@@ -93,47 +93,32 @@ class BookRepo extends EntityRepo ...@@ -93,47 +93,32 @@ class BookRepo extends EntityRepo
93 */ 93 */
94 public function getChildren(Book $book, $filterDrafts = false) 94 public function getChildren(Book $book, $filterDrafts = false)
95 { 95 {
96 - $pageQuery = $book->pages()->where('chapter_id', '=', 0); 96 + $q = $this->permissionService->bookChildrenQuery($book->id, $filterDrafts);
97 - $pageQuery = $this->permissionService->enforcePageRestrictions($pageQuery, 'view'); 97 + $entities = [];
98 + $parents = [];
99 + $tree = [];
98 100
99 - if ($filterDrafts) { 101 + foreach ($q as $index => $rawEntity) {
100 - $pageQuery = $pageQuery->where('draft', '=', false); 102 + if ($rawEntity->entity_type === 'Bookstack\\Page') {
103 + $entities[$index] = $this->page->newFromBuilder($rawEntity);
104 + } else if ($rawEntity->entity_type === 'Bookstack\\Chapter') {
105 + $entities[$index] = $this->chapter->newFromBuilder($rawEntity);
106 + $key = $entities[$index]->entity_type . ':' . $entities[$index]->id;
107 + $parents[$key] = $entities[$index];
108 + $parents[$key]->setAttribute('pages', collect());
101 } 109 }
102 - 110 + if ($entities[$index]->chapter_id === 0) $tree[] = $entities[$index];
103 - $pages = $pageQuery->get(); 111 + $entities[$index]->book = $book;
104 -
105 - $chapterQuery = $book->chapters()->with(['pages' => function ($query) use ($filterDrafts) {
106 - $this->permissionService->enforcePageRestrictions($query, 'view');
107 - if ($filterDrafts) $query->where('draft', '=', false);
108 - }]);
109 - $chapterQuery = $this->permissionService->enforceChapterRestrictions($chapterQuery, 'view');
110 - $chapters = $chapterQuery->get();
111 - $children = $pages->values();
112 - foreach ($chapters as $chapter) {
113 - $children->push($chapter);
114 } 112 }
115 - $bookSlug = $book->slug;
116 113
117 - $children->each(function ($child) use ($bookSlug) { 114 + foreach ($entities as $entity) {
118 - $child->setAttribute('bookSlug', $bookSlug); 115 + if ($entity->chapter_id === 0) continue;
119 - if ($child->isA('chapter')) { 116 + $parentKey = 'Bookstack\\Chapter:' . $entity->chapter_id;
120 - $child->pages->each(function ($page) use ($bookSlug) { 117 + $chapter = $parents[$parentKey];
121 - $page->setAttribute('bookSlug', $bookSlug); 118 + $chapter->pages->push($entity);
122 - });
123 - $child->pages = $child->pages->sortBy(function ($child, $key) {
124 - $score = $child->priority;
125 - if ($child->draft) $score -= 100;
126 - return $score;
127 - });
128 } 119 }
129 - });
130 120
131 - // Sort items with drafts first then by priority. 121 + return collect($tree);
132 - return $children->sortBy(function ($child, $key) {
133 - $score = $child->priority;
134 - if ($child->isA('page') && $child->draft) $score -= 100;
135 - return $score;
136 - });
137 } 122 }
138 123
139 } 124 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -7,7 +7,6 @@ use BookStack\Exceptions\NotFoundException; ...@@ -7,7 +7,6 @@ use BookStack\Exceptions\NotFoundException;
7 use BookStack\Page; 7 use BookStack\Page;
8 use BookStack\Services\PermissionService; 8 use BookStack\Services\PermissionService;
9 use BookStack\Services\ViewService; 9 use BookStack\Services\ViewService;
10 -use Illuminate\Database\Eloquent\Builder;
11 use Illuminate\Support\Collection; 10 use Illuminate\Support\Collection;
12 11
13 class EntityRepo 12 class EntityRepo
...@@ -127,6 +126,7 @@ class EntityRepo ...@@ -127,6 +126,7 @@ class EntityRepo
127 public function getBySlug($type, $slug, $bookSlug = false) 126 public function getBySlug($type, $slug, $bookSlug = false)
128 { 127 {
129 $q = $this->entityQuery($type)->where('slug', '=', $slug); 128 $q = $this->entityQuery($type)->where('slug', '=', $slug);
129 +
130 if (strtolower($type) === 'chapter' || strtolower($type) === 'page') { 130 if (strtolower($type) === 'chapter' || strtolower($type) === 'page') {
131 $q = $q->where('book_id', '=', function($query) use ($bookSlug) { 131 $q = $q->where('book_id', '=', function($query) use ($bookSlug) {
132 $query->select('id') 132 $query->select('id')
......
...@@ -114,7 +114,7 @@ class ActivityService ...@@ -114,7 +114,7 @@ class ActivityService
114 114
115 $activity = $this->permissionService 115 $activity = $this->permissionService
116 ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type') 116 ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
117 - ->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get(); 117 + ->orderBy('created_at', 'desc')->with(['entity', 'user.avatar'])->skip($count * $page)->take($count)->get();
118 118
119 return $this->filterSimilar($activity); 119 return $this->filterSimilar($activity);
120 } 120 }
......
...@@ -8,6 +8,7 @@ use BookStack\Ownable; ...@@ -8,6 +8,7 @@ use BookStack\Ownable;
8 use BookStack\Page; 8 use BookStack\Page;
9 use BookStack\Role; 9 use BookStack\Role;
10 use BookStack\User; 10 use BookStack\User;
11 +use Illuminate\Database\Connection;
11 use Illuminate\Database\Eloquent\Builder; 12 use Illuminate\Database\Eloquent\Builder;
12 use Illuminate\Support\Collection; 13 use Illuminate\Support\Collection;
13 14
...@@ -23,6 +24,8 @@ class PermissionService ...@@ -23,6 +24,8 @@ class PermissionService
23 public $chapter; 24 public $chapter;
24 public $page; 25 public $page;
25 26
27 + protected $db;
28 +
26 protected $jointPermission; 29 protected $jointPermission;
27 protected $role; 30 protected $role;
28 31
...@@ -31,18 +34,21 @@ class PermissionService ...@@ -31,18 +34,21 @@ class PermissionService
31 /** 34 /**
32 * PermissionService constructor. 35 * PermissionService constructor.
33 * @param JointPermission $jointPermission 36 * @param JointPermission $jointPermission
37 + * @param Connection $db
34 * @param Book $book 38 * @param Book $book
35 * @param Chapter $chapter 39 * @param Chapter $chapter
36 * @param Page $page 40 * @param Page $page
37 * @param Role $role 41 * @param Role $role
38 */ 42 */
39 - public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role) 43 + public function __construct(JointPermission $jointPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role)
40 { 44 {
45 + $this->db = $db;
41 $this->jointPermission = $jointPermission; 46 $this->jointPermission = $jointPermission;
42 $this->role = $role; 47 $this->role = $role;
43 $this->book = $book; 48 $this->book = $book;
44 $this->chapter = $chapter; 49 $this->chapter = $chapter;
45 $this->page = $page; 50 $this->page = $page;
51 + // TODO - Update so admin still goes through filters
46 } 52 }
47 53
48 /** 54 /**
...@@ -302,6 +308,10 @@ class PermissionService ...@@ -302,6 +308,10 @@ class PermissionService
302 $explodedAction = explode('-', $action); 308 $explodedAction = explode('-', $action);
303 $restrictionAction = end($explodedAction); 309 $restrictionAction = end($explodedAction);
304 310
311 + if ($role->system_name === 'admin') {
312 + return $this->createJointPermissionDataArray($entity, $role, $action, true, true);
313 + }
314 +
305 if ($entity->isA('book')) { 315 if ($entity->isA('book')) {
306 316
307 if (!$entity->restricted) { 317 if (!$entity->restricted) {
...@@ -461,6 +471,51 @@ class PermissionService ...@@ -461,6 +471,51 @@ class PermissionService
461 return $q; 471 return $q;
462 } 472 }
463 473
474 + public function bookChildrenQuery($book_id, $filterDrafts = false) {
475 +
476 + // Draft setup
477 + $params = [
478 + 'userId' => $this->currentUser()->id,
479 + 'bookIdPage' => $book_id,
480 + 'bookIdChapter' => $book_id
481 + ];
482 + if (!$filterDrafts) {
483 + $params['userIdDrafts'] = $this->currentUser()->id;
484 + }
485 + // Role setup
486 + $userRoles = $this->getRoles();
487 + $roleBindings = [];
488 + $roleValues = [];
489 + foreach ($userRoles as $index => $roleId) {
490 + $roleBindings[':role'.$index] = $roleId;
491 + $roleValues['role'.$index] = $roleId;
492 + }
493 + // TODO - Clean this up, Maybe extract into a nice class for doing these kind of manual things
494 + // Something which will handle the above role crap in a nice clean way
495 + $roleBindingString = implode(',', array_keys($roleBindings));
496 + $query = "SELECT * from (
497 +(SELECT 'Bookstack\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft FROM {$this->page->getTable()}
498 + where book_id = :bookIdPage AND ". ($filterDrafts ? '(draft = 0)' : '(draft = 0 OR (draft = 1 AND created_by = :userIdDrafts))') .")
499 +UNION
500 +(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)
501 +) as U WHERE (
502 + SELECT COUNT(*) FROM {$this->jointPermission->getTable()} jp
503 + WHERE
504 + jp.entity_id=U.id AND
505 + jp.entity_type=U.entity_type AND
506 + jp.action = 'view' AND
507 + jp.role_id IN ({$roleBindingString}) AND
508 + (
509 + jp.has_permission = 1 OR
510 + (jp.has_permission_own = 1 AND jp.created_by = :userId)
511 + )
512 +) > 0
513 +ORDER BY draft desc, priority asc";
514 +
515 + $this->clean();
516 + return $this->db->select($query, array_replace($roleValues, $params));
517 + }
518 +
464 /** 519 /**
465 * Add restrictions for a page query 520 * Add restrictions for a page query
466 * @param $query 521 * @param $query
...@@ -608,7 +663,7 @@ class PermissionService ...@@ -608,7 +663,7 @@ class PermissionService
608 private function isAdmin() 663 private function isAdmin()
609 { 664 {
610 if ($this->isAdminUser === null) { 665 if ($this->isAdminUser === null) {
611 - $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false; 666 + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasSystemRole('admin') : false;
612 } 667 }
613 668
614 return $this->isAdminUser; 669 return $this->isAdminUser;
......
...@@ -37,7 +37,7 @@ class ViewService ...@@ -37,7 +37,7 @@ class ViewService
37 37
38 // Otherwise create new view count 38 // Otherwise create new view count
39 $entity->views()->save($this->view->create([ 39 $entity->views()->save($this->view->create([
40 - 'user_id' => user()->id, 40 + 'user_id' => $user->id,
41 'views' => 1 41 'views' => 1
42 ])); 42 ]));
43 43
......
...@@ -75,6 +75,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -75,6 +75,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
75 } 75 }
76 76
77 /** 77 /**
78 + * Check if the user has a role.
79 + * @param $role
80 + * @return mixed
81 + */
82 + public function hasSystemRole($role)
83 + {
84 + return $this->roles->pluck('system_name')->contains('admin');
85 + }
86 +
87 + /**
78 * Get all permissions belonging to a the current user. 88 * Get all permissions belonging to a the current user.
79 * @param bool $cache 89 * @param bool $cache
80 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough 90 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
......