Bladeren bron

Merge pull request #1405 from tiagojsag/dashboard_group_role

Group visibility by Role
Thomas 11 jaren geleden
bovenliggende
commit
aa4fc6a5d0

+ 8 - 2
DependencyInjection/Compiler/AddDependencyCallsCompilerPass.php

@@ -68,7 +68,8 @@ class AddDependencyCallsCompilerPass implements CompilerPassInterface
                 if (!isset($groupDefaults[$groupName])) {
                 if (!isset($groupDefaults[$groupName])) {
                     $groupDefaults[$groupName] = array(
                     $groupDefaults[$groupName] = array(
                         'label'           => $groupName,
                         'label'           => $groupName,
-                        'label_catalogue' => $labelCatalogue
+                        'label_catalogue' => $labelCatalogue,
+                        'roles' => array()
                     );
                     );
                 }
                 }
 
 
@@ -84,7 +85,8 @@ class AddDependencyCallsCompilerPass implements CompilerPassInterface
                 if (!isset($groupDefaults[$groupName])) {
                 if (!isset($groupDefaults[$groupName])) {
                     $groupDefaults[$groupName] = array(
                     $groupDefaults[$groupName] = array(
                         'items' => array(),
                         'items' => array(),
-                        'label' => $groupName
+                        'label' => $groupName,
+                        'roles' => array()
                     );
                     );
                 }
                 }
 
 
@@ -103,6 +105,10 @@ class AddDependencyCallsCompilerPass implements CompilerPassInterface
                 if (!empty($group['item_adds'])) {
                 if (!empty($group['item_adds'])) {
                     $groups[$groupName]['items'] = array_merge($groups[$groupName]['items'], $group['item_adds']);
                     $groups[$groupName]['items'] = array_merge($groups[$groupName]['items'], $group['item_adds']);
                 }
                 }
