mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/master' into upgrade-sf5
This commit is contained in:
commit
54d045f261
6
.changes/v2.22.0.md
Normal file
6
.changes/v2.22.0.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
## v2.22.0 - 2024-06-25
|
||||||
|
### Feature
|
||||||
|
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||||
|
|
||||||
|
### Traduction francophone
|
||||||
|
* Exports sont ajoutés pour la module événement.
|
5
.changes/v2.22.1.md
Normal file
5
.changes/v2.22.1.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## v2.22.1 - 2024-07-01
|
||||||
|
### Fixed
|
||||||
|
* Remove debug word
|
||||||
|
### DX
|
||||||
|
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
3
.changes/v2.22.2.md
Normal file
3
.changes/v2.22.2.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -6,6 +6,23 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
||||||
|
|
||||||
|
## v2.22.1 - 2024-07-01
|
||||||
|
### Fixed
|
||||||
|
* Remove debug word
|
||||||
|
### DX
|
||||||
|
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
||||||
|
|
||||||
|
## v2.22.0 - 2024-06-25
|
||||||
|
### Feature
|
||||||
|
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||||
|
|
||||||
|
### Traduction francophone
|
||||||
|
* Exports sont ajoutés pour la module événement.
|
||||||
|
|
||||||
## v2.21.0 - 2024-06-18
|
## v2.21.0 - 2024-06-18
|
||||||
### Feature
|
### Feature
|
||||||
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
||||||
|
@ -87,7 +87,6 @@
|
|||||||
<li>
|
<li>
|
||||||
{% if bloc.type == 'user' %}
|
{% if bloc.type == 'user' %}
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
hello
|
|
||||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\EventType;
|
use Chill\EventBundle\Form\EventType;
|
||||||
use Chill\EventBundle\Form\Type\PickEventType;
|
use Chill\EventBundle\Form\Type\PickEventType;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
@ -418,7 +418,6 @@ final class EventController extends AbstractController
|
|||||||
$builder->add('event_id', HiddenType::class, [
|
$builder->add('event_id', HiddenType::class, [
|
||||||
'data' => $event->getId(),
|
'data' => $event->getId(),
|
||||||
]);
|
]);
|
||||||
dump($event->getId());
|
|
||||||
|
|
||||||
return $builder->getForm();
|
return $builder->getForm();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\ParticipationType;
|
use Chill\EventBundle\Form\ParticipationType;
|
||||||
use Chill\EventBundle\Repository\EventRepository;
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Chill\PersonBundle\Repository\PersonRepository;
|
use Chill\PersonBundle\Repository\PersonRepository;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
@ -11,8 +11,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\DependencyInjection;
|
namespace Chill\EventBundle\DependencyInjection;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
@ -33,12 +33,13 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
|
|||||||
|
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||||
$loader->load('services.yaml');
|
$loader->load('services.yaml');
|
||||||
$loader->load('services/authorization.yaml');
|
$loader->load('services/security.yaml');
|
||||||
$loader->load('services/fixtures.yaml');
|
$loader->load('services/fixtures.yaml');
|
||||||
$loader->load('services/forms.yaml');
|
$loader->load('services/forms.yaml');
|
||||||
$loader->load('services/repositories.yaml');
|
$loader->load('services/repositories.yaml');
|
||||||
$loader->load('services/search.yaml');
|
$loader->load('services/search.yaml');
|
||||||
$loader->load('services/timeline.yaml');
|
$loader->load('services/timeline.yaml');
|
||||||
|
$loader->load('services/export.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** (non-PHPdoc).
|
/** (non-PHPdoc).
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
<?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\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventDateAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
private const CHOICES = [
|
||||||
|
'by month' => 'month',
|
||||||
|
'by week' => 'week',
|
||||||
|
'by year' => 'year',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const DEFAULT_CHOICE = 'year';
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$order = null;
|
||||||
|
|
||||||
|
switch ($data['frequency']) {
|
||||||
|
case 'month':
|
||||||
|
$fmt = 'YYYY-MM';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'week':
|
||||||
|
$fmt = 'YYYY-IW';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'year':
|
||||||
|
$fmt = 'YYYY';
|
||||||
|
$order = 'DESC';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf("TO_CHAR(event.date, '%s') AS date_aggregator", $fmt));
|
||||||
|
$qb->addGroupBy('date_aggregator');
|
||||||
|
$qb->addOrderBy('date_aggregator', $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('frequency', ChoiceType::class, [
|
||||||
|
'choices' => self::CHOICES,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['frequency' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
return static function ($value) use ($data): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'by '.$data['frequency'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return match ($data['frequency']) {
|
||||||
|
default => $value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return ['date_aggregator'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return 'Group event by date';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
<?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\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventTypeAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'event_type_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected EventTypeRepository $eventTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('eventtype', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event.type', 'eventtype');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event.type) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $t = $this->eventTypeRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($t->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by event type';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
<?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\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class RoleAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'part_role_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected RoleRepository $roleRepository, protected TranslatableStringHelperInterface $translatableStringHelper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('event_part', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event_part.role', 'role');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event_part.role) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Participant role';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $r = $this->roleRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($r->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by participant role';
|
||||||
|
}
|
||||||
|
}
|
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?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\EventBundle\Export;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class declare constants used for the export framework.
|
||||||
|
*/
|
||||||
|
abstract class Declarations
|
||||||
|
{
|
||||||
|
final public const EVENT = 'event';
|
||||||
|
|
||||||
|
final public const EVENT_PARTICIPANTS = 'event_participants';
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
<?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\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\ParticipationRepository;
|
||||||
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEventParticipations implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private ParticipationRepository $participationRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count participants to an event by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event_participants' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Count event participants' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event_participants'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count event participants';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->participationRepository
|
||||||
|
->createQueryBuilder('event_part')
|
||||||
|
->join('event_part.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(event_part.id) as export_count_event_participants');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return ParticipationVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT_PARTICIPANTS,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
128
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
128
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?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\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEvents implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EventRepository $eventRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count events by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Number of events' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->eventRepository
|
||||||
|
->createQueryBuilder('event')
|
||||||
|
->leftJoin('event.participations', 'epart')
|
||||||
|
->leftJoin('epart.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(event.id) as export_count_event');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return EventVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
<?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\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Doctrine\ORM\Query\Expr;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class EventDateFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(protected TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$where = $qb->getDQLPart('where');
|
||||||
|
$clause = $qb->expr()->between(
|
||||||
|
'event.date',
|
||||||
|
':date_from',
|
||||||
|
':date_to'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($where instanceof Expr\Andx) {
|
||||||
|
$where->add($clause);
|
||||||
|
} else {
|
||||||
|
$where = $qb->expr()->andX($clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->add('where', $where);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_from',
|
||||||
|
$this->rollingDateConverter->convert($data['date_from'])
|
||||||
|
);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_to',
|
||||||
|
$this->rollingDateConverter->convert($data['date_to'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('date_from', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events after this date',
|
||||||
|
])
|
||||||
|
->add('date_to', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events before this date',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%',
|
||||||
|
[
|
||||||
|
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
|
||||||
|
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event date';
|
||||||
|
}
|
||||||
|
}
|
@ -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\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class EventTypeFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected EventTypeRepository $eventTypeRepository
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event.type', ':selected_event_types');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_event_types', $data['types']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('types', EntityType::class, [
|
||||||
|
'choices' => $this->eventTypeRepository->findAllActive(),
|
||||||
|
'class' => EventType::class,
|
||||||
|
'choice_label' => fn (EventType $ety) => $this->translatableStringHelper->localize($ety->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$typeNames = array_map(
|
||||||
|
fn (EventType $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||||
|
$this->eventTypeRepository->findBy(['id' => $data['types'] instanceof \Doctrine\Common\Collections\Collection ? $data['types']->toArray() : $data['types']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by event type: only %list%', [
|
||||||
|
'%list%' => implode(', ', $typeNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['types'] || 0 === \count($data['types'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one type must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
95
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
@ -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\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class RoleFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected RoleRepository $roleRepository
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event_part.role', ':selected_part_roles');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_part_roles', $data['part_roles']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('part_roles', EntityType::class, [
|
||||||
|
'choices' => $this->roleRepository->findAllActive(),
|
||||||
|
'class' => Role::class,
|
||||||
|
'choice_label' => fn (Role $r) => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$roleNames = array_map(
|
||||||
|
fn (Role $r): string => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
$this->roleRepository->findBy(['id' => $data['part_roles'] instanceof \Doctrine\Common\Collections\Collection ? $data['part_roles']->toArray() : $data['part_roles']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by participant roles: only %list%', [
|
||||||
|
'%list%' => implode(', ', $roleNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filter by participant roles';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['part_roles'] || 0 === \count($data['part_roles'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one role must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
@ -13,7 +13,7 @@ namespace Chill\EventBundle\Repository;
|
|||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
@ -12,13 +12,57 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Repository;
|
namespace Chill\EventBundle\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Role;
|
use Chill\EventBundle\Entity\Role;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
class RoleRepository extends ServiceEntityRepository
|
readonly class RoleRepository implements ObjectRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelper $translatableStringHelper)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, Role::class);
|
$this->repository = $entityManager->getRepository(Role::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||||
|
{
|
||||||
|
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllActive(): array
|
||||||
|
{
|
||||||
|
$roles = $this->repository->findBy(['active' => true]);
|
||||||
|
|
||||||
|
usort($roles, fn (Role $a, Role $b) => $this->translatableStringHelper->localize($a->getName()) <=> $this->translatableStringHelper->localize($b->getName()));
|
||||||
|
|
||||||
|
return $roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria)
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return Role::class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of EventVoter.
|
* Description of EventVoter.
|
||||||
@ -42,61 +43,46 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Event::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Event' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Event && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $attribute
|
|
||||||
* @param Event $subject
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
{
|
{
|
||||||
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
||||||
@ -118,15 +104,5 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
{
|
{
|
||||||
@ -39,58 +40,48 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_PARTICIPATION_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Participation::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Participation' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Participation && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
* @param Participation $subject
|
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -115,15 +106,5 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<?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\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEventParticipations;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventParticipationsTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEventParticipations $countEventParticipations;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEventParticipations = self::$container->get(CountEventParticipations::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEventParticipations->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?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\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEvents;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEvents $countEvents;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEvents = self::$container->get(CountEvents::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEvents->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
<?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 Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\EventDateAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::$container->get(EventDateAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array|\Generator
|
||||||
|
{
|
||||||
|
yield ['frequency' => 'YYYY'];
|
||||||
|
yield ['frequency' => 'YYYY-MM'];
|
||||||
|
yield ['frequency' => 'YYYY-IV'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
<?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 Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\EventTypeAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::$container->get(EventTypeAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?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 Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\RoleAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::$container->get(RoleAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?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 Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventDateFilter;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RollingDateConverterInterface $rollingDateConverter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return new EventDateFilter($this->rollingDateConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'date_from' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||||
|
'date_to' => new RollingDate(RollingDate::T_TODAY),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
<?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 Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventTypeFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private EventTypeFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->filter = self::$container->get(EventTypeFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter(): EventTypeFilter|\Chill\MainBundle\Export\FilterInterface
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(EventType::class, 'et')
|
||||||
|
->select('et')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'types' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?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 Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Filter\RoleFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RoleFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->filter = self::$container->get(RoleFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(Role::class, 'r')
|
||||||
|
->select('r')
|
||||||
|
->getQuery()
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'roles' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Tests\Repository;
|
namespace Chill\EventBundle\Tests\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
services:
|
|
||||||
chill_event.event_voter:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\EventVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
||||||
|
|
||||||
chill_event.event_participation:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\ParticipationVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
# indicators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Export\CountEvents:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_events' }
|
||||||
|
Chill\EventBundle\Export\Export\CountEventParticipations:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_event_participants' }
|
||||||
|
|
||||||
|
# filters
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventDateFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_date_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventTypeFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_type_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\RoleFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'role_filter' }
|
||||||
|
|
||||||
|
# aggregators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventTypeAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_type_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventDateAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_date_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\RoleAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: role_aggregator }
|
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
services:
|
||||||
|
Chill\EventBundle\Security\EventVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
||||||
|
|
||||||
|
Chill\EventBundle\Security\ParticipationVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
@ -81,9 +81,31 @@ Pick an event: Choisir un événement
|
|||||||
Pick a type of event: Choisir un type d'événement
|
Pick a type of event: Choisir un type d'événement
|
||||||
Pick a moderator: Choisir un animateur
|
Pick a moderator: Choisir un animateur
|
||||||
|
|
||||||
|
# exports
|
||||||
Select a format: Choisir un format
|
Select a format: Choisir un format
|
||||||
Export: Exporter
|
Export: Exporter
|
||||||
|
|
||||||
|
Count events: Nombre d'événements
|
||||||
|
Count events by various parameters.: Compte le nombre d'événements selon divers critères
|
||||||
|
Exports of events: Exports d'événements
|
||||||
|
|
||||||
|
Filtered by event date: Filtrer par date d'événement
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%': "Filtré par date d'événement: uniquement entre le %date_from% et le %date_to%"
|
||||||
|
Events after this date: Événements après cette date
|
||||||
|
Events before this date: Événements avant cette date
|
||||||
|
Filtered by event type: Filtrer par type d'événement
|
||||||
|
'Filtered by event type: only %list%': "Filtré par type: uniquement %list%"
|
||||||
|
Group event by date: Grouper par date d'événement
|
||||||
|
Group by event type: Grouper par type d'événement
|
||||||
|
|
||||||
|
Count event participants: Nombre de participations
|
||||||
|
Count participants to an event by various parameters.: Compte le nombre de participations selon divers critères
|
||||||
|
Exports of event participants: Exports de participations
|
||||||
|
'Filtered by participant roles: only %list%': "Filtré par rôles de participation: uniquement %list%"
|
||||||
|
Filter by participant roles: Filtrer par rôles de participation
|
||||||
|
Part roles: Rôles de participation
|
||||||
|
Group by participant role: Grouper par rôle de participation
|
||||||
|
|
||||||
|
|
||||||
Events configuration: Configuration des événements
|
Events configuration: Configuration des événements
|
||||||
Events configuration menu: Menu des événements
|
Events configuration menu: Menu des événements
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
<?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\Command;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Service\Import\AddressReferenceLU;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class LoadAddressesLUFromBDAddressCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultDescription = 'Import LUX addresses from BD addresses (see https://data.public.lu/fr/datasets/adresses-georeferencees-bd-adresses/)';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly AddressReferenceLU $addressImporter,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('chill:main:address-ref-lux');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$this->addressImporter->import();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
<?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\Service\Import;
|
||||||
|
|
||||||
|
use League\Csv\Reader;
|
||||||
|
use League\Csv\Statement;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
class AddressReferenceLU
|
||||||
|
{
|
||||||
|
private const RELEASE = 'https://data.public.lu/fr/datasets/r/5cadc5b8-6a7d-4283-87bc-f9e58dd771f7';
|
||||||
|
|
||||||
|
public function __construct(private readonly HttpClientInterface $client, private readonly AddressReferenceBaseImporter $addressBaseImporter, private readonly PostalCodeBaseImporter $postalCodeBaseImporter, private readonly AddressToReferenceMatcher $addressToReferenceMatcher)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function import(): void
|
||||||
|
{
|
||||||
|
$downloadUrl = self::RELEASE;
|
||||||
|
|
||||||
|
$response = $this->client->request('GET', $downloadUrl);
|
||||||
|
|
||||||
|
if (200 !== $response->getStatusCode()) {
|
||||||
|
throw new \Exception('Could not download CSV: '.$response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = tmpfile();
|
||||||
|
|
||||||
|
foreach ($this->client->stream($response) as $chunk) {
|
||||||
|
fwrite($file, $chunk->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek($file, 0);
|
||||||
|
|
||||||
|
$csv = Reader::createFromStream($file);
|
||||||
|
$csv->setDelimiter(';');
|
||||||
|
$csv->setHeaderOffset(0);
|
||||||
|
|
||||||
|
$this->process_postal_code($csv);
|
||||||
|
|
||||||
|
$this->process_address($csv);
|
||||||
|
|
||||||
|
$this->addressToReferenceMatcher->checkAddressesMatchingReferences();
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process_address(Reader $csv): void
|
||||||
|
{
|
||||||
|
$stmt = Statement::create()->process($csv);
|
||||||
|
foreach ($stmt as $record) {
|
||||||
|
$this->addressBaseImporter->importAddress(
|
||||||
|
$record['id_geoportail'],
|
||||||
|
$record['code_postal'],
|
||||||
|
$record['code_postal'],
|
||||||
|
$record['rue'],
|
||||||
|
$record['numero'],
|
||||||
|
'bd-addresses.lux',
|
||||||
|
(float) $record['lat_wgs84'],
|
||||||
|
(float) $record['lon_wgs84'],
|
||||||
|
4326
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addressBaseImporter->finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process_postal_code(Reader $csv): void
|
||||||
|
{
|
||||||
|
$stmt = Statement::create()->process($csv);
|
||||||
|
$arr_postal_codes = [];
|
||||||
|
foreach ($stmt as $record) {
|
||||||
|
if (false === \array_key_exists($record['code_postal'], $arr_postal_codes)) {
|
||||||
|
$this->postalCodeBaseImporter->importCode(
|
||||||
|
'LU',
|
||||||
|
trim((string) $record['localite']),
|
||||||
|
trim((string) $record['code_postal']),
|
||||||
|
trim((string) $record['code_postal']),
|
||||||
|
'bd-addresses.lux',
|
||||||
|
(float) $record['lat_wgs84'],
|
||||||
|
(float) $record['lon_wgs84'],
|
||||||
|
4326
|
||||||
|
);
|
||||||
|
$arr_postal_codes[$record['code_postal']] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -59,6 +59,12 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: console.command }
|
- { name: console.command }
|
||||||
|
|
||||||
|
Chill\MainBundle\Command\LoadAddressesLUFromBDAddressCommand:
|
||||||
|
autoconfigure: true
|
||||||
|
autowire: true
|
||||||
|
tags:
|
||||||
|
- { name: console.command }
|
||||||
|
|
||||||
Chill\MainBundle\Command\ExecuteCronJobCommand:
|
Chill\MainBundle\Command\ExecuteCronJobCommand:
|
||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
autowire: true
|
autowire: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user