421 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}