Completed
Push — master ( fc8ba7...397a08 )
by Axel
16:38 queued 11:30
created

UserAdministrationController::modifyAction()   B

Complexity

Conditions 10
Paths 8

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 30
c 0
b 0
f 0
nc 8
nop 6
dl 0
loc 52
rs 7.6666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\UsersModule\Controller;
15
16
use InvalidArgumentException;
17
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
18
use Symfony\Component\Form\FormInterface;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\Routing\Annotation\Route;
23
use Symfony\Component\Routing\RouterInterface;
24
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
25
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
26
use Translation\Extractor\Annotation\Desc;
27
use Zikula\Bundle\CoreBundle\Controller\AbstractController;
28
use Zikula\Bundle\CoreBundle\Filter\AlphaFilter;
29
use Zikula\Bundle\CoreBundle\Response\PlainResponse;
30
use Zikula\Bundle\HookBundle\Dispatcher\HookDispatcherInterface;
31
use Zikula\Bundle\HookBundle\Hook\ProcessHook;
32
use Zikula\Bundle\HookBundle\Hook\ValidationHook;
33
use Zikula\Bundle\HookBundle\Hook\ValidationProviders;
34
use Zikula\Component\SortableColumns\Column;
35
use Zikula\Component\SortableColumns\SortableColumns;
36
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
37
use Zikula\GroupsModule\Constant;
38
use Zikula\PermissionsModule\Annotation\PermissionCheck;
39
use Zikula\ThemeModule\Engine\Annotation\Theme;
40
use Zikula\UsersModule\Api\ApiInterface\CurrentUserApiInterface;
41
use Zikula\UsersModule\Collector\AuthenticationMethodCollector;
42
use Zikula\UsersModule\Constant as UsersConstant;
43
use Zikula\UsersModule\Entity\RepositoryInterface\UserRepositoryInterface;
44
use Zikula\UsersModule\Entity\UserEntity;
45
use Zikula\UsersModule\Event\ActiveUserPostDeletedEvent;
46
use Zikula\UsersModule\Event\ActiveUserPostUpdatedEvent;
47
use Zikula\UsersModule\Event\DeleteUserFormPostCreatedEvent;
48
use Zikula\UsersModule\Event\DeleteUserFormPostValidatedEvent;
49
use Zikula\UsersModule\Event\EditUserFormPostCreatedEvent;
50
use Zikula\UsersModule\Event\EditUserFormPostValidatedEvent;
51
use Zikula\UsersModule\Event\RegistrationPostDeletedEvent;
52
use Zikula\UsersModule\Event\RegistrationPostUpdatedEvent;
53
use Zikula\UsersModule\Form\Type\AdminModifyUserType;
54
use Zikula\UsersModule\Form\Type\DeleteConfirmationType;
55
use Zikula\UsersModule\Form\Type\DeleteType;
56
use Zikula\UsersModule\Form\Type\MailType;
57
use Zikula\UsersModule\Form\Type\RegistrationType\ApproveRegistrationConfirmationType;
58
use Zikula\UsersModule\Form\Type\SearchUserType;
59
use Zikula\UsersModule\Helper\AdministrationActionsHelper;
60
use Zikula\UsersModule\Helper\MailHelper;
61
use Zikula\UsersModule\Helper\RegistrationHelper;
62
use Zikula\UsersModule\HookSubscriber\UserManagementUiHooksSubscriber;
63
64
/**
65
 * Class UserAdministrationController
66
 *
67
 * @Route("/admin")
68
 */
