Completed
Push — master ( 8fc13e...c19aa3 )
by Axel
06:21
created

UserAdministrationController::approveAction()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 30
nc 32
nop 5
dl 0
loc 53
rs 6.9666
c 0
b 0
f 0

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 Doctrine\Common\Collections\ArrayCollection;
17
use InvalidArgumentException;
18
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
19
use Symfony\Component\Form\FormInterface;
20
use Symfony\Component\HttpFoundation\RedirectResponse;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpFoundation\Response;
23
use Symfony\Component\Routing\Annotation\Route;
24
use Symfony\Component\Routing\RouterInterface;
25
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
26
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
27
use Translation\Extractor\Annotation\Desc;
28
use Zikula\Bundle\CoreBundle\Controller\AbstractController;
29
use Zikula\Bundle\CoreBundle\Event\GenericEvent;
30
use Zikula\Bundle\CoreBundle\Response\PlainResponse;
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\Constant as UsersConstant;
43
use Zikula\UsersModule\Entity\RepositoryInterface\UserRepositoryInterface;
44
use Zikula\UsersModule\Entity\UserEntity;
45
use Zikula\UsersModule\Event\UserFormAwareEvent;
46
use Zikula\UsersModule\Event\UserFormDataEvent;
47
use Zikula\UsersModule\Form\Type\AdminModifyUserType;
48
use Zikula\UsersModule\Form\Type\DeleteConfirmationType;
49
use Zikula\UsersModule\Form\Type\DeleteType;
50
use Zikula\UsersModule\Form\Type\MailType;
51
use Zikula\UsersModule\Form\Type\RegistrationType\ApproveRegistrationConfirmationType;
52
use Zikula\UsersModule\Form\Type\SearchUserType;
53
use Zikula\UsersModule\Helper\AdministrationActionsHelper;
54
use Zikula\UsersModule\Helper\MailHelper;
55
use Zikula\UsersModule\Helper\RegistrationHelper;
56
use Zikula\UsersModule\HookSubscriber\UserManagementUiHooksSubscriber;
57
use Zikula\UsersModule\RegistrationEvents;
58
use Zikula\UsersModule\UserEvents;
59
60
/**
61
 * Class UserAdministrationController
62
 *
63
 * @Route("/admin")
64
 */
