Skip to content

Instantly share code, notes, and snippets.

@juanmf
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juanmf/42e22c37567702e729e2 to your computer and use it in GitHub Desktop.
Save juanmf/42e22c37567702e729e2 to your computer and use it in GitHub Desktop.
{% extends '::base.html.twig' %}
{% block body -%}
<h1>{{ 'views.edit.edit'|trans({'%entity%': 'Role'}) }}</h1>
{{ form_start(edit_form, { "attr" : { "class" : "well" } }) }}
{{ form_widget(edit_form) }}
<div>
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-ok"></span> {{ 'views.edit.editbutton'|trans }}
</button>
</div>
{{ form_end(edit_form) }}
<div class="well-sm">
<ul class="record_actions list-inline">
<li>
<a href="{{ path('admin_role') }}">
<span class="glyphicon glyphicon-list"></span>
{{ 'views.recordactions.backtothelist'|trans }}
</a>
</li>
<li>
{{ form_start(delete_form) }}
{{ form_widget(delete_form) }}
<button type="submit" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span> {{ 'views.recordactions.delete'|trans }}
</button>
{{ form_end(delete_form) }}
</li>
</ul>
</div>{% endblock %}
<?php
namespace Application\ProcessBundle\Subscriber;
//Since Doctrine 2.4
//use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
//use Doctrine\Common\Persistence\Event\PreUpdateEventArgs;
use Symfony\Bridge\Monolog\Logger as SfLogger;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\UnitOfWork;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Application\GeneralBundle\Util\Logger\Logger;
use Application\GeneralBundle\Util\Role\Role as RoleUtil;
use Application\ProcessBundle\Entity as ProcessEntity;
use Application\UsuarioBundle\Entity\Role;
/**
* Description of ProcessRolesSubscriber
*
* @author Juan Manuel Fernandez <juanmf@gmail.com>
*/
class ProcessRolesSubscriber implements EventSubscriber
{
const WORKFLOW_MASTER_ROLE = 'ROLE_WORKFLOW_ADMIN';
private $handledEntityChanges = array(
'Application\ProcessBundle\Entity\Activity',
'Application\ProcessBundle\Entity\Process',
'Application\ProcessBundle\Entity\Area',
);
private $handle = array(
'Application\ProcessBundle\Entity\Activity' => 'activity',
'Application\ProcessBundle\Entity\Process' => 'process',
'Application\ProcessBundle\Entity\Area' => 'area',
);
/**
*
* @var SfLogger
*/
private $logger;
/**
*
* @var EntityManager
*/
private $em;
/**
* En algunas situaciones:<pre>
* * activityPreUpdateProcess(): Al cambiar el Proceso padre se crea un nuevo rol.
* Eliminar preexistente.
* </pre>
*
* Se setea este flag en preXxx para borrar algun Role en el postXxx
*
* @var Role[]
*/
private $attemptRemoveRole = array();
/**
* Inicializa el Logger.
*
* @param \Symfony\Bridge\Monolog\Logger $logger
*
* @see Logger
*/
public function __construct(SfLogger $logger)
{
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return array(
// preUpdate No actualiza cambios en Role
'onFlush',
'postUpdate',
);
}
/**
* Deriva la ejecucion a:<pre>
* * preUpdate
* * prePersist
* </pre>
*
* @param \Doctrine\ORM\Event\OnFlushEventArgs $eventArgs
*/
public function onFlush(OnFlushEventArgs $eventArgs)
{
$this->em = $em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() AS $entity) {
if (! in_array($class = get_class($entity), $this->handledEntityChanges)) {
break;
}
$this->prePersist($uow, $entity);
}
foreach ($uow->getScheduledEntityUpdates() AS $entity) {
if (! in_array($class = get_class($entity), $this->handledEntityChanges)) {
break;
}
$this->preUpdate($uow, $entity);
}
foreach ($uow->getScheduledEntityDeletions() AS $entity) {
if (! in_array($class = get_class($entity), $this->handledEntityChanges)) {
break;
}
$this->preRemove($uow, $entity);
}
}
/**
* Deriva las tareas de update de cada objeto manejado a su metodo especializado.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param Object $entity Uno de los objetos manejados
* {@link $handledEntityChanges}
*
* @see self::$handle
*/
public function preUpdate(UnitOfWork $uow, $entity)
{
$class = get_class($entity);
$this->{$this->handle[$class] . 'PreUpdate'}($uow, $entity);
}
/**
* Deriva las tareas de remove de cada objeto manejado a su metodo especializado.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param Object $entity Uno de los objetos manejados
* {@link $handledEntityChanges}
*
* @see self::$handle
*/
public function preRemove(UnitOfWork $uow, $entity)
{
$class = get_class($entity);
$this->{$this->handle[$class] . 'PreRemove'}($uow, $entity);
}
/**
* Deriva las tareas de persist de cada objeto manejado a su metodo especializado.
* Ene ste caso, solo se atiende a Persis de Activity. pues son las unicas que
* reciben roles, hasta el momento, pues el acceso al proceso y al area se computa
* con la Expression anyRoleBeggins("ROLE_*").
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param Object $entity Uno de los objetos manejados
* {@link $handledEntityChanges}
*
* @see self::$handle
*/
public function prePersist(UnitOfWork $uow, $entity)
{
$class = get_class($entity);
if (! is_callable(array($this, $this->handle[$class] . 'PrePersist'))) {
return;
}
$this->{$this->handle[$class] . 'PrePersist'}($uow, $entity);
}
/**
* Crea el Rol correspondiente.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param \Application\ProcessBundle\Entity\Activity $activity La activity que cambio.
*/
private function activityPrePersist(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
$masterRole = $this->getRole(self::WORKFLOW_MASTER_ROLE);
$newRole = new Role(RoleUtil::getRoleForActivity($activity));
$newRole->setParent($masterRole);
$roleMetadata = $this->em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
$this->em->persist($newRole);
$uow->computeChangeSet($roleMetadata, $newRole);
$this->logRoleEdit($newRole, $activity, 'Nuevo Rol: %s; Para actividad: %s');
}
/**
* Borrar el Rol de la actividad que desaparece.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param \Application\ProcessBundle\Entity\Activity $activity La activity que cambio.
*/
private function activityPreRemove(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
$role = $this->getRole(RoleUtil::getRoleForActivity($activity));
$this->em->remove($role);
$roleMetadata = $this->em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
$uow->computeChangeSet($roleMetadata, $role);
$this->logRoleEdit($role->getRole(), $activity, 'Removing Role: %s. as a Result of Removing Activity: %s');
}
/**
* Borrar el Rol de las actividades hijas del rol que desaparece.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param \Application\ProcessBundle\Entity\Process $process El proceso que cambio.
*/
private function processPreRemove(UnitOfWork $uow, ProcessEntity\Process $process)
{
foreach ($process->getActivities() as $activity) {
$this->activityPreRemove($uow, $activity);
}
}
/**
* Borrar el Rol de las actividades descendientes del area que desaparece.
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Area $area El area que cambio.
*/
private function areaPreRemove(UnitOfWork $uow, ProcessEntity\Area $area)
{
foreach ($area->getProcesses() as $process) {
$this->processPreRemove($uow, $process);
}
}
/**
* Si actualizan {'name', 'process'} hay que reflejarlo en el ROLE_AREA_PROCESS_ACTIVITY
* {'name' => ACTIVITY, 'process' => PROCESS}
*
* PROCESS changes might involve security issues, as users would keep the relation to
* the Role id, gaining access to a different part (area tab in the view layer).
* Therefor the chosen approach is to delete the role, and create a new one on PROCESS change.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param \Application\ProcessBundle\Entity\Activity $activity La activity que cambio.
*/
private function activityPreUpdate(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
if (! $in = array_intersect_key($uow->getEntityChangeSet($activity), array('name' => 1, 'process' => 1))) {
return;
}
switch (true) {
case isset($in['process']):
$this->activityPreUpdateProcess($uow, $activity);
break;
case isset($in['name']):
$this->activityPreUpdateName($uow, $activity);
break;
}
}
/**
* Si actualizan {'name', 'area'} hay que reflejarlo en el ROLE_AREA_PROCESS_ACTIVITY
* {'name' => PROCESS, 'area' => AREA}
*
* AREA changes might involve security issues, as users would keep the relation to
* the Role id, gaining access to a different part (area landing page in the view layer).
* Therefor the chosen approach is to delete the role, and create a new one on AREA change.
*
* @param \Doctrine\ORM\UnitOfWork $uow UnitOfWork
* @param \Application\ProcessBundle\Entity\Process $process El proceso que cambio.
*/
private function processPreUpdate(UnitOfWork $uow, ProcessEntity\Process $process)
{
if (! $in = array_intersect_key($uow->getEntityChangeSet($process), array('name' => 1, 'area' => 1))) {
return;
}
switch (true) {
case isset($in['area']):
$this->processPreUpdateArea($uow, $process);
break;
case isset($in['name']):
$this->processPreUpdateName($uow, $process);
break;
}
}
/**
* Si actualizan {'name', 'area'} hay que reflejarlo en el ROLE_AREA_PROCESS_ACTIVITY
* {'name' => PROCESS, 'area' => AREA}
*
* AREA changes might involve security issues, as users would keep the relation to
* the Role id, gaining access to a different part (area landing page in the view layer).
* Therefor the chosen approach is to delete the role, and create a new one on AREA change.
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Area $area El area que cambio.
*/
private function areaPreUpdate(UnitOfWork $uow, ProcessEntity\Area $area)
{
if (! $in = array_intersect_key($uow->getEntityChangeSet($area), array('name' => 1))) {
return;
}
if (isset($in['name'])) {
$this->areaPreUpdateName($uow, $area);
}
}
/**
* Actualizacion simple, solo el nombre del tramo PROCESS de ROLE_AREA_PROCESS_ACTIVITY
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Area $area El area que cambio.
*
* @throws \LogicException
*/
private function areaPreUpdateName(UnitOfWork $uow, ProcessEntity\Area $area)
{
$originalarea = $this->revertChanges($area, $uow);
$roleMetadata = $this->em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
foreach ($area->getProcesses() as $process) {
foreach ($process->getActivities() as $activity) {
list($oldRoleName, $newRoleName, $role) =
$this->packAreaActivityHandleDeps($activity, $originalarea);
$role->setName($newRoleName);
$uow->recomputeSingleEntityChangeSet($roleMetadata, $role);
$this->logRoleEdit($oldRoleName, $newRoleName);
}
}
}
/**
* Actualizacion simple, solo el nombre del tramo PROCESS de ROLE_AREA_PROCESS_ACTIVITY
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Process $process El proceso que cambio
*
* @throws \LogicException
*/
private function processPreUpdateName(UnitOfWork $uow, ProcessEntity\Process $process)
{
$originalProcess = $this->revertChanges($process, $uow);
$roleMetadata = $this->em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
foreach ($process->getActivities() as $activity) {
list($oldRoleName, $newRoleName, $role) =
$this->packProcessActivityHandleDeps($activity, $originalProcess);
$role->setName($newRoleName);
$uow->recomputeSingleEntityChangeSet($roleMetadata, $role);
$this->logRoleEdit($oldRoleName, $newRoleName);
}
}
/**
* Un cambio de AreaId en el Proceso puede. No tiene sentido que los usuarios se
* cambien de area solo por una reasignacion de parent a Proceso. Eliminar Role.
*
* Agrega sufijo _INVALIDATED, que lo inutiliza al no coincidir mas con alguna Activity
* en este flush y trata de remover el Role, despues del flush.
* Crea un Nuevo Role para la nueva combinacion ROLE_AREA_PROCESS_ACTIVITY
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Process $process El proceso que cambio
*
* @see self::$attemptRemoveRole
*/
private function processPreUpdateArea(UnitOfWork $uow, ProcessEntity\Process $process)
{
$originalProcess = $this->revertChanges($process, $uow);
$roleMetadata = $this->em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
foreach ($process->getActivities() as $activity) {
list($oldRoleName, $newRoleName, $role) =
$this->packProcessActivityHandleDeps($activity, $originalProcess);
$this->replaceRole($uow, $roleMetadata, $role, $oldRoleName, $newRoleName);
}
}
/**
* sufixes role with '_INVALIDATED' and set {@link self::} flag for remval.
*
* @param UnitOfWork $uow UnitOfWork
* @param ClassMetadata $roleMetadata ClassMetadata del Entity\Role
* @param Role $role El objeto Role.
* @param string $oldRoleName Nombre del rol antes del cambio, a reemplazar.
* @param string $newRoleName Nombre del nuevo Rol a crear.
*/
private function replaceRole(
UnitOfWork $uow, ClassMetadata $roleMetadata, Role $role, $oldRoleName, $newRoleName
) {
$role->setName($oldRoleName . '_INVALIDATED');
$uow->recomputeSingleEntityChangeSet($roleMetadata, $role);
$this->logRoleEdit($oldRoleName, $oldRoleName, 'Altering Role: %s ; NewRoleName: %s_INVALIDATED');
$newRole = new Role($newRoleName);
$newRole->setParent($role->getParent());
$this->em->persist($newRole);
$uow->computeChangeSet($roleMetadata, $newRole);
$this->attemptRemoveRole[] = $role;
$this->logRoleEdit(
$newRoleName, $oldRoleName, 'New Role: %s; Replacing Old Role: %s'
);
}
/**
* Actualizacion simple, solo el nombre del ultimo tramo de ROLE_AREA_PROCESS_ACTIVITY
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Activity $activity El activity que cambio
*
*/
private function activityPreUpdateName(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
list($oldRoleName, $newRoleName, $role) = $this->packActivityHandleDeps($uow, $activity);
$em = $this->em;
$role->setName($newRoleName);
$uow->recomputeSingleEntityChangeSet(
$em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role'),
$role
);
$this->logRoleEdit($oldRoleName, $newRoleName);
}
/**
* Centraliza logica para obtener los roles y revertir cambios de la actividad.
*
* @param UnitOfWork $uow UnitOfWork
* @param ProcessEntity\Activity $activity El activity que cambio
*/
private function packActivityHandleDeps(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
$originalActivity = $this->revertChanges($activity, $uow);
$oldRoleName = RoleUtil::getRoleForActivity($originalActivity);
$newRoleName = RoleUtil::getRoleForActivity($activity);
$role = $this->getRole($oldRoleName);
return array($oldRoleName, $newRoleName, $role);
}
/**
* Centraliza logica para obtener los roles asociados a las actividades del
* proceso que cambio.
*
* @param \Application\ProcessBundle\Entity\Activity $activity
* @param \Application\ProcessBundle\Entity\Process $originalProcess
*
* @return array [$oldRoleName, $newRoleName, $role]
*/
private function packProcessActivityHandleDeps(
ProcessEntity\Activity $activity, ProcessEntity\Process $originalProcess
) {
$mockActivity = clone $activity;
$mockActivity->setProcess($originalProcess);
$oldRoleName = RoleUtil::getRoleForActivity($mockActivity);
$newRoleName = RoleUtil::getRoleForActivity($activity);
$role = $this->getRole($oldRoleName);
return array($oldRoleName, $newRoleName, $role);
}
/**
* Centraliza logica para obtener los roles asociados a las actividades del
* area que cambio.
*
* @param \Application\ProcessBundle\Entity\Activity $activity
* @param \Application\ProcessBundle\Entity\Area $originalArea
*
* @return array [$oldRoleName, $newRoleName, $role]
*/
private function packAreaActivityHandleDeps(
ProcessEntity\Activity $activity, ProcessEntity\Area $originalArea
) {
$mockActivity = clone $activity;
$porcess = $mockActivity->getProcess();
$mockPorcess = clone $porcess;
$mockPorcess->setArea($originalArea);
$mockActivity->setProcess($mockPorcess);
$oldRoleName = RoleUtil::getRoleForActivity($mockActivity);
$newRoleName = RoleUtil::getRoleForActivity($activity);
$role = $this->getRole($oldRoleName);
return array($oldRoleName, $newRoleName, $role);
}
/**
* Busca el Rol
*
* @param type $roleName
*
* @return Role
* @throws \LogicException
*/
private function getRole($roleName)
{
$em = $this->em;
$role = $em->getRepository('ApplicationUsuarioBundle:Role')->findOneBy(array('name' => $roleName));
if (! $role) {
throw new \LogicException($roleName . '. No tiene un objeto Role.');
}
return $role;
}
/**
* Usa getEntityChangeSet() para revertir los cambios en la entidad.
*
* @param Object $entity La entidad.
* @param \Doctrine\ORM\Event\PreUpdateEventArgs $args
*
* @return Object La entidad clonada con los valores antes de ser cambiados.
*/
private function revertChanges($entity, UnitOfWork $uow)
{
$old = 0;
$new = 1;
$originalEntity = clone $entity;
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($uow->getEntityChangeSet($entity) as $property => $oldAndNewValue) {
$accessor->setValue($originalEntity, $property, $oldAndNewValue[$old]);
}
return $originalEntity;
}
/**
* Un cambio de ProcesoId en la Activity puede impliar que se va del area. Y siempre
* implica que se va del proceso. No tiene sentido que los usuarios se cambien de area
* solo por una reasignacion de parent a Avtivity. Eliminar Role.
*
* Agrega sufijo _INVALIDATED, que lo inutiliza al no coincidir mas con alguna Activity
* en este flush y trata de remover el Role, despues del flush.
* Crea un Nuevo Role para la nueva combinacion ROLE_AREA_PROCESS_ACTIVITY
*
*
* @param \Doctrine\ORM\UnitOfWork $uow
* @param \Application\ProcessBundle\Entity\Activity $activity
*
* @see self::$attemptRemoveRole
*/
private function activityPreUpdateProcess(UnitOfWork $uow, ProcessEntity\Activity $activity)
{
list($oldRoleName, $newRoleName, $role) = $this->packActivityHandleDeps($uow, $activity);
$em = $this->em;
$roleMetadata = $em->getMetadataFactory()->getMetadataFor('ApplicationUsuarioBundle:Role');
$this->replaceRole($uow, $roleMetadata, $role, $oldRoleName, $newRoleName);
}
/**
* Se encarga de borrar Roles que se hayan agendado en {@link $this->attemptRemoveRole}
*
* @param \Doctrine\ORM\Event\LifecycleEventArgs $args
* @return type
*
* @throws \Exception
*/
public function postUpdate(LifecycleEventArgs $args)
{
if (0 === count($this->attemptRemoveRole)) {
return;
}
foreach ($this->attemptRemoveRole as $k => $role)
{
unset($this->attemptRemoveRole[$k]);
$this->em->remove($role);
$this->logRoleEdit($role->getRole(), '', 'Removing Role: %s');
}
try {
$this->em->flush();
} catch (\Exception $exc) {
$this->logRoleEdit($role->getRole(), $exc->getMessage(), 'Problems Removing Role: %s; %s');
throw $exc;
}
}
/**
*
* @param type $msg
* @param type $oldRoleName
* @param type $newRoleName
*/
private function logRoleEdit($oldRoleName, $newRoleName, $msg = '', $role = '')
{
$msg || $msg = 'Edited Role: %s ; NewRoleName: %s. %s';
Logger::debug(
sprintf(
$msg, $oldRoleName, $newRoleName, $role
), $this->logger
);
}
}
<?php
namespace Application\UsuarioBundle\Entity;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="fos_role")
* @ORM\Entity(repositoryClass="Application\UsuarioBundle\Entity\RoleRepository")
*/
class Role implements RoleInterface
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="name", type="string", length=80, unique=true)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Role", inversedBy="children", fetch="EAGER")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
* @var Role[]
*/
private $parent;
/**
* @ORM\OneToMany(targetEntity="Role", mappedBy="parent", fetch="EAGER")
* @var ArrayCollection|Role[]
*/
private $children;
/**
* @ORM\ManyToMany(targetEntity="Usuario", mappedBy="roles")
*/
private $users;
public function __construct($role = '')
{
if (0 !== strlen($role)) {
$this->name = strtoupper($role);
}
$this->users = new ArrayCollection();
$this->children = new ArrayCollection();
}
/**
* @see RoleInterface
*/
public function getRole()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getUsers()
{
return $this->users;
}
public function addUser($user, $addRoleToUser = true)
{
$this->users->add($user);
$addRoleToUser && $user->addRole($this, false);
}
public function removeUser($user)
{
$this->users->removeElement($user);
}
public function getChildren()
{
return $this->children;
}
public function addChildren(Role $child, $setParentToChild = true)
{
$this->children->add($child);
$setParentToChild && $child->setParent($this, false);
}
public function getDescendant(& $descendants = array())
{
foreach ($this->children as $role) {
$descendants[spl_object_hash($role)] = $role;
$role->getDescendant($descendants);
}
return $descendants;
}
public function removeChildren(Role $children)
{
$this->children->removeElement($children);
}
public function getParent()
{
return $this->parent;
}
public function setParent(Role $parent, $addChildToParent = true)
{
$addChildToParent && $parent->addChildren($this, false);
$this->parent = $parent;
}
public function __toString()
{
if ($this->children->count()) {
$childNameList = array();
foreach ($this->children as $child) {
$childNameList[] = $child->getName();
}
return sprintf('%s [%s]', $this->name, implode(', ', $childNameList));
}
return sprintf('%s', $this->name);
}
}
<?php
namespace Application\UsuarioBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;use Pagerfanta\Pagerfanta;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\View\TwitterBootstrap3View;
use Application\UsuarioBundle\Entity\Role;
use Application\UsuarioBundle\Form\RoleType;
/**
* Role controller.
*
* @Route("/admin/role")
*/
class RoleController extends Controller
{
/**
* Lists all Role entities.
*
* @Route("/", name="admin_role")
* @Method({"GET", "POST"})
* @Template()
*/
public function indexAction()
{
list($filterForm, $queryBuilder) = $this->filter();
list($entities, $pagerHtml) = $this->paginator($queryBuilder);
return array(
'entities' => $entities,
'pagerHtml' => $pagerHtml,
'filterForm' => $filterForm->createView(),
);
}
/**
* Create filter form and process filter request.
*/
protected function filter()
{
$request = $this->getRequest();
$session = $request->getSession();
$filterForm = $this->createFilterForm(new RoleType());
$em = $this->getDoctrine()->getManager();
$queryBuilder = $em->getRepository('ApplicationUsuarioBundle:Role')->createQueryBuilder('e');
// Reset filter
if ($request->get('filter_action') == 'reset') {
$session->remove('RoleControllerFilter');
}
// Filter action
if ($request->get('filter_action') == 'filter') {
// Bind values from the request
$filterForm->handleRequest($request);
if ($filterForm->isValid()) {
// Build the query from the given form object
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($filterForm, $queryBuilder);
// Save filter to session
$filterData = $request->get($filterForm->getName());
$session->set('RoleControllerFilter', $filterData);
}
} else {
// Get filter from session
if ($session->has('RoleControllerFilter')) {
$filterData = $session->get('RoleControllerFilter');
$filterForm->submit($filterData);
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($filterForm, $queryBuilder);
}
}
return array($filterForm, $queryBuilder);
}
/**
* Get results from paginator and get paginator view.
*/
protected function paginator($queryBuilder)
{
// Paginator
$adapter = new DoctrineORMAdapter($queryBuilder);
$pagerfanta = new Pagerfanta($adapter);
$currentPage = $this->getRequest()->get('page', 1);
$pagerfanta->setCurrentPage($currentPage);
$entities = $pagerfanta->getCurrentPageResults();
// Paginator - route generator
$me = $this;
$routeGenerator = function($page) use ($me)
{
return $me->generateUrl('admin_role', array('page' => $page));
};
// Paginator - view
$translator = $this->get('translator');
$view = new TwitterBootstrap3View();
$pagerHtml = $view->render(
$pagerfanta,
$routeGenerator,
array(
'proximity' => 3,
'prev_message' => $translator->trans('views.index.pagprev'),
'next_message' => $translator->trans('views.index.pagnext'),
)
);
return array($entities, $pagerHtml);
}
/**
* Creates a Filter form to search for Entities.
*
* @param AbstractType|string $formType The `generate:doctrine:form` generated Type or its FQCN.
*
* @return \Symfony\Component\Form\Form The filter Form
*/
private function createFilterForm($formType)
{
$adapter = $this->get('dd_form.form_adapter');
$form = $adapter->adaptForm(
$formType,
$this->generateUrl('admin_role')
);
$form->remove('submit');
return $form;
}
/**
* Creates a new Role entity.
*
* @Route("/create", name="admin_role_create")
* @Method("POST")
* @Template("ApplicationUsuarioBundle:Role:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new Role();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
$this->get('session')->getFlashBag()->add(
'success',
$this->get('translator')->trans('flash.create.success')
);
return $this->redirect($this->generateUrl('admin_role_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Creates a form to create a Role entity.
*
* @param Role $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Role $entity)
{
$form = $this->createForm(new RoleType(), $entity, array(
'action' => $this->generateUrl('admin_role_create'),
'method' => 'POST',
));
return $form;
}
/**
* Displays a form to create a new Role entity.
*
* @Route("/new", name="admin_role_new")
* @Method("GET")
* @Template()
*/
public function newAction()
{
$entity = new Role();
$form = $this->createCreateForm($entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Finds and displays a Role entity.
*
* @Route("/{id}/show", name="admin_role_show")
* @Method("GET")
* @Template()
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ApplicationUsuarioBundle:Role')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Role entity.');
}
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
);
}
/**
* Displays a form to edit an existing Role entity.
*
* @Route("/{id}/edit", name="admin_role_edit")
* @Method("GET")
* @Template()
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ApplicationUsuarioBundle:Role')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Role entity.');
}
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Creates a form to edit a Role entity.
*
* @param Role $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createEditForm(Role $entity)
{
$form = $this->createForm(new RoleType(), $entity, array(
'action' => $this->generateUrl('admin_role_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
return $form;
}
/**
* Edits an existing Role entity.
*
* @Route("/{id}/update", name="admin_role_update")
* @Method("PUT")
* @Template("ApplicationUsuarioBundle:Role:edit.html.twig")
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ApplicationUsuarioBundle:Role')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Role entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
$this->get('session')->getFlashBag()->add(
'success',
$this->get('translator')->trans('flash.update.success')
);
return $this->redirect($this->generateUrl('admin_role_edit', array('id' => $id)));
} else {
$this->get('session')->getFlashBag()->add(
'danger',
$this->get('translator')->trans('flash.update.error')
);
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Deletes a Role entity.
*
* @Route("/{id}", name="admin_role_delete")
* @Method("DELETE")
*/
public function deleteAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ApplicationUsuarioBundle:Role')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Role entity.');
}
$em->remove($entity);
$em->flush();
$this->get('session')->getFlashBag()->add(
'success',
$this->get('translator')->trans('flash.delete.success')
);
} else {
$this->get('session')->getFlashBag()->add(
'danger',
$this->get('translator')->trans('flash.delete.error')
);
}
return $this->redirect($this->generateUrl('admin_role'));
}
/**
* Creates a form to delete a Role entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('admin_role_delete', array('id' => $id)))
->setMethod('DELETE') ->getForm()
;
}
}
<?php
namespace DocDigital\Bundle\UserBundle\Role;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
/**
* RoleHierarchy defines a role hierarchy.
*
* @author Juan Manuel Fernandez <juanmf@gmail.com>
*/
class RoleHierarchy implements RoleHierarchyInterface
{
/**
*
* @var \DocDigital\Bundle\UserBundle\Entity\Role[]
*/
private $hierarchy;
private $parents;
/**
* Constructor.
*
* @param array $hierarchy An array defining the hierarchy
*/
public function __construct(EntityManager $em)
{
// Debug Tool Bar triggers this construct 12 times, only in dev.
$this->hierarchy = $em->getRepository('DdUserBundle:Role')->findAllWithSons();
$this->buildRoleMap();
}
/**
* {@inheritdoc}
*/
public function getReachableRoles(array $roles)
{
$reachableRoles = array();
foreach ($roles as $role) {
$reachableRoles[spl_object_hash($role)] = $role;
if (!isset($this->parents[$role->getRole()])) {
continue;
}
$this->parents[$role->getRole()]->getDescendant($reachableRoles);
}
return array_values($reachableRoles);
}
protected function buildRoleMap()
{
$this->parents = array();
foreach ($this->hierarchy as $role) {
if ($role->getChildren()->count()) {
$this->parents[$role->getRole()] = $role;
}
}
}
}
<?php
namespace DocDigital\Bundle\UserBundle\Entity;
use Doctrine\ORM\EntityRepository;
/**
* RoleRepository Class
*
* @author Juan Manuel Fernandez <juanmf@gmail.com>
*/
class RoleRepository extends EntityRepository
{
/**
* Gets Roles withou parents i.e. Root Roles.
*
* @return array
*/
public function getRootRoles()
{
return $this->createQueryBuilder('r')
->where('r.parent IS NULL')
->getQuery()
->execute();
}
/**
* Prevents lots of lazy loading queries when making Role hierarchy.
*
* @return array
*/
public function findAllWithSons()
{
return $this->createQueryBuilder('r')
->leftJoin('r.children', 'p')
->select('r, p')
->getQuery()
->execute();
}
}
<?php
namespace Application\UsuarioBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class RoleType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('parent')
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Application\UsuarioBundle\Entity\Role'
));
}
/**
* @return string
*/
public function getName()
{
return 'application_usuariobundle_role';
}
}
security:
access_decision_manager:
# strategy can be: affirmative, unanimous or consensus. Default is affirmative
strategy: affirmative
encoders:
FOS\UserBundle\Model\UserInterface: sha512
# role_hierarchy:
# ROLE_ADMIN: [ROLE_USER, ROLE_CONSULTAS_ESTADO_LEGAL, ROLE_WORKFLOW_ADMIN]
# ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
# ROLE_WORKFLOW_ADMIN: [ROLE_MESADEENTRADASYSALIDAS_INVENTARIO_CARGARCARATULA, ROLE_ESTADOLEGAL_SOLICITUDDETIERRA_REDACTARINFORME, ROLE_MESADEENTRADASYSALIDAS_INVENTARIO_IMPRIMIRCARATULA, ROLE_MESADEENTRADASYSALIDAS_SOLICITUDDETIERRA_PRESENTARDOCUMENTACION, ROLE_MESADEENTRADASYSALIDAS_SOLICITUDDETIERRA_SOLICITARINFORMEAESTADOLEGAL, ROLE_TOPOGRAFIA_SOLICITUDDETIERRA_INFORMELIMITEYLINDEROS]
...
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="application_usuario.access.role_hierarchy.class">Application\UsuarioBundle\Role\RoleHierarchy</parameter>
</parameters>
<services>
<service id="usuario_user.registration.form.type" class="Application\UsuarioBundle\Form\UsuarioType">
<tag name="form.type" alias="application_usuariobundle_usuario" />
<argument>%fos_user.model.user.class%</argument>
<argument type="service" id="doctrine.orm.entity_manager" />
</service>
<service id="application_usuario.access.role_hierarchy_voter" class="%security.access.role_hierarchy_voter.class%">
<argument type="service" id="application_usuario.access.role_hierarchy" />
<tag name="security.voter" priority="245" />
</service>
<service id="security.access.expression_voter" class="%security.access.expression_voter.class%">
<argument type="service" id="security.expression_language" />
<argument type="service" id="security.authentication.trust_resolver" />
<argument type="service" id="application_usuario.access.role_hierarchy" on-invalid="null" />
<tag name="security.voter" priority="245" />
</service>
<service id="application_usuario.access.role_hierarchy" class="%application_usuario.access.role_hierarchy.class%">
<argument type="service" id="doctrine.orm.entity_manager" />
</service>
</services>
</container>
<?php
namespace Application\UsuarioBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Application\ProcessBundle\Entity\UserLocation;
/**
* @ORM\Entity
* @ORM\Table(name="fos_user")
* @Assert\Callback(methods={"validatePassword"})
*/
class Usuario extends BaseUser
{
use TimestampableEntity;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// $this->roles = new ArrayCollection();
}
/**
* @var string
* @ORM\Column(name="apellido", type="string", length=255, nullable=true)
* @Assert\NotBlank
*/
private $apellido;
/**
* @var string
* @ORM\Column(name="nombre", type="string", length=255, nullable=true)
* @Assert\NotBlank
*/
private $nombre;
/**
* @ORM\OneToMany(targetEntity="Application\ProcessBundle\Entity\UserLocation", mappedBy="user")
* @var ArrayCollection|UserLocation[]
*/
private $enabledLocations;
/**
* @var ArrayCollection
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users", fetch="EAGER", cascade={"persist"})
* @ORM\JoinTable(name="fos_usuario_role")
*/
protected $roles;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set apellido
*
* @param string $apellido
* @return Usuario
*/
public function setApellido($apellido)
{
$this->apellido = $apellido;
return $this;
}
/**
* Get apellido
*
* @return string
*/
public function getApellido()
{
return $this->apellido;
}
/**
* Set nombre
*
* @param string $nombre
* @return Usuario
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* @return string
*/
public function getNombre()
{
return $this->nombre;
}
public function getEnabledLocations()
{
return $this->enabledLocations;
}
public function setEnabledLocations($enabledLocations)
{
$this->enabledLocations = $enabledLocations;
}
public function addEnabledLocation(UserLocation $enabledLocation)
{
$this->enabledLocations->add($enabledLocation);
}
public function removeEnabledLocation(UserLocation $enabledLocation)
{
$this->enabledLocations->removeElement($enabledLocation);
}
/**
* Si es un usuario nuevo el password debe ser obligatorio.
*
* @param ExecutionContextInterface $ec Contexto de Ejecuci..n.
*
* @return void
*/
public function validatePassword(ExecutionContextInterface $ec)
{
$plainPassword = $this->getPlainPassword();
if (!$this->id && empty($plainPassword)) {
$ec->addViolationAt('plainPassword', 'La contraseña no debe estar vacía.');
}
}
public function getRoles()
{
if (! $this->roles->count()) {
return array(parent::ROLE_DEFAULT);
}
$roles = $this->roles->toArray();
foreach ($this->getGroups() as $group) {
$roles = array_merge($roles, $group->getRoles());
}
foreach ($roles as $k => $role) {
/*
* Ensure String[] to prevent bad unserialized UsernamePasswordToken with for instance
* UPT#roles:{Role('ROLE_USER'), 'ROLE_USER'} which ends in Error: Call to a member
* function getRole() on a non-object
*/
$roles[$k] = $role instanceof RoleInterface ? $role->getRole() : (string) $role;
}
return array_flip(array_flip($roles));
}
public function addRole($role)
{
! ($role instanceof Role) && $role = new Role($role);
$role->addUser($this, false);
$this->roles->add($role);
return $this;
}
public function removeRole($role)
{
$role = $this->roles->filter(
function(Role $r) use ($role) {
if ($role instanceof Role) {
return $r->getRole() === $role->getRole();
} else {
return $r->getRole() === strtoupper($role);
}
}
)->first();
if ($role) {
$this->roles->removeElement($role);
}
return $this;
}
/**
* Nombre y apellido
*/
public function __toString()
{
return sprintf('%s, %s', $this->nombre, $this->apellido);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment