Implemented database structure and inital interfaces for entity restrictions
Showing
22 changed files
with
431 additions
and
19 deletions
| ... | @@ -48,7 +48,6 @@ abstract class Entity extends Ownable | ... | @@ -48,7 +48,6 @@ abstract class Entity extends Ownable |
| 48 | 48 | ||
| 49 | /** | 49 | /** |
| 50 | * Get View objects for this entity. | 50 | * Get View objects for this entity. |
| 51 | - * @return mixed | ||
| 52 | */ | 51 | */ |
| 53 | public function views() | 52 | public function views() |
| 54 | { | 53 | { |
| ... | @@ -56,6 +55,25 @@ abstract class Entity extends Ownable | ... | @@ -56,6 +55,25 @@ abstract class Entity extends Ownable |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | /** | 57 | /** |
| 58 | + * Get this entities restrictions. | ||
| 59 | + */ | ||
| 60 | + public function restrictions() | ||
| 61 | + { | ||
| 62 | + return $this->morphMany('BookStack\Restriction', 'restrictable'); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * Check if this entity has a specific restriction set against it. | ||
| 67 | + * @param $role_id | ||
| 68 | + * @param $action | ||
| 69 | + * @return bool | ||
| 70 | + */ | ||
| 71 | + public function hasRestriction($role_id, $action) | ||
| 72 | + { | ||
| 73 | + return $this->restrictions->where('role_id', $role_id)->where('action', $action)->count() > 0; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + /** | ||
| 59 | * Allows checking of the exact class, Used to check entity type. | 77 | * Allows checking of the exact class, Used to check entity type. |
| 60 | * Cleaner method for is_a. | 78 | * Cleaner method for is_a. |
| 61 | * @param $type | 79 | * @param $type | ... | ... |
| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | namespace BookStack\Http\Controllers; | 3 | namespace BookStack\Http\Controllers; |
| 4 | 4 | ||
| 5 | use Activity; | 5 | use Activity; |
| 6 | +use BookStack\Repos\UserRepo; | ||
| 6 | use Illuminate\Http\Request; | 7 | use Illuminate\Http\Request; |
| 7 | 8 | ||
| 8 | use Illuminate\Support\Facades\Auth; | 9 | use Illuminate\Support\Facades\Auth; |
| ... | @@ -19,18 +20,21 @@ class BookController extends Controller | ... | @@ -19,18 +20,21 @@ class BookController extends Controller |
| 19 | protected $bookRepo; | 20 | protected $bookRepo; |
| 20 | protected $pageRepo; | 21 | protected $pageRepo; |
| 21 | protected $chapterRepo; | 22 | protected $chapterRepo; |
| 23 | + protected $userRepo; | ||
| 22 | 24 | ||
| 23 | /** | 25 | /** |
| 24 | * BookController constructor. | 26 | * BookController constructor. |
| 25 | * @param BookRepo $bookRepo | 27 | * @param BookRepo $bookRepo |
| 26 | * @param PageRepo $pageRepo | 28 | * @param PageRepo $pageRepo |
| 27 | * @param ChapterRepo $chapterRepo | 29 | * @param ChapterRepo $chapterRepo |
| 30 | + * @param UserRepo $userRepo | ||
| 28 | */ | 31 | */ |
| 29 | - public function __construct(BookRepo $bookRepo, PageRepo $pageRepo, ChapterRepo $chapterRepo) | 32 | + public function __construct(BookRepo $bookRepo, PageRepo $pageRepo, ChapterRepo $chapterRepo, UserRepo $userRepo) |
| 30 | { | 33 | { |
| 31 | $this->bookRepo = $bookRepo; | 34 | $this->bookRepo = $bookRepo; |
| 32 | $this->pageRepo = $pageRepo; | 35 | $this->pageRepo = $pageRepo; |
| 33 | $this->chapterRepo = $chapterRepo; | 36 | $this->chapterRepo = $chapterRepo; |
| 37 | + $this->userRepo = $userRepo; | ||
| 34 | parent::__construct(); | 38 | parent::__construct(); |
| 35 | } | 39 | } |
| 36 | 40 | ||
| ... | @@ -177,7 +181,6 @@ class BookController extends Controller | ... | @@ -177,7 +181,6 @@ class BookController extends Controller |
| 177 | 181 | ||
| 178 | /** | 182 | /** |
| 179 | * Saves an array of sort mapping to pages and chapters. | 183 | * Saves an array of sort mapping to pages and chapters. |
| 180 | - * | ||
| 181 | * @param string $bookSlug | 184 | * @param string $bookSlug |
| 182 | * @param Request $request | 185 | * @param Request $request |
| 183 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | 186 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector |
| ... | @@ -223,7 +226,6 @@ class BookController extends Controller | ... | @@ -223,7 +226,6 @@ class BookController extends Controller |
| 223 | 226 | ||
| 224 | /** | 227 | /** |
| 225 | * Remove the specified book from storage. | 228 | * Remove the specified book from storage. |
| 226 | - * | ||
| 227 | * @param $bookSlug | 229 | * @param $bookSlug |
| 228 | * @return Response | 230 | * @return Response |
| 229 | */ | 231 | */ |
| ... | @@ -236,4 +238,36 @@ class BookController extends Controller | ... | @@ -236,4 +238,36 @@ class BookController extends Controller |
| 236 | $this->bookRepo->destroyBySlug($bookSlug); | 238 | $this->bookRepo->destroyBySlug($bookSlug); |
| 237 | return redirect('/books'); | 239 | return redirect('/books'); |
| 238 | } | 240 | } |
| 241 | + | ||
| 242 | + /** | ||
| 243 | + * Show the Restrictions view. | ||
| 244 | + * @param $bookSlug | ||
| 245 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 246 | + */ | ||
| 247 | + public function showRestrict($bookSlug) | ||
| 248 | + { | ||
| 249 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 250 | + $this->checkOwnablePermission('restrictions-manage', $book); | ||
| 251 | + $roles = $this->userRepo->getRestrictableRoles(); | ||
| 252 | + return view('books/restrictions', [ | ||
| 253 | + 'book' => $book, | ||
| 254 | + 'roles' => $roles | ||
| 255 | + ]); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + /** | ||
| 259 | + * Set the restrictions for this book. | ||
| 260 | + * @param $bookSlug | ||
| 261 | + * @param $bookSlug | ||
| 262 | + * @param Request $request | ||
| 263 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||
| 264 | + */ | ||
| 265 | + public function restrict($bookSlug, Request $request) | ||
| 266 | + { | ||
| 267 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 268 | + $this->checkOwnablePermission('restrictions-manage', $book); | ||
| 269 | + $this->bookRepo->updateRestrictionsFromRequest($request, $book); | ||
| 270 | + session()->flash('success', 'Page Restrictions Updated'); | ||
| 271 | + return redirect($book->getUrl()); | ||
| 272 | + } | ||
| 239 | } | 273 | } | ... | ... |
| 1 | <?php namespace BookStack\Http\Controllers; | 1 | <?php namespace BookStack\Http\Controllers; |
| 2 | 2 | ||
| 3 | use Activity; | 3 | use Activity; |
| 4 | +use BookStack\Repos\UserRepo; | ||
| 4 | use Illuminate\Http\Request; | 5 | use Illuminate\Http\Request; |
| 5 | use BookStack\Http\Requests; | 6 | use BookStack\Http\Requests; |
| 6 | use BookStack\Repos\BookRepo; | 7 | use BookStack\Repos\BookRepo; |
| ... | @@ -12,16 +13,19 @@ class ChapterController extends Controller | ... | @@ -12,16 +13,19 @@ class ChapterController extends Controller |
| 12 | 13 | ||
| 13 | protected $bookRepo; | 14 | protected $bookRepo; |
| 14 | protected $chapterRepo; | 15 | protected $chapterRepo; |
| 16 | + protected $userRepo; | ||
| 15 | 17 | ||
| 16 | /** | 18 | /** |
| 17 | * ChapterController constructor. | 19 | * ChapterController constructor. |
| 18 | - * @param $bookRepo | 20 | + * @param BookRepo $bookRepo |
| 19 | - * @param $chapterRepo | 21 | + * @param ChapterRepo $chapterRepo |
| 22 | + * @param UserRepo $userRepo | ||
| 20 | */ | 23 | */ |
| 21 | - public function __construct(BookRepo $bookRepo, ChapterRepo $chapterRepo) | 24 | + public function __construct(BookRepo $bookRepo, ChapterRepo $chapterRepo, UserRepo $userRepo) |
| 22 | { | 25 | { |
| 23 | $this->bookRepo = $bookRepo; | 26 | $this->bookRepo = $bookRepo; |
| 24 | $this->chapterRepo = $chapterRepo; | 27 | $this->chapterRepo = $chapterRepo; |
| 28 | + $this->userRepo = $userRepo; | ||
| 25 | parent::__construct(); | 29 | parent::__construct(); |
| 26 | } | 30 | } |
| 27 | 31 | ||
| ... | @@ -144,4 +148,39 @@ class ChapterController extends Controller | ... | @@ -144,4 +148,39 @@ class ChapterController extends Controller |
| 144 | $this->chapterRepo->destroy($chapter); | 148 | $this->chapterRepo->destroy($chapter); |
| 145 | return redirect($book->getUrl()); | 149 | return redirect($book->getUrl()); |
| 146 | } | 150 | } |
| 151 | + | ||
| 152 | + /** | ||
| 153 | + * Show the Restrictions view. | ||
| 154 | + * @param $bookSlug | ||
| 155 | + * @param $chapterSlug | ||
| 156 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 157 | + */ | ||
| 158 | + public function showRestrict($bookSlug, $chapterSlug) | ||
| 159 | + { | ||
| 160 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 161 | + $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); | ||
| 162 | + $this->checkOwnablePermission('restrictions-manage', $chapter); | ||
| 163 | + $roles = $this->userRepo->getRestrictableRoles(); | ||
| 164 | + return view('chapters/restrictions', [ | ||
| 165 | + 'chapter' => $chapter, | ||
| 166 | + 'roles' => $roles | ||
| 167 | + ]); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + /** | ||
| 171 | + * Set the restrictions for this chapter. | ||
| 172 | + * @param $bookSlug | ||
| 173 | + * @param $chapterSlug | ||
| 174 | + * @param Request $request | ||
| 175 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||
| 176 | + */ | ||
| 177 | + public function restrict($bookSlug, $chapterSlug, Request $request) | ||
| 178 | + { | ||
| 179 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 180 | + $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); | ||
| 181 | + $this->checkOwnablePermission('restrictions-manage', $chapter); | ||
| 182 | + $this->chapterRepo->updateRestrictionsFromRequest($request, $chapter); | ||
| 183 | + session()->flash('success', 'Page Restrictions Updated'); | ||
| 184 | + return redirect($chapter->getUrl()); | ||
| 185 | + } | ||
| 147 | } | 186 | } | ... | ... |
| 1 | <?php namespace BookStack\Http\Controllers; | 1 | <?php namespace BookStack\Http\Controllers; |
| 2 | 2 | ||
| 3 | use Activity; | 3 | use Activity; |
| 4 | +use BookStack\Repos\UserRepo; | ||
| 4 | use BookStack\Services\ExportService; | 5 | use BookStack\Services\ExportService; |
| 5 | use Illuminate\Http\Request; | 6 | use Illuminate\Http\Request; |
| 6 | use BookStack\Http\Requests; | 7 | use BookStack\Http\Requests; |
| ... | @@ -17,6 +18,7 @@ class PageController extends Controller | ... | @@ -17,6 +18,7 @@ class PageController extends Controller |
| 17 | protected $bookRepo; | 18 | protected $bookRepo; |
| 18 | protected $chapterRepo; | 19 | protected $chapterRepo; |
| 19 | protected $exportService; | 20 | protected $exportService; |
| 21 | + protected $userRepo; | ||
| 20 | 22 | ||
| 21 | /** | 23 | /** |
| 22 | * PageController constructor. | 24 | * PageController constructor. |
| ... | @@ -24,13 +26,15 @@ class PageController extends Controller | ... | @@ -24,13 +26,15 @@ class PageController extends Controller |
| 24 | * @param BookRepo $bookRepo | 26 | * @param BookRepo $bookRepo |
| 25 | * @param ChapterRepo $chapterRepo | 27 | * @param ChapterRepo $chapterRepo |
| 26 | * @param ExportService $exportService | 28 | * @param ExportService $exportService |
| 29 | + * @param UserRepo $userRepo | ||
| 27 | */ | 30 | */ |
| 28 | - public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService) | 31 | + public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService, UserRepo $userRepo) |
| 29 | { | 32 | { |
| 30 | $this->pageRepo = $pageRepo; | 33 | $this->pageRepo = $pageRepo; |
| 31 | $this->bookRepo = $bookRepo; | 34 | $this->bookRepo = $bookRepo; |
| 32 | $this->chapterRepo = $chapterRepo; | 35 | $this->chapterRepo = $chapterRepo; |
| 33 | $this->exportService = $exportService; | 36 | $this->exportService = $exportService; |
| 37 | + $this->userRepo = $userRepo; | ||
| 34 | parent::__construct(); | 38 | parent::__construct(); |
| 35 | } | 39 | } |
| 36 | 40 | ||
| ... | @@ -308,4 +312,39 @@ class PageController extends Controller | ... | @@ -308,4 +312,39 @@ class PageController extends Controller |
| 308 | ]); | 312 | ]); |
| 309 | } | 313 | } |
| 310 | 314 | ||
| 315 | + /** | ||
| 316 | + * Show the Restrictions view. | ||
| 317 | + * @param $bookSlug | ||
| 318 | + * @param $pageSlug | ||
| 319 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 320 | + */ | ||
| 321 | + public function showRestrict($bookSlug, $pageSlug) | ||
| 322 | + { | ||
| 323 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 324 | + $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | ||
| 325 | + $this->checkOwnablePermission('restrictions-manage', $page); | ||
| 326 | + $roles = $this->userRepo->getRestrictableRoles(); | ||
| 327 | + return view('pages/restrictions', [ | ||
| 328 | + 'page' => $page, | ||
| 329 | + 'roles' => $roles | ||
| 330 | + ]); | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + /** | ||
| 334 | + * Set the restrictions for this page. | ||
| 335 | + * @param $bookSlug | ||
| 336 | + * @param $pageSlug | ||
| 337 | + * @param Request $request | ||
| 338 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||
| 339 | + */ | ||
| 340 | + public function restrict($bookSlug, $pageSlug, Request $request) | ||
| 341 | + { | ||
| 342 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 343 | + $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | ||
| 344 | + $this->checkOwnablePermission('restrictions-manage', $page); | ||
| 345 | + $this->pageRepo->updateRestrictionsFromRequest($request, $page); | ||
| 346 | + session()->flash('success', 'Page Restrictions Updated'); | ||
| 347 | + return redirect($page->getUrl()); | ||
| 348 | + } | ||
| 349 | + | ||
| 311 | } | 350 | } | ... | ... |
| ... | @@ -19,6 +19,8 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -19,6 +19,8 @@ Route::group(['middleware' => 'auth'], function () { |
| 19 | Route::delete('/{id}', 'BookController@destroy'); | 19 | Route::delete('/{id}', 'BookController@destroy'); |
| 20 | Route::get('/{slug}/sort-item', 'BookController@getSortItem'); | 20 | Route::get('/{slug}/sort-item', 'BookController@getSortItem'); |
| 21 | Route::get('/{slug}', 'BookController@show'); | 21 | Route::get('/{slug}', 'BookController@show'); |
| 22 | + Route::get('/{bookSlug}/restrict', 'BookController@showRestrict'); | ||
| 23 | + Route::put('/{bookSlug}/restrict', 'BookController@restrict'); | ||
| 22 | Route::get('/{slug}/delete', 'BookController@showDelete'); | 24 | Route::get('/{slug}/delete', 'BookController@showDelete'); |
| 23 | Route::get('/{bookSlug}/sort', 'BookController@sort'); | 25 | Route::get('/{bookSlug}/sort', 'BookController@sort'); |
| 24 | Route::put('/{bookSlug}/sort', 'BookController@saveSort'); | 26 | Route::put('/{bookSlug}/sort', 'BookController@saveSort'); |
| ... | @@ -32,6 +34,8 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -32,6 +34,8 @@ Route::group(['middleware' => 'auth'], function () { |
| 32 | Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageController@exportPlainText'); | 34 | Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageController@exportPlainText'); |
| 33 | Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit'); | 35 | Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit'); |
| 34 | Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); | 36 | Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); |
| 37 | + Route::get('/{bookSlug}/page/{pageSlug}/restrict', 'PageController@showRestrict'); | ||
| 38 | + Route::put('/{bookSlug}/page/{pageSlug}/restrict', 'PageController@restrict'); | ||
| 35 | Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update'); | 39 | Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update'); |
| 36 | Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy'); | 40 | Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy'); |
| 37 | 41 | ||
| ... | @@ -47,6 +51,8 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -47,6 +51,8 @@ Route::group(['middleware' => 'auth'], function () { |
| 47 | Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show'); | 51 | Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show'); |
| 48 | Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update'); | 52 | Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update'); |
| 49 | Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit'); | 53 | Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit'); |
| 54 | + Route::get('/{bookSlug}/chapter/{chapterSlug}/restrict', 'ChapterController@showRestrict'); | ||
| 55 | + Route::put('/{bookSlug}/chapter/{chapterSlug}/restrict', 'ChapterController@restrict'); | ||
| 50 | Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete'); | 56 | Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete'); |
| 51 | Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy'); | 57 | Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy'); |
| 52 | 58 | ... | ... |
| ... | @@ -238,4 +238,27 @@ class BookRepo | ... | @@ -238,4 +238,27 @@ class BookRepo |
| 238 | return $books; | 238 | return $books; |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | + /** | ||
| 242 | + * Updates books restrictions from a request | ||
| 243 | + * @param $request | ||
| 244 | + * @param $book | ||
| 245 | + */ | ||
| 246 | + public function updateRestrictionsFromRequest($request, $book) | ||
| 247 | + { | ||
| 248 | + // TODO - extract into shared repo | ||
| 249 | + $book->restricted = $request->has('restricted') && $request->get('restricted') === 'true'; | ||
| 250 | + $book->restrictions()->delete(); | ||
| 251 | + if ($request->has('restrictions')) { | ||
| 252 | + foreach($request->get('restrictions') as $roleId => $restrictions) { | ||
| 253 | + foreach ($restrictions as $action => $value) { | ||
| 254 | + $book->restrictions()->create([ | ||
| 255 | + 'role_id' => $roleId, | ||
| 256 | + 'action' => strtolower($action) | ||
| 257 | + ]); | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | + $book->save(); | ||
| 262 | + } | ||
| 263 | + | ||
| 241 | } | 264 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -161,4 +161,27 @@ class ChapterRepo | ... | @@ -161,4 +161,27 @@ class ChapterRepo |
| 161 | return $chapter; | 161 | return $chapter; |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | + /** | ||
| 165 | + * Updates pages restrictions from a request | ||
| 166 | + * @param $request | ||
| 167 | + * @param $chapter | ||
| 168 | + */ | ||
| 169 | + public function updateRestrictionsFromRequest($request, $chapter) | ||
| 170 | + { | ||
| 171 | + // TODO - extract into shared repo | ||
| 172 | + $chapter->restricted = $request->has('restricted') && $request->get('restricted') === 'true'; | ||
| 173 | + $chapter->restrictions()->delete(); | ||
| 174 | + if ($request->has('restrictions')) { | ||
| 175 | + foreach($request->get('restrictions') as $roleId => $restrictions) { | ||
| 176 | + foreach ($restrictions as $action => $value) { | ||
| 177 | + $chapter->restrictions()->create([ | ||
| 178 | + 'role_id' => $roleId, | ||
| 179 | + 'action' => strtolower($action) | ||
| 180 | + ]); | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + $chapter->save(); | ||
| 185 | + } | ||
| 186 | + | ||
| 164 | } | 187 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -407,4 +407,27 @@ class PageRepo | ... | @@ -407,4 +407,27 @@ class PageRepo |
| 407 | return $this->page->orderBy('updated_at', 'desc')->paginate($count); | 407 | return $this->page->orderBy('updated_at', 'desc')->paginate($count); |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | + /** | ||
| 411 | + * Updates pages restrictions from a request | ||
| 412 | + * @param $request | ||
| 413 | + * @param $page | ||
| 414 | + */ | ||
| 415 | + public function updateRestrictionsFromRequest($request, $page) | ||
| 416 | + { | ||
| 417 | + // TODO - extract into shared repo | ||
| 418 | + $page->restricted = $request->has('restricted') && $request->get('restricted') === 'true'; | ||
| 419 | + $page->restrictions()->delete(); | ||
| 420 | + if ($request->has('restrictions')) { | ||
| 421 | + foreach($request->get('restrictions') as $roleId => $restrictions) { | ||
| 422 | + foreach ($restrictions as $action => $value) { | ||
| 423 | + $page->restrictions()->create([ | ||
| 424 | + 'role_id' => $roleId, | ||
| 425 | + 'action' => strtolower($action) | ||
| 426 | + ]); | ||
| 427 | + } | ||
| 428 | + } | ||
| 429 | + } | ||
| 430 | + $page->save(); | ||
| 431 | + } | ||
| 432 | + | ||
| 410 | } | 433 | } | ... | ... |
| ... | @@ -164,4 +164,14 @@ class UserRepo | ... | @@ -164,4 +164,14 @@ class UserRepo |
| 164 | ]; | 164 | ]; |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | + /** | ||
| 168 | + * Get all the roles which can be given restricted access to | ||
| 169 | + * other entities in the system. | ||
| 170 | + * @return mixed | ||
| 171 | + */ | ||
| 172 | + public function getRestrictableRoles() | ||
| 173 | + { | ||
| 174 | + return $this->role->where('name', '!=', 'admin')->get(); | ||
| 175 | + } | ||
| 176 | + | ||
| 167 | } | 177 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
app/Restriction.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace BookStack; | ||
| 4 | + | ||
| 5 | +use Illuminate\Database\Eloquent\Model; | ||
| 6 | + | ||
| 7 | +class Restriction extends Model | ||
| 8 | +{ | ||
| 9 | + | ||
| 10 | + protected $fillable = ['role_id', 'action']; | ||
| 11 | + public $timestamps = false; | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * Get all this restriction's attached entity. | ||
| 15 | + * @return \Illuminate\Database\Eloquent\Relations\MorphTo | ||
| 16 | + */ | ||
| 17 | + public function restrictable() | ||
| 18 | + { | ||
| 19 | + return $this->morphTo(); | ||
| 20 | + } | ||
| 21 | +} |
| ... | @@ -26,7 +26,9 @@ class UpdatePermissionsAndRoles extends Migration | ... | @@ -26,7 +26,9 @@ class UpdatePermissionsAndRoles extends Migration |
| 26 | $permissionsToCreate = [ | 26 | $permissionsToCreate = [ |
| 27 | 'settings-manage' => 'Manage Settings', | 27 | 'settings-manage' => 'Manage Settings', |
| 28 | 'users-manage' => 'Manage Users', | 28 | 'users-manage' => 'Manage Users', |
| 29 | - 'user-roles-manage' => 'Manage Roles & Permissions' | 29 | + 'user-roles-manage' => 'Manage Roles & Permissions', |
| 30 | + 'restrictions-manage-all' => 'Manage All Entity Restrictions', | ||
| 31 | + 'restrictions-manage-own' => 'Manage Entity Restrictions On Own Content' | ||
| 30 | ]; | 32 | ]; |
| 31 | foreach ($permissionsToCreate as $name => $displayName) { | 33 | foreach ($permissionsToCreate as $name => $displayName) { |
| 32 | $newPermission = new \BookStack\Permission(); | 34 | $newPermission = new \BookStack\Permission(); | ... | ... |
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +use Illuminate\Database\Schema\Blueprint; | ||
| 4 | +use Illuminate\Database\Migrations\Migration; | ||
| 5 | + | ||
| 6 | +class AddEntityAccessControls extends Migration | ||
| 7 | +{ | ||
| 8 | + /** | ||
| 9 | + * Run the migrations. | ||
| 10 | + * | ||
| 11 | + * @return void | ||
| 12 | + */ | ||
| 13 | + public function up() | ||
| 14 | + { | ||
| 15 | + Schema::table('images', function (Blueprint $table) { | ||
| 16 | + $table->integer('uploaded_to')->default(0); | ||
| 17 | + $table->index('uploaded_to'); | ||
| 18 | + }); | ||
| 19 | + | ||
| 20 | + Schema::table('books', function (Blueprint $table) { | ||
| 21 | + $table->boolean('restricted')->default(false); | ||
| 22 | + $table->index('restricted'); | ||
| 23 | + }); | ||
| 24 | + | ||
| 25 | + Schema::table('chapters', function (Blueprint $table) { | ||
| 26 | + $table->boolean('restricted')->default(false); | ||
| 27 | + $table->index('restricted'); | ||
| 28 | + }); | ||
| 29 | + | ||
| 30 | + Schema::table('pages', function (Blueprint $table) { | ||
| 31 | + $table->boolean('restricted')->default(false); | ||
| 32 | + $table->index('restricted'); | ||
| 33 | + }); | ||
| 34 | + | ||
| 35 | + Schema::create('restrictions', function(Blueprint $table) { | ||
| 36 | + $table->increments('id'); | ||
| 37 | + $table->integer('restrictable_id'); | ||
| 38 | + $table->string('restrictable_type'); | ||
| 39 | + $table->integer('role_id'); | ||
| 40 | + $table->string('action'); | ||
| 41 | + $table->index('role_id'); | ||
| 42 | + $table->index('action'); | ||
| 43 | + $table->index(['restrictable_id', 'restrictable_type']); | ||
| 44 | + }); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * Reverse the migrations. | ||
| 49 | + * | ||
| 50 | + * @return void | ||
| 51 | + */ | ||
| 52 | + public function down() | ||
| 53 | + { | ||
| 54 | + Schema::table('images', function (Blueprint $table) { | ||
| 55 | + $table->dropColumn('uploaded_to'); | ||
| 56 | + }); | ||
| 57 | + | ||
| 58 | + Schema::table('books', function (Blueprint $table) { | ||
| 59 | + $table->dropColumn('restricted'); | ||
| 60 | + }); | ||
| 61 | + | ||
| 62 | + Schema::table('chapters', function (Blueprint $table) { | ||
| 63 | + $table->dropColumn('restricted'); | ||
| 64 | + }); | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + Schema::table('pages', function (Blueprint $table) { | ||
| 68 | + $table->dropColumn('restricted'); | ||
| 69 | + }); | ||
| 70 | + | ||
| 71 | + Schema::drop('restrictions'); | ||
| 72 | + } | ||
| 73 | +} |
resources/views/books/restrictions.blade.php
0 → 100644
| ... | @@ -17,6 +17,9 @@ | ... | @@ -17,6 +17,9 @@ |
| 17 | <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> | 17 | <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> |
| 18 | <a href="{{ $book->getUrl() }}/sort" class="text-primary text-button"><i class="zmdi zmdi-sort"></i>Sort</a> | 18 | <a href="{{ $book->getUrl() }}/sort" class="text-primary text-button"><i class="zmdi zmdi-sort"></i>Sort</a> |
| 19 | @endif | 19 | @endif |
| 20 | + @if(userCan('restrictions-manage', $book)) | ||
| 21 | + <a href="{{$book->getUrl()}}/restrict" class="text-primary text-button"><i class="zmdi zmdi-lock-outline"></i>Restrict</a> | ||
| 22 | + @endif | ||
| 20 | @if(userCan('book-delete', $book)) | 23 | @if(userCan('book-delete', $book)) |
| 21 | <a href="{{ $book->getUrl() }}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> | 24 | <a href="{{ $book->getUrl() }}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> |
| 22 | @endif | 25 | @endif | ... | ... |
| ... | @@ -18,6 +18,9 @@ | ... | @@ -18,6 +18,9 @@ |
| 18 | @if(userCan('chapter-update', $chapter)) | 18 | @if(userCan('chapter-update', $chapter)) |
| 19 | <a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> | 19 | <a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> |
| 20 | @endif | 20 | @endif |
| 21 | + @if(userCan('restrictions-manage', $chapter)) | ||
| 22 | + <a href="{{$chapter->getUrl()}}/restrict" class="text-primary text-button"><i class="zmdi zmdi-lock-outline"></i>Restrict</a> | ||
| 23 | + @endif | ||
| 21 | @if(userCan('chapter-delete', $chapter)) | 24 | @if(userCan('chapter-delete', $chapter)) |
| 22 | <a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> | 25 | <a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> |
| 23 | @endif | 26 | @endif | ... | ... |
resources/views/form/checkbox.blade.php
0 → 100644
| 1 | + | ||
| 2 | +<label> | ||
| 3 | + <input value="true" id="{{$name}}" type="checkbox" name="{{$name}}" | ||
| 4 | + @if($errors->has($name)) class="neg" @endif | ||
| 5 | + @if(old($name) || (!old() && isset($model) && $model->$name)) checked="checked" @endif | ||
| 6 | + > | ||
| 7 | + {{ $label }} | ||
| 8 | +</label> | ||
| 9 | + | ||
| 10 | +@if($errors->has($name)) | ||
| 11 | + <div class="text-neg text-small">{{ $errors->first($name) }}</div> | ||
| 12 | +@endif | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | + | ||
| 2 | +<label> | ||
| 3 | + <input value="true" id="{{$name}}[{{$role->id}}][{{$action}}]" type="checkbox" name="{{$name}}[{{$role->id}}][{{$action}}]" | ||
| 4 | + @if(old($name .'.'.$role->id.'.'.$action) || (!old() && isset($model) && $model->hasRestriction($role->id, $action))) checked="checked" @endif | ||
| 5 | + > | ||
| 6 | + {{ $label }} | ||
| 7 | +</label> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | +<form action="{{ $model->getUrl() }}/restrict" method="POST"> | ||
| 2 | + {!! csrf_field() !!} | ||
| 3 | + <input type="hidden" name="_method" value="PUT"> | ||
| 4 | + | ||
| 5 | + <div class="form-group"> | ||
| 6 | + @include('form/checkbox', ['name' => 'restricted', 'label' => 'Restrict this page?']) | ||
| 7 | + </div> | ||
| 8 | + | ||
| 9 | + <table class="table"> | ||
| 10 | + <tr> | ||
| 11 | + <th>Role</th> | ||
| 12 | + <th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>Actions</th> | ||
| 13 | + </tr> | ||
| 14 | + @foreach($roles as $role) | ||
| 15 | + <tr> | ||
| 16 | + <td>{{ $role->display_name }}</td> | ||
| 17 | + <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'View', 'action' => 'view'])</td> | ||
| 18 | + @if(!$model->isA('page')) | ||
| 19 | + <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Create', 'action' => 'create'])</td> | ||
| 20 | + @endif | ||
| 21 | + <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Update', 'action' => 'update'])</td> | ||
| 22 | + <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Delete', 'action' => 'delete'])</td> | ||
| 23 | + </tr> | ||
| 24 | + @endforeach | ||
| 25 | + </table> | ||
| 26 | + | ||
| 27 | + <button type="submit" class="button pos">Save Restrictions</button> | ||
| 28 | +</form> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
resources/views/pages/restrictions.blade.php
0 → 100644
| ... | @@ -22,17 +22,20 @@ | ... | @@ -22,17 +22,20 @@ |
| 22 | <span dropdown class="dropdown-container"> | 22 | <span dropdown class="dropdown-container"> |
| 23 | <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>Export</div> | 23 | <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>Export</div> |
| 24 | <ul class="wide"> | 24 | <ul class="wide"> |
| 25 | - <li><a href="{{$page->getUrl() . '/export/html'}}" target="_blank">Contained Web File <span class="text-muted float right">.html</span></a></li> | 25 | + <li><a href="{{$page->getUrl()}}/export/html" target="_blank">Contained Web File <span class="text-muted float right">.html</span></a></li> |
| 26 | - <li><a href="{{$page->getUrl() . '/export/pdf'}}" target="_blank">PDF File <span class="text-muted float right">.pdf</span></a></li> | 26 | + <li><a href="{{$page->getUrl()}}/export/pdf" target="_blank">PDF File <span class="text-muted float right">.pdf</span></a></li> |
| 27 | - <li><a href="{{$page->getUrl() . '/export/plaintext'}}" target="_blank">Plain Text File <span class="text-muted float right">.txt</span></a></li> | 27 | + <li><a href="{{$page->getUrl()}}/export/plaintext" target="_blank">Plain Text File <span class="text-muted float right">.txt</span></a></li> |
| 28 | </ul> | 28 | </ul> |
| 29 | </span> | 29 | </span> |
| 30 | @if(userCan('page-update', $page)) | 30 | @if(userCan('page-update', $page)) |
| 31 | - <a href="{{$page->getUrl() . '/revisions'}}" class="text-primary text-button"><i class="zmdi zmdi-replay"></i>Revisions</a> | 31 | + <a href="{{$page->getUrl()}}/revisions" class="text-primary text-button"><i class="zmdi zmdi-replay"></i>Revisions</a> |
| 32 | - <a href="{{$page->getUrl() . '/edit'}}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a> | 32 | + <a href="{{$page->getUrl()}}/edit" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a> |
| 33 | + @endif | ||
| 34 | + @if(userCan('restrictions-manage', $page)) | ||
| 35 | + <a href="{{$page->getUrl()}}/restrict" class="text-primary text-button"><i class="zmdi zmdi-lock-outline"></i>Restrict</a> | ||
| 33 | @endif | 36 | @endif |
| 34 | @if(userCan('page-delete', $page)) | 37 | @if(userCan('page-delete', $page)) |
| 35 | - <a href="{{$page->getUrl() . '/delete'}}" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> | 38 | + <a href="{{$page->getUrl()}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> |
| 36 | @endif | 39 | @endif |
| 37 | </div> | 40 | </div> |
| 38 | </div> | 41 | </div> | ... | ... |
| ... | @@ -12,13 +12,28 @@ | ... | @@ -12,13 +12,28 @@ |
| 12 | @include('form/text', ['name' => 'description']) | 12 | @include('form/text', ['name' => 'description']) |
| 13 | </div> | 13 | </div> |
| 14 | <hr class="even"> | 14 | <hr class="even"> |
| 15 | - <div class="form-group"> | 15 | + <div class="row"> |
| 16 | - <label>Manage users @include('settings/roles/checkbox', ['permission' => 'users-manage'])</label> | 16 | + <div class="col-md-6"> |
| 17 | + <label> @include('settings/roles/checkbox', ['permission' => 'users-manage']) Manage users</label> | ||
| 18 | + </div> | ||
| 19 | + <div class="col-md-6"> | ||
| 20 | + <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) Manage user roles & Permissions</label> | ||
| 21 | + </div> | ||
| 22 | + </div> | ||
| 17 | <hr class="even"> | 23 | <hr class="even"> |
| 18 | - <label>Manage user roles & Permissions @include('settings/roles/checkbox', ['permission' => 'user-roles-manage'])</label> | 24 | + <div class="row"> |
| 25 | + <div class="col-md-6"> | ||
| 26 | + <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-all']) Manage all restrictions</label> | ||
| 27 | + </div> | ||
| 28 | + <div class="col-md-6"> | ||
| 29 | + <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-own']) Manage restrictions on own content</label> | ||
| 30 | + </div> | ||
| 31 | + </div> | ||
| 19 | <hr class="even"> | 32 | <hr class="even"> |
| 20 | - <label>Manage app settings @include('settings/roles/checkbox', ['permission' => 'settings-manage'])</label> | 33 | + <div class="form-group"> |
| 34 | + <label>@include('settings/roles/checkbox', ['permission' => 'settings-manage']) Manage app settings</label> | ||
| 21 | </div> | 35 | </div> |
| 36 | + | ||
| 22 | </div> | 37 | </div> |
| 23 | 38 | ||
| 24 | <div class="col-md-6"> | 39 | <div class="col-md-6"> | ... | ... |
-
Please register or sign in to post a comment