Added ability to copy a role

- Copies via loading in model on create view.
- Updated role views while editing to bring up to similar format as
  that used for more modern app areas.
- Added tests to cover.

Related to #1123
This commit is contained in:
Dan Brown 2021-12-19 12:27:14 +00:00
parent 67b6c07548
commit da01913616
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
5 changed files with 312 additions and 272 deletions

View File

@ -3,6 +3,7 @@
namespace BookStack\Http\Controllers;
use BookStack\Auth\Permissions\PermissionsRepo;
use BookStack\Auth\Role;
use BookStack\Exceptions\PermissionsException;
use Exception;
use Illuminate\Http\Request;
@ -34,11 +35,21 @@ class RoleController extends Controller
/**
* Show the form to create a new role.
*/
public function create()
public function create(Request $request)
{
$this->checkPermission('user-roles-manage');
return view('settings.roles.create');
/** @var ?Role $role */
$role = null;
if ($request->has('copy_from')) {
$role = Role::query()->find($request->get('copy_from'));
}
if ($role) {
$role->display_name .= ' (' . trans('common.copy') . ')';
}
return view('settings.roles.create', ['role' => $role]);
}
/**
@ -49,7 +60,7 @@ class RoleController extends Controller
$this->checkPermission('user-roles-manage');
$this->validate($request, [
'display_name' => ['required', 'min:3', 'max:180'],
'description' => 'max:180',
'description' => ['max:180'],
]);
$this->permissionsRepo->saveNewRole($request->all());
@ -84,7 +95,7 @@ class RoleController extends Controller
$this->checkPermission('user-roles-manage');
$this->validate($request, [
'display_name' => ['required', 'min:3', 'max:180'],
'description' => 'max:180',
'description' => ['max:180'],
]);
$this->permissionsRepo->updateRole($id, $request->all());

View File

@ -8,9 +8,21 @@
@include('settings.parts.navbar', ['selected' => 'roles'])
</div>
<div class="card content-wrap">
<h1 class="list-heading">{{ trans('settings.role_create') }}</h1>
<form action="{{ url("/settings/roles/new") }}" method="POST">
@include('settings.roles.parts.form', ['title' => trans('settings.role_create')])
{{ csrf_field() }}
@include('settings.roles.parts.form', ['role' => $role ?? null])
<div class="form-group text-right">
<a href="{{ url("/settings/roles") }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button">{{ trans('settings.role_save') }}</button>
</div>
</form>
</div>
</div>
@stop

View File

@ -7,10 +7,53 @@
@include('settings.parts.navbar', ['selected' => 'roles'])
</div>
<div class="card content-wrap">
<h1 class="list-heading">{{ trans('settings.role_edit') }}</h1>
<form action="{{ url("/settings/roles/{$role->id}") }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('settings.roles.parts.form', ['model' => $role, 'title' => trans('settings.role_edit'), 'icon' => 'edit'])
{{ csrf_field() }}
{{ method_field('PUT') }}
@include('settings.roles.parts.form', ['role' => $role])
<div class="form-group text-right">
<a href="{{ url("/settings/roles") }}" class="button outline">{{ trans('common.cancel') }}</a>
<a href="{{ url("/settings/roles/new?copy_from={$role->id}") }}" class="button outline">{{ trans('common.copy') }}</a>
<a href="{{ url("/settings/roles/delete/{$role->id}") }}" class="button outline">{{ trans('settings.role_delete') }}</a>
<button type="submit" class="button">{{ trans('settings.role_save') }}</button>
</div>
</form>
</div>
<div class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.role_users') }}</h2>
@if(count($role->users ?? []) > 0)
<div class="grid third">
@foreach($role->users as $user)
<div class="user-list-item">
<div>
<img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}">
</div>
<div>
@if(userCan('users-manage') || user()->id == $user->id)
<a href="{{ url("/settings/users/{$user->id}") }}">
@endif
{{ $user->name }}
@if(userCan('users-manage') || user()->id == $user->id)
</a>
@endif
</div>
</div>
@endforeach
</div>
@else
<p class="text-muted">
{{ trans('settings.role_users_none') }}
</p>
@endif
</div>
</div>
@stop

View File

@ -1,9 +1,4 @@
{!! csrf_field() !!}
<div class="card content-wrap">
<h1 class="list-heading">{{ $title }}</h1>
<div class="setting-list">
<div class="setting-list">
<div class="grid half">
<div>
@ -12,20 +7,20 @@
<div>
<div class="form-group">
<label for="display_name">{{ trans('settings.role_name') }}</label>
@include('form.text', ['name' => 'display_name'])
@include('form.text', ['name' => 'display_name', 'model' => $role])
</div>
<div class="form-group">
<label for="description">{{ trans('settings.role_desc') }}</label>
@include('form.text', ['name' => 'description'])
@include('form.text', ['name' => 'description', 'model' => $role])
</div>
<div class="form-group">
@include('form.checkbox', ['name' => 'mfa_enforced', 'label' => trans('settings.role_mfa_enforced') ])
@include('form.checkbox', ['name' => 'mfa_enforced', 'label' => trans('settings.role_mfa_enforced'), 'model' => $role ])
</div>
@if(in_array(config('auth.method'), ['ldap', 'saml2', 'oidc']))
<div class="form-group">
<label for="name">{{ trans('settings.role_external_auth_id') }}</label>
@include('form.text', ['name' => 'external_auth_id'])
@include('form.text', ['name' => 'external_auth_id', 'model' => $role])
</div>
@endif
</div>
@ -226,42 +221,4 @@
</tr>
</table>
</div>
</div>
<div class="form-group text-right">
<a href="{{ url("/settings/roles") }}" class="button outline">{{ trans('common.cancel') }}</a>
@if (isset($role) && $role->id)
<a href="{{ url("/settings/roles/delete/{$role->id}") }}" class="button outline">{{ trans('settings.role_delete') }}</a>
@endif
<button type="submit" class="button">{{ trans('settings.role_save') }}</button>
</div>
</div>
<div class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.role_users') }}</h2>
@if(count($role->users ?? []) > 0)
<div class="grid third">
@foreach($role->users as $user)
<div class="user-list-item">
<div>
<img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}">
</div>
<div>
@if(userCan('users-manage') || user()->id == $user->id)
<a href="{{ url("/settings/users/{$user->id}") }}">
@endif
{{ $user->name }}
@if(userCan('users-manage') || user()->id == $user->id)
</a>
@endif
</div>
</div>
@endforeach
</div>
@else
<p class="text-muted">
{{ trans('settings.role_users_none') }}
</p>
@endif
</div>

View File

@ -163,6 +163,23 @@ class RolesTest extends TestCase
$this->assertEquals($this->user->id, $roleA->users()->first()->id);
}
public function test_copy_role_button_shown()
{
/** @var Role $role */
$role = Role::query()->first();
$resp = $this->asAdmin()->get("/settings/roles/{$role->id}");
$resp->assertElementContains('a[href$="/roles/new?copy_from=' . $role->id . '"]', 'Copy');
}
public function test_copy_from_param_on_create_prefills_with_other_role_data()
{
/** @var Role $role */
$role = Role::query()->first();
$resp = $this->asAdmin()->get("/settings/roles/new?copy_from={$role->id}");
$resp->assertOk();
$resp->assertElementExists('input[name="display_name"][value="' . ($role->display_name . ' (Copy)') . '"]');
}
public function test_manage_user_permission()
{
$this->actingAs($this->user)->get('/settings/users')->assertRedirect('/');