From 63a985bafd8247f97910c4a1b27b1d01100d1b16 Mon Sep 17 00:00:00 2001 From: vgreb Date: Wed, 6 May 2026 14:04:16 +0200 Subject: [PATCH] CFP - Envoi d'un mail de confirmation lors d'une soumission --- .env.dist | 2 +- compose.yml | 3 + .../AppBundle/Event/Talk/TalkFormHandler.php | 20 ++++- .../Talk/TalkSubmissionConfirmationMail.php | 80 +++++++++++++++++++ ..._submission_confirmation_content.html.twig | 14 ++++ tests/behat/features/EventPages/Cfp.feature | 4 + tests/behat/features/EventPages/CfpEn.feature | 18 +++++ translations/messages.en.yml | 13 +++ translations/messages.fr.yml | 13 +++ 9 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 sources/AppBundle/Event/Talk/TalkSubmissionConfirmationMail.php create mode 100644 templates/mail_templates/cfp_submission_confirmation_content.html.twig diff --git a/.env.dist b/.env.dist index 25c5795cc..a249f6cdd 100644 --- a/.env.dist +++ b/.env.dist @@ -20,7 +20,7 @@ ALGOLIA_FRONTEND_API_KEY=74e71e5r5c2Rb353f5a03376gb9878777 HOME_ALGOLIA_ENABLED=false -SLACK_URL=http://yolo +SLACK_URL=http://httpbin:8080/status/200 BLUESKY_API_IDENTIFIER=xxx BLUESKY_API_APP_PASSWORD=yyy diff --git a/compose.yml b/compose.yml index 89177346e..193f65095 100644 --- a/compose.yml +++ b/compose.yml @@ -80,3 +80,6 @@ services: image: nginx:1.29.7 volumes: - ./docker/dockerfiles/statictestresources:/usr/share/nginx/html:ro + + httpbin: + image: mccutchen/go-httpbin diff --git a/sources/AppBundle/Event/Talk/TalkFormHandler.php b/sources/AppBundle/Event/Talk/TalkFormHandler.php index 763893a79..3e85e5e1b 100644 --- a/sources/AppBundle/Event/Talk/TalkFormHandler.php +++ b/sources/AppBundle/Event/Talk/TalkFormHandler.php @@ -13,6 +13,7 @@ use AppBundle\Notifier\SlackNotifier; use CCMBenchmark\Ting\UnitOfWork; use DateTime; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; @@ -27,6 +28,8 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, private readonly TalkToSpeakersRepository $talkToSpeakersRepository, private readonly UnitOfWork $unitOfWork, + private readonly TalkSubmissionConfirmationMail $confirmationMail, + private readonly LoggerInterface $logger, ) {} public function handle(Request $request, Event $event, FormInterface $form, Speaker $speaker): bool @@ -39,9 +42,24 @@ public function handle(Request $request, Event $event, FormInterface $form, Spea $talk = $form->getData(); $talk->setSubmittedOn(new DateTime()); $this->speakerRepository->save($speaker); + $locale = $request->getLocale(); if (!$this->unitOfWork->isManaged($talk)) { $this->eventDispatcher->addListener(KernelEvents::TERMINATE, function () use ($talk, $event): void { - $this->slackNotifier->notifyTalk($talk, $event); + try { + $this->slackNotifier->notifyTalk($talk, $event); + } catch (\Throwable $e) { + $this->logger->warning('Slack talk notification failed: ' . $e->getMessage()); + } + }); + $this->eventDispatcher->addListener(KernelEvents::TERMINATE, function () use ($talk, $event, $speaker, $locale): void { + try { + $this->confirmationMail->send($talk, $event, $speaker, $locale); + } catch (\Throwable $e) { + $this->logger->error('CFP confirmation mail failed: ' . $e->getMessage(), [ + 'exception' => $e, + 'talkId' => $talk->getId(), + ]); + } }); } $this->talkRepository->save($talk); diff --git a/sources/AppBundle/Event/Talk/TalkSubmissionConfirmationMail.php b/sources/AppBundle/Event/Talk/TalkSubmissionConfirmationMail.php new file mode 100644 index 000000000..ca10b095c --- /dev/null +++ b/sources/AppBundle/Event/Talk/TalkSubmissionConfirmationMail.php @@ -0,0 +1,80 @@ +getTitle(); + + if ($locale === 'fr') { + $firstChar = mb_strtolower(mb_substr($eventTitle, 0, 1)); + $eventParam = in_array($firstChar, ['a', 'e', 'i', 'o', 'u', 'h'], true) + ? "de l'" . $eventTitle + : 'du ' . $eventTitle; + } else { + $eventParam = $eventTitle; + } + + $votePageUrl = null; + if ($event->getVoteEnabled()) { + $votePageUrl = $this->urlGenerator->generate( + 'vote_index', + ['eventSlug' => $event->getPath()], + UrlGeneratorInterface::ABSOLUTE_URL, + ); + } + + $subject = $this->translator->trans( + 'mail.cfp_submission.subject', + ['%event%' => $eventTitle, '%title%' => $talk->getTitle()], + 'messages', + $locale, + ); + + $bodyTitle = $this->translator->trans( + 'mail.cfp_submission.title', + ['%event%' => $eventParam], + 'messages', + $locale, + ); + + $content = $this->twig->render('mail_templates/cfp_submission_confirmation_content.html.twig', [ + 'talk' => $talk, + 'event' => $event, + 'eventParam' => $eventParam, + 'cfpEndDate' => $event->getDateEndCallForPapers(), + 'votePageUrl' => $votePageUrl, + 'locale' => $locale, + ]); + + $message = new Message( + $subject, + MailUserFactory::conferences(), + new MailUser($speaker->getEmail(), $speaker->getLabel()), + ); + + $this->mailer->sendTransactional($message, $content, MailUserFactory::conferences()->getEmail(), $bodyTitle); + } +} diff --git a/templates/mail_templates/cfp_submission_confirmation_content.html.twig b/templates/mail_templates/cfp_submission_confirmation_content.html.twig new file mode 100644 index 000000000..7703d9b35 --- /dev/null +++ b/templates/mail_templates/cfp_submission_confirmation_content.html.twig @@ -0,0 +1,14 @@ +{{ 'mail.cfp_submission.text'|trans({ + '%title%': talk.title|e, + '%event%': eventParam|e, + '%date%': cfpEndDate|date('d/m/Y') +}, 'messages', locale)|raw }} + +{% if votePageUrl %} +{{ 'mail.cfp_submission.vote_section'|trans({'%url%': votePageUrl}, 'messages', locale)|raw }} +{% endif %} + +

