Compare commits

..

16 Commits

Author SHA1 Message Date
29536449ae Improve locale setting within user profile, using Languages symfony component 2025-11-07 14:37:46 +01:00
77311b6290 Changer order of form fields 2025-10-27 12:53:04 +01:00
52d71d48df php cs and rector fixes 2025-10-23 15:32:59 +02:00
15842d8066 Use translation keys instead of hardcoded french in templates 2025-10-23 14:13:42 +02:00
89f5b424c2 rename templates as they use |trans and are therefore not limited to language fr 2025-10-23 14:12:43 +02:00
8a55b0e8dc Implement LocaleSwitcher in commented block - ready for when we migrate to symfony 7.2 2025-10-23 14:11:25 +02:00
6d72ce6909 Implement form field for user profile to allow the update of a user's local preference 2025-10-22 16:27:16 +02:00
2a362a3340 Add locale property to User 2025-10-22 16:13:54 +02:00
bc2fbee5c6 Fix: notification edit template
form field addressesEmail removed
2025-10-06 12:14:00 +02:00
ebd10ca522 Merge branch 'fix/history-of-versions-stored-object' into 'master'
Fix the rendering of storedObject's history

See merge request Chill-Projet/chill-bundles!893
2025-10-03 20:47:06 +00:00
d3a31be412 Fix re-ordering of StoredObjectVersion in the list of versions
As some intermediate versions are remove, this may lead to situation where the indexes are not continous. In that case, the array is not a list, and is rendered as an array with numeric indexes, instead of a list of elements. The HistoryListItem component fails to render.

- Ensured proper handling of removed versions by using `array_values` to reindex items.
- Added test case to validate the result after removing a version.
- Asserted the results are a proper list in the API response.
2025-10-03 22:40:59 +02:00
d159a82f88 Update import paths in HistoryButtonListItem.vue to use aliases
- Changed types import to use `ChillDocStoreAssets/types`.
- Updated `ISOToDatetime` import to use `ChillMainAssets/chill/js/date`.
2025-10-03 22:20:51 +02:00
c2d9c73fd4 Release v4.5.1 2025-10-03 14:11:41 +02:00
0d6d15fcf7 Merge branch 'fix/conversion-exception' into 'master'
Introduce `ConversionWithSameMimeTypeException` for improved error handling in document conversion.

See merge request Chill-Projet/chill-bundles!892
2025-10-03 12:10:24 +00:00
f9ad96c78b Introduce ConversionWithSameMimeTypeException for improved error handling in document conversion.
- Added the `ConversionWithSameMimeTypeException` to handle cases where document conversion is requested for the same MIME type.
- Updated `StoredObjectToPdfConverter` to throw the new exception when encountering such cases.
- Enhanced error logging in `PostSendExternalMessageHandler` to capture these specific conversion errors.
2025-10-03 13:57:06 +02:00
fcc9529a20 Add missing javascript dependency in package.json 2025-10-03 13:56:20 +02:00
34 changed files with 397 additions and 27 deletions

View File

@@ -0,0 +1,6 @@
kind: Fixed
body: Fix the rendering of list of StoredObjectVersions, where there are kept version (before converting to pdf) and intermediate versions deleted
time: 2025-10-03T22:40:44.685474863+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -0,0 +1,6 @@
kind: Fixed
body: 'Notification: fix editing of sent notification by removing form.addressesEmails, a field that no longer exists'
time: 2025-10-06T12:13:15.45905994+02:00
custom:
Issue: "434"
SchemaChange: No schema change

4
.changes/v4.5.1.md Normal file
View File

@@ -0,0 +1,4 @@
## v4.5.1 - 2025-10-03
### Fixed
* Add missing javascript dependency
* Add exception handling for conversion of attachment on sending external, when documens are already in pdf

View File

@@ -6,6 +6,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v4.5.1 - 2025-10-03
### Fixed
* Add missing javascript dependency
* Add exception handling for conversion of attachment on sending external, when documens are already in pdf
## v4.5.0 - 2025-10-03
### Feature
* Only allow delete of attachment on workflows that are not final

View File