69
class UserAdministrationController extends AbstractController
70
{
71
    /**
72
     * @Route("/list/{sort}/{sortdir}/{letter}/{page}", methods = {"GET"}, requirements={"page" = "\d+"})
73
     * @PermissionCheck("moderate")
74
     * @Theme("admin")
75
     * @Template("@ZikulaUsersModule/UserAdministration/list.html.twig")
76
     */
77
    public function listAction(
78
        Request $request,
79
        UserRepositoryInterface $userRepository,
80
        RouterInterface $router,
81
        AdministrationActionsHelper $actionsHelper,
82
        AuthenticationMethodCollector $authenticationMethodCollector,
83
        string $sort = 'uid',
84
        string $sortdir = 'DESC',
85
        string $letter = 'all',
86
        int $page = 1
87
    ): array {
88
        $sortableColumns = new SortableColumns($router, 'zikulausersmodule_useradministration_list', 'sort', 'sortdir');
89
        $sortableColumns->addColumns([new Column('uname'), new Column('uid'), new Column('registrationDate'), new Column('lastLogin'), new Column('activated')]);
90
        $sortableColumns->setOrderByFromRequest($request);
91
        $sortableColumns->setAdditionalUrlParameters([
92
            'letter' => $letter,
93
            'page' => $page
94
        ]);
95
96
        $filter = [];
97
        if (!empty($letter) && 'all' !== $letter) {
98
            $filter['uname'] = ['operator' => 'like', 'operand' => "${letter}%"];
99
        }
100
        $pageSize = $this->getVar(UsersConstant::MODVAR_ITEMS_PER_PAGE, UsersConstant::DEFAULT_ITEMS_PER_PAGE);
101
        $paginator = $userRepository->query($filter, [$sort => $sortdir], 'and', $page, $pageSize);
102
        $paginator->setRoute('zikulausersmodule_useradministration_list');
103
        $routeParameters = [
104
            'sort' => $sort,
105
            'sortdir' => $sortdir,
106
            'letter' => $letter,
107
        ];
108
        $paginator->setRouteParameters($routeParameters);
109
110
        return [
111
            'sort' => $sortableColumns->generateSortableColumns(),
112
            'actionsHelper' => $actionsHelper,
113
            'authMethodCollector' => $authenticationMethodCollector,
114
            'alpha' => new AlphaFilter('zikulausersmodule_useradministration_list', $routeParameters, $letter),
115
            'paginator' => $paginator
116
        ];
117
    }
118
119
    /**
120
     * Called from UsersModule/Resources/public/js/Zikula.Users.Admin.View.js
121
     * to populate a username search
122
     *
123
     * @Route("/getusersbyfragmentastable", methods = {"POST"}, options={"expose"=true, "i18n"=false})
124
     */
125
    public function getUsersByFragmentAsTableAction(
126
        Request $request,
127
        UserRepositoryInterface $userRepository,
128
        AdministrationActionsHelper $actionsHelper
129
    ): Response {
130
        if (!$this->hasPermission('ZikulaUsersModule', '::', ACCESS_MODERATE)) {
131
            return new PlainResponse('');
132
        }
133
        $fragment = $request->request->get('fragment');
134
        $filter = [
135
            'activated' => ['operator' => 'notIn', 'operand' => [
136
                UsersConstant::ACTIVATED_PENDING_REG,
137
                UsersConstant::ACTIVATED_PENDING_DELETE
138
            ]],
139
            'uname' => ['operator' => 'like', 'operand' => "${fragment}%"]
140
        ];
141
        $users = $userRepository->query($filter);
142
143
        return $this->render('@ZikulaUsersModule/UserAdministration/userlist.html.twig', [
144
            'users' => $users,
145
            'actionsHelper' => $actionsHelper
146
        ], new PlainResponse());
147
    }
148
149
    /**
150
     * @Route("/user/modify/{user}", requirements={"user" = "^[1-9]\d*$"})
151
     * @Theme("admin")
152
     * @Template("@ZikulaUsersModule/UserAdministration/modify.html.twig")
153
     *
154
     * @return array|RedirectResponse
155
     * @throws AccessDeniedException Thrown if the user hasn't edit permissions for the user record
156
     */
157
    public function modifyAction(
158
        Request $request,
159
        UserEntity $user,
160
        CurrentUserApiInterface $currentUserApi,
161
        VariableApiInterface $variableApi,
162
        EventDispatcherInterface $eventDispatcher,
163
        HookDispatcherInterface $hookDispatcher
164
    ) {
165
        if (!$this->hasPermission('ZikulaUsersModule::', $user->getUname() . '::' . $user->getUid(), ACCESS_EDIT)) {
166
            throw new AccessDeniedException();
167
        }
168
        if (UsersConstant::USER_ID_ANONYMOUS === $user->getUid()) {
169
            throw new AccessDeniedException($this->trans("Error! You can't edit the guest account."));
170
        }
171
172
        $form = $this->createForm(AdminModifyUserType::class, $user);
173
        $originalUser = clone $user;
174
        $editUserFormPostCreatedEvent = new EditUserFormPostCreatedEvent($form);
175
        $eventDispatcher->dispatch($editUserFormPostCreatedEvent);
176
        $form->handleRequest($request);
177
178
        $hook = new ValidationHook(new ValidationProviders());
179
        $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::EDIT_VALIDATE, $hook);
180
        $validators = $hook->getValidators();
181
182
        if ($form->isSubmitted() && $form->isValid() && !$validators->hasErrors()) {
183
            if ($form->get('submit')->isClicked()) {
0 ignored issues
show
Bug introduced by
The method isClicked() does not exist on Symfony\Component\Form\FormInterface. It seems like you code against a sub-type of Symfony\Component\Form\FormInterface such as Symfony\Component\Form\SubmitButton. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

183
            if ($form->get('submit')->/** @scrutinizer ignore-call */ isClicked()) {
Loading history...
184
                $user = $form->getData();
185
                $this->checkSelf($currentUserApi, $variableApi, $user, $originalUser->getGroups()->toArray());
186
187
                $eventDispatcher->dispatch(new EditUserFormPostValidatedEvent($form, $user));
188
189
                $this->getDoctrine()->getManager()->flush();
190
191
                $updateEvent = UsersConstant::ACTIVATED_PENDING_REG === $user->getActivated()
192
                    ? new RegistrationPostUpdatedEvent($user, $originalUser)
193
                    : new ActiveUserPostUpdatedEvent($user, $originalUser);
194
                $eventDispatcher->dispatch($updateEvent);
195
196
                $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::EDIT_PROCESS, new ProcessHook($user->getUid()));
197
198
                $this->addFlash('status', "Done! Saved user's account information.");
199
            } elseif ($form->get('cancel')->isClicked()) {
200
                $this->addFlash('status', 'Operation cancelled.');
201
            }
202
203
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
204
        }
205
206
        return [
207
            'form' => $form->createView(),
208
            'additionalTemplates' => isset($editUserFormPostCreatedEvent) ? $editUserFormPostCreatedEvent->getTemplates() : []
209
        ];
210
    }
