Make a hierarchy in roles

This is more understandable for users.
This commit is contained in:
Julien Fastré 2017-04-19 21:24:35 +02:00
parent b6d1f05e00
commit 0e5ab47474
7 changed files with 190 additions and 33 deletions

View File

@ -108,6 +108,8 @@ class PermissionsGroupController extends Controller
$translatableStringHelper = $this->get('chill.main.helper.translatable_string');
$roleScopes = $permissionsGroup->getRoleScopes()->toArray();
// sort $roleScopes by name
usort($roleScopes,
function(RoleScope $a, RoleScope $b) use ($translatableStringHelper) {
if ($a->getScope() === NULL) {
@ -122,10 +124,21 @@ class PermissionsGroupController extends Controller
$translatableStringHelper->localize($b->getScope()->getName())
);
});
// sort role scope by title
/* @var $roleProvider \Chill\MainBundle\Security\RoleProvider */
$roleProvider = $this->get('chill.main.role_provider');
$roleScopesSorted = array();
foreach($roleScopes as $roleScope) {
/* @var $roleScope RoleScope */
$title = $roleProvider->getRoleTitle($roleScope->getRole());
$roleScopesSorted[$title][] = $roleScope;
}
ksort($roleScopesSorted);
return $this->render('ChillMainBundle:PermissionsGroup:show.html.twig', array(
'entity' => $permissionsGroup,
'role_scopes' => $roleScopes,
'role_scopes_sorted' => $roleScopesSorted,
'expanded_roles' => $this->getExpandedRoles($roleScopes)
));
}
@ -171,6 +184,7 @@ class PermissionsGroupController extends Controller
throw $this->createNotFoundException('Unable to find PermissionsGroup entity.');
}
// create all the forms
$editForm = $this->createEditForm($permissionsGroup);
$deleteRoleScopesForm = array();
@ -180,9 +194,21 @@ class PermissionsGroupController extends Controller
}
$addRoleScopesForm = $this->createAddRoleScopeForm($permissionsGroup);
// sort role scope by title
/* @var $roleProvider \Chill\MainBundle\Security\RoleProvider */
$roleProvider = $this->get('chill.main.role_provider');
$roleScopesSorted = array();
foreach($permissionsGroup->getRoleScopes()->toArray() as $roleScope) {
/* @var $roleScope RoleScope */
$title = $roleProvider->getRoleTitle($roleScope->getRole());
$roleScopesSorted[$title][] = $roleScope;
}
ksort($roleScopesSorted);
return $this->render('ChillMainBundle:PermissionsGroup:edit.html.twig', array(
'entity' => $permissionsGroup,
'role_scopes_sorted' => $roleScopesSorted,
'edit_form' => $editForm->createView(),
'expanded_roles' => $this->getExpandedRoles($permissionsGroup->getRoleScopes()->toArray()),
'delete_role_scopes_form' => array_map( function($form) {

View File

@ -56,12 +56,20 @@ class ComposedRoleScopeType extends AbstractType
*/
private $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper,
RoleProvider $roleProvider)
{
/**
*
* @var RoleProvider
*/
private $roleProvider;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
RoleProvider $roleProvider
) {
$this->roles = $roleProvider->getRoles();
$this->rolesWithoutScope = $roleProvider->getRolesWithoutScopes();
$this->translatableStringHelper = $translatableStringHelper;
$this->roleProvider = $roleProvider;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@ -86,6 +94,9 @@ class ComposedRoleScopeType extends AbstractType
} else {
return array('data-has-scope' => '1');
}
},
'group_by' => function($role, $key, $index) {
return $this->roleProvider->getRoleTitle($role);
}
))
->add('scope', 'entity', array(

View File

@ -15,6 +15,10 @@
<h2>{{ 'Grant those permissions'|trans }} :</h2>
{%- if entity.getRoleScopes|length > 0 -%}
{% for title, role_scopes in role_scopes_sorted %}
<h3>{{ title|default("Unclassified")|trans }}</h3>
<table class="striped rounded">
<thead>
<tr>
@ -25,7 +29,7 @@
</thead>
<tbody>
{% for role_scope in entity.getRoleScopes %}
{% for role_scope in role_scopes %}
<tr>
<td>
<span class="role_scope role">{{ role_scope.role|trans }}</span>
@ -53,6 +57,7 @@
{% endfor %}
</tbody>
</table>
{% endfor %}
{%- else -%}
<p>{{ 'This group does not provide any permission'|trans }}</p>
@ -64,15 +69,24 @@
{{ form_errors(add_role_scopes_form) }}
{{ form_row(add_role_scopes_form.composed_role_scope.role) }}
{{ form_row(add_role_scopes_form.composed_role_scope.scope) }}
{{ form_row(add_role_scopes_form.submit, { 'attr' : { 'class': 'sc-button green' } } ) }}
<ul class="record_actions">
<li>
{{ form_row(add_role_scopes_form.submit, { 'attr' : { 'class': 'sc-button bt-create' } } ) }}
</li>
<li>
<a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}" class="sc-button bt-see">{{ 'Cancel'|trans }}</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup') }}" class="sc-button bt-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
{{ form_end(add_role_scopes_form) }}
<ul class="record_actions">
<li>
<a href="{{ path('admin_permissionsgroup') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -17,12 +17,12 @@
<tr>
<td><a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}">{{ entity.name }}</a></td>
<td>
<ul>
<ul class="record_actions">
<li>
<a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}">{{ 'show'|trans }}</a>
<a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}" class="sc-button bt-see">{{ 'See'|trans }}</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup_edit', { 'id': entity.id }) }}">{{ 'edit'|trans }}</a>
<a href="{{ path('admin_permissionsgroup_edit', { 'id': entity.id }) }}" class="sc-button bt-edit">{{ 'Edit'|trans }}</a>
</li>
</ul>
</td>
@ -31,9 +31,9 @@
</tbody>
</table>
<ul>
<ul class="record_actions">
<li>
<a href="{{ path('admin_permissionsgroup_new') }}">
<a href="{{ path('admin_permissionsgroup_new') }}" class="sc-button bt-create">
{{ 'Create a new permissions group'| trans }}
</a>
</li>

