UserEditAction   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 74
dl 0
loc 134
rs 10
c 0
b 0
f 0
wmc 16

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
C handle() 0 106 15
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\Http\RequestHandlers;
21
22
use Fisharebest\Webtrees\Auth;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Auth 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\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...
24
use Fisharebest\Webtrees\FlashMessages;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\FlashMessages 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...
25
use Fisharebest\Webtrees\Http\Exceptions\HttpNotFoundException;
26
use Fisharebest\Webtrees\I18N;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\I18N 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...
27
use Fisharebest\Webtrees\Services\EmailService;
28
use Fisharebest\Webtrees\Services\TreeService;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Services\TreeService 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...
29
use Fisharebest\Webtrees\Services\UserService;
30
use Fisharebest\Webtrees\SiteUser;
31
use Fisharebest\Webtrees\User;
32
use Fisharebest\Webtrees\Validator;
33
use Psr\Http\Message\ResponseInterface;
34
use Psr\Http\Message\ServerRequestInterface;
35
use Psr\Http\Server\RequestHandlerInterface;
36
37
use function redirect;
38
use function route;
39
40
/**
41
 * Edit a user.
42
 */
43
class UserEditAction implements RequestHandlerInterface
44
{
45
    private EmailService $email_service;
46
47
    private UserService $user_service;
48
49
    private TreeService $tree_service;
50
51
    /**
52
     * @param EmailService $email_service
53
     * @param TreeService  $tree_service
54
     * @param UserService  $user_service
55
     */
56
    public function __construct(
57
        EmailService $email_service,
58
        TreeService $tree_service,
59
        UserService $user_service
60
    ) {
61
        $this->email_service = $email_service;
62
        $this->tree_service  = $tree_service;
63
        $this->user_service  = $user_service;
64
    }
65
66
    /**
67
     * @param ServerRequestInterface $request
68
     *
69
     * @return ResponseInterface
70
     */
71
    public function handle(ServerRequestInterface $request): ResponseInterface
72
    {
73
        $user           = Validator::attributes($request)->user();
74
        $user_id        = Validator::parsedBody($request)->integer('user_id');
75
        $username       = Validator::parsedBody($request)->string('username');
76
        $real_name      = Validator::parsedBody($request)->string('real_name');
77
        $email          = Validator::parsedBody($request)->string('email');
78
        $password       = Validator::parsedBody($request)->string('password');
79
        $theme          = Validator::parsedBody($request)->string('theme');
80
        $language       = Validator::parsedBody($request)->string('language');
81
        $timezone       = Validator::parsedBody($request)->string('timezone');
82
        $contact_method = Validator::parsedBody($request)->string('contact-method');
83
        $comment        = Validator::parsedBody($request)->string('comment');
84
        $auto_accept    = Validator::parsedBody($request)->boolean('auto_accept', false);
85
        $canadmin       = Validator::parsedBody($request)->boolean('canadmin', false);
86
        $visible_online = Validator::parsedBody($request)->boolean('visible-online', false);
87
        $verified       = Validator::parsedBody($request)->boolean('verified', false);
88
        $approved       = Validator::parsedBody($request)->boolean('approved', false);
89
90
        $edit_user = $this->user_service->find($user_id);
91
92
        if ($edit_user === null) {
93
            throw new HttpNotFoundException(I18N::translate('%s does not exist.', 'user_id:' . $user_id));
94
        }
95
96
        // We have just approved a user.  Tell them
97
        if ($approved && $edit_user->getPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED) !== '1') {
98
            I18N::init($edit_user->getPreference(UserInterface::PREF_LANGUAGE, 'en-US'));
99
100
            $base_url = Validator::attributes($request)->string('base_url');
101
102
            $this->email_service->send(
103
                new SiteUser(),
104
                $edit_user,
105
                Auth::user(),
106
                /* I18N: %s is a server name/URL */
107
                I18N::translate('New user at %s', $base_url),
108
                view('emails/approve-user-text', ['user' => $edit_user, 'base_url' => $base_url]),
109
                view('emails/approve-user-html', ['user' => $edit_user, 'base_url' => $base_url])
110
            );
111
        }
112
113
        $edit_user->setRealName($real_name);
114
        $edit_user->setPreference(UserInterface::PREF_THEME, $theme);
115
        $edit_user->setPreference(UserInterface::PREF_LANGUAGE, $language);
116
        $edit_user->setPreference(UserInterface::PREF_TIME_ZONE, $timezone);
117
        $edit_user->setPreference(UserInterface::PREF_CONTACT_METHOD, $contact_method);
118
        $edit_user->setPreference(UserInterface::PREF_NEW_ACCOUNT_COMMENT, $comment);
119
        $edit_user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, (string) $auto_accept);
120
        $edit_user->setPreference(UserInterface::PREF_IS_VISIBLE_ONLINE, (string) $visible_online);
121
        $edit_user->setPreference(UserInterface::PREF_IS_EMAIL_VERIFIED, (string) $verified);
122
        $edit_user->setPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED, (string) $approved);
123
124
        if ($password !== '') {
125
            $edit_user->setPassword($password);
126
        }
127
128
        // We cannot change our own admin status. Another admin will need to do it.
129
        if ($edit_user->id() !== $user->id()) {
130
            $edit_user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, $canadmin ? '1' : '');
131
        }
132
133
        foreach ($this->tree_service->all() as $tree) {
134
            $path_length = Validator::parsedBody($request)->integer('RELATIONSHIP_PATH_LENGTH' . $tree->id(), 0);
135
            $gedcom_id   = Validator::parsedBody($request)->string('gedcomid' . $tree->id(), '');
136
            $can_edit    = Validator::parsedBody($request)->string('canedit' . $tree->id(), '');
137
138
            // Do not allow a path length to be set if the individual ID is not
139
            if ($gedcom_id === '') {
140
                $path_length = 0;
141
            }
142
143
            $tree->setUserPreference($edit_user, UserInterface::PREF_TREE_ACCOUNT_XREF, $gedcom_id);
144
            $tree->setUserPreference($edit_user, UserInterface::PREF_TREE_ROLE, $can_edit);
145
            $tree->setUserPreference($edit_user, UserInterface::PREF_TREE_PATH_LENGTH, (string) $path_length);
146
        }
147
148
        // Changing the email address - make sure it isn't used by another user.
149
        if ($edit_user->email() !== $email) {
150
            $existing = $this->user_service->findByEmail($email);
151
152
            if ($existing instanceof User && $existing->id() !== $edit_user->id()) {
153
                $message = I18N::translate('Duplicate email address. A user with that email already exists.') . ' ' . $existing->email();
154
                FlashMessages::addMessage($message, 'danger');
155
156
                return redirect(route(UserEditPage::class, ['user_id' => $edit_user->id()]));
157
            }
158
        }
159
160
        // Changing the username - make sure it isn't used by another user
161
        if ($edit_user->userName() !== $username) {
162
            $existing = $this->user_service->findByUserName($username);
163
164
            if ($existing instanceof User && $existing->id() !== $edit_user->id()) {
165
                $message = I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.') . ' ' . $existing->userName();
166
                FlashMessages::addMessage($message, 'danger');
167
168
                return redirect(route(UserEditPage::class, ['user_id' => $edit_user->id()]));
169
            }
170
        }
171
172
        $edit_user
173
            ->setEmail($email)
174
            ->setUserName($username);
175
176
        return redirect(route(UserListPage::class));
177
    }
178
}
179