Commit 9c368d6b authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

WIP Module Cotutelle

parent 14630eef
Loading
Loading
Loading
Loading
+4 −0
Changes for CHANGELOG.md: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
Journal des modifications
=========================

14.0.0
------
- Nouveau module de gestion des cotutelles de thèse.

13.5.0
------
- Formations : petites améliorations concernant les enquêtes.
+193 −0
Changes for bin/generate_entity_elements/Module/config/place-holder.config.php: 193 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Module;

use Laminas\Router\Http\Segment;
use Module\Assertion\PlaceHolderAssertion;
use Module\Assertion\PlaceHolderAssertionFactory;
use Module\Controller\PlaceHolderController;
use Module\Controller\PlaceHolderControllerFactory;
use Module\Entity\Db\Service\PlaceHolderService;
use Module\Entity\Db\Service\PlaceHolderServiceFactory;
use Module\Form\PlaceHolderForm;
use Module\Form\PlaceHolderFormFactory;
use Module\Hydrator\PlaceHolderHydrator;
use Module\Hydrator\PlaceHolderHydratorFactory;
use Module\Provider\PlaceHolderPrivileges;
use UnicaenPrivilege\Guard\PrivilegeController;
use UnicaenPrivilege\Provider\Rule\PrivilegeRuleProvider;