65
class UserAdministrationController extends AbstractController
66
{
67
    /**
68
     * @Route("/list/{sort}/{sortdir}/{letter}/{startnum}")
69
     * @PermissionCheck("moderate")
70
     * @Theme("admin")
71
     * @Template("@ZikulaUsersModule/UserAdministration/list.html.twig")
72
     */
73
    public function listAction(
74
        Request $request,
75
        UserRepositoryInterface $userRepository,
76
        RouterInterface $router,
77
        AdministrationActionsHelper $actionsHelper,
78
        string $sort = 'uid',
79
        string $sortdir = 'DESC',
80
        string $letter = 'all',
81
        int $startnum = 0
82
    ): array {
83
        $startnum = $startnum > 0 ? $startnum - 1 : 0;
84
85
        $sortableColumns = new SortableColumns($router, 'zikulausersmodule_useradministration_list', 'sort', 'sortdir');
86
        $sortableColumns->addColumns([new Column('uname'), new Column('uid'), new Column('registrationDate'), new Column('lastLogin'), new Column('activated')]);
87
        $sortableColumns->setOrderByFromRequest($request);
88
        $sortableColumns->setAdditionalUrlParameters([
89
            'letter' => $letter,
90
            'startnum' => $startnum
91
        ]);
92
93
        $filter = [];
94
        if (!empty($letter) && 'all' !== $letter) {
95
            $filter['uname'] = ['operator' => 'like', 'operand' => "${letter}%"];
96
        }
97
        $limit = $this->getVar(UsersConstant::MODVAR_ITEMS_PER_PAGE, UsersConstant::DEFAULT_ITEMS_PER_PAGE);
98
        $users = $userRepository->query($filter, [$sort => $sortdir], $limit, $startnum);
99
100
        return [
101
            'sort' => $sortableColumns->generateSortableColumns(),
102
            'pager' => [
103
                'count' => $users->count(),
104
                'limit' => $limit
105
            ],
106
            'actionsHelper' => $actionsHelper,
107
            'users' => $users
108
        ];
109
    }
110
111
    /**
112
     * Called from UsersModule/Resources/public/js/Zikula.Users.Admin.View.js
113
     * to populate a username search
114
     *
115
     * @Route("/getusersbyfragmentastable", methods = {"POST"}, options={"expose"=true})
116
     */
117
    public function getUsersByFragmentAsTableAction(
118
        Request $request,
119
        UserRepositoryInterface $userRepository,
120
        AdministrationActionsHelper $actionsHelper
121
    ): Response {
122
        if (!$this->hasPermission('ZikulaUsersModule', '::', ACCESS_MODERATE)) {
123
            return new PlainResponse('');
124
        }
125
        $fragment = $request->request->get('fragment');
126
        $filter = [
127
            'activated' => ['operator' => 'notIn', 'operand' => [
128
                UsersConstant::ACTIVATED_PENDING_REG,
129
                UsersConstant::ACTIVATED_PENDING_DELETE
130
            ]],
131
            'uname' => ['operator' => 'like', 'operand' => "${fragment}%"]
132
        ];
133
        $users = $userRepository->query($filter);
134
135
        return $this->render('@ZikulaUsersModule/UserAdministration/userlist.html.twig', [
136
            'users' => $users,
137
            'actionsHelper' => ${$actionsHelper}
138
        ], new PlainResponse());
139
    }
140
141
    /**
142
     * @Route("/user/modify/{user}", requirements={"user" = "^[1-9]\d*$"})
143
     * @Theme("admin")
144
     * @Template("@ZikulaUsersModule/UserAdministration/modify.html.twig")
145
     *
146
     * @return array|RedirectResponse
147
     * @throws AccessDeniedException Thrown if the user hasn't edit permissions for the user record
148
     */
149
    public function modifyAction(
150
        Request $request,
151
        UserEntity $user,
152
        CurrentUserApiInterface $currentUserApi,
153
        VariableApiInterface $variableApi,
154
        EventDispatcherInterface $eventDispatcher,
155
        HookDispatcherInterface $hookDispatcher
156
    ) {
157
        if (!$this->hasPermission('ZikulaUsersModule::', $user->getUname() . '::' . $user->getUid(), ACCESS_EDIT)) {
158
            throw new AccessDeniedException();
159
        }
160
        if (UsersConstant::USER_ID_ANONYMOUS === $user->getUid()) {
161
            throw new AccessDeniedException($this->trans("Error! You can't edit the guest account."));
162
        }
163
164
        $form = $this->createForm(AdminModifyUserType::class, $user);
165
        $originalUserName = $user->getUname();
166
        $originalGroups = $user->getGroups()->toArray();
167
        $formEvent = new UserFormAwareEvent($form);
168
        $eventDispatcher->dispatch($formEvent, UserEvents::EDIT_FORM);
169
        $form->handleRequest($request);
170
171
        $hook = new ValidationHook(new ValidationProviders());
172
        $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::EDIT_VALIDATE, $hook);
173
        $validators = $hook->getValidators();
174
175
        if ($form->isSubmitted() && $form->isValid() && !$validators->hasErrors()) {
176
            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

176
            if ($form->get('submit')->/** @scrutinizer ignore-call */ isClicked()) {
Loading history...
177
                $user = $form->getData();
178
                $this->checkSelf($currentUserApi, $variableApi, $user, $originalGroups);
179
180
                $formDataEvent = new UserFormDataEvent($user, $form);
181
                $eventDispatcher->dispatch($formDataEvent, UserEvents::EDIT_FORM_HANDLE);
182
183
                $this->getDoctrine()->getManager()->flush();
184
185
                $eventArgs = [
186
                    'action'    => 'setVar',
187
                    'field'     => 'uname',
188
                    'attribute' => null,
189
                ];
190
                $eventData = ['old_value' => $originalUserName];
191
                $updateEvent = new GenericEvent($user, $eventArgs, $eventData);
192
                $eventDispatcher->dispatch($updateEvent, UserEvents::UPDATE_ACCOUNT);
193
194
                $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::EDIT_PROCESS, new ProcessHook($user->getUid()));
195
196
                $this->addFlash('status', "Done! Saved user's account information.");
197
            } elseif ($form->get('cancel')->isClicked()) {
198
                $this->addFlash('status', 'Operation cancelled.');
199
            }
200
201
            return $this->redirectToRoute('zikulausersmodule_useradministration_list');
202
        }
203
204
        return [
205
            'form' => $form->createView(),
206
            'additional_templates' => isset($formEvent) ? $formEvent->getTemplates() : []
207
        ];
208
    }
209
210
    /**
211
     * @Route("/approve/{user}/{force}", requirements={"user" = "^[1-9]\d*$"})
212
     * @PermissionCheck("moderate")
213
     * @Theme("admin")
214
     * @Template("@ZikulaUsersModule/UserAdministration/approve.html.twig")
215
     *
216
     * @return array|RedirectResponse
217
     */
