diff --git a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php
index e3cc9c408..3d7b95c61 100644
--- a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php
+++ b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php
@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Workflow;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
+use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
@@ -36,6 +37,13 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
$this->translator = $translator;
}
+ public function getDeletionRoles(): array
+ {
+ return [
+ AccompanyingCourseDocumentVoter::DELETE,
+ ];
+ }
+
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$course = $this->getRelatedEntity($entityWorkflow)
@@ -66,6 +74,18 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
+ /**
+ * @param AccompanyingCourseDocument $object
+ *
+ * @return array[]
+ */
+ public function getRelatedObjects(object $object): array
+ {
+ return [
+ ['entityClass' => AccompanyingCourseDocument::class, 'entityId' => $object->getId()],
+ ];
+ }
+
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@@ -84,6 +104,11 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
];
}
+ public function isObjectSupported(object $object): bool
+ {
+ return $object instanceof AccompanyingCourseDocument;
+ }
+
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class;
diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
index 5ad7348ee..09668f0e3 100644
--- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
+++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
@@ -256,6 +256,13 @@ class ChillMainExtension extends Extension implements
'channels' => ['chill'],
]);
+ $container->prependExtensionConfig('security', [
+ 'access_decision_manager' => [
+ 'strategy' => 'unanimous',
+ 'allow_if_all_abstain' => false,
+ ],
+ ]);
+
//add crud api
$this->prependCruds($container);
}
diff --git a/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php b/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php
index ab58801fc..a73da74b0 100644
--- a/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php
+++ b/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php
@@ -55,6 +55,30 @@ class EntityWorkflowRepository implements ObjectRepository
return (int) $qb->getQuery()->getSingleScalarResult();
}
+ public function countRelatedWorkflows(array $relateds): int
+ {
+ $qb = $this->repository->createQueryBuilder('w');
+
+ $orX = $qb->expr()->orX();
+ $i = 0;
+
+ foreach ($relateds as $related) {
+ $orX->add(
+ $qb->expr()->andX(
+ $qb->expr()->eq('w.relatedEntityClass', ':entity_class_' . $i),
+ $qb->expr()->eq('w.relatedEntityId', ':entity_id_' . $i)
+ )
+ );
+ $qb
+ ->setParameter('entity_class_' . $i, $related['entityClass'])
+ ->setParameter('entity_id_' . $i, $related['entityId']);
+ ++$i;
+ }
+ $qb->where($orX);
+
+ return $qb->select('COUNT(w)')->getQuery()->getSingleScalarResult();
+ }
+
public function find($id): ?EntityWorkflow
{
return $this->repository->find($id);
diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php
new file mode 100644
index 000000000..079c43c3b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php
@@ -0,0 +1,65 @@
+handlers = $handlers;
+ $this->entityWorkflowRepository = $entityWorkflowRepository;
+ }
+
+ protected function supports($attribute, $subject)
+ {
+ if (!is_object($subject)) {
+ return false;
+ }
+
+ foreach ($this->handlers as $handler) {
+ if ($handler->isObjectSupported($subject)
+ && in_array($attribute, $handler->getDeletionRoles($subject), true)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
+ {
+ foreach ($this->handlers as $handler) {
+ if ($handler->isObjectSupported($subject)) {
+ return 0 === $this->entityWorkflowRepository->countRelatedWorkflows(
+ $handler->getRelatedObjects($subject)
+ );
+ }
+ }
+
+ throw new RuntimeException('no handlers found');
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php
index 93ba4351e..b63546742 100644
--- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php
+++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php
@@ -15,12 +15,19 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
interface EntityWorkflowHandlerInterface
{
+ /**
+ * @return array|string[]
+ */
+ public function getDeletionRoles(): array;
+
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object;
+ public function getRelatedObjects(object $object): array;
+
/**
* Return a string representing the role required for seeing the workflow.
*
@@ -33,6 +40,8 @@ interface EntityWorkflowHandlerInterface
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;
+ public function isObjectSupported(object $object): bool;
+
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;
diff --git a/src/Bundle/ChillMainBundle/config/services/security.yaml b/src/Bundle/ChillMainBundle/config/services/security.yaml
index b884a92fc..3347871e3 100644
--- a/src/Bundle/ChillMainBundle/config/services/security.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/security.yaml
@@ -75,3 +75,9 @@ services:
$locker: '@Chill\MainBundle\Security\PasswordRecover\PasswordRecoverLocker'
tags:
- { name: security.voter }
+
+ Chill\MainBundle\Security\Authorization\WorkflowEntityDeletionVoter:
+ autoconfigure: true
+ autowire: true
+ arguments:
+ $handlers: !tagged_iterator chill_main.workflow_handler
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/_item.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/_item.html.twig
index 5af04c843..d7e4f4e96 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/_item.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/_item.html.twig
@@ -130,11 +130,13 @@
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
>
-
-
-
+ {% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
+
+
+
+ {% endif %}
{% endif %}
diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodWorkVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodWorkVoter.php
index 7193c524e..24df2bbc5 100644
--- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodWorkVoter.php
+++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodWorkVoter.php
@@ -24,6 +24,8 @@ class AccompanyingPeriodWorkVoter extends Voter
{
public const CREATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE';
+ public const DELETE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE';
+
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE';
public const UPDATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE';
@@ -60,6 +62,7 @@ class AccompanyingPeriodWorkVoter extends Voter
case self::CREATE:
case self::UPDATE:
+ case self::DELETE:
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $subject->getAccompanyingPeriod());
default:
@@ -86,6 +89,6 @@ class AccompanyingPeriodWorkVoter extends Voter
private function getRoles(): array
{
- return [self::SEE, self::CREATE, self::UPDATE];
+ return [self::SEE, self::CREATE, self::UPDATE, self::DELETE];
}
}
diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php
index eac460ee5..1e02d0465 100644
--- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php
+++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php
@@ -37,6 +37,13 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
$this->translator = $translator;
}
+ public function getDeletionRoles(): array
+ {
+ return [
+ '_',
+ ];
+ }
+
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$doc = $this->getRelatedEntity($entityWorkflow);
@@ -63,6 +70,18 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
+ /**
+ * @param AccompanyingPeriodWorkEvaluationDocument $object
+ *
+ * @return array[]
+ */
+ public function getRelatedObjects(object $object): array
+ {
+ return [
+ ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, $object->getId()],
+ ];
+ }
+
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationDocumentVoter::SEE;
@@ -84,6 +103,11 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
];
}
+ public function isObjectSupported(object $object): bool
+ {
+ return $object instanceof AccompanyingPeriodWorkEvaluationDocument;
+ }
+
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluationDocument::class;
diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php
index 5af305ac5..7bbf54eef 100644
--- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php
+++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php
@@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -37,6 +38,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
$this->translator = $translator;
}
+ public function getDeletionRoles(): array
+ {
+ return ['_'];
+ }
+
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$evaluation = $this->getRelatedEntity($entityWorkflow);
@@ -61,6 +67,20 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
+ /**
+ * @param AccompanyingPeriodWorkEvaluation $object
+ */
+ public function getRelatedObjects(object $object): array
+ {
+ $relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $object->getId()];
+
+ foreach ($object->getDocuments() as $doc) {
+ $relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
+ }
+
+ return $relateds;
+ }
+
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationVoter::SEE;
@@ -79,6 +99,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
];
}
+ public function isObjectSupported(object $object): bool
+ {
+ return $object instanceof AccompanyingPeriodWorkEvaluation;
+ }
+
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluation::class;
diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php
index 1385cb841..90e7b4e46 100644
--- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php
+++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php
@@ -15,7 +15,10 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
+use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
@@ -36,6 +39,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
$this->translator = $translator;
}
+ public function getDeletionRoles(): array
+ {
+ return [AccompanyingPeriodWorkVoter::DELETE];
+ }
+
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
return [
@@ -58,6 +66,24 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
+ /**
+ * @param AccompanyingPeriodWork $object
+ */
+ public function getRelatedObjects(object $object): array
+ {
+ $relateds[] = ['entityClass' => AccompanyingPeriodWork::class, 'entityId' => $object->getId()];
+
+ foreach ($object->getAccompanyingPeriodWorkEvaluations() as $evaluation) {
+ $relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $evaluation->getId()];
+
+ foreach ($evaluation->getDocuments() as $doc) {
+ $relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
+ }
+ }
+
+ return $relateds;
+ }
+
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@@ -76,6 +102,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
];
}
+ public function isObjectSupported(object $object): bool
+ {
+ return $object instanceof AccompanyingPeriodWork;
+ }
+
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWork::class;