View File

@ -13,9 +13,11 @@
</tr>
</tbody>
</table>
{% if role_scopes|length > 0 %}
{% if role_scopes_sorted|length > 0 %}
<h2>{{ 'Grant those permissions'|trans }}&nbsp;:</h2>
{% for title, role_scopes in role_scopes_sorted %}
<h3>{{ title|default('Unclassified')|trans }}</h3>
<table class="striped rounded">
<thead>
<tr>
@ -24,6 +26,7 @@
</tr>
</thead>
<tbody>
{% for role_scope in role_scopes %}
<tr>
<td>
@ -43,6 +46,7 @@
{% endfor %}
</tbody>
</table>
{% endfor %}
{% else %}
@ -51,16 +55,18 @@
{{ 'add permissions'|trans|capitalize }}</a></p>
{% endif %}
<ul class="record_actions">
<li>
<a href="{{ path('admin_permissionsgroup') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup_edit', { 'id': entity.id }) }}">
{{ 'Edit'|trans }}
</a>
</li>
</ul>
<ul class="record_actions">
<li>
<a href="{{ path('admin_permissionsgroup_edit', { 'id': entity.id }) }}" class="sc-button bt-edit">
{{ 'Edit'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup') }}" class="sc-button bt-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,42 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Security;
/**
* Give a hierarchy for the role.
*
* This hierarchy allow to sort roles, which is useful in UI
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
interface ProvideRoleHierarchyInterface extends ProvideRoleInterface
{
/**
* Return an array of roles, where keys are the hierarchy, and values
* an array of roles.
*
* Example:
*
* ```
* [ 'Title' => [ 'CHILL_FOO_SEE', 'CHILL_FOO_UPDATE' ] ]
* ```
*
* @return array where keys are the hierarchy, and values an array of roles: `[ 'title' => [ 'CHILL_FOO_SEE', 'CHILL_FOO_UPDATE' ] ]`
*/
public function getRolesWithHierarchy();
}

View File

@ -32,6 +32,16 @@ class RoleProvider
*/
private $providers = array();
/**
* an array where keys are the role, and value is the title
* for the given role.
*
* Null when not initialized.
*
* @var array|null
*/
private $rolesTitlesCache = null;
/**
* Add a role provider
*
@ -75,4 +85,52 @@ class RoleProvider
return $roles;
}
/**
* initialize the array for caching role and titles
*
*/
private function initializeRolesTitlesCache()
{
// break if already initialized
if ($this->rolesTitlesCache !== null) {
return;
}
foreach ($this->providers as $provider) {
if ($provider instanceof ProvideRoleHierarchyInterface) {
foreach ($provider->getRolesWithHierarchy() as $title => $roles) {
foreach($roles as $role) {
$this->rolesTitlesCache[$role] = $title;
}
}
} else {
if ($provider->getRoles() !== null) {
$this->rolesTitlesCache = \array_merge(
$this->rolesTitlesCache,
\array_fill_keys($provider->getRoles(), null)
);
}
}
}
}
/**
* Get the title for each role.
*
* @param string $role
* @return string the title of the role
*/
public function getRoleTitle($role)
{
$this->initializeRolesTitlesCache();
if (! \array_key_exists($role, $this->rolesTitlesCache)) {
// this case might happens when the role is not described in
// `getRolesWithHierarchy`
return null;
}
return $this->rolesTitlesCache[$role];
}
}