From 95145171da05a9669f084a55a646e6ea631b6a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Apr 2017 17:29:25 +0200 Subject: [PATCH] add a `ScopePickerType` and a `UserPickerType` Those type allow easily to - pick a Scope/circle in form - pick a user in form --- Entity/PermissionsGroup.php | 7 + Entity/RoleScope.php | 7 + Form/Type/ScopePickerType.php | 124 ++++++++++++++++++ Form/Type/UserPickerType.php | 114 ++++++++++++++++ Resources/config/doctrine/GroupCenter.orm.yml | 6 +- .../config/doctrine/PermissionsGroup.orm.yml | 5 + Resources/config/doctrine/RoleScope.orm.yml | 4 + Resources/config/doctrine/User.orm.yml | 1 + Resources/config/services/form.yml | 19 +++ Resources/config/services/repositories.yml | 6 + 10 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 Form/Type/ScopePickerType.php create mode 100644 Form/Type/UserPickerType.php diff --git a/Entity/PermissionsGroup.php b/Entity/PermissionsGroup.php index 95630e120..d3de7e2d5 100644 --- a/Entity/PermissionsGroup.php +++ b/Entity/PermissionsGroup.php @@ -49,9 +49,16 @@ class PermissionsGroup */ private $roleScopes; + /** + * + * @var \Doctrine\Common\Collections\Collection + */ + private $groupCenters; + public function __construct() { $this->roleScopes = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection(); } public function getId() diff --git a/Entity/RoleScope.php b/Entity/RoleScope.php index d8a0d2256..fa39052fc 100644 --- a/Entity/RoleScope.php +++ b/Entity/RoleScope.php @@ -45,8 +45,15 @@ class RoleScope */ private $scope; + /** + * + * @var \Doctrine\Common\Collections\Collection + */ + private $permissionsGroups; + public function __construct() { $this->new = true; + $this->permissionsGroups = new \Doctrine\Common\Collections\ArrayCollection(); } public function getId() diff --git a/Form/Type/ScopePickerType.php b/Form/Type/ScopePickerType.php new file mode 100644 index 000000000..838ecaae1 --- /dev/null +++ b/Form/Type/ScopePickerType.php @@ -0,0 +1,124 @@ + + * + * 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 . + */ +namespace Chill\MainBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Doctrine\ORM\EntityRepository; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\OptionsResolver\Options; +use Chill\MainBundle\Entity\Scope; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Chill\MainBundle\Templating\TranslatableStringHelper; + +/** + * Allow to pick amongst available scope for the current + * user. + * + * options : + * + * - `center`: the center of the entity + * - `role` : the role of the user + * + * @author Julien Fastré + */ +class ScopePickerType extends AbstractType +{ + /** + * + * @var AuthorizationHelper + */ + protected $authorizationHelper; + + /** + * + * @var TokenStorageInterface + */ + protected $tokenStorage; + + /** + * + * @var EntityRepository + */ + protected $scopeRepository; + + /** + * + * @var TranslatableStringHelper + */ + protected $translatableStringHelper; + + public function __construct( + AuthorizationHelper $authorizationHelper, + TokenStorageInterface $tokenStorage, + EntityRepository $scopeRepository, + TranslatableStringHelper $translatableStringHelper + ) { + $this->authorizationHelper = $authorizationHelper; + $this->tokenStorage = $tokenStorage; + $this->scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + // create `center` option + ->setRequired('center') + ->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ]) + // create ``role` option + ->setRequired('role') + ->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ]) + ; + + $resolver + ->setDefault('class', Scope::class) + ->setDefault('placeholder', 'Choose the circle') + ->setDefault('choice_label', function(Scope $c) { + return $this->translatableStringHelper->localize($c->getName()); + }) + ->setNormalizer('query_builder', function(Options $options) { + $qb = $this->scopeRepository->createQueryBuilder('s'); + $qb + // jointure to center + ->join('s.roleScopes', 'rs') + ->join('rs.permissionsGroups', 'pg') + ->join('pg.groupCenters', 'gc') + //->join('gc.users', 'user') + // add center constraint + ->where($qb->expr()->eq('IDENTITY(gc.center)', ':center')) + ->setParameter('center', $options['center']->getId()) + // role constraints + ->andWhere($qb->expr()->eq('rs.role', ':role')) + ->setParameter('role', $options['role']) + // user contraint + ->andWhere(':user MEMBER OF gc.users') + ->setParameter('user', $this->tokenStorage->getToken()->getUser()) + ; + + return $qb; + }) + ; + } + + public function getParent() + { + return \Symfony\Bridge\Doctrine\Form\Type\EntityType::class; + } +} diff --git a/Form/Type/UserPickerType.php b/Form/Type/UserPickerType.php new file mode 100644 index 000000000..9fbc34dac --- /dev/null +++ b/Form/Type/UserPickerType.php @@ -0,0 +1,114 @@ + + * + * 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 . + */ +namespace Chill\MainBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Doctrine\ORM\EntityRepository; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Chill\MainBundle\Entity\User; + + +/** + * Pick a user available for the given role and center. + * + * Options : + * + * - `role` : the role the user can reach + * - `center`: the center a user can reach + * + * @author Julien Fastré + */ +class UserPickerType extends AbstractType +{ + /** + * + * @var AuthorizationHelper + */ + protected $authorizationHelper; + + /** + * + * @var TokenStorageInterface + */ + protected $tokenStorage; + + /** + * + * @var EntityRepository + */ + protected $userRepository; + + public function __construct( + AuthorizationHelper $authorizationHelper, + TokenStorageInterface $tokenStorage, + EntityRepository $userRepository + ) { + $this->authorizationHelper = $authorizationHelper; + $this->tokenStorage = $tokenStorage; + $this->userRepository = $userRepository; + } + + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + // create `center` option + ->setRequired('center') + ->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ]) + // create ``role` option + ->setRequired('role') + ->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ]) + ; + + $resolver + ->setDefault('class', User::class) + ->setDefault('empty_data', $this->tokenStorage->getToken()->getUser()) + ->setDefault('choice_label', function(User $u) { + return $u->getUsername(); + }) + ->setNormalizer('query_builder', function(Options $options) { + $qb = $this->userRepository->createQueryBuilder('u'); + $qb + // add center constraint + ->join('u.groupCenters', 'ug') + ->where($qb->expr()->eq('ug.center', ':center')) + ->setParameter('center', $options['center']) + // link to permission groups + ->join('ug.permissionsGroup', 'pg') + // role constraints + ->join('pg.roleScopes', 'roleScope') + ->andWhere($qb->expr()->eq('roleScope.role', ':role')) + ->setParameter('role', $options['role']) + // add active constraint + ->andWhere('u.enabled = :enabled') + ->setParameter('enabled', true) + ; + + return $qb; + }) + ; + } + + public function getParent() + { + return \Symfony\Bridge\Doctrine\Form\Type\EntityType::class; + } +} diff --git a/Resources/config/doctrine/GroupCenter.orm.yml b/Resources/config/doctrine/GroupCenter.orm.yml index 13b56067d..1fc2cc75d 100644 --- a/Resources/config/doctrine/GroupCenter.orm.yml +++ b/Resources/config/doctrine/GroupCenter.orm.yml @@ -19,4 +19,8 @@ Chill\MainBundle\Entity\GroupCenter: permissionsGroup: targetEntity: Chill\MainBundle\Entity\PermissionsGroup cache: - usage: NONSTRICT_READ_WRITE \ No newline at end of file + usage: NONSTRICT_READ_WRITE + manyToMany: + users: + targetEntity: Chill\MainBundle\Entity\User + mappedBy: groupCenters \ No newline at end of file diff --git a/Resources/config/doctrine/PermissionsGroup.orm.yml b/Resources/config/doctrine/PermissionsGroup.orm.yml index 100c2c17e..094bec153 100644 --- a/Resources/config/doctrine/PermissionsGroup.orm.yml +++ b/Resources/config/doctrine/PermissionsGroup.orm.yml @@ -17,6 +17,11 @@ Chill\MainBundle\Entity\PermissionsGroup: manyToMany: roleScopes: targetEntity: Chill\MainBundle\Entity\RoleScope + inversedBy: permissionsGroups cache: usage: NONSTRICT_READ_WRITE + oneToMany: + groupCenters: + targetEntity: Chill\MainBundle\Entity\GroupCenter + mappedBy: permissionsGroup \ No newline at end of file diff --git a/Resources/config/doctrine/RoleScope.orm.yml b/Resources/config/doctrine/RoleScope.orm.yml index 983db8712..f9a20f38e 100644 --- a/Resources/config/doctrine/RoleScope.orm.yml +++ b/Resources/config/doctrine/RoleScope.orm.yml @@ -21,4 +21,8 @@ Chill\MainBundle\Entity\RoleScope: nullable: true cache: usage: NONSTRICT_READ_WRITE + manyToMany: + permissionsGroups: + targetEntity: Chill\MainBundle\Entity\PermissionsGroup + mappedBy: roleScopes \ No newline at end of file diff --git a/Resources/config/doctrine/User.orm.yml b/Resources/config/doctrine/User.orm.yml index acb9b6b50..05b434260 100644 --- a/Resources/config/doctrine/User.orm.yml +++ b/Resources/config/doctrine/User.orm.yml @@ -30,6 +30,7 @@ Chill\MainBundle\Entity\User: manyToMany: groupCenters: targetEntity: Chill\MainBundle\Entity\GroupCenter + inversedBy: users cache: usage: NONSTRICT_READ_WRITE diff --git a/Resources/config/services/form.yml b/Resources/config/services/form.yml index 03f244e1d..e205292bd 100644 --- a/Resources/config/services/form.yml +++ b/Resources/config/services/form.yml @@ -89,5 +89,24 @@ services: chill.main.form.date_type: class: Chill\MainBundle\Form\Type\ChillDateType + tags: + - { name: form.type } + + chill.main.form.pick_user_type: + class: Chill\MainBundle\Form\Type\UserPickerType + arguments: + - "@chill.main.security.authorization.helper" + - "@security.token_storage" + - "@chill.main.user_repository" + tags: + - { name: form.type } + + chill.main.form.pick_scope_type: + class: Chill\MainBundle\Form\Type\ScopePickerType + arguments: + - "@chill.main.security.authorization.helper" + - "@security.token_storage" + - "@chill.main.scope_repository" + - "@chill.main.helper.translatable_string" tags: - { name: form.type } \ No newline at end of file diff --git a/Resources/config/services/repositories.yml b/Resources/config/services/repositories.yml index 754d770c7..3f40e82dc 100644 --- a/Resources/config/services/repositories.yml +++ b/Resources/config/services/repositories.yml @@ -10,3 +10,9 @@ services: factory: ["@doctrine.orm.entity_manager", getRepository] arguments: - "Chill\\MainBundle\\Entity\\User" + + chill.main.scope_repository: + class: Doctrine\ORM\EntityRepository + factory: ["@doctrine.orm.entity_manager", getRepository] + arguments: + - "Chill\\MainBundle\\Entity\\Scope" \ No newline at end of file