diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 3f8356bb4..cba5abeb7 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -269,8 +269,7 @@ final class ActivityController extends AbstractController } - // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période - // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_CREATE', $entity); + $this->denyAccessUnlessGranted(ActivityVoter::CREATE, $entity); $form = $this->createForm(ActivityType::class, $entity, [ 'center' => $entity->getCenter(), diff --git a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php index 331db2305..44df0d543 100644 --- a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php +++ b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php @@ -19,6 +19,7 @@ namespace Chill\ActivityBundle\DataFixtures\ORM; +use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Persistence\ObjectManager; @@ -40,7 +41,7 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac return 16000; } - + public function load(ObjectManager $manager) { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { @@ -58,20 +59,23 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac case 'direction': if (in_array($scope->getName()['en'], array('administrative', 'social'))) { break 2; // we do not want any power on social or administrative - } + } break; } - + printf("Adding CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE & CHILL_ACTIVITY_DELETE, and stats and list permissions to %s " - . "permission group, scope '%s' \n", + . "permission group, scope '%s' \n", $permissionsGroup->getName(), $scope->getName()['en']); $roleScopeUpdate = (new RoleScope()) ->setRole('CHILL_ACTIVITY_UPDATE') ->setScope($scope); $permissionsGroup->addRoleScope($roleScopeUpdate); $roleScopeCreate = (new RoleScope()) - ->setRole('CHILL_ACTIVITY_CREATE') + ->setRole(ActivityVoter::CREATE_ACCOMPANYING_COURSE) ->setScope($scope); + $roleScopeCreate = (new RoleScope()) + ->setRole(ActivityVoter::CREATE_PERSON) + ->setScope($scope); $permissionsGroup->addRoleScope($roleScopeCreate); $roleScopeDelete = (new RoleScope()) ->setRole('CHILL_ACTIVITY_DELETE') @@ -85,14 +89,14 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac ->setRole(ActivityStatsVoter::STATS) ; $permissionsGroup->addRoleScope($roleScopeStat); - + $manager->persist($roleScopeUpdate); $manager->persist($roleScopeCreate); $manager->persist($roleScopeDelete); } - + } - + $manager->flush(); } diff --git a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php index f4aeba174..a7d0ebf25 100644 --- a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php +++ b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php @@ -85,7 +85,8 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf $container->prependExtensionConfig('security', array( 'role_hierarchy' => array( ActivityVoter::UPDATE => array(ActivityVoter::SEE_DETAILS), - ActivityVoter::CREATE => array(ActivityVoter::SEE_DETAILS), + ActivityVoter::CREATE_PERSON => array(ActivityVoter::SEE_DETAILS), + ActivityVoter::CREATE_ACCOMPANYING_COURSE => array(ActivityVoter::SEE_DETAILS), ActivityVoter::DELETE => array(ActivityVoter::SEE_DETAILS), ActivityVoter::SEE_DETAILS => array(ActivityVoter::SEE), ActivityVoter::FULL => [ActivityVoter::CREATE, ActivityVoter::DELETE, diff --git a/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityVoter.php b/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityVoter.php index a8df6ff26..6c81baab4 100644 --- a/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityVoter.php +++ b/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityVoter.php @@ -40,7 +40,32 @@ use Symfony\Component\Security\Core\Security; */ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface { + /** + * allow to create an activity, which will either be associated to an + * accompanying course or person. + * + * It is safe for usage in template and controller + */ const CREATE = 'CHILL_ACTIVITY_CREATE'; + + /** + * role to allow to create an activity associated win an accompanying course. + * + * Before using this, check if @link{self::CREATE} is not sufficiant + * + * @internal + */ + const CREATE_ACCOMPANYING_COURSE = 'CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE'; + + /** + * role to allow to create an activity associated with a person + * + * Before using this, check if @link{self::CREATE} is not sufficiant + * + * @internal + */ + const CREATE_PERSON = 'CHILL_ACTIVITY_CREATE_PERSON'; + const SEE = 'CHILL_ACTIVITY_SEE'; const SEE_DETAILS = 'CHILL_ACTIVITY_SEE_DETAILS'; const UPDATE = 'CHILL_ACTIVITY_UPDATE'; @@ -90,10 +115,22 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn if (!$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) { return false; } + + // change attribute CREATE + if (self::CREATE === $attribute) { + $attribute = self::CREATE_PERSON; + } } elseif ($subject->getAccompanyingPeriod() instanceof AccompanyingPeriod) { if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getAccompanyingPeriod())) { return false; } + + if (self::CREATE === $attribute) { + if (AccompanyingPeriod::STEP_CLOSED === $subject->getAccompanyingPeriod()->getStep()) { + return false; + } + $attribute = self::CREATE_ACCOMPANYING_COURSE; + } } else { throw new \RuntimeException("could not determine context of activity"); } @@ -106,16 +143,23 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn } } } - + return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); } public function getRoles() { - return self::ALL; + return [ + self::CREATE_PERSON, + self::CREATE_ACCOMPANYING_COURSE, + self::UPDATE, + self::DELETE, + self::FULL + ]; } + public function getRolesWithoutScope() { return []; diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/index.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/index.html.twig index fbea1229a..dd0c9cc62 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/index.html.twig +++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/index.html.twig @@ -62,7 +62,7 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php b/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php index 651ee47e4..0e9b2f40a 100644 --- a/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php +++ b/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php @@ -71,17 +71,21 @@ class AccompanyingCourseDocumentVoter extends AbstractChillVoter implements Prov return false; } - if ($subject instanceof AccompanyingCourseDocument - && !$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getCourse())) { - return false; - } - if ($subject instanceof AccompanyingPeriod) { if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) { - if (\in_array($attribute, [self::UPDATE, self::CREATE, self::DELETE])) { + if ($attribute === self::CREATE) { return false; } } + } elseif ($subject instanceof AccompanyingCourseDocument) { + if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getCourse())) { + return false; + } + + if (AccompanyingPeriod::STEP_CLOSED === $subject->getCourse()->getStep() + && \in_array($attribute, [self::CREATE, self::DELETE, self::UPDATE])) { + return false; + } } return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 2c978e212..05c610af5 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -5,6 +5,7 @@ namespace Chill\PersonBundle\Menu; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Knp\Menu\MenuItem; +use Symfony\Component\Workflow\Registry; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -20,10 +21,12 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface * @var TranslatorInterface */ protected $translator; + protected Registry $registry; - public function __construct(TranslatorInterface $translator) + public function __construct(TranslatorInterface $translator, Registry $registry) { $this->translator = $translator; + $this->registry = $registry; } public static function getMenuIds(): array @@ -33,6 +36,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface public function buildMenu($menuId, MenuItem $menu, array $parameters): void { + /** @var AccompanyingPeriod $period */ $period = $parameters['accompanyingCourse']; $menu->addChild($this->translator->trans('Resume Accompanying Course'), [ @@ -68,13 +72,16 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface ]]) ->setExtras(['order' => 40]); - $menu->addChild($this->translator->trans('Close Accompanying Course'), [ - 'route' => 'chill_person_accompanying_course_close', - 'routeParameters' => [ - 'accompanying_period_id' => $period->getId() - ]]) - ->setExtras(['order' => 500]); + $workflow = $this->registry->get($period, 'accompanying_period_lifecycle'); + if ($workflow->can($period, 'close')) { + $menu->addChild($this->translator->trans('Close Accompanying Course'), [ + 'route' => 'chill_person_accompanying_course_close', + 'routeParameters' => [ + 'accompanying_period_id' => $period->getId() + ]]) + ->setExtras(['order' => 99999]); + } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue index 5c9597dc1..c342c84a2 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue @@ -27,9 +27,17 @@ - + {{ $t('course.step.closed') }} + + + {{ $d(accompanyingCourse.openingDate.datetime, 'text') }} - {{ $d(accompanyingCourse.closingDate.datetime, 'text') }} + + + {{ $t('course.referrer') }}: {{ accompanyingCourse.user.username }} + +