218
    public function approveAction(
219
        Request $request,
220
        UserEntity $user,
221
        RegistrationHelper $registrationHelper,
222
        MailHelper $mailHelper,
223
        bool $force = false
224
    ) {
225
        $forceVerification = $this->hasPermission('ZikulaUsersModule', '::', ACCESS_ADMIN) && $force;
226
        $form = $this->createForm(ApproveRegistrationConfirmationType::class, [
227
            'user' => $user->getUid(),
228
            'force' => $forceVerification
229
        ], [
230
            'buttonLabel' => $this->trans('Approve')
231
        ]);
232
        $redirectToRoute = 'zikulausersmodule_useradministration_list';
233
234
        if (!$forceVerification) {
235
            if ($user->isApproved()) {
236
                $this->addFlash('error', $this->trans('Warning! Nothing to do! %sub% is already approved.', ['%sub%' => $user->getUname()]));
237
238
                return $this->redirectToRoute($redirectToRoute);
239
            }
240
            if (!$user->isApproved() && !$this->hasPermission('ZikulaUsersModule::', '::', ACCESS_ADMIN)) {
241
                $this->addFlash('error', $this->trans('Error! %sub% cannot be approved.', ['%sub%' => $user->getUname()]));
242
243
                return $this->redirectToRoute($redirectToRoute);
244
            }
245
        }
246
247
        $form->handleRequest($request);
248
        if ($form->isSubmitted() && $form->isValid()) {
249
            if ($form->get('confirm')->isClicked()) {
250
                $registrationHelper->approve($user);
251
                if (UsersConstant::ACTIVATED_PENDING_REG === $user->getActivated()) {
252
                    $notificationErrors = $mailHelper->createAndSendRegistrationMail($user, true, false);
253
                } else {
254
                    $notificationErrors = $mailHelper->createAndSendUserMail($user, true, false);
255
                }
256
257
                if ($notificationErrors) {
258
                    $this->addFlash('error', implode('<br />', $notificationErrors));
259
                }
260
                $this->addFlash('status', $this->trans('Done! %sub% has been approved.', ['%sub%' => $user->getUname()]));
261
            } elseif ($form->get('cancel')->isClicked()) {
262
                $this->addFlash('status', 'Operation cancelled.');
263
            }
264
265
            return $this->redirectToRoute($redirectToRoute);
266
        }
267
268
        return [
269
            'form' => $form->createView(),
270
            'user' => $user
271
        ];
272
    }
273
274
    /**
275
     * @Route("/delete/{user}", requirements={"user" = "^[1-9]\d*$"})
276
     * @PermissionCheck("delete")
277
     * @Theme("admin")
278
     * @Template("@ZikulaUsersModule/UserAdministration/delete.html.twig")
279
     *
280
     * @return array|RedirectResponse
281
     */
282
    public function deleteAction(
283
        Request $request,
284
        CurrentUserApiInterface $currentUserApi,
285
        UserRepositoryInterface $userRepository,
286
        HookDispatcherInterface $hookDispatcher,
287
        EventDispatcherInterface $eventDispatcher,
288
        UserEntity $user = null
289
    ) {
290
        $users = new ArrayCollection();
291
        if ('POST' === $request->getMethod()) {
292
            $deleteForm = $this->createForm(DeleteType::class, [], [
293
                'choices' => $userRepository->queryBySearchForm(),
294
                'action' => $this->generateUrl('zikulausersmodule_useradministration_delete')
295
            ]);
296
            $deleteForm->handleRequest($request);
297
            if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
298
                $data = $deleteForm->getData();
299
                $users = $data['users'];
300
            }
301
        } else {
302
            if (isset($user)) {
303
                $users->add($user);
304
            }
305
        }
306
        $uids = [];
307
        foreach ($users as $affectedUser) {
308
            $uids[] = $affectedUser->getUid();
309
        }
310
        $usersImploded = implode(',', $uids);
311
312
        $deleteConfirmationForm = $this->createForm(DeleteConfirmationType::class, [
313
            'users' => $usersImploded
314
        ]);
315
        $deleteConfirmationForm->handleRequest($request);
316
        if ($users instanceof ArrayCollection && $users->isEmpty() && !$deleteConfirmationForm->isSubmitted()) {
317
            throw new InvalidArgumentException($this->trans('No users selected.'));
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
                $event = new GenericEvent(null, ['id' => $uid], new ValidationProviders());
335
                $validators = $eventDispatcher->dispatch($event, UserEvents::DELETE_VALIDATE)->getData();
336
                $hook = new ValidationHook($validators);
337
                $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_VALIDATE, $hook);