@@ -1,5 +1,5 @@
chill_main:
available_languages: [ '%env(resolve:LOCALE)%', 'en' ]
available_languages: [ '%env(resolve:LOCALE)%', 'en', 'nl' ]
available_countries: ['BE', 'FR']
notifications:
from_email: '%env(resolve:NOTIFICATION_FROM_EMAIL)%'

View File

@@ -55,6 +55,7 @@
"@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5",
"@types/leaflet": "^1.9.3",
"@vueuse/core": "^13.9.0",
"bootstrap-icons": "^1.11.3",
"dropzone": "^5.7.6",
"es6-promise": "^4.2.8",

View File

@@ -25,6 +25,8 @@ use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Contracts\Translation\TranslatorInterface;
// use Symfony\Component\Translation\LocaleSwitcher;
/**
* @see OnGenerationFailsTest for test suite
*/
@@ -40,6 +42,7 @@ final readonly class OnGenerationFails implements EventSubscriberInterface
private StoredObjectRepositoryInterface $storedObjectRepository,
private TranslatorInterface $translator,
private UserRepositoryInterface $userRepository,
// private LocaleSwitcher $localeSwitcher,
) {}
public static function getSubscribedEvents()
@@ -118,6 +121,25 @@ final readonly class OnGenerationFails implements EventSubscriberInterface
return;
}
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
/*
$this->localeSwitcher->runWithLocale($creator->getLocale(), function () use ($message, $errors, $template, $creator) {
$email = (new TemplatedEmail())
->to($message->getSendResultToEmail())
->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))
->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig')
->context([
'errors' => $errors,
'template' => $template,
'creator' => $creator,
'stored_object_id' => $message->getDestinationStoredObjectId(),
]);
$this->mailer->send($email);
});
*/
// Current implementation:
$email = (new TemplatedEmail())
->to($message->getSendResultToEmail())
->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))

View File