{{ 'mail.cfp_submission.abstract'|trans({}, 'messages', locale) }}

+
{{ talk.abstract }}
+ +

{{ 'mail.cfp_submission.signature'|trans({}, 'messages', locale) }}

diff --git a/tests/behat/features/EventPages/Cfp.feature b/tests/behat/features/EventPages/Cfp.feature index 172be2795..3d8b39b12 100644 --- a/tests/behat/features/EventPages/Cfp.feature +++ b/tests/behat/features/EventPages/Cfp.feature @@ -44,6 +44,7 @@ Feature: Event pages - CFP Then I should see "Les nouvelles conférences à noter" And I should see "Il n'y a plus aucune conférence à noter !" + @clearEmails Scenario: On crée une nouvelle proposition en tant que userGithub1 mais on ne peut pas la noter Given I am on "/event/forum/cfp" Then I should see "Oauth login test" @@ -62,6 +63,9 @@ Feature: Event pages - CFP And I press "Sauvegarder" Then I should not see "Cette valeur ne doit pas être vide." And I should see "Proposition enregistrée !" + And I should only receive the following emails: + | to | subject | + | | AFUP - forum - Confirmation de soumission au CFP - Généalogie des poissons rouges | When I am on "/event/forum/cfp" Then I should see "Généalogie des poissons rouges" When I am on "/event/forum/vote" diff --git a/tests/behat/features/EventPages/CfpEn.feature b/tests/behat/features/EventPages/CfpEn.feature index 660c3a204..0412fb0d1 100644 --- a/tests/behat/features/EventPages/CfpEn.feature +++ b/tests/behat/features/EventPages/CfpEn.feature @@ -18,3 +18,21 @@ Feature: Event pages - CFP - en anglais And I attach the file "avatar1.png" to "speaker[photoFile]" And I press "Save" Then I should see "Profile saved." + + @clearEmails + Scenario: On submitting a new talk a confirmation email is sent + Given I am on "/event/forum/cfp?_locale=en" + Then I should see "Oauth login test" + When I follow "Connect as userGithub1" + Then I should see "My speaker area" + When I follow "New submission" + When I fill in "talk[title]" with "The history of PHP" + And I fill in "talk[abstract]" with "An abstract about the history of PHP" + And I fill in "talk[hasAllowedToSharingWithLocalOffices]" with "1" + And I check "talk[codeOfConduct]" + And I check "talk[selectionAcknowledgement]" + And I press "Save" + Then I should see "Talk saved!" + And I should only receive the following emails: + | to | subject | + | | AFUP - forum - CFP submission confirmation - The history of PHP | diff --git a/translations/messages.en.yml b/translations/messages.en.yml index b66a9aeb4..3e442e6d9 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -218,3 +218,16 @@ speaker_infos.microphone.label: "Microphone type" speaker_infos.microphone.placeholder: "No preference" speaker_infos.microphone.choice.headset: "Headset microphone" speaker_infos.microphone.choice.handheld: "Handheld microphone" +'mail.cfp_submission.subject': 'AFUP - %event% - CFP submission confirmation - %title%' +'mail.cfp_submission.title': 'CFP submission confirmation - %event%' +'mail.cfp_submission.text': | +

