Showing
20 changed files
with
264 additions
and
156 deletions
| ... | @@ -49,6 +49,7 @@ class RegeneratePermissions extends Command | ... | @@ -49,6 +49,7 @@ class RegeneratePermissions extends Command |
| 49 | $connection = \DB::getDefaultConnection(); | 49 | $connection = \DB::getDefaultConnection(); |
| 50 | if ($this->option('database') !== null) { | 50 | if ($this->option('database') !== null) { |
| 51 | \DB::setDefaultConnection($this->option('database')); | 51 | \DB::setDefaultConnection($this->option('database')); |
| 52 | + $this->permissionService->setConnection(\DB::connection($this->option('database'))); | ||
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | $this->permissionService->buildJointPermissions(); | 55 | $this->permissionService->buildJointPermissions(); | ... | ... |
| ... | @@ -44,6 +44,7 @@ class RegenerateSearch extends Command | ... | @@ -44,6 +44,7 @@ class RegenerateSearch extends Command |
| 44 | $connection = \DB::getDefaultConnection(); | 44 | $connection = \DB::getDefaultConnection(); |
| 45 | if ($this->option('database') !== null) { | 45 | if ($this->option('database') !== null) { |
| 46 | \DB::setDefaultConnection($this->option('database')); | 46 | \DB::setDefaultConnection($this->option('database')); |
| 47 | + $this->searchService->setConnection(\DB::connection($this->option('database'))); | ||
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | $this->searchService->indexAllEntities(); | 50 | $this->searchService->indexAllEntities(); | ... | ... |
| ... | @@ -95,17 +95,6 @@ class Entity extends Ownable | ... | @@ -95,17 +95,6 @@ class Entity extends Ownable |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | /** | 97 | /** |
| 98 | - * Check if this entity has live (active) restrictions in place. | ||
| 99 | - * @param $role_id | ||
| 100 | - * @param $action | ||
| 101 | - * @return bool | ||
| 102 | - */ | ||
| 103 | - public function hasActiveRestriction($role_id, $action) | ||
| 104 | - { | ||
| 105 | - return $this->getRawAttribute('restricted') && $this->hasRestriction($role_id, $action); | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - /** | ||
| 109 | * Get the entity jointPermissions this is connected to. | 98 | * Get the entity jointPermissions this is connected to. |
| 110 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany | 99 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany |
| 111 | */ | 100 | */ |
| ... | @@ -176,5 +165,11 @@ class Entity extends Ownable | ... | @@ -176,5 +165,11 @@ class Entity extends Ownable |
| 176 | */ | 165 | */ |
| 177 | public function entityRawQuery(){return '';} | 166 | public function entityRawQuery(){return '';} |
| 178 | 167 | ||
| 168 | + /** | ||
| 169 | + * Get the url of this entity | ||
| 170 | + * @param $path | ||
| 171 | + * @return string | ||
| 172 | + */ | ||
| 173 | + public function getUrl($path){return '/';} | ||
| 179 | 174 | ||
| 180 | } | 175 | } | ... | ... |
| 1 | <?php namespace BookStack\Http\Controllers; | 1 | <?php namespace BookStack\Http\Controllers; |
| 2 | 2 | ||
| 3 | use Activity; | 3 | use Activity; |
| 4 | +use BookStack\Book; | ||
| 4 | use BookStack\Repos\EntityRepo; | 5 | use BookStack\Repos\EntityRepo; |
| 5 | use BookStack\Repos\UserRepo; | 6 | use BookStack\Repos\UserRepo; |
| 6 | use BookStack\Services\ExportService; | 7 | use BookStack\Services\ExportService; |
| ... | @@ -207,13 +208,12 @@ class BookController extends Controller | ... | @@ -207,13 +208,12 @@ class BookController extends Controller |
| 207 | 208 | ||
| 208 | // Add activity for books | 209 | // Add activity for books |
| 209 | foreach ($sortedBooks as $bookId) { | 210 | foreach ($sortedBooks as $bookId) { |
| 211 | + /** @var Book $updatedBook */ | ||
| 210 | $updatedBook = $this->entityRepo->getById('book', $bookId); | 212 | $updatedBook = $this->entityRepo->getById('book', $bookId); |
| 213 | + $this->entityRepo->buildJointPermissionsForBook($updatedBook); | ||
| 211 | Activity::add($updatedBook, 'book_sort', $updatedBook->id); | 214 | Activity::add($updatedBook, 'book_sort', $updatedBook->id); |
| 212 | } | 215 | } |
| 213 | 216 | ||
| 214 | - // Update permissions on changed models | ||
| 215 | - if (count($updatedModels) === 0) $this->entityRepo->buildJointPermissions($updatedModels); | ||
| 216 | - | ||
| 217 | return redirect($book->getUrl()); | 217 | return redirect($book->getUrl()); |
| 218 | } | 218 | } |
| 219 | 219 | ... | ... |
| ... | @@ -46,7 +46,7 @@ class HomeController extends Controller | ... | @@ -46,7 +46,7 @@ class HomeController extends Controller |
| 46 | * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response | 46 | * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response |
| 47 | */ | 47 | */ |
| 48 | public function getTranslations() { | 48 | public function getTranslations() { |
| 49 | - $locale = trans()->getLocale(); | 49 | + $locale = app()->getLocale(); |
| 50 | $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale; | 50 | $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale; |
| 51 | if (cache()->has($cacheKey) && config('app.env') !== 'development') { | 51 | if (cache()->has($cacheKey) && config('app.env') !== 'development') { |
| 52 | $resp = cache($cacheKey); | 52 | $resp = cache($cacheKey); | ... | ... |
| ... | @@ -15,7 +15,17 @@ class Localization | ... | @@ -15,7 +15,17 @@ class Localization |
| 15 | public function handle($request, Closure $next) | 15 | public function handle($request, Closure $next) |
| 16 | { | 16 | { |
| 17 | $defaultLang = config('app.locale'); | 17 | $defaultLang = config('app.locale'); |
| 18 | - $locale = setting()->getUser(user(), 'language', $defaultLang); | 18 | + if (user()->isDefault()) { |
| 19 | + $locale = $defaultLang; | ||
| 20 | + $availableLocales = config('app.locales'); | ||
| 21 | + foreach ($request->getLanguages() as $lang) { | ||
| 22 | + if (!in_array($lang, $availableLocales)) continue; | ||
| 23 | + $locale = $lang; | ||
| 24 | + break; | ||
| 25 | + } | ||
| 26 | + } else { | ||
| 27 | + $locale = setting()->getUser(user(), 'language', $defaultLang); | ||
| 28 | + } | ||
| 19 | app()->setLocale($locale); | 29 | app()->setLocale($locale); |
| 20 | Carbon::setLocale($locale); | 30 | Carbon::setLocale($locale); |
| 21 | return $next($request); | 31 | return $next($request); | ... | ... |
| ... | @@ -533,11 +533,11 @@ class EntityRepo | ... | @@ -533,11 +533,11 @@ class EntityRepo |
| 533 | 533 | ||
| 534 | /** | 534 | /** |
| 535 | * Alias method to update the book jointPermissions in the PermissionService. | 535 | * Alias method to update the book jointPermissions in the PermissionService. |
| 536 | - * @param Collection $collection collection on entities | 536 | + * @param Book $book |
| 537 | */ | 537 | */ |
| 538 | - public function buildJointPermissions(Collection $collection) | 538 | + public function buildJointPermissionsForBook(Book $book) |
| 539 | { | 539 | { |
| 540 | - $this->permissionService->buildJointPermissionsForEntities($collection); | 540 | + $this->permissionService->buildJointPermissionsForEntity($book); |
| 541 | } | 541 | } |
| 542 | 542 | ||
| 543 | /** | 543 | /** |
| ... | @@ -730,6 +730,7 @@ class EntityRepo | ... | @@ -730,6 +730,7 @@ class EntityRepo |
| 730 | if ($chapter) $page->chapter_id = $chapter->id; | 730 | if ($chapter) $page->chapter_id = $chapter->id; |
| 731 | 731 | ||
| 732 | $book->pages()->save($page); | 732 | $book->pages()->save($page); |
| 733 | + $page = $this->page->find($page->id); | ||
| 733 | $this->permissionService->buildJointPermissionsForEntity($page); | 734 | $this->permissionService->buildJointPermissionsForEntity($page); |
| 734 | return $page; | 735 | return $page; |
| 735 | } | 736 | } | ... | ... |
| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | use BookStack\Book; | 3 | use BookStack\Book; |
| 4 | use BookStack\Chapter; | 4 | use BookStack\Chapter; |
| 5 | use BookStack\Entity; | 5 | use BookStack\Entity; |
| 6 | +use BookStack\EntityPermission; | ||
| 6 | use BookStack\JointPermission; | 7 | use BookStack\JointPermission; |
| 7 | use BookStack\Ownable; | 8 | use BookStack\Ownable; |
| 8 | use BookStack\Page; | 9 | use BookStack\Page; |
| ... | @@ -10,6 +11,7 @@ use BookStack\Role; | ... | @@ -10,6 +11,7 @@ use BookStack\Role; |
| 10 | use BookStack\User; | 11 | use BookStack\User; |
| 11 | use Illuminate\Database\Connection; | 12 | use Illuminate\Database\Connection; |
| 12 | use Illuminate\Database\Eloquent\Builder; | 13 | use Illuminate\Database\Eloquent\Builder; |
| 14 | +use Illuminate\Database\Query\Builder as QueryBuilder; | ||
| 13 | use Illuminate\Support\Collection; | 15 | use Illuminate\Support\Collection; |
| 14 | 16 | ||
| 15 | class PermissionService | 17 | class PermissionService |
| ... | @@ -28,22 +30,25 @@ class PermissionService | ... | @@ -28,22 +30,25 @@ class PermissionService |
| 28 | 30 | ||
| 29 | protected $jointPermission; | 31 | protected $jointPermission; |
| 30 | protected $role; | 32 | protected $role; |
| 33 | + protected $entityPermission; | ||
| 31 | 34 | ||
| 32 | protected $entityCache; | 35 | protected $entityCache; |
| 33 | 36 | ||
| 34 | /** | 37 | /** |
| 35 | * PermissionService constructor. | 38 | * PermissionService constructor. |
| 36 | * @param JointPermission $jointPermission | 39 | * @param JointPermission $jointPermission |
| 40 | + * @param EntityPermission $entityPermission | ||
| 37 | * @param Connection $db | 41 | * @param Connection $db |
| 38 | * @param Book $book | 42 | * @param Book $book |
| 39 | * @param Chapter $chapter | 43 | * @param Chapter $chapter |
| 40 | * @param Page $page | 44 | * @param Page $page |
| 41 | * @param Role $role | 45 | * @param Role $role |
| 42 | */ | 46 | */ |
| 43 | - public function __construct(JointPermission $jointPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role) | 47 | + public function __construct(JointPermission $jointPermission, EntityPermission $entityPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role) |
| 44 | { | 48 | { |
| 45 | $this->db = $db; | 49 | $this->db = $db; |
| 46 | $this->jointPermission = $jointPermission; | 50 | $this->jointPermission = $jointPermission; |
| 51 | + $this->entityPermission = $entityPermission; | ||
| 47 | $this->role = $role; | 52 | $this->role = $role; |
| 48 | $this->book = $book; | 53 | $this->book = $book; |
| 49 | $this->chapter = $chapter; | 54 | $this->chapter = $chapter; |
| ... | @@ -52,6 +57,15 @@ class PermissionService | ... | @@ -52,6 +57,15 @@ class PermissionService |
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | /** | 59 | /** |
| 60 | + * Set the database connection | ||
| 61 | + * @param Connection $connection | ||
| 62 | + */ | ||
| 63 | + public function setConnection(Connection $connection) | ||
| 64 | + { | ||
| 65 | + $this->db = $connection; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + /** | ||
| 55 | * Prepare the local entity cache and ensure it's empty | 69 | * Prepare the local entity cache and ensure it's empty |
| 56 | */ | 70 | */ |
| 57 | protected function readyEntityCache() | 71 | protected function readyEntityCache() |
| ... | @@ -133,22 +147,48 @@ class PermissionService | ... | @@ -133,22 +147,48 @@ class PermissionService |
| 133 | $this->readyEntityCache(); | 147 | $this->readyEntityCache(); |
| 134 | 148 | ||
| 135 | // Get all roles (Should be the most limited dimension) | 149 | // Get all roles (Should be the most limited dimension) |
| 136 | - $roles = $this->role->with('permissions')->get(); | 150 | + $roles = $this->role->with('permissions')->get()->all(); |
| 137 | 151 | ||
| 138 | // Chunk through all books | 152 | // Chunk through all books |
| 139 | - $this->book->with('permissions')->chunk(500, function ($books) use ($roles) { | 153 | + $this->bookFetchQuery()->chunk(5, function ($books) use ($roles) { |
| 140 | - $this->createManyJointPermissions($books, $roles); | 154 | + $this->buildJointPermissionsForBooks($books, $roles); |
| 141 | }); | 155 | }); |
| 156 | + } | ||
| 142 | 157 | ||
| 143 | - // Chunk through all chapters | 158 | + /** |
| 144 | - $this->chapter->with('book', 'permissions')->chunk(500, function ($chapters) use ($roles) { | 159 | + * Get a query for fetching a book with it's children. |
| 145 | - $this->createManyJointPermissions($chapters, $roles); | 160 | + * @return QueryBuilder |
| 146 | - }); | 161 | + */ |
| 162 | + protected function bookFetchQuery() | ||
| 163 | + { | ||
| 164 | + return $this->book->newQuery()->select(['id', 'restricted', 'created_by'])->with(['chapters' => function($query) { | ||
| 165 | + $query->select(['id', 'restricted', 'created_by', 'book_id']); | ||
| 166 | + }, 'pages' => function($query) { | ||
| 167 | + $query->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']); | ||
| 168 | + }]); | ||
| 169 | + } | ||
| 147 | 170 | ||
| 148 | - // Chunk through all pages | 171 | + /** |
| 149 | - $this->page->with('book', 'chapter', 'permissions')->chunk(500, function ($pages) use ($roles) { | 172 | + * Build joint permissions for an array of books |
| 150 | - $this->createManyJointPermissions($pages, $roles); | 173 | + * @param Collection $books |
| 151 | - }); | 174 | + * @param array $roles |
| 175 | + * @param bool $deleteOld | ||
| 176 | + */ | ||
| 177 | + protected function buildJointPermissionsForBooks($books, $roles, $deleteOld = false) { | ||
| 178 | + $entities = clone $books; | ||
| 179 | + | ||
| 180 | + /** @var Book $book */ | ||
| 181 | + foreach ($books->all() as $book) { | ||
| 182 | + foreach ($book->getRelation('chapters') as $chapter) { | ||
| 183 | + $entities->push($chapter); | ||
| 184 | + } | ||
| 185 | + foreach ($book->getRelation('pages') as $page) { | ||
| 186 | + $entities->push($page); | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + if ($deleteOld) $this->deleteManyJointPermissionsForEntities($entities->all()); | ||
| 191 | + $this->createManyJointPermissions($entities, $roles); | ||
| 152 | } | 192 | } |
| 153 | 193 | ||
| 154 | /** | 194 | /** |
| ... | @@ -157,18 +197,22 @@ class PermissionService | ... | @@ -157,18 +197,22 @@ class PermissionService |
| 157 | */ | 197 | */ |
| 158 | public function buildJointPermissionsForEntity(Entity $entity) | 198 | public function buildJointPermissionsForEntity(Entity $entity) |
| 159 | { | 199 | { |
| 160 | - $roles = $this->role->get(); | 200 | + $entities = [$entity]; |
| 161 | - $entities = collect([$entity]); | ||
| 162 | - | ||
| 163 | if ($entity->isA('book')) { | 201 | if ($entity->isA('book')) { |
| 164 | - $entities = $entities->merge($entity->chapters); | 202 | + $books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get(); |
| 165 | - $entities = $entities->merge($entity->pages); | 203 | + $this->buildJointPermissionsForBooks($books, $this->role->newQuery()->get(), true); |
| 166 | - } elseif ($entity->isA('chapter')) { | 204 | + return; |
| 167 | - $entities = $entities->merge($entity->pages); | ||
| 168 | } | 205 | } |
| 169 | 206 | ||
| 207 | + $entities[] = $entity->book; | ||
| 208 | + if ($entity->isA('page') && $entity->chapter_id) $entities[] = $entity->chapter; | ||
| 209 | + if ($entity->isA('chapter')) { | ||
| 210 | + foreach ($entity->pages as $page) { | ||
| 211 | + $entities[] = $page; | ||
| 212 | + } | ||
| 213 | + } | ||
| 170 | $this->deleteManyJointPermissionsForEntities($entities); | 214 | $this->deleteManyJointPermissionsForEntities($entities); |
| 171 | - $this->createManyJointPermissions($entities, $roles); | 215 | + $this->buildJointPermissionsForEntities(collect($entities)); |
| 172 | } | 216 | } |
| 173 | 217 | ||
| 174 | /** | 218 | /** |
| ... | @@ -177,8 +221,8 @@ class PermissionService | ... | @@ -177,8 +221,8 @@ class PermissionService |
| 177 | */ | 221 | */ |
| 178 | public function buildJointPermissionsForEntities(Collection $entities) | 222 | public function buildJointPermissionsForEntities(Collection $entities) |
| 179 | { | 223 | { |
| 180 | - $roles = $this->role->get(); | 224 | + $roles = $this->role->newQuery()->get(); |
| 181 | - $this->deleteManyJointPermissionsForEntities($entities); | 225 | + $this->deleteManyJointPermissionsForEntities($entities->all()); |
| 182 | $this->createManyJointPermissions($entities, $roles); | 226 | $this->createManyJointPermissions($entities, $roles); |
| 183 | } | 227 | } |
| 184 | 228 | ||
| ... | @@ -188,23 +232,12 @@ class PermissionService | ... | @@ -188,23 +232,12 @@ class PermissionService |
| 188 | */ | 232 | */ |
| 189 | public function buildJointPermissionForRole(Role $role) | 233 | public function buildJointPermissionForRole(Role $role) |
| 190 | { | 234 | { |
| 191 | - $roles = collect([$role]); | 235 | + $roles = [$role]; |
| 192 | - | ||
| 193 | $this->deleteManyJointPermissionsForRoles($roles); | 236 | $this->deleteManyJointPermissionsForRoles($roles); |
| 194 | 237 | ||
| 195 | // Chunk through all books | 238 | // Chunk through all books |
| 196 | - $this->book->with('permissions')->chunk(500, function ($books) use ($roles) { | 239 | + $this->bookFetchQuery()->chunk(5, function ($books) use ($roles) { |
| 197 | - $this->createManyJointPermissions($books, $roles); | 240 | + $this->buildJointPermissionsForBooks($books, $roles); |
| 198 | - }); | ||
| 199 | - | ||
| 200 | - // Chunk through all chapters | ||
| 201 | - $this->chapter->with('book', 'permissions')->chunk(500, function ($books) use ($roles) { | ||
| 202 | - $this->createManyJointPermissions($books, $roles); | ||
| 203 | - }); | ||
| 204 | - | ||
| 205 | - // Chunk through all pages | ||
| 206 | - $this->page->with('book', 'chapter', 'permissions')->chunk(500, function ($books) use ($roles) { | ||
| 207 | - $this->createManyJointPermissions($books, $roles); | ||
| 208 | }); | 241 | }); |
| 209 | } | 242 | } |
| 210 | 243 | ||
| ... | @@ -223,9 +256,10 @@ class PermissionService | ... | @@ -223,9 +256,10 @@ class PermissionService |
| 223 | */ | 256 | */ |
| 224 | protected function deleteManyJointPermissionsForRoles($roles) | 257 | protected function deleteManyJointPermissionsForRoles($roles) |
| 225 | { | 258 | { |
| 226 | - foreach ($roles as $role) { | 259 | + $roleIds = array_map(function($role) { |
| 227 | - $role->jointPermissions()->delete(); | 260 | + return $role->id; |
| 228 | - } | 261 | + }, $roles); |
| 262 | + $this->jointPermission->newQuery()->whereIn('id', $roleIds)->delete(); | ||
| 229 | } | 263 | } |
| 230 | 264 | ||
| 231 | /** | 265 | /** |
| ... | @@ -244,53 +278,88 @@ class PermissionService | ... | @@ -244,53 +278,88 @@ class PermissionService |
| 244 | protected function deleteManyJointPermissionsForEntities($entities) | 278 | protected function deleteManyJointPermissionsForEntities($entities) |
| 245 | { | 279 | { |
| 246 | if (count($entities) === 0) return; | 280 | if (count($entities) === 0) return; |
| 247 | - $query = $this->jointPermission->newQuery(); | 281 | + |
| 248 | - foreach ($entities as $entity) { | 282 | + $this->db->transaction(function() use ($entities) { |
| 249 | - $query->orWhere(function($query) use ($entity) { | 283 | + |
| 250 | - $query->where('entity_id', '=', $entity->id) | 284 | + foreach (array_chunk($entities, 1000) as $entityChunk) { |
| 251 | - ->where('entity_type', '=', $entity->getMorphClass()); | 285 | + $query = $this->db->table('joint_permissions'); |
| 252 | - }); | 286 | + foreach ($entityChunk as $entity) { |
| 287 | + $query->orWhere(function(QueryBuilder $query) use ($entity) { | ||
| 288 | + $query->where('entity_id', '=', $entity->id) | ||
| 289 | + ->where('entity_type', '=', $entity->getMorphClass()); | ||
| 290 | + }); | ||
| 291 | + } | ||
| 292 | + $query->delete(); | ||
| 253 | } | 293 | } |
| 254 | - $query->delete(); | 294 | + |
| 295 | + }); | ||
| 255 | } | 296 | } |
| 256 | 297 | ||
| 257 | /** | 298 | /** |
| 258 | * Create & Save entity jointPermissions for many entities and jointPermissions. | 299 | * Create & Save entity jointPermissions for many entities and jointPermissions. |
| 259 | * @param Collection $entities | 300 | * @param Collection $entities |
| 260 | - * @param Collection $roles | 301 | + * @param array $roles |
| 261 | */ | 302 | */ |
| 262 | protected function createManyJointPermissions($entities, $roles) | 303 | protected function createManyJointPermissions($entities, $roles) |
| 263 | { | 304 | { |
| 264 | $this->readyEntityCache(); | 305 | $this->readyEntityCache(); |
| 265 | $jointPermissions = []; | 306 | $jointPermissions = []; |
| 307 | + | ||
| 308 | + // Fetch Entity Permissions and create a mapping of entity restricted statuses | ||
| 309 | + $entityRestrictedMap = []; | ||
| 310 | + $permissionFetch = $this->entityPermission->newQuery(); | ||
| 311 | + foreach ($entities as $entity) { | ||
| 312 | + $entityRestrictedMap[$entity->getMorphClass() . ':' . $entity->id] = boolval($entity->getRawAttribute('restricted')); | ||
| 313 | + $permissionFetch->orWhere(function($query) use ($entity) { | ||
| 314 | + $query->where('restrictable_id', '=', $entity->id)->where('restrictable_type', '=', $entity->getMorphClass()); | ||
| 315 | + }); | ||
| 316 | + } | ||
| 317 | + $permissions = $permissionFetch->get(); | ||
| 318 | + | ||
| 319 | + // Create a mapping of explicit entity permissions | ||
| 320 | + $permissionMap = []; | ||
| 321 | + foreach ($permissions as $permission) { | ||
| 322 | + $key = $permission->restrictable_type . ':' . $permission->restrictable_id . ':' . $permission->role_id . ':' . $permission->action; | ||
| 323 | + $isRestricted = $entityRestrictedMap[$permission->restrictable_type . ':' . $permission->restrictable_id]; | ||
| 324 | + $permissionMap[$key] = $isRestricted; | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + // Create a mapping of role permissions | ||
| 328 | + $rolePermissionMap = []; | ||
| 329 | + foreach ($roles as $role) { | ||
| 330 | + foreach ($role->getRelationValue('permissions') as $permission) { | ||
| 331 | + $rolePermissionMap[$role->getRawAttribute('id') . ':' . $permission->getRawAttribute('name')] = true; | ||
| 332 | + } | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + // Create Joint Permission Data | ||
| 266 | foreach ($entities as $entity) { | 336 | foreach ($entities as $entity) { |
| 267 | foreach ($roles as $role) { | 337 | foreach ($roles as $role) { |
| 268 | foreach ($this->getActions($entity) as $action) { | 338 | foreach ($this->getActions($entity) as $action) { |
| 269 | - $jointPermissions[] = $this->createJointPermissionData($entity, $role, $action); | 339 | + $jointPermissions[] = $this->createJointPermissionData($entity, $role, $action, $permissionMap, $rolePermissionMap); |
| 270 | } | 340 | } |
| 271 | } | 341 | } |
| 272 | } | 342 | } |
| 273 | - $this->jointPermission->insert($jointPermissions); | 343 | + |
| 344 | + $this->db->transaction(function() use ($jointPermissions) { | ||
| 345 | + foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) { | ||
| 346 | + $this->db->table('joint_permissions')->insert($jointPermissionChunk); | ||
| 347 | + } | ||
| 348 | + }); | ||
| 274 | } | 349 | } |
| 275 | 350 | ||
| 276 | 351 | ||
| 277 | /** | 352 | /** |
| 278 | * Get the actions related to an entity. | 353 | * Get the actions related to an entity. |
| 279 | - * @param $entity | 354 | + * @param Entity $entity |
| 280 | * @return array | 355 | * @return array |
| 281 | */ | 356 | */ |
| 282 | - protected function getActions($entity) | 357 | + protected function getActions(Entity $entity) |
| 283 | { | 358 | { |
| 284 | $baseActions = ['view', 'update', 'delete']; | 359 | $baseActions = ['view', 'update', 'delete']; |
| 285 | - | 360 | + if ($entity->isA('chapter') || $entity->isA('book')) $baseActions[] = 'page-create'; |
| 286 | - if ($entity->isA('chapter')) { | 361 | + if ($entity->isA('book')) $baseActions[] = 'chapter-create'; |
| 287 | - $baseActions[] = 'page-create'; | 362 | + return $baseActions; |
| 288 | - } else if ($entity->isA('book')) { | ||
| 289 | - $baseActions[] = 'page-create'; | ||
| 290 | - $baseActions[] = 'chapter-create'; | ||
| 291 | - } | ||
| 292 | - | ||
| 293 | - return $baseActions; | ||
| 294 | } | 363 | } |
| 295 | 364 | ||
| 296 | /** | 365 | /** |
| ... | @@ -298,14 +367,16 @@ class PermissionService | ... | @@ -298,14 +367,16 @@ class PermissionService |
| 298 | * for a particular action. | 367 | * for a particular action. |
| 299 | * @param Entity $entity | 368 | * @param Entity $entity |
| 300 | * @param Role $role | 369 | * @param Role $role |
| 301 | - * @param $action | 370 | + * @param string $action |
| 371 | + * @param array $permissionMap | ||
| 372 | + * @param array $rolePermissionMap | ||
| 302 | * @return array | 373 | * @return array |
| 303 | */ | 374 | */ |
| 304 | - protected function createJointPermissionData(Entity $entity, Role $role, $action) | 375 | + protected function createJointPermissionData(Entity $entity, Role $role, $action, $permissionMap, $rolePermissionMap) |
| 305 | { | 376 | { |
| 306 | $permissionPrefix = (strpos($action, '-') === false ? ($entity->getType() . '-') : '') . $action; | 377 | $permissionPrefix = (strpos($action, '-') === false ? ($entity->getType() . '-') : '') . $action; |
| 307 | - $roleHasPermission = $role->hasPermission($permissionPrefix . '-all'); | 378 | + $roleHasPermission = isset($rolePermissionMap[$role->getRawAttribute('id') . ':' . $permissionPrefix . '-all']); |
| 308 | - $roleHasPermissionOwn = $role->hasPermission($permissionPrefix . '-own'); | 379 | + $roleHasPermissionOwn = isset($rolePermissionMap[$role->getRawAttribute('id') . ':' . $permissionPrefix . '-own']); |
| 309 | $explodedAction = explode('-', $action); | 380 | $explodedAction = explode('-', $action); |
| 310 | $restrictionAction = end($explodedAction); | 381 | $restrictionAction = end($explodedAction); |
| 311 | 382 | ||
| ... | @@ -313,54 +384,46 @@ class PermissionService | ... | @@ -313,54 +384,46 @@ class PermissionService |
| 313 | return $this->createJointPermissionDataArray($entity, $role, $action, true, true); | 384 | return $this->createJointPermissionDataArray($entity, $role, $action, true, true); |
| 314 | } | 385 | } |
| 315 | 386 | ||
| 316 | - if ($entity->isA('book')) { | 387 | + if ($entity->restricted) { |
| 317 | - | 388 | + $hasAccess = $this->mapHasActiveRestriction($permissionMap, $entity, $role, $restrictionAction); |
| 318 | - if (!$entity->restricted) { | 389 | + return $this->createJointPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess); |
| 319 | - return $this->createJointPermissionDataArray($entity, $role, $action, $roleHasPermission, $roleHasPermissionOwn); | 390 | + } |
| 320 | - } else { | ||
| 321 | - $hasAccess = $entity->hasActiveRestriction($role->id, $restrictionAction); | ||
| 322 | - return $this->createJointPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess); | ||
| 323 | - } | ||
| 324 | 391 | ||
| 325 | - } elseif ($entity->isA('chapter')) { | 392 | + if ($entity->isA('book')) { |
| 393 | + return $this->createJointPermissionDataArray($entity, $role, $action, $roleHasPermission, $roleHasPermissionOwn); | ||
| 394 | + } | ||
| 326 | 395 | ||
| 327 | - if (!$entity->restricted) { | 396 | + // For chapters and pages, Check if explicit permissions are set on the Book. |
| 328 | - $book = $this->getBook($entity->book_id); | 397 | + $book = $this->getBook($entity->book_id); |
| 329 | - $hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction); | 398 | + $hasExplicitAccessToParents = $this->mapHasActiveRestriction($permissionMap, $book, $role, $restrictionAction); |
| 330 | - $hasPermissiveAccessToBook = !$book->restricted; | 399 | + $hasPermissiveAccessToParents = !$book->restricted; |
| 331 | - return $this->createJointPermissionDataArray($entity, $role, $action, | 400 | + |
| 332 | - ($hasExplicitAccessToBook || ($roleHasPermission && $hasPermissiveAccessToBook)), | 401 | + // For pages with a chapter, Check if explicit permissions are set on the Chapter |
| 333 | - ($hasExplicitAccessToBook || ($roleHasPermissionOwn && $hasPermissiveAccessToBook))); | 402 | + if ($entity->isA('page') && $entity->chapter_id !== 0) { |
| 334 | - } else { | 403 | + $chapter = $this->getChapter($entity->chapter_id); |
| 335 | - $hasAccess = $entity->hasActiveRestriction($role->id, $restrictionAction); | 404 | + $hasPermissiveAccessToParents = $hasPermissiveAccessToParents && !$chapter->restricted; |
| 336 | - return $this->createJointPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess); | 405 | + if ($chapter->restricted) { |
| 406 | + $hasExplicitAccessToParents = $this->mapHasActiveRestriction($permissionMap, $chapter, $role, $restrictionAction); | ||
| 337 | } | 407 | } |
| 408 | + } | ||
| 338 | 409 | ||
| 339 | - } elseif ($entity->isA('page')) { | 410 | + return $this->createJointPermissionDataArray($entity, $role, $action, |
| 340 | - | 411 | + ($hasExplicitAccessToParents || ($roleHasPermission && $hasPermissiveAccessToParents)), |
| 341 | - if (!$entity->restricted) { | 412 | + ($hasExplicitAccessToParents || ($roleHasPermissionOwn && $hasPermissiveAccessToParents)) |
| 342 | - $book = $this->getBook($entity->book_id); | 413 | + ); |
| 343 | - $hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction); | 414 | + } |
| 344 | - $hasPermissiveAccessToBook = !$book->restricted; | ||
| 345 | - | ||
| 346 | - $chapter = $this->getChapter($entity->chapter_id); | ||
| 347 | - $hasExplicitAccessToChapter = $chapter && $chapter->hasActiveRestriction($role->id, $restrictionAction); | ||
| 348 | - $hasPermissiveAccessToChapter = $chapter && !$chapter->restricted; | ||
| 349 | - $acknowledgeChapter = ($chapter && $chapter->restricted); | ||
| 350 | - | ||
| 351 | - $hasExplicitAccessToParents = $acknowledgeChapter ? $hasExplicitAccessToChapter : $hasExplicitAccessToBook; | ||
| 352 | - $hasPermissiveAccessToParents = $acknowledgeChapter ? $hasPermissiveAccessToChapter : $hasPermissiveAccessToBook; | ||
| 353 | - | ||
| 354 | - return $this->createJointPermissionDataArray($entity, $role, $action, | ||
| 355 | - ($hasExplicitAccessToParents || ($roleHasPermission && $hasPermissiveAccessToParents)), | ||
| 356 | - ($hasExplicitAccessToParents || ($roleHasPermissionOwn && $hasPermissiveAccessToParents)) | ||
| 357 | - ); | ||
| 358 | - } else { | ||
| 359 | - $hasAccess = $entity->hasRestriction($role->id, $action); | ||
| 360 | - return $this->createJointPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess); | ||
| 361 | - } | ||
| 362 | 415 | ||
| 363 | - } | 416 | + /** |
| 417 | + * Check for an active restriction in an entity map. | ||
| 418 | + * @param $entityMap | ||
| 419 | + * @param Entity $entity | ||
| 420 | + * @param Role $role | ||
| 421 | + * @param $action | ||
| 422 | + * @return bool | ||
| 423 | + */ | ||
| 424 | + protected function mapHasActiveRestriction($entityMap, Entity $entity, Role $role, $action) { | ||
| 425 | + $key = $entity->getMorphClass() . ':' . $entity->getRawAttribute('id') . ':' . $role->getRawAttribute('id') . ':' . $action; | ||
| 426 | + return isset($entityMap[$key]) ? $entityMap[$key] : false; | ||
| 364 | } | 427 | } |
| 365 | 428 | ||
| 366 | /** | 429 | /** |
| ... | @@ -375,11 +438,10 @@ class PermissionService | ... | @@ -375,11 +438,10 @@ class PermissionService |
| 375 | */ | 438 | */ |
| 376 | protected function createJointPermissionDataArray(Entity $entity, Role $role, $action, $permissionAll, $permissionOwn) | 439 | protected function createJointPermissionDataArray(Entity $entity, Role $role, $action, $permissionAll, $permissionOwn) |
| 377 | { | 440 | { |
| 378 | - $entityClass = get_class($entity); | ||
| 379 | return [ | 441 | return [ |
| 380 | 'role_id' => $role->getRawAttribute('id'), | 442 | 'role_id' => $role->getRawAttribute('id'), |
| 381 | 'entity_id' => $entity->getRawAttribute('id'), | 443 | 'entity_id' => $entity->getRawAttribute('id'), |
| 382 | - 'entity_type' => $entityClass, | 444 | + 'entity_type' => $entity->getMorphClass(), |
| 383 | 'action' => $action, | 445 | 'action' => $action, |
| 384 | 'has_permission' => $permissionAll, | 446 | 'has_permission' => $permissionAll, |
| 385 | 'has_permission_own' => $permissionOwn, | 447 | 'has_permission_own' => $permissionOwn, |
| ... | @@ -476,7 +538,7 @@ class PermissionService | ... | @@ -476,7 +538,7 @@ class PermissionService |
| 476 | * @param integer $book_id | 538 | * @param integer $book_id |
| 477 | * @param bool $filterDrafts | 539 | * @param bool $filterDrafts |
| 478 | * @param bool $fetchPageContent | 540 | * @param bool $fetchPageContent |
| 479 | - * @return \Illuminate\Database\Query\Builder | 541 | + * @return QueryBuilder |
| 480 | */ | 542 | */ |
| 481 | public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) { | 543 | public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) { |
| 482 | $pageSelect = $this->db->table('pages')->selectRaw($this->page->entityRawQuery($fetchPageContent))->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) { | 544 | $pageSelect = $this->db->table('pages')->selectRaw($this->page->entityRawQuery($fetchPageContent))->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) { | ... | ... |
| ... | @@ -51,6 +51,15 @@ class SearchService | ... | @@ -51,6 +51,15 @@ class SearchService |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| 54 | + * Set the database connection | ||
| 55 | + * @param Connection $connection | ||
| 56 | + */ | ||
| 57 | + public function setConnection(Connection $connection) | ||
| 58 | + { | ||
| 59 | + $this->db = $connection; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + /** | ||
| 54 | * Search all entities in the system. | 63 | * Search all entities in the system. |
| 55 | * @param string $searchString | 64 | * @param string $searchString |
| 56 | * @param string $entityType | 65 | * @param string $entityType | ... | ... |
| ... | @@ -58,6 +58,7 @@ return [ | ... | @@ -58,6 +58,7 @@ return [ |
| 58 | */ | 58 | */ |
| 59 | 59 | ||
| 60 | 'locale' => env('APP_LANG', 'en'), | 60 | 'locale' => env('APP_LANG', 'en'), |
| 61 | + 'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk'], | ||
| 61 | 62 | ||
| 62 | /* | 63 | /* |
| 63 | |-------------------------------------------------------------------------- | 64 | |-------------------------------------------------------------------------- | ... | ... |
| ... | @@ -28,6 +28,12 @@ class DummyContentSeeder extends Seeder | ... | @@ -28,6 +28,12 @@ class DummyContentSeeder extends Seeder |
| 28 | $book->pages()->saveMany($pages); | 28 | $book->pages()->saveMany($pages); |
| 29 | }); | 29 | }); |
| 30 | 30 | ||
| 31 | + $largeBook = factory(\BookStack\Book::class)->create(['name' => 'Large book' . str_random(10), 'created_by' => $user->id, 'updated_by' => $user->id]); | ||
| 32 | + $pages = factory(\BookStack\Page::class, 200)->make(['created_by' => $user->id, 'updated_by' => $user->id]); | ||
| 33 | + $chapters = factory(\BookStack\Chapter::class, 50)->make(['created_by' => $user->id, 'updated_by' => $user->id]); | ||
| 34 | + $largeBook->pages()->saveMany($pages); | ||
| 35 | + $largeBook->chapters()->saveMany($chapters); | ||
| 36 | + | ||
| 31 | app(\BookStack\Services\PermissionService::class)->buildJointPermissions(); | 37 | app(\BookStack\Services\PermissionService::class)->buildJointPermissions(); |
| 32 | app(\BookStack\Services\SearchService::class)->indexAllEntities(); | 38 | app(\BookStack\Services\SearchService::class)->indexAllEntities(); |
| 33 | } | 39 | } | ... | ... |
| ... | @@ -43,6 +43,8 @@ Once done you can run `phpunit` in the application root directory to run all tes | ... | @@ -43,6 +43,8 @@ Once done you can run `phpunit` in the application root directory to run all tes |
| 43 | ## Translations | 43 | ## Translations |
| 44 | 44 | ||
| 45 | As part of BookStack v0.14 support for translations has been built in. All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`. | 45 | As part of BookStack v0.14 support for translations has been built in. All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`. |
| 46 | + | ||
| 47 | +You will also need to add the language to the `locales` array in the `config/app.php` file. | ||
| 46 | 48 | ||
| 47 | Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time. | 49 | Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time. |
| 48 | 50 | ... | ... |
| ... | @@ -215,7 +215,7 @@ module.exports = function (ngApp, events) { | ... | @@ -215,7 +215,7 @@ module.exports = function (ngApp, events) { |
| 215 | } | 215 | } |
| 216 | }]); | 216 | }]); |
| 217 | 217 | ||
| 218 | - const md = new MarkdownIt(); | 218 | + const md = new MarkdownIt({html: true}); |
| 219 | md.use(mdTasksLists, {label: true}); | 219 | md.use(mdTasksLists, {label: true}); |
| 220 | 220 | ||
| 221 | /** | 221 | /** | ... | ... |
| ... | @@ -166,7 +166,7 @@ return [ | ... | @@ -166,7 +166,7 @@ return [ |
| 166 | 'start_a' => ':count usuarios han comenzado a editar esta página', | 166 | 'start_a' => ':count usuarios han comenzado a editar esta página', |
| 167 | 'start_b' => ':userName ha comenzado a editar esta página', | 167 | 'start_b' => ':userName ha comenzado a editar esta página', |
| 168 | 'time_a' => 'desde que las página fue actualizada', | 168 | 'time_a' => 'desde que las página fue actualizada', |
| 169 | - 'time_b' => 'en los Ãltimos :minCount minutos', | 169 | + 'time_b' => 'en los últimos :minCount minutos', |
| 170 | 'message' => ':start :time. Ten cuidado de no sobreescribir los cambios del otro usuario', | 170 | 'message' => ':start :time. Ten cuidado de no sobreescribir los cambios del otro usuario', |
| 171 | ], | 171 | ], |
| 172 | 'pages_draft_discarded' => 'Borrador descartado, el editor ha sido actualizado con el contenido de la página actual', | 172 | 'pages_draft_discarded' => 'Borrador descartado, el editor ha sido actualizado con el contenido de la página actual', |
| ... | @@ -189,7 +189,7 @@ return [ | ... | @@ -189,7 +189,7 @@ return [ |
| 189 | 'attachments_set_link' => 'Setear Link', | 189 | 'attachments_set_link' => 'Setear Link', |
| 190 | 'attachments_delete_confirm' => 'Haga click en borrar nuevamente para confirmar que quiere borrar este adjunto.', | 190 | 'attachments_delete_confirm' => 'Haga click en borrar nuevamente para confirmar que quiere borrar este adjunto.', |
| 191 | 'attachments_dropzone' => 'Arrastre ficheros aquío haga click aquípara adjuntar un fichero', | 191 | 'attachments_dropzone' => 'Arrastre ficheros aquío haga click aquípara adjuntar un fichero', |
| 192 | - 'attachments_no_files' => 'NingÃn fichero ha sido adjuntado', | 192 | + 'attachments_no_files' => 'Ningún fichero ha sido adjuntado', |
| 193 | 'attachments_explain_link' => 'Ud. puede agregar un link o si lo prefiere puede agregar un fichero. Esto puede ser un link a otra página o un link a un fichero en la nube.', | 193 | 'attachments_explain_link' => 'Ud. puede agregar un link o si lo prefiere puede agregar un fichero. Esto puede ser un link a otra página o un link a un fichero en la nube.', |
| 194 | 'attachments_link_name' => 'Nombre de Link', | 194 | 'attachments_link_name' => 'Nombre de Link', |
| 195 | 'attachment_link' => 'Link adjunto', | 195 | 'attachment_link' => 'Link adjunto', | ... | ... |
| 1 | <?php namespace Tests; | 1 | <?php namespace Tests; |
| 2 | 2 | ||
| 3 | use BookStack\Role; | 3 | use BookStack\Role; |
| 4 | +use BookStack\Services\PermissionService; | ||
| 4 | use Illuminate\Contracts\Console\Kernel; | 5 | use Illuminate\Contracts\Console\Kernel; |
| 5 | use Illuminate\Foundation\Testing\DatabaseTransactions; | 6 | use Illuminate\Foundation\Testing\DatabaseTransactions; |
| 6 | use Laravel\BrowserKitTesting\TestCase; | 7 | use Laravel\BrowserKitTesting\TestCase; |
| ... | @@ -105,11 +106,9 @@ abstract class BrowserKitTest extends TestCase | ... | @@ -105,11 +106,9 @@ abstract class BrowserKitTest extends TestCase |
| 105 | { | 106 | { |
| 106 | if ($updaterUser === false) $updaterUser = $creatorUser; | 107 | if ($updaterUser === false) $updaterUser = $creatorUser; |
| 107 | $book = factory(\BookStack\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]); | 108 | $book = factory(\BookStack\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]); |
| 108 | - $chapter = factory(\BookStack\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]); | 109 | + $chapter = factory(\BookStack\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]); |
| 109 | - $page = factory(\BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]); | 110 | + $page = factory(\BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id, 'chapter_id' => $chapter->id]); |
| 110 | - $book->chapters()->saveMany([$chapter]); | 111 | + $restrictionService = $this->app[PermissionService::class]; |
| 111 | - $chapter->pages()->saveMany([$page]); | ||
| 112 | - $restrictionService = $this->app[\BookStack\Services\PermissionService::class]; | ||
| 113 | $restrictionService->buildJointPermissionsForEntity($book); | 112 | $restrictionService->buildJointPermissionsForEntity($book); |
| 114 | return [ | 113 | return [ |
| 115 | 'book' => $book, | 114 | 'book' => $book, | ... | ... |
| ... | @@ -42,7 +42,7 @@ class EntitySearchTest extends TestCase | ... | @@ -42,7 +42,7 @@ class EntitySearchTest extends TestCase |
| 42 | 42 | ||
| 43 | public function test_book_search() | 43 | public function test_book_search() |
| 44 | { | 44 | { |
| 45 | - $book = \BookStack\Book::all()->first(); | 45 | + $book = \BookStack\Book::first(); |
| 46 | $page = $book->pages->last(); | 46 | $page = $book->pages->last(); |
| 47 | $chapter = $book->chapters->last(); | 47 | $chapter = $book->chapters->last(); |
| 48 | 48 | ... | ... |
| ... | @@ -14,6 +14,23 @@ class LanguageTest extends TestCase | ... | @@ -14,6 +14,23 @@ class LanguageTest extends TestCase |
| 14 | $this->langs = array_diff(scandir(resource_path('lang')), ['..', '.']); | 14 | $this->langs = array_diff(scandir(resource_path('lang')), ['..', '.']); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | + public function test_locales_config_key_set_properly() | ||
| 18 | + { | ||
| 19 | + $configLocales = config('app.locales'); | ||
| 20 | + sort($configLocales); | ||
| 21 | + sort($this->langs); | ||
| 22 | + $this->assertTrue(implode(':', $this->langs) === implode(':', $configLocales), 'app.locales configuration variable matches found lang files'); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public function test_correct_language_if_not_logged_in() | ||
| 26 | + { | ||
| 27 | + $loginReq = $this->get('/login'); | ||
| 28 | + $loginReq->assertSee('Log In'); | ||
| 29 | + | ||
| 30 | + $loginPageFrenchReq = $this->get('/login', ['Accept-Language' => 'fr']); | ||
| 31 | + $loginPageFrenchReq->assertSee('Se Connecter'); | ||
| 32 | + } | ||
| 33 | + | ||
| 17 | public function test_js_endpoint_for_each_language() | 34 | public function test_js_endpoint_for_each_language() |
| 18 | { | 35 | { |
| 19 | 36 | ... | ... |
| ... | @@ -226,6 +226,7 @@ class RestrictionsTest extends BrowserKitTest | ... | @@ -226,6 +226,7 @@ class RestrictionsTest extends BrowserKitTest |
| 226 | ->type('test content', 'html') | 226 | ->type('test content', 'html') |
| 227 | ->press('Save Page') | 227 | ->press('Save Page') |
| 228 | ->seePageIs($chapter->book->getUrl() . '/page/test-page'); | 228 | ->seePageIs($chapter->book->getUrl() . '/page/test-page'); |
| 229 | + | ||
| 229 | $this->visit($chapterUrl)->seeInElement('.action-buttons', 'New Page'); | 230 | $this->visit($chapterUrl)->seeInElement('.action-buttons', 'New Page'); |
| 230 | } | 231 | } |
| 231 | 232 | ... | ... |
| 1 | <?php namespace Tests; | 1 | <?php namespace Tests; |
| 2 | 2 | ||
| 3 | +use BookStack\Repos\PermissionsRepo; | ||
| 4 | +use BookStack\Role; | ||
| 5 | + | ||
| 3 | class RolesTest extends BrowserKitTest | 6 | class RolesTest extends BrowserKitTest |
| 4 | { | 7 | { |
| 5 | protected $user; | 8 | protected $user; |
| ... | @@ -34,11 +37,11 @@ class RolesTest extends BrowserKitTest | ... | @@ -34,11 +37,11 @@ class RolesTest extends BrowserKitTest |
| 34 | /** | 37 | /** |
| 35 | * Create a new basic role for testing purposes. | 38 | * Create a new basic role for testing purposes. |
| 36 | * @param array $permissions | 39 | * @param array $permissions |
| 37 | - * @return static | 40 | + * @return Role |
| 38 | */ | 41 | */ |
| 39 | protected function createNewRole($permissions = []) | 42 | protected function createNewRole($permissions = []) |
| 40 | { | 43 | { |
| 41 | - $permissionRepo = app('BookStack\Repos\PermissionsRepo'); | 44 | + $permissionRepo = app(PermissionsRepo::class); |
| 42 | $roleData = factory(\BookStack\Role::class)->make()->toArray(); | 45 | $roleData = factory(\BookStack\Role::class)->make()->toArray(); |
| 43 | $roleData['permissions'] = array_flip($permissions); | 46 | $roleData['permissions'] = array_flip($permissions); |
| 44 | return $permissionRepo->saveNewRole($roleData); | 47 | return $permissionRepo->saveNewRole($roleData); |
| ... | @@ -107,16 +110,16 @@ class RolesTest extends BrowserKitTest | ... | @@ -107,16 +110,16 @@ class RolesTest extends BrowserKitTest |
| 107 | 110 | ||
| 108 | public function test_manage_user_permission() | 111 | public function test_manage_user_permission() |
| 109 | { | 112 | { |
| 110 | - $this->actingAs($this->user)->visit('/')->visit('/settings/users') | 113 | + $this->actingAs($this->user)->visit('/settings/users') |
| 111 | ->seePageIs('/'); | 114 | ->seePageIs('/'); |
| 112 | $this->giveUserPermissions($this->user, ['users-manage']); | 115 | $this->giveUserPermissions($this->user, ['users-manage']); |
| 113 | - $this->actingAs($this->user)->visit('/')->visit('/settings/users') | 116 | + $this->actingAs($this->user)->visit('/settings/users') |
| 114 | ->seePageIs('/settings/users'); | 117 | ->seePageIs('/settings/users'); |
| 115 | } | 118 | } |
| 116 | 119 | ||
| 117 | public function test_user_roles_manage_permission() | 120 | public function test_user_roles_manage_permission() |
| 118 | { | 121 | { |
| 119 | - $this->actingAs($this->user)->visit('/')->visit('/settings/roles') | 122 | + $this->actingAs($this->user)->visit('/settings/roles') |
| 120 | ->seePageIs('/')->visit('/settings/roles/1')->seePageIs('/'); | 123 | ->seePageIs('/')->visit('/settings/roles/1')->seePageIs('/'); |
| 121 | $this->giveUserPermissions($this->user, ['user-roles-manage']); | 124 | $this->giveUserPermissions($this->user, ['user-roles-manage']); |
| 122 | $this->actingAs($this->user)->visit('/settings/roles') | 125 | $this->actingAs($this->user)->visit('/settings/roles') |
| ... | @@ -126,10 +129,10 @@ class RolesTest extends BrowserKitTest | ... | @@ -126,10 +129,10 @@ class RolesTest extends BrowserKitTest |
| 126 | 129 | ||
| 127 | public function test_settings_manage_permission() | 130 | public function test_settings_manage_permission() |
| 128 | { | 131 | { |
| 129 | - $this->actingAs($this->user)->visit('/')->visit('/settings') | 132 | + $this->actingAs($this->user)->visit('/settings') |
| 130 | ->seePageIs('/'); | 133 | ->seePageIs('/'); |
| 131 | $this->giveUserPermissions($this->user, ['settings-manage']); | 134 | $this->giveUserPermissions($this->user, ['settings-manage']); |
| 132 | - $this->actingAs($this->user)->visit('/')->visit('/settings') | 135 | + $this->actingAs($this->user)->visit('/settings') |
| 133 | ->seePageIs('/settings')->press('Save Settings')->see('Settings Saved'); | 136 | ->seePageIs('/settings')->press('Save Settings')->see('Settings Saved'); |
| 134 | } | 137 | } |
| 135 | 138 | ||
| ... | @@ -181,27 +184,26 @@ class RolesTest extends BrowserKitTest | ... | @@ -181,27 +184,26 @@ class RolesTest extends BrowserKitTest |
| 181 | * @param string $permission | 184 | * @param string $permission |
| 182 | * @param array $accessUrls Urls that are only accessible after having the permission | 185 | * @param array $accessUrls Urls that are only accessible after having the permission |
| 183 | * @param array $visibles Check this text, In the buttons toolbar, is only visible with the permission | 186 | * @param array $visibles Check this text, In the buttons toolbar, is only visible with the permission |
| 184 | - * @param null $callback | ||
| 185 | */ | 187 | */ |
| 186 | private function checkAccessPermission($permission, $accessUrls = [], $visibles = []) | 188 | private function checkAccessPermission($permission, $accessUrls = [], $visibles = []) |
| 187 | { | 189 | { |
| 188 | foreach ($accessUrls as $url) { | 190 | foreach ($accessUrls as $url) { |
| 189 | - $this->actingAs($this->user)->visit('/')->visit($url) | 191 | + $this->actingAs($this->user)->visit($url) |
| 190 | ->seePageIs('/'); | 192 | ->seePageIs('/'); |
| 191 | } | 193 | } |
| 192 | foreach ($visibles as $url => $text) { | 194 | foreach ($visibles as $url => $text) { |
| 193 | - $this->actingAs($this->user)->visit('/')->visit($url) | 195 | + $this->actingAs($this->user)->visit($url) |
| 194 | ->dontSeeInElement('.action-buttons',$text); | 196 | ->dontSeeInElement('.action-buttons',$text); |
| 195 | } | 197 | } |
| 196 | 198 | ||
| 197 | $this->giveUserPermissions($this->user, [$permission]); | 199 | $this->giveUserPermissions($this->user, [$permission]); |
| 198 | 200 | ||
| 199 | foreach ($accessUrls as $url) { | 201 | foreach ($accessUrls as $url) { |
| 200 | - $this->actingAs($this->user)->visit('/')->visit($url) | 202 | + $this->actingAs($this->user)->visit($url) |
| 201 | ->seePageIs($url); | 203 | ->seePageIs($url); |
| 202 | } | 204 | } |
| 203 | foreach ($visibles as $url => $text) { | 205 | foreach ($visibles as $url => $text) { |
| 204 | - $this->actingAs($this->user)->visit('/')->visit($url) | 206 | + $this->actingAs($this->user)->visit($url) |
| 205 | ->see($text); | 207 | ->see($text); |
| 206 | } | 208 | } |
| 207 | } | 209 | } |
| ... | @@ -391,8 +393,8 @@ class RolesTest extends BrowserKitTest | ... | @@ -391,8 +393,8 @@ class RolesTest extends BrowserKitTest |
| 391 | 393 | ||
| 392 | public function test_page_create_own_permissions() | 394 | public function test_page_create_own_permissions() |
| 393 | { | 395 | { |
| 394 | - $book = \BookStack\Book::take(1)->get()->first(); | 396 | + $book = \BookStack\Book::first(); |
| 395 | - $chapter = \BookStack\Chapter::take(1)->get()->first(); | 397 | + $chapter = \BookStack\Chapter::first(); |
| 396 | 398 | ||
| 397 | $entities = $this->createEntityChainBelongingToUser($this->user); | 399 | $entities = $this->createEntityChainBelongingToUser($this->user); |
| 398 | $ownBook = $entities['book']; | 400 | $ownBook = $entities['book']; |
| ... | @@ -405,7 +407,7 @@ class RolesTest extends BrowserKitTest | ... | @@ -405,7 +407,7 @@ class RolesTest extends BrowserKitTest |
| 405 | $accessUrls = [$createUrl, $createUrlChapter]; | 407 | $accessUrls = [$createUrl, $createUrlChapter]; |
| 406 | 408 | ||
| 407 | foreach ($accessUrls as $url) { | 409 | foreach ($accessUrls as $url) { |
| 408 | - $this->actingAs($this->user)->visit('/')->visit($url) | 410 | + $this->actingAs($this->user)->visit($url) |
| 409 | ->seePageIs('/'); | 411 | ->seePageIs('/'); |
| 410 | } | 412 | } |
| 411 | 413 | ||
| ... | @@ -417,7 +419,7 @@ class RolesTest extends BrowserKitTest | ... | @@ -417,7 +419,7 @@ class RolesTest extends BrowserKitTest |
| 417 | $this->giveUserPermissions($this->user, ['page-create-own']); | 419 | $this->giveUserPermissions($this->user, ['page-create-own']); |
| 418 | 420 | ||
| 419 | foreach ($accessUrls as $index => $url) { | 421 | foreach ($accessUrls as $index => $url) { |
| 420 | - $this->actingAs($this->user)->visit('/')->visit($url); | 422 | + $this->actingAs($this->user)->visit($url); |
| 421 | $expectedUrl = \BookStack\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl(); | 423 | $expectedUrl = \BookStack\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl(); |
| 422 | $this->seePageIs($expectedUrl); | 424 | $this->seePageIs($expectedUrl); |
| 423 | } | 425 | } |
| ... | @@ -449,7 +451,7 @@ class RolesTest extends BrowserKitTest | ... | @@ -449,7 +451,7 @@ class RolesTest extends BrowserKitTest |
| 449 | $accessUrls = [$createUrl, $createUrlChapter]; | 451 | $accessUrls = [$createUrl, $createUrlChapter]; |
| 450 | 452 | ||
| 451 | foreach ($accessUrls as $url) { | 453 | foreach ($accessUrls as $url) { |
| 452 | - $this->actingAs($this->user)->visit('/')->visit($url) | 454 | + $this->actingAs($this->user)->visit($url) |
| 453 | ->seePageIs('/'); | 455 | ->seePageIs('/'); |
| 454 | } | 456 | } |
| 455 | 457 | ||
| ... | @@ -461,7 +463,7 @@ class RolesTest extends BrowserKitTest | ... | @@ -461,7 +463,7 @@ class RolesTest extends BrowserKitTest |
| 461 | $this->giveUserPermissions($this->user, ['page-create-all']); | 463 | $this->giveUserPermissions($this->user, ['page-create-all']); |
| 462 | 464 | ||
| 463 | foreach ($accessUrls as $index => $url) { | 465 | foreach ($accessUrls as $index => $url) { |
| 464 | - $this->actingAs($this->user)->visit('/')->visit($url); | 466 | + $this->actingAs($this->user)->visit($url); |
| 465 | $expectedUrl = \BookStack\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl(); | 467 | $expectedUrl = \BookStack\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl(); |
| 466 | $this->seePageIs($expectedUrl); | 468 | $this->seePageIs($expectedUrl); |
| 467 | } | 469 | } | ... | ... |
-
Please register or sign in to post a comment