return [
    'bjyauthorize' => [
        'resource_providers' => [
            'BjyAuthorize\Provider\Resource\Config' => [
                'PlaceHolder' => [],
            ],
        ],
        'rule_providers' => [
            PrivilegeRuleProvider::class => [
                'allow' => [
                    [
                        'privileges' => [
                            PlaceHolderPrivileges::PlaceHolder_CONSULTER_TOUTES,
                            PlaceHolderPrivileges::PlaceHolder_CONSULTER_MIENNES,
                            PlaceHolderPrivileges::PlaceHolder_AJOUTER_TOUTES,
                            PlaceHolderPrivileges::PlaceHolder_AJOUTER_MIENNES,
                            PlaceHolderPrivileges::PlaceHolder_MODIFIER_TOUTES,
                            PlaceHolderPrivileges::PlaceHolder_MODIFIER_MIENNES,
                            PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_TOUTES,
                            PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_MIENNES,
                        ],
                        'resources' => ['PlaceHolder'],
                        'assertion' => PlaceHolderAssertion::class,
                    ],
                ],
            ],
        ],
        'guards' => [
            PrivilegeController::class => [
                [
                    'controller' => PlaceHolderController::class,
                    'action' => [
                        'consulter',
                    ],
                    'privileges' => [
                        PlaceHolderPrivileges::PlaceHolder_CONSULTER_TOUTES,
                        PlaceHolderPrivileges::PlaceHolder_CONSULTER_MIENNES,
                    ],
                    'assertion' => PlaceHolderAssertion::class,place-holder
                ],
                [
                    'controller' => PlaceHolderController::class,
                    'action' => [
                        'ajouter',
                    ],
                    'privileges' => [
                        PlaceHolderPrivileges::PlaceHolder_AJOUTER_TOUTES,
                        PlaceHolderPrivileges::PlaceHolder_AJOUTER_MIENNES,
                    ],
                    'assertion' => PlaceHolderAssertion::class,
                ],
                [
                    'controller' => PlaceHolderController::class,
                    'action' => [
                        'modifier',
                    ],
                    'privileges' => [
                        PlaceHolderPrivileges::PlaceHolder_MODIFIER_TOUTES,
                        PlaceHolderPrivileges::PlaceHolder_MODIFIER_MIENNES,
                    ],
                    'assertion' => PlaceHolderAssertion::class,
                ],
                [
                    'controller' => PlaceHolderController::class,
                    'action' => [
                        'supprimer',
                    ],
                    'privileges' => [
                        PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_TOUTES,
                        PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_MIENNES,
                    ],
                    'assertion' => PlaceHolderAssertion::class,
                ],
            ],
        ],
    ],
    'router' => [
        'routes' => [
            'these' => [
                'child_routes' => [
                    'place-holder' => [
                        'type' => Segment::class,
                        'options' => [
                            'route' => '/place-holder',
                            'defaults' => [
                                'controller' => PlaceHolderController::class,
                            ],
                        ],
                        'may_terminate' => false,
                        'child_routes' => [
                            'ajouter' => [
                                'type' => Segment::class,
                                'options' => [
                                    'route' => '/ajouter/:these',
                                    'constraints' => [
                                        'these' => '\d+',
                                    ],
                                    'defaults' => [
                                        /** @see PlaceHolderController::ajouterAction() */
                                        'action' => 'ajouter',
                                    ],
                                ],
                            ],
                            'consulter' => [
                                'type' => Segment::class,
                                'options' => [
                                    'route' => '/consulter/:these',
                                    'constraints' => [
                                        'these' => '\d+',
                                    ],
                                    'defaults' => [
                                        /** @see PlaceHolderController::consulterAction() */
                                        'action' => 'consulter',
                                    ],
                                ],
                            ],
                            'modifier' => [
                                'type' => Segment::class,
                                'options' => [
                                    'route' => '/modifier/:placeHolder',
                                    'constraints' => [
                                        'placeHolder' => '\d+',
                                    ],
                                    'defaults' => [
                                        /** @see PlaceHolderController::modifierAction() */
                                        'action' => 'modifier',
                                    ],
                                ],
                            ],
                            'supprimer' => [
                                'type' => Segment::class,
                                'options' => [
                                    'route' => '/supprimer/:placeHolder',
                                    'constraints' => [
                                        'placeHolder' => '\d+',
                                    ],
                                    'defaults' => [
                                        /** @see PlaceHolderController::supprimerAction() */
                                        'action' => 'supprimer',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
    'form_elements' => [
        'factories' => [
            PlaceHolderForm::class => PlaceHolderFormFactory::class,
        ],
    ],
    'validators' => [
        'factories' => [
        ],
    ],
    'hydrators' => [
        'factories' => [
            PlaceHolderHydrator::class  => PlaceHolderHydratorFactory::class,
        ],
    ],
    'controllers' => [
        'factories' => [
            PlaceHolderController::class => PlaceHolderControllerFactory::class,
        ],
    ],
    'service_manager' => [
        'factories' => [
            PlaceHolderAssertion::class => PlaceHolderAssertionFactory::class,
            PlaceHolderService::class => PlaceHolderServiceFactory::class,
        ],
    ],
];
 No newline at end of file
+201 −0
Changes for bin/generate_entity_elements/Module/src/Module/Assertion/PlaceHolderAssertion.php: 201 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Module\Assertion;

use Application\Assertion\AbstractAssertion;
use Application\Assertion\Exception\FailedAssertionException;
use Laminas\Permissions\Acl\Resource\ResourceInterface;
use These\Assertion\These\AssertAppartenanceTheseTrait;
use These\Entity\Db\These;
use These\Service\These\TheseServiceAwareTrait;
use Module\Entity\Db\PlaceHolder;
use Module\Entity\Db\Service\PlaceHolderServiceAwareTrait;
use Module\Provider\PlaceHolderPrivileges;
use Webmozart\Assert\Assert;

class PlaceHolderAssertion extends AbstractAssertion
{
    use TheseServiceAwareTrait;
    use PlaceHolderServiceAwareTrait;
    use AssertAppartenanceTheseTrait;

    protected function assertEntity(ResourceInterface $entity, $privilege = null): bool
    {
        if (!parent::assertEntity($entity, $privilege)) {
            return false;
        }

        Assert::isInstanceOf($entity, PlaceHolder::class);
        $placeHolder = $entity;
        $these = $placeHolder->getThese();

        try {
            switch ($privilege) {
                case PlaceHolderPrivileges::PlaceHolder_CONSULTER_MIENNES:
                case PlaceHolderPrivileges::PlaceHolder_AJOUTER_MIENNES:
                case PlaceHolderPrivileges::PlaceHolder_MODIFIER_MIENNES:
                case PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_MIENNES:
                    $this->assertAppartenanceThese($these);
            }

            switch ($privilege) {
                case PlaceHolderPrivileges::PlaceHolder_AJOUTER_TOUTES:
                case PlaceHolderPrivileges::PlaceHolder_AJOUTER_MIENNES:
                    $this->assertCotutelleInexistante($these);
            }

            switch ($privilege) {
                case PlaceHolderPrivileges::PlaceHolder_MODIFIER_TOUTES:
                case PlaceHolderPrivileges::PlaceHolder_MODIFIER_MIENNES:
//                    $this->assertEtatCotutelle($placeHolder, [
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_NEGOCIATION,
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_SIGNATURE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_SIGNE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_PROJET_ABANDONNE,
//                    ]);
            }

            switch ($privilege) {
                case PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_TOUTES:
                case PlaceHolderPrivileges::PlaceHolder_SUPPRIMER_MIENNES:
//                    $this->assertEtatCotutelle($placeHolder, [
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_NEGOCIATION,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_SIGNATURE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_SIGNE,
//                        PlaceHolder::ETAT_ADMINISTRATIF_PROJET_ABANDONNE,
//                    ]);
            }
        } catch (FailedAssertionException $e) {
            if ($e->getMessage()) {
                $this->getServiceMessageCollector()->addMessage($e->getMessage(), __CLASS__);
            }
            return false;
        }

        return true;
    }

    protected function assertController($controller, $action = null, $privilege = null): bool
    {
        if (!parent::assertController($controller, $action, $privilege)) {
            return false;
        }

        // init en fonction de l'action
        $placeHolder = null;
        $these = null;
        switch ($action) {
            case 'consulter':
            case 'ajouter':
                $these = $this->getRequestedThese();
                if ($these === null) {
                    // pas de thèse dispo dans le route match (on est dans ce cas lors de la génération de la navigation)
                    // pas le choix : il faut retourner `true`
                    return true;
                }
                break;

            case 'modifier':
            case 'supprimer':
                $placeHolder = $this->getRequestedPlaceHolder();
                if ($placeHolder === null) {
                    // pas de cotutelle dispo dans le route match (on est dans ce cas lors de la génération de la navigation)
                    // pas le choix : il faut retourner `true`
                    return true;
                }
                $these = $placeHolder->getThese();
                break;
        }

        try {
            switch ($action) {
                case 'consulter':
                case 'ajouter':
                case 'modifier':
                case 'supprimer':
                    $this->assertAppartenanceThese($these);
            }

            switch ($action) {
                case 'ajouter':
                    $this->assertCotutelleInexistante($these);
                    break;
            }

            switch ($action) {
                case 'modifier':
//                    $this->assertEtatCotutelle($placeHolder, [
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_NEGOCIATION,
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_SIGNATURE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_SIGNE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_PROJET_ABANDONNE,
//                    ]);
                    break;
            }

            switch ($action) {
                case 'supprimer':
//                    $this->assertEtatCotutelle($placeHolder, [
//                        PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_NEGOCIATION,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_EN_COURS_DE_SIGNATURE,
//                        //PlaceHolder::ETAT_ADMINISTRATIF_SIGNE,
//                        PlaceHolder::ETAT_ADMINISTRATIF_PROJET_ABANDONNE,
//                    ]);
                    break;
            }
        } catch (FailedAssertionException $e) {
            if ($e->getMessage()) {
                $this->getServiceMessageCollector()->addMessage($e->getMessage(), __CLASS__);
            }
            return false;
        }

        return true;
    }

    private function getRequestedThese(): ?These
    {
        $these = null;
        if ($id = $this->getRouteMatch()?->getParam('these')) {
            /** @var These $these */
            $these = $this->theseService->getRepository()->find($id);
            Assert::notNull($these);
        }

        return $these;
    }

    private function getRequestedPlaceHolder(): ?PlaceHolder
    {
        $placeHolder = null;
        if ($id = $this->getRouteMatch()?->getParam('place-holder')) {
            /** @var PlaceHolder $placeHolder */
            $placeHolder = $this->placeHolderService->getRepository()->find($id);
            Assert::notNull($placeHolder);
        }

        return $placeHolder;
    }

    /**
     * @throws \Application\Assertion\Exception\FailedAssertionException
     */
    private function assertCotutelleInexistante(These $these): void
    {
        $this->assertTrue(
            $these->getCotutelle() === null,
            "Une cotutelle existe déjà sur cette thèse."
        );
    }

    /**
     * @throws \Application\Assertion\Exception\FailedAssertionException
     */
    private function assertEtatCotutelle(PlaceHolder $placeHolder, array $etatsPossibles): void
    {
        $this->assertTrue(
            in_array($placeHolder->getEtatAdministratif(), $etatsPossibles),
            "L'état administratif de cette cotutelle ne permet plus sa modification."
        );
    }
}
 No newline at end of file
+50 −0
Changes for bin/generate_entity_elements/Module/src/Module/Assertion/PlaceHolderAssertionFactory.php: 50 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Module\Assertion;

use Application\Assertion\AbstractAssertion;
use Psr\Container\ContainerInterface;
use These\Service\These\TheseService;
use Module\Entity\Db\Service\PlaceHolderService;
use UnicaenAuthentification\Service\UserContext;

class PlaceHolderAssertionFactory
{
    /**
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function __invoke(ContainerInterface $container): PlaceHolderAssertion
    {
        $assertion = new PlaceHolderAssertion();

        /** @var \These\Service\These\TheseService $theseService */
        $theseService = $container->get(TheseService::class);
        $assertion->setTheseService($theseService);

        /** @var \Module\Entity\Db\Service\PlaceHolderService $placeHolderService */
        $placeHolderService = $container->get(PlaceHolderService::class);
        $assertion->setPlaceHolderService($placeHolderService);

        $this->injectCommons($assertion, $container);

        return $assertion;
    }

    /**
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    protected function injectCommons(AbstractAssertion $assertion, ContainerInterface $container): void
    {
        $authorizeService = $container->get('BjyAuthorize\Service\Authorize');
        $mvcEvent = $container->get('Application')->getMvcEvent();
        $userContext = $container->get(UserContext::class);
        $messageCollector = $container->get('MessageCollector');

        $assertion->setServiceAuthorize($authorizeService);
        $assertion->setMvcEvent($mvcEvent);
        $assertion->setUserContextService($userContext);
        $assertion->setServiceMessageCollector($messageCollector);
    }
}
 No newline at end of file
+152 −0
Changes for bin/generate_entity_elements/Module/src/Module/Controller/PlaceHolderController.php: 152 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Module\Controller;

use Application\Controller\AbstractController;
use Exception;
use Laminas\Http\Response;
use Laminas\View\Model\ViewModel;
use Structure\Service\Etablissement\EtablissementServiceAwareTrait;
use These\Entity\Db\These;
use These\Service\These\TheseServiceAwareTrait;
use Module\Entity\Db\PlaceHolder;
use Module\Entity\Db\Service\PlaceHolderServiceAwareTrait;
use Module\Form\PlaceHolderForm;
use Webmozart\Assert\Assert;

class PlaceHolderController extends AbstractController
{
    use TheseServiceAwareTrait;
    use PlaceHolderServiceAwareTrait;
    use EtablissementServiceAwareTrait;

    private These $these;
    protected ?PlaceHolder $placeHolder;
    protected PlaceHolderForm $placeHolderForm;

    public function setPlaceHolderForm(PlaceHolderForm $placeHolderForm): void
    {
        $this->placeHolderForm = $placeHolderForm;
    }

    public function consulterAction(): ViewModel
    {
        $vm = new ViewModel();

        $this->these = $this->getRequestedThese();
        $this->placeHolder = $this->placeHolderService->getRepository()->findOneByThese($this->these);

        $vm->setVariables([
            'these' => $this->these,
            'placeHolder' => $this->placeHolder,
        ]);

        return $vm;
    }

    public function ajouterAction(): Response|ViewModel
    {
        $this->these = $this->getRequestedThese();
        $this->placeHolder = new PlaceHolder($this->these);

        return $this->ajouterModifier();
    }

    public function modifierAction(): Response|ViewModel
    {
        $this->placeHolder = $this->getRequestedPlaceHolder();
        $this->these = $this->placeHolder->getThese();

        return $this->ajouterModifier();
    }

    private function ajouterModifier(): Response|ViewModel
    {
        $this->placeHolderForm->setAttribute('action', $this->url()->fromRoute(reuseMatchedParams: true));
        $this->placeHolderForm->bind($this->placeHolder);

        $request = $this->getRequest();
        if ($request->isPost()) {
            $data = $request->getPost();
            $this->placeHolderForm->setData($data);
            if ($this->placeHolderForm->isValid()) {
                try {
                    $this->savePlaceHolder();
                    $this->flashMessenger()->addSuccessMessage(sprintf(
                        "La cotutelle pour la thèse de %s a été enregistrée avec succès.",
                        $this->placeHolder->getThese()->getDoctorant()
                    ));
                    return $this->redirect()->toRoute(
                        'these/cotutelle/consulter',
                        ['these' => $this->these->getId(), 'place-holder' => $this->placeHolder->getId()],
                        reuseMatchedParams: true
                    );
                } catch (Exception $e) {
                    $this->flashMessenger()->addErrorMessage(sprintf(
                        "Impossible d'enregistrer la cotutelle pour la thèse de %s ! " . $e->getMessage(),
                        $this->placeHolder->getThese()->getDoctorant()
                    ));
                }
//                if (!$request->isXmlHttpRequest()) {
//                    return $this->redirect()->toUrl($this->urlIdentite());
//                }
            }
        }

        $vm = new ViewModel();
        $vm->setVariables([
            'form' => $this->placeHolderForm,
            'title' => $this->placeHolder->getId() ? "Modification" : "Ajout",
        ]);
        $vm->setTemplate("module/place-holder/modifier");

        return $vm;
    }

    public function supprimerAction(): Response
    {
        $this->placeHolder = $this->getRequestedPlaceHolder();

        $this->placeHolder->historiser($this->userContextService->getDbUser());

        try {
            $this->placeHolderService->savePlaceHolder($this->placeHolder);
            $this->flashMessenger()->addSuccessMessage(sprintf(
                "La cotutelle pour la thèse de %s a été supprimée (historisée).",
                $this->placeHolder->getThese()->getDoctorant()
            ));
        } catch (Exception $e) {
            $this->flashMessenger()->addErrorMessage(sprintf(
                "Impossible de supprimer la cotutelle pour la thèse de %s ! " . $e->getMessage(),
                $this->placeHolder->getThese()->getDoctorant()
            ));
        }

        return $this->redirect()->toRoute('these/identite', ['these' => $this->placeHolder->getThese()->getId()]);
    }

    /**
     * @throws \Exception
     */
    private function savePlaceHolder(): void
    {
        $this->placeHolderService->savePlaceHolder($this->placeHolder);

    }

    private function getRequestedThese(): These
    {
        /** @var \These\Entity\Db\These $these */
        $these = $this->theseService->getRepository()->find($this->params('these'));
        Assert::notNull($these);
        return $these;
    }

    private function getRequestedPlaceHolder(): PlaceHolder
    {
        /** @var \Module\Entity\Db\PlaceHolder $placeHolder */
        $placeHolder = $this->placeHolderService->getRepository()->find($this->params('placeHolder'));
        Assert::notNull($placeHolder);
        return $placeHolder;
    }
}
 No newline at end of file
Loading