Commit 8e70ab67 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Merge branch 'liste_diff' into develop

parents 59f4f8b2 c5670132
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@
#   2/ l'URL du web service, FACULTATIF ("https://facile.cines.fr/xml", par défaut).
##########################################################################################

#export http_proxy=proxy.unicaen.fr:3128
#export https_proxy=proxy.unicaen.fr:3128
export http_proxy=proxy.unicaen.fr:3128
export https_proxy=proxy.unicaen.fr:3128

DEFAULT_URL="https://facile.cines.fr/xml"

+38 −8
Original line number Diff line number Diff line
@@ -7,10 +7,12 @@ use Application\Controller\ListeDiffusionController;
use Application\Provider\Privilege\ListeDiffusionPrivileges;
use Application\Service\ListeDiffusion\ListeDiffusionService;
use Application\Service\ListeDiffusion\ListeDiffusionServiceFactory;
use Application\Service\ListeDiffusion\Plugin\ListeDiffusionRolePlugin;
use Application\Service\ListeDiffusion\Plugin\ListeDiffusionRolePluginFactory;
use Application\Service\ListeDiffusion\Plugin\ListeDiffusionStructurePlugin;
use Application\Service\ListeDiffusion\Plugin\ListeDiffusionStructurePluginFactory;
use Application\Service\ListeDiffusion\Plugin\Structure\ListeDiffusionEcoleDoctoralePlugin;
use Application\Service\ListeDiffusion\Plugin\Structure\ListeDiffusionEcoleDoctoralePluginFactory;
use Application\Service\ListeDiffusion\Handler\ListeDiffusionHandler;
use Application\Service\ListeDiffusion\Handler\ListeDiffusionHandlerFactory;
use Application\Service\ListeDiffusion\Plugin\Structure\ListeDiffusionUniteRecherchePlugin;
use Application\Service\ListeDiffusion\Plugin\Structure\ListeDiffusionUniteRecherchePluginFactory;
use UnicaenAuth\Guard\PrivilegeController;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
@@ -30,6 +32,7 @@ return [
                    'controller' => ListeDiffusionController::class,
                    'action' => [
                        'consulter',
                        'exporter-tableau',
                    ],
                    'privileges' => ListeDiffusionPrivileges::LISTE_DIFFUSION_CONSULTER,
                ],
