mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-18 16:24:24 +00:00
421 lines
13 KiB
PHP
421 lines
13 KiB
PHP
<?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\Export;
|
||
|
||
use Chill\MainBundle\Entity\User;
|
||
use Chill\MainBundle\Form\Type\Export\ExportType;
|
||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||
use Psr\Log\LoggerInterface;
|
||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||
|
||
/**
|
||
* Collects all agregators, filters and export from
|
||
* the installed bundle, and performs the export logic.
|
||
*/
|
||
class ExportManager
|
||
{
|
||
/**
|
||
* The collected aggregators, injected by DI.
|
||
*
|
||
* @var array|AggregatorInterface[]
|
||
*/
|
||
private array $aggregators = [];
|
||
|
||
/**
|
||
* Collected Exports, injected by DI.
|
||
*
|
||
* @var array|ExportInterface[]
|
||
*/
|
||
private array $exports = [];
|
||
|
||
/**
|
||
* The collected filters, injected by DI.
|
||
*
|
||
* @var array|FilterInterface[]
|
||
*/
|
||
private array $filters = [];
|
||
|
||
/**
|
||
* Collected Formatters, injected by DI.
|
||
*
|
||
* @var array|FormatterInterface[]
|
||
*/
|
||
private array $formatters = [];
|
||
|
||
public function __construct(
|
||
private readonly LoggerInterface $logger,
|
||
private readonly AuthorizationCheckerInterface $authorizationChecker,
|
||
private readonly AuthorizationHelperInterface $authorizationHelper,
|
||
private readonly TokenStorageInterface $tokenStorage,
|
||
iterable $exports,
|
||
iterable $aggregators,
|
||
iterable $filters,
|
||
// iterable $formatters,
|
||
// iterable $exportElementProvider
|
||
) {
|
||
$this->exports = iterator_to_array($exports);
|
||
$this->aggregators = iterator_to_array($aggregators);
|
||
$this->filters = iterator_to_array($filters);
|
||
// NOTE: PHP crashes on the next line (exit error code 11). This is desactivated until further investigation
|
||
// $this->formatters = iterator_to_array($formatters);
|
||
|
||
// foreach ($exportElementProvider as $prefix => $provider) {
|
||
// $this->addExportElementsProvider($provider, $prefix);
|
||
// }
|
||
}
|
||
|
||
/**
|
||
* Return a \Generator containing filter which support type. If `$centers` is
|
||
* not null, restrict the given filters to the center the user have access to.
|
||
*
|
||
* if $centers is null, the function will returns all filters where the user
|
||
* has access in every centers he can reach (if the user can use the filter F in
|
||
* center A, but not in center B, the filter F will not be returned)
|
||
*
|
||
* @param \Chill\MainBundle\Entity\Center[] $centers the centers where the user have access to
|
||
*
|
||
* @return FilterInterface[] a \Generator that contains filters. The key is the filter's alias
|
||
*/
|
||
public function getFiltersApplyingOn(DirectExportInterface|ExportInterface $export, ?array $centers = null): array
|
||
{
|
||
if ($export instanceof DirectExportInterface) {
|
||
return [];
|
||
}
|
||
|
||
$filters = [];
|
||
|
||
foreach ($this->filters as $alias => $filter) {
|
||
if (
|
||
\in_array($filter->applyOn(), $export->supportsModifiers(), true)
|
||
&& $this->isGrantedForElement($filter, $export, $centers)
|
||
) {
|
||
$filters[$alias] = $filter;
|
||
}
|
||
}
|
||
|
||
return $filters;
|
||
}
|
||
|
||
/**
|
||
* Return a \Generator containing aggregators supported by the given export.
|
||
*
|
||
* @internal This class check the interface implemented by export, and, if ´ListInterface´ is used, return an empty array
|
||
*
|
||
* @return array<string, AggregatorInterface> an array that contains aggregators. The key is the filter's alias
|
||
*/
|
||
public function getAggregatorsApplyingOn(DirectExportInterface|ExportInterface $export, ?array $centers = null): array
|
||
{
|
||
if ($export instanceof ListInterface || $export instanceof DirectExportInterface) {
|
||
return [];
|
||
}
|
||
|
||
$aggregators = [];
|
||
|
||
foreach ($this->aggregators as $alias => $aggregator) {
|
||
if (
|
||
\in_array($aggregator->applyOn(), $export->supportsModifiers(), true)
|
||
&& $this->isGrantedForElement($aggregator, $export, $centers)
|
||
) {
|
||
$aggregators[$alias] = $aggregator;
|
||
}
|
||
}
|
||
|
||
return $aggregators;
|
||
}
|
||
|
||
public function addExportElementsProvider(ExportElementsProviderInterface $provider, string $prefix): void
|
||
{
|
||
foreach ($provider->getExportElements() as $suffix => $element) {
|
||
$alias = $prefix.'_'.$suffix;
|
||
|
||
if ($element instanceof ExportInterface) {
|
||
$this->exports[$alias] = $element;
|
||
} elseif ($element instanceof FilterInterface) {
|
||
$this->filters[$alias] = $element;
|
||
} elseif ($element instanceof AggregatorInterface) {
|
||
$this->aggregators[$alias] = $element;
|
||
} elseif ($element instanceof FormatterInterface) {
|
||
$this->addFormatter($element, $alias);
|
||
} else {
|
||
throw new \LogicException('This element '.$element::class.' is not an instance of export element');
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* add a formatter.
|
||
*
|
||
* @internal used by DI
|
||
*/
|
||
public function addFormatter(FormatterInterface $formatter, string $alias)
|
||
{
|
||
$this->formatters[$alias] = $formatter;
|
||
}
|
||
|
||
/**
|
||
* @param string $alias
|
||
*
|
||
* @return AggregatorInterface
|
||
*
|
||
* @throws \RuntimeException if the aggregator is not known
|
||
*/
|
||
public function getAggregator($alias)
|
||
{
|
||
if (!\array_key_exists($alias, $this->aggregators)) {
|
||
throw new \RuntimeException("The aggregator with alias {$alias} is not known.");
|
||
}
|
||
|
||
return $this->aggregators[$alias];
|
||
}
|
||
|
||
/**
|
||
* @return iterable<string, AggregatorInterface>
|
||
*/
|
||
public function getAggregators(array $aliases): iterable
|
||
{
|
||
foreach ($aliases as $alias) {
|
||
yield $alias => $this->getAggregator($alias);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get the types for known exports.
|
||
*
|
||
* @return list<string> the existing type for known exports
|
||
*/
|
||
public function getExistingExportsTypes(): array
|
||
{
|
||
$existingTypes = [];
|
||
|
||
foreach ($this->exports as $export) {
|
||
if (!\in_array($export->getType(), $existingTypes, true)) {
|
||
$existingTypes[] = $export->getType();
|
||
}
|
||
}
|
||
|
||
return $existingTypes;
|
||
}
|
||
|
||
/**
|
||
* Return an export by his alias.
|
||
*
|
||
* @param string $alias
|
||
*
|
||
* @throws \RuntimeException
|
||
*/
|
||
public function getExport($alias): DirectExportInterface|ExportInterface
|
||
{
|
||
if (!\array_key_exists($alias, $this->exports)) {
|
||
throw new \RuntimeException("The export with alias {$alias} is not known.");
|
||
}
|
||
|
||
return $this->exports[$alias];
|
||
}
|
||
|
||
/**
|
||
* Return all exports. The exports's alias are the array's keys.
|
||
*
|
||
* @param bool $whereUserIsGranted if true (default), restrict to user which are granted the right to execute the export
|
||
*
|
||
* @return iterable<string, ExportInterface|DirectExportInterface> an array where export's alias are keys
|
||
*/
|
||
public function getExports($whereUserIsGranted = true): iterable
|
||
{
|
||
foreach ($this->exports as $alias => $export) {
|
||
if ($whereUserIsGranted) {
|
||
if ($this->isGrantedForElement($export, null, null)) {
|
||
yield $alias => $export;
|
||
}
|
||
} else {
|
||
yield $alias => $export;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get all exports grouped in an array.
|
||
*
|
||
* @return array<string, array<string, ExportInterface|DirectExportInterface>> where keys are the groups's name and value is an array of exports
|
||
*/
|
||
public function getExportsGrouped(bool $whereUserIsGranted = true): array
|
||
{
|
||
$groups = ['_' => []];
|
||
|
||
foreach ($this->getExports($whereUserIsGranted) as $alias => $export) {
|
||
if ($export instanceof GroupedExportInterface) {
|
||
$groups[$export->getGroup()][$alias] = $export;
|
||
} else {
|
||
$groups['_'][$alias] = $export;
|
||
}
|
||
}
|
||
|
||
return $groups;
|
||
}
|
||
|
||
/**
|
||
* @throws \RuntimeException if the filter is not known
|
||
*/
|
||
public function getFilter(string $alias): FilterInterface
|
||
{
|
||
if (!\array_key_exists($alias, $this->filters)) {
|
||
throw new \RuntimeException("The filter with alias {$alias} is not known.");
|
||
}
|
||
|
||
return $this->filters[$alias];
|
||
}
|
||
|
||
public function getAllFilters(): array
|
||
{
|
||
$filters = [];
|
||
|
||
foreach ($this->filters as $alias => $filter) {
|
||
$filters[$alias] = $filter;
|
||
}
|
||
|
||
return $filters;
|
||
}
|
||
|
||
/**
|
||
* get all filters.
|
||
*
|
||
* @param array<string> $aliases
|
||
*
|
||
* @return iterable<string, FilterInterface> $aliases
|
||
*/
|
||
public function getFilters(array $aliases): iterable
|
||
{
|
||
foreach ($aliases as $alias) {
|
||
yield $alias => $this->getFilter($alias);
|
||
}
|
||
}
|
||
|
||
public function getFormatter(string $alias): FormatterInterface
|
||
{
|
||
if (!\array_key_exists($alias, $this->formatters)) {
|
||
throw new \RuntimeException("The formatter with alias {$alias} is not known.");
|
||
}
|
||
|
||
return $this->formatters[$alias];
|
||
}
|
||
|
||
/**
|
||
* get the formatter alias from the form export data.
|
||
*
|
||
* @param array $data the data from the export form
|
||
*
|
||
* @string the formatter alias|null
|
||
*/
|
||
public function getFormatterAlias(array $data): ?string
|
||
{
|
||
if (\array_key_exists(ExportType::PICK_FORMATTER_KEY, $data)) {
|
||
return $data[ExportType::PICK_FORMATTER_KEY]['alias'];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Get all formatters which supports one of the given types.
|
||
*
|
||
* @return iterable<string, FormatterInterface>
|
||
*/
|
||
public function getFormattersByTypes(array $types): iterable
|
||
{
|
||
foreach ($this->formatters as $alias => $formatter) {
|
||
if (\in_array($formatter->getType(), $types, true)) {
|
||
yield $alias => $formatter;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get the Center picked by the user for this export. The data are
|
||
* extracted from the PickCenterType data.
|
||
*
|
||
* @param array $data the data from a PickCenterType
|
||
*
|
||
* @return \Chill\MainBundle\Entity\Center[] the picked center
|
||
*/
|
||
public function getPickedCenters(array $data): array
|
||
{
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* get the aggregators types used in the form export data.
|
||
*
|
||
* @param array $data the data from the export form
|
||
*
|
||
* @return list<string>
|
||
*/
|
||
public function getUsedAggregatorsAliases(array $data): array
|
||
{
|
||
$keys = [];
|
||
|
||
foreach ($data as $alias => $aggregatorData) {
|
||
if (true === $aggregatorData['enabled']) {
|
||
$keys[] = $alias;
|
||
}
|
||
}
|
||
|
||
return array_values(array_unique($keys));
|
||
}
|
||
|
||
/**
|
||
* Return true if the current user has access to the ExportElement for every
|
||
* center, false if the user hasn't access to element for at least one center.
|
||
*/
|
||
public function isGrantedForElement(
|
||
DirectExportInterface|ExportInterface|ModifierInterface $element,
|
||
DirectExportInterface|ExportInterface|null $export = null,
|
||
?array $centers = null,
|
||
): bool {
|
||
if ($element instanceof ExportInterface || $element instanceof DirectExportInterface) {
|
||
$role = $element->requiredRole();
|
||
} else {
|
||
if (null === $element->addRole()) {
|
||
if (null === $export) {
|
||
throw new \LogicException('The export should not be null: as the ModifierInstance element is not an export, we should be aware of the export to determine which role is required');
|
||
}
|
||
$role = $export->requiredRole();
|
||
} else {
|
||
$role = $element->addRole();
|
||
}
|
||
}
|
||
|
||
if (null === $centers || [] === $centers) {
|
||
// we want to try if at least one center is reachable
|
||
return [] !== $this->authorizationHelper->getReachableCenters(
|
||
$this->tokenStorage->getToken()->getUser(),
|
||
$role
|
||
);
|
||
}
|
||
|
||
foreach ($centers as $center) {
|
||
if (false === $this->authorizationChecker->isGranted($role, $center)) {
|
||
// debugging
|
||
$this->logger->debug('user has no access to element', [
|
||
'method' => __METHOD__,
|
||
'type' => $element::class,
|
||
'center' => $center->getName(),
|
||
'role' => $role,
|
||
]);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|