Remove the label if there is only one scope and no scope picking field is displayed.

This commit is contained in:
2025-10-30 15:31:49 +01:00
parent bf38ec22c9
commit 9db1a960db
7 changed files with 102 additions and 41 deletions

View 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

View File

@@ -18,6 +18,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\DocStoreBundle\Form\CollectionStoredObjectType; use Chill\DocStoreBundle\Form\CollectionStoredObjectType;
use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\CommentType; use Chill\MainBundle\Form\Type\CommentType;
@@ -47,6 +48,7 @@ use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security;
class ActivityType extends AbstractType class ActivityType extends AbstractType
{ {
@@ -60,6 +62,7 @@ class ActivityType extends AbstractType
protected array $timeChoices, protected array $timeChoices,
protected SocialIssueRender $socialIssueRender, protected SocialIssueRender $socialIssueRender,
protected SocialActionRender $socialActionRender, protected SocialActionRender $socialActionRender,
private readonly Security $security,
) { ) {
if (!$tokenStorage->getToken()->getUser() instanceof User) { if (!$tokenStorage->getToken()->getUser() instanceof User) {
throw new \RuntimeException('you should have a valid user'); throw new \RuntimeException('you should have a valid user');
@@ -87,10 +90,22 @@ class ActivityType extends AbstractType
$activityType = $options['activityType']; $activityType = $options['activityType'];
if (null !== $options['data']->getPerson()) { if (null !== $options['data']->getPerson()) {
$reachableScopes = array_values(
array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
$options['center']
),
static fn (Scope $s) => $s->isActive()
)
);
$builder->add('scope', ScopePickerType::class, [ $builder->add('scope', ScopePickerType::class, [
'center' => $options['center'], 'reachable_scopes' => $reachableScopes,
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
'required' => true, 'required' => true,
'label' => count($reachableScopes) > 1 ? 'Scope' : false,
]); ]);
} }

View File

@@ -14,9 +14,11 @@ namespace Chill\DocStoreBundle\Form;
use Chill\DocStoreBundle\Entity\Document; use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\DocumentCategory; use Chill\DocStoreBundle\Entity\DocumentCategory;
use Chill\DocStoreBundle\Entity\PersonDocument; use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\MainBundle\Entity\Scope;
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 +29,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)
{ {
@@ -56,9 +59,19 @@ 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']) {
$reachableScopes = array_values(
array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
$options['role'],
$this->centerResolverDispatcher->resolveCenter($document)
),
static fn (Scope $s) => $s->isActive()
)
);
$builder->add('scope', ScopePickerType::class, [ $builder->add('scope', ScopePickerType::class, [
'center' => $this->centerResolverDispatcher->resolveCenter($document), 'reachable_scopes' => $reachableScopes,
'role' => $options['role'], 'label' => count($reachableScopes) > 1 ? 'Scope' : false,
]); ]);
} }
} }

View File

@@ -40,41 +40,39 @@ use Symfony\Component\Security\Core\Security;
class ScopePickerType extends AbstractType class ScopePickerType extends AbstractType
{ {
public function __construct( public function __construct(
private readonly AuthorizationHelperInterface $authorizationHelper,
private readonly Security $security,
private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatableStringHelperInterface $translatableStringHelper,
) {} ) {}
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$items = array_values( /* $items = array_values(
array_filter( array_filter(
$this->authorizationHelper->getReachableScopes( $this->authorizationHelper->getReachableScopes(
$this->security->getUser(), $this->security->getUser(),
$options['role'], $options['role'],
$options['center'] $options['center']
), ),
static fn (Scope $s) => $s->isActive() static fn (Scope $s) => $s->isActive()
) )
); );*/
if (0 === \count($items)) { if (0 === \count($options['reachable_scopes'])) {
throw new \RuntimeException('no scopes are reachable. This form should not be shown to user'); throw new \RuntimeException('no scopes are reachable. This form should not be shown to user');
} }
if (1 !== \count($items)) { if (1 !== \count($options['reachable_scopes'])) {
$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' => $options['reachable_scopes'],
]); ]);
$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' => $options['reachable_scopes'][0]->getId(),
]); ]);
$builder->setDataMapper(new ScopePickerDataMapper($items[0])); $builder->setDataMapper(new ScopePickerDataMapper($options['reachable_scopes'][0]));
} }
} }
@@ -86,11 +84,13 @@ class ScopePickerType extends AbstractType
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver $resolver
// create `center` option ->setRequired('reachable_scopes')
->setRequired('center') ->setAllowedTypes('reachable_scopes', ['array']);
->setAllowedTypes('center', [Center::class, 'array', 'null']) // create `center` option
// create ``role` option // ->setRequired('center')
->setRequired('role') // ->setAllowedTypes('center', [Center::class, 'array', 'null'])
->setAllowedTypes('role', ['string']); // create ``role` option
// ->setRequired('role')
// ->setAllowedTypes('role', ['string']);
} }
} }

View File

@@ -11,7 +11,6 @@ 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;
@@ -42,8 +41,7 @@ final class ScopePickerTypeTest extends TypeTestCase
public function estBuildOneScopeIsSuccessful() public function estBuildOneScopeIsSuccessful()
{ {
$form = $this->factory->create(ScopePickerType::class, null, [ $form = $this->factory->create(ScopePickerType::class, null, [
'center' => new Center(), 'reachable_scopes' => [new Scope()],
'role' => 'ONE_SCOPE',
]); ]);
$view = $form->createView(); $view = $form->createView();
@@ -54,8 +52,7 @@ 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(), 'reachable_scopes' => [new Scope(), new Scope(), new Scope()],
'role' => 'THREE_SCOPE',
]); ]);
$view = $form->createView(); $view = $form->createView();
@@ -66,8 +63,7 @@ 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(), 'reachable_scopes' => [new Scope(), new Scope()],
'role' => 'TWO_SCOPE',
]); ]);
$view = $form->createView(); $view = $form->createView();

View File

@@ -167,10 +167,20 @@ final readonly class PersonContext implements PersonContextInterface
} }
if ($this->isScopeNecessary($entity)) { if ($this->isScopeNecessary($entity)) {
$reachableScopes = array_values(
array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
PersonDocumentVoter::CREATE,
$this->centerResolverManager->resolveCenters($entity)
),
static fn (Scope $s) => $s->isActive()
)
);
$builder->add('scope', ScopePickerType::class, [ $builder->add('scope', ScopePickerType::class, [
'center' => $this->centerResolverManager->resolveCenters($entity), 'reachable_scopes' => $reachableScopes,
'role' => PersonDocumentVoter::CREATE, 'label' => count($reachableScopes) > 1 ? 'Scope' : false,
'label' => 'Scope',
]); ]);
} }
} }

View File

@@ -11,11 +11,13 @@ declare(strict_types=1);
namespace Chill\TaskBundle\Form; namespace Chill\TaskBundle\Form;
use Chill\MainBundle\Entity\Scope;
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\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 +26,18 @@ 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;
use Symfony\Contracts\Translation\TranslatorInterface;
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)
{ {
@@ -62,11 +72,22 @@ 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']) {
$reachableScopes = array_values(
array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
$options['role'],
$center
),
static fn (Scope $s) => $s->isActive()
)
);
$builder $builder
->add('circle', ScopePickerType::class, [ ->add('circle', ScopePickerType::class, [
'center' => $center, 'reachable_scopes' => $reachableScopes,
'role' => $options['role'],
'required' => true, 'required' => true,
'label' => count($reachableScopes) > 1 ? 'Scope' : false,
]); ]);
} }
} }