diff --git a/module/Application/config/module.config.php b/module/Application/config/module.config.php index 96eb838a7cec42d8230dfe4afc3ad146815337a3..fc72d236bb4f64b7a6f721b8be57e71cd22402e3 100755 --- a/module/Application/config/module.config.php +++ b/module/Application/config/module.config.php @@ -222,6 +222,9 @@ $main = array( ), ), 'view_helpers' => array( + 'factories' => array( + 'userProfileSelectRadioItem' => 'Application\View\Helper\UserProfileSelectRadioItemFactory', + ), 'invokables' => array( 'historiqueDl' => 'Application\View\Helper\HistoriqueDl', 'validationDl' => 'Application\View\Helper\ValidationDl', @@ -249,6 +252,7 @@ $main = array( 'controllers' => array( 'invokables' => array( 'Application\Controller\Index' => 'Application\Controller\IndexController', + 'UnicaenAuth\Controller\Utilisateur' => 'Application\Controller\UtilisateurController', ), ), 'view_manager' => array( diff --git a/module/Application/src/Application/Acl/AdministrateurRole.php b/module/Application/src/Application/Acl/AdministrateurRole.php index d26c5ff24fa744d82c52f12c1b114bf4ebff6bea..fe9a83dddfdd1a60cde6a5e0d4a497481c3d7c39 100644 --- a/module/Application/src/Application/Acl/AdministrateurRole.php +++ b/module/Application/src/Application/Acl/AdministrateurRole.php @@ -2,7 +2,8 @@ namespace Application\Acl; -use UnicaenAuth\Acl\NamedRole; +use Application\Interfaces\StructureAwareInterface; +use Application\Traits\StructureAwareTrait; use Application\Interfaces\PersonnelAwareInterface; use Application\Traits\PersonnelAwareTrait; @@ -11,8 +12,9 @@ use Application\Traits\PersonnelAwareTrait; * * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr> */ -class AdministrateurRole extends Role implements PersonnelAwareInterface +class AdministrateurRole extends Role implements /*StructureAwareInterface,*/ PersonnelAwareInterface { + use StructureAwareTrait; use PersonnelAwareTrait; const ROLE_ID = 'administrateur'; @@ -21,4 +23,14 @@ class AdministrateurRole extends Role implements PersonnelAwareInterface { parent::__construct($id, $parent, $name, $description, $selectable); } + + /** + * Retourne la représentation littérale de cet objet. + * + * @return string + */ + public function __toString() + { + return $this->getRoleName() . (($s = $this->getStructure()) ? " ($s)" : null); + } } \ No newline at end of file diff --git a/module/Application/src/Application/Controller/UtilisateurController.php b/module/Application/src/Application/Controller/UtilisateurController.php new file mode 100644 index 0000000000000000000000000000000000000000..ec59c3e9ad3cc14e864bb52cb68ebd0cd194fd4d --- /dev/null +++ b/module/Application/src/Application/Controller/UtilisateurController.php @@ -0,0 +1,46 @@ +<?php + +namespace Application\Controller; + +use UnicaenAuth\Controller\UtilisateurController as BaseController; + +/** + * + * + * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr> + */ +class UtilisateurController extends BaseController implements \Application\Service\ContextProviderAwareInterface +{ + use \Application\Service\ContextProviderAwareTrait; + + /** + * Traite les requêtes AJAX POST de sélection d'un profil utilisateur. + * La sélection est mémorisé en session par le service AuthUserContext. + */ + public function selectionnerProfilAction($addFlashMessage = true) + { + parent::selectionnerProfilAction($addFlashMessage = false); + + $role = $this->getAuthUserContextService()->getSelectedIdentityRole(); + $structureId = $this->getRequest()->getPost('structure'); + + if ($role instanceof \Application\Acl\AdministrateurRole) { + $structure = null; + if ($structureId) { + $structure = $this->getServiceLocator()->get('ApplicationStructure')->get($structureId); + } + $this->getContextProvider()->getGlobalContext()->setStructure($structure); + + $message = sprintf("Vous endossez à présent le profil utilisateur <strong>%s</strong>%s.", + $role->getRoleName(), + $structure ? " pour la structure <strong>$structure</strong>" : null); + } + else { + $message = sprintf("Vous endossez à présent le profil utilisateur <strong>%s</strong>.", $role); + } + + $this->flashMessenger()->addSuccessMessage($message); + + exit; + } +} \ No newline at end of file diff --git a/module/Application/src/Application/Provider/Role/RoleProvider.php b/module/Application/src/Application/Provider/Role/RoleProvider.php index 5add51a72876b82d95c01ab0ac32050810d9a7c6..1ffdfe8e2978b11d2f5a7459357428fe96e5cbcf 100644 --- a/module/Application/src/Application/Provider/Role/RoleProvider.php +++ b/module/Application/src/Application/Provider/Role/RoleProvider.php @@ -2,10 +2,16 @@ namespace Application\Provider\Role; +use Application\Acl\AdministrateurRole; +use Application\Entity\Db\Role; +use Application\Entity\Db\Structure as StructureEntity; +use Application\Interfaces\StructureAwareInterface; use BjyAuthorize\Provider\Role\ProviderInterface; +use Exception; +use UnicaenApp\Exception\LogicException; use UnicaenApp\Service\EntityManagerAwareInterface; use UnicaenApp\Service\EntityManagerAwareTrait; -use Application\Interfaces\StructureAwareInterface; +use Zend\Permissions\Acl\Role\RoleInterface; /** * Fournisseur des rôles utilisateurs de l'application : @@ -42,7 +48,7 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface } /** - * @return \Zend\Permissions\Acl\Role\RoleInterface[] + * @return RoleInterface[] */ public function getRoles() { @@ -52,10 +58,10 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface // Chargement des rôles de base foreach( $this->config as $classname ){ if (class_exists( $classname )){ - $role = new $classname; /* @var $role \Zend\Permissions\Acl\Role\RoleInterface */ + $role = new $classname; /* @var $role RoleInterface */ $this->roles[$role->getRoleId()] = $role; }else{ - throw new \UnicaenApp\Exception\LogicException('La classe "'.$classname.'" déclarée dans la configuration du fournisseur de rôles n\'a pas été trouvée.'); + throw new LogicException('La classe "'.$classname.'" déclarée dans la configuration du fournisseur de rôles n\'a pas été trouvée.'); } } @@ -66,10 +72,10 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface ->distinct() ->join("r.type", "tr") ->leftJoin("r.structure", "s"); - foreach ($qb->getQuery()->getResult() as $role) { /* @var $role \Application\Entity\Db\Role */ + foreach ($qb->getQuery()->getResult() as $role) { /* @var $role Role */ $roleId = $role->getType()->getCode(); if (! isset($this->roles[$roleId])){ - throw new \Exception('Le rôle "'.$roleId.'" est inconnu.'); + throw new Exception('Le rôle "'.$roleId.'" est inconnu.'); } $classname = get_class($this->roles[$roleId]); if ($this->roles[$roleId] instanceof StructureAwareInterface && $role->getStructure()){ @@ -80,8 +86,40 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface $this->roles[$roleId] = new $classname($roleId); } $this->roles[$roleId]->setTypeRole( $role->getType() ); + + $this->injectSelectedStructureInRole($this->roles[$roleId]); } } + return $this->roles; } + + /** + * Inject la structure sélectionnée en session dans le rôle Administrateur. + * + * @param Role $role + * @return self + */ + public function injectSelectedStructureInRole($role) + { + if (! $role instanceof AdministrateurRole) { + return $this; + } + + $role->setStructure($this->structureSelectionnee); + + return $this; + } + + /** + * @var StructureEntity + */ + protected $structureSelectionnee; + + public function setStructureSelectionnee(StructureEntity $structureSelectionnee = null) + { + $this->structureSelectionnee = $structureSelectionnee; + + return $this; + } } diff --git a/module/Application/src/Application/Provider/Role/RoleProviderFactory.php b/module/Application/src/Application/Provider/Role/RoleProviderFactory.php index f27b89b918455e30cdea455b9d7e26f8ff47a4fa..103b9263f65a037789dd52b9b85c53981154f3e3 100644 --- a/module/Application/src/Application/Provider/Role/RoleProviderFactory.php +++ b/module/Application/src/Application/Provider/Role/RoleProviderFactory.php @@ -31,10 +31,14 @@ class RoleProviderFactory implements FactoryInterface } $providerConfig = $config['role_providers']['ApplicationRoleProvider']; + $contextProvider = $serviceLocator->get('ApplicationContextProvider'); $roleProvider = new RoleProvider( $providerConfig ); - $roleProvider->setEntityManager($em); - $roleProvider->init(); + $roleProvider + ->setEntityManager($em) + ->setStructureSelectionnee($contextProvider->getGlobalContext()->getStructure()) + ->init(); + return $roleProvider; } } \ No newline at end of file diff --git a/module/Application/src/Application/Service/ContextProvider.php b/module/Application/src/Application/Service/ContextProvider.php index 63034bda06d525cfe77ece0e1399b8eacce8335c..13579c403e5c268a8762ccbf891c0301fd6ebb6e 100644 --- a/module/Application/src/Application/Service/ContextProvider.php +++ b/module/Application/src/Application/Service/ContextProvider.php @@ -71,8 +71,10 @@ class ContextProvider extends AbstractService } } - - $this->globalContext = new GlobalContext(); + /** + * @todo Faire du GlobalContext un service ! + */ + $this->globalContext = new GlobalContext($this->getServiceLocator()); $this->globalContext ->setUtilisateur($utilisateur) ->setIntervenant($intervenant) diff --git a/module/Application/src/Application/Service/GlobalContext.php b/module/Application/src/Application/Service/GlobalContext.php index 6920fb73a02767af44d0b167df8f232cf0cc8f2d..922f8a6fa9af575242ad5eb644fdf7d37799b2f6 100644 --- a/module/Application/src/Application/Service/GlobalContext.php +++ b/module/Application/src/Application/Service/GlobalContext.php @@ -2,11 +2,16 @@ namespace Application\Service; +use Application\Entity\Db\Annee; use Application\Entity\Db\Annee as AnneeEntity; use Application\Entity\Db\Etablissement as EntityEtablissement; use Application\Entity\Db\Intervenant as EntityIntervenant; use Application\Entity\Db\Personnel as PersonnelEntity; +use Application\Entity\Db\Structure as StructureEntity; use Application\Entity\Db\Utilisateur as UtilisateurEntity; +use DateTime; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Session\Container; /** * Classe regroupant les données globales de fonctionnement de l'application. @@ -15,6 +20,10 @@ use Application\Entity\Db\Utilisateur as UtilisateurEntity; */ class GlobalContext { + /** + * @var ServiceLocatorInterface + */ + protected $sl; /** * @var UtilisateurEntity @@ -48,10 +57,20 @@ class GlobalContext /** * - * @var \DateTime + * @var DateTime */ protected $dateFinSaisiePermanents; + /** + * Constructeur. + * + * @param ServiceLocatorInterface $sl + */ + public function __construct(ServiceLocatorInterface $sl) + { + $this->sl = $sl; + } + /** * @var EntityEtablissement */ @@ -148,10 +167,59 @@ class GlobalContext return $this; } - function setDateFinSaisiePermanents(\DateTime $dateFinSaisiePermanents) + function setDateFinSaisiePermanents(DateTime $dateFinSaisiePermanents) { $this->dateFinSaisiePermanents = $dateFinSaisiePermanents; return $this; } + + /** + * Retourne l'éventuelle structure sélectionnée mémorisée en session. + * + * @return StructureEntity|null + */ + function getStructure() + { + $structure = $this->getSessionContainer()->structure; + + if ($structure instanceof StructureEntity) { + return $structure; + } + if ($structure) { + $structure = $this->sl->get('ApplicationStructure')->get($structure); + } + + return $structure; + } + /** + * Spécifie l'éventuelle structure sélectionnée à mémoriser en session. + * + * @param StructureEntity|int|null $structure + */ + function setStructure($structure) + { + if ($structure instanceof StructureEntity) { + $structure = $structure->getId(); + } + + $this->getSessionContainer()->structure = $structure; + } + + /** + * @var Container + */ + protected $sessionContainer; + + /** + * @return Container + */ + function getSessionContainer() + { + if (null === $this->sessionContainer) { + $this->sessionContainer = new Container(__CLASS__); + } + + return $this->sessionContainer; + } } \ No newline at end of file diff --git a/module/Application/src/Application/Service/Structure.php b/module/Application/src/Application/Service/Structure.php index e4f1400e942f008dbb9a6b55d5c9274a1fcead64..efb1657a8e264946675415eab8df041ad455c658 100644 --- a/module/Application/src/Application/Service/Structure.php +++ b/module/Application/src/Application/Service/Structure.php @@ -127,15 +127,19 @@ class Structure extends AbstractEntityService } /** - * Retourne la liste des structures pour lesquelles le rôle est autorisé à officier + * Si un rôle est spécifié, retourne la liste des structures pour lesquelles ce rôle est autorisé à officier. + * Si <code>true</code> est spécifié, retourne la liste des structures associées à des rôles. * - * @param \Application\Acl\Role $role + * @param \Application\Acl\Role|true $role */ public function finderByRole( $role, QueryBuilder $qb=null, $alias=null) { list($qb,$alias) = $this->initQuery($qb, $alias); - if ($role instanceof \Application\Interfaces\StructureAwareInterface){ + if (true === $role) { + $qb->andWhere("EXISTS ( SELECT r from Application\Entity\Db\Role r WHERE r.structure = $alias)"); + } + elseif ($role instanceof \Application\Interfaces\StructureAwareInterface) { $this->finderByStructure( $role->getStructure(), $qb, $alias ); } diff --git a/module/Application/src/Application/View/Helper/UserProfileSelectRadioItem.php b/module/Application/src/Application/View/Helper/UserProfileSelectRadioItem.php new file mode 100644 index 0000000000000000000000000000000000000000..9405ca0d1a109936ce19655df22b71c050ded3e5 --- /dev/null +++ b/module/Application/src/Application/View/Helper/UserProfileSelectRadioItem.php @@ -0,0 +1,111 @@ +<?php +namespace Application\View\Helper; + +use UnicaenAuth\View\Helper\UserProfileSelectRadioItem as UnicaenAuthViewHelper; +use UnicaenAuth\View\Helper\UserProfileSelect; +use Application\Service\Structure as StructureService; +use Application\Entity\Db\Structure as StructureEntity; + +/** + * Aide de vue dessinant un item de sélection d'un profil utilisateur. + * Utilisé par l'aide de vue UserProfileSelect. + * + * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr> + * @see UserProfileSelect + */ +class UserProfileSelectRadioItem extends UnicaenAuthViewHelper +{ + /** + * Retourne le code HTML généré par cette aide de vue. + * + * @return string + */ + public function render() + { + $html = parent::render(); + +// if ($this->role instanceof \Application\Acl\AdministrateurRole) { +// $selectClass = 'user-profile-select-input-structure'; +// +// $select = new \Zend\Form\Element\Select('structure'); +// $select +// ->setEmptyOption("(Aucune)") +// ->setValueOptions(array_map(function($v) { return (string) $v; }, $this->getStructures())) +// ->setValue($this->structureSelectionnee ? $this->structureSelectionnee->getId() : null) +// ->setAttribute('class', $selectClass) +// ->setAttribute('title', "Cliquez pour sélectionner la structure associée au profil $this->role"); +// +// $html .= ' ' . $this->getView()->formSelect($select); +// +// $html .= <<<EOS +//<script> +// $(function() { +// $("select.$selectClass").tooltip({ delay: 500, placement: 'right' }).change(function() { +// var roleSelect = $("input.user-profile-select-input"); +// if (! roleSelect.attr("checked")) { +// roleSelect.attr("checked", true); +// } +// submitProfile(); +// }); +// }); +//</script> +//EOS; +// } + + return $html; + } + + /** + * Surcharge pour ne pas faire figurer la structure associée au rôle Administrateur + * car elle figure dans la liste déroulante voisine. + * + * @return Radio + */ + protected function createRadio() + { + $radio = parent::createRadio(); + + if ($this->role instanceof \Application\Acl\AdministrateurRole) { + $id = $this->role->getRoleId(); + $radio->setValueOptions(array($id => $this->role->getRoleName())); + } + + return $radio; + } + + /** + * Retourne la liste des structures associées à des rôles. + * + * @return array + */ + private function getStructures() + { + $qb = $this->structureService->finderByRole(true); + + return $this->structureService->getList($qb); + } + + /** + * @var StructureService + */ + protected $structureService; + + function setServiceStructure(StructureService $structureService) + { + $this->structureService = $structureService; + + return $this; + } + + /** + * @var StructureEntity + */ + protected $structureSelectionnee; + + public function setStructureSelectionnee(StructureEntity $structureSelectionnee = null) + { + $this->structureSelectionnee = $structureSelectionnee; + + return $this; + } +} \ No newline at end of file diff --git a/module/Application/src/Application/View/Helper/UserProfileSelectRadioItemFactory.php b/module/Application/src/Application/View/Helper/UserProfileSelectRadioItemFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b78e33a9d136a7c28da782f1421b5134bd4e849f --- /dev/null +++ b/module/Application/src/Application/View/Helper/UserProfileSelectRadioItemFactory.php @@ -0,0 +1,34 @@ +<?php + +namespace Application\View\Helper; + +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * + * + * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr> + */ +class UserProfileSelectRadioItemFactory extends \UnicaenApp\View\Helper\UserProfileSelectFactory +{ + /** + * Create service + * + * @param ServiceLocatorInterface $helperPluginManager + * @return UserProfile + */ + public function createService(ServiceLocatorInterface $helperPluginManager) + { + $serviceLocator = $helperPluginManager->getServiceLocator(); + $userContextService = $serviceLocator->get('AuthUserContext'); + $structureService = $serviceLocator->get('ApplicationStructure'); + $contextProvider = $serviceLocator->get('ApplicationContextProvider'); + + $service = new UserProfileSelectRadioItem($userContextService); + $service + ->setServiceStructure($structureService) + ->setStructureSelectionnee($contextProvider->getGlobalContext()->getStructure()); + + return $service; + } +} \ No newline at end of file