mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-03-11 16:37:46 +00:00
Partage d'export enregistré et génération asynchrone des exports
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
<?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\Form\DataMapper;
|
||||
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
final readonly class ExportPickCenterDataMapper implements DataMapperInterface
|
||||
{
|
||||
public function mapDataToForms($viewData, \Traversable $forms): void
|
||||
{
|
||||
if (null === $viewData) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<string, FormInterface> $form */
|
||||
$form = iterator_to_array($forms);
|
||||
|
||||
$form['center']->setData($viewData);
|
||||
|
||||
// NOTE: we do not map back the regroupments
|
||||
}
|
||||
|
||||
public function mapFormsToData(\Traversable $forms, &$viewData): void
|
||||
{
|
||||
/** @var array<string, FormInterface> $forms */
|
||||
$forms = iterator_to_array($forms);
|
||||
|
||||
$centers = [];
|
||||
|
||||
foreach ($forms['center']->getData() as $center) {
|
||||
$centers[spl_object_hash($center)] = $center;
|
||||
}
|
||||
|
||||
if (\array_key_exists('regroupment', $forms)) {
|
||||
/** @var Regroupment $regroupment */
|
||||
foreach ($forms['regroupment']->getData() as $regroupment) {
|
||||
foreach ($regroupment->getCenters() as $center) {
|
||||
$centers[spl_object_hash($center)] = $center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$viewData = array_values($centers);
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,22 @@ namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\SavedExport;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\PickUserGroupOrUserDynamicType;
|
||||
use Chill\MainBundle\Security\Authorization\SavedExportVoter;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class SavedExportType extends AbstractType
|
||||
{
|
||||
public function __construct(private readonly Security $security) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$savedExport = $options['data'];
|
||||
|
||||
$builder
|
||||
->add('title', TextType::class, [
|
||||
'required' => true,
|
||||
@@ -29,6 +36,14 @@ class SavedExportType extends AbstractType
|
||||
->add('description', ChillTextareaType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
if ($this->security->isGranted(SavedExportVoter::SHARE, $savedExport)) {
|
||||
$builder->add('share', PickUserGroupOrUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'label' => 'saved_export.Share',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
|
||||
@@ -66,8 +66,11 @@ class EntityToJsonTransformer implements DataTransformerInterface
|
||||
]);
|
||||
}
|
||||
|
||||
private function denormalizeOne(array $item)
|
||||
private function denormalizeOne(array|string $item)
|
||||
{
|
||||
if ('me' === $item) {
|
||||
return $item;
|
||||
}
|
||||
if (!\array_key_exists('type', $item)) {
|
||||
throw new TransformationFailedException('the key "type" is missing on element');
|
||||
}
|
||||
@@ -98,5 +101,6 @@ class EntityToJsonTransformer implements DataTransformerInterface
|
||||
'json',
|
||||
$context,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,18 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AggregatorType extends AbstractType
|
||||
{
|
||||
public const ENABLED_FIELD = 'enabled';
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$exportManager = $options['export_manager'];
|
||||
$aggregator = $exportManager->getAggregator($options['aggregator_alias']);
|
||||
|
||||
$builder
|
||||
->add('enabled', CheckboxType::class, [
|
||||
->add(self::ENABLED_FIELD, CheckboxType::class, [
|
||||
'value' => true,
|
||||
'required' => false,
|
||||
'disabled' => $options['disable_enable_field'],
|
||||
]);
|
||||
|
||||
$aggregatorFormBuilder = $builder->create('form', FormType::class, [
|
||||
@@ -53,6 +56,7 @@ class AggregatorType extends AbstractType
|
||||
{
|
||||
$resolver->setRequired('aggregator_alias')
|
||||
->setRequired('export_manager')
|
||||
->setDefault('disable_enable_field', false)
|
||||
->setDefault('compound', true)
|
||||
->setDefault('error_bubbling', false);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class ExportType extends AbstractType
|
||||
public function __construct(
|
||||
private readonly ExportManager $exportManager,
|
||||
private readonly SortExportElement $sortExportElement,
|
||||
protected ParameterBagInterface $parameterBag,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->personFieldsConfig = $parameterBag->get('chill_person.person_fields');
|
||||
}
|
||||
@@ -43,6 +43,8 @@ class ExportType extends AbstractType
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$export = $this->exportManager->getExport($options['export_alias']);
|
||||
/** @var bool $canEditFull */
|
||||
$canEditFull = $options['can_edit_full'];
|
||||
|
||||
$exportOptions = [
|
||||
'compound' => true,
|
||||
@@ -59,8 +61,18 @@ class ExportType extends AbstractType
|
||||
|
||||
if ($export instanceof \Chill\MainBundle\Export\ExportInterface) {
|
||||
// add filters
|
||||
$filters = $this->exportManager->getFiltersApplyingOn($export, $options['picked_centers']);
|
||||
$filterAliases = $options['allowed_filters'];
|
||||
$filters = [];
|
||||
if (is_iterable($filterAliases)) {
|
||||
foreach ($filterAliases as $alias => $filter) {
|
||||
$filters[$alias] = $filter;
|
||||
}
|
||||
} else {
|
||||
$filters = $this->exportManager->getFiltersApplyingOn($export, $options['picked_centers']);
|
||||
}
|
||||
|
||||
$this->sortExportElement->sortFilters($filters);
|
||||
|
||||
$filterBuilder = $builder->create(self::FILTER_KEY, FormType::class, ['compound' => true]);
|
||||
|
||||
foreach ($filters as $alias => $filter) {
|
||||
@@ -70,15 +82,26 @@ class ExportType extends AbstractType
|
||||
'constraints' => [
|
||||
new ExportElementConstraint(['element' => $filter]),
|
||||
],
|
||||
'disable_enable_field' => !$canEditFull,
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->add($filterBuilder);
|
||||
|
||||
// add aggregators
|
||||
$aggregators = $this->exportManager
|
||||
->getAggregatorsApplyingOn($export, $options['picked_centers']);
|
||||
$aggregatorsAliases = $options['allowed_aggregators'];
|
||||
$aggregators = [];
|
||||
if (is_iterable($aggregatorsAliases)) {
|
||||
foreach ($aggregatorsAliases as $alias => $aggregator) {
|
||||
$aggregators[$alias] = $aggregator;
|
||||
}
|
||||
} else {
|
||||
$aggregators = $this->exportManager
|
||||
->getAggregatorsApplyingOn($export, $options['picked_centers']);
|
||||
}
|
||||
|
||||
$this->sortExportElement->sortAggregators($aggregators);
|
||||
|
||||
$aggregatorBuilder = $builder->create(
|
||||
self::AGGREGATOR_KEY,
|
||||
FormType::class,
|
||||
@@ -96,11 +119,11 @@ class ExportType extends AbstractType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$aggregatorBuilder->add($alias, AggregatorType::class, [
|
||||
'aggregator_alias' => $alias,
|
||||
'export_manager' => $this->exportManager,
|
||||
'label' => $aggregator->getTitle(),
|
||||
'disable_enable_field' => !$canEditFull,
|
||||
'constraints' => [
|
||||
new ExportElementConstraint(['element' => $aggregator]),
|
||||
],
|
||||
@@ -125,8 +148,13 @@ class ExportType extends AbstractType
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setRequired(['export_alias', 'picked_centers'])
|
||||
$resolver->setRequired(['export_alias', 'picked_centers', 'can_edit_full'])
|
||||
->setAllowedTypes('export_alias', ['string'])
|
||||
->setAllowedValues('can_edit_full', [true, false])
|
||||
->setDefault('allowed_filters', null)
|
||||
->setAllowedTypes('allowed_filters', ['iterable', 'null'])
|
||||
->setDefault('allowed_aggregators', null)
|
||||
->setAllowedTypes('allowed_aggregators', ['iterable', 'null'])
|
||||
->setDefault('compound', true)
|
||||
->setDefault('constraints', [
|
||||
// new \Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraint()
|
||||
|
||||
@@ -34,6 +34,7 @@ class FilterType extends AbstractType
|
||||
->add(self::ENABLED_FIELD, CheckboxType::class, [
|
||||
'value' => true,
|
||||
'required' => false,
|
||||
'disabled' => $options['disable_enable_field'],
|
||||
]);
|
||||
|
||||
$filterFormBuilder = $builder->create('form', FormType::class, [
|
||||
@@ -58,6 +59,7 @@ class FilterType extends AbstractType
|
||||
$resolver
|
||||
->setRequired('filter')
|
||||
->setAllowedTypes('filter', [FilterInterface::class])
|
||||
->setDefault('disable_enable_field', false)
|
||||
->setDefault('compound', true)
|
||||
->setDefault('error_bubbling', false);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Chill\MainBundle\Form\Type\Export;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Chill\MainBundle\Export\ExportManager;
|
||||
use Chill\MainBundle\Form\DataMapper\ExportPickCenterDataMapper;
|
||||
use Chill\MainBundle\Repository\RegroupmentRepository;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||
use Chill\MainBundle\Service\Regroupement\RegroupementFiltering;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -27,27 +27,26 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
*/
|
||||
final class PickCenterType extends AbstractType
|
||||
{
|
||||
public const CENTERS_IDENTIFIERS = 'c';
|
||||
|
||||
public function __construct(
|
||||
private readonly ExportManager $exportManager,
|
||||
private readonly RegroupmentRepository $regroupmentRepository,
|
||||
private readonly AuthorizationHelperForCurrentUserInterface $authorizationHelper,
|
||||
private readonly RegroupementFiltering $regroupementFiltering,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$export = $this->exportManager->getExport($options['export_alias']);
|
||||
$centers = $this->authorizationHelper->getReachableCenters(
|
||||
$export->requiredRole()
|
||||
$export->requiredRole(),
|
||||
);
|
||||
|
||||
$centersActive = array_filter($centers, fn (Center $c) => $c->getIsActive());
|
||||
|
||||
// order alphabetically
|
||||
usort($centersActive, fn (Center $a, Center $b) => $a->getCenter() <=> $b->getName());
|
||||
usort($centersActive, fn (Center $a, Center $b) => $a->getName() <=> $b->getName());
|
||||
|
||||
$builder->add('center', EntityType::class, [
|
||||
$builder->add('centers', EntityType::class, [
|
||||
'class' => Center::class,
|
||||
'choices' => $centersActive,
|
||||
'label' => 'center',
|
||||
@@ -56,18 +55,22 @@ final class PickCenterType extends AbstractType
|
||||
'choice_label' => static fn (Center $c) => $c->getName(),
|
||||
]);
|
||||
|
||||
if (\count($this->regroupmentRepository->findAllActive()) > 0) {
|
||||
$builder->add('regroupment', EntityType::class, [
|
||||
$groups = $this->regroupementFiltering
|
||||
->filterContainsAtLeastOneCenter($this->regroupmentRepository->findAllActive(), $centersActive);
|
||||
|
||||
// order alphabetically
|
||||
usort($groups, fn (Regroupment $a, Regroupment $b) => $a->getName() <=> $b->getName());
|
||||
|
||||
if (\count($groups) > 0) {
|
||||
$builder->add('regroupments', EntityType::class, [
|
||||
'class' => Regroupment::class,
|
||||
'label' => 'regroupment',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'choices' => $this->regroupmentRepository->findAllActive(),
|
||||
'choices' => $groups,
|
||||
'choice_label' => static fn (Regroupment $r) => $r->getName(),
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->setDataMapper(new ExportPickCenterDataMapper());
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\EntityToJsonTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* Pick user dymically, using vuejs module "AddPerson".
|
||||
*
|
||||
* Possible options:
|
||||
*
|
||||
* - `multiple`: pick one or more users
|
||||
* - `suggested`: a list of suggested users
|
||||
* - `suggest_myself`: append the current user to the list of suggested
|
||||
* - `as_id`: only the id will be set in the returned data
|
||||
* - `submit_on_adding_new_entity`: the browser will immediately submit the form when new users are checked
|
||||
*/
|
||||
class PickUserOrMeDynamicType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DenormalizerInterface $denormalizer,
|
||||
private readonly SerializerInterface $serializer,
|
||||
private readonly NormalizerInterface $normalizer,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer(new EntityToJsonTransformer($this->denormalizer, $this->serializer, $options['multiple'], 'user'));
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['multiple'] = $options['multiple'];
|
||||
$view->vars['types'] = ['user'];
|
||||
$view->vars['uniqid'] = uniqid('pick_user_or_me_dyn');
|
||||
$view->vars['suggested'] = [];
|
||||
$view->vars['as_id'] = true === $options['as_id'] ? '1' : '0';
|
||||
$view->vars['submit_on_adding_new_entity'] = true === $options['submit_on_adding_new_entity'] ? '1' : '0';
|
||||
|
||||
foreach ($options['suggested'] as $user) {
|
||||
$view->vars['suggested'][] = $this->normalizer->normalize($user, 'json', ['groups' => 'read']);
|
||||
}
|
||||
// $user = /* should come from context */ $options['context'];
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false)
|
||||
->setDefault('suggested', [])
|
||||
// if set to true, only the id will be set inside the content. The denormalization will not work.
|
||||
->setDefault('as_id', false)
|
||||
->setAllowedTypes('as_id', ['bool'])
|
||||
->setDefault('submit_on_adding_new_entity', false)
|
||||
->setAllowedTypes('submit_on_adding_new_entity', ['bool']);
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'pick_entity_dynamic';
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
@@ -23,6 +24,9 @@ class UserGroupType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $options['data'];
|
||||
|
||||
$builder
|
||||
->add('label', TranslatableStringFormType::class, [
|
||||
'label' => 'user_group.Label',
|
||||
@@ -46,20 +50,25 @@ class UserGroupType extends AbstractType
|
||||
'help' => 'user_group.ExcludeKeyHelp',
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
])
|
||||
->add('users', PickUserDynamicType::class, [
|
||||
'label' => 'user_group.Users',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'empty_data' => [],
|
||||
])
|
||||
->add('adminUsers', PickUserDynamicType::class, [
|
||||
'label' => 'user_group.adminUsers',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'empty_data' => [],
|
||||
'help' => 'user_group.adminUsersHelp',
|
||||
])
|
||||
;
|
||||
]);
|
||||
|
||||
if (!$userGroup->hasUserJob()) {
|
||||
$builder
|
||||
->add('users', PickUserDynamicType::class, [
|
||||
'label' => 'user_group.Users',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'empty_data' => [],
|
||||
])
|
||||
->add('adminUsers', PickUserDynamicType::class, [
|
||||
'label' => 'user_group.adminUsers',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'empty_data' => [],
|
||||
'help' => 'user_group.adminUsersHelp',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user