diff --git a/.changes/unreleased/Fixed-20250910-180935.yaml b/.changes/unreleased/Fixed-20250910-180935.yaml new file mode 100644 index 000000000..9e0a90cc8 --- /dev/null +++ b/.changes/unreleased/Fixed-20250910-180935.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Fix saving notification preferences in user's profile +time: 2025-09-10T18:09:35.525979715+02:00 +custom: + Issue: "" + SchemaChange: No schema change diff --git a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php new file mode 100644 index 000000000..f0751cb58 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php @@ -0,0 +1,64 @@ +getPhonenumber()); + + foreach ($flagManager->getAllNotificationFlagProviders() as $provider) { + $updateProfileCommand->setNotificationFlag( + $provider->getFlag(), + User::NOTIF_FLAG_IMMEDIATE_EMAIL, + $user->isNotificationSendImmediately($provider->getFlag()) + ); + $updateProfileCommand->setNotificationFlag( + $provider->getFlag(), + User::NOTIF_FLAG_DAILY_DIGEST, + $user->isNotificationDailyDigest($provider->getFlag()) + ); + } + + return $updateProfileCommand; + } + + /** + * @param User::NOTIF_FLAG_IMMEDIATE_EMAIL|User::NOTIF_FLAG_DAILY_DIGEST $kind + */ + private function setNotificationFlag(string $type, string $kind, bool $value): void + { + if (!array_key_exists($type, $this->notificationFlags)) { + $this->notificationFlags[$type] = ['immediate_email' => true, 'daily_digest' => false]; + } + + $k = match ($kind) { + User::NOTIF_FLAG_IMMEDIATE_EMAIL => 'immediate_email', + User::NOTIF_FLAG_DAILY_DIGEST => 'daily_digest', + }; + + $this->notificationFlags[$type][$k] = $value; + } +} diff --git a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php new file mode 100644 index 000000000..4c46e686e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php @@ -0,0 +1,27 @@ +setPhonenumber($command->phonenumber); + + foreach ($command->notificationFlags as $flag => $values) { + $user->setNotificationImmediately($flag, $values['immediate_email']); + $user->setNotificationDailyDigest($flag, $values['daily_digest']); + } + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/UserProfileController.php b/src/Bundle/ChillMainBundle/Controller/UserProfileController.php deleted file mode 100644 index b022a2b60..000000000 --- a/src/Bundle/ChillMainBundle/Controller/UserProfileController.php +++ /dev/null @@ -1,63 +0,0 @@ -security->isGranted('ROLE_USER')) { - throw new AccessDeniedHttpException(); - } - - $user = $this->security->getUser(); - $editForm = $this->createForm(UserProfileType::class, $user); - - $editForm->get('notificationFlags')->setData($user->getNotificationFlags()); - - $editForm->handleRequest($request); - - if ($editForm->isSubmitted() && $editForm->isValid()) { - $notificationFlagsData = $editForm->get('notificationFlags')->getData(); - $user->setNotificationFlags($notificationFlagsData); - - $em = $this->managerRegistry->getManager(); - $em->flush(); - $this->addFlash('success', $this->translator->trans('user.profile.Profile successfully updated!')); - - return $this->redirectToRoute('chill_main_user_profile'); - } - - return $this->render('@ChillMain/User/profile.html.twig', [ - 'user' => $user, - 'form' => $editForm->createView(), - ]); - } -} diff --git a/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php b/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php new file mode 100644 index 000000000..b23c201f0 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php @@ -0,0 +1,75 @@ +security->isGranted('ROLE_USER')) { + throw new AccessDeniedHttpException(); + } + + $user = $this->security->getUser(); + + $command = UpdateProfileCommand::create($user, $this->notificationFlagManager); + $editForm = $this->formFactory->create(UpdateProfileType::class, $command); + + $editForm->handleRequest($request); + + if ($editForm->isSubmitted() && $editForm->isValid()) { + $this->updateProfileCommandHandler->updateProfile($user, $command); + $this->entityManager->flush(); + $session->getFlashBag()->add('success', $this->translator->trans('user.profile.Profile successfully updated!')); + + return new RedirectResponse($this->urlGenerator->generate('chill_main_user_profile')); + } + + return new Response($this->twig->render('@ChillMain/User/profile.html.twig', [ + 'user' => $user, + 'form' => $editForm->createView(), + ])); + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 791391659..70b032238 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -652,42 +652,66 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter return true; } - public function getNotificationFlags(): array + private function getNotificationFlagData(string $flag): array { - return $this->notificationFlags; - } - - public function setNotificationFlags(array $notificationFlags) - { - $this->notificationFlags = $notificationFlags; - } - - public function getNotificationFlagData(string $flag): array - { - return $this->notificationFlags[$flag] ?? []; - } - - public function setNotificationFlagData(string $flag, array $data): void - { - $this->notificationFlags[$flag] = $data; + return $this->notificationFlags[$flag] ?? [self::NOTIF_FLAG_IMMEDIATE_EMAIL]; } public function isNotificationSendImmediately(string $type): bool { - if ([] === $this->getNotificationFlagData($type) || in_array(User::NOTIF_FLAG_IMMEDIATE_EMAIL, $this->getNotificationFlagData($type), true)) { - return true; + return $this->isNotificationForElement($type, self::NOTIF_FLAG_IMMEDIATE_EMAIL); + } + + public function setNotificationImmediately(string $type, bool $active): void + { + $this->setNotificationFlagElement($type, $active, self::NOTIF_FLAG_IMMEDIATE_EMAIL); + } + + public function setNotificationDailyDigest(string $type, bool $active): void + { + $this->setNotificationFlagElement($type, $active, self::NOTIF_FLAG_DAILY_DIGEST); + } + + /** + * @param self::NOTIF_FLAG_IMMEDIATE_EMAIL|self::NOTIF_FLAG_DAILY_DIGEST $kind + */ + private function setNotificationFlagElement(string $type, bool $active, string $kind): void + { + $notificationFlags = [...$this->notificationFlags]; + $changed = false; + + if (!isset($notificationFlags[$type])) { + $notificationFlags[$type] = [self::NOTIF_FLAG_IMMEDIATE_EMAIL]; + $changed = true; } - return false; + if ($active) { + if (!in_array($kind, $notificationFlags[$type], true)) { + $notificationFlags[$type] = [...$notificationFlags[$type], $kind]; + $changed = true; + } + } else { + if (in_array($kind, $notificationFlags[$type], true)) { + $notificationFlags[$type] = array_values( + array_filter($notificationFlags[$type], static fn ($k) => $k !== $kind) + ); + $changed = true; + } + } + + if ($changed) { + $this->notificationFlags = [...$notificationFlags]; + } + } + + private function isNotificationForElement(string $type, string $kind): bool + { + return in_array($kind, $this->getNotificationFlagData($type), true); } public function isNotificationDailyDigest(string $type): bool { - if (in_array(User::NOTIF_FLAG_DAILY_DIGEST, $this->getNotificationFlagData($type), true)) { - return true; - } - - return false; + return $this->isNotificationForElement($type, self::NOTIF_FLAG_DAILY_DIGEST); } public function getLocale(): string diff --git a/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php b/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php deleted file mode 100644 index d904ed5b5..000000000 --- a/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php +++ /dev/null @@ -1,75 +0,0 @@ -notificationFlagProviders as $flagProvider) { - $flag = $flagProvider->getFlag(); - - if (isset($formsArray[$flag])) { - $flagForm = $formsArray[$flag]; - - $immediateEmailChecked = in_array(User::NOTIF_FLAG_IMMEDIATE_EMAIL, $viewData[$flag] ?? [], true) - || !array_key_exists($flag, $viewData); - $dailyEmailChecked = in_array(User::NOTIF_FLAG_DAILY_DIGEST, $viewData[$flag] ?? [], true); - - if ($flagForm->has('immediate_email')) { - $flagForm->get('immediate_email')->setData($immediateEmailChecked); - } - if ($flagForm->has('daily_email')) { - $flagForm->get('daily_email')->setData($dailyEmailChecked); - } - } - } - } - - public function mapFormsToData($forms, &$viewData): void - { - $formsArray = iterator_to_array($forms); - $viewData = []; - - foreach ($this->notificationFlagProviders as $flagProvider) { - $flag = $flagProvider->getFlag(); - - if (isset($formsArray[$flag])) { - $flagForm = $formsArray[$flag]; - $viewData[$flag] = []; - - if (true === $flagForm['immediate_email']->getData()) { - $viewData[$flag][] = User::NOTIF_FLAG_IMMEDIATE_EMAIL; - } - - if (true === $flagForm['daily_email']->getData()) { - $viewData[$flag][] = User::NOTIF_FLAG_DAILY_DIGEST; - } - - if ([] === $viewData[$flag]) { - $viewData[$flag][] = User::NOTIF_FLAG_IMMEDIATE_EMAIL; - } - } - } - } -} diff --git a/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php b/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php index 4535a4815..b9e15f5d9 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php @@ -11,11 +11,9 @@ declare(strict_types=1); namespace Chill\MainBundle\Form\Type; -use Chill\MainBundle\Form\DataMapper\NotificationFlagDataMapper; use Chill\MainBundle\Notification\NotificationFlagManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -30,27 +28,24 @@ class NotificationFlagsType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder->setDataMapper(new NotificationFlagDataMapper($this->notificationFlagProviders)); - foreach ($this->notificationFlagProviders as $flagProvider) { $flag = $flagProvider->getFlag(); - $builder->add($flag, FormType::class, [ + $flagBuilder = $builder->create($flag, options: [ 'label' => $flagProvider->getLabel(), - 'required' => false, + 'compound' => true, ]); - $builder->get($flag) + $flagBuilder ->add('immediate_email', CheckboxType::class, [ 'label' => false, 'required' => false, - 'mapped' => false, ]) - ->add('daily_email', CheckboxType::class, [ + ->add('daily_digest', CheckboxType::class, [ 'label' => false, 'required' => false, - 'mapped' => false, ]) ; + $builder->add($flagBuilder); } } @@ -58,6 +53,7 @@ class NotificationFlagsType extends AbstractType { $resolver->setDefaults([ 'data_class' => null, + 'compound' => true, ]); } } diff --git a/src/Bundle/ChillMainBundle/Form/UserProfileType.php b/src/Bundle/ChillMainBundle/Form/UpdateProfileType.php similarity index 75% rename from src/Bundle/ChillMainBundle/Form/UserProfileType.php rename to src/Bundle/ChillMainBundle/Form/UpdateProfileType.php index f9fa65991..6aa8f0943 100644 --- a/src/Bundle/ChillMainBundle/Form/UserProfileType.php +++ b/src/Bundle/ChillMainBundle/Form/UpdateProfileType.php @@ -11,31 +11,29 @@ declare(strict_types=1); namespace Chill\MainBundle\Form; +use Chill\MainBundle\Action\User\UpdateProfile\UpdateProfileCommand; use Chill\MainBundle\Form\Type\ChillPhoneNumberType; use Chill\MainBundle\Form\Type\NotificationFlagsType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -class UserProfileType extends AbstractType +class UpdateProfileType extends AbstractType { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('phonenumber', ChillPhoneNumberType::class, [ 'required' => false, ]) - ->add('notificationFlags', NotificationFlagsType::class, [ - 'label' => false, - 'mapped' => false, - ]) + ->add('notificationFlags', NotificationFlagsType::class) ; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'data_class' => \Chill\MainBundle\Entity\User::class, + 'data_class' => UpdateProfileCommand::class, ]); } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig b/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig index d25f6645f..0b268af03 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig @@ -64,7 +64,7 @@ {{ form_widget(flag.immediate_email, {'label_attr': { 'class': 'checkbox-inline checkbox-switch'}}) }}