diff --git a/composer.json b/composer.json index f3e80d3a82891c0203a840575bf58f2a0887937b..ddaefa9aa9241b5a2179032612e643c6c055ec0a 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ } ], "require": { - "unicaen/privilege": "^6" + "unicaen/privilege": "^6", + "symfony/mailer": "^7" }, "autoload": { "psr-4": { diff --git a/src/UnicaenMail/Controller/MailController.php b/src/UnicaenMail/Controller/MailController.php index 35251ee663e81f9650078b23597044c224f9fc6c..5bcffa87f3ca182998ef636c55ac7a2a40eb852e 100644 --- a/src/UnicaenMail/Controller/MailController.php +++ b/src/UnicaenMail/Controller/MailController.php @@ -3,17 +3,18 @@ namespace UnicaenMail\Controller; use Laminas\Http\Response; -use UnicaenMail\Service\Mail\MailServiceAwareTrait; use Laminas\Mvc\Controller\AbstractActionController; use Laminas\View\Model\ViewModel; +use UnicaenMail\Service\Mail\MailServiceAwareTrait; -class MailController extends AbstractActionController { +class MailController extends AbstractActionController +{ use MailServiceAwareTrait; - public function indexAction() : ViewModel + public function indexAction(): ViewModel { $filtre = $this->params()->fromQuery(); - if (!isset($filtre['date']) OR $filtre['date'] === '') $filtre['date'] = '1W'; + if (!isset($filtre['date']) or $filtre['date'] === '') $filtre['date'] = '1W'; $mails = $this->getMailService()->getMailsWithFiltre($filtre); @@ -30,7 +31,7 @@ class MailController extends AbstractActionController { $mail = $this->getMailService()->getRequestedMail($this); return new ViewModel([ - 'title' => "Affichage du mail #". $mail->getId(), + 'title' => "Affichage du mail #" . $mail->getId(), 'mail' => $mail, ]); } @@ -40,16 +41,22 @@ class MailController extends AbstractActionController { $request = $this->getRequest(); if ($request->isPost()) { $data = $request->getPost(); - if(!isset($data['mail-to']) || !filter_var($data['mail-to'], FILTER_VALIDATE_EMAIL)) { + if (!isset($data['mail-to']) || !filter_var($data['mail-to'], FILTER_VALIDATE_EMAIL)) { return ['title' => "Envoyer un mail de test", 'error' => "L'adresse mail saisie n'est pas valide."]; } - $mail = $this->getMailService()->sendMail($data['mail-to'], 'Mail de test', 'Ceci est un mail de test. <br/> <hr/>Merci de ne pas en tenir compte.'); - if ($mail){ - $mail->setMotsClefs(['TEST']); - $this->getMailService()->update($mail); - } + + + $mail = $this->getMailService()->sendMail( + to: $data['mail-to'], + subject: "Courrier électronique de test", + texte: "Ceci est un mail de test. <br/> <hr/>Merci de ne pas en tenir compte.", + ); + $mail->setMotsClefs(['TEST']); + $this->getMailService()->update($mail); } return ['title' => "Envoyer un mail de test"]; + + } public function reenvoiAction(): Response diff --git a/src/UnicaenMail/Entity/Db/Mail.php b/src/UnicaenMail/Entity/Db/Mail.php index 593f700b8b11809b9e9f5157424d95834d6933c1..013b9ccf4340c8670f25dbbeb4eaa39f43195ad5 100644 --- a/src/UnicaenMail/Entity/Db/Mail.php +++ b/src/UnicaenMail/Entity/Db/Mail.php @@ -16,6 +16,7 @@ class Mail { private ?string $destinataires = null; private ?string $destinatairesInitials = null; private ?string $copies = null; + private ?string $attachmentPaths = null; /** Contenu *******************************************************************************************************/ @@ -131,4 +132,14 @@ class Mail { $this->log = $log; } + public function getAttachmentPaths(): ?string + { + return $this->attachmentPaths; + } + + public function setAttachmentPaths(?string $attachmentPaths): void + { + $this->attachmentPaths = $attachmentPaths; + } + } \ No newline at end of file diff --git a/src/UnicaenMail/Entity/Db/Mapping/UnicaenMail.Entity.Db.Mail.dcm.xml b/src/UnicaenMail/Entity/Db/Mapping/UnicaenMail.Entity.Db.Mail.dcm.xml index 2863a9836480166ec95b686ccc162b1687108bd1..49db236c1734807876cd07f4f72f29e20e171494 100644 --- a/src/UnicaenMail/Entity/Db/Mapping/UnicaenMail.Entity.Db.Mail.dcm.xml +++ b/src/UnicaenMail/Entity/Db/Mapping/UnicaenMail.Entity.Db.Mail.dcm.xml @@ -1,20 +1,23 @@ <?xml version="1.0" encoding="utf-8"?> -<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> +<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="UnicaenMail\Entity\Db\Mail" table="unicaen_mail_mail"> <id name="id" type="integer" column="id"> <generator strategy="AUTO"/> </id> - <field name="dateEnvoi" type="datetime" column="date_envoi" nullable="false"/> - <field name="statusEnvoi" type="string" length="256" column="status_envoi" nullable="false" /> - <field name="destinataires" type="string" length="256" column="destinataires" nullable="false" /> - <field name="destinatairesInitials" type="string" length="256" column="destinataires_initials" nullable="true" /> - <field name="copies" type="text" column="copies" nullable="true" /> - <field name="sujet" type="string" length="9999" column="sujet" nullable="true" /> - <field name="corps" type="string" length="9999" column="corps" nullable="true" /> - <field name="motsClefs" type="string" length="9999" column="mots_clefs" nullable="true" /> + <field name="dateEnvoi" type="datetime" column="date_envoi"/> + <field name="statusEnvoi" length="256" column="status_envoi"/> + <field name="destinataires" length="256" column="destinataires"/> + <field name="destinatairesInitials" length="256" column="destinataires_initials" nullable="true"/> + <field name="copies" type="text" column="copies" nullable="true"/> + <field name="sujet" length="9999" column="sujet" nullable="true"/> + <field name="corps" length="9999" column="corps" nullable="true"/> + <field name="motsClefs" length="9999" column="mots_clefs" nullable="true"/> + <field name="attachmentPaths" type="text" column="attachment_paths" nullable="true"/> - <field name="log" type="string" length="9999" column="log" nullable="true" /> + <field name="log" length="9999" column="log" nullable="true"/> </entity> </doctrine-mapping> diff --git a/src/UnicaenMail/Service/Mail/MailService.php b/src/UnicaenMail/Service/Mail/MailService.php index cee86e455274c7b606044007d5764720f2eb10c4..c9ab2b113525661d12e81d6804abfde4274dffe7 100644 --- a/src/UnicaenMail/Service/Mail/MailService.php +++ b/src/UnicaenMail/Service/Mail/MailService.php @@ -4,28 +4,38 @@ namespace UnicaenMail\Service\Mail; use DateInterval; use DateTime; -use Doctrine\ORM\EntityManager; use Doctrine\ORM\NonUniqueResultException; -use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\QueryBuilder; use DoctrineModule\Persistence\ProvidesObjectManager; use Exception; use RuntimeException; +use Symfony\Component\Mailer\Exception\TransportExceptionInterface; +use Symfony\Component\Mailer\Mailer; +use Symfony\Component\Mime\Email; use UnicaenMail\Entity\Db\Mail; use Laminas\Mail\Message; -use Laminas\Mail\Transport\TransportInterface; use Laminas\Mime\Message as MimeMessage; use Laminas\Mime\Mime; use Laminas\Mime\Part; use Laminas\Mvc\Controller\AbstractActionController; use UnicaenMail\Exception\NotFoundConfigException; -/** - * @property EntityManager $objectManager - */ class MailService { use ProvidesObjectManager; + private ?Mailer $mailer; + + public function getMailer(): ?Mailer + { + return $this->mailer; + } + + public function setMailer(?Mailer $mailer): void + { + $this->mailer = $mailer; + } + + private ?string $entityClass = null; private array $config = []; @@ -50,44 +60,25 @@ class MailService { return $entity; } - private ?TransportInterface $transport; - - public function __construct(TransportInterface $transport) - { - $this->transport = $transport; - } - /** GESTION DES ENTITES *******************************************************************************************/ public function create(Mail $mail) : Mail { - try { - $this->objectManager->persist($mail); - $this->objectManager->flush($mail); - } catch (ORMException $e) { - throw new RuntimeException("Un problème est survenue lors de l'enregistrement en BD du Mail.", 0, $e); - } + $this->objectManager->persist($mail); + $this->objectManager->flush($mail); return $mail; } public function update(Mail $mail) : Mail { - try { - $this->objectManager->flush($mail); - } catch (ORMException $e) { - throw new RuntimeException("Un problème est survenue lors de l'enregistrement en BD du Mail.", 0, $e); - } + $this->objectManager->flush($mail); return $mail; } public function delete(Mail $mail) : Mail { - try { - $this->objectManager->remove($mail); - $this->objectManager->flush($mail); - } catch (ORMException $e) { - throw new RuntimeException("Un problème est survenue lors de l'enregistrement en BD du Mail.", 0, $e); - } + $this->objectManager->remove($mail); + $this->objectManager->flush($mail); return $mail; } @@ -163,28 +154,80 @@ class MailService { return $value; } - public function sendMail($to, $subject, $texte, ?string $module = null, $attachement_path = null, $copie = null, ?string $complement = null) : ?Mail + public function transform(Mail $mail, ?string $module = null) : Email { try { $fromEmail = $this->fetchValueFromConfig('from_email', $module); $fromName = $this->fetchValueFromConfig('from_name', $module); - $doNotSend = $this->fetchValueFromConfig('do_not_send', $module); + $subjectPrefix = $this->fetchValueFromConfig('subject_prefix', $module); $redirect = $this->fetchValueFromConfig('redirect', $module); $redirectTo = $this->fetchValueFromConfig('redirect_to', $module); - $subjectPrefix = $this->fetchValueFromConfig('subject_prefix', $module); } catch (NotFoundConfigException $e) { throw new RuntimeException("Un problème est survenu lors de la récupération de valeurs de config",0,$e); } + $to = (is_string($mail->getDestinataires()))?[$mail->getDestinataires()]:$mail->getDestinataires(); + $co = (is_string($mail->getCopies()))?[$mail->getCopies()]:$mail->getCopies(); + $sujet = "[".$subjectPrefix. "] ".$mail->getSujet(); + $corps = "<p><i>Ce courrier électronique vous a été adressé <strong>automatiquement</strong> par l'application ".$subjectPrefix.". </i></p>" . $mail->getCorps(); + + if ($redirect) { + $to = $redirectTo; + + $sujet .= " {REDIR}"; + + $corps .= "<br/><br/><hr/><br/>"; + $corps .= "Initialement envoyé à :"; + $corps .= "<ul>"; + foreach (explode(",",$mail->getDestinatairesInitials()) as $t) $corps .= "<li>" . $t . "</li>"; + $corps .= "</ul>"; + if ($mail->getCopies() !== null && !empty($co)) { + $corps .= "Copie à :"; + $corps .= "<ul>"; + foreach ($co as $e) $corps .= "<li>".$e."</li>"; + $corps .= "</ul>"; + } + } + + $message = (new Email()) + ->from("'\"".$fromName."\" <".$fromEmail.">'") + ->subject($sujet) + ->text(strip_tags($corps)) + ->html($corps); + foreach ($to as $t) $message->to($t); + + $attachments = ($mail->getAttachmentPaths())?explode("#<>#",$mail->getAttachmentPaths()):[]; + foreach ($attachments as $path) { + $basename = basename($path); + $extension = (explode('.', $basename)[1])??'bin'; + $message->attachFromPath($path, $basename, 'application/' . $extension); + } + + if (!$redirect) { + foreach ($co as $c) $message->cc($c); + } + + if ($mail->getCopies()) $message = $message->bcc($mail->getCopies()); + return $message; + } + + public function sendMail($to, $subject, $texte, ?string $module = null, $attachement_path = null, $copie = null) : ?Mail + { + try { + $doNotSend = $this->fetchValueFromConfig('do_not_send', $module); + $redirect = $this->fetchValueFromConfig('redirect', $module); + $redirectTo = $this->fetchValueFromConfig('redirect_to', $module); + } catch (NotFoundConfigException $e) { + throw new RuntimeException("Un problème est survenu lors de la récupération de valeurs de config",0,$e); + } - if ($to === "" OR $to === []) { + if ($to=== null OR $to === "" OR $to === []) { return null; } if (is_string($to)) $to=explode(',', $to); if (!is_array($to)) $to = [$to]; - if (is_string($copie)) $copie=explode(',', $copie); if (!is_array($copie)) $copie = [$copie]; @@ -201,81 +244,21 @@ class MailService { } if ($copie !== null and !empty($copie)) $mail->setCopies(implode(",", $copie)); $mail->setSujet($subject); - - - $sujet = '['.$subjectPrefix.'] ' . $subject; - if ($redirect) { - $sujet .= ' {REDIR}'; - } - - $message = (new Message())->setEncoding('UTF-8'); - - $message->setFrom($fromEmail, $fromName); - if ($redirect) { - foreach ($redirectTo as $e) $message->addTo($e); - } else { - foreach ($to as $e) $message->addTo($e); - if ($copie !== null and !empty($copie)) { - foreach ($copie as $e) { - if ($e !== null) $message->addCc($e); - } - } - } - $message->setSubject($sujet); $mail->setCorps($texte); - $this->create($mail); - - $texte = "<p><i>Ce courrier électronique vous a été adressé <strong>automatiquement</strong> par l'application ".$subjectPrefix.". </i></p>" . $texte; - if ($complement) $texte = $complement.$texte; - - if ($redirect) { - $texte .= "<br/><br/><hr/><br/>"; - $texte .= "Initialement envoyé à :"; - $texte .= "<ul>"; - foreach (explode(",",$mail->getDestinatairesInitials()) as $t) $texte .= "<li>" . $t . "</li>"; - $texte .= "</ul>"; - if ($copie !== null && !empty($copie)) { - $texte .= "Copie à :"; - $texte .= "<ul>"; - foreach ($copie as $e) $texte .= "<li>".$e."</li>"; - $texte .= "</ul>"; - } - } - - $parts = []; - - $part = new Part($texte); - $part->type = Mime::TYPE_HTML; - $part->charset = 'UTF-8'; - $parts[] = $part; - - if ($attachement_path) { - if (!is_array($attachement_path)) $attachement_path = [$attachement_path]; - foreach ($attachement_path as $attachement_pat) { - $attachement = new Part(); - $attachement->setType("application/pdf"); - $attachement->setContent(file_get_contents($attachement_pat)); - $filename = explode('/', $attachement_pat); - $attachement->setFileName($filename[2]); - $attachement->setEncoding(Mime::ENCODING_BASE64); - $attachement->setDisposition(Mime::DISPOSITION_ATTACHMENT); - $parts[] = $attachement; - } + if ($attachement_path !== null) { + $attachement_path = (is_string($attachement_path))?$attachement_path:implode("#<>#",$attachement_path); + $mail->setAttachmentPaths($attachement_path); } + $this->create($mail); if ($doNotSend !== true) { - $body = new MimeMessage(); - $body->setParts($parts); - $message->setBody($body); - - // var_dump($fromEmail); - // var_dump($fromName); - // var_dump($doNotSend); - // var_dump($redirectTo); - // var_dump($subjectPrefix); - // die(); - $this->transport->send($message); + try { + $email = $this->transform($mail, $module); + $this->getMailer()->send($email); + } catch (TransportExceptionInterface $e) { + throw new RuntimeException("Échec de l'envoi du message", 0, $e); + } $mail->setStatusEnvoi(Mail::SUCCESS); } else { $mail->setStatusEnvoi(Mail::NOTSENT); diff --git a/src/UnicaenMail/Service/Mail/MailServiceFactory.php b/src/UnicaenMail/Service/Mail/MailServiceFactory.php index b87c58f3048afc2be16985235f5cb6a51fa4fdc9..83028032a36188b98151f0738225884ff31dcb39 100644 --- a/src/UnicaenMail/Service/Mail/MailServiceFactory.php +++ b/src/UnicaenMail/Service/Mail/MailServiceFactory.php @@ -4,10 +4,10 @@ namespace UnicaenMail\Service\Mail; use Doctrine\ORM\EntityManager; use Interop\Container\ContainerInterface; -use Laminas\Mail\Transport\Smtp; -use Laminas\Mail\Transport\SmtpOptions; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use Symfony\Component\Mailer\Mailer; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; class MailServiceFactory { @@ -20,14 +20,16 @@ class MailServiceFactory { public function __invoke(ContainerInterface $container) : MailService { $config = $container->get('Configuration')['unicaen-mail']; - $transport = new Smtp(new SmtpOptions($config['transport_options'])); + $transport = new EsmtpTransport(host: $config['transport_options']['host'], port: $config['transport_options']['port']); + $mailer = new Mailer($transport); /** * @var EntityManager $entityManager */ $entityManager = $container->get('doctrine.entitymanager.orm_default'); - $service = new MailService($transport); + $service = new MailService(); + $service->setMailer($mailer); $service->setConfig($config); $service->setEntityClass($config['mail_entity_class']); $service->setObjectManager($entityManager); diff --git a/src/UnicaenMail/View/Helper/partial/mails.phtml b/src/UnicaenMail/View/Helper/partial/mails.phtml index 291f41431630cc1975af437089be72c048063191..d34de6d97d72aaacb187aca4dbd0c9c22bb569e0 100644 --- a/src/UnicaenMail/View/Helper/partial/mails.phtml +++ b/src/UnicaenMail/View/Helper/partial/mails.phtml @@ -34,8 +34,8 @@ if (!isset($options['droits']) OR !isset($options['droits']['supprimer'])) { <table id="mails-liste" class="table table-condensed table-hover"> <thead> <tr> - <th> Destinataires </th> <th data-type="num"> Date d'envoi </th> + <th> Destinataires </th> <th> Statut </th> <th> Sujet </th> <th> Infos </th> @@ -45,6 +45,7 @@ if (!isset($options['droits']) OR !isset($options['droits']['supprimer'])) { <tbody> <?php foreach ($mails as $mail) : ?> <tr> + <td data-order="<?php echo $mail->getDateEnvoi()->getTimestamp(); ?>"> <?php echo $mail->getDateEnvoi()->format('d/m/Y <br/> H:i'); ?> </td> <td> <?php $destinataires = explode(",",$mail->getDestinataires()); ?> <ul> @@ -76,7 +77,6 @@ if (!isset($options['droits']) OR !isset($options['droits']['supprimer'])) { <?php endif; ?> </td> - <td data-order="<?php echo $mail->getDateEnvoi()->getTimestamp(); ?>"> <?php echo $mail->getDateEnvoi()->format('d/m/Y <br/> H:i'); ?> </td> <td> <span class="badge <?php echo $mail->getStatusEnvoi(); ?>"> <?php echo $mail->getStatusEnvoi(); ?> diff --git a/view/unicaen-mail/mail/index.phtml b/view/unicaen-mail/mail/index.phtml index 9fc93abd8dff8295bd63b517b399445bf7a2adcc..58241edc1419611659e98f73f01ddd2a3be1cbd9 100644 --- a/view/unicaen-mail/mail/index.phtml +++ b/view/unicaen-mail/mail/index.phtml @@ -48,6 +48,7 @@ $canTest = $this->isAllowed(MailPrivileges::getResourceId(MailPrivileges::MAIL_A if (jQuery().dataTable) { $('#mails-liste').DataTable({ "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "Tous"]], + order: [[0, 'desc']], "columnDefs": [ {targets: 5, orderable: false, searchable: false}, ],