mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-28 13:06:13 +00:00
[generic doc] listing generic doc for person
This commit is contained in:
parent
eb107f5a15
commit
40af1e64ac
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle;
|
namespace Chill\DocStoreBundle;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
@ -22,6 +23,8 @@ class ChillDocStoreBundle extends Bundle
|
|||||||
{
|
{
|
||||||
$container->registerForAutoconfiguration(GenericDocForAccompanyingPeriodProviderInterface::class)
|
$container->registerForAutoconfiguration(GenericDocForAccompanyingPeriodProviderInterface::class)
|
||||||
->addTag('chill_doc_store.generic_doc_accompanying_period_provider');
|
->addTag('chill_doc_store.generic_doc_accompanying_period_provider');
|
||||||
|
$container->registerForAutoconfiguration(GenericDocForPersonProviderInterface::class)
|
||||||
|
->addTag('chill_doc_store.generic_doc_person_provider');
|
||||||
$container->registerForAutoconfiguration(GenericDocRendererInterface::class)
|
$container->registerForAutoconfiguration(GenericDocRendererInterface::class)
|
||||||
->addTag('chill_doc_store.generic_doc_renderer');
|
->addTag('chill_doc_store.generic_doc_renderer');
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Manager;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
use Symfony\Component\Templating\EngineInterface;
|
||||||
|
|
||||||
|
final readonly class GenericDocForPerson
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private FilterOrderHelperFactory $filterOrderHelperFactory,
|
||||||
|
private Manager $manager,
|
||||||
|
private PaginatorFactory $paginator,
|
||||||
|
private Security $security,
|
||||||
|
private EngineInterface $twig,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
*
|
||||||
|
* @Route("/{_locale}/doc-store/generic-doc/by-person/{id}/index", name="chill_docstore_generic-doc_by-person_index")
|
||||||
|
*/
|
||||||
|
public function list(Person $person): Response
|
||||||
|
{
|
||||||
|
if (!$this->security->isGranted(PersonDocumentVoter::SEE, $person)) {
|
||||||
|
throw new AccessDeniedHttpException("not allowed to see the documents for person");
|
||||||
|
}
|
||||||
|
|
||||||
|
$filterBuilder = $this->filterOrderHelperFactory
|
||||||
|
->create(self::class)
|
||||||
|
->addSearchBox()
|
||||||
|
->addDateRange('dateRange', 'generic_doc.filter.date-range');
|
||||||
|
|
||||||
|
if ([] !== $places = $this->manager->placesForPerson($person)) {
|
||||||
|
$filterBuilder->addCheckbox('places', $places, [], array_map(
|
||||||
|
static fn (string $k) => 'generic_doc.filter.keys.' . $k,
|
||||||
|
$places
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter = $filterBuilder
|
||||||
|
->build();
|
||||||
|
|
||||||
|
['to' => $endDate, 'from' => $startDate ] = $filter->getDateRangeData('dateRange');
|
||||||
|
$content = $filter->getQueryString();
|
||||||
|
|
||||||
|
$nb = $this->manager->countDocForPerson(
|
||||||
|
$person,
|
||||||
|
$startDate,
|
||||||
|
$endDate,
|
||||||
|
$content,
|
||||||
|
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
|
||||||
|
);
|
||||||
|
$paginator = $this->paginator->create($nb);
|
||||||
|
|
||||||
|
$documents = $this->manager->findDocForPerson(
|
||||||
|
$person,
|
||||||
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
|
$paginator->getItemsPerPage(),
|
||||||
|
$startDate,
|
||||||
|
$endDate,
|
||||||
|
$content,
|
||||||
|
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Response($this->twig->render(
|
||||||
|
'@ChillDocStore/GenericDoc/person_list.html.twig',
|
||||||
|
[
|
||||||
|
'person' => $person,
|
||||||
|
'pagination' => $paginator,
|
||||||
|
'documents' => iterator_to_array($documents),
|
||||||
|
'filter' => $filter,
|
||||||
|
]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,8 +25,6 @@ interface GenericDocForAccompanyingPeriodProviderInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the user is allowed to see some documents for this provider.
|
* Return true if the user is allowed to see some documents for this provider.
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool;
|
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool;
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?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\GenericDoc;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
interface GenericDocForPersonProviderInterface
|
||||||
|
{
|
||||||
|
public function buildFetchQueryForPerson(
|
||||||
|
Person $person,
|
||||||
|
?DateTimeImmutable $startDate = null,
|
||||||
|
?DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null,
|
||||||
|
?string $origin = null
|
||||||
|
): FetchQueryInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the user is allowed to see some documents for this provider.
|
||||||
|
*/
|
||||||
|
public function isAllowedForPerson(Person $person): bool;
|
||||||
|
}
|
@ -17,16 +17,21 @@ use Doctrine\DBAL\Connection;
|
|||||||
use Doctrine\DBAL\Exception;
|
use Doctrine\DBAL\Exception;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
|
||||||
class Manager
|
final readonly class Manager
|
||||||
{
|
{
|
||||||
private readonly FetchQueryToSqlBuilder $builder;
|
private FetchQueryToSqlBuilder $builder;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
/**
|
/**
|
||||||
* @var iterable<GenericDocForAccompanyingPeriodProviderInterface>
|
* @var iterable<GenericDocForAccompanyingPeriodProviderInterface>
|
||||||
*/
|
*/
|
||||||
private readonly iterable $providersForAccompanyingPeriod,
|
private iterable $providersForAccompanyingPeriod,
|
||||||
private readonly Connection $connection,
|
|
||||||
|
/**
|
||||||
|
* @var iterable<GenericDocForPersonProviderInterface>
|
||||||
|
*/
|
||||||
|
private iterable $providersForPerson,
|
||||||
|
private Connection $connection,
|
||||||
) {
|
) {
|
||||||
$this->builder = new FetchQueryToSqlBuilder();
|
$this->builder = new FetchQueryToSqlBuilder();
|
||||||
}
|
}
|
||||||
@ -44,6 +49,11 @@ class Manager
|
|||||||
): int {
|
): int {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
|
return $this->countDoc($sql, $params, $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function countDoc(string $sql, array $params, array $types): int
|
||||||
|
{
|
||||||
if ($sql === '') {
|
if ($sql === '') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -60,8 +70,21 @@ class Manager
|
|||||||
return $number;
|
return $number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countDocForPerson(
|
||||||
|
Person $person,
|
||||||
|
?\DateTimeImmutable $startDate = null,
|
||||||
|
?\DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null,
|
||||||
|
array $places = []
|
||||||
|
): int {
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
|
return $this->countDoc($sql, $params, $types);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param list<string> $places places to search. When empty, search in all places
|
* @param list<string> $places places to search. When empty, search in all places
|
||||||
|
* @return iterable<GenericDocDTO>
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function findDocForAccompanyingPeriod(
|
public function findDocForAccompanyingPeriod(
|
||||||
@ -75,6 +98,15 @@ class Manager
|
|||||||
): iterable {
|
): iterable {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
|
return $this->findDocs($accompanyingPeriod, $sql, $params, $types, $offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \JsonException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function findDocs(AccompanyingPeriod|Person $linked, string $sql, array $params, array $types, int $offset, int $limit): iterable
|
||||||
|
{
|
||||||
if ($sql === '') {
|
if ($sql === '') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -88,20 +120,50 @@ class Manager
|
|||||||
$row['key'],
|
$row['key'],
|
||||||
json_decode($row['identifiers'], true, 512, JSON_THROW_ON_ERROR),
|
json_decode($row['identifiers'], true, 512, JSON_THROW_ON_ERROR),
|
||||||
new \DateTimeImmutable($row['doc_date']),
|
new \DateTimeImmutable($row['doc_date']),
|
||||||
$accompanyingPeriod,
|
$linked,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<string> $places places to search. When empty, search in all places
|
||||||
|
* @return iterable<GenericDocDTO>
|
||||||
|
*/
|
||||||
|
public function findDocForPerson(
|
||||||
|
Person $person,
|
||||||
|
int $offset = 0,
|
||||||
|
int $limit = 20,
|
||||||
|
?\DateTimeImmutable $startDate = null,
|
||||||
|
?\DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null,
|
||||||
|
array $places = []
|
||||||
|
): iterable {
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
|
return $this->findDocs($person, $sql, $params, $types, $offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function placesForPerson(Person $person): array
|
||||||
|
{
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person);
|
||||||
|
|
||||||
|
return $this->places($sql, $params, $types);
|
||||||
|
}
|
||||||
|
|
||||||
public function placesForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): array
|
public function placesForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): array
|
||||||
{
|
{
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod);
|
||||||
|
|
||||||
|
return $this->places($sql, $params, $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function places(string $sql, array $params, array $types): array
|
||||||
|
{
|
||||||
if ($sql === '') {
|
if ($sql === '') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq";
|
$runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq ORDER BY key";
|
||||||
|
|
||||||
$keys = [];
|
$keys = [];
|
||||||
|
|
||||||
@ -122,28 +184,38 @@ class Manager
|
|||||||
?string $content = null,
|
?string $content = null,
|
||||||
array $places = [],
|
array $places = [],
|
||||||
): array {
|
): array {
|
||||||
$sql = [];
|
$queries = [];
|
||||||
$params = [];
|
|
||||||
$types = [];
|
|
||||||
|
|
||||||
if ($linked instanceof AccompanyingPeriod) {
|
if ($linked instanceof AccompanyingPeriod) {
|
||||||
foreach ($this->providersForAccompanyingPeriod as $provider) {
|
foreach ($this->providersForAccompanyingPeriod as $provider) {
|
||||||
if (!$provider->isAllowedForAccompanyingPeriod($linked)) {
|
if (!$provider->isAllowedForAccompanyingPeriod($linked)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$query = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content);
|
$queries[] = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
if ([] !== $places and !in_array($query->getSelectKeyString(), $places, true)) {
|
} else {
|
||||||
|
foreach ($this->providersForPerson as $provider) {
|
||||||
|
if (!$provider->isAllowedForPerson($linked)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
$queries[] = $provider->buildFetchQueryForPerson($linked, $startDate, $endDate, $content);
|
||||||
['sql' => $q, 'params' => $p, 'types' => $t ] = $this->builder->toSql($query);
|
|
||||||
|
|
||||||
$sql[] = $q;
|
|
||||||
$params = [...$params, ...$p];
|
|
||||||
$types = [...$types, ...$t];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$sql = [];
|
||||||
|
$params = [];
|
||||||
|
$types = [];
|
||||||
|
|
||||||
|
foreach ($queries as $query) {
|
||||||
|
if ([] !== $places and !in_array($query->getSelectKeyString(), $places, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
['sql' => $q, 'params' => $p, 'types' => $t ] = $this->builder->toSql($query);
|
||||||
|
|
||||||
|
$sql[] = $q;
|
||||||
|
$params = [...$params, ...$p];
|
||||||
|
$types = [...$types, ...$t];
|
||||||
|
}
|
||||||
|
|
||||||
return ['sql' => implode(' UNION ', $sql), 'params' => $params, 'types' => $types];
|
return ['sql' => implode(' UNION ', $sql), 'params' => $params, 'types' => $types];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
<?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\GenericDoc\Providers;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final readonly class PersonDocumentGenericDocProvider implements GenericDocForPersonProviderInterface
|
||||||
|
{
|
||||||
|
public const KEY = 'person_document';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private Security $security,
|
||||||
|
private PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForPerson(
|
||||||
|
Person $person,
|
||||||
|
?DateTimeImmutable $startDate = null,
|
||||||
|
?DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null,
|
||||||
|
?string $origin = null
|
||||||
|
): FetchQueryInterface {
|
||||||
|
return $this->personDocumentACLAwareRepository->buildFetchQueryForPerson(
|
||||||
|
$person,
|
||||||
|
$startDate,
|
||||||
|
$endDate,
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAllowedForPerson(Person $person): bool
|
||||||
|
{
|
||||||
|
return $this->security->isGranted(PersonDocumentVoter::SEE, $person);
|
||||||
|
}
|
||||||
|
}
|
@ -12,20 +12,25 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\GenericDoc\Renderer;
|
namespace Chill\DocStoreBundle\GenericDoc\Renderer;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||||
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
||||||
use Chill\DocStoreBundle\GenericDoc\Providers\AccompanyingProviderCourseDocumentGenericDoc;
|
use Chill\DocStoreBundle\GenericDoc\Providers\AccompanyingProviderCourseDocumentGenericDoc;
|
||||||
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
|
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentRepository;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
|
||||||
final readonly class AccompanyingCourseDocumentGenericDocRenderer implements GenericDocRendererInterface
|
final readonly class AccompanyingCourseDocumentGenericDocRenderer implements GenericDocRendererInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AccompanyingCourseDocumentRepository $accompanyingCourseDocumentRepository,
|
private AccompanyingCourseDocumentRepository $accompanyingCourseDocumentRepository,
|
||||||
|
private PersonDocumentRepository $personDocumentRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports(GenericDocDTO $genericDocDTO, $options = []): bool
|
public function supports(GenericDocDTO $genericDocDTO, $options = []): bool
|
||||||
{
|
{
|
||||||
return $genericDocDTO->key === AccompanyingProviderCourseDocumentGenericDoc::KEY;
|
return $genericDocDTO->key === AccompanyingProviderCourseDocumentGenericDoc::KEY
|
||||||
|
|| $genericDocDTO->key === PersonDocumentGenericDocProvider::KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string
|
public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string
|
||||||
@ -35,9 +40,18 @@ final readonly class AccompanyingCourseDocumentGenericDocRenderer implements Gen
|
|||||||
|
|
||||||
public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array
|
public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array
|
||||||
{
|
{
|
||||||
|
if ($genericDocDTO->linked instanceof AccompanyingPeriod) {
|
||||||
|
return [
|
||||||
|
'document' => $this->accompanyingCourseDocumentRepository->find($genericDocDTO->identifiers['id']),
|
||||||
|
'accompanyingCourse' => $genericDocDTO->linked,
|
||||||
|
'options' => $options,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a person
|
||||||
return [
|
return [
|
||||||
'document' => $this->accompanyingCourseDocumentRepository->find($genericDocDTO->identifiers['id']),
|
'document' => $this->personDocumentRepository->find($genericDocDTO->identifiers['id']),
|
||||||
'accompanyingCourse' => $genericDocDTO->linked,
|
'person' => $genericDocDTO->linked,
|
||||||
'options' => $options,
|
'options' => $options,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -12,30 +12,36 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Repository;
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareRepositoryInterface
|
class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
private AuthorizationHelperInterface $authorizationHelper;
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser;
|
||||||
|
|
||||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
private CenterResolverManagerInterface $centerResolverManager;
|
||||||
|
|
||||||
private EntityManagerInterface $em;
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
private Security $security;
|
public function __construct(EntityManagerInterface $em, CenterResolverManagerInterface $centerResolverManager, AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser)
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em, AuthorizationHelperInterface $authorizationHelper, CenterResolverDispatcher $centerResolverDispatcher, Security $security)
|
|
||||||
{
|
{
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
$this->centerResolverManager = $centerResolverManager;
|
||||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
$this->authorizationHelperForCurrentUser = $authorizationHelperForCurrentUser;
|
||||||
$this->security = $security;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildQueryByPerson(Person $person): QueryBuilder
|
public function buildQueryByPerson(Person $person): QueryBuilder
|
||||||
@ -49,6 +55,62 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
|||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
$query = $this->buildBaseFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||||
|
|
||||||
|
return $this->addFetchQueryByPersonACL($query, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildBaseFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
{
|
||||||
|
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||||
|
|
||||||
|
$query = new FetchQuery(
|
||||||
|
PersonDocumentGenericDocProvider::KEY,
|
||||||
|
sprintf('jsonb_build_object(\'id\', person_document.%s)', $personDocMetadata->getSingleIdentifierColumnName()),
|
||||||
|
sprintf('person_document.%s', $personDocMetadata->getColumnName('date')),
|
||||||
|
sprintf('%s AS person_document', $personDocMetadata->getSchemaName().'.'.$personDocMetadata->getTableName())
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('person_document.%s = ?', $personDocMetadata->getSingleAssociationJoinColumnName('person')),
|
||||||
|
[$person->getId()],
|
||||||
|
[Types::INTEGER]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (null !== $startDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('? <= %s', $personDocMetadata->getColumnName('date')),
|
||||||
|
[$startDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $endDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('? >= %s', $personDocMetadata->getColumnName('date')),
|
||||||
|
[$endDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $content and '' !== $content) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf(
|
||||||
|
'(%s ilike ? OR %s ilike ?)',
|
||||||
|
$personDocMetadata->getColumnName('title'),
|
||||||
|
$personDocMetadata->getColumnName('description')
|
||||||
|
),
|
||||||
|
['%' . $content . '%', '%' . $content . '%'],
|
||||||
|
[Types::STRING, Types::STRING]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
public function countByPerson(Person $person): int
|
public function countByPerson(Person $person): int
|
||||||
{
|
{
|
||||||
$qb = $this->buildQueryByPerson($person)->select('COUNT(d)');
|
$qb = $this->buildQueryByPerson($person)->select('COUNT(d)');
|
||||||
@ -75,16 +137,58 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
|||||||
|
|
||||||
private function addACL(QueryBuilder $qb, Person $person): void
|
private function addACL(QueryBuilder $qb, Person $person): void
|
||||||
{
|
{
|
||||||
$center = $this->centerResolverDispatcher->resolveCenter($person);
|
$reachableScopes = [];
|
||||||
|
|
||||||
$reachableScopes = $this->authorizationHelper
|
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||||
->getReachableScopes(
|
$reachableScopes = [
|
||||||
$this->security->getUser(),
|
...$reachableScopes,
|
||||||
PersonDocumentVoter::SEE,
|
...$this->authorizationHelperForCurrentUser
|
||||||
$center
|
->getReachableScopes(
|
||||||
);
|
PersonDocumentVoter::SEE,
|
||||||
|
$center
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $reachableScopes) {
|
||||||
|
$qb->andWhere("'FALSE' = 'TRUE'");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$qb->andWhere($qb->expr()->in('d.scope', ':scopes'))
|
$qb->andWhere($qb->expr()->in('d.scope', ':scopes'))
|
||||||
->setParameter('scopes', $reachableScopes);
|
->setParameter('scopes', $reachableScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addFetchQueryByPersonACL(FetchQuery $fetchQuery, Person $person): FetchQuery
|
||||||
|
{
|
||||||
|
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||||
|
|
||||||
|
$reachableScopes = [];
|
||||||
|
|
||||||
|
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||||
|
$reachableScopes = [
|
||||||
|
...$reachableScopes,
|
||||||
|
...$this->authorizationHelperForCurrentUser->getReachableScopes(PersonDocumentVoter::SEE, $center)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $reachableScopes) {
|
||||||
|
$fetchQuery->addWhereClause('FALSE = TRUE');
|
||||||
|
|
||||||
|
return $fetchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fetchQuery->addWhereClause(
|
||||||
|
sprintf(
|
||||||
|
'person_document.%s IN (%s)',
|
||||||
|
$personDocMetadata->getSingleAssociationJoinColumnName('scope'),
|
||||||
|
implode(', ', array_fill(0, count($reachableScopes), '?'))
|
||||||
|
),
|
||||||
|
array_map(static fn (Scope $s) => $s->getId(), $reachableScopes),
|
||||||
|
array_fill(0, count($reachableScopes), Types::INTEGER)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $fetchQuery;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,25 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocStoreBundle\Repository;
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
interface PersonDocumentACLAwareRepositoryInterface
|
interface PersonDocumentACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @deprecated use fetch query for listing and counting person documents
|
||||||
|
*/
|
||||||
public function countByPerson(Person $person): int;
|
public function countByPerson(Person $person): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use fetch query for listing and counting person documents
|
||||||
|
*/
|
||||||
public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array;
|
public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array;
|
||||||
|
|
||||||
|
public function buildFetchQueryForPerson(
|
||||||
|
Person $person,
|
||||||
|
?\DateTimeImmutable $startDate = null,
|
||||||
|
?\DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null
|
||||||
|
): FetchQueryInterface;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template ObjectRepository<PersonDocument::class>
|
||||||
|
*/
|
||||||
|
readonly class PersonDocumentRepository implements ObjectRepository
|
||||||
|
{
|
||||||
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $entityManager
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->repository = $this->entityManager->getRepository($this->getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id): ?PersonDocument
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll()
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null)
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria): ?PersonDocument
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return PersonDocument::class;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
{#
|
||||||
|
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||||
|
|
||||||
|
{% set activeRouteKey = '' %}
|
||||||
|
|
||||||
|
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ parent() }}
|
||||||
|
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
|
||||||
|
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||||
|
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ parent() }}
|
||||||
|
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
|
||||||
|
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||||
|
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="col-md-10 col-xxl">
|
||||||
|
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
|
||||||
|
|
||||||
|
{{ filter|chill_render_filter_order_helper }}
|
||||||
|
|
||||||
|
{% if documents|length == 0 %}
|
||||||
|
<p class="chill-no-data-statement">{{ 'No documents'|trans }}</p>
|
||||||
|
{% else %}
|
||||||
|
<div class="flex-table chill-task-list">
|
||||||
|
{% for document in documents %}
|
||||||
|
{{ document|chill_generic_doc_render }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ chill_pagination(pagination) }}
|
||||||
|
|
||||||
|
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\PersonBundle\Entity\Person" data-entity-id="{{ person.id }}"></div>
|
||||||
|
|
||||||
|
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
|
||||||
|
<ul class="record_actions sticky-form-buttons">
|
||||||
|
<li class="create">
|
||||||
|
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="btn btn-create">
|
||||||
|
{{ 'Create new document' | trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -14,9 +14,12 @@ namespace Chill\DocStoreBundle\Tests\GenericDoc;
|
|||||||
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
use Chill\DocStoreBundle\GenericDoc\Manager;
|
use Chill\DocStoreBundle\GenericDoc\Manager;
|
||||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
@ -53,7 +56,8 @@ class ManagerTest extends KernelTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$manager = new Manager(
|
$manager = new Manager(
|
||||||
[new SimpleGenericDocProvider()],
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
$this->connection,
|
$this->connection,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -62,6 +66,27 @@ class ManagerTest extends KernelTestCase
|
|||||||
self::assertIsInt($nb);
|
self::assertIsInt($nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCountByPerson(): void
|
||||||
|
{
|
||||||
|
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $person) {
|
||||||
|
throw new \UnexpectedValueException("person found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = new Manager(
|
||||||
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
|
$this->connection,
|
||||||
|
);
|
||||||
|
|
||||||
|
$nb = $manager->countDocForPerson($person);
|
||||||
|
|
||||||
|
self::assertIsInt($nb);
|
||||||
|
}
|
||||||
|
|
||||||
public function testFindDocByAccompanyingPeriod(): void
|
public function testFindDocByAccompanyingPeriod(): void
|
||||||
{
|
{
|
||||||
$period = $this->em->createQuery('SELECT a FROM '.AccompanyingPeriod::class.' a')
|
$period = $this->em->createQuery('SELECT a FROM '.AccompanyingPeriod::class.' a')
|
||||||
@ -73,7 +98,8 @@ class ManagerTest extends KernelTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$manager = new Manager(
|
$manager = new Manager(
|
||||||
[new SimpleGenericDocProvider()],
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
$this->connection,
|
$this->connection,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -81,14 +107,77 @@ class ManagerTest extends KernelTestCase
|
|||||||
self::assertInstanceOf(GenericDocDTO::class, $doc);
|
self::assertInstanceOf(GenericDocDTO::class, $doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFindDocByPerson(): void
|
||||||
|
{
|
||||||
|
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $person) {
|
||||||
|
throw new \UnexpectedValueException("person not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = new Manager(
|
||||||
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
|
$this->connection,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($manager->findDocForPerson($person) as $doc) {
|
||||||
|
self::assertInstanceOf(GenericDocDTO::class, $doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPlacesForPerson(): void
|
||||||
|
{
|
||||||
|
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $person) {
|
||||||
|
throw new \UnexpectedValueException("person not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = new Manager(
|
||||||
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
|
$this->connection,
|
||||||
|
);
|
||||||
|
|
||||||
|
$places = $manager->placesForPerson($person);
|
||||||
|
|
||||||
|
self::assertEquals(['dummy_person_doc'], $places);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPlacesForAccompanyingPeriod(): void
|
||||||
|
{
|
||||||
|
$period = $this->em->createQuery('SELECT a FROM '.AccompanyingPeriod::class.' a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $period) {
|
||||||
|
throw new \UnexpectedValueException("period not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = new Manager(
|
||||||
|
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||||
|
[new SimpleGenericDocPersonProvider()],
|
||||||
|
$this->connection,
|
||||||
|
);
|
||||||
|
|
||||||
|
$places = $manager->placesForAccompanyingPeriod($period);
|
||||||
|
|
||||||
|
self::assertEquals(['accompanying_course_document_dummy'], $places);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final readonly class SimpleGenericDocProvider implements GenericDocForAccompanyingPeriodProviderInterface
|
final readonly class SimpleGenericDocAccompanyingPeriodProvider implements GenericDocForAccompanyingPeriodProviderInterface
|
||||||
{
|
{
|
||||||
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
{
|
{
|
||||||
$query = new FetchQuery(
|
$query = new FetchQuery(
|
||||||
'accompanying_course_document',
|
'accompanying_course_document_dummy',
|
||||||
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
|
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
|
||||||
'd',
|
'd',
|
||||||
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
|
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
|
||||||
@ -104,3 +193,25 @@ final readonly class SimpleGenericDocProvider implements GenericDocForAccompanyi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final readonly class SimpleGenericDocPersonProvider implements GenericDocForPersonProviderInterface
|
||||||
|
{
|
||||||
|
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
$query = new FetchQuery(
|
||||||
|
'dummy_person_doc',
|
||||||
|
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
|
||||||
|
'd',
|
||||||
|
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addWhereClause('d > ?', [new \DateTimeImmutable('2023-01-01')], [Types::DATE_IMMUTABLE]);
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAllowedForPerson(Person $person): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
<?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\Tests\GenericDoc\Providers;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class PersonDocumentGenericDocProviderTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||||
|
$this->personDocumentACLAwareRepository = self::$container->get(PersonDocumentACLAwareRepositoryInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataBuildFetchQueryForPerson
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
* @throws \Doctrine\ORM\NoResultException
|
||||||
|
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||||
|
*/
|
||||||
|
public function testBuildFetchQueryForPerson(?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?string $content): void
|
||||||
|
{
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$person = $this->entityManager->createQuery('SELECT a FROM '.Person::class.' a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $person) {
|
||||||
|
throw new \UnexpectedValueException("person found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = new PersonDocumentGenericDocProvider(
|
||||||
|
$security->reveal(),
|
||||||
|
$this->personDocumentACLAwareRepository
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = $provider->buildFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||||
|
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||||
|
|
||||||
|
$nb = $this->entityManager->getConnection()
|
||||||
|
->fetchOne("SELECT COUNT(*) AS nb FROM (${sql}) AS sq", $params, $types);
|
||||||
|
|
||||||
|
self::assertIsInt($nb, "Test that the query is syntactically correct");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDataBuildFetchQueryForPerson(): iterable
|
||||||
|
{
|
||||||
|
yield [null, null, null];
|
||||||
|
yield [new DateTimeImmutable('1 year ago'), null, null];
|
||||||
|
yield [null, new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [null, null, 'test'];
|
||||||
|
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), 'test'];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
<?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\Tests\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepository;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private ScopeRepositoryInterface $scopeRepository;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||||
|
$this->scopeRepository = self::$container->get(ScopeRepositoryInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataBuildFetchQueryForPerson
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
* @throws \Doctrine\ORM\NoResultException
|
||||||
|
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||||
|
*/
|
||||||
|
public function testBuildFetchQueryForPerson(?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): void
|
||||||
|
{
|
||||||
|
$centerManager = $this->prophesize(CenterResolverManagerInterface::class);
|
||||||
|
$centerManager->resolveCenters(Argument::type(Person::class))
|
||||||
|
->willReturn([new Center()]);
|
||||||
|
|
||||||
|
$scopes = $this->scopeRepository->findAll();
|
||||||
|
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$authorizationHelper->getReachableScopes(PersonDocumentVoter::SEE, Argument::any())->willReturn($scopes);
|
||||||
|
|
||||||
|
$repository = new PersonDocumentACLAwareRepository(
|
||||||
|
$this->entityManager,
|
||||||
|
$centerManager->reveal(),
|
||||||
|
$authorizationHelper->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
|
$person = $this->entityManager->createQuery("SELECT p FROM " . Person::class . " p")
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
if (null === $person) {
|
||||||
|
throw new \RuntimeException("person not exists in database");
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $repository->buildFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||||
|
|
||||||
|
$nb = $this->entityManager->getConnection()
|
||||||
|
->fetchOne("SELECT COUNT(*) FROM ({$sql}) AS sq", $params, $types);
|
||||||
|
|
||||||
|
self::assertIsInt($nb, "test that the query could be executed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDataBuildFetchQueryForPerson(): iterable
|
||||||
|
{
|
||||||
|
yield [null, null, null];
|
||||||
|
yield [new DateTimeImmutable('1 year ago'), null, null];
|
||||||
|
yield [null, new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [null, null, 'test'];
|
||||||
|
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), 'test'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -50,6 +50,7 @@ services:
|
|||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
arguments:
|
arguments:
|
||||||
$providersForAccompanyingPeriod: !tagged_iterator chill_doc_store.generic_doc_accompanying_period_provider
|
$providersForAccompanyingPeriod: !tagged_iterator chill_doc_store.generic_doc_accompanying_period_provider
|
||||||
|
$providersForPerson: !tagged_iterator chill_doc_store.generic_doc_person_provider
|
||||||
|
|
||||||
Chill\DocStoreBundle\GenericDoc\Twig\GenericDocExtension:
|
Chill\DocStoreBundle\GenericDoc\Twig\GenericDocExtension:
|
||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user