From e972beee112bea4ae56453a669064744450d4952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 25 Dec 2021 20:47:54 +0100 Subject: [PATCH 01/71] fix controller loading and add UserMenu entry --- .../Controller/NotificationController.php | 34 ++++++++++++------- .../ChillMainBundle/Entity/Notification.php | 4 +-- .../Routing/MenuBuilder/UserMenuBuilder.php | 17 +++++++++- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php index 4eed79740..8ce15e6a4 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Notification\NotificationRenderer; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Repository\NotificationRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; @@ -23,27 +24,34 @@ use Symfony\Component\Security\Core\Security; */ class NotificationController extends AbstractController { - private $security; + private Security $security; + private NotificationRepository $notificationRepository; + private NotificationRenderer $notificationRenderer; + private PaginatorFactory $paginatorFactory; - public function __construct(Security $security) - { + public function __construct( + Security $security, + NotificationRepository $notificationRepository, + NotificationRenderer $notificationRenderer, + PaginatorFactory $paginatorFactory + ) { $this->security = $security; + $this->notificationRepository = $notificationRepository; + $this->notificationRenderer = $notificationRenderer; + $this->paginatorFactory = $paginatorFactory; } /** * @Route("/show", name="chill_main_notification_show") */ - public function showAction( - NotificationRepository $notificationRepository, - NotificationRenderer $notificationRenderer, - PaginatorFactory $paginatorFactory - ) { + public function showAction(): Response + { $currentUser = $this->security->getUser(); - $notificationsNbr = $notificationRepository->countAllForAttendee(($currentUser)); - $paginator = $paginatorFactory->create($notificationsNbr); + $notificationsNbr = $this->notificationRepository->countAllForAttendee(($currentUser)); + $paginator = $this->paginatorFactory->create($notificationsNbr); - $notifications = $notificationRepository->findAllForAttendee( + $notifications = $this->notificationRepository->findAllForAttendee( $currentUser, $limit = $paginator->getItemsPerPage(), $offset = $paginator->getCurrentPage()->getFirstItemNumber() @@ -53,8 +61,8 @@ class NotificationController extends AbstractController foreach ($notifications as $notification) { $data = [ - 'template' => $notificationRenderer->getTemplate($notification), - 'template_data' => $notificationRenderer->getTemplateData($notification), + 'template' => $this->notificationRenderer->getTemplate($notification), + 'template_data' => $this->notificationRenderer->getTemplateData($notification), 'notification' => $notification, ]; $templateData[] = $data; diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php index cba4f33e5..bfcdf1ee6 100644 --- a/src/Bundle/ChillMainBundle/Entity/Notification.php +++ b/src/Bundle/ChillMainBundle/Entity/Notification.php @@ -48,7 +48,7 @@ class Notification /** * @ORM\Column(type="text") */ - private string $message; + private string $message = ''; /** * @ORM\Column(type="json") @@ -58,7 +58,7 @@ class Notification /** * @ORM\Column(type="string", length=255) */ - private string $relatedEntityClass; + private string $relatedEntityClass = ''; /** * @ORM\Column(type="integer") diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php index 51758d979..495b2ca8b 100644 --- a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php +++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php @@ -14,14 +14,18 @@ namespace Chill\MainBundle\Routing\MenuBuilder; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatorInterface; class UserMenuBuilder implements LocalMenuBuilderInterface { private Security $security; - public function __construct(Security $security) + private TranslatorInterface $translator; + + public function __construct(Security $security, TranslatorInterface $translator) { $this->security = $security; + $this->translator = $translator; } public function buildMenu($menuId, \Knp\Menu\MenuItem $menu, array $parameters) @@ -44,6 +48,17 @@ class UserMenuBuilder implements LocalMenuBuilderInterface 'order' => -9999999, 'icon' => 'map-marker', ]); + + $menu + ->addChild( + $this->translator->trans('My notifications'), + ['route' => 'chill_main_notification_show'] + ) + ->setExtras([ + 'order' => 600, + 'icon' => 'envelope' + ]); + $menu ->addChild( 'Change password', From d62893827bd934f1c126b419d8eaaba088c2da5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 25 Dec 2021 22:16:34 +0100 Subject: [PATCH 02/71] create a notification controller --- ...{NotificationRenderer.php => NotificationHandlerManager.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Bundle/ChillMainBundle/Notification/{NotificationRenderer.php => NotificationHandlerManager.php} (97%) diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php b/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php similarity index 97% rename from src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php rename to src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php index 048dfc02f..950a3f09e 100644 --- a/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php +++ b/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php @@ -16,7 +16,7 @@ use Chill\MainBundle\Entity\Notification; use Chill\PersonBundle\Notification\AccompanyingPeriodNotificationRenderer; use Exception; -final class NotificationRenderer +final class NotificationHandlerManager { private array $renderers; From 11824adda45dbf2bffba618c2cf7fbde0c684fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 25 Dec 2021 22:41:25 +0100 Subject: [PATCH 03/71] notification: create --- .../ActivityNotificationRenderer.php | 3 +- .../Controller/NotificationController.php | 97 +++++++++++++++++-- .../ChillMainBundle/Entity/Notification.php | 3 +- .../ChillMainBundle/Form/NotificationType.php | 41 ++++++++ .../Exception/NotificationHandlerNotFound.php | 18 ++++ .../NotificationHandlerInterface.php | 16 +++ .../NotificationHandlerManager.php | 26 ++--- .../views/Notification/create.html.twig | 25 +++++ ...AccompanyingPeriodNotificationRenderer.php | 3 +- .../views/AccompanyingCourse/index.html.twig | 4 + 10 files changed, 210 insertions(+), 26 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Form/NotificationType.php create mode 100644 src/Bundle/ChillMainBundle/Notification/Exception/NotificationHandlerNotFound.php create mode 100644 src/Bundle/ChillMainBundle/Notification/NotificationHandlerInterface.php create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig diff --git a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php index f7f19e3f1..2d3d333d9 100644 --- a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php +++ b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php @@ -13,8 +13,9 @@ namespace Chill\ActivityBundle\Notification; use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Entity\Notification; +use Chill\MainBundle\Notification\NotificationHandlerInterface; -final class ActivityNotificationRenderer +final class ActivityNotificationRenderer implements NotificationHandlerInterface { public function getTemplate() { diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php index 8ce15e6a4..19d5142ca 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -11,41 +11,118 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; -use Chill\MainBundle\Notification\NotificationRenderer; +use Chill\MainBundle\Entity\Notification; +use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Form\NotificationType; +use Chill\MainBundle\Notification\Exception\NotificationHandlerNotFound; +use Chill\MainBundle\Notification\NotificationHandlerManager; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Repository\NotificationRepository; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @Route("/{_locale}/notification") */ class NotificationController extends AbstractController { - private Security $security; + private EntityManagerInterface $em; + + private NotificationHandlerManager $notificationHandlerManager; + private NotificationRepository $notificationRepository; - private NotificationRenderer $notificationRenderer; + private PaginatorFactory $paginatorFactory; + private Security $security; + + private TranslatorInterface $translator; + public function __construct( + EntityManagerInterface $em, Security $security, NotificationRepository $notificationRepository, - NotificationRenderer $notificationRenderer, - PaginatorFactory $paginatorFactory + NotificationHandlerManager $notificationHandlerManager, + PaginatorFactory $paginatorFactory, + TranslatorInterface $translator ) { + $this->em = $em; $this->security = $security; $this->notificationRepository = $notificationRepository; - $this->notificationRenderer = $notificationRenderer; + $this->notificationHandlerManager = $notificationHandlerManager; $this->paginatorFactory = $paginatorFactory; + $this->translator = $translator; } /** - * @Route("/show", name="chill_main_notification_show") + * @Route("/create", name="chill_main_notification_create") */ - public function showAction(): Response + public function createAction(Request $request): Response { + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED'); + + if (!$this->security->getUser() instanceof User) { + throw new AccessDeniedHttpException('You must be authenticated and a user to create a notification'); + } + + if (!$request->query->has('entityClass')) { + throw new BadRequestHttpException('Missing entityClass parameter'); + } + + if (!$request->query->has('entityId')) { + throw new BadRequestHttpException('missing entityId parameter'); + } + + $notification = new Notification(); + $notification + ->setRelatedEntityClass($request->query->get('entityClass')) + ->setRelatedEntityId($request->query->getInt('entityId')) + ->setSender($this->security->getUser()); + + try { + $handler = $this->notificationHandlerManager->getHandler($notification); + } catch (NotificationHandlerNotFound $e) { + throw new BadRequestHttpException('no handler for this notification'); + } + + $form = $this->createForm(NotificationType::class, $notification); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->em->persist($notification); + $this->em->flush(); + + $this->addFlash('success', $this->translator->trans('notification.Notification created')); + + if ($request->query->has('returnPath')) { + return new RedirectResponse($request->query->get('returnPath')); + } + + return $this->redirectToRoute('chill_main_homepage'); + } + + return $this->render('@ChillMain/Notification/create.html.twig', [ + 'form' => $form->createView(), + 'handler' => $handler, + 'notification' => $notification, + ]); + } + + /** + * @Route("/my", name="chill_main_notification_my") + */ + public function myAction(): Response + { + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED'); $currentUser = $this->security->getUser(); $notificationsNbr = $this->notificationRepository->countAllForAttendee(($currentUser)); @@ -61,8 +138,8 @@ class NotificationController extends AbstractController foreach ($notifications as $notification) { $data = [ - 'template' => $this->notificationRenderer->getTemplate($notification), - 'template_data' => $this->notificationRenderer->getTemplateData($notification), + 'template' => $this->notificationHandlerManager->getTemplate($notification), + 'template_data' => $this->notificationHandlerManager->getTemplateData($notification), 'notification' => $notification, ]; $templateData[] = $data; diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php index bfcdf1ee6..a9b576f1f 100644 --- a/src/Bundle/ChillMainBundle/Entity/Notification.php +++ b/src/Bundle/ChillMainBundle/Entity/Notification.php @@ -53,7 +53,7 @@ class Notification /** * @ORM\Column(type="json") */ - private array $read; + private array $read = []; /** * @ORM\Column(type="string", length=255) @@ -74,6 +74,7 @@ class Notification public function __construct() { $this->addressees = new ArrayCollection(); + $this->setDate(new DateTimeImmutable()); } public function addAddressee(User $addressee): self diff --git a/src/Bundle/ChillMainBundle/Form/NotificationType.php b/src/Bundle/ChillMainBundle/Form/NotificationType.php new file mode 100644 index 000000000..af884dfbb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/NotificationType.php @@ -0,0 +1,41 @@ +add('addressees', EntityType::class, [ + 'class' => User::class, + 'choice_label' => 'label', + 'multiple' => true, + ]) + ->add('message', ChillTextareaType::class, [ + 'required' => false, + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('class', Notification::class); + } +} diff --git a/src/Bundle/ChillMainBundle/Notification/Exception/NotificationHandlerNotFound.php b/src/Bundle/ChillMainBundle/Notification/Exception/NotificationHandlerNotFound.php new file mode 100644 index 000000000..00add5f4f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Notification/Exception/NotificationHandlerNotFound.php @@ -0,0 +1,18 @@ +renderers[] = $activityNotificationRenderer; } - public function getTemplate(Notification $notification) - { - return $this->getRenderer($notification)->getTemplate(); - } - - public function getTemplateData(Notification $notification) - { - return $this->getRenderer($notification)->getTemplateData($notification); - } - - private function getRenderer(Notification $notification) + public function getHandler(Notification $notification): NotificationHandlerInterface { foreach ($this->renderers as $renderer) { if ($renderer->supports($notification)) { @@ -49,6 +39,16 @@ final class NotificationHandlerManager } } - throw new Exception('No renderer for ' . $notification); + throw new NotificationHandlerNotFound(); + } + + public function getTemplate(Notification $notification) + { + return $this->getHandler($notification)->getTemplate(); + } + + public function getTemplateData(Notification $notification) + { + return $this->getHandler($notification)->getTemplateData($notification); } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig new file mode 100644 index 000000000..a6a52acf7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig @@ -0,0 +1,25 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block content %} +
+
+ {% include handler.template(notification) with handler.templateData(notification) %} + + {{ form_start(form, { 'attr': { 'id': 'notification' }}) }} + + {{ form_row(form.addressees) }} + {{ form_row(form.message) }} + + {{ form_end(form) }} + + +
+
+{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php index c67c3d8a8..934f3f1c0 100644 --- a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php @@ -12,9 +12,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Notification; use Chill\MainBundle\Entity\Notification; +use Chill\MainBundle\Notification\NotificationHandlerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; -final class AccompanyingPeriodNotificationRenderer +final class AccompanyingPeriodNotificationRenderer implements NotificationHandlerInterface { public function getTemplate() { diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig index 39e924d18..2ced8f611 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig @@ -108,6 +108,10 @@ {% endif %} + + {% if accompanyingCourse.requestorPerson is not null or accompanyingCourse.requestorThirdParty is not null %}
{% if accompanyingCourse.requestorPerson is not null %} From 5cebcfddb7f1ce183827754134c80d7ee286bcaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 25 Dec 2021 22:51:58 +0100 Subject: [PATCH 04/71] fix cs ? --- .../Normalizer/CollectionDocGenNormalizer.php | 2 +- .../Normalizer/DocGenObjectNormalizer.php | 2 +- .../{show.html.twig => list.html.twig} | 0 .../Routing/MenuBuilder/UserMenuBuilder.php | 4 ++-- .../Serializer/Normalizer/AddressNormalizer.php | 2 +- .../Normalizer/CollectionNormalizer.php | 2 +- .../Tests/Export/ExportManagerTest.php | 16 ++++++++-------- .../PasswordRecover/TokenManagerTest.php | 3 ++- .../config/services/notification.yaml | 2 +- .../ChillMainBundle/translations/messages.fr.yml | 4 ++++ .../Controller/HouseholdMemberController.php | 2 +- .../DataFixtures/ORM/LoadHouseholdPosition.php | 2 +- .../AccompanyingPeriodDocGenNormalizer.php | 2 +- .../AccompanyingPeriodOriginNormalizer.php | 2 +- ...AccompanyingPeriodParticipationNormalizer.php | 2 +- .../Normalizer/PersonJsonNormalizer.php | 2 +- .../Normalizer/RelationshipDocGenNormalizer.php | 2 +- .../Tests/Form/Type/PickPersonTypeTest.php | 3 ++- .../Normalizer/ThirdPartyNormalizer.php | 2 +- 19 files changed, 31 insertions(+), 25 deletions(-) rename src/Bundle/ChillMainBundle/Resources/views/Notification/{show.html.twig => list.html.twig} (100%) diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php index 55774dd83..cd01a003a 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php @@ -26,7 +26,7 @@ class CollectionDocGenNormalizer implements ContextAwareNormalizerInterface, Nor /** * @param Collection $object - * @param null|string $format + * @param string|null $format * * @return array|ArrayObject|bool|float|int|string|void|null */ diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php index fc4b4beb9..0a3f6e403 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php @@ -66,7 +66,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte if (!$this->classMetadataFactory->hasMetadataFor($classMetadataKey)) { throw new LogicException(sprintf( 'This object does not have metadata: %s. Add groups on this entity to allow to serialize with the format %s and groups %s', - is_object($object) ? get_class($object) : '(todo' /*$context['docgen:expects'],*/, + is_object($object) ? get_class($object) : '(todo' /*$context['docgen:expects'],*/ , $format, implode(', ', ($context['groups'] ?? [])) )); diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig similarity index 100% rename from src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig rename to src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php index 495b2ca8b..74a8ff31d 100644 --- a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php +++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php @@ -52,11 +52,11 @@ class UserMenuBuilder implements LocalMenuBuilderInterface $menu ->addChild( $this->translator->trans('My notifications'), - ['route' => 'chill_main_notification_show'] + ['route' => 'chill_main_notification_my'] ) ->setExtras([ 'order' => 600, - 'icon' => 'envelope' + 'icon' => 'envelope', ]); $menu diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php index f66eb4de6..119126a2e 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php @@ -48,7 +48,7 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw /** * @param Address $address - * @param null|string $format + * @param string|null $format */ public function normalize($address, $format = null, array $context = []) { diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/CollectionNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CollectionNormalizer.php index 7ca4d563c..08b22871f 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/CollectionNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CollectionNormalizer.php @@ -22,7 +22,7 @@ class CollectionNormalizer implements NormalizerAwareInterface, NormalizerInterf /** * @param Collection $collection - * @param null|string $format + * @param string|null $format */ public function normalize($collection, $format = null, array $context = []) { diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php index c09385e50..94266077a 100644 --- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php @@ -388,7 +388,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetAggregatorNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -446,7 +446,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetExportNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -485,7 +485,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetFilterNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -646,7 +646,7 @@ final class ExportManagerTest extends KernelTestCase public function testNonExistingFormatter() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -668,10 +668,10 @@ final class ExportManagerTest extends KernelTestCase ?UserInterface $user = null ): ExportManager { $localUser = $user ?? self::$container->get( - 'doctrine.orm.entity_manager' - ) - ->getRepository('ChillMainBundle:User') - ->findOneBy(['username' => 'center a_social']); + 'doctrine.orm.entity_manager' + ) + ->getRepository('ChillMainBundle:User') + ->findOneBy(['username' => 'center a_social']); $token = new UsernamePasswordToken($localUser, 'password', 'provider'); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); diff --git a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php index 294ddceb6..cc10e43cb 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Security\PasswordRecover\TokenManager; use DateInterval; use DateTimeImmutable; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use UnexpectedValueException; /** * @internal @@ -57,7 +58,7 @@ final class TokenManagerTest extends KernelTestCase public function testGenerateEmptyUsernameCanonical() { - $this->expectException(\UnexpectedValueException::class); + $this->expectException(UnexpectedValueException::class); $tokenManager = $this->tokenManager; // set a username, but not a username canonical diff --git a/src/Bundle/ChillMainBundle/config/services/notification.yaml b/src/Bundle/ChillMainBundle/config/services/notification.yaml index efb25c5b5..34027d631 100644 --- a/src/Bundle/ChillMainBundle/config/services/notification.yaml +++ b/src/Bundle/ChillMainBundle/config/services/notification.yaml @@ -13,4 +13,4 @@ services: $translator: '@Symfony\Component\Translation\TranslatorInterface' $routeParameters: '%chill_main.notifications%' - Chill\MainBundle\Notification\NotificationRenderer: ~ + Chill\MainBundle\Notification\NotificationHandlerManager: ~ diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 4ae23f1e3..1d6fa70e0 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -351,3 +351,7 @@ By: Par For: Pour Created for: Créé pour Created by: Créé par + +notification: + Notify: Notifier + Notification create: Notification envoyée diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php index 78234d114..ddf5d9683 100644 --- a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php +++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php @@ -187,7 +187,7 @@ class HouseholdMemberController extends ApiController $_format, ['groups' => ['read']] ); - } catch (Exception\InvalidArgumentException|Exception\UnexpectedValueException $e) { + } catch (Exception\InvalidArgumentException | Exception\UnexpectedValueException $e) { throw new BadRequestException("Deserialization error: {$e->getMessage()}", 45896, $e); } diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdPosition.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdPosition.php index 1642a71b9..fed7d2817 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdPosition.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdPosition.php @@ -33,7 +33,7 @@ class LoadHouseholdPosition extends Fixture { foreach ( self::POSITIONS_DATA as [$name, $share, $allowHolder, - $ordering, $ref, ] + $ordering, $ref, ] ) { $position = (new Position()) ->setLabel(['fr' => $name]) diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodDocGenNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodDocGenNormalizer.php index 4343561fc..561067652 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodDocGenNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodDocGenNormalizer.php @@ -93,7 +93,7 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf /** * @param AccompanyingPeriod|null $period - * @param null|string $format + * @param string|null $format */ public function normalize($period, $format = null, array $context = []) { diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodOriginNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodOriginNormalizer.php index 88dce414f..66f65b67b 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodOriginNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodOriginNormalizer.php @@ -21,7 +21,7 @@ final class AccompanyingPeriodOriginNormalizer implements NormalizerInterface { /** * @param Origin $origin - * @param null|string $format + * @param string|null $format */ public function normalize($origin, $format = null, array $context = []) { diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php index b392ccc24..d9472184e 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php @@ -21,7 +21,7 @@ class AccompanyingPeriodParticipationNormalizer implements NormalizerAwareInterf /** * @param AccompanyingPeriodParticipation $participation - * @param null|string $format + * @param string|null $format */ public function normalize($participation, $format = null, array $context = []) { diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php index 273c2aa32..8962acc83 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php @@ -141,7 +141,7 @@ class PersonJsonNormalizer implements /** * @param Person $person - * @param null|string $format + * @param string|null $format */ public function normalize($person, $format = null, array $context = []) { diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/RelationshipDocGenNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/RelationshipDocGenNormalizer.php index cfae9f6e6..52a0f701c 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/RelationshipDocGenNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/RelationshipDocGenNormalizer.php @@ -31,7 +31,7 @@ class RelationshipDocGenNormalizer implements ContextAwareNormalizerInterface, N /** * @param Relationship $relation - * @param null|string $format + * @param string|null $format */ public function normalize($relation, $format = null, array $context = []) { diff --git a/src/Bundle/ChillPersonBundle/Tests/Form/Type/PickPersonTypeTest.php b/src/Bundle/ChillPersonBundle/Tests/Form/Type/PickPersonTypeTest.php index 5c7942de3..6a9e37531 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Form/Type/PickPersonTypeTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Form/Type/PickPersonTypeTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Form\Type; use Chill\PersonBundle\Form\Type\PickPersonType; +use RuntimeException; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use function count; @@ -52,7 +53,7 @@ final class PickPersonTypeTest extends KernelTestCase */ public function testWithInvalidOptionCenters() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->markTestSkipped('need to inject locale into url generator without request'); $this->formFactory diff --git a/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php b/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php index d95192f2a..3f4fe87bf 100644 --- a/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php +++ b/src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php @@ -30,7 +30,7 @@ class ThirdPartyNormalizer implements NormalizerAwareInterface, NormalizerInterf /** * @param ThirdParty $thirdParty - * @param null|string $format + * @param string|null $format */ public function normalize($thirdParty, $format = null, array $context = []) { From f6f0786d38afb061f45447468f64fb7cf21295af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 25 Dec 2021 22:53:35 +0100 Subject: [PATCH 05/71] notificaiton: load interface automatically --- .../ChillMainBundle/ChillMainBundle.php | 3 +++ .../NotificationHandlerManager.php | 27 ++++++++++--------- .../views/Notification/list.html.twig | 3 ++- .../config/services/notification.yaml | 4 ++- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Bundle/ChillMainBundle/ChillMainBundle.php b/src/Bundle/ChillMainBundle/ChillMainBundle.php index 2ff7ef862..0416edf76 100644 --- a/src/Bundle/ChillMainBundle/ChillMainBundle.php +++ b/src/Bundle/ChillMainBundle/ChillMainBundle.php @@ -22,6 +22,7 @@ use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass; use Chill\MainBundle\DependencyInjection\CompilerPass\WidgetsCompilerPass; use Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass; use Chill\MainBundle\DependencyInjection\RoleProvidersCompilerPass; +use Chill\MainBundle\Notification\NotificationHandlerInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Search\SearchApiInterface; use Chill\MainBundle\Security\ProvideRoleInterface; @@ -50,6 +51,8 @@ class ChillMainBundle extends Bundle ->addTag('chill.render_entity'); $container->registerForAutoconfiguration(SearchApiInterface::class) ->addTag('chill.search_api_provider'); + $container->registerForAutoconfiguration(NotificationHandlerInterface::class) + ->addTag('chill_main.notification_handler'); $container->addCompilerPass(new SearchableServicesCompilerPass()); $container->addCompilerPass(new ConfigConsistencyCompilerPass()); diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php b/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php index a28afd5e9..214f24239 100644 --- a/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php +++ b/src/Bundle/ChillMainBundle/Notification/NotificationHandlerManager.php @@ -11,29 +11,30 @@ declare(strict_types=1); namespace Chill\MainBundle\Notification; -use Chill\ActivityBundle\Notification\ActivityNotificationRenderer; use Chill\MainBundle\Entity\Notification; use Chill\MainBundle\Notification\Exception\NotificationHandlerNotFound; -use Chill\PersonBundle\Notification\AccompanyingPeriodNotificationRenderer; +use Doctrine\ORM\EntityManagerInterface; final class NotificationHandlerManager { - private array $renderers; + private EntityManagerInterface $em; + + private iterable $handlers; public function __construct( - AccompanyingPeriodNotificationRenderer $accompanyingPeriodNotificationRenderer, - ActivityNotificationRenderer $activityNotificationRenderer + iterable $handlers, + EntityManagerInterface $em ) { - // TODO configure automatically - // TODO CREER UNE INTERFACE POUR ETRE SUR QUE LES RENDERERS SONT OK - - $this->renderers[] = $accompanyingPeriodNotificationRenderer; - $this->renderers[] = $activityNotificationRenderer; + $this->handlers = $handlers; + $this->em = $em; } + /** + * @throw NotificationHandlerNotFound if handler is not found + */ public function getHandler(Notification $notification): NotificationHandlerInterface { - foreach ($this->renderers as $renderer) { + foreach ($this->handlers as $renderer) { if ($renderer->supports($notification)) { return $renderer; } @@ -42,12 +43,12 @@ final class NotificationHandlerManager throw new NotificationHandlerNotFound(); } - public function getTemplate(Notification $notification) + public function getTemplate(Notification $notification): string { return $this->getHandler($notification)->getTemplate(); } - public function getTemplateData(Notification $notification) + public function getTemplateData(Notification $notification): array { return $this->getHandler($notification)->getTemplateData($notification); } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig index 34aeee049..4f4f81ba2 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig @@ -4,7 +4,6 @@

{{ "Notifications list" | trans }}

- {%for data in datas %} {% set notification = data.notification %} @@ -36,6 +35,8 @@ {% include data.template with data.template_data %} + {% else %} +

{{ notification.Any notification received }}

{% endfor %}
diff --git a/src/Bundle/ChillMainBundle/config/services/notification.yaml b/src/Bundle/ChillMainBundle/config/services/notification.yaml index 34027d631..5d4390877 100644 --- a/src/Bundle/ChillMainBundle/config/services/notification.yaml +++ b/src/Bundle/ChillMainBundle/config/services/notification.yaml @@ -13,4 +13,6 @@ services: $translator: '@Symfony\Component\Translation\TranslatorInterface' $routeParameters: '%chill_main.notifications%' - Chill\MainBundle\Notification\NotificationHandlerManager: ~ + Chill\MainBundle\Notification\NotificationHandlerManager: + arguments: + $handlers: !tagged_iterator chill_main.notification_handler From bd3919efcb11f314071525bdc97979a3c9fdb4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 26 Dec 2021 01:00:50 +0100 Subject: [PATCH 06/71] notification: store users which are unread instead of read, and in dedicated table The query "which are the unread notification" is much more frequent than the read one. We then store the unread items in a dedicated table. --- composer.json | 11 +- .../Controller/NotificationController.php | 58 ++++++++-- .../ChillMainBundle/Entity/Notification.php | 57 +++++++--- .../Repository/NotificationRepository.php | 102 +++++++++++++++--- .../views/Notification/list.html.twig | 86 +++++++++------ .../migrations/Version20211225231532.php | 39 +++++++ .../translations/messages.fr.yml | 6 +- .../Controller/HouseholdMemberController.php | 2 +- .../config/services/notification.yaml | 1 + 9 files changed, 283 insertions(+), 79 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20211225231532.php diff --git a/composer.json b/composer.json index a46446531..312cd1cee 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "php": "^7.4", "champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290", "champs-libres/wopi-bundle": "dev-master#59b468503b9413f8d588ef9e626e7675560db3d8", - "ocramius/package-versions": "^1.10", "doctrine/doctrine-bundle": "^2.1", "doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/orm": "^2.7", @@ -22,6 +21,7 @@ "knplabs/knp-time-bundle": "^1.12", "league/csv": "^9.7.1", "nyholm/psr7": "^1.4", + "ocramius/package-versions": "^1.10", "phpoffice/phpspreadsheet": "^1.16", "ramsey/uuid-doctrine": "^1.7", "sensio/framework-extra-bundle": "^5.5", @@ -47,6 +47,7 @@ "twig/extra-bundle": "^3.0", "twig/intl-extra": "^3.0", "twig/markdown-extra": "^3.3", + "twig/string-extra": "^3.3", "twig/twig": "^3.0" }, "conflict": { @@ -72,7 +73,13 @@ "bin-dir": "bin", "optimize-autoloader": true, "sort-packages": true, - "vendor-dir": "tests/app/vendor" + "vendor-dir": "tests/app/vendor", + "allow-plugins": { + "composer/package-versions-deprecated": true, + "phpstan/extension-installer": true, + "ergebnis/composer-normalize": true, + "phpro/grumphp": true + } }, "autoload": { "psr-4": { diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php index 19d5142ca..c3ec0e549 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -118,9 +118,9 @@ class NotificationController extends AbstractController } /** - * @Route("/my", name="chill_main_notification_my") + * @Route("/inbox", name="chill_main_notification_my") */ - public function myAction(): Response + public function inboxAction(): Response { $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED'); $currentUser = $this->security->getUser(); @@ -134,21 +134,61 @@ class NotificationController extends AbstractController $offset = $paginator->getCurrentPage()->getFirstItemNumber() ); + return $this->render('@ChillMain/Notification/list.html.twig', [ + 'datas' => $this->itemsForTemplate($notifications), + 'notifications' => $notifications, + 'paginator' => $paginator, + 'step' => 'inbox', + 'unreads' => $this->countUnread(), + ]); + } + + /** + * @Route("/sent", name="chill_main_notification_sent") + */ + public function sentAction(): Response + { + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED'); + $currentUser = $this->security->getUser(); + + $notificationsNbr = $this->notificationRepository->countAllForSender($currentUser); + $paginator = $this->paginatorFactory->create($notificationsNbr); + + $notifications = $this->notificationRepository->findAllForSender( + $currentUser, + $limit = $paginator->getItemsPerPage(), + $offset = $paginator->getCurrentPage()->getFirstItemNumber() + ); + + return $this->render('@ChillMain/Notification/list.html.twig', [ + 'datas' => $this->itemsForTemplate($notifications), + 'notifications' => $notifications, + 'paginator' => $paginator, + 'step' => 'sent', + 'unreads' => $this->countUnread(), + ]); + } + + private function countUnread(): array + { + return [ + 'sent' => $this->notificationRepository->countUnreadByUserWhereSender($this->security->getUser()), + 'inbox' => $this->notificationRepository->countUnreadByUserWhereAddressee($this->security->getUser()), + ]; + } + + private function itemsForTemplate(array $notifications): array + { $templateData = []; foreach ($notifications as $notification) { - $data = [ + $templateData[] = [ 'template' => $this->notificationHandlerManager->getTemplate($notification), 'template_data' => $this->notificationHandlerManager->getTemplateData($notification), 'notification' => $notification, ]; - $templateData[] = $data; } - return $this->render('@ChillMain/Notification/show.html.twig', [ - 'datas' => $templateData, - 'notifications' => $notifications, - 'paginator' => $paginator, - ]); + return $templateData; } } diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php index a9b576f1f..c484f9826 100644 --- a/src/Bundle/ChillMainBundle/Entity/Notification.php +++ b/src/Bundle/ChillMainBundle/Entity/Notification.php @@ -50,11 +50,6 @@ class Notification */ private string $message = ''; - /** - * @ORM\Column(type="json") - */ - private array $read = []; - /** * @ORM\Column(type="string", length=255) */ @@ -71,9 +66,16 @@ class Notification */ private User $sender; + /** + * @ORM\ManyToMany(targetEntity=User::class) + * @ORM\JoinTable(name="chill_main_notification_addresses_unread") + */ + private Collection $unreadBy; + public function __construct() { $this->addressees = new ArrayCollection(); + $this->unreadBy = new ArrayCollection(); $this->setDate(new DateTimeImmutable()); } @@ -81,6 +83,16 @@ class Notification { if (!$this->addressees->contains($addressee)) { $this->addressees[] = $addressee; + $this->addUnreadBy($addressee); + } + + return $this; + } + + public function addUnreadBy(User $user): self + { + if (!$this->unreadBy->contains($user)) { + $this->unreadBy->add($user); } return $this; @@ -109,11 +121,6 @@ class Notification return $this->message; } - public function getRead(): array - { - return $this->read; - } - public function getRelatedEntityClass(): ?string { return $this->relatedEntityClass; @@ -129,9 +136,32 @@ class Notification return $this->sender; } + public function isReadBy(User $user): bool + { + return !$this->unreadBy->contains($user); + } + + public function markAsReadBy(User $user): self + { + return $this->removeUnreadBy($user); + } + + public function markAsUnreadBy(User $user): self + { + return $this->addUnreadBy($user); + } + public function removeAddressee(User $addressee): self { $this->addressees->removeElement($addressee); + $this->unreadBy->removeElement($addressee); + + return $this; + } + + public function removeUnreadBy(User $user): self + { + $this->unreadBy->removeElement($user); return $this; } @@ -150,13 +180,6 @@ class Notification return $this; } - public function setRead(array $read): self - { - $this->read = $read; - - return $this; - } - public function setRelatedEntityClass(string $relatedEntityClass): self { $this->relatedEntityClass = $relatedEntityClass; diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index 4af19550c..eea2d41e1 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -13,25 +13,75 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Notification; use Chill\MainBundle\Entity\User; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; final class NotificationRepository implements ObjectRepository { + private EntityManagerInterface $em; + private EntityRepository $repository; public function __construct(EntityManagerInterface $entityManager) { + $this->em = $entityManager; $this->repository = $entityManager->getRepository(Notification::class); } - public function countAllForAttendee(User $addressee): int // TODO passer à attendees avec S + public function countAllForAttendee(User $addressee): int { - $query = $this->queryAllForAttendee($addressee, $countQuery = true); + return $this->queryByAddressee($addressee) + ->select('count(n)') + ->getQuery() + ->getSingleScalarResult(); + } - return $query->getSingleScalarResult(); + public function countAllForSender(User $sender): int + { + return $this->queryBySender($sender) + ->select('count(n)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function countUnreadByUser(User $user): int + { + $sql = 'SELECT count(*) AS c FROM chill_main_notification_addresses_unread WHERE user_id = ?'; + + $rsm = new Query\ResultSetMapping(); + $rsm->addScalarResult('c', 'c', Types::INTEGER); + + $nq = $this->em->createNativeQuery($sql, $rsm); + + return $nq->getSingleScalarResult(); + } + + public function countUnreadByUserWhereAddressee(User $user): int + { + $qb = $this->repository->createQueryBuilder('n'); + $qb + ->select('count(n)') + ->where($qb->expr()->isMemberOf(':user', 'n.addressees')) + ->andWhere($qb->expr()->isMemberOf(':user', 'n.unreadBy')) + ->setParameter('user', $user); + + return $qb->getQuery()->getSingleScalarResult(); + } + + public function countUnreadByUserWhereSender(User $user): int + { + $qb = $this->repository->createQueryBuilder('n'); + $qb + ->select('count(n)') + ->where($qb->expr()->eq('n.sender', ':user')) + ->andWhere($qb->expr()->isMemberOf(':user', 'n.unreadBy')) + ->setParameter('user', $user); + + return $qb->getQuery()->getSingleScalarResult(); } public function find($id, $lockMode = null, $lockVersion = null): ?Notification @@ -53,9 +103,9 @@ final class NotificationRepository implements ObjectRepository * * @return Notification[] */ - public function findAllForAttendee(User $addressee, $limit = null, $offset = null): array // TODO passer à attendees avec S + public function findAllForAttendee(User $addressee, $limit = null, $offset = null): array { - $query = $this->queryAllForAttendee($addressee); + $query = $this->queryByAddressee($addressee)->select('n'); if ($limit) { $query = $query->setMaxResults($limit); @@ -65,7 +115,22 @@ final class NotificationRepository implements ObjectRepository $query = $query->setFirstResult($offset); } - return $query->getResult(); + return $query->getQuery()->getResult(); + } + + public function findAllForSender(User $sender, $limit = null, $offset = null): array + { + $query = $this->queryBySender($sender)->select('n'); + + if ($limit) { + $query = $query->setMaxResults($limit); + } + + if ($offset) { + $query = $query->setFirstResult($offset); + } + + return $query->getQuery()->getResult(); } /** @@ -89,22 +154,25 @@ final class NotificationRepository implements ObjectRepository return Notification::class; } - private function queryAllForAttendee(User $addressee, bool $countQuery = false): Query + private function queryByAddressee(User $addressee, bool $countQuery = false): QueryBuilder { $qb = $this->repository->createQueryBuilder('n'); - $select = 'n'; - - if ($countQuery) { - $select = 'count(n)'; - } - $qb - ->select($select) - ->join('n.addressees', 'a') - ->where('a = :addressee') + ->where($qb->expr()->isMemberOf(':addressee', 'n.addressees')) ->setParameter('addressee', $addressee); - return $qb->getQuery(); + return $qb; + } + + private function queryBySender(User $sender): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('n'); + + $qb + ->where($qb->expr()->eq('n.sender', ':sender')) + ->setParameter('sender', $sender); + + return $qb; } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig index 4f4f81ba2..4a1f504f4 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig @@ -1,43 +1,65 @@ {% extends "@ChillMain/layout.html.twig" %} +{% block title 'notification.List'|trans %} + {% block content %}
-
-

{{ "Notifications list" | trans }}

+
+
+

{{ block('title') }}

- {%for data in datas %} - {% set notification = data.notification %} + -
-
{{ 'Message'|trans }}
-
{{ notification.message }}
-
+ {% if datas|length == 0 %} + {% if step == 'inbox' %} +

{{ 'notification.Any notification received'|trans }}

+ {% else %} +

{{ 'notification.Any notification sent'|trans }}

+ {% endif %} + {% else %} +
+ {% for data in datas %} + {% set notification = data.notification %} +
+ {% include data.template with data.template_data %} +
+
+ {% if step == 'inbox' %} +
{{ 'notification.from'|trans }}: {{ notification.sender|chill_entity_render_string }}
+ {% endif %} +
{{ 'notification.adressees'|trans }}{% for a in notification.addressees %}{{ a|chill_entity_render_string }}{% if not loop.last %}, {% endif %}{% endfor %}
+
{{ notification.date|format_datetime('long', 'short') }}
+
+
+ {{ notification.message|u.truncate(250, '…', false)|chill_markdown_to_html }} +
+
+
+ {% endfor %} -
-
{{ 'Date'|trans }}
-
{{ notification.date | date('long') }}
-
+
+ {% endif %} +
- -
-
{{ 'Sender'|trans }}
-
{{ notification.sender }}
-
- -
-
{{ 'Addressees'|trans }}
-
{{ notification.addressees |join(', ') }}
-
- -
-
{{ 'Entity'|trans }}
-
- {% include data.template with data.template_data %} -
-
- {% else %} -

{{ notification.Any notification received }}

- {% endfor %}
{% endblock content %} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20211225231532.php b/src/Bundle/ChillMainBundle/migrations/Version20211225231532.php new file mode 100644 index 000000000..eabcce641 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20211225231532.php @@ -0,0 +1,39 @@ +addSql('DROP TABLE chill_main_notification_addresses_unread'); + $this->addSql('ALTER TABLE chill_main_notification ADD read JSONB DEFAULT \'[]\''); + } + + public function getDescription(): string + { + return 'Store notification readed by user in a specific table'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE TABLE chill_main_notification_addresses_unread (notification_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(notification_id, user_id))'); + $this->addSql('CREATE INDEX IDX_154A075FEF1A9D84 ON chill_main_notification_addresses_unread (notification_id)'); + $this->addSql('CREATE INDEX IDX_154A075FA76ED395 ON chill_main_notification_addresses_unread (user_id)'); + $this->addSql('ALTER TABLE chill_main_notification_addresses_unread ADD CONSTRAINT FK_154A075FEF1A9D84 FOREIGN KEY (notification_id) REFERENCES chill_main_notification (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_notification_addresses_unread ADD CONSTRAINT FK_154A075FA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_notification DROP read'); + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 1d6fa70e0..9b810a837 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -354,4 +354,8 @@ Created by: Créé par notification: Notify: Notifier - Notification create: Notification envoyée + Notification created: Notification envoyée + Any notification received: Aucune notification reçue + Any notification sent: Aucune notification envoyée + Notifications received: Notifications reçues + Notifications sent: Notification envoyées diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php index ddf5d9683..78234d114 100644 --- a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php +++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php @@ -187,7 +187,7 @@ class HouseholdMemberController extends ApiController $_format, ['groups' => ['read']] ); - } catch (Exception\InvalidArgumentException | Exception\UnexpectedValueException $e) { + } catch (Exception\InvalidArgumentException|Exception\UnexpectedValueException $e) { throw new BadRequestException("Deserialization error: {$e->getMessage()}", 45896, $e); } diff --git a/src/Bundle/ChillPersonBundle/config/services/notification.yaml b/src/Bundle/ChillPersonBundle/config/services/notification.yaml index 58187defb..c5d04f983 100644 --- a/src/Bundle/ChillPersonBundle/config/services/notification.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/notification.yaml @@ -1,3 +1,4 @@ services: Chill\PersonBundle\Notification\AccompanyingPeriodNotificationRenderer: autowire: true + autoconfigure: true From 700bb02374c33447d75f64c7e5e9263e4a9f17f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 26 Dec 2021 01:12:32 +0100 Subject: [PATCH 07/71] notification: add test for unread consistency --- .../ChillMainBundle/Entity/Notification.php | 9 ++++++-- .../ChillMainBundle/Form/NotificationType.php | 1 + .../Tests/Entity/NotificationTest.php | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php index c484f9826..b50b9de53 100644 --- a/src/Bundle/ChillMainBundle/Entity/Notification.php +++ b/src/Bundle/ChillMainBundle/Entity/Notification.php @@ -92,7 +92,7 @@ class Notification public function addUnreadBy(User $user): self { if (!$this->unreadBy->contains($user)) { - $this->unreadBy->add($user); + $this->unreadBy[] = $user; } return $this; @@ -136,6 +136,11 @@ class Notification return $this->sender; } + public function getUnreadBy(): Collection + { + return $this->unreadBy; + } + public function isReadBy(User $user): bool { return !$this->unreadBy->contains($user); @@ -154,7 +159,7 @@ class Notification public function removeAddressee(User $addressee): self { $this->addressees->removeElement($addressee); - $this->unreadBy->removeElement($addressee); + $this->removeUnreadBy($addressee); return $this; } diff --git a/src/Bundle/ChillMainBundle/Form/NotificationType.php b/src/Bundle/ChillMainBundle/Form/NotificationType.php index af884dfbb..b54752cde 100644 --- a/src/Bundle/ChillMainBundle/Form/NotificationType.php +++ b/src/Bundle/ChillMainBundle/Form/NotificationType.php @@ -28,6 +28,7 @@ class NotificationType extends AbstractType 'class' => User::class, 'choice_label' => 'label', 'multiple' => true, + 'by_reference' => true, ]) ->add('message', ChillTextareaType::class, [ 'required' => false, diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php new file mode 100644 index 000000000..9be00dde3 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php @@ -0,0 +1,23 @@ +addAddressee($user1 = new User()); + $notification->addAddressee($user2 = new User()); + + $this->assertCount(2, $notification->getAddressees()); + $this->assertCount(2, $notification->getUnreadBy()); + $this->assertContains($user1, $notification->getUnreadBy()->toArray()); + $this->assertContains($user2, $notification->getUnreadBy()->toArray()); + } + +} From d5d64cdf65a23bb15c3faa8d91f8f284f60b4009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 26 Dec 2021 01:12:42 +0100 Subject: [PATCH 08/71] upgrade test app --- tests/app | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app b/tests/app index bd95d3c96..5952eda44 160000 --- a/tests/app +++ b/tests/app @@ -1 +1 @@ -Subproject commit bd95d3c96a437757b7e8f35cdfd30da9aeac1a01 +Subproject commit 5952eda44831896991989c2e4881adc26329140e From b323ddae0597cb66be4b9748d245a0188a45379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 28 Dec 2021 19:40:38 +0100 Subject: [PATCH 09/71] fix loading of unread addressees in notification --- .../ChillMainBundle/Entity/Notification.php | 40 +++++-- .../Tests/Entity/NotificationTest.php | 102 +++++++++++++++++- .../migrations/Version20211228183221.php | 34 ++++++ 3 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20211228183221.php diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php index b50b9de53..1fa4e668e 100644 --- a/src/Bundle/ChillMainBundle/Entity/Notification.php +++ b/src/Bundle/ChillMainBundle/Entity/Notification.php @@ -20,19 +20,21 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Entity * @ORM\Table( * name="chill_main_notification", - * uniqueConstraints={ - * @ORM\UniqueConstraint(columns={"relatedEntityClass", "relatedEntityId"}) - * } * ) + * @ORM\HasLifecycleCallbacks */ class Notification { + private array $addedAddresses = []; + /** * @ORM\ManyToMany(targetEntity=User::class) * @ORM\JoinTable(name="chill_main_notification_addresses_user") */ private Collection $addressees; + private ?ArrayCollection $addressesOnLoad = null; + /** * @ORM\Column(type="datetime_immutable") */ @@ -60,6 +62,8 @@ class Notification */ private int $relatedEntityId; + private array $removedAddresses = []; + /** * @ORM\ManyToOne(targetEntity=User::class) * @ORM\JoinColumn(nullable=false) @@ -83,7 +87,7 @@ class Notification { if (!$this->addressees->contains($addressee)) { $this->addressees[] = $addressee; - $this->addUnreadBy($addressee); + $this->addedAddresses[] = $addressee; } return $this; @@ -103,6 +107,11 @@ class Notification */ public function getAddressees(): Collection { + // keep a copy to compute changes later + if (null === $this->addressesOnLoad) { + $this->addressesOnLoad = new ArrayCollection($this->addressees->toArray()); + } + return $this->addressees; } @@ -156,10 +165,29 @@ class Notification return $this->addUnreadBy($user); } + /** + * @ORM\PreFlush + */ + public function registerUnread() + { + foreach ($this->addedAddresses as $addressee) { + $this->addUnreadBy($addressee); + } + + if (null !== $this->addressesOnLoad) { + foreach ($this->addressees as $existingAddresse) { + if (!$this->addressesOnLoad->contains($existingAddresse)) { + $this->addUnreadBy($existingAddresse); + } + } + } + } + public function removeAddressee(User $addressee): self { - $this->addressees->removeElement($addressee); - $this->removeUnreadBy($addressee); + if ($this->addressees->removeElement($addressee)) { + $this->removedAddresses[] = $addressee; + } return $this; } diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php index 9be00dde3..6343800d5 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php @@ -1,23 +1,117 @@ get(EntityManagerInterface::class); + + foreach ($this->toDelete as [$className, $id]) { + $object = $em->find($className, $id); + $em->remove($object); + } + + $em->flush(); + } + + public function generateNotificationData() + { + self::bootKernel(); + $userRepository = self::$container->get(UserRepository::class); + + $senderId = $userRepository + ->findOneBy(['username' => 'center b_social']) + ->getId(); + + $addressesIds = []; + $addressesIds[] = $userRepository + ->findOneBy(['username' => 'center b_direction']) + ->getId(); + + yield [ + $senderId, + $addressesIds, + ]; + } + public function testAddAddresseeStoreAnUread() { $notification = new Notification(); $notification->addAddressee($user1 = new User()); $notification->addAddressee($user2 = new User()); + $notification->getAddressees()->add($user3 = new User()); - $this->assertCount(2, $notification->getAddressees()); - $this->assertCount(2, $notification->getUnreadBy()); + $this->assertCount(3, $notification->getAddressees()); + + // launch listener + $notification->registerUnread(); + $this->assertCount(3, $notification->getUnreadBy()); $this->assertContains($user1, $notification->getUnreadBy()->toArray()); $this->assertContains($user2, $notification->getUnreadBy()->toArray()); + $this->assertContains($user3, $notification->getUnreadBy()->toArray()); } + /** + * @dataProvider generateNotificationData + */ + public function testPrePersistComputeUnread(int $senderId, array $addressesIds) + { + $em = self::$container->get(EntityManagerInterface::class); + $notification = new Notification(); + $notification + ->setSender($em->find(User::class, $senderId)) + ->setRelatedEntityId(0) + ->setRelatedEntityClass(AccompanyingPeriod::class) + ->setMessage('Fake message'); + + foreach ($addressesIds as $addresseeId) { + $notification + ->getAddressees()->add($em->find(User::class, $addresseeId)); + } + + $em->persist($notification); + $em->flush(); + $em->refresh($notification); + + $this->toDelete[] = [Notification::class, $notification->getId()]; + + $this->assertEquals($senderId, $notification->getSender()->getId()); + $this->assertCount(count($addressesIds), $notification->getUnreadBy()); + + $unreadIds = $notification->getUnreadBy()->map(static function (User $u) { return $u->getId(); }); + + foreach ($addressesIds as $addresseeId) { + $this->assertContains($addresseeId, $unreadIds); + } + } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20211228183221.php b/src/Bundle/ChillMainBundle/migrations/Version20211228183221.php new file mode 100644 index 000000000..b74579959 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20211228183221.php @@ -0,0 +1,34 @@ +addSql('create unique index uniq_5bdc8067567988b4440f6072 + on chill_main_notification (relatedentityclass, relatedentityid)'); + } + + public function getDescription(): string + { + return 'remove unique index which prevent to notify twice an entity'; + } + + public function up(Schema $schema): void + { + $this->addSql('DROP INDEX uniq_5bdc8067567988b4440f6072'); + } +} From f453dbb543f3dbba6d4aa434193aed7bd04e60bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 28 Dec 2021 20:04:30 +0100 Subject: [PATCH 10/71] notification: more check in test --- .../Tests/Entity/NotificationTest.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php index 6343800d5..6bf966bbb 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php @@ -27,7 +27,7 @@ final class NotificationTest extends KernelTestCase { private array $toDelete = []; - protected function setUp() + protected function setUp(): void { self::bootKernel(); } @@ -70,15 +70,21 @@ final class NotificationTest extends KernelTestCase $notification->addAddressee($user1 = new User()); $notification->addAddressee($user2 = new User()); $notification->getAddressees()->add($user3 = new User()); + $notification->getAddressees()->add($user4 = new User()); - $this->assertCount(3, $notification->getAddressees()); + $this->assertCount(4, $notification->getAddressees()); // launch listener $notification->registerUnread(); - $this->assertCount(3, $notification->getUnreadBy()); + $this->assertCount(4, $notification->getUnreadBy()); $this->assertContains($user1, $notification->getUnreadBy()->toArray()); $this->assertContains($user2, $notification->getUnreadBy()->toArray()); $this->assertContains($user3, $notification->getUnreadBy()->toArray()); + + $notification->markAsReadBy($user1); + + $this->assertCount(3, $notification->getUnreadBy()); + $this->assertNotContains($user1, $notification->getUnreadBy()->toArray()); } /** From 1ad6a958e203540cbd2bd0ac1528297d807a10b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 28 Dec 2021 22:35:15 +0100 Subject: [PATCH 11/71] notification: add show action --- .../Controller/NotificationController.php | 21 ++++++++ .../views/Notification/list.html.twig | 44 +++++++++++----- .../views/Notification/show.html.twig | 28 ++++++++++ .../Authorization/NotificationVoter.php | 51 +++++++++++++++++++ .../config/services/security.yaml | 2 + 5 files changed, 133 insertions(+), 13 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig create mode 100644 src/Bundle/ChillMainBundle/Security/Authorization/NotificationVoter.php diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php index c3ec0e549..df38c15d9 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Notification\Exception\NotificationHandlerNotFound; use Chill\MainBundle\Notification\NotificationHandlerManager; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Repository\NotificationRepository; +use Chill\MainBundle\Security\Authorization\NotificationVoter; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -169,6 +170,26 @@ class NotificationController extends AbstractController ]); } + /** + * @Route("/{id}/show", name="chill_main_notification_show") + */ + public function showAction(Notification $notification, Request $request): Response + { + $this->denyAccessUnlessGranted(NotificationVoter::SEE, $notification); + + $response = $this->render('@ChillMain/Notification/show.html.twig', [ + 'notification' => $notification, + 'handler' => $this->notificationHandlerManager->getHandler($notification), + ]); + + if ($this->getUser() instanceof User && !$notification->isReadBy($this->getUser())) { + $notification->markAsReadBy($this->getUser()); + $this->em->flush(); + } + + return $response; + } + private function countUnread(): array { return [ diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig index 4a1f504f4..96074e14d 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/list.html.twig @@ -12,9 +12,11 @@