@@ -60,11 +63,11 @@ return [
                    'liste' => [
                        'type' => Segment::class,
                        'options' => [
                            'route' => '/:liste',
                            'route' => '/:adresse',
                            // exemple: /consulter/ed591.doctorants.ucn@normandie-univ.fr
                            // exemple: /consulter/ed591.doctorants@normandie-univ.fr
                            'constraints' => [
                                'liste' => '[a-zA-Z0-9-_.@]+',
                                'adresse' => '[a-zA-Z0-9-_.@]+',
                            ],
                            'defaults' => [
                                /** @see ListeDiffusionController::consulterAction() */
@@ -95,6 +98,34 @@ return [
                            ],
                        ],
                    ],
                    'export-tableau' => [
                        'type' => Literal::class,
                        'options' => [
                            'route' => '/exporter-tableau',
                            'defaults' => [
                                /** @see ListeDiffusionController::exporterTableauAction() */
                                'action' => 'exporter-tableau',
                            ],
                        ],
                    ]
                ],
            ],
        ],
    ],
    'navigation' => [
        'default' => [
            'home' => [
                'pages' => [
                    'admin' => [
                        'pages' => [
                            'liste-diffusion' => [
                                'label'    => 'Listes de diffusion',
                                'route'    => 'liste-diffusion',
                                'icon'     => 'glyphicon glyphicon-enveloppe',
                                'resource' => PrivilegeController::getResourceId(ListeDiffusionController::class, 'index'),
                            ],
                        ],
                    ],
                ],
            ],
        ],
@@ -102,8 +133,7 @@ return [
    'service_manager' => [
        'factories' => [
            ListeDiffusionService::class => ListeDiffusionServiceFactory::class,
            ListeDiffusionStructurePlugin::class => ListeDiffusionStructurePluginFactory::class,
            ListeDiffusionRolePlugin::class => ListeDiffusionRolePluginFactory::class,
            ListeDiffusionHandler::class => ListeDiffusionHandlerFactory::class,
        ],
    ],
    'controllers' => [
+28 −0
Original line number Diff line number Diff line
@@ -3,10 +3,14 @@
namespace Application\Controller\Factory;

use Application\Controller\ListeDiffusionController;
use Application\Service\EcoleDoctorale\EcoleDoctoraleService;
use Application\Service\Etablissement\EtablissementService;
use Application\Service\File\FileService;
use Application\Service\Individu\IndividuService;
use Application\Service\ListeDiffusion\ListeDiffusionService;
use Application\Service\Notification\NotifierService;
use Application\Service\Role\RoleService;
use Application\Service\Structure\StructureService;
use Interop\Container\ContainerInterface;

class ListeDiffusionControllerFactory
@@ -39,6 +43,30 @@ class ListeDiffusionControllerFactory
        $notifierService = $container->get(NotifierService::class);
        $controller->setNotifierService($notifierService);

        /**
         * @var RoleService $roleService
         */
        $roleService = $container->get(RoleService::class);
        $controller->setRoleService($roleService);

        /**
         * @var EtablissementService $etablissementService
         */
        $etablissementService = $container->get(EtablissementService::class);
        $controller->setEtablissementService($etablissementService);

        /**
         * @var StructureService $structureService
         */
        $structureService = $container->get(StructureService::class);
        $controller->setStructureService($structureService);

        /**
         * @var EtablissementService $etablissementService
         */
        $ecoleDoctoraleService = $container->get(EcoleDoctoraleService::class);
        $controller->setEcoleDoctoraleService($ecoleDoctoraleService);

        return $controller;
    }
}
 No newline at end of file
+272 −11
Original line number Diff line number Diff line
@@ -2,13 +2,27 @@

namespace Application\Controller;

use Application\Entity\Db\EcoleDoctorale;
use Application\Entity\Db\Etablissement;
use Application\Entity\Db\Individu;
use Application\Entity\Db\ListeDiffusion;
use Application\Entity\Db\Role;
use Application\Entity\Db\TypeStructure;
use Application\Service\EcoleDoctorale\EcoleDoctoraleServiceAwareTrait;
use Application\Service\Etablissement\EtablissementServiceAwareTrait;
use Application\Service\File\FileServiceAwareTrait;
use Application\Service\Individu\IndividuServiceAwareTrait;
use Application\Service\ListeDiffusion\Address\ListeDiffusionAddressGenerator;
use Application\Service\ListeDiffusion\ListeDiffusionServiceAwareTrait;
use Application\Service\Notification\NotifierServiceAwareTrait;
use Application\Service\Role\RoleServiceAwareTrait;
use Application\Service\Structure\StructureServiceAwareTrait;
use Doctrine\ORM\ORMException;
use InvalidArgumentException;
use SplObjectStorage;
use UnicaenApp\View\Model\CsvModel;
use Webmozart\Assert\Assert;
use Zend\Http\Response;
use Zend\View\Model\ViewModel;

class ListeDiffusionController extends AbstractController
@@ -17,6 +31,10 @@ class ListeDiffusionController extends AbstractController
    use IndividuServiceAwareTrait;
    use FileServiceAwareTrait;
    use NotifierServiceAwareTrait;
    use StructureServiceAwareTrait;
    use EtablissementServiceAwareTrait;
    use EcoleDoctoraleServiceAwareTrait;
    use RoleServiceAwareTrait;

    /**
     * Adresse complète de la liste de diffusion, ex :
@@ -29,22 +47,245 @@ class ListeDiffusionController extends AbstractController
     * - 'doctorants' (ou 'dirtheses') est la "cible" ;
     * - 'insa' est le source_code unique de l'établissement en minuscules.
     *
     * @var string
     * @var ListeDiffusion
     */
    protected $liste;
    private $liste;

    /**
     * @return array|ViewModel
     * @var SplObjectStorage
     */
    private $dataByEtablissement;

    /**
     * @var ListeDiffusion[] Format : 'adresse' => ListeDiffusion
     */
    private $listesDiffusionActives = [];

    /**
     * @var string[] Format : 'adresse' => 'adresse'
     */
    private $adressesGenerees = [];

    /**
     * @return array|Response|ViewModel
     */
    public function indexAction()
    {
        $listes = $this->listeDiffusionService->fetchListesDiffusion();
        $etablissement = $this->params()->fromQuery('etablissement');

        if ($this->params()->fromPost()) {
            return $this->modifierListes();
        }

        /** @var EcoleDoctorale[] $ecolesDoctorales */
        $etablissements = $this->etablissementService->getRepository()->findAllEtablissementsInscriptions(true);
        $ecolesDoctorales = $this->structureService->getAllStructuresAffichablesByType(TypeStructure::CODE_ECOLE_DOCTORALE, 'libelle', false, true);
        $codesRolesAvecTemoinsED = [
            Role::CODE_DOCTORANT => true,
            Role::CODE_DIRECTEUR_THESE => true,
            Role::CODE_BU => false,
            Role::CODE_BDD => false,
            Role::CODE_ADMIN_TECH => false, // NB: pas de structure liée
        ];
        $roles = $this->roleService->getRepository()->findByCodes(array_keys($codesRolesAvecTemoinsED));

        $etablissementsAsStructures = array_map(function(Etablissement $e) {
            return $e->getStructure();
        }, $etablissements);
        $roles = array_filter($roles, function(Role $role) use ($etablissementsAsStructures) {
            return
                $role->getStructure() === null || // ex: Admin tech
                in_array($role->getStructure(), $etablissementsAsStructures);
        });
        $this->prepareDataForView($etablissements, $ecolesDoctorales, $roles, $codesRolesAvecTemoinsED);
        $this->loadListesDiffusionActives();

        $adressesListesActives = array_keys($this->listesDiffusionActives);
        $adressesListesActivesMaisInexistanteDansGenerees = array_diff($adressesListesActives, $this->adressesGenerees);

        return new ViewModel([
            'listes' => $listes,
            'codeEtablissement' => $etablissement,
            'dataByEtablissement' => $this->dataByEtablissement,
            'listesDiffusionActives' => $this->listesDiffusionActives,
            'adressesListesActivesMaisInexistanteDansGenerees' => $adressesListesActivesMaisInexistanteDansGenerees,
            'urlSympa' => $this->listeDiffusionService->getUrlSympa(),
        ]);
    }

    /**
     * Fetche en bdd les listes de diffusion actives.
     * NB: pour l'instant le table LISTE_DIFF ne contient que les listes activées.
     */
    private function loadListesDiffusionActives()
    {
        $this->listesDiffusionActives = [];
        $listesDiffusionActives = $this->listeDiffusionService->fetchListesDiffusionActives();
        foreach ($listesDiffusionActives as $listeDiffusion) {
            $this->listesDiffusionActives[$listeDiffusion->getAdresse()] = $listeDiffusion;
        }
    }

    /**
     * @return Response
     */
    private function modifierListes()
    {
        $post = $this->params()->fromPost();
        $etablissement = $post['etablissement'] ?? 'Tous';
        $adresses = $post['listes'] ?? [];
        try {
            $this->enregistrer((array) $adresses, $etablissement);
        } catch (ORMException $e) {
            $this->flashMessenger()->addErrorMessage("Erreur rencontrée lors de l'enregistrement des listes : " . $e->getMessage());
            error_log($e->getMessage() . PHP_EOL . $e->getTraceAsString());
        }


        return $this->redirect()->toRoute(null, [], ['query' => ['etablissement' => $etablissement]], true);
    }

    /**
     * @param string[] $adresses
     * @param string $etablissement
     * @throws ORMException
     */
    private function enregistrer(array $adresses, string $etablissement)
    {
        $adressesToDelete = array_filter($adresses, function (string $checked) { return $checked === '0'; });
        $adressesToInsert = array_filter($adresses, function (string $checked) { return $checked === '1'; });

        $this->listeDiffusionService->deleteListesDiffusions(array_keys($adressesToDelete));
        $listes = [];
        foreach (array_keys($adressesToInsert) as $adresse) {
            $liste = $this->listeDiffusionService->findListeDiffusionByAdresse($adresse);
            $data = [
                'adresse' => $adresse,
                'enabled' => true,
            ];
            if ($liste === null) {
                $liste = $this->listeDiffusionService->createListeDiffusion($data);
            } else {
                $this->listeDiffusionService->updateListeDiffusion($liste, $data);
            }
            $listes[$adresse] = $liste;
        }

        $this->listeDiffusionService->saveListesDiffusions($listes);

        $this->flashMessenger()->addSuccessMessage(
            sprintf("Enregistrement effectué avec succès pour '%s' : %d adresses actives, %d inactives.",
                $etablissement, count($adressesToInsert), count($adressesToDelete)));
    }

    /**
     * @param Etablissement[] $etablissements
     * @param EcoleDoctorale[] $ecolesDoctorales
     * @param Role[] $roles
     * @param array $codesRolesAvecTemoinsED
     */
    private function prepareDataForView(array $etablissements, array $ecolesDoctorales, array $roles, array $codesRolesAvecTemoinsED)
    {
        $this->adressesGenerees = [];

        // pour tests :
        //$etablissements = array_slice($etablissements, -1);

        //
        // Par établissement.
        //
        $dataByEtablissement = new SplObjectStorage();
        foreach ($etablissements as $etablissement) {
            $rolesForEtablissement = array_filter($roles, function (Role $r) use ($etablissement) {
                return $r->getStructure() === $etablissement->getStructure();
            });
            $dataByRole = new SplObjectStorage();
            foreach ($rolesForEtablissement as $role) {
                if ($codesRolesAvecTemoinsED[$role->getCode()] === true) {
                    $dataByED = new SplObjectStorage();
                    // d'abord, toute ED confondue
                    $edTouteConfondue = $this->ecoleDoctoraleService->createTouteEcoleDoctoraleConfondue();
                    $ng = $this->listeDiffusionService->createNameGenerator($edTouteConfondue, $role, $role->getStructure());
                    $dataByED->attach($edTouteConfondue, $this->prepareListeDataForView($ng));
                    // ensuite, par ED
                    foreach ($ecolesDoctorales as $ed) {
                        $ng = $this->listeDiffusionService->createNameGenerator($ed, $role, $role->getStructure());
                        $dataByED->attach($ed, $this->prepareListeDataForView($ng));
                    }
                    $dataByRole->attach($role, $dataByED);
                } else {
                    // hors ED
                    $ng = $this->listeDiffusionService->createNameGenerator(null, $role, $role->getStructure());
                    $dataByRole->attach($role, $this->prepareListeDataForView($ng));
                }
            }
            $dataByEtablissement->attach($etablissement, $dataByRole);
        }
        $rolesAvecTemoinsED = array_filter($roles, function (Role $r) use ($codesRolesAvecTemoinsED) {
            return $codesRolesAvecTemoinsED[$r->getCode()] === true;
        });

        if (count($etablissements) > 1) {
            //
            // Tous établissements confondus.
            //
            /** @var Role $rolePrec */
            $rolePrec = null;
            $dataByRole = new SplObjectStorage();
            foreach ($rolesAvecTemoinsED as $role) {
                if ($rolePrec && ($isSameRoleAgain = $rolePrec->getCode() === $role->getCode())) {
                    continue; // on ne retient qu'un rôle pour tous les établissements confondus
                }
                $dataByED = new SplObjectStorage();
                // d'abord, toute ED confondue
                $edTouteConfondue = $this->ecoleDoctoraleService->createTouteEcoleDoctoraleConfondue();
                $ng = $this->listeDiffusionService->createNameGenerator($edTouteConfondue, $role, null);
                $dataByED->attach($edTouteConfondue, $this->prepareListeDataForView($ng));
                // ensuite, par ED
                foreach ($ecolesDoctorales as $ed) {
                    $ng = $this->listeDiffusionService->createNameGenerator($ed, $role, null);
                    $dataByED->attach($ed, $this->prepareListeDataForView($ng));
                }
                $dataByRole->attach($role, $dataByED);
                $rolePrec = $role;
            }
            $etablissementToutConfondu = $this->etablissementService->createToutEtablissementConfondu();
            $dataByEtablissement->attach($etablissementToutConfondu, $dataByRole);
        }

        // NB: $this->adressesGenerees a été peuplé.

        $this->dataByEtablissement = $dataByEtablissement;
    }

    /**
     * @param ListeDiffusionAddressGenerator $namer
     * @return array
     */
    private function prepareListeDataForView(ListeDiffusionAddressGenerator $namer)
    {
        $domain = $this->listeDiffusionService->getEmailDomain();
        $namer->setDomain($domain);
        try {
            $name = $namer->generateName();
            $link = $this->url()->fromRoute('liste-diffusion/liste', ['adresse' => $name]);
            $enabled = true;
        } catch (InvalidArgumentException $e) {
            $name = "Anomalie rencontrée : " . $e->getMessage();
            $link = null;
            $enabled = false;
        }

        // collecte avec indexation par adresse
        $this->adressesGenerees[$name] = $name;

        return [
            'name' => $name,
            'label' => $name,
            'link' => $link,
            'enabled' => $enabled,
        ];
    }

    /**
     * @return ViewModel
     */
@@ -52,9 +293,6 @@ class ListeDiffusionController extends AbstractController
    {
        $this->loadRequestParams();

        $listesDeclarees = $this->listeDiffusionService->fetchListesDiffusion();
        Assert::inArray($this->liste, $listesDeclarees, "Liste spécifiée non déclarée.");

        $this->listeDiffusionService->setListe($this->liste);
        $this->listeDiffusionService->init();

@@ -123,6 +361,25 @@ class ListeDiffusionController extends AbstractController
        $this->fileService->downloadFileFromContent($content, $filename);
    }

    /**
     * @return CsvModel
     */
    public function exporterTableauAction()
    {
        $data = $this->listeDiffusionService->createDataForCsvExport($this->url());
        $header = current($data);
        $data = array_slice($data, 1);

        $model = new CsvModel();
        $model->setDelimiter(';');
        $model->setEnclosure('"');
        $model->setHeader($header);
        $model->setData($data);
        $model->setFilename(sprintf('sygal_listediff_sympa_%s.csv', date_create('now')->format("Ymd-His")));

        return $model;
    }

    /**
     * Gestion des ABONNÉS sans adresse mail.
     */
@@ -153,12 +410,16 @@ class ListeDiffusionController extends AbstractController
    }

    /**
     * @return string
     * @return ListeDiffusion
     */
    private function getRequestedListe()
    {
        $liste = $this->params()->fromRoute('liste');
        Assert::notNull($liste, "Aucune liste spécifiée.");
        $adresse = $this->params()->fromRoute('adresse');
        Assert::notNull($adresse, "Aucune adresse spécifiée.");

        /** @var ListeDiffusion $liste */
        $liste = $this->listeDiffusionService->getRepository()->findOneBy(['adresse' => $adresse]);
        Assert::notNull($liste, "Aucune liste active trouvée avec l'adresse spécifiée.");

        return $liste;
    }
+11 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@ class EcoleDoctorale implements StructureConcreteInterface, HistoriqueAwareInter
    use HistoriqueAwareTrait;
    use SourceAwareTrait;

    const CODE_TOUTE_ECOLE_DOCTORALE_CONFONDUE = 'TOUTE_ED';

    /**
     * @var integer
     */
@@ -210,5 +212,13 @@ class EcoleDoctorale implements StructureConcreteInterface, HistoriqueAwareInter
        return $this;
    }


    /**
     * Teste si cette école doctorale est la pseudo-école doctorale "Toute école doctorale confondue".
     *
     * @return bool
     */
    public function estTouteEcoleDoctoraleConfondue()
    {
        return $this->getStructure()->getCode() === self::CODE_TOUTE_ECOLE_DOCTORALE_CONFONDUE;
    }
}
 No newline at end of file
Loading