211
212
    /**
213
     * @Route("/approve/{user}/{force}", requirements={"user" = "^[1-9]\d*$"})
214
     * @PermissionCheck("moderate")
215
     * @Theme("admin")
216
     * @Template("@ZikulaUsersModule/UserAdministration/approve.html.twig")
217
     *
218
     * @return array|RedirectResponse
219
     */
220
    public function approveAction(
221
        Request $request,
222
        UserEntity $user,
223
        RegistrationHelper $registrationHelper,
224
        MailHelper $mailHelper,
225
        bool $force = false
226
    ) {
227
        $forceVerification = $this->hasPermission('ZikulaUsersModule', '::', ACCESS_ADMIN) && $force;
228
        $form = $this->createForm(ApproveRegistrationConfirmationType::class, [
229
            'user' => $user->getUid(),
230
            'force' => $forceVerification
231
        ], [
232
            'buttonLabel' => $this->trans('Approve')
233
        ]);
234
        $redirectToRoute = 'zikulausersmodule_useradministration_list';
235
236
        if (!$forceVerification) {
237
            if ($user->isApproved()) {
238
                $this->addFlash('error', $this->trans('Warning! Nothing to do! %sub% is already approved.', ['%sub%' => $user->getUname()]));
239
240
                return $this->redirectToRoute($redirectToRoute);
241
            }
242
            if (!$user->isApproved() && !$this->hasPermission('ZikulaUsersModule::', '::', ACCESS_ADMIN)) {
243
                $this->addFlash('error', $this->trans('Error! %sub% cannot be approved.', ['%sub%' => $user->getUname()]));
244
245
                return $this->redirectToRoute($redirectToRoute);
246
            }
247
        }
248
249
        $form->handleRequest($request);
250
        if ($form->isSubmitted() && $form->isValid()) {
251
            if ($form->get('confirm')->isClicked()) {
252
                $registrationHelper->approve($user);
253
                if (UsersConstant::ACTIVATED_PENDING_REG === $user->getActivated()) {
254
                    $notificationErrors = $mailHelper->createAndSendRegistrationMail($user, true, false);
255
                } else {
256
                    $notificationErrors = $mailHelper->createAndSendUserMail($user, true, false);
257
                }
258
259
                if ($notificationErrors) {
260
                    $this->addFlash('error', implode('<br />', $notificationErrors));
261
                }
262
                $this->addFlash('status', $this->trans('Done! %sub% has been approved.', ['%sub%' => $user->getUname()]));
263
            } elseif ($form->get('cancel')->isClicked()) {
264
                $this->addFlash('status', 'Operation cancelled.');
265
            }
266
267
            return $this->redirectToRoute($redirectToRoute);
268
        }
269
270
        return [
271
            'form' => $form->createView(),
272
            'user' => $user
273
        ];
274
    }
275
276
    /**
277
     * @Route("/delete/{user}", requirements={"user" = "^[1-9]\d*$"})
278
     * @PermissionCheck("delete")
279
     * @Theme("admin")
280
     * @Template("@ZikulaUsersModule/UserAdministration/delete.html.twig")
281
     *
282
     * @return array|RedirectResponse
283
     */
