Passed
Push — main ( 742cd1...bef85d )
by Greg
06:43
created

UserList   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 100
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 54
dl 0
loc 100
rs 10
c 1
b 0
f 0
wmc 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A quoteCsvValue() 0 3 1
A __construct() 0 3 1
A formatTimestamp() 0 8 2
A configure() 0 11 1
A quoteCsvRow() 0 5 1
B execute() 0 54 8
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Cli\Commands;
21
22
use Fisharebest\Webtrees\Contracts\UserInterface;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Contracts\UserInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Fisharebest\Webtrees\Registry;
24
use Fisharebest\Webtrees\Services\UserService;
25
use Fisharebest\Webtrees\User;
26
use Symfony\Component\Console\Command\Command;
27
use Symfony\Component\Console\Helper\Table;
28
use Symfony\Component\Console\Input\InputInterface;
29
use Symfony\Component\Console\Input\InputOption;
30
use Symfony\Component\Console\Output\OutputInterface;
31
use Symfony\Component\Console\Style\SymfonyStyle;
32
33
use function addcslashes;
34
use function array_map;
35
use function implode;
36
37
final class UserList extends Command
38
{
39
    public function __construct(private readonly UserService $user_service)
40
    {
41
        parent::__construct();
42
    }
43
44
    protected function configure(): void
45
    {
46
        $this
47
            ->setName(name: 'user-list')
48
            ->setDescription(description: 'List users')
49
            ->addOption(
50
                name: 'format',
51
                shortcut: 'f',
52
                mode: InputOption::VALUE_REQUIRED,
53
                description: 'Output format (table, json, csv)',
54
                default: 'table',
55
            );
56
    }
57
58
    protected function execute(InputInterface $input, OutputInterface $output): int
59
    {
60
        $format = $input->getOption(name: 'format');
61
62
        $io = new SymfonyStyle(input: $input, output: $output);
63
64
        $users = $this->user_service->all()->sort(callback: fn ($a, $b) => $a->id() <=> $b->id());
65
66
        $headers = ['ID', 'Username', 'Real Name', 'Email', 'Admin', 'Approved', 'Verified', 'Language', 'Timezone', 'Contact', 'Registered', 'Last login'];
67
68
        $rows = $users->map(callback: fn (User $user): array => [
69
            'id'         => $user->id(),
70
            'username'   => $user->userName(),
71
            'real_name'  => $user->realName(),
72
            'email'      => $user->email(),
73
            'admin'      => $user->getPreference(setting_name: UserInterface::PREF_IS_ADMINISTRATOR) ? 'yes' : 'no',
74
            'approved'   => $user->getPreference(setting_name: UserInterface::PREF_IS_ACCOUNT_APPROVED) ? 'yes' : 'no',
75
            'verified'   => $user->getPreference(setting_name: UserInterface::PREF_IS_EMAIL_VERIFIED) ? 'yes' : 'no',
76
            'language'   => $user->getPreference(setting_name: UserInterface::PREF_LANGUAGE),
77
            'timezone'   => $user->getPreference(setting_name: UserInterface::PREF_TIME_ZONE),
78
            'contact'    => $user->getPreference(setting_name: UserInterface::PREF_CONTACT_METHOD),
79
            'registered' => $this->formatTimestamp(timestamp: (int) $user->getPreference(setting_name: UserInterface::PREF_TIMESTAMP_REGISTERED)),
80
            'last_login' => $this->formatTimestamp(timestamp: (int) $user->getPreference(setting_name: UserInterface::PREF_TIMESTAMP_ACTIVE)),
81
        ])
82
        ->values()
83
        ->all();
84
85
        switch ($format) {
86
            case 'table':
87
                $table = new Table(output: $output);
88
                $table->setHeaders(headers: $headers);
89
                $table->setRows(rows: $rows);
90
                $table->render();
91
                break;
92
93
            case 'csv':
94
                $output->writeln(messages: $this->quoteCsvRow(columns: $headers));
95
96
                foreach ($rows as $row) {
97
                    $output->writeln(messages: $this->quoteCsvRow(columns: $row));
98
                }
99
                break;
100
101
            case 'json':
102
                $output->writeln(messages: json_encode(value: $rows, flags: JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
103
                break;
104
105
            default:
106
                $io->error(message: 'Invalid format: ‘' . $format . '’');
107
108
                return Command::FAILURE;
109
        }
110
111
        return Command::SUCCESS;
112
    }
113
114
    private function formatTimestamp(int $timestamp): string
115
    {
116
117
        if ($timestamp === 0) {
118
            return '';
119
        }
120
121
        return Registry::timestampFactory()->make(timestamp: $timestamp)->format(format: 'Y-m-d H:i:s');
122
    }
123
124
    /**
125
     * @param array<string|int> $columns
126
     */
127
    private function quoteCsvRow(array $columns): string
128
    {
129
        $columns = array_map(callback: $this->quoteCsvValue(...), array: $columns);
130
131
        return implode(separator: ',', array: $columns);
132
    }
133
134
    private function quoteCsvValue(string|int $value): string
0 ignored issues
show
Unused Code introduced by
The method quoteCsvValue() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
135
    {
136
        return '"' . addcslashes(string: (string) $value, characters: '"') . '"';
137
    }
138
}
139