+
+                if (empty($group['roles'])) {
+                    $groups[$groupName]['roles'] = $groupDefaults[$groupName]['roles'];
+                }
             }
             }
         } else {
         } else {
             $groups = $groupDefaults;
             $groups = $groupDefaults;

+ 3 - 0
DependencyInjection/Configuration.php

@@ -97,6 +97,9 @@ class Configuration implements ConfigurationInterface
                                     ->arrayNode('item_adds')
                                     ->arrayNode('item_adds')
                                         ->prototype('scalar')->end()
                                         ->prototype('scalar')->end()
                                     ->end()
                                     ->end()
+                                    ->arrayNode('roles')
+                                        ->prototype('scalar')->defaultValue(array())->end()
+                                    ->end()
                                 ->end()
                                 ->end()
                             ->end()
                             ->end()
                         ->end()
                         ->end()

+ 6 - 1
Resources/doc/reference/dashboard.rst

@@ -128,6 +128,7 @@ configuration method overrides the configuration defined as part of the
                     items: ~
                     items: ~
                     item_adds:
                     item_adds:
                         - sonata.admin.page
                         - sonata.admin.page
+                    roles: [ ROLE_ONE, ROLE_TWO ]
                 acme.admin.group.misc: ~
                 acme.admin.group.misc: ~
 
 
 .. note::
 .. note::
@@ -147,6 +148,10 @@ declared as belonging to this group will not be displayed here.
 Secondly, we declare a ``acme.admin.group.blog`` as having all its default items 
 Secondly, we declare a ``acme.admin.group.blog`` as having all its default items 
 (by default we mean the ones specified in the ``Admin`` services declaration), plus
 (by default we mean the ones specified in the ``Admin`` services declaration), plus
 an additional ``sonata.admin.page`` mapping, that was not initially part of this group.
 an additional ``sonata.admin.page`` mapping, that was not initially part of this group.
+We also use the ``roles`` option here, used to specify that , instead of being visible
+to everyone, only users with ``ROLE_ONE`` or ``ROLE_TWO`` will be able to see this group. 
+Users with ``ROLE_SUPER_ADMIN`` are always able to see groups that would otherwise be
+hidden by this configuration option. 
 
 
 The third group keeps all the default values, as declared on the ``Admin`` service
 The third group keeps all the default values, as declared on the ``Admin`` service
 declaration.
 declaration.
@@ -214,4 +219,4 @@ which can be configured to better fit this scenario.
 In this example, you would have two ``Admin`` list blocks on your dashboard, each of
 In this example, you would have two ``Admin`` list blocks on your dashboard, each of
 them containing just the respectively configured groups.
 them containing just the respectively configured groups.
 
 
-.. _`documentation page`:  http://sonata-project.org/bundles/block/master/doc/index.html
+.. _`documentation page`:  http://sonata-project.org/bundles/block/master/doc/index.html

+ 52 - 45
Resources/views/Block/block_admin_list.html.twig

@@ -13,53 +13,60 @@ file that was distributed with this source code.
 
 
 {% block block %}
 {% block block %}
     {% for group in groups %}
     {% for group in groups %}
-        <table class="table table-bordered table-striped sonata-ba-list">
-            <thead>
-                <tr>
-                    <th colspan="3">{{ group.label|trans({}, group.label_catalogue) }}</th>
-                </tr>
-            </thead>
+        {% set display = (group.roles is empty or is_granted('ROLE_SUPER_ADMIN') ) %}
+        {% for role in group.roles if not display %}
+            {% set display = is_granted(role)%}
+        {% endfor %}
 
 
-            <tbody>
-                {% for admin in group.items %}
-                    {% if admin.hasroute('create') and admin.isGranted('CREATE') or admin.hasroute('list') and admin.isGranted('LIST') %}
-                        <tr>
-                            <td class="sonata-ba-list-label">{{ admin.label|trans({}, admin.translationdomain) }}</td>
-                            <td>
-                                <div class="btn-group">
-                                    {% if admin.hasroute('create') and admin.isGranted('CREATE') %}
-                                        {% if admin.subClasses is empty %}
-                                            <a class="btn btn-small" href="{{ admin.generateUrl('create')}}">
-                                                <i class="icon-plus"></i>
-                                                {% trans from 'SonataAdminBundle' %}link_add{% endtrans %}
-                                            </a>
-                                        {% else %}
-                                            <a class="btn btn-small dropdown-toggle" data-toggle="dropdown" href="#">
-                                                <i class="icon-plus"></i>
-                                                {% trans from 'SonataAdminBundle' %}link_add{% endtrans %}
-                                                <span class="caret"></span>
+        {% if display %}
+            <table class="table table-bordered table-striped sonata-ba-list">
+                <thead>
+                    <tr>
+                        <th colspan="3">{{ group.label|trans({}, group.label_catalogue) }}</th>
+                    </tr>
+                </thead>
+
+                <tbody>
+                    {% for admin in group.items %}
+                        {% if admin.hasroute('create') and admin.isGranted('CREATE') or admin.hasroute('list') and admin.isGranted('LIST') %}
+                            <tr>
+                                <td class="sonata-ba-list-label">{{ admin.label|trans({}, admin.translationdomain) }}</td>
+                                <td>
+                                    <div class="btn-group">
+                                        {% if admin.hasroute('create') and admin.isGranted('CREATE') %}
+                                            {% if admin.subClasses is empty %}
+                                                <a class="btn btn-small" href="{{ admin.generateUrl('create')}}">
+                                                    <i class="icon-plus"></i>
+                                                    {% trans from 'SonataAdminBundle' %}link_add{% endtrans %}
+                                                </a>
+                                            {% else %}
+                                                <a class="btn btn-small dropdown-toggle" data-toggle="dropdown" href="#">
+                                                    <i class="icon-plus"></i>
+                                                    {% trans from 'SonataAdminBundle' %}link_add{% endtrans %}
+                                                    <span class="caret"></span>
+                                                </a>
+                                                <ul class="dropdown-menu">
+                                                    {% for subclass in admin.subclasses|keys %}
+                                                    <li>
+                                                        <a href="{{ admin.generateUrl('create', {'subclass': subclass}) }}">{{ subclass }}</a>
+                                                    </li>
+                                                    {% endfor %}
+                                                </ul>
+                                            {% endif %}
+                                        {% endif %}
+                                        {% if admin.hasroute('list') and admin.isGranted('LIST') %}
+                                            <a class="btn btn-small" href="{{ admin.generateUrl('list')}}">
+                                                <i class="icon-list"></i>
+                                                {% trans from 'SonataAdminBundle' %}link_list{% endtrans -%}
                                             </a>
                                             </a>
-                                            <ul class="dropdown-menu">
-                                                {% for subclass in admin.subclasses|keys %}
-                                                <li>
-                                                    <a href="{{ admin.generateUrl('create', {'subclass': subclass}) }}">{{ subclass }}</a>
-                                                </li>
-                                                {% endfor %}
-                                            </ul>
                                         {% endif %}
                                         {% endif %}
-                                    {% endif %}
-                                    {% if admin.hasroute('list') and admin.isGranted('LIST') %}
-                                        <a class="btn btn-small" href="{{ admin.generateUrl('list')}}">
-                                            <i class="icon-list"></i>
-                                            {% trans from 'SonataAdminBundle' %}link_list{% endtrans -%}
-                                        </a>
-                                    {% endif %}
-                                </div>
-                            </td>
-                        </tr>
-                    {% endif %}
-                {% endfor %}
-            </tbody>
-        </table>
+                                    </div>
+                                </td>
+                            </tr>
+                        {% endif %}
+                    {% endfor %}
+                </tbody>
+            </table>
+        {% endif %}
     {% endfor %}
     {% endfor %}
 {% endblock %}
 {% endblock %}

+ 7 - 0
Resources/views/standard_layout.html.twig

@@ -103,6 +103,12 @@ file that was distributed with this source code.
                                     {% block sonata_top_bar_nav %}
                                     {% block sonata_top_bar_nav %}
                                         {% if app.security.token and is_granted('ROLE_SONATA_ADMIN') %}
                                         {% if app.security.token and is_granted('ROLE_SONATA_ADMIN') %}
                                             {% for group in admin_pool.dashboardgroups %}
                                             {% for group in admin_pool.dashboardgroups %}
+                                                {% set display = (group.roles is empty or is_granted('ROLE_SUPER_ADMIN') ) %}
+                                                {% for role in group.roles if not display %}
+                                                    {% set display = is_granted(role)%}
+                                                {% endfor %}
+
+                                                {% if display %}
                                                 <li class="dropdown">
                                                 <li class="dropdown">
                                                     <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ group.label|trans({}, group.label_catalogue) }} <span class="caret"></span></a>
                                                     <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ group.label|trans({}, group.label_catalogue) }} <span class="caret"></span></a>
                                                     <ul class="dropdown-menu">
                                                     <ul class="dropdown-menu">