284
    public function deleteAction(
285
        Request $request,
286
        CurrentUserApiInterface $currentUserApi,
287
        UserRepositoryInterface $userRepository,
288
        HookDispatcherInterface $hookDispatcher,
289
        EventDispatcherInterface $eventDispatcher,
290
        UserEntity $user = null
291
    ) {
292
        $uids = [];
293
        if (!isset($user) && 'POST' === $request->getMethod() && $request->request->has('zikulausersmodule_delete')) {
294
            $deletionData = $request->request->get('zikulausersmodule_delete');
295
            if (isset($deletionData['users']) && !empty($deletionData['users'])) {
296
                $uids = $deletionData['users'];
297
            }
298
        } elseif (isset($user)) {
299
            $uids = [$user->getUid()];
300
        }
301
        if (!count($uids)) {
302
            $this->addFlash('warning', 'No users selected.');
303
304
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
305
        }
306
        $usersImploded = implode(',', $uids);
307
308
        $deleteConfirmationForm = $this->createForm(DeleteConfirmationType::class, [
309
            'users' => $usersImploded
310
        ]);
311
        $deleteUserFormPostCreatedEvent = new DeleteUserFormPostCreatedEvent($deleteConfirmationForm);
312
        $eventDispatcher->dispatch($deleteUserFormPostCreatedEvent);
313
        $deleteConfirmationForm->handleRequest($request);
314
        if (empty($uids) && !$deleteConfirmationForm->isSubmitted()) {
315
            $this->addFlash('warning', 'No users selected.');
316
317
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
318
        }
319
        if ($deleteConfirmationForm->isSubmitted()) {
320
            if ($deleteConfirmationForm->get('cancel')->isClicked()) {
321
                $this->addFlash('success', 'Operation cancelled.');
322
323
                return $this->redirectToRoute('zikulausersmodule_useradministration_list');
324
            }
325
            $userIdsImploded = $deleteConfirmationForm->get('users')->getData();
326
            $userIds = explode(',', $userIdsImploded);
327
            $valid = true;
328
            foreach ($userIds as $k => $uid) {
329
                if (in_array($uid, [UsersConstant::USER_ID_ANONYMOUS, UsersConstant::USER_ID_ADMIN, $currentUserApi->get('uid')], true)) {
330
                    unset($userIds[$k]);
331
                    $this->addFlash('danger', $this->trans('You are not allowed to delete user id %uid%', ['%uid%' => $uid]));
332
                    continue;
333
                }
334
                $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_VALIDATE, $hook = new ValidationHook());
335
                if ($hook->getValidators()->hasErrors()) {
336
                    $valid = false;
337
                }
338
            }
339
            if ($valid && $deleteConfirmationForm->isValid()) {
340
                // send email to 'denied' registrations. see MailHelper::sendNotification (regdeny) #2915
341
                $deletedUsers = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => $userIds]]);
342
                foreach ($deletedUsers as $deletedUser) {
343
                    if (UsersConstant::ACTIVATED_ACTIVE === $deletedUser->getActivated()) {
344
                        $eventDispatcher->dispatch(new ActiveUserPostDeletedEvent($deletedUser));
345
                    } else {
346
                        $eventDispatcher->dispatch(new RegistrationPostDeletedEvent($deletedUser));
347
                    }
348
                    $eventDispatcher->dispatch(new DeleteUserFormPostValidatedEvent($deleteConfirmationForm, $deletedUser));
349
                    $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_PROCESS, new ProcessHook($deletedUser->getUid()));
350
                    $userRepository->removeAndFlush($deletedUser);
351
                }
352
                $this->addFlash(
353
                    'success',
354
                    /** @Desc("{count, plural,\n  one   {User deleted!}\n  other {# users deleted!}\n}") */
355
                    $this->getTranslator()->trans(
356
                        'plural_n.users.deleted',
357
                        ['%count%' => count($deletedUsers)]
358
                    )
359
                );
360
361
                return $this->redirectToRoute('zikulausersmodule_useradministration_list');
362
            }
363
        }
364
        $users = $userRepository->findByUids($uids);
365
366
        return [
367
            'users' => $users,
368
            'form' => $deleteConfirmationForm->createView(),
369
            'additionalTemplates' => isset($deleteUserFormPostCreatedEvent) ? $deleteUserFormPostCreatedEvent->getTemplates() : []
370
        ];
371
    }
