Skip to content

Commit

Permalink
new RolesMatrixType to display available actions per admin as matrix (#…
Browse files Browse the repository at this point in the history
…1005)

Role permissions can now be displayed in a matrix view using the Sonata\UserBundle\Form\Type\RolesMatrixType
  • Loading branch information
Silas Joisten authored and OskarStark committed May 8, 2018
1 parent cfd6e7e commit 3ede651
Show file tree
Hide file tree
Showing 26 changed files with 1,833 additions and 3 deletions.
Binary file added docs/images/roles_matrix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ User Bundle
reference/two_step_validation
reference/api
reference/user_impersonation
reference/roles_matrix
57 changes: 57 additions & 0 deletions docs/reference/roles_matrix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Roles Matrix
============

The ``Sonata\UserBundle\Form\Type\RolesMatrixType`` was built to show all
roles in a matrix view.


.. figure:: ../images/roles_matrix.png
:align: center
:alt: Role matrix
:width: 700px

Every admin has defined default roles like:

- EDIT
- LIST
- CREATE
- VIEW
- DELETE
- EXPORT
- ALL

The roles matrix consists of two parts:

1. one that shows the matrix with each admin and their permissions.
2. another that shows the custom roles which are configured in
``security.yml`` and lists them as checkboxes (and shows their
inherited roles).

.. note::

The user can just use roles which he is granted.

How to exclude an admin
-----------------------

You can set the ``show_in_roles_matrix`` option to ``false``, like this:

.. configuration-block::

.. code-block:: yaml
# config/services.yaml
services:
app.admin.post:
class: App\Admin\PostAdmin
tags:
- name: sonata.admin
manager_type: orm
label: "Post"
show_in_roles_matrix: false
arguments:
- ~
- App\Entity\Post
- ~
public: true
37 changes: 37 additions & 0 deletions src/DependencyInjection/Compiler/RolesMatrixCompilerPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\UserBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* @author Christian Gripp <[email protected]>
* @author Cengizhan Çalışkan <[email protected]>
* @author Silas Joisten <[email protected]>
*/
final class RolesMatrixCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
foreach ($container->findTaggedServiceIds('sonata.admin') as $name => $items) {
foreach ($items as $item) {
if (($item['show_in_roles_matrix'] ?? true) === false) {
$container->getDefinition('sonata.user.admin_roles_builder')
->addMethodCall('addExcludeAdmin', [$name]);
}
}
}
}
}
103 changes: 103 additions & 0 deletions src/Form/Type/RolesMatrixType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\UserBundle\Form\Type;

use Sonata\UserBundle\Security\RolesBuilder\ExpandableRolesBuilderInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @author Silas Joisten <[email protected]>
*/
final class RolesMatrixType extends AbstractType
{
/**
* @var ExpandableRolesBuilderInterface
*/
private $rolesBuilder;

public function __construct(ExpandableRolesBuilderInterface $rolesBuilder)
{
$this->rolesBuilder = $rolesBuilder;
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'expanded' => true,
'choices' => function (Options $options, $parentChoices): array {
if (!empty($parentChoices)) {
return [];
}

$roles = $this->rolesBuilder->getRoles(
$options['choice_translation_domain'],
$options['expanded']
);
$roles = array_keys($roles);

return array_combine($roles, $roles);
},
'choice_translation_domain' => function (Options $options, $value): ?string {
// if choice_translation_domain is true, then it's the same as translation_domain
if (true === $value) {
$value = $options['translation_domain'];
}
if (null === $value) {
// no translation domain yet, try to ask sonata admin
$admin = null;
if (isset($options['sonata_admin'])) {
$admin = $options['sonata_admin'];
}
if (null === $admin && isset($options['sonata_field_description'])) {
$admin = $options['sonata_field_description']->getAdmin();
}
if (null !== $admin) {
$value = $admin->getTranslationDomain();
}
}

return $value;
},

'data_class' => null,
]);

// Symfony 2.8 BC
if (method_exists(FormTypeInterface::class, 'setDefaultOptions')) {
$resolver->setDefault('choices_as_values', true);
}
}

public function getParent(): string
{
return ChoiceType::class;
}

public function getBlockPrefix(): string
{
return 'sonata_roles_matrix';
}