@@ -27,6 +27,8 @@ use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
// use Symfony\Component\Translation\LocaleSwitcher;
/**
* Handle the request of document generation.
*/
@@ -46,6 +48,7 @@ class RequestGenerationHandler implements MessageHandlerInterface
private readonly MailerInterface $mailer,
private readonly TranslatorInterface $translator,
private readonly StoredObjectManagerInterface $storedObjectManager,
// private readonly LocaleSwitcher $localeSwitcher,
) {}
public function __invoke(RequestGenerationMessage $message)
@@ -122,6 +125,30 @@ class RequestGenerationHandler implements MessageHandlerInterface
private function sendDataDump(StoredObject $destinationStoredObject, RequestGenerationMessage $message): void
{
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
// Note: This method sends emails to admin addresses, not user addresses, so locale switching may not be needed
/*
$this->localeSwitcher->runWithLocale('fr', function () use ($destinationStoredObject, $message) {
// Get the content of the document
$content = $this->storedObjectManager->read($destinationStoredObject);
$filename = $destinationStoredObject->getFilename();
$contentType = $destinationStoredObject->getType();
// Create the email with the document as an attachment
$email = (new TemplatedEmail())
->to($message->getSendResultToEmail())
->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
->context([
'filename' => $filename,
])
->subject($this->translator->trans('docgen.data_dump_email.subject'))
->attach($content, $filename, $contentType);
$this->mailer->send($email);
});
*/
// Current implementation:
// Get the content of the document
$content = $this->storedObjectManager->read($destinationStoredObject);
$filename = $destinationStoredObject->getFilename();

View File

@@ -59,7 +59,7 @@ final readonly class StoredObjectVersionApiController
return new JsonResponse(
$this->serializer->serialize(
new Collection($items, $paginator),
new Collection(array_values($items->toArray()), $paginator),
'json',
[AbstractNormalizer::GROUPS => ['read', StoredObjectVersionNormalizer::WITH_POINT_IN_TIMES_CONTEXT, StoredObjectVersionNormalizer::WITH_RESTORED_CONTEXT]]
),

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocStoreBundle\Exception;
class ConversionWithSameMimeTypeException extends \RuntimeException
{
public function __construct(string $mimeType, ?\Throwable $previous = null)
{
parent::__construct("Conversion to same MIME type '{$mimeType}' is not allowed: already at the same MIME type", 0, $previous);
}
}

View File

@@ -3,9 +3,9 @@ import {
StoredObject,
StoredObjectPointInTime,
StoredObjectVersionWithPointInTime,
} from "./../../../types";
} from "ChillDocStoreAssets/types";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
import { ISOToDatetime } from "ChillMainAssets/chill/js/date";
import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
import RestoreVersionButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue";
import DownloadButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DownloadButton.vue";

View File

@@ -15,6 +15,7 @@ use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTime;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTimeReasonEnum;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Exception\ConversionWithSameMimeTypeException;
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
use Chill\WopiBundle\Service\WopiConverter;
use Symfony\Component\Mime\MimeTypesInterface;
@@ -41,9 +42,10 @@ class StoredObjectToPdfConverter
*
* @return array{0: StoredObjectPointInTime, 1: StoredObjectVersion, 2?: string} contains the point in time before conversion and the new version of the stored object. The converted content is included in the response if $includeConvertedContent is true
*
* @throws \UnexpectedValueException if the preferred mime type for the conversion is not found
* @throws \RuntimeException if the conversion or storage of the new version fails
* @throws \UnexpectedValueException if the preferred mime type for the conversion is not found
* @throws \RuntimeException if the conversion or storage of the new version fails
* @throws StoredObjectManagerException
* @throws ConversionWithSameMimeTypeException if the document has already the same mime type79*
*/
public function addConvertedVersion(StoredObject $storedObject, string $lang, $convertTo = 'pdf', bool $includeConvertedContent = false): array
{
@@ -56,7 +58,7 @@ class StoredObjectToPdfConverter
$currentVersion = $storedObject->getCurrentVersion();
if ($currentVersion->getType() === $newMimeType) {
throw new \UnexpectedValueException('Already at the same mime type');
throw new ConversionWithSameMimeTypeException($newMimeType);
}
$content = $this->storedObjectManager->read($currentVersion);

View File

@@ -40,6 +40,10 @@ class StoredObjectVersionApiControllerTest extends \PHPUnit\Framework\TestCase
$storedObject->registerVersion();
}
// remove one version in the history
$v5 = $storedObject->getVersions()->get(5);
$storedObject->removeVersion($v5);
$security = $this->prophesize(Security::class);
$security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)
->willReturn(true)
@@ -53,6 +57,7 @@ class StoredObjectVersionApiControllerTest extends \PHPUnit\Framework\TestCase
self::assertEquals($response->getStatusCode(), 200);
self::assertIsArray($body);
self::assertArrayHasKey('results', $body);
self::assertIsList($body['results']);
self::assertCount(10, $body['results']);
}

View File

