Commit 3e8f1786 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Merge branch 'liste_sympa' into release-1.4.7

parents 5ca59185 25730b77
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@ Idem pour les attestations et le privilège "Modification des attestations conce
- Correction d'un bug dans la recherche de thèses par nom du doctorant.
- Possibilité d'attribuer un "identifiant permanent" à un fichier (ex: 'RAPPORT_ANNUEL_MODELE') facilitant l'intégration
  de lien de téléchargement de ce fichier dans une page.
- Listes de diffusion dynamique Sympa alimentées par SyGAL : pages de consultation des listes de diffusion déclarées
  dans la config ; une URL pour fournir les abonnés, une autre pour fournir les propriétaires.

1.4.6 (29/05/2020)
------------------
+15 −0
Original line number Diff line number Diff line
@@ -116,4 +116,19 @@ from these t
         left join acteurs a on a.these_id = t.id
         left join individu ia on ia.id = a.INDIVIDU_ID
;

insert into CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE)
    select categorie_privilege_id_seq.nextval, 'liste-diffusion', 'Liste de diffusion', 50 from dual ; -- si échoue, retenter jusqu'au succès

insert into PRIVILEGE(ID, CATEGORIE_ID, CODE, LIBELLE, ORDRE)
    select privilege_id_seq.nextval, cp.id, 'consulter', 'Consulter une liste de diffusion', 250
    from CATEGORIE_PRIVILEGE cp
    where cp.CODE = 'liste-diffusion';
insert into PRIVILEGE(ID, CATEGORIE_ID, CODE, LIBELLE, ORDRE)
    select privilege_id_seq.nextval, cp.id, 'lister', 'Lister les listes de diffusion', 300
    from CATEGORIE_PRIVILEGE cp
    where cp.CODE = 'liste-diffusion';