@@ -113,6 +119,7 @@ file that was distributed with this source code.
                                                         {% endfor %}
                                                         {% endfor %}
                                                     </ul>
                                                     </ul>
                                                 </li>
                                                 </li>
+                                                {% endif %}
                                             {% endfor %}
                                             {% endfor %}
                                         {% endif %}
                                         {% endif %}
                                     {% endblock %}
                                     {% endblock %}

+ 206 - 0
Tests/DependencyInjection/Compiler/AddDependencyCallsCompilerPassTest.php

@@ -0,0 +1,206 @@
+<?php
+
+/*
+ * This file is part of the Sonata package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Tests\DependencyInjection;
+
+use Sonata\AdminBundle\DependencyInjection\SonataAdminExtension;
+use Sonata\AdminBundle\DependencyInjection\Compiler\AddDependencyCallsCompilerPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @author     Tiago Garcia
+ */
+class AddDependencyCallsCompilerPassTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var SonataAdminExtension $extension */
+    private $extension;
+    
+    /** @var array $config */
+    private $config;
+
+    public function setUp()
+    {
+        parent::setUp();
+
+        $this->extension = new SonataAdminExtension();
+        $this->config    = $this->getConfig();
+    }
+
+    /**
+     * @covers Sonata\AdminBundle\DependencyInjection\Compiler\AddDependencyCallsCompilerPass::process
+     */
+    public function testProcessParsingFullValidConfig()
+    {
+        $container = $this->getContainer();
+        $this->extension->load(array($this->config), $container);
+
+        $compilerPass = new AddDependencyCallsCompilerPass();
+        $compilerPass->process($container);
+        $container->compile();
+        
+        $this->assertTrue($container->hasParameter('sonata.admin.configuration.dashboard_groups'));
+        
+        $dashboardGroupsSettings = $container->getParameter('sonata.admin.configuration.dashboard_groups');
+
+        $this->assertArrayHasKey('sonata_group_one', $dashboardGroupsSettings);
+
+        $this->assertArrayHasKey('label', $dashboardGroupsSettings['sonata_group_one']);
+        $this->assertArrayHasKey('label_catalogue', $dashboardGroupsSettings['sonata_group_one']);
+        $this->assertArrayHasKey('items', $dashboardGroupsSettings['sonata_group_one']);
+        $this->assertArrayHasKey('item_adds', $dashboardGroupsSettings['sonata_group_one']);
+        $this->assertArrayHasKey('roles', $dashboardGroupsSettings['sonata_group_one']);
+
+        $this->assertEquals('Group One Label', $dashboardGroupsSettings['sonata_group_one']['label']);
+        $this->assertEquals('SonataAdminBundle', $dashboardGroupsSettings['sonata_group_one']['label_catalogue']);
+        $this->assertContains('sonata_post_admin', $dashboardGroupsSettings['sonata_group_one']['items']);
+        $this->assertContains('sonata_news_admin', $dashboardGroupsSettings['sonata_group_one']['item_adds']);
+        $this->assertContains('ROLE_ONE', $dashboardGroupsSettings['sonata_group_one']['roles']);
+    }
+
+    /**
+     * @covers Sonata\AdminBundle\DependencyInjection\Compiler\AddDependencyCallsCompilerPass::process
+     */
+    public function testProcessResultingConfig()
+    {
+        $container = $this->getContainer();
+        $this->extension->load(array($this->config), $container);
+
+        $compilerPass = new AddDependencyCallsCompilerPass();
+        $compilerPass->process($container);
+        $container->compile();
+        
+        $this->assertTrue($container->hasDefinition('sonata.admin.pool'));
+        $this->assertTrue($container->hasDefinition('sonata_post_admin'));
+        $this->assertTrue($container->hasDefinition('sonata_article_admin'));
+        $this->assertTrue($container->hasDefinition('sonata_news_admin'));
+        
+        $pool = $container->get('sonata.admin.pool');
+        $adminServiceIds = $pool->getAdminServiceIds();
+        $adminGroups = $pool->getAdminGroups();
+        $adminClasses = $pool->getAdminClasses();
+
+        $this->assertContains('sonata_post_admin', $adminServiceIds);
+        $this->assertContains('sonata_article_admin', $adminServiceIds);
+        $this->assertContains('sonata_news_admin', $adminServiceIds);
+        
+        $this->assertArrayHasKey('sonata_group_one', $adminGroups);
+        $this->assertArrayHasKey('label', $adminGroups['sonata_group_one']);
+        $this->assertArrayHasKey('label_catalogue', $adminGroups['sonata_group_one']);
+        $this->assertArrayHasKey('items', $adminGroups['sonata_group_one']);
+        $this->assertArrayHasKey('item_adds', $adminGroups['sonata_group_one']);
+        $this->assertArrayHasKey('roles', $adminGroups['sonata_group_one']);
+        $this->assertEquals('Group One Label', $adminGroups['sonata_group_one']['label']);
+        $this->assertEquals('SonataAdminBundle', $adminGroups['sonata_group_one']['label_catalogue']);
+        $this->assertContains('sonata_post_admin', $adminGroups['sonata_group_one']['items']);
+        $this->assertContains('sonata_news_admin', $adminGroups['sonata_group_one']['items']);
+        $this->assertContains('sonata_news_admin', $adminGroups['sonata_group_one']['item_adds']);
+        $this->assertFalse(in_array('sonata_article_admin', $adminGroups['sonata_group_one']['items']));
+        $this->assertContains('ROLE_ONE', $adminGroups['sonata_group_one']['roles']);
+
+        $this->assertArrayHasKey('Sonata\AdminBundle\Tests\DependencyInjection\Post', $adminClasses);
+        $this->assertContains('sonata_post_admin', $adminClasses['Sonata\AdminBundle\Tests\DependencyInjection\Post']);
+        $this->assertArrayHasKey('Sonata\AdminBundle\Tests\DependencyInjection\Article', $adminClasses);
+        $this->assertContains('sonata_article_admin', $adminClasses['Sonata\AdminBundle\Tests\DependencyInjection\Article']);
+        $this->assertArrayHasKey('Sonata\AdminBundle\Tests\DependencyInjection\News', $adminClasses);
+        $this->assertContains('sonata_news_admin', $adminClasses['Sonata\AdminBundle\Tests\DependencyInjection\News']);
+    }
+
+    /**
+     * @return array
+     */
+    protected function getConfig()
+    {
+        $config = array(
+            'dashboard' => array(
+                'groups' => array(
+                    'sonata_group_one' => array(
+                        'label' => 'Group One Label',
+                        'label_catalogue' => 'SonataAdminBundle',
+                        'items' => array(
+                            'sonata_post_admin'
+                        ),
+                        'item_adds' => array(
+                            'sonata_news_admin'
+                        ),
+                        'roles' => array('ROLE_ONE'),
+                    ),
+                )
+            )
+        );
+        return $config;
+    }
+    
+    private function getContainer()
+    {
+        $container = new ContainerBuilder();
+        $container->setParameter('kernel.bundles', array());
+
+        // Add dependencies for SonataAdminBundle (these services will never get called so dummy classes will do)
+        $container
+            ->register('twig')
+            ->setClass('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface');
+        $container
+            ->register('templating')
+            ->setClass('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface');
+        $container
+            ->register('translator')
+            ->setClass('Symfony\Bundle\FrameworkBundle\Translation\TranslatorInterface');
+        $container
+            ->register('validator')
+            ->setClass('Symfony\Bundle\FrameworkBundle\Validator\Validator');
+        $container
+            ->register('validator.validator_factory')
+            ->setClass('Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory');
+        $container
+            ->register('router')
+            ->setClass('Symfony\Component\Routing\RouterInterface');
+        $container
+            ->register('form.factory')
+            ->setClass('Symfony\Component\Form\FormFactoryInterface');
+        $container
+            ->register('sonata.admin.manager.orm')
+            ->setClass('Sonata\DoctrineORMAdminBundle\Model\ModelManager');
+        $container
+            ->register('sonata.admin.builder.orm_form')
+            ->setClass('Sonata\DoctrineORMAdminBundle\Builder\FormContractor');
+        $container
+            ->register('sonata.admin.builder.orm_show')
+            ->setClass('Sonata\DoctrineORMAdminBundle\Builder\ShowBuilder');
+        $container
+            ->register('sonata.admin.builder.orm_list')
+            ->setClass('Sonata\DoctrineORMAdminBundle\Builder\ListBuilder');
+        $container
+            ->register('sonata.admin.builder.orm_datagrid')
+            ->setClass('Sonata\DoctrineORMAdminBundle\Builder\DatagridBuilder');
+        $container
+            ->register('knp_menu.factory')
+            ->setClass('Knp\Menu\Silex\RouterAwareFactory');
+
+        // Add admin definition's
+        $container
+            ->register('sonata_post_admin')
+            ->setClass('Sonata\AdminBundle\Tests\DependencyInjection\MockAdmin')
+            ->setArguments(array('', 'Sonata\AdminBundle\Tests\DependencyInjection\Post', 'SonataAdminBundle:CRUD'))
+            ->addTag('sonata.admin', array('group' => 'sonata_group_one', 'manager_type' => 'orm'));
+        $container
+            ->register('sonata_news_admin')
+            ->setClass('Sonata\AdminBundle\Tests\DependencyInjection\MockAdmin')
+            ->setArguments(array('', 'Sonata\AdminBundle\Tests\DependencyInjection\News', 'SonataAdminBundle:CRUD'))
+            ->addTag('sonata.admin', array('group' => 'sonata_group_two', 'manager_type' => 'orm'));
+        $container
+            ->register('sonata_article_admin')
+            ->setClass('Sonata\AdminBundle\Tests\DependencyInjection\MockAdmin')
+            ->setArguments(array('', 'Sonata\AdminBundle\Tests\DependencyInjection\Article', 'SonataAdminBundle:CRUD'))
+            ->addTag('sonata.admin', array('group' => 'sonata_group_one', 'manager_type' => 'orm'));
+
+        return $container;
+    }
+}