Completed
Push — master ( d8b95b...caeb59 )
by Axel
09:34 queued 04:32
created

UserAdministrationController::listUsers()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 23
c 0
b 0
f 0
nc 2
nop 9
dl 0
loc 39
rs 9.552

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula - 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\CoreBundle\Site\SiteDefinitionInterface;
31
use Zikula\Bundle\HookBundle\Dispatcher\HookDispatcherInterface;
32
use Zikula\Bundle\HookBundle\Hook\ProcessHook;
33
use Zikula\Bundle\HookBundle\Hook\ValidationHook;
34
use Zikula\Bundle\HookBundle\Hook\ValidationProviders;
35
use Zikula\Component\SortableColumns\Column;
36
use Zikula\Component\SortableColumns\SortableColumns;
37
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
38
use Zikula\GroupsModule\Constant;
39
use Zikula\PermissionsModule\Annotation\PermissionCheck;
40
use Zikula\ThemeModule\Engine\Annotation\Theme;
41
use Zikula\UsersModule\Api\ApiInterface\CurrentUserApiInterface;
42
use Zikula\UsersModule\Collector\AuthenticationMethodCollector;
43
use Zikula\UsersModule\Constant as UsersConstant;
44
use Zikula\UsersModule\Entity\RepositoryInterface\UserRepositoryInterface;
45
use Zikula\UsersModule\Entity\UserEntity;
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\RegistrationPostUpdatedEvent;
52
use Zikula\UsersModule\Form\Type\AdminModifyUserType;
53
use Zikula\UsersModule\Form\Type\DeleteConfirmationType;
54
use Zikula\UsersModule\Form\Type\DeleteType;
55
use Zikula\UsersModule\Form\Type\MailType;
56
use Zikula\UsersModule\Form\Type\RegistrationType\ApproveRegistrationConfirmationType;
57
use Zikula\UsersModule\Form\Type\SearchUserType;
58
use Zikula\UsersModule\Helper\AdministrationActionsHelper;
59
use Zikula\UsersModule\Helper\DeleteHelper;
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 listUsers(
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->paginatedQuery($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 getUsersByFragmentAsTable(
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 modify(
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 approve(
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 delete(
285
        Request $request,
286
        CurrentUserApiInterface $currentUserApi,
287
        UserRepositoryInterface $userRepository,
288
        HookDispatcherInterface $hookDispatcher,
289
        EventDispatcherInterface $eventDispatcher,
290
        DeleteHelper $deleteHelper,
291
        UserEntity $user = null
292
    ) {
293
        $uids = [];
294
        if (!isset($user) && Request::METHOD_POST === $request->getMethod() && $request->request->has('zikulausersmodule_delete')) {
295
            $deletionData = $request->request->get('zikulausersmodule_delete');
296
            if (isset($deletionData['users']) && !empty($deletionData['users'])) {
297
                $uids = $deletionData['users'];
298
            }
299
        } elseif (isset($user)) {
300
            $uids = [$user->getUid()];
301
        }
302
        if (!count($uids)) {
303
            $this->addFlash('warning', 'No users selected.');
304
305
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
306
        }
307
        $usersImploded = implode(',', $uids);
308
309
        $deleteConfirmationForm = $this->createForm(DeleteConfirmationType::class, [
310
            'users' => $usersImploded
311
        ]);
312
        $deleteUserFormPostCreatedEvent = new DeleteUserFormPostCreatedEvent($deleteConfirmationForm);
313
        $eventDispatcher->dispatch($deleteUserFormPostCreatedEvent);
314
        $deleteConfirmationForm->handleRequest($request);
315
        if (empty($uids) && !$deleteConfirmationForm->isSubmitted()) {
316
            $this->addFlash('warning', 'No users selected.');
317
318
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
319
        }
320
        if ($deleteConfirmationForm->isSubmitted()) {
321
            if ($deleteConfirmationForm->get('cancel')->isClicked()) {
322
                $this->addFlash('success', 'Operation cancelled.');
323
324
                return $this->redirectToRoute('zikulausersmodule_useradministration_list');
325
            }
326
            $userIdsImploded = $deleteConfirmationForm->get('users')->getData();
327
            $userIds = explode(',', $userIdsImploded);
328
            $valid = true;
329
            foreach ($userIds as $k => $uid) {
330
                if (in_array($uid, [UsersConstant::USER_ID_ANONYMOUS, UsersConstant::USER_ID_ADMIN, $currentUserApi->get('uid')], true)) {
331
                    unset($userIds[$k]);
332
                    $this->addFlash('danger', $this->trans('You are not allowed to delete user id %uid%', ['%uid%' => $uid]));
333
                    continue;
334
                }
335
                $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_VALIDATE, $hook = new ValidationHook());
336
                if ($hook->getValidators()->hasErrors()) {
337
                    $valid = false;
338
                }
339
            }
340
            if ($valid && $deleteConfirmationForm->isValid()) {
341
                $deletedUsers = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => $userIds]]);
342
                $force = $deleteConfirmationForm->get('force')->getData();
343
                foreach ($deletedUsers as $deletedUser) {
344
                    $deleteHelper->deleteUser($deletedUser, $force);
345
                    $eventDispatcher->dispatch(new DeleteUserFormPostValidatedEvent($deleteConfirmationForm, $deletedUser));
346
                }
347
                $this->addFlash(
348
                    'success',
349
                    /** @Desc("{count, plural,\n  one   {User deleted!}\n  other {# users deleted!}\n}") */
350
                    $this->getTranslator()->trans(
351
                        'plural_n.users.deleted',
352
                        ['%count%' => count($deletedUsers)]
353
                    )
354
                );
355
356
                return $this->redirectToRoute('zikulausersmodule_useradministration_list');
357
            }
358
        }
359
        $users = $userRepository->findByUids($uids);
360
361
        return [
362
            'users' => $users,
363
            'form' => $deleteConfirmationForm->createView(),
364
            'additionalTemplates' => isset($deleteUserFormPostCreatedEvent) ? $deleteUserFormPostCreatedEvent->getTemplates() : []
365
        ];
366
    }
367
368
    /**
369
     * @Route("/search")
370
     * @PermissionCheck("moderate")
371
     * @Theme("admin")
372
     * @Template("@ZikulaUsersModule/UserAdministration/search.html.twig")
373
     *
374
     * @return array|Response
375
     */
376
    public function search(
377
        Request $request,
378
        UserRepositoryInterface $userRepository,
379
        VariableApiInterface $variableApi,
380
        SiteDefinitionInterface $site
381
    ) {
382
        $form = $this->createForm(SearchUserType::class, []);
383
        $form->handleRequest($request);
384
        if ($form->isSubmitted()) {
385
            $resultsForm = $this->createForm(DeleteType::class, [], [
386
                'choices' => $userRepository->queryBySearchForm($form->getData()),
387
                'action' => $this->generateUrl('zikulausersmodule_useradministration_delete')
388
            ]);
389
390
            return $this->render('@ZikulaUsersModule/UserAdministration/searchResults.html.twig', [
391
                'resultsForm' => $resultsForm->createView(),
392
                'mailForm' => $this->buildMailForm($variableApi, $site)->createView()
393
            ]);
394
        }
395
396
        return [
397
            'form' => $form->createView()
398
        ];
399
    }
400
401
    /**
402
     * @Route("/mail")
403
     * @PermissionCheck({"$_zkModule::MailUsers", "::", "comment"})
404
     */
405
    public function mailUsers(
406
        Request $request,
407
        UserRepositoryInterface $userRepository,
408
        VariableApiInterface $variableApi,
409
        MailHelper $mailHelper,
410
        SiteDefinitionInterface $site
411
    ): RedirectResponse {
412
        $mailForm = $this->buildMailForm($variableApi, $site);
413
        $mailForm->handleRequest($request);
414
        if ($mailForm->isSubmitted() && $mailForm->isValid()) {
415
            $data = $mailForm->getData();
416
            $users = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => explode(',', $data['userIds'])]]);