@@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Notification\NotificationFlagManager;
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
use libphonenumber\PhoneNumber;
use Symfony\Component\Validator\Constraints as Assert;
final class UpdateProfileCommand
{
@@ -23,11 +24,13 @@ final class UpdateProfileCommand
public function __construct(
#[PhonenumberConstraint]
public ?PhoneNumber $phonenumber,
#[Assert\Choice(choices: ['fr', 'nl'], message: 'Locale must be either "fr" or "nl"')]
public string $locale = 'fr',
) {}
public static function create(User $user, NotificationFlagManager $flagManager): self
{
$updateProfileCommand = new self($user->getPhonenumber());
$updateProfileCommand = new self($user->getPhonenumber(), $user->getLocale());
foreach ($flagManager->getAllNotificationFlagProviders() as $provider) {
$updateProfileCommand->setNotificationFlag(

View File

@@ -18,6 +18,7 @@ final readonly class UpdateProfileCommandHandler
public function updateProfile(User $user, UpdateProfileCommand $command): void
{
$user->setPhonenumber($command->phonenumber);
$user->setLocale($command->locale);
foreach ($command->notificationFlags as $flag => $values) {
$user->setNotificationImmediately($flag, $values['immediate_email']);

View File

@@ -128,6 +128,12 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
private array $notificationFlags = [];
/**
* User's preferred locale.
*/
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 5, nullable: false, options: ['default' => 'fr'])]
private string $locale = 'fr';
/**
* User constructor.
*/
@@ -716,7 +722,14 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
public function getLocale(): string
{
return 'fr';
return $this->locale;
}
public function setLocale(string $locale): self
{
$this->locale = $locale;
return $this;
}
#[Assert\Callback]

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Intl\Languages;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserLocaleType extends AbstractType
{
public function __construct(private readonly array $availableLanguages) {}
public function configureOptions(OptionsResolver $resolver): void
{
$choices = [];
foreach ($this->availableLanguages as $languageCode) {
$choices[Languages::getName($languageCode)] = $languageCode;
}
$resolver->setDefaults([
'choices' => $choices,
'placeholder' => 'user.locale.placeholder',
'required' => true,
'label' => 'user.locale.label',
'help' => 'user.locale.help',
]);
}
public function getParent(): string
{
return ChoiceType::class;
}
}

View File

@@ -14,6 +14,7 @@ namespace Chill\MainBundle\Form;
use Chill\MainBundle\Action\User\UpdateProfile\UpdateProfileCommand;
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
use Chill\MainBundle\Form\Type\NotificationFlagsType;
use Chill\MainBundle\Form\Type\UserLocaleType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -26,6 +27,7 @@ class UpdateProfileType extends AbstractType
->add('phonenumber', ChillPhoneNumberType::class, [
'required' => false,
])
->add('locale', UserLocaleType::class)
->add('notificationFlags', NotificationFlagsType::class)
;
}

View File

@@ -24,6 +24,8 @@ use Symfony\Component\Mime\Email;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
// use Symfony\Component\Translation\LocaleSwitcher;
readonly class NotificationMailer
{
public function __construct(
@@ -31,6 +33,7 @@ readonly class NotificationMailer
private LoggerInterface $logger,
private MessageBusInterface $messageBus,
private TranslatorInterface $translator,
// private LocaleSwitcher $localeSwitcher,
) {}
public function postPersistComment(NotificationComment $comment, PostPersistEventArgs $eventArgs): void
@@ -56,7 +59,7 @@ readonly class NotificationMailer
$email
->to($dest->getEmail())
->subject('Re: '.$comment->getNotification()->getTitle())
->textTemplate('@ChillMain/Notification/email_notification_comment_persist.fr.md.twig')
->textTemplate('@ChillMain/Notification/email_notification_comment_persist.md.twig')
->context([
'comment' => $comment,
'dest' => $dest,
@@ -137,13 +140,53 @@ readonly class NotificationMailer
return;
}
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
/*
$this->localeSwitcher->runWithLocale($addressee->getLocale(), function () use ($notification, $addressee) {
if ($notification->isSystem()) {
$email = new Email();
$email->text($notification->getMessage());
} else {
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->context([
'notification' => $notification,
'dest' => $addressee,
]);
}
$email
->subject($notification->getTitle())
->to($addressee->getEmail());
try {
$this->mailer->send($email);
$this->logger->info('[NotificationMailer] Email sent successfully', [
'notification_id' => $notification->getId(),
'addressee_email' => $addressee->getEmail(),
'locale' => $addressee->getLocale(),
]);
} catch (TransportExceptionInterface $e) {
$this->logger->warning('[NotificationMailer] Could not send an email notification', [
'to' => $addressee->getEmail(),
'notification_id' => $notification->getId(),
'error_message' => $e->getMessage(),
'error_trace' => $e->getTraceAsString(),
]);
throw $e;
}
});
*/
// Current implementation:
if ($notification->isSystem()) {
$email = new Email();
$email->text($notification->getMessage());
} else {
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.fr.md.twig')
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->context([
'notification' => $notification,
'dest' => $addressee,
@@ -182,9 +225,43 @@ readonly class NotificationMailer
return;
}
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
/*
$this->localeSwitcher->runWithLocale($user->getLocale(), function () use ($user, $notifications) {
$email = new TemplatedEmail();
$email
->htmlTemplate('@ChillMain/Notification/email_daily_digest.md.twig')
->context([
'user' => $user,
'notifications' => $notifications,
'notification_count' => count($notifications),
])
->subject($this->translator->trans('notification.Daily Notification Digest'))
->to($user->getEmail());
try {
$this->mailer->send($email);
$this->logger->info('[NotificationMailer] Daily digest email sent successfully', [
'user_email' => $user->getEmail(),
'notification_count' => count($notifications),
'locale' => $user->getLocale(),
]);
} catch (TransportExceptionInterface $e) {
$this->logger->warning('[NotificationMailer] Could not send daily digest email', [
'to' => $user->getEmail(),
'notification_count' => count($notifications),
'error_message' => $e->getMessage(),
'error_trace' => $e->getTraceAsString(),
]);
throw $e;
}
});
*/
// Current implementation:
$email = new TemplatedEmail();
$email
->htmlTemplate('@ChillMain/Notification/email_daily_digest.fr.md.twig')
->htmlTemplate('@ChillMain/Notification/email_daily_digest.md.twig')
->context([
'user' => $user,
'notifications' => $notifications,
@@ -222,7 +299,7 @@ readonly class NotificationMailer
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content_to_email.fr.md.twig')
->textTemplate('@ChillMain/Notification/email_non_system_notification_content_to_email.md.twig')
->context([
'notification' => $notification,
'dest' => $emailAddress,

View File

@@ -21,8 +21,6 @@
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
{{ form_row(form.addressesEmails) }}
{% include handler.template(notification) with handler.templateData(notification) %}
<div class="mb-3 row">

View File

@@ -14,7 +14,7 @@
Vous pouvez visualiser la notification et y répondre ici:
{{ absolute_url(path('chill_main_notification_show', {'_locale': 'fr', 'id': notification.id }, false)) }}
{{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': notification.id }, false)) }}
--
Le logiciel Chill

View File

@@ -13,7 +13,7 @@ Commentaire:
Vous pouvez visualiser la notification et y répondre ici:
{{ absolute_url(path('chill_main_notification_show', {'_locale': 'fr', 'id': comment.notification.id }, false)) }}
{{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': comment.notification.id }, false)) }}
--
Le logiciel Chill

View File

@@ -44,6 +44,7 @@
<div>
{{ form_start(form) }}
{{ form_row(form.phonenumber) }}
{{ form_row(form.locale) }}
<h2 class="mb-4">{{ 'user.profile.notification_preferences'|trans }}</h2>
<table class="table table-striped align-middle">

View File

@@ -1,16 +1,16 @@
{{ dest.label }},
Un suivi "{{ workflow.text }}" a atteint une nouvelle étape: {{ workflow.text }}
{{ 'workflow.notification.content.new_step_reached'|trans({'%workflow%': workflow.text}) }}
Titre du workflow: "{{ title }}".
{{ 'workflow.notification.content.workflow_title'|trans({'%title%': title}) }}
{% if is_dest %}
Vous êtes invités à valider cette étape au plus tôt.
{{ 'workflow.notification.content.validation_needed'|trans }}
{% endif %}
Vous pouvez visualiser le workflow sur cette page:
{{ 'workflow.notification.content.view_workflow'|trans }}
{{ absolute_url(path('chill_main_workflow_show', {'id': entity_workflow.id, '_locale': 'fr'})) }}
{{ absolute_url(path('chill_main_workflow_show', {'id': entity_workflow.id, '_locale': dest.locale|default('fr')})) }}
Cordialement,
{{ 'workflow.notification.content.regards'|trans }}

View File

@@ -1,5 +1,5 @@
{%- if is_dest -%}
Un suivi {{ workflow.text }} demande votre attention: {{ title }}
{{ 'workflow.notification.title.attention_needed'|trans({'%workflow%': workflow.text, '%title%': title}) }}
{%- else -%}
Un suivi {{ workflow.text }} a atteint une nouvelle étape: {{ place.text }}: {{ title }}
{{ 'workflow.notification.title.new_step'|trans({'%workflow%': workflow.text, '%place%': place.text, '%title%': title}) }}
{%- endif -%}

View File

@@ -16,11 +16,13 @@ use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
// use Symfony\Component\Translation\LocaleSwitcher;
class RecoverPasswordHelper
{
final public const RECOVER_PASSWORD_ROUTE = 'password_recover';
public function __construct(private readonly TokenManager $tokenManager, private readonly UrlGeneratorInterface $urlGenerator, private readonly MailerInterface $mailer) {}
public function __construct(private readonly TokenManager $tokenManager, private readonly UrlGeneratorInterface $urlGenerator, private readonly MailerInterface $mailer/* , private readonly LocaleSwitcher $localeSwitcher */) {}
/**
* @param bool $absolute
@@ -53,6 +55,24 @@ class RecoverPasswordHelper
throw new \UnexpectedValueException('No emaail associated to the user');
}
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
/*
$this->localeSwitcher->runWithLocale($user->getLocale(), function () use ($user, $expiration, $template, $templateParameters, $emailSubject, $additionalUrlParameters) {
$email = (new TemplatedEmail())
->subject($emailSubject)
->to($user->getEmail())
->textTemplate($template)
->context([
'user' => $user,
'url' => $this->generateUrl($user, $expiration, true, $additionalUrlParameters),
...$templateParameters,
]);
$this->mailer->send($email);
});
*/
// Current implementation:
$email = (new TemplatedEmail())
->subject($emailSubject)
->to($user->getEmail())

View File

@@ -22,6 +22,8 @@ use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\Workflow\Registry;
// use Symfony\Component\Translation\LocaleSwitcher;
final readonly class NotificationToUserGroupsOnTransition implements EventSubscriberInterface
{
public function __construct(
@@ -31,6 +33,7 @@ final readonly class NotificationToUserGroupsOnTransition implements EventSubscr
private MailerInterface $mailer,
private EntityManagerInterface $entityManager,
private EntityWorkflowManager $entityWorkflowManager,
// private LocaleSwitcher $localeSwitcher,
) {}
public static function getSubscribedEvents(): array
@@ -87,6 +90,24 @@ final readonly class NotificationToUserGroupsOnTransition implements EventSubscr
'title' => $title,
];
// Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
// Note: This sends emails to user groups, not individual users, so locale switching may use default locale
/*
$this->localeSwitcher->runWithLocale('fr', function () use ($context, $userGroup) {
$email = new TemplatedEmail();
$email
->htmlTemplate('@ChillMain/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig')
->context($context)
->subject(
$this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig', $context)
)
->to($userGroup->getEmail());
$this->mailer->send($email);
});
*/
// Current implementation:
$email = new TemplatedEmail();
$email
->htmlTemplate('@ChillMain/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig')

View File

@@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\Workflow\Messenger;
use Chill\DocStoreBundle\Exception\ConversionWithSameMimeTypeException;
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
use Chill\DocStoreBundle\Service\StoredObjectToPdfConverter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
@@ -55,6 +56,8 @@ final readonly class PostSendExternalMessageHandler implements MessageHandlerInt
$this->storedObjectToPdfConverter->addConvertedVersion($attachment->getProxyStoredObject(), $locale);
} catch (StoredObjectManagerException $e) {
$this->logger->error('Error converting attachment to PDF', ['backtrace' => $e->getTraceAsString(), 'attachment_id' => $attachment->getId()]);
} catch (ConversionWithSameMimeTypeException $e) {
$this->logger->error('Error converting attachment to PDF: already at the same MIME type', ['backtrace' => $e->getTraceAsString(), 'attachment_id' => $attachment->getId()]);
}
}
@@ -65,6 +68,8 @@ final readonly class PostSendExternalMessageHandler implements MessageHandlerInt
$this->storedObjectToPdfConverter->addConvertedVersion($storedObject, $locale);
} catch (StoredObjectManagerException $e) {
$this->logger->error('Error converting stored object to PDF', ['backtrace' => $e->getTraceAsString(), 'stored_object_id' => $storedObject->getId(), 'workflow_id' => $entityWorkflow->getId()]);
} catch (ConversionWithSameMimeTypeException $e) {
$this->logger->error('Error converting stored object to PDF: already at the same MIME type', ['backtrace' => $e->getTraceAsString(), 'stored_object_id' => $storedObject->getId(), 'workflow_id' => $entityWorkflow->getId()]);
}
}
}

View File

@@ -12,6 +12,12 @@ services:
tags:
- { name: form.type, alias: translatable_string }
Chill\MainBundle\Form\Type\UserLocaleType:
arguments:
- "%chill_main.available_languages%"
tags:
- { name: form.type }
chill.main.form.type.select2choice:
class: Chill\MainBundle\Form\Type\Select2ChoiceType
tags:

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20251022140718 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add locale field to users table for user language preferences';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE users ADD locale VARCHAR(5) DEFAULT \'fr\' NOT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE users DROP locale');
}
}

View File

@@ -56,6 +56,10 @@ user:
no job: Pas de métier assigné
no scope: Pas de cercle assigné
notification_preferences: Préférences pour mes notifications
locale:
label: Langue de communication
help: Langue utilisée pour les notifications par email et autres communications.
placeholder: Choisissez une langue
user_group:
inactive: Inactif
@@ -668,6 +672,17 @@ workflow:
reject_are_you_sure: Êtes-vous sûr de vouloir rejeter la signature de %signer%
waiting_for: En attente de modification de l'état de la signature
notification:
title:
attention_needed: "Attention requise dans le workflow %workflow% pour %title%"
new_step: "Nouvelle étape dans le workflow %workflow% (%place%) pour %title%"
content:
new_step_reached: "Une nouvelle étape a été atteinte dans le workflow %workflow%."
workflow_title: "Titre du workflow : %title%"
validation_needed: "Votre validation est nécessaire pour cette étape."
view_workflow: "Vous pouvez consulter le workflow ici :"
regards: "Cordialement,"
attachments:
title: Pièces jointes
@@ -745,7 +760,22 @@ notification:
greeting: "Bonjour %user%"
intro: "Vous avez reçu %notification_count% nouvelle(s) notification(s)."
view_notification: "Vous pouvez visualiser la notification et y répondre ici:"
signature: "Le logiciel Chill"
signature: "L'équipe Chill"
daily_notifications: "{1}Vous avez 1 nouvelle notification.|]1,Inf[Vous avez %notification_count% nouvelles notifications."
docgen:
failure_email:
"The generation of a document failed": "La génération d'un document a échoué"
"The generation of the document %template_name% failed": "La génération du document %template_name% a échoué"
"Forward this email to your administrator for solving": "Transmettez cet email à votre administrateur pour résolution"
"References": "Références"
"The following errors were encoutered": "Les erreurs suivantes ont été rencontrées"
data_dump_email:
subject: "Export de données disponible"
"Dear": "Cher utilisateur,"
"data_dump_ready_and_attached": "Votre export de données est prêt et joint à cet email."
"filename": "Nom du fichier : %filename%"
CHILL_MAIN_COMPOSE_EXPORT: Exécuter des exports et les sauvegarder
CHILL_MAIN_GENERATE_SAVED_EXPORT: Exécuter et modifier des exports préalablement sauvegardés

View File

@@ -46,6 +46,14 @@ No title: Geen titel
User profile: Mijn gebruikersprofiel
Phonenumber successfully updated!: Telefoonnummer bijgewerkt!
user:
locale:
label: Communicatietaal
help: Taal gebruikt voor e-mailmeldingen en andere communicatie.
placeholder: Kies een taal
choice:
french: Français
dutch: Nederlands
Edit: Bewerken
Update: Updaten
@@ -423,6 +431,17 @@ workflow:
For: Pour
Cc: Cc
notification:
title:
attention_needed: "Aandacht vereist in workflow %workflow% voor %title%"
new_step: "Nieuwe stap in workflow %workflow% (%place%) voor %title%"
content:
new_step_reached: "Een nieuwe stap is bereikt in workflow %workflow%."
workflow_title: "Workflow titel: %title%"
validation_needed: "Uw validatie is nodig voor deze stap."
view_workflow: "U kunt de workflow hier bekijken:"
regards: "Met vriendelijke groeten,"
Subscribe final: Recevoir une notification à l'étape finale
Subscribe all steps: Recevoir une notification à chaque étape