mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-02-02 22:47:16 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
3402e4863f
|
|||
| 1f0974ea68 | |||
| 9997fb287a | |||
| f9a9de1148 | |||
|
|
c34f720f94 | ||
| e1b1f592fa | |||
| 8546f4dadc |
4
.changes/v4.12.1.md
Normal file
4
.changes/v4.12.1.md
Normal 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
|
||||
|
||||
@@ -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.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 +26,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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
kind: Added
|
||||
body: Use admin delegated account for handling authentication
|
||||
time: 2026-01-22T15:32:23.932994899+01:00
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user