372
373
    /**
374
     * @Route("/search")
375
     * @PermissionCheck("moderate")
376
     * @Theme("admin")
377
     * @Template("@ZikulaUsersModule/UserAdministration/search.html.twig")
378
     *
379
     * @return array|Response
380
     */
381
    public function searchAction(
382
        Request $request,
383
        UserRepositoryInterface $userRepository,
384
        VariableApiInterface $variableApi
385
    ) {
386
        $form = $this->createForm(SearchUserType::class, []);
387
        $form->handleRequest($request);
388
        if ($form->isSubmitted()) {
389
            $resultsForm = $this->createForm(DeleteType::class, [], [
390
                'choices' => $userRepository->queryBySearchForm($form->getData())->getResults(),
391
                'action' => $this->generateUrl('zikulausersmodule_useradministration_delete')
392
            ]);
393
394
            return $this->render('@ZikulaUsersModule/UserAdministration/searchResults.html.twig', [
395
                'resultsForm' => $resultsForm->createView(),
396
                'mailForm' => $this->buildMailForm($variableApi)->createView()
397
            ]);
398
        }
399
400
        return [
401
            'form' => $form->createView()
402
        ];
403
    }
404
405
    /**
406
     * @Route("/mail")
407
     * @PermissionCheck({"$_zkModule::MailUsers", "::", "comment"})
408
     */
409
    public function mailUsersAction(
410
        Request $request,
411
        UserRepositoryInterface $userRepository,
412
        VariableApiInterface $variableApi,
413
        MailHelper $mailHelper
414
    ): RedirectResponse {
415
        $mailForm = $this->buildMailForm($variableApi);
416
        $mailForm->handleRequest($request);
417
        if ($mailForm->isSubmitted() && $mailForm->isValid()) {
418
            $data = $mailForm->getData();
419
            $users = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => explode(',', $data['userIds'])]]);
420
            if (empty($users)) {
421
                throw new InvalidArgumentException($this->trans('No users found.'));
422
            }
423
            if ($mailHelper->mailUsers($users, $data)) {
424
                $this->addFlash('success', 'Done! Mail sent.');
425
            } else {
426
                $this->addFlash('error', 'Could not send mail.');
427
            }
428
        } else {
429
            $this->addFlash('error', 'Could not send mail.');
430
        }
431
432
        return $this->redirectToRoute('zikulausersmodule_useradministration_search');
433
    }
434
435
    private function buildMailForm(VariableApiInterface $variableApi): FormInterface
436
    {
437
        return $this->createForm(MailType::class, [
438
            'from' => $variableApi->getSystemVar('sitename'),
439
            'replyto' => $variableApi->getSystemVar('adminmail'),
440
            'format' => 'text',
441
            'batchsize' => 100
442
        ], [
443
            'action' => $this->generateUrl('zikulausersmodule_useradministration_mailusers')
444
        ]);
445
    }
446
447
    /**
448
     * Prevent user from modifying certain aspects of self.
449
     */
450
    private function checkSelf(
451
        CurrentUserApiInterface $currentUserApi,
452
        VariableApiInterface $variableApi,
453
        UserEntity $userBeingModified,
454
        array $originalGroups = []
455
    ): void {
456
        $currentUserId = $currentUserApi->get('uid');
457
        if ($currentUserId !== $userBeingModified->getUid()) {
458
            return;
459
        }
460
461
        // current user not allowed to deactivate self
462
        if (UsersConstant::ACTIVATED_ACTIVE !== $userBeingModified->getActivated()) {
463
            $this->addFlash('info', 'You are not allowed to alter your own active state.');
464
            $userBeingModified->setActivated(UsersConstant::ACTIVATED_ACTIVE);
465
        }
466
        // current user not allowed to remove self from default group
467
        $defaultGroup = $variableApi->get('ZikulaGroupsModule', 'defaultgroup', 1);
468
        if (!$userBeingModified->getGroups()->containsKey($defaultGroup)) {
469
            $this->addFlash('info', 'You are not allowed to remove yourself from the default group.');
470
            $userBeingModified->getGroups()->add($originalGroups[$defaultGroup]);
471
        }
472
        // current user not allowed to remove self from admin group if currently a member
473
        if (isset($originalGroups[Constant::GROUP_ID_ADMIN]) && !$userBeingModified->getGroups()->containsKey(Constant::GROUP_ID_ADMIN)) {
474
            $this->addFlash('info', 'You are not allowed to remove yourself from the primary administrator group.');
475
            $userBeingModified->getGroups()->add($originalGroups[Constant::GROUP_ID_ADMIN]);
476
        }
477
    }
478
}
479