diff --git a/app/Auth/Permissions/PermissionFormData.php b/app/Auth/Permissions/PermissionFormData.php index 07c95c534..781209043 100644 --- a/app/Auth/Permissions/PermissionFormData.php +++ b/app/Auth/Permissions/PermissionFormData.php @@ -15,16 +15,15 @@ class PermissionFormData } /** - * Get the roles with permissions assigned. + * Get the permissions with assigned roles. */ - public function rolesWithPermissions(): array + public function permissionsWithRoles(): array { return $this->entity->permissions() ->with('role') ->where('role_id', '!=', 0) - ->get(['id', 'role_id']) - ->pluck('role') - ->sortBy('display_name') + ->get() + ->sortBy('role.display_name') ->all(); } diff --git a/app/Http/Controllers/PermissionsController.php b/app/Http/Controllers/PermissionsController.php index d8dca9825..dd6c29a8a 100644 --- a/app/Http/Controllers/PermissionsController.php +++ b/app/Http/Controllers/PermissionsController.php @@ -2,7 +2,9 @@ namespace BookStack\Http\Controllers; +use BookStack\Auth\Permissions\EntityPermission; use BookStack\Auth\Permissions\PermissionFormData; +use BookStack\Auth\Role; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Chapter; @@ -148,4 +150,20 @@ class PermissionsController extends Controller return redirect($shelf->getUrl()); } + + /** + * Get an empty entity permissions form row for the given role. + */ + public function formRowForRole(string $entityType, string $roleId) + { + $this->checkPermissionOr('restrictions-manage', fn() => userCan('restrictions-manage-all')); + + $role = Role::query()->findOrFail($roleId); + + return view('form.entity-permissions-row', [ + 'role' => $role, + 'permission' => new EntityPermission(), + 'entityType' => $entityType, + ]); + } } diff --git a/resources/js/components/entity-permissions.js b/resources/js/components/entity-permissions.js index 8b57d3376..a18fc7a97 100644 --- a/resources/js/components/entity-permissions.js +++ b/resources/js/components/entity-permissions.js @@ -1,14 +1,21 @@ - - +/** + * @extends {Component} + */ class EntityPermissions { setup() { + this.container = this.$el; + this.entityType = this.$opts.entityType; + this.everyoneInheritToggle = this.$refs.everyoneInherit; + this.roleSelect = this.$refs.roleSelect; + this.roleContainer = this.$refs.roleContainer; this.setupListeners(); } setupListeners() { + // "Everyone Else" inherit toggle this.everyoneInheritToggle.addEventListener('change', event => { const inherit = event.target.checked; const permissions = document.querySelectorAll('input[type="checkbox"][name^="restrictions[0]["]'); @@ -16,7 +23,56 @@ class EntityPermissions { permission.disabled = inherit; permission.checked = false; } - }) + }); + + // Remove role row button click + this.container.addEventListener('click', event => { + const button = event.target.closest('button'); + if (button && button.dataset.roleId) { + this.removeRowOnButtonClick(button) + } + }); + + // Role select change + this.roleSelect.addEventListener('change', event => { + const roleId = this.roleSelect.value; + if (roleId) { + this.addRoleRow(roleId); + } + }); + } + + async addRoleRow(roleId) { + this.roleSelect.disabled = true; + + // Remove option from select + const option = this.roleSelect.querySelector(`option[value="${roleId}"]`); + if (option) { + option.remove(); + } + + // Get and insert new row + const resp = await window.$http.get(`/permissions/form-row/${this.entityType}/${roleId}`); + const wrap = document.createElement('div'); + wrap.innerHTML = resp.data; + const row = wrap.children[0]; + this.roleContainer.append(row); + window.components.init(row); + + this.roleSelect.disabled = false; + } + + removeRowOnButtonClick(button) { + const row = button.closest('.content-permissions-row'); + const roleId = button.dataset.roleId; + const roleName = button.dataset.roleName; + + const option = document.createElement('option'); + option.value = roleId; + option.textContent = roleName; + + this.roleSelect.append(option); + row.remove(); } } diff --git a/resources/sass/_buttons.scss b/resources/sass/_buttons.scss index 714dfc42c..83d17352d 100644 --- a/resources/sass/_buttons.scss +++ b/resources/sass/_buttons.scss @@ -109,12 +109,23 @@ button { display: block; } -.button.icon { +.button.icon, .icon-button { .svg-icon { margin-inline-end: 0; } } +.icon-button { + text-align: center; + border: 1px solid transparent; +} +.icon-button:hover { + background-color: rgba(0, 0, 0, 0.05); + border-radius: 4px; + border-color: #DDD; + cursor: pointer; +} + .button.svg { display: flex; align-items: center; diff --git a/resources/views/form/entity-permissions-row.blade.php b/resources/views/form/entity-permissions-row.blade.php index ce8beaec3..2bf19db64 100644 --- a/resources/views/form/entity-permissions-row.blade.php +++ b/resources/views/form/entity-permissions-row.blade.php @@ -1,3 +1,9 @@ +{{-- +$role - The Role to display this row for. +$entityType - String identifier for type of entity having permissions applied. +$permission - The entity permission containing the permissions. +--}} +