--
-- ATTENTION >>> Dans l'interface graphique, accorder ces nouveaux privilèges aux profils voulus (a minima Doctorant et AdminTech).
--
```
+114 −0
Original line number Diff line number Diff line
<?php

namespace Application;

use Application\Controller\Factory\ListeDiffusionControllerFactory;
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 UnicaenAuth\Guard\PrivilegeController;
use Zend\Mvc\Router\Http\Literal;
use Zend\Mvc\Router\Http\Segment;

return [
    'bjyauthorize'    => [
        'guards' => [
            PrivilegeController::class => [
                [
                    'controller' => ListeDiffusionController::class,
                    'action' => [
                        'index',
                    ],
                    'privileges' => ListeDiffusionPrivileges::LISTE_DIFFUSION_LISTER,
                ],
                [
                    'controller' => ListeDiffusionController::class,
                    'action' => [
                        'consulter',
                    ],
                    'privileges' => ListeDiffusionPrivileges::LISTE_DIFFUSION_CONSULTER,
                ],
                [
                    'controller' => ListeDiffusionController::class,
                    'action' => [
                        'generate-member-include',
                        'generate-owner-include',
                    ],
                    'roles' => [], // doit être accessible librement
                ],
            ],
        ],
    ],
    'router'          => [
        'routes' => [
            'liste-diffusion' => [
                'type' => Literal::class,
                'options' => [
                    'route' => '/liste-diffusion',
                    'defaults' => [
                        'controller' => ListeDiffusionController::class,
                        'action' => 'index',
                    ],
                ],
                'may_terminate' => true,
                'child_routes' => [
                    'liste' => [
                        'type' => Segment::class,
                        'options' => [
                            'route' => '/:liste',
                            // exemple: /consulter/ed591.doctorants.ucn@normandie-univ.fr
                            // exemple: /consulter/ed591.doctorants@normandie-univ.fr
                            'constraints' => [
                                'liste' => '[a-zA-Z0-9-_.@]+',
                            ],
                            'defaults' => [
                                /** @see ListeDiffusionController::consulterAction() */
                                'action' => 'consulter',
                            ],
                        ],
                        'may_terminate' => true,
                        'child_routes' => [
                            'generate-member-include' => [
                                'type' => Literal::class,
                                'options' => [
                                    'route' => '/95c66625fe1e45a41e4a51d3f786210ab0a47479/generate-member-include/',
                                    'defaults' => [
                                        /** @see ListeDiffusionController::generateMemberIncludeAction() */
                                        'action' => 'generate-member-include',
                                    ],
                                ],
                            ],
                            'generate-owner-include' => [
                                'type' => Literal::class,
                                'options' => [
                                    'route' => '/ef35998526851f484a027478ef9b7e936a0366e5/generate-owner-include',
                                    'defaults' => [
                                        /** @see ListeDiffusionController::generateOwnerIncludeAction() */
                                        'action' => 'generate-owner-include',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
    'service_manager' => [
        'factories' => [
            ListeDiffusionService::class => ListeDiffusionServiceFactory::class,
            ListeDiffusionStructurePlugin::class => ListeDiffusionStructurePluginFactory::class,
            ListeDiffusionRolePlugin::class => ListeDiffusionRolePluginFactory::class,
        ],
    ],
    'controllers' => [
        'factories' => [
            ListeDiffusionController::class => ListeDiffusionControllerFactory::class,
        ],
    ],
];
+46 −0
Original line number Diff line number Diff line
<?php

namespace Application\Controller\Factory;

use Application\Controller\ListeDiffusionController;
use Application\Service\File\FileService;
use Application\Service\Individu\IndividuService;
use Application\Service\ListeDiffusion\ListeDiffusionService;
use Application\Service\Notification\NotifierService;
use Zend\ServiceManager\ServiceLocatorInterface as ContainerInterface;

class ListeDiffusionControllerFactory
{
    public function __invoke(ContainerInterface $container)
    {
        $container = $container->getServiceLocator();

        $controller = new ListeDiffusionController();

        /**
         * @var ListeDiffusionService $individuService
         */
        $individuService = $container->get(ListeDiffusionService::class);
        $controller->setListeDiffusionService($individuService);

        /**
         * @var IndividuService $individuService
         */
        $individuService = $container->get('IndividuService');
        $controller->setIndividuService($individuService);

        /**
         * @var FileService $fileService
         */
        $fileService = $container->get(FileService::class);
        $controller->setFileService($fileService);

        /**
         * @var NotifierService $notifierService
         */
        $notifierService = $container->get(NotifierService::class);
        $controller->setNotifierService($notifierService);

        return $controller;
    }
}
 No newline at end of file
+175 −0
Original line number Diff line number Diff line
<?php

namespace Application\Controller;

use Application\Entity\Db\Individu;
use Application\Entity\Db\Role;
use Application\Service\File\FileServiceAwareTrait;
use Application\Service\Individu\IndividuServiceAwareTrait;
use Application\Service\ListeDiffusion\ListeDiffusionServiceAwareTrait;
use Application\Service\Notification\NotifierServiceAwareTrait;
use Webmozart\Assert\Assert;
use Zend\View\Model\ViewModel;

class ListeDiffusionController extends AbstractController
{
    use ListeDiffusionServiceAwareTrait;
    use IndividuServiceAwareTrait;
    use FileServiceAwareTrait;
    use NotifierServiceAwareTrait;

    /**
     * Adresse complète de la liste de diffusion, ex :
     *   - ed591.doctorants.insa@normandie-univ.fr
     *   - ed591.doctorants@normandie-univ.fr
     *   - ed591.dirtheses@normandie-univ.fr
     *
     * Où :
     * - '591' est le numéro national de l'école doctorale ;
     * - 'doctorants' (ou 'dirtheses') est la "cible" ;
     * - 'insa' est le source_code unique de l'établissement en minuscules.
     *
     * @var string
     */
    protected $liste;

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

        return new ViewModel([
            'listes' => $listes,
        ]);
    }

    /**
     * @return ViewModel
     */
    public function consulterAction()
    {
        $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();

        $this->listeDiffusionService->createMemberIncludeFileContent();
        $memberIndividusAvecAdresse = $this->listeDiffusionService->getIndividusAvecAdresse();
        $memberIndividusSansAdresse = $this->listeDiffusionService->getIndividusSansAdresse();

        $this->listeDiffusionService->createOwnerIncludeFileContent();
        $ownerIndividusAvecAdresse = $this->listeDiffusionService->getIndividusAvecAdresse();
        $ownerIndividusSansAdresse = $this->listeDiffusionService->getIndividusSansAdresse();

        return new ViewModel([
            'liste' => $this->liste,
            'memberIndividusAvecAdresse' => $memberIndividusAvecAdresse,
            'memberIndividusSansAdresse' => $memberIndividusSansAdresse,
            'ownerIndividusAvecAdresse' => $ownerIndividusAvecAdresse,
            'ownerIndividusSansAdresse' => $ownerIndividusSansAdresse,
        ]);
    }

    /**
     * Dépouillage des paramètres de la requête.
     *
     * Les paramètres de routage acceptés sont les suivants :
     *   - `liste` (OBLIGATOIRE) : cf. {@see $liste}.
     */
    private function loadRequestParams()
    {
        $this->liste = $this->getRequestedListe(); // ex: 'ed591.doctorants.insa@normandie-univ.fr'
    }

    /**
     * Génération du fichier attendu par Sympa pour obtenir les ABONNÉS d'une liste de diffusion.
     *
     * Le fichier retourné contient une adresse électronique par ligne.
     */
    public function generateMemberIncludeAction()
    {
        $this->loadRequestParams();

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

        $content = $this->listeDiffusionService->createMemberIncludeFileContent();
        //$this->handleMemberIncludeNotFoundEmails(); // PAS POSSIBLE : Sympa interroge toutes les heures !

        $filename = $this->listeDiffusionService->generateResultFileName('member');
        $this->fileService->downloadFileFromContent($content, $filename);
    }

    /**
     * Génération du fichier attendu par Sympa pour obtenir les PROPRIÉTAIRES d'une liste de diffusion.
     *
     * Le fichier retourné contient une adresse électronique par ligne.
     */
    public function generateOwnerIncludeAction()
    {
        $this->loadRequestParams();

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

        $content = $this->listeDiffusionService->createOwnerIncludeFileContent();

        $filename = $this->listeDiffusionService->generateResultFileName('owner');
        $this->fileService->downloadFileFromContent($content, $filename);
    }

    /**
     * Gestion des ABONNÉS sans adresse mail.
     */
    private function handleMemberIncludeNotFoundEmails()
    {
        $individusSansAdresse = $this->listeDiffusionService->getIndividusSansAdresse();
        if (empty($individusSansAdresse)) {
            return;
        }

        $individusAvecAdresse = $this->listeDiffusionService->getIndividusAvecAdresse();

        // Envoi d'une notif aux admin tech
        $to = $this->fetchAdminTechEmails();
        $this->notifierService->triggerAbonnesListeDiffusionSansAdresse(
            $to,
            $this->liste,
            $individusAvecAdresse,
            $individusSansAdresse);
    }

    /**
     * Gestion des PROPRÉTAIRES sans adresse mail.
     */
    private function handleOwnerIncludeNotFoundEmails()
    {

    }

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

        return $liste;
    }

    /**
     * @return string[]
     */
    private function fetchAdminTechEmails()
    {
        $individus = $this->individuService->getRepository()->findByRole(Role::CODE_ADMIN_TECH);

        return array_map(function(Individu $i) { return $i->getEmailUtilisateur(); }, $individus);
    }
}
 No newline at end of file
Loading