Compare commits

..

25 Commits

Author SHA1 Message Date
966f9f7e33 Release v4.13.0 2026-02-23 17:13:24 +01:00
7a5300b713 Merge branch '495-fix-quote-notification-email' into 'master'
Remove unused method `sendNotificationEmailsToAddressesEmails` from `NotificationMailer`

Closes #495

See merge request Chill-Projet/chill-bundles!967
2026-02-23 15:49:39 +00:00
dc3a585e5b Remove unused method sendNotificationEmailsToAddressesEmails from NotificationMailer 2026-02-23 15:49:39 +00:00
7712d76889 Merge branch '494-titre-toute-la-journée-tronqué-sur-la-page-mes-rendez-vous' into 'master'
Resolve "Titre 'Toute la journée' tronqué sur la page Mes Rendez-vous"

Closes #494

See merge request Chill-Projet/chill-bundles!965
2026-02-23 15:08:48 +00:00
Boris Waaub
69bb7026c9 Resolve "Titre 'Toute la journée' tronqué sur la page Mes Rendez-vous" 2026-02-23 15:08:48 +00:00
acd7240903 Merge branch '501-fix-deprecation-markdown-parser' into 'master'
Resolve "Depréciation dans le paquet de transformation markdown"

Closes #501

See merge request Chill-Projet/chill-bundles!966
2026-02-23 14:50:15 +00:00
22049558da Resolve "Depréciation dans le paquet de transformation markdown" 2026-02-23 14:50:14 +00:00
c0f2f3f3e0 Merge branch '500-limit-public-download' into 'master'
Resolve "Téléchargement des documents d'un workflow: limiter à 30 téléchargements plutôt que 100"

Closes #500

See merge request Chill-Projet/chill-bundles!964
2026-02-23 14:24:53 +00:00
bf56b3cc65 Resolve "Téléchargement des documents d'un workflow: limiter à 30 téléchargements plutôt que 100" 2026-02-23 14:24:53 +00:00
f85973f7ae Merge branch '499-fix-loading-postal-code' into 'master'
Resolve "Des codes postaux marqués comme supprimés apparaissent toujours dans la recherche d'adresse"

Closes #499

See merge request Chill-Projet/chill-bundles!963
2026-02-23 14:16:46 +00:00
f1446d7abe Resolve "Des codes postaux marqués comme supprimés apparaissent toujours dans la recherche d'adresse" 2026-02-23 14:16:45 +00:00
76d675ac02 Fixed translations of address in exports (addresse -> adresse) 2026-02-17 14:11:15 +01:00
cf0a2b7393 Merge branch '438-parcours-designer-comme-adresse-du-parcours-to-be-green' into 'master'
Resolve "Parcours - "Désigner comme adresse du parcours" to be green"

Closes #438

See merge request Chill-Projet/chill-bundles!958
2026-02-12 08:50:00 +00:00
Boris Waaub
80b05a8133 Resolve "Parcours - "Désigner comme adresse du parcours" to be green" 2026-02-12 08:50:00 +00:00
69aba8d9c9 Merge branch 'changie/add-mr-to-question' into 'master'
Changie/add mr to question

See merge request Chill-Projet/chill-bundles!960
2026-02-11 13:27:48 +00:00
a87d936828 Changie/add mr to question 2026-02-11 13:27:48 +00:00
290fa7a77c Merge branch '498-fix-workflow-initiator' into 'master'
Take workflow creator into account when granting edit permissions on documents

Closes #498

See merge request Chill-Projet/chill-bundles!959
2026-02-10 15:05:50 +00:00
0e1d233d79 Take workflow creator into account when granting edit permissions on documents 2026-02-10 15:05:49 +00:00
3402e4863f Release v4.12.1 2026-02-01 18:52:21 +01:00
1f0974ea68 Merge branch 'cs/update-cs-fixer-3.93' into 'master'
Update PHP-CS-Fixer to version 3.93.0 in composer dependencies

