mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
show documents from person in list of document from course
This commit is contained in:
parent
5495b1cb44
commit
7a1feaa8cb
@ -12,14 +12,16 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\GenericDoc\Providers;
|
namespace Chill\DocStoreBundle\GenericDoc\Providers;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
final readonly class PersonDocumentGenericDocProvider implements GenericDocForPersonProviderInterface
|
final readonly class PersonDocumentGenericDocProvider implements GenericDocForPersonProviderInterface, GenericDocForAccompanyingPeriodProviderInterface
|
||||||
{
|
{
|
||||||
public const KEY = 'person_document';
|
public const KEY = 'person_document';
|
||||||
|
|
||||||
@ -48,4 +50,16 @@ final readonly class PersonDocumentGenericDocProvider implements GenericDocForPe
|
|||||||
{
|
{
|
||||||
return $this->security->isGranted(PersonDocumentVoter::SEE, $person);
|
return $this->security->isGranted(PersonDocumentVoter::SEE, $person);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
return $this->personDocumentACLAwareRepository->buildFetchQueryForAccompanyingPeriod($accompanyingPeriod, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool
|
||||||
|
{
|
||||||
|
// we assume that the user is allowed to see at least one person of the course
|
||||||
|
// this will be double checked when running the query
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ 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\CenterResolverDispatcherInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
@ -29,19 +31,14 @@ 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
|
final readonly class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser;
|
public function __construct(
|
||||||
|
private EntityManagerInterface $em,
|
||||||
private CenterResolverManagerInterface $centerResolverManager;
|
private CenterResolverManagerInterface $centerResolverManager,
|
||||||
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
|
||||||
private EntityManagerInterface $em;
|
private Security $security,
|
||||||
|
) {
|
||||||
public function __construct(EntityManagerInterface $em, CenterResolverManagerInterface $centerResolverManager, AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser)
|
|
||||||
{
|
|
||||||
$this->em = $em;
|
|
||||||
$this->centerResolverManager = $centerResolverManager;
|
|
||||||
$this->authorizationHelperForCurrentUser = $authorizationHelperForCurrentUser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildQueryByPerson(Person $person): QueryBuilder
|
public function buildQueryByPerson(Person $person): QueryBuilder
|
||||||
@ -63,6 +60,66 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
|||||||
return $this->addFetchQueryByPersonACL($query, $person);
|
return $this->addFetchQueryByPersonACL($query, $person);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||||
|
$participationMetadata = $this->em->getClassMetadata(AccompanyingPeriodParticipation::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->addJoinClause(
|
||||||
|
sprintf(
|
||||||
|
'JOIN %s AS participation ON participation.%s = person_document.%s '.
|
||||||
|
'AND person_document.%s BETWEEN participation.%s AND COALESCE(participation.%s, \'infinity\'::date)',
|
||||||
|
$participationMetadata->getTableName(),
|
||||||
|
$participationMetadata->getSingleAssociationJoinColumnName('person'),
|
||||||
|
$personDocMetadata->getSingleAssociationJoinColumnName('person'),
|
||||||
|
$personDocMetadata->getColumnName('date'),
|
||||||
|
$participationMetadata->getColumnName('startDate'),
|
||||||
|
$participationMetadata->getColumnName('endDate')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('participation.%s = ?', $participationMetadata->getSingleAssociationJoinColumnName('accompanyingPeriod')),
|
||||||
|
[$period->getId()],
|
||||||
|
[Types::INTEGER]
|
||||||
|
);
|
||||||
|
|
||||||
|
// can we see the document for this person ?
|
||||||
|
$orPersonId = [];
|
||||||
|
foreach ($period->getParticipations() as $participation) {
|
||||||
|
if (!$this->security->isGranted(PersonDocumentVoter::SEE, $participation->getPerson())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$orPersonId[] = $participation->getPerson()->getId();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $orPersonId) {
|
||||||
|
$query->addWhereClause('FALSE = TRUE');
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf(
|
||||||
|
'participation.%s IN (%s)',
|
||||||
|
$participationMetadata->getSingleAssociationJoinColumnName('person'),
|
||||||
|
implode(', ', array_fill(0, count($orPersonId), '?'))
|
||||||
|
),
|
||||||
|
$orPersonId,
|
||||||
|
array_fill(0, count($orPersonId), Types::INTEGER)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->addFilterClauses($query, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
|
||||||
public function buildBaseFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
public function buildBaseFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
{
|
{
|
||||||
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||||
@ -80,6 +137,13 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
|||||||
[Types::INTEGER]
|
[Types::INTEGER]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return $this->addFilterClauses($query, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addFilterClauses(FetchQuery $query, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
{
|
||||||
|
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||||
|
|
||||||
if (null !== $startDate) {
|
if (null !== $startDate) {
|
||||||
$query->addWhereClause(
|
$query->addWhereClause(
|
||||||
sprintf('? <= %s', $personDocMetadata->getColumnName('date')),
|
sprintf('? <= %s', $personDocMetadata->getColumnName('date')),
|
||||||
@ -107,7 +171,6 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
|||||||
[Types::STRING, Types::STRING]
|
[Types::STRING, Types::STRING]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Repository;
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
interface PersonDocumentACLAwareRepositoryInterface
|
interface PersonDocumentACLAwareRepositoryInterface
|
||||||
@ -32,4 +33,11 @@ interface PersonDocumentACLAwareRepositoryInterface
|
|||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null
|
?string $content = null
|
||||||
): FetchQueryInterface;
|
): FetchQueryInterface;
|
||||||
|
|
||||||
|
public function buildFetchQueryForAccompanyingPeriod(
|
||||||
|
AccompanyingPeriod $period,
|
||||||
|
?\DateTimeImmutable $startDate = null,
|
||||||
|
?\DateTimeImmutable $endDate = null,
|
||||||
|
?string $content = null
|
||||||
|
): FetchQueryInterface;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,14 @@
|
|||||||
<i class="fa fa-random"></i> {{ accompanyingCourse.id }}
|
<i class="fa fa-random"></i> {{ accompanyingCourse.id }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% elseif context == 'accompanying-period' and person is defined %}
|
||||||
|
<div>
|
||||||
|
<span class="badge bg-primary">
|
||||||
|
{{ 'Document from person %name%'|trans({ '%name%': document.person|chill_entity_render_string }) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
<div class="denomination h2">
|
<div class="denomination h2">
|
||||||
{{ document.title|chill_print_or_message("No title") }}
|
{{ document.title|chill_print_or_message("No title") }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,12 +21,14 @@ 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\CenterResolverDispatcherInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -66,7 +68,8 @@ class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
|
|||||||
$repository = new PersonDocumentACLAwareRepository(
|
$repository = new PersonDocumentACLAwareRepository(
|
||||||
$this->entityManager,
|
$this->entityManager,
|
||||||
$centerManager->reveal(),
|
$centerManager->reveal(),
|
||||||
$authorizationHelper->reveal()
|
$authorizationHelper->reveal(),
|
||||||
|
$this->prophesize(Security::class)->reveal()
|
||||||
);
|
);
|
||||||
|
|
||||||
$person = $this->entityManager->createQuery("SELECT p FROM " . Person::class . " p")
|
$person = $this->entityManager->createQuery("SELECT p FROM " . Person::class . " p")
|
||||||
@ -86,6 +89,62 @@ class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
|
|||||||
self::assertIsInt($nb, "test that the query could be executed");
|
self::assertIsInt($nb, "test that the query could be executed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDateForFetchQueryForAccompanyingPeriod
|
||||||
|
*/
|
||||||
|
public function testBuildFetchQueryForAccompanyingPeriod(
|
||||||
|
AccompanyingPeriod $period,
|
||||||
|
?\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);
|
||||||
|
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted(PersonDocumentVoter::SEE, Argument::type(Person::class))->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new PersonDocumentACLAwareRepository(
|
||||||
|
$this->entityManager,
|
||||||
|
$centerManager->reveal(),
|
||||||
|
$authorizationHelper->reveal(),
|
||||||
|
$security->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = $repository->buildFetchQueryForAccompanyingPeriod($period, $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 provideDateForFetchQueryForAccompanyingPeriod(): iterable
|
||||||
|
{
|
||||||
|
$this->setUp();
|
||||||
|
|
||||||
|
if (null === $period = $this->entityManager->createQuery(
|
||||||
|
"SELECT p FROM " . AccompanyingPeriod::class . " p WHERE SIZE(p.participations) > 0"
|
||||||
|
)
|
||||||
|
->setMaxResults(1)->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("no course found");
|
||||||
|
}
|
||||||
|
|
||||||
|
yield [$period, null, null, null];
|
||||||
|
yield [$period, new DateTimeImmutable('1 year ago'), null, null];
|
||||||
|
yield [$period, null, new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [$period, new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), null];
|
||||||
|
yield [$period, null, null, 'test'];
|
||||||
|
yield [$period, new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), 'test'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function provideDataBuildFetchQueryForPerson(): iterable
|
public function provideDataBuildFetchQueryForPerson(): iterable
|
||||||
{
|
{
|
||||||
yield [null, null, null];
|
yield [null, null, null];
|
||||||
|
@ -18,6 +18,7 @@ No document found: Aucun document trouvé
|
|||||||
The document is successfully registered: Le document est enregistré
|
The document is successfully registered: Le document est enregistré
|
||||||
The document is successfully updated: Le document est mis à jour
|
The document is successfully updated: Le document est mis à jour
|
||||||
Any description: Aucune description
|
Any description: Aucune description
|
||||||
|
Document from person %name%: Document de l'usager %name%
|
||||||
|
|
||||||
document:
|
document:
|
||||||
Any title: Aucun titre
|
Any title: Aucun titre
|
||||||
@ -26,6 +27,7 @@ generic_doc:
|
|||||||
filter:
|
filter:
|
||||||
keys:
|
keys:
|
||||||
accompanying_course_document: Document du parcours
|
accompanying_course_document: Document du parcours
|
||||||
|
person_document: Documents de l'usager
|
||||||
date-range: Date du document
|
date-range: Date du document
|
||||||
|
|
||||||
# delete
|
# delete
|
||||||
|
@ -269,6 +269,7 @@ class AccompanyingPeriod implements
|
|||||||
* cascade={"persist", "refresh", "remove", "merge", "detach"})
|
* cascade={"persist", "refresh", "remove", "merge", "detach"})
|
||||||
* @Groups({"read", "docgen:read"})
|
* @Groups({"read", "docgen:read"})
|
||||||
* @ParticipationOverlap(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
* @ParticipationOverlap(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
||||||
|
* @var Collection<AccompanyingPeriodParticipation>
|
||||||
*/
|
*/
|
||||||
private Collection $participations;
|
private Collection $participations;
|
||||||
|
|
||||||
@ -870,6 +871,7 @@ class AccompanyingPeriod implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Participations Collection.
|
* Get Participations Collection.
|
||||||
|
* @return Collection<AccompanyingPeriodParticipation>
|
||||||
*/
|
*/
|
||||||
public function getParticipations(): Collection
|
public function getParticipations(): Collection
|
||||||
{
|
{
|
||||||
|
@ -1236,4 +1236,3 @@ generic_doc:
|
|||||||
filter:
|
filter:
|
||||||
keys:
|
keys:
|
||||||
accompanying_period_work_evaluation_document: Document des actions d'accompagnement
|
accompanying_period_work_evaluation_document: Document des actions d'accompagnement
|
||||||
person_document: Documents de la personne
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user