Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.60% covered (warning)
76.60%
144 / 188
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserType
76.60% covered (warning)
76.60%
144 / 188
66.67% covered (warning)
66.67%
2 / 3
15.17
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 buildForm
75.00% covered (warning)
75.00%
132 / 176
0.00% covered (danger)
0.00%
0 / 1
12.89
 configureOptions
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * This file is part of the MADIS - RGPD Management application.
5 *
6 * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22declare(strict_types=1);
23
24namespace App\Domain\User\Form\Type;
25
26use App\Domain\User\Form\DataTransformer\MoreInfoTransformer;
27use App\Domain\User\Form\DataTransformer\RoleTransformer;
28use App\Domain\User\Model\Collectivity;
29use App\Domain\User\Model\Service;
30use App\Domain\User\Model\User;
31use Doctrine\ORM\EntityRepository;
32use Knp\DictionaryBundle\Form\Type\DictionaryType;
33use Symfony\Bridge\Doctrine\Form\Type\EntityType;
34use Symfony\Component\Form\AbstractType;
35use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
36use Symfony\Component\Form\Extension\Core\Type\EmailType;
37use Symfony\Component\Form\Extension\Core\Type\PasswordType;
38use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
39use Symfony\Component\Form\Extension\Core\Type\TextType;
40use Symfony\Component\Form\FormBuilderInterface;
41use Symfony\Component\Form\FormEvent;
42use Symfony\Component\Form\FormEvents;
43use Symfony\Component\Form\FormInterface;
44use Symfony\Component\OptionsResolver\OptionsResolver;
45use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
46use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
47use Symfony\Component\Security\Core\Security;
48
49class UserType extends AbstractType
50{
51    /**
52     * @var AuthorizationCheckerInterface
53     */
54    private $authorizationChecker;
55
56    /**
57     * @var EncoderFactoryInterface
58     */
59    private $encoderFactory;
60
61    /**
62     * @var Security
63     */
64    private $security;
65
66    private bool $activeNotifications;
67
68    /**
69     * UserType constructor.
70     */
71    public function __construct(
72        AuthorizationCheckerInterface $authorizationChecker,
73        EncoderFactoryInterface $encoderFactory,
74        Security $security,
75        bool $activeNotifications,
76    ) {
77        $this->authorizationChecker = $authorizationChecker;
78        $this->encoderFactory       = $encoderFactory;
79        $this->security             = $security;
80        $this->activeNotifications  = $activeNotifications;
81    }
82
83    /**
84     * Build type form.
85     */
86    public function buildForm(FormBuilderInterface $builder, array $options)
87    {
88        if (array_key_exists('data', $options)) {
89            // Role est mono-valué dans le form, j'enleve ROLE_API
90            $options['data']->setRoles(array_diff($options['data']->getRoles(), ['ROLE_API']));
91        }
92
93        $collectivity    = $options['data']->getCollectivity();
94        $serviceDisabled = !$collectivity->getIsServicesEnabled();
95        /** @var User $authenticatedUser */
96        $authenticatedUser = $this->security->getUser();
97
98        $encoderFactory = $this->encoderFactory;
99
100        // Add collectivity general information only for admins
101        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
102            $builder
103                ->add('collectivity', EntityType::class, [
104                    'class'         => Collectivity::class,
105                    'label'         => 'global.label.organization',
106                    'query_builder' => function (EntityRepository $er) {
107                        return $er->createQueryBuilder('c')
108                            ->orderBy('c.name', 'ASC');
109                    },
110                    'required' => true,
111                    'attr'     => [
112                        'autocomplete' => 'organization',
113                    ],
114                ])
115                ->add('roles', DictionaryType::class, [
116                    'label'    => 'user.user.label.roles',
117                    'required' => true,
118                    'name'     => 'user_user_role',
119                    'multiple' => false,
120                    'expanded' => true,
121                ])
122                ->add('apiAuthorized', CheckboxType::class, [
123                    'label'    => 'user.user.label.apiAuthorized',
124                    'required' => false,
125                ])
126
127                ->add('enabled', CheckboxType::class, [
128                    'label'    => 'user.user.label.enabled',
129                    'required' => false,
130                ])
131                ->add('collectivitesReferees', EntityType::class, [
132                    'class'         => Collectivity::class,
133                    'label'         => 'user.user.label.collectivitesReferees',
134                    'query_builder' => function (EntityRepository $er) {
135                        return $er->createQueryBuilder('c')
136                            ->orderBy('c.name', 'ASC');
137                    },
138                    'required' => false,
139                    'multiple' => true,
140                    'expanded' => false,
141                    'attr'     => [
142                        'class'            => 'selectpicker',
143                        'title'            => 'global.placeholder.multiple_select',
144                        'data-live-search' => true,
145                        'aria-label'       => 'Structures rattachées',
146                        'autocomplete'     => 'organization',
147                    ],
148                ])
149                ->add('ssoKey', TextType::class, [
150                    'label'    => 'user.user.label.sso_key',
151                    'required' => false,
152                    'attr'     => [
153                        'maxlength' => 255,
154                    ],
155                    'purify_html' => true,
156                ]);
157
158            $builder
159                ->get('roles')
160                ->addModelTransformer(new RoleTransformer());
161        }
162
163        $formModifier = function (FormInterface $form, Collectivity $collectivity) use ($serviceDisabled, $authenticatedUser) {
164            $queryBuilder = function (EntityRepository $er) use ($serviceDisabled, $authenticatedUser, $collectivity) {
165                if ($serviceDisabled) {
166                    return $er->createQueryBuilder('s')
167                        ->where(':user MEMBER OF s.users')
168                        ->setParameter(':user', $authenticatedUser)
169                        ->orderBy('s.name', 'ASC');
170                }
171
172                return $er->createQueryBuilder('s')
173                    ->where('s.collectivity = :collectivity')
174                    ->setParameter(':collectivity', $collectivity)
175                    ->orderBy('s.name', 'ASC');
176            };
177
178            if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
179                $form->add('services', EntityType::class, [
180                    'class'         => Service::class,
181                    'label'         => 'user.user.label.services',
182                    'disabled'      => $serviceDisabled,
183                    'required'      => false,
184                    'multiple'      => true,
185                    'expanded'      => false,
186                    'query_builder' => $queryBuilder,
187                ]);
188            } else {
189                $form->add('services', EntityType::class, [
190                    'class'         => Service::class,
191                    'label'         => 'user.user.label.services',
192                    'disabled'      => true,
193                    'required'      => false,
194                    'multiple'      => true,
195                    'expanded'      => false,
196                    'query_builder' => $queryBuilder,
197                    'attr'          => [
198                        'readonly' => true, ],
199                ]);
200            }
201        };
202
203        if ($this->authorizationChecker->isGranted('ROLE_PREVIEW') && !$serviceDisabled) {
204            $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formModifier) {
205                $formModifier($event->getForm(), $event->getData()->getCollectivity());
206            });
207            $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use ($formModifier) {
208                $formModifier($event->getForm(), $event->getData()->getCollectivity());
209            });
210        }
211
212        // Now add standard information
213        $builder
214            ->add('firstName', TextType::class, [
215                'label'    => 'global.label.contact.first_name',
216                'required' => true,
217                'attr'     => [
218                    'maxlength'    => 255,
219                    'autocomplete' => 'given-name',
220                ],
221                'purify_html' => true,
222            ])
223            ->add('lastName', TextType::class, [
224                'label'    => 'global.label.contact.last_name',
225                'required' => true,
226                'attr'     => [
227                    'maxlength'    => 255,
228                    'autocomplete' => 'family-name',
229                ],
230                'purify_html' => true,
231            ])
232            ->add('email', EmailType::class, [
233                'label'    => 'global.label.contact.email',
234                'required' => true,
235                'attr'     => [
236                    'maxlength'    => 255,
237                    'autocomplete' => 'email',
238                ],
239            ])
240            ->add('moreInfos', DictionaryType::class, [
241                'label'       => 'user.user.label.moreInfos',
242                'required'    => false,
243                'name'        => 'user_user_moreInfo',
244                'multiple'    => false,
245                'expanded'    => true,
246                'placeholder' => 'Aucune information',
247            ])
248            ->add('plainPassword', RepeatedType::class, [
249                'type'          => PasswordType::class,
250                'first_options' => [
251                    'label' => 'user.security.label.password',
252                    'attr'  => [
253                        'maxlength' => 255,
254                    ],
255                ],
256                'second_options' => [
257                    'label' => 'user.security.label.password_repeat',
258                    'attr'  => [
259                        'maxlength' => 255,
260                    ],
261                ],
262                'required' => false,
263            ])
264        ;
265
266        if ($this->activeNotifications) {
267            $builder->add('emailNotificationPreference', EmailNotificationPreferenceType::class);
268            if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
269                $builder->add('notGeneratesNotifications', CheckboxType::class, [
270                    'label'    => 'notifications.label.not_generates_notifications',
271                    'required' => false,
272                ]);
273            }
274        }
275
276        $builder
277            ->get('moreInfos')
278            ->addModelTransformer(new MoreInfoTransformer())
279        ;
280
281        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($encoderFactory) {
282            $user = $event->getData();
283            if (null === $user->getPlainPassword() || !$event->getForm()->isValid()) {
284                return;
285            }
286
287            $encoder = $encoderFactory->getEncoder($user);
288            $user->setPassword($encoder->encodePassword($user->getPlainPassword(), '')); // No salt with bcrypt
289
290            $user->eraseCredentials();
291        });
292    }
293
294    /**
295     * Provide type options.
296     */
297    public function configureOptions(OptionsResolver $resolver)
298    {
299        $resolver
300            ->setDefaults([
301                'data_class'        => User::class,
302                'validation_groups' => [
303                    'default',
304                    'user',
305                ],
306            ]);
307    }
308}