See merge request Chill-Projet/chill-bundles!955
2026-01-29 12:27:56 +00:00
9997fb287a Update PHP-CS-Fixer to version 3.93.0 in composer dependencies 2026-01-29 12:27:56 +00:00
f9a9de1148 Merge branch '496-allow-remove-double-refid-ban-address-importer' into 'master'
Adding the option to deal with duplicate addresses in the BAN importer

Closes #496

See merge request Chill-Projet/chill-bundles!954
2026-01-27 10:26:57 +00:00
juminet
c34f720f94 Adding the option to deal with duplicate addresses in the BAN importer 2026-01-27 10:26:57 +00:00
e1b1f592fa Merge branch 'zimbra/use-delegated-admin' into 'master'
[Zimbra] Use admin delegated account for authenticating users against Zimbra

See merge request Chill-Projet/chill-bundles!952
2026-01-22 14:39:46 +00:00
8546f4dadc [Zimbra] Use admin delegated account for authenticating users against Zimbra 2026-01-22 14:39:46 +00:00
40 changed files with 267 additions and 127 deletions

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

@@ -0,0 +1,4 @@
## v4.12.1 - 2026-02-01
### Fixed
* ([#496](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/496)) Add the option to deal with duplicate address in BAN adress importer

15
.changes/v4.13.0.md Normal file
View File

@@ -0,0 +1,15 @@
## v4.13.0 - 2026-02-23
### Feature
* ([#500](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/500)) ([!964](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/964)) Limit the number of public download of stored object to 30 downloads
* ([#495](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/495)) ([!967](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/967)) Send email related to notification in both html and txt format, and render quote correctly
### Fixed
* ([#438](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/438)) Change wrong color of submit button "Désigner comme adresse du parcours"
* ([#498](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/498)) For giving edit permissions on documents, take into account the workflow creator
* Fixed mispelling of address in translations: addresse -> adresse
* ([#499](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/499)) ([!963](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/963)) Fix: some postal code appears in the UI, although they are marked as deleted
* ([#501](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/501)) ([!966](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/966)) Fix deprecation in the markdown rendering
* ([#494](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/494)) ([!965](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/965)) Remove unused all-day slot display
### DX
* ([!960](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/960)) Configure changie to ask for merge request number for a better tracking of changes

View File

@@ -7,7 +7,7 @@ versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
kindFormat: '### {{.Kind}}'
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
changeFormat: >-
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{ .Body }} {{ if and .Custom.SchemaChange (ne .Custom.SchemaChange "No schema change") }}
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{ if not (eq .Custom.MR "") }}([!{{ .Custom.MR }}](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/{{ .Custom.MR }})) {{ end }}{{ .Body }} {{ if and .Custom.SchemaChange (ne .Custom.SchemaChange "No schema change") }}
**Schema Change**: {{ .Custom.SchemaChange }}
{{- end -}}
@@ -30,6 +30,12 @@ custom:
type: int
minInt: 1
- key: MR
label: Merge request number (on chill-bundles repository) (optional)
optional: true
type: int
minInt: 1
body:
# allow multiline messages
block: true

View File

@@ -6,6 +6,27 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v4.13.0 - 2026-02-23
### Feature
* ([#500](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/500)) ([!964](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/964)) Limit the number of public download of stored object to 30 downloads
* ([#495](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/495)) ([!967](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/967)) Send email related to notification in both html and txt format, and render quote correctly
### Fixed
* ([#438](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/438)) Change wrong color of submit button "Désigner comme adresse du parcours"
* ([#498](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/498)) For giving edit permissions on documents, take into account the workflow creator
* Fixed mispelling of address in translations: addresse -> adresse
* ([#499](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/499)) ([!963](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/963)) Fix: some postal code appears in the UI, although they are marked as deleted
* ([#501](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/501)) ([!966](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/966)) Fix deprecation in the markdown rendering
* ([#494](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/494)) ([!965](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/965)) Remove unused all-day slot display
### DX
* ([!960](https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/960)) Configure changie to ask for merge request number for a better tracking of changes
## v4.12.1 - 2026-02-01
### Fixed
* ([#496](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/496)) Add the option to deal with duplicate address in BAN adress importer
## v4.12.0 - 2026-01-15
### Feature
* ([#473](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/473)) Display version of chill bundles in application footer
@@ -21,6 +42,8 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
* ([#492](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/492)) fix CommentInput: replace deprecated value binding with model-value
* ([#493](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/493)) fix issue with stored object permissions associated with workflows (as attachment, or through a related entity)
BC: the constructor's signature of `\Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter` has changed.
## v4.11.0 - 2025-12-17
### Feature
* ([#478](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/478)) Add filtering to admin lists: social actions, social issues, goals, results, and evaluations

View File

@@ -98,7 +98,7 @@
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13",
"friendsofphp/php-cs-fixer": "3.92.5",
"friendsofphp/php-cs-fixer": "3.93.0",
"jangregor/phpstan-prophecy": "^1.0",
"nelmio/alice": "^3.8",
"nikic/php-parser": "^4.15",

View File

@@ -0,0 +1,3 @@
kind: Added
body: Use admin delegated account for handling authentication
time: 2026-01-22T15:32:23.932994899+01:00

View File

@@ -80,12 +80,19 @@ final readonly class CreateZimbraComponent
$location = $calendar->getCalendar()->getLocation();
$hasLocation = $calendar->getCalendar()->hasLocation();
$isPrivate = $calendar->getCalendar()->getAccompanyingPeriod()?->isConfidential() ?? false;
} else {
} elseif ($calendar instanceof Calendar) {
$startDate = $calendar->getStartDate();
$endDate = $calendar->getEndDate();
$location = $calendar->getLocation();
$hasLocation = $calendar->hasLocation();
$isPrivate = $calendar->getAccompanyingPeriod()?->isConfidential() ?? false;
} else {
// Calendar range case
$startDate = $calendar->getStartDate();
$endDate = $calendar->getEndDate();
$location = $calendar->getLocation();
$hasLocation = $calendar->hasLocation();
$isPrivate = false;
}
$comp = new InviteComponent();

View File

@@ -11,48 +11,84 @@ declare(strict_types=1);
namespace Chill\ZimbraBundle\Calendar\Connector\ZimbraConnector;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Zimbra\Admin\AdminApi;
use Zimbra\Common\Enum\AccountBy;
use Zimbra\Common\Soap\ClientFactory;
use Zimbra\Common\Struct\AccountSelector;
use Zimbra\Common\Struct\Header\AccountInfo;
use Zimbra\Mail\MailApi;
final readonly class SoapClientBuilder
final class SoapClientBuilder
{
private string $username;
private readonly string $username;
private string $password;
private readonly string $password;
private string $url;
private readonly string $url;
public function __construct(private ParameterBagInterface $parameterBag, private HttpClientInterface $client)
{
private readonly string $adminUrl;
private readonly bool $verifyHost;
private readonly bool $verifyPeer;
private readonly bool $adminVerifyHost;
private readonly bool $adminVerifyPeer;
/**
* Keep the cache of the tokens.
*
* @var array<string, array{token: string, expirationTime: \DateTimeImmutable}>
*/
private array $tokenCache = [];
public function __construct(
private readonly ParameterBagInterface $parameterBag,
private readonly HttpClientInterface $client,
private readonly ClockInterface $clock,
) {
$dsn = $this->parameterBag->get('chill_calendar.remote_calendar_dsn');
$url = parse_url($dsn);
$this->username = urldecode($url['user']);
$this->password = urldecode($url['pass']);
if ('zimbra+http' === $url['scheme']) {
$scheme = 'http://';
$scheme = 'http';
$port = $url['port'] ?? 80;
} elseif ('zimbra+https' === $url['scheme']) {
$scheme = 'https://';
$scheme = 'https';
$port = $url['port'] ?? 443;
} else {
throw new \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException('Unsupported remote calendar scheme: '.$url['scheme']);
}
$this->url = $scheme.$url['host'].':'.$port;
// get attributes for adminUrl
$query = [];
parse_str($url['query'] ?? '', $query);
$adminPort = $query['adminPort'] ?? '7071';
$adminHost = $query['adminHost'] ?? $url['host'];
$adminScheme = $query['adminScheme'] ?? $scheme;
$this->verifyPeer = (bool) ($query['verifyPeer'] ?? true);
$this->verifyHost = (bool) ($query['verifyHost'] ?? true);
$this->adminVerifyHost = (bool) ($query['adminVerifyHost'] ?? $this->verifyPeer);
$this->adminVerifyPeer = (bool) ($query['adminVerifyPeer'] ?? $this->verifyHost);
$this->url = $scheme.'://'.$url['host'].':'.$port;
$this->adminUrl = $adminScheme.'://'.$adminHost.':'.$adminPort;
}
private function buildApi(): MailApi
{
$baseClient = $this->client->withOptions([
'base_uri' => $location = $this->url.'/service/soap',
'verify_host' => false,
'verify_peer' => false,
'verify_host' => $this->verifyHost,
'verify_peer' => $this->verifyPeer,
]);
$psr18Client = new Psr18Client($baseClient);
$api = new MailApi();
@@ -62,12 +98,36 @@ final readonly class SoapClientBuilder
return $api;
}
private function buildAdminApi(): AdminApi
{
$baseClient = $this->client->withOptions([
'base_uri' => $location = $this->adminUrl.'/service/admin/soap',
'verify_host' => $this->adminVerifyHost,
'verify_peer' => $this->adminVerifyPeer,
]);
$psr18Client = new Psr18Client($baseClient);
$api = new AdminApi();
$client = ClientFactory::create($location, $psr18Client);
$api->setClient($client);
return $api;
}
public function getApiForAccount(string $accountName): MailApi
{
$api = $this->buildApi();
$response = $api->authByAccountName($this->username, $this->password);
['token' => $token, 'expirationTime' => $expirationTime] = $this->tokenCache[$accountName]
?? ['token' => null, 'expirationTime' => null];
$token = $response->getAuthToken();
if (null === $token || null === $expirationTime || $expirationTime <= $this->clock->now()) {
$adminApi = $this->buildAdminApi();
$adminApi->auth($this->username, $this->password);
$delegateResponse = $adminApi->delegateAuth(new AccountSelector(AccountBy::NAME, $accountName));
$token = $delegateResponse->getAuthToken();
$expiration = $delegateResponse->getLifetime();
$expirationTime = $this->clock->now()->add(new \DateInterval('PT'.$expiration.'S'));
$this->tokenCache[$accountName] = ['token' => $token, 'expirationTime' => $expirationTime];
}
$apiBy = $this->buildApi();
$apiBy->setAuthToken($token);

View File

@@ -69,7 +69,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
}
/** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
* @see PrependExtensionInterface::prepend()
*/
public function prependRoutes(ContainerBuilder $container)
{

View File

@@ -56,7 +56,7 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
}
/** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
* @see PrependExtensionInterface::prepend()
*/
public function prependRoutes(ContainerBuilder $container)
{

View File

@@ -346,6 +346,7 @@ const baseOptions = ref<CalendarOptions>({
center: "title",
right: "timeGridWeek,timeGridDay",
},
allDaySlot: false,
});
const ranges = computed<EventInput[]>(() => {

View File

@@ -52,7 +52,7 @@ class ChillCustomFieldsExtension extends Extension implements PrependExtensionIn
}
/** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
* @see PrependExtensionInterface::prepend()
*/
public function prepend(ContainerBuilder $container)
{

View File

@@ -25,7 +25,7 @@ class ChoiceWithOtherType extends AbstractType
private string $otherValueLabel = 'Other value';
/** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::buildForm()
* @see AbstractType::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
@@ -42,7 +42,7 @@ class ChoiceWithOtherType extends AbstractType
}
/** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::configureOptions()
* @see AbstractType::configureOptions()
*/
public function configureOptions(OptionsResolver $resolver)
{

View File

@@ -22,7 +22,7 @@ use Symfony\Component\Form\FormEvents;
class ChoicesListType extends AbstractType
{
/** (non-PHPdoc).
* @see \Symfony\Component\Form\AbstractType::buildForm()
* @see AbstractType::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{

View File

@@ -82,7 +82,7 @@ class CustomFieldProvider implements ContainerAwareInterface
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\DependencyInjection\ContainerAwareInterface::setContainer()
* @see ContainerAwareInterface::setContainer()
*/
public function setContainer(?ContainerInterface $container = null)
{

View File

@@ -52,7 +52,7 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
}
/** (non-PHPdoc).
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
* @see PrependExtensionInterface::prepend()
*/
public function prepend(ContainerBuilder $container): void
{

View File

@@ -31,7 +31,8 @@ class LoadAddressesFRFromBANCommand extends Command
{
$this->setName('chill:main:address-ref-from-ban')
->addArgument('departementNo', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'a list of departement numbers')
->addOption('send-report-email', 's', InputOption::VALUE_REQUIRED, 'Email address where a list of unimported addresses can be send');
->addOption('send-report-email', 's', InputOption::VALUE_REQUIRED, 'Email address where a list of unimported addresses can be send')
->addOption('allow-remove-double-refid', 'd', InputOption::VALUE_NONE, 'Should the address importer be allowed to remove same refid in the source data, if any');
}
protected function execute(InputInterface $input, OutputInterface $output): int
@@ -40,7 +41,7 @@ class LoadAddressesFRFromBANCommand extends Command
foreach ($input->getArgument('departementNo') as $departementNo) {
$output->writeln('Import addresses for '.$departementNo);
$this->addressReferenceFromBAN->import($departementNo, $input->hasOption('send-report-email') ? $input->getOption('send-report-email') : null);
$this->addressReferenceFromBAN->import($departementNo, $input->hasOption('send-report-email') ? $input->getOption('send-report-email') : null, allowRemoveDoubleRefId: $input->hasOption('allow-remove-double-refid') ? $input->getOption('allow-remove-double-refid') : false);
}
return Command::SUCCESS;

View File

@@ -48,7 +48,7 @@ class LoadAndUpdateLanguagesCommand extends Command
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\Console\Command\Command::configure()
* @see Command::configure()
*/
protected function configure()
{
@@ -73,7 +73,7 @@ class LoadAndUpdateLanguagesCommand extends Command
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\Console\Command\Command::execute()
* @see Command::execute()
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{

View File

@@ -51,7 +51,7 @@ class LoadCountriesCommand extends Command
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\Console\Command\Command::configure()
* @see Command::configure()
*/
protected function configure()
{
@@ -61,7 +61,7 @@ class LoadCountriesCommand extends Command
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\Console\Command\Command::execute()
* @see Command::execute()
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{

View File

@@ -79,5 +79,7 @@ final class PostalCodeAPIController extends ApiController
$qb->andWhere('e.origin = :zero')
->setParameter('zero', 0);
$qb->andWhere('e.deletedAt IS NULL');
}
}

View File

@@ -62,15 +62,15 @@ final readonly class WorkflowViewSendPublicController
);
}
if (100 < $workflowSend->getViews()->count()) {
$this->chillLogger->info(self::LOG_PREFIX.'100 view reached, not allowed to see it again');
throw new AccessDeniedHttpException('100 views reached, not allowed to see it again');
if (30 < $workflowSend->getViews()->count()) {
$this->chillLogger->info(self::LOG_PREFIX.'30 view reached, not allowed to see it again');
throw new AccessDeniedHttpException('30 views reached, not allowed to see it again');
}
try {
$metadata = new EntityWorkflowViewMetadataDTO(
$workflowSend->getViews()->count(),
100 - $workflowSend->getViews()->count(),
30 - $workflowSend->getViews()->count(),
);
$response = new Response(
$this->entityWorkflowManager->renderPublicView($workflowSend, $metadata),

View File

@@ -20,7 +20,7 @@ class SearchableServicesCompilerPass implements CompilerPassInterface
/**
* (non-PHPdoc).
*
* @see \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process()
* @see CompilerPassInterface::process()
*/
public function process(ContainerBuilder $container)
{

View File

@@ -32,7 +32,7 @@ abstract class AbstractWidgetFactory implements WidgetFactoryInterface
* Will create the definition by returning the definition from the `services.yml`
* file (or `services.xml` or `what-you-want.yml`).
*
* @see \Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface::createDefinition()
* @see WidgetFactoryInterface::createDefinition()
*/
public function createDefinition(ContainerBuilder $containerBuilder, $place, $order, array $config)
{

View File

@@ -394,6 +394,10 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
public function isUserInvolved(User $user): bool
{
if ($this->getCreatedBy() === $user) {
return true;
}
foreach ($this->getSteps() as $step) {
if ($step->getAllDestUser()->contains($user)) {
return true;

View File

@@ -59,7 +59,8 @@ readonly class NotificationMailer
$email
->to($dest->getEmail())
->subject('Re: '.$comment->getNotification()->getTitle())
->textTemplate('@ChillMain/Notification/email_notification_comment_persist.md.twig')
->textTemplate('@ChillMain/Notification/email_notification_comment_persist.txt.twig')
->htmlTemplate('@ChillMain/Notification/email_notification_comment_persist.md.twig')
->context([
'comment' => $comment,
'dest' => $dest,
@@ -83,7 +84,6 @@ readonly class NotificationMailer
public function postPersistNotification(Notification $notification, PostPersistEventArgs $eventArgs): void
{
$this->sendNotificationEmailsToAddressees($notification);
$this->sendNotificationEmailsToAddressesEmails($notification);
}
private function sendNotificationEmailsToAddressees(Notification $notification): void
@@ -149,7 +149,8 @@ readonly class NotificationMailer
} else {
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.txt.twig')
->htmlTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->context([
'notification' => $notification,
'dest' => $addressee,
@@ -186,7 +187,8 @@ readonly class NotificationMailer
} else {
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->textTemplate('@ChillMain/Notification/email_non_system_notification_content.txt.twig')
->htmlTemplate('@ChillMain/Notification/email_non_system_notification_content.md.twig')
->context([
'notification' => $notification,
'dest' => $addressee,
@@ -286,38 +288,4 @@ readonly class NotificationMailer
throw $e;
}
}
private function sendNotificationEmailsToAddressesEmails(Notification $notification): void
{
foreach ($notification->getAddresseeUserGroups() as $userGroup) {
if (!$userGroup->hasEmail()) {
continue;
}
$emailAddress = $userGroup->getEmail();
$email = new TemplatedEmail();
$email
->textTemplate('@ChillMain/Notification/email_non_system_notification_content_to_email.md.twig')
->context([
'notification' => $notification,
'dest' => $emailAddress,
]);
$email
->subject($notification->getTitle())
->to($emailAddress);
try {
$this->mailer->send($email);
} catch (TransportExceptionInterface $e) {
$this->logger->warning('[NotificationMailer] could not send an email notification', [
'to' => $emailAddress,
'error_message' => $e->getMessage(),
'error_trace' => $e->getTraceAsString(),
]);
}
}
}
}

View File

@@ -100,7 +100,9 @@ final readonly class PostalCodeRepository implements PostalCodeRepositoryInterfa
$query
->setFromClause('chill_main_postal_code cmpc')
->andWhereClause('cmpc.origin = 0');
->andWhereClause('cmpc.origin = 0')
->andWhereClause('cmpc.deletedAt IS NULL')
;
if (null !== $country) {
$query->andWhereClause('cmpc.country_id = ?', [$country->getId()]);

View File

@@ -1,20 +1,17 @@
{% apply markdown_to_html %}
{{ dest.label }},
{{ notification.sender.label }} a créé une notification pour vous:
> {{ notification.title }}
>
>
{%- for line in notification.message|split("\n") %}
**Titre de la notification**: {{ notification.title }}
{% for line in notification.message|split("\n") %}
> {{ line }}
{%- if not loop.last %}
>
{%- endif %}
{%- endfor %}
{% endfor %}
Vous pouvez visualiser la notification et y répondre ici:
[Vous pouvez visualiser la notification et y répondre ici.]({{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': notification.id }, false)) }})
{{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': notification.id }, false)) }}
-----
--
Le logiciel Chill
{% endapply %}

View File

@@ -0,0 +1,14 @@
{{ dest.label }},
{{ notification.sender.label }} a créé une notification pour vous:
Titre de la notification: {{ notification.title }}
{% for line in notification.message|split("\n") %}
> {{ line|raw }}
{% endfor %}
Vous pouvez visualiser la notification et y répondre ici: {{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': notification.id }, false)) }}.
--
Le logiciel Chill

View File

@@ -1,20 +0,0 @@
{{ dest }},
{{ notification.sender.label }} a créé une notification pour vous:
> {{ notification.title }}
>
>
{%- for line in notification.message|split("\n") %}
> {{ line }}
{%- if not loop.last %}
>
{%- endif %}
{%- endfor %}
Vous pouvez cliquer sur ce lien pour obtenir un accès permanent à la notification:
{{ absolute_url(path('chill_main_notification_grant_access_by_access_key', {'_locale': 'fr', 'id': notification.id, 'accessKey': notification.accessKey, 'email': dest})) }}
--
Le logiciel Chill

View File

@@ -1,3 +1,4 @@
{% apply markdown_to_html %}
{{ dest.label }},
{{ comment.createdBy.label }} a créé un commentaire sur la notification "{{ comment.notification.title }}".
@@ -6,14 +7,11 @@ Commentaire:
{% for line in comment.content|split("\n") %}
> {{ line }}
{%- if not loop.last %}
>
{%- endif %}
{%- endfor %}
{% endfor %}
Vous pouvez visualiser la notification et y répondre ici:
[Vous pouvez visualiser la notification et y répondre ici.]({{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': comment.notification.id }, false)) }})
{{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': comment.notification.id }, false)) }}
----
--
Le logiciel Chill
{% endapply %}

View File

@@ -0,0 +1,14 @@
{{ dest.label }},
{{ comment.createdBy.label }} a créé un commentaire sur la notification "{{ comment.notification.title }}".
Commentaire:
{% for line in comment.content|split("\n") %}
> {{ line }}
{%- endfor %}
Vous pouvez visualiser la notification et y répondre ici: {{ absolute_url(path('chill_main_notification_show', {'_locale': dest.locale, 'id': comment.notification.id }, false)) }}
--
Le logiciel Chill

View File

@@ -33,7 +33,7 @@ final readonly class ScopeResolverDispatcher
}
/**
* @return Scope|iterable<Scope>|Scope|null
* @return Scope|iterable<Scope>|null
*/
public function resolveScope(mixed $entity, ?array $options = []): iterable|Scope|null
{

View File

@@ -23,7 +23,7 @@ class AddressReferenceFromBAN
private readonly AddressToReferenceMatcher $addressToReferenceMatcher,
) {}
public function import(string $departementNo, ?string $sendAddressReportToEmail = null): void
public function import(string $departementNo, ?string $sendAddressReportToEmail = null, ?bool $allowRemoveDoubleRefId = false): void
{
if (!is_numeric($departementNo)) {
throw new \UnexpectedValueException('Could not parse this department number');
@@ -96,7 +96,7 @@ class AddressReferenceFromBAN
);
}
$this->baseImporter->finalize(sendAddressReportToEmail: $sendAddressReportToEmail);
$this->baseImporter->finalize(allowRemoveDoubleRefId: $allowRemoveDoubleRefId, sendAddressReportToEmail: $sendAddressReportToEmail);
$this->addressToReferenceMatcher->checkAddressesMatchingReferences();

View File

@@ -41,6 +41,6 @@ final class ChillMarkdownRenderExtension extends AbstractExtension
public function renderMarkdownToHtml(?string $var): string
{
return $this->parsedown->parse((string) $var);
return $this->parsedown->text((string) $var);
}
}

View File

@@ -37,7 +37,7 @@ final class ChillMarkdownRenderExtensionTest extends TestCase
MD;
private const UNAUTHORIZED_HTML = <<<'HTML'
<p>&lt;script&gt;alert(&quot;ok&quot;);&lt;/script&gt;</p>
<p>&lt;script&gt;alert("ok");&lt;/script&gt;</p>
HTML;
private const UNAUTHORIZED_MARKDOWN = <<<'MD'

View File

@@ -0,0 +1,41 @@
<?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 Version20260223134919 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create a partial index for postal_code search_name_code, to avoid deleted records';
}
public function up(Schema $schema): void
{
$this->addSql('DROP INDEX public.search_name_code');
$this->addSql('CREATE INDEX search_name_code ON public.chill_main_postal_code USING GIN (LOWER(code) gin_trgm_ops, LOWER(label) gin_trgm_ops) WHERE deletedAt IS NULL');
$this->addSql('DROP INDEX public.chill_internal_postal_code_canonicalized');
$this->addSql('CREATE INDEX chill_internal_postal_code_canonicalized ON chill_main_postal_code USING GIST (canonical gist_trgm_ops) WHERE origin = 0 AND deletedAt IS NULL');
}
public function down(Schema $schema): void
{
$this->addSql('DROP INDEX public.search_name_code');
$this->addSql('CREATE INDEX search_name_code ON chill_main_postal_code USING GIN (LOWER(code) gin_trgm_ops, LOWER(label) gin_trgm_ops)');
$this->addSql('DROP INDEX public.chill_internal_postal_code_canonicalized');
$this->addSql('CREATE INDEX chill_internal_postal_code_canonicalized ON chill_main_postal_code USING GIST (canonical gist_trgm_ops) WHERE origin = 0');
}
}

View File

@@ -27,7 +27,7 @@
<p>{{ $t("courselocation.sure_description") }}</p>
</template>
<template #footer>
<button class="btn btn-danger" @click="assignAddress">
<button class="btn btn-submit" @click="assignAddress">
{{ $t("courselocation.ok") }}
</button>
</template>

View File

@@ -167,7 +167,7 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::getOrder()
* @see SearchInterface::getOrder()
*/
public function getOrder(): int
{
@@ -177,7 +177,7 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::isActiveByDefault()
* @see SearchInterface::isActiveByDefault()
*/
public function isActiveByDefault()
{

View File

@@ -453,8 +453,8 @@ Filtered by entrusted child status: Uniquement les usagers qui sont "enfant conf
Filter by nomadic status: Filtrer les usagers "gens du voyage"
Filtered by nomadic status: Uniquement les usagers qui sont "gens du voyage"
"Filter by person's who have a residential address located at another user": Filtrer les usagers qui ont une addresse de résidence chez une autre usager
"Filtered by person's who have a residential address located at another user": Uniquement les usagers qui ont une addresse de résidence chez une autre usager
"Filter by person's who have a residential address located at another user": Filtrer les usagers qui ont une adresse de résidence chez une autre usager
"Filtered by person's who have a residential address located at another user": Uniquement les usagers qui ont une adresse de résidence chez une autre usager
Filter by person's that are alive or have deceased at a certain date: Filtrer les usagers qui sont décédés ou vivantes à une certaine date
Filtered by person's that are alive or have deceased at a certain date: Uniquement les usagers qui sont décédés ou vivantes à une certaine date

View File

@@ -129,11 +129,11 @@ export:
thirdParties: Tiers intervenant
# exports filters/aggregators
Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type%
Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les usagers qui ont une adresse de résidence chez un tiers de catégorie %thirdparty_type%
is thirdparty: Le demandeur est un tiers
Filter by person's who have a residential address located at a thirdparty of type: Filtrer les usagers qui ont une addresse de résidence chez un tiers
"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%"
Filter by person's who have a residential address located at a thirdparty of type: Filtrer les usagers qui ont une adresse de résidence chez un tiers
"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les usagers qui ont une adresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%"
# admin
admin: