mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-09 13:48:23 +00:00
Compare commits
2 Commits
456-doc-ge
...
449-scope-
| Author | SHA1 | Date | |
|---|---|---|---|
| 3641e904d2 | |||
| 9db1a960db |
6
.changes/unreleased/UX-20251030-153126.yaml
Normal file
6
.changes/unreleased/UX-20251030-153126.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: UX
|
||||||
|
body: Remove the label if there is only one scope and no scope picking field is displayed.
|
||||||
|
time: 2025-10-30T15:31:26.807444365+01:00
|
||||||
|
custom:
|
||||||
|
Issue: "449"
|
||||||
|
SchemaChange: No schema change
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Display whether doc generation template is active or not in admin and order templates alphabetically
|
|
||||||
time: 2025-11-03T16:19:10.051947925+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "456"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -88,8 +88,8 @@ class ActivityType extends AbstractType
|
|||||||
|
|
||||||
if (null !== $options['data']->getPerson()) {
|
if (null !== $options['data']->getPerson()) {
|
||||||
$builder->add('scope', ScopePickerType::class, [
|
$builder->add('scope', ScopePickerType::class, [
|
||||||
'center' => $options['center'],
|
|
||||||
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
|
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
|
||||||
|
'center' => $options['center'],
|
||||||
'required' => true,
|
'required' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,24 +25,12 @@
|
|||||||
<div class="item-bloc">
|
<div class="item-bloc">
|
||||||
<div class="item-row">
|
<div class="item-row">
|
||||||
<div class="item-col" style="flex-basis:100%;">
|
<div class="item-col" style="flex-basis:100%;">
|
||||||
<h2>{{ entity.name|localize_translatable_string }} </h2>
|
<h2>{{ entity.name|localize_translatable_string }}</h2>
|
||||||
<p style="margin-left: 1rem;"><span class="badge bg-chill-gray">
|
|
||||||
{% if entity.active %}
|
|
||||||
{{ 'admin.active'|trans }}
|
|
||||||
{% else %}
|
|
||||||
{{ 'admin.not active'|trans }}
|
|
||||||
{% endif %}
|
|
||||||
</span></p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-row">
|
<div class="item-row">
|
||||||
<p><span class="badge bg-chill-green-dark">{{ contextManager.getContextByKey(entity.context).name|trans }}</span></p>
|
<p><span class="badge bg-chill-green-dark">{{ contextManager.getContextByKey(entity.context).name|trans }}</span></p>
|
||||||
</div>
|
</div>
|
||||||
{# <div class="item-row">#}
|
|
||||||
{# <div class="item-col" style="flex-basis:100%;">#}
|
|
||||||
{##}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
<div class="item-row">
|
<div class="item-row">
|
||||||
<div class="item-col"></div>
|
<div class="item-col"></div>
|
||||||
<ul class="record_actions item-col flex-shrink-1">
|
<ul class="record_actions item-col flex-shrink-1">
|
||||||
|
|||||||
@@ -49,7 +49,3 @@ crud:
|
|||||||
|
|
||||||
|
|
||||||
Template file: Fichier modèle
|
Template file: Fichier modèle
|
||||||
|
|
||||||
admin:
|
|
||||||
active: Actif
|
|
||||||
not active: Non-actif
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use Chill\DocStoreBundle\Entity\PersonDocument;
|
|||||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
@@ -27,10 +28,11 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class PersonDocumentType extends AbstractType
|
class PersonDocumentType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly ScopeResolverDispatcher $scopeResolverDispatcher, private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcher $centerResolverDispatcher) {}
|
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly Security $security, private readonly AuthorizationHelperInterface $authorizationHelper, private readonly ScopeResolverDispatcher $scopeResolverDispatcher, private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcher $centerResolverDispatcher) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
@@ -57,8 +59,8 @@ class PersonDocumentType extends AbstractType
|
|||||||
|
|
||||||
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
||||||
$builder->add('scope', ScopePickerType::class, [
|
$builder->add('scope', ScopePickerType::class, [
|
||||||
'center' => $this->centerResolverDispatcher->resolveCenter($document),
|
|
||||||
'role' => $options['role'],
|
'role' => $options['role'],
|
||||||
|
'subject' => $document,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Scope;
|
|||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
|
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
@@ -32,65 +33,84 @@ use Symfony\Component\Security\Core\Security;
|
|||||||
* Allow to pick amongst available scope for the current
|
* Allow to pick amongst available scope for the current
|
||||||
* user.
|
* user.
|
||||||
*
|
*
|
||||||
* options :
|
* Options:
|
||||||
*
|
* - `role`: string, the role to check permissions for
|
||||||
* - `center`: the center of the entity
|
* - Either `subject`: object, entity to resolve centers from
|
||||||
* - `role` : the role of the user
|
* - Or `center`: Center|array|null, the center(s) to check
|
||||||
*/
|
*/
|
||||||
class ScopePickerType extends AbstractType
|
class ScopePickerType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly AuthorizationHelperInterface $authorizationHelper,
|
private readonly AuthorizationHelperInterface $authorizationHelper,
|
||||||
private readonly Security $security,
|
private readonly Security $security,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly CenterResolverManagerInterface $centerResolverManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$items = array_values(
|
// Compute centers from subject
|
||||||
|
$centers = $options['center'] ?? null;
|
||||||
|
if (null === $centers && isset($options['subject'])) {
|
||||||
|
$centers = $this->centerResolverManager->resolveCenters($options['subject']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $centers) {
|
||||||
|
throw new \RuntimeException('Either "center" or "subject" must be provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
$reachableScopes = array_values(
|
||||||
array_filter(
|
array_filter(
|
||||||
$this->authorizationHelper->getReachableScopes(
|
$this->authorizationHelper->getReachableScopes(
|
||||||
$this->security->getUser(),
|
$this->security->getUser(),
|
||||||
$options['role'],
|
$options['role'],
|
||||||
$options['center']
|
$centers
|
||||||
),
|
),
|
||||||
static fn (Scope $s) => $s->isActive()
|
static fn (Scope $s) => $s->isActive()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (0 === \count($items)) {
|
$builder->setAttribute('reachable_scopes_count', count($reachableScopes));
|
||||||
throw new \RuntimeException('no scopes are reachable. This form should not be shown to user');
|
|
||||||
|
if (0 === count($reachableScopes)) {
|
||||||
|
$builder->setAttribute('has_scopes', false);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1 !== \count($items)) {
|
$builder->setAttribute('has_scopes', true);
|
||||||
|
|
||||||
|
if (1 !== count($reachableScopes)) {
|
||||||
$builder->add('scope', EntityType::class, [
|
$builder->add('scope', EntityType::class, [
|
||||||
'class' => Scope::class,
|
'class' => Scope::class,
|
||||||
'placeholder' => 'Choose the circle',
|
'placeholder' => 'Choose the circle',
|
||||||
'choice_label' => fn (Scope $c) => $this->translatableStringHelper->localize($c->getName()),
|
'choice_label' => fn (Scope $c) => $this->translatableStringHelper->localize($c->getName()),
|
||||||
'choices' => $items,
|
'choices' => $reachableScopes,
|
||||||
]);
|
]);
|
||||||
$builder->setDataMapper(new ScopePickerDataMapper());
|
$builder->setDataMapper(new ScopePickerDataMapper());
|
||||||
} else {
|
} else {
|
||||||
$builder->add('scope', HiddenType::class, [
|
$builder->add('scope', HiddenType::class, [
|
||||||
'data' => $items[0]->getId(),
|
'data' => $reachableScopes[0]->getId(),
|
||||||
]);
|
]);
|
||||||
$builder->setDataMapper(new ScopePickerDataMapper($items[0]));
|
$builder->setDataMapper(new ScopePickerDataMapper($reachableScopes[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||||
{
|
{
|
||||||
$view->vars['fullWidth'] = true;
|
$view->vars['fullWidth'] = true;
|
||||||
|
// display of label is handled by the EntityType
|
||||||
|
$view->vars['label'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver
|
$resolver
|
||||||
// create `center` option
|
|
||||||
->setRequired('center')
|
|
||||||
->setAllowedTypes('center', [Center::class, 'array', 'null'])
|
|
||||||
// create ``role` option
|
|
||||||
->setRequired('role')
|
->setRequired('role')
|
||||||
->setAllowedTypes('role', ['string']);
|
->setAllowedTypes('role', ['string'])
|
||||||
|
->setDefined('subject')
|
||||||
|
->setAllowedTypes('subject', ['object'])
|
||||||
|
->setDefined('center')
|
||||||
|
->setAllowedTypes('center', [Center::class, 'array', 'null']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Form\Type;
|
namespace Form\Type;
|
||||||
|
|
||||||
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;
|
||||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
|
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
|
||||||
@@ -39,11 +39,11 @@ final class ScopePickerTypeTest extends TypeTestCase
|
|||||||
{
|
{
|
||||||
use ProphecyTrait;
|
use ProphecyTrait;
|
||||||
|
|
||||||
public function estBuildOneScopeIsSuccessful()
|
public function testBuildOneScopeIsSuccessful()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||||
'center' => new Center(),
|
|
||||||
'role' => 'ONE_SCOPE',
|
'role' => 'ONE_SCOPE',
|
||||||
|
'center' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$view = $form->createView();
|
$view = $form->createView();
|
||||||
@@ -54,8 +54,8 @@ final class ScopePickerTypeTest extends TypeTestCase
|
|||||||
public function testBuildThreeScopesIsSuccessful()
|
public function testBuildThreeScopesIsSuccessful()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||||
'center' => new Center(),
|
|
||||||
'role' => 'THREE_SCOPE',
|
'role' => 'THREE_SCOPE',
|
||||||
|
'center' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$view = $form->createView();
|
$view = $form->createView();
|
||||||
@@ -66,8 +66,8 @@ final class ScopePickerTypeTest extends TypeTestCase
|
|||||||
public function testBuildTwoScopesIsSuccessful()
|
public function testBuildTwoScopesIsSuccessful()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||||
'center' => new Center(),
|
|
||||||
'role' => 'TWO_SCOPE',
|
'role' => 'TWO_SCOPE',
|
||||||
|
'center' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$view = $form->createView();
|
$view = $form->createView();
|
||||||
@@ -101,10 +101,13 @@ final class ScopePickerTypeTest extends TypeTestCase
|
|||||||
static fn ($args) => $args[0]['fr']
|
static fn ($args) => $args[0]['fr']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$centerResolverManager = $this->prophesize(CenterResolverManagerInterface::class);
|
||||||
|
|
||||||
$type = new ScopePickerType(
|
$type = new ScopePickerType(
|
||||||
|
$translatableStringHelper->reveal(),
|
||||||
$authorizationHelper->reveal(),
|
$authorizationHelper->reveal(),
|
||||||
$security->reveal(),
|
$security->reveal(),
|
||||||
$translatableStringHelper->reveal()
|
$centerResolverManager->reveal()
|
||||||
);
|
);
|
||||||
|
|
||||||
// add the mocks for creating EntityType
|
// add the mocks for creating EntityType
|
||||||
|
|||||||
@@ -168,9 +168,8 @@ final readonly class PersonContext implements PersonContextInterface
|
|||||||
|
|
||||||
if ($this->isScopeNecessary($entity)) {
|
if ($this->isScopeNecessary($entity)) {
|
||||||
$builder->add('scope', ScopePickerType::class, [
|
$builder->add('scope', ScopePickerType::class, [
|
||||||
'center' => $this->centerResolverManager->resolveCenters($entity),
|
|
||||||
'role' => PersonDocumentVoter::CREATE,
|
'role' => PersonDocumentVoter::CREATE,
|
||||||
'label' => 'Scope',
|
'subject' => $entity,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\ChillTextareaType;
|
|||||||
use Chill\MainBundle\Form\Type\DateIntervalType;
|
use Chill\MainBundle\Form\Type\DateIntervalType;
|
||||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||||
use Chill\MainBundle\Form\Type\UserPickerType;
|
use Chill\MainBundle\Form\Type\UserPickerType;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||||
@@ -24,10 +25,17 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class SingleTaskType extends AbstractType
|
class SingleTaskType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcherInterface $centerResolverDispatcher, private readonly ScopeResolverDispatcher $scopeResolverDispatcher) {}
|
public function __construct(
|
||||||
|
private readonly ParameterBagInterface $parameterBag,
|
||||||
|
private readonly CenterResolverDispatcherInterface $centerResolverDispatcher,
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly AuthorizationHelperInterface $authorizationHelper,
|
||||||
|
private readonly ScopeResolverDispatcher $scopeResolverDispatcher,
|
||||||
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
@@ -64,8 +72,8 @@ class SingleTaskType extends AbstractType
|
|||||||
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
||||||
$builder
|
$builder
|
||||||
->add('circle', ScopePickerType::class, [
|
->add('circle', ScopePickerType::class, [
|
||||||
'center' => $center,
|
|
||||||
'role' => $options['role'],
|
'role' => $options['role'],
|
||||||
|
'subject' => $task,
|
||||||
'required' => true,
|
'required' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
{% block title 'Tasks for {{ name }}'|trans({ '{{ name }}' : person|chill_entity_render_string }) %}
|
{% block title 'Tasks for {{ name }}'|trans({ '{{ name }}' : person|chill_entity_render_string }) %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-md-10 col-xxl">
|
<div class="task-list"">
|
||||||
|
|
||||||
<h1>{{ block('title') }}</h1>
|
<h1>{{ block('title') }}</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-md-10 col-xxl tasks">
|
<div class="col-md-9 col-xxl tasks">
|
||||||
{% include '@ChillTask/SingleTask/AccompanyingCourse/list.html.twig' %}
|
{% include '@ChillTask/SingleTask/AccompanyingCourse/list.html.twig' %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user