Hello,

+ +

We confirm receipt of your talk submission « %title% » to the CFP %event%.

+ +

Once the call for papers closes on %date%, the conference team will make a selection following the process described on the dedicated page.

+ +

Whatever the outcome, you will receive an email in the weeks following the end of the CFP.

+'mail.cfp_submission.vote_section': '

Feel free to check the public comments and votes on the dedicated page. Please note these are advisory — the conference team has sole decision-making authority.

' +'mail.cfp_submission.abstract': 'Here is an abstract of your submission:' +'mail.cfp_submission.signature': 'Thank you for your contribution,' diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 824af5e61..59ad582df 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -242,3 +242,16 @@ speaker_infos.microphone.label: "Type de microphone" speaker_infos.microphone.placeholder: "Pas de préférence" speaker_infos.microphone.choice.headset: "Micro casque" speaker_infos.microphone.choice.handheld: "Micro à main" +'mail.cfp_submission.subject': 'AFUP - %event% - Confirmation de soumission au CFP - %title%' +'mail.cfp_submission.title': 'Confirmation de soumission au CFP %event%' +'mail.cfp_submission.text': | +

Bonjour,

+ +

Nous vous confirmons la bonne réception de votre soumission « %title% » au CFP %event%.

+ +

Une fois l'appel à conférences terminé, le %date%, le pôle conférences effectuera une sélection en respectant bien le processus tel que décrit sur la page dédiée.

+ +

Quel que soit le résultat, un courriel vous sera envoyé dans les semaines qui suivront la fin du CFP.

+'mail.cfp_submission.vote_section': '

N''hésitez pas à consulter les commentaires et votes du public sur la page dédiée. Notez bien que ceux-ci sont indicatifs, le pôle conférences est seul décisionnaire sur la sélection.

' +'mail.cfp_submission.abstract': 'Voici un extrait de votre soumission :' +'mail.cfp_submission.signature': 'Merci pour votre contribution,'