338
                $validators = $hook->getValidators();
339
                if ($validators->hasErrors()) {
340
                    $valid = false;
341
                }
342
            }
343
            if ($valid && $deleteConfirmationForm->isValid()) {
344
                // send email to 'denied' registrations. see MailHelper::sendNotification (regdeny) #2915
345
                $deletedUsers = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => $userIds]]);
346
                foreach ($deletedUsers as $deletedUser) {
347
                    $eventName = UsersConstant::ACTIVATED_ACTIVE === $deletedUser->getActivated() ? UserEvents::DELETE_ACCOUNT : RegistrationEvents::DELETE_REGISTRATION;
348
                    $eventDispatcher->dispatch(new GenericEvent($deletedUser->getUid()), $eventName);
349
                    $eventDispatcher->dispatch(new GenericEvent(null, ['id' => $deletedUser->getUid()]), UserEvents::DELETE_PROCESS);
350
                    $hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_PROCESS, new ProcessHook($deletedUser->getUid()));
351
                    $userRepository->removeAndFlush($deletedUser);
352
                }
353
                $this->addFlash(
354
                    'success',
355
                    /** @Desc("{count, plural,\n  one   {User deleted!}\n  other {# users deleted!}\n}") */
356
                    $this->getTranslator()->trans(
357
                        'plural_n.users.deleted',
358
                        ['%count%' => count($deletedUsers)]
359
                    )
360
                );
361
362
                return $this->redirectToRoute('zikulausersmodule_useradministration_list');
363
            }
364
        }
365
366
        return [
367
            'users' => $users,
368
            'form' => $deleteConfirmationForm->createView()
369
        ];
370
    }
371
372
    /**
373
     * @Route("/search")
374
     * @PermissionCheck("moderate")
375
     * @Theme("admin")
376
     * @Template("@ZikulaUsersModule/UserAdministration/search.html.twig")
377
     *
378
     * @return array|Response
379
     */
380
    public function searchAction(
381
        Request $request,
382
        UserRepositoryInterface $userRepository,
383
        VariableApiInterface $variableApi
384
    ) {
385
        $form = $this->createForm(SearchUserType::class, []);
386
        $form->handleRequest($request);
387
        if ($form->isSubmitted()) {
388
            $resultsForm = $this->createForm(DeleteType::class, [], [
389
                'choices' => $userRepository->queryBySearchForm($form->getData()),
390
                'action' => $this->generateUrl('zikulausersmodule_useradministration_delete')
391
            ]);
392
393
            return $this->render('@ZikulaUsersModule/UserAdministration/searchResults.html.twig', [
394
                'resultsForm' => $resultsForm->createView(),
395
                'mailForm' => $this->buildMailForm($variableApi)->createView()
396
            ]);
397
        }
398
399
        return [
400
            'form' => $form->createView()
401
        ];
402
    }
403
404
    /**
405
     * @Route("/mail")
406
     * @PermissionCheck({"$_zkModule::MailUsers", "::", "comment"})
407
     */
408
    public function mailUsersAction(
409
        Request $request,
410
        UserRepositoryInterface $userRepository,
411
        VariableApiInterface $variableApi,
412
        MailHelper $mailHelper
413
    ): RedirectResponse {
414
        $mailForm = $this->buildMailForm($variableApi);
415
        $mailForm->handleRequest($request);
416
        if ($mailForm->isSubmitted() && $mailForm->isValid()) {
417
            $data = $mailForm->getData();
418
            $users = $userRepository->query(['uid' => ['operator' => 'in', 'operand' => explode(',', $data['userIds'])]]);
419
            if (empty($users)) {
420
                throw new InvalidArgumentException($this->trans('No users found.'));
421
            }
422
            if ($mailHelper->mailUsers($users, $data)) {
423
                $this->addFlash('success', 'Done! Mail sent.');
424
            } else {
425
                $this->addFlash('error', 'Could not send mail.');
426
            }
427
        } else {
428
            $this->addFlash('error', 'Could not send mail.');
429
        }
430
431
        return $this->redirectToRoute('zikulausersmodule_useradministration_search');
432
    }
433
434
    private function buildMailForm(VariableApiInterface $variableApi): FormInterface
435
    {
436
        return $this->createForm(MailType::class, [
437
            'from' => $variableApi->getSystemVar('sitename'),
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