public function getName(): string
{
return $this->getBlockPrefix();
}
}
24 changes: 22 additions & 2 deletions src/Resources/config/admin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.editable_role_builder" class="Sonata\UserBundle\Security\EditableRolesBuilder">
<service id="sonata.user.editable_role_builder" class="Sonata\UserBundle\Security\EditableRolesBuilder" public="false">
<argument type="service" id="security.token_storage"/>
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
Expand All @@ -10,9 +10,29 @@
<argument type="service" id="translator"/>
</call>
</service>
<service id="sonata.user.form.type.security_roles" class="Sonata\UserBundle\Form\Type\SecurityRolesType">
<service id="sonata.user.form.type.security_roles" class="Sonata\UserBundle\Form\Type\SecurityRolesType" public="false">
<tag name="form.type" alias="sonata_security_roles"/>
<argument type="service" id="sonata.user.editable_role_builder"/>
</service>
<service id="sonata.user.matrix_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\MatrixRolesBuilder" public="false">
<argument type="service" id="security.token_storage"/>
<argument type="service" id="sonata.user.admin_roles_builder"/>
<argument type="service" id="sonata.user.security_roles_builder"/>
</service>
<service id="sonata.user.admin_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\AdminRolesBuilder" public="false">
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
<argument type="service" id="translator"/>
</service>
<service id="sonata.user.security_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\SecurityRolesBuilder" public="false">
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
<argument type="service" id="translator"/>
<argument>%security.role_hierarchy.roles%</argument>
</service>
<service id="sonata.user.form.roles_matrix_type" class="Sonata\UserBundle\Form\Type\RolesMatrixType">
<tag name="form.type"/>
<argument type="service" id="sonata.user.matrix_roles_builder"/>
</service>
</services>
</container>
6 changes: 5 additions & 1 deletion src/Resources/config/twig.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.twig.global" class="Sonata\UserBundle\Twig\GlobalVariables">
<service id="sonata.user.twig.global" class="Sonata\UserBundle\Twig\GlobalVariables" public="false">
<argument type="service" id="service_container"/>
</service>
<service id="sonata.user.roles_matrix_extension" class="Sonata\UserBundle\Twig\RolesMatrixExtension" public="false">
<argument type="service" id="sonata.user.matrix_roles_builder"/>
<tag name="twig.extension"/>
</service>
</services>
</container>
9 changes: 9 additions & 0 deletions src/Resources/views/Form/form_admin_fields.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ file that was distributed with this source code.
{% endif %}
{% endspaceless %}
{% endblock sonata_security_roles_widget %}

{% block sonata_roles_matrix_widget %}
{% spaceless %}
{{ renderMatrix(form)|raw }}
<ul class="list-unstyled">
{{ renderRolesList(form)|raw }}
</ul>
{% endspaceless %}
{% endblock sonata_roles_matrix_widget %}
41 changes: 41 additions & 0 deletions src/Resources/views/Form/roles_matrix.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{#
This file is part of the Sonata package.
(c) Thomas Rabaix <[email protected]>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
<table class="table table-condensed">
<thead>
<tr>
<th></th>
{% for label in permission_labels|sort %}
<th> {{ label }} </th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for admin_label, roles in grouped_roles %}
<tr>
<th>{{ admin_label }}</th>
{% for role, attributes in roles|sort %}
<td>
{{ form_widget(attributes.form, { label: false }) }}
{% if not attributes.is_granted %}
<script>
$('input[value="{{ role }}"]').iCheck('disable');
$('form').on('submit', function() {
$('input[value="{{ role }}"]').iCheck('enable');
});
</script>
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>

21 changes: 21 additions & 0 deletions src/Resources/views/Form/roles_matrix_list.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{#
This file is part of the Sonata package.
(c) Thomas Rabaix <[email protected]>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% for role, attributes in roles|sort %}
<li>{{ form_widget(attributes.form, {label: attributes.role_translated, value: attributes.role}) }}</li>
{% if not attributes.is_granted %}
<script>
$('input[value="{{ role }}"]').iCheck('disable');
$('form').on('submit', function() {
$('input[value="{{ role }}"]').iCheck('enable');
});
</script>
{% endif %}
{% endfor %}
Loading

0 comments on commit 3ede651

Please sign in to comment.