417
            if (empty($users)) {
418
                throw new InvalidArgumentException($this->trans('No users found.'));
419
            }
420
            if ($mailHelper->mailUsers($users, $data)) {
421
                $this->addFlash('success', 'Done! Mail sent.');
422
            } else {
423
                $this->addFlash('error', 'Could not send mail.');
424
            }
425
        } else {
426
            $this->addFlash('error', 'Could not send mail.');
427
        }
428
429
        return $this->redirectToRoute('zikulausersmodule_useradministration_search');
430
    }
431
432
    private function buildMailForm(
433
        VariableApiInterface $variableApi,
434
        SiteDefinitionInterface $site
435
    ): FormInterface {
436
        return $this->createForm(MailType::class, [
437
            'from' => $site->getName(),
438
            'replyto' => $variableApi->getSystemVar('adminmail'),
439
            'format' => 'text',
440
            'batchsize' => 100
441
        ], [
442
            'action' => $this->generateUrl('zikulausersmodule_useradministration_mailusers')
443
        ]);
444
    }
445
446
    /**
447
     * Prevent user from modifying certain aspects of self.
448
     */
449
    private function checkSelf(
450
        CurrentUserApiInterface $currentUserApi,
451
        VariableApiInterface $variableApi,
452
        UserEntity $userBeingModified,
453
        array $originalGroups = []
454
    ): void {
455
        $currentUserId = $currentUserApi->get('uid');
456
        if ($currentUserId !== $userBeingModified->getUid()) {
457
            return;
458
        }
459
460
        // current user not allowed to deactivate self
461
        if (UsersConstant::ACTIVATED_ACTIVE !== $userBeingModified->getActivated()) {
462
            $this->addFlash('info', 'You are not allowed to alter your own active state.');
463
            $userBeingModified->setActivated(UsersConstant::ACTIVATED_ACTIVE);
464
        }
465
        // current user not allowed to remove self from default group
466
        $defaultGroup = $variableApi->get('ZikulaGroupsModule', 'defaultgroup', 1);
467
        if (!$userBeingModified->getGroups()->containsKey($defaultGroup)) {
468
            $this->addFlash('info', 'You are not allowed to remove yourself from the default group.');
469
            $userBeingModified->getGroups()->add($originalGroups[$defaultGroup]);
470
        }
471
        // current user not allowed to remove self from admin group if currently a member
472
        if (isset($originalGroups[Constant::GROUP_ID_ADMIN]) && !$userBeingModified->getGroups()->containsKey(Constant::GROUP_ID_ADMIN)) {
473
            $this->addFlash('info', 'You are not allowed to remove yourself from the primary administrator group.');
474
            $userBeingModified->getGroups()->add($originalGroups[Constant::GROUP_ID_ADMIN]);
475
        }
476
    }
477
}
478