Test Setup Failed
Push — master ( 41e149...de913b )
by Craig
06:01
created

DeleteCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Zikula\UsersModule\Command;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Symfony\Component\Console\Command\Command;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Console\Style\SymfonyStyle;
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
12
use Zikula\Bundle\CoreBundle\Event\GenericEvent;
13
use Zikula\Bundle\HookBundle\Dispatcher\HookDispatcherInterface;
14
use Zikula\Bundle\HookBundle\Hook\ProcessHook;
15
use Zikula\GroupsModule\Constant;
16
use Zikula\GroupsModule\Entity\RepositoryInterface\GroupRepositoryInterface;
17
use Zikula\UsersModule\Constant as UsersConstant;
18
use Zikula\UsersModule\Entity\RepositoryInterface\UserRepositoryInterface;
19
use Zikula\UsersModule\Entity\UserEntity;
20
use Zikula\UsersModule\HookSubscriber\UserManagementUiHooksSubscriber;
21
use Zikula\UsersModule\RegistrationEvents;
22
use Zikula\UsersModule\UserEvents;
23
24
class DeleteCommand extends Command
25
{
26
    protected static $defaultName = 'zikula:users:delete';
27
28
    /**
29
     * @var EventDispatcherInterface
30
     */
31
    private $eventDispatcher;
32
33
    /**
34
     * @var HookDispatcherInterface
35
     */
36
    private $hookDispatcher;
37
38
    /**
39
     * @var UserRepositoryInterface
40
     */
41
    private $userRepository;
42
43
    /**
44
     * @var GroupRepositoryInterface
45
     */
46
    private $groupRespository;
47
48
    public function __construct(
49
        EventDispatcherInterface $eventDispatcher,
50
        HookDispatcherInterface $hookDispatcher,
51
        UserRepositoryInterface $userRepository,
52
        GroupRepositoryInterface $groupRespository
53
    ) {
54
        parent::__construct();
55
        $this->eventDispatcher = $eventDispatcher;
56
        $this->hookDispatcher = $hookDispatcher;
57
        $this->userRepository = $userRepository;
58
        $this->groupRespository = $groupRespository;
59
    }
60
61
    protected function configure()
62
    {
63
        $this
64
            ->addOption('uid', 'u', InputOption::VALUE_REQUIRED, 'User ID (int)')
65
            ->addOption('gid', 'g', InputOption::VALUE_REQUIRED, 'Group ID (int)')
66
            ->addOption('status', 's', InputOption::VALUE_REQUIRED, 'User activated status (A|I|P|M)')
67
            ->addOption('date', 'd', InputOption::VALUE_REQUIRED, 'filter by user_regdate *before* YYYYMMDDHHMMSS')
68
            ->setDescription('Delete one or more users')
69
            ->setHelp(
70
                <<<'EOT'
71
The <info>%command.name%</info> command deletes one or more users.
72
This command dispatches all events and hooks like a standard user deletion.
73
Do not use uid, gid or status simultaneously. Use only one
74
75
<info>php %command.full_name% -u 478</info>
76
77
This will delete user with uid 478.
78
79
Options:
80
<info>--uid</info> <comment>(int)</comment> delete one user by uid
81
82
<info>--gid</info> <comment>(int)</comment> delete all users in group gid (does not delete the actual group)
83
84
<info>--status</info> <comment>I|P|M</comment> delete users by status (active members cannot be deleted this way) (I=inactive, P=pending, M=marked for deletion)
85
86
<info>--date</info> <comment>YYYYMMDDHHMMSS</comment> before deleting, filter user collection by date <comment>before</comment> this date.
87
88
EOT
89
            );
90
    }
91
92
    protected function execute(InputInterface $input, OutputInterface $output): int
93
    {
94
        $io = new SymfonyStyle($input, $output);
95
        $uid = $input->getOption('uid');
96
        $gid = $input->getOption('gid');
97
        $status = $input->getOption('status');
98
        $date = $input->getOption('date');
99
100
        if (($uid && $gid) || ($uid && $status) || ($gid && $status)) {
101
            $io->error('Do not use more than one option or argument.');
102
103
            return 1;
104
        }
105
106
        if (isset($gid)) {
107
            if (Constant::GROUP_ID_USERS === (int) $gid) {
108
                if (!$io->confirm('You have selected to delete from the main user group. This is not recommended. Do you wish to proceed?', false)) {
109
                    $io->caution('Deletion cancelled');
110
111
                    return 0;
112
                }
113
            }
114
            $users = $this->groupRespository->find($gid)->getUsers();
115
        }
116
117
        if (isset($status)) {
118
            $statuses = [
119
                'I' => UsersConstant::ACTIVATED_INACTIVE,
120
                'P' => UsersConstant::ACTIVATED_PENDING_REG,
121
                'M' => UsersConstant::ACTIVATED_PENDING_DELETE
122
            ];
123
            if (!array_key_exists($status, $statuses)) {
124
                $io->error('Invalid status value');
125
126
                return 2;
127
            }
128
            $users = $this->userRepository->findBy(['activated' => $statuses[$status]]);
129
            $users = new ArrayCollection($users);
130
        }
131
132
        if (isset($uid)) {
133
            $user = $this->userRepository->find($uid);
134
            $users = new ArrayCollection([$user]);
135
        }
136
137
        if (isset($date)) {
138
            $date = \DateTime::createFromFormat('YmdHis', $date, new \DateTimeZone('UTC'));
0 ignored issues
show
Bug introduced by
It seems like $date can also be of type string[]; however, parameter $time of DateTime::createFromFormat() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

138
            $date = \DateTime::createFromFormat('YmdHis', /** @scrutinizer ignore-type */ $date, new \DateTimeZone('UTC'));
Loading history...
139
            $users = $users->filter(function (UserEntity $user) use ($date) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $users does not seem to be defined for all execution paths leading up to this point.
Loading history...
140
                return $user->getRegistrationDate() < $date;
141
            });
142
        }
143
144
        $adminUser = $this->userRepository->find(UsersConstant::USER_ID_ADMIN);
145
        if ($users->contains($adminUser)) {
146
            $users->removeElement($adminUser);
147
            $io->note(sprintf('The main admin user cannot be deleted (uname: %s)', $adminUser->getUname()));
148
        }
149
150
        if ($users->isEmpty()) {
151
            $io->error('No users found!');
152
153
            return 3;
154
        }
155
156
        $io->title('Zikula user deletion');
157
        $count = count($users);
158
        $io->progressStart($count);
159
        foreach ($users as $user) {
160
            $eventName = UsersConstant::ACTIVATED_ACTIVE === $user->getActivated() ? UserEvents::DELETE_ACCOUNT : RegistrationEvents::DELETE_REGISTRATION;
161
            $this->eventDispatcher->dispatch(new GenericEvent($user->getUid()), $eventName);
162
            $this->eventDispatcher->dispatch(new GenericEvent(null, ['id' => $user->getUid()]), UserEvents::DELETE_PROCESS);
163
            $this->hookDispatcher->dispatch(UserManagementUiHooksSubscriber::DELETE_PROCESS, new ProcessHook($user->getUid()));
164
            $this->userRepository->removeAndFlush($user);
165
            $io->progressAdvance();
166
        }
167
        $io->progressFinish();
168
169
        $io->success(sprintf('Success! %d users deleted.', $count));
170
171
        return 0;
172
    }
173
}
174