Passed
Branch — master (60084b)
by Greg
10:38
created

edituser.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2018 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees;
17
18
use DateTimeZone;
19
use Fisharebest\Webtrees\Controller\PageController;
20
use Fisharebest\Webtrees\Functions\FunctionsEdit;
21
22
/** @global Tree $WT_TREE */
23
global $WT_TREE;
24
25
require 'includes/session.php';
26
27
// Need to be logged in
28
if (!Auth::check()) {
29
	header('Location: index.php');
30
31
	return;
32
}
33
34
// Extract form variables
35
$action         = Filter::post('action', 'update|delete', '');
36
$username       = Filter::post('username');
37
$real_name      = Filter::post('real-name');
38
$password_1     = Filter::post('password-1', WT_REGEX_PASSWORD);
39
$password_2     = Filter::post('password-2', WT_REGEX_PASSWORD);
40
$email          = Filter::post('email');
41
$root_id        = Filter::post('root-id', WT_REGEX_XREF);
42
$theme          = Filter::post('theme', implode('|', array_keys(Theme::themeNames())), '');
43
$language       = Filter::post('language', null, '');
44
$time_zone      = Filter::post('time-zone', null, 'UTC');
45
$contact_method = Filter::post('contact-method', null, '');
46
$visible_online = Filter::postBool('visible-online');
47
48
// Respond to form action
49
if ($action !== '' && Filter::checkCsrf()) {
50
	switch ($action) {
51
		case 'update':
52
			if ($username !== Auth::user()->getUserName() && User::findByUserName($username)) {
53
				FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
54
			} elseif ($email !== Auth::user()->getEmail() && User::findByEmail($email)) {
55
				FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
56
			} else {
57
				// Change username
58
				if ($username !== Auth::user()->getUserName()) {
59
					Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $username);
60
					Auth::user()->setUserName($username);
61
				}
62
63
				// Change password
64
				if ($password_1 !== '' && $password_1 === $password_2) {
65
					Auth::user()->setPassword($password_1);
66
				}
67
68
				// Change other settings
69
				Auth::user()
70
					->setRealName($real_name)
71
					->setEmail($email)
72
					->setPreference('language', $language)
73
					->setPreference('TIMEZONE', $time_zone)
74
					->setPreference('contactmethod', $contact_method)
75
					->setPreference('visibleonline', $visible_online ? '1' : '0');
76
77
				Auth::user()->setPreference('theme', $theme);
78
79
				$WT_TREE->setUserPreference(Auth::user(), 'rootid', $root_id);
80
			}
81
			break;
82
83
		case 'delete':
84
			// An administrator can only be deleted by another administrator
85
			if (!Auth::user()->getPreference('canadmin')) {
86
				$currentUser = Auth::user();
87
				Auth::logout();
88
				$currentUser->delete();
89
			}
90
			break;
91
	}
92
93
	header('Location: edituser.php');
94
95
	return;
96
}
97
98
$controller = new PageController;
99
$controller
100
	->setPageTitle(I18N::translate('My account'))
101
	->pageHeader();
102
103
$my_individual_record = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'gedcomid'), $WT_TREE);
104
$default_individual   = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'rootid'), $WT_TREE);
105
106
// Form validation
107
?>
108
<script>
109
function checkform(frm) {
110
	if (frm.form_pass1.value!=frm.form_pass2.value) {
111
		alert("<?= I18N::translate('The passwords do not match.') ?>");
112
		frm.form_pass1.focus();
113
		return false;
114
	}
115
	if (frm.form_pass1.value.length > 0 && frm.form_pass1.value.length < 6) {
116
		alert("<?= I18N::translate('Passwords must contain at least 6 characters.') ?>");
117
		frm.form_pass1.focus();
118
		return false;
119
	}
120
	return true;
121
}
122
</script>
123
124
<h2><?= $controller->getPageTitle() ?></h2>
125
126
<form name="editform" method="post" onsubmit="return checkform(this);">
127
	<input type="hidden" name="action" value="update">
128
	<?= Filter::getCsrf() ?>
129
130
	<div class="row form-group">
131
		<label class="col-sm-3 col-form-label" for="username">
132
			<?= I18N::translate('Username') ?>
133
		</label>
134
		<div class="col-sm-9">
135
			<input type="text" class="form-control" id="username" name="username" value="<?= e(Auth::user()->getUserName()) ?>" dir="auto" aria-describedby="username-description" required>
136
			<p class="small text-muted" id="username-description">
137
				<?= I18N::translate('Usernames are case-insensitive and ignore accented letters, so that ā€œchloeā€, ā€œchloĆ«ā€, and ā€œChloeā€ are considered to be the same.') ?>
138
			</p>
139
		</div>
140
	</div>
141
142
	<div class="row form-group">
143
		<label class="col-sm-3 col-form-label" for="real-name">
144
			<?= I18N::translate('Real name') ?>
145
		</label>
146
		<div class="col-sm-9">
147
			<input type="text" class="form-control" id="real-name" name="real-name" value="<?= e(Auth::user()->getRealName()) ?>" dir="auto" aria-describedby="real-name-description" required>
148
			<p class="small text-muted" id="username-description">
149
				<?= I18N::translate('This is your real name, as you would like it displayed on screen.') ?>
150
			</p>
151
		</div>
152
	</div>
153
154
	<div class="row form-group">
155
		<label class="col-sm-3 col-form-label" for="gedcom-id">
156
			<?= I18N::translate('Individual record') ?>
157
		</label>
158
		<div class="col-sm-9">
159
			<select class="form-control" id="gedcom-id" aria-describedby="gedcom-id-description" disabled>
160
			<?php if ($my_individual_record instanceof Individual): ?>
161
				<option value=""><?= $my_individual_record->getFullName() ?></option>
162
			<?php else: ?>
163
				<option value=""><?= I18N::translateContext('unknown people', 'Unknown') ?></option>
164
			<?php endif ?>
165
			</select>
166
			<p class="small text-muted" id="gedcom-id-description">
167
				<?= I18N::translate('This is a link to your own record in the family tree. If this is the wrong individual, contact an administrator.') ?>
168
			</p>
169
		</div>
170
	</div>
171
172
	<div class="row form-group">
173
		<label class="col-sm-3 col-form-label" for="root-id">
174
			<?= I18N::translate('Default individual') ?>
175
		</label>
176
		<div class="col-sm-9">
177
			<?= FunctionsEdit::formControlIndividual($default_individual, ['id' => 'root-id', 'name' => 'root-id', 'aria-describedby' => 'root-id-description']) ?>
0 ignored issues
show
It seems like $default_individual defined by \Fisharebest\Webtrees\In...), 'rootid'), $WT_TREE) on line 104 can also be of type object<Fisharebest\Webtrees\GedcomRecord>; however, Fisharebest\Webtrees\Fun...formControlIndividual() does only seem to accept null|object<Fisharebest\Webtrees\Individual>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
178
			<p class="small text-muted" id="root-id-description">
179
				<?= I18N::translate('This individual will be selected by default when viewing charts and reports.') ?>
180
			</p>
181
		</div>
182
	</div>
183
184
	<div class="row form-group">
185
		<label class="col-sm-3 col-form-label" for="password-1">
186
			<?= I18N::translate('Password') ?>
187
		</label>
188
		<div class="col-sm-9">
189
			<input class="form-control" type="password" id="password-1" name="password-1" aria-describedby="password-1-description" autocomplete="new-password">
190
			<p class="small text-muted" id="password-1-description">
191
				<?= I18N::translate('Passwords must be at least 6 characters long and are case-sensitive, so that ā€œsecretā€ is different from ā€œSECRETā€.') ?>
192
				<br>
193
				<?= I18N::translate('Leave the password blank if you want to keep the current password.') ?>
194
			</p>
195
		</div>
196
	</div>
197
198
	<div class="row form-group">
199
		<label class="col-sm-3 col-form-label" for="password-2">
200
			<?= I18N::translate('Confirm password') ?>
201
		</label>
202
		<div class="col-sm-9">
203
			<input class="form-control" type="password" id="password-2" name="password-2" aria-describedby="password-2-description" autocomplete="new-password">
204
			<p class="small text-muted" id="password-2-description">
205
				<?= I18N::translate('Type your password again, to make sure you have typed it correctly.') ?>
206
			</p>
207
		</div>
208
	</div>
209
210
	<div class="row form-group">
211
		<label class="col-sm-3 col-form-label" for="language">
212
			<?= I18N::translate('Language') ?>
213
		</label>
214
		<div class="col-sm-9">
215
			<?= Bootstrap4::select(FunctionsEdit::optionsInstalledLanguages(), Auth::user()->getPreference('language'), ['id' => 'language', 'name' => 'language']) ?>
216
		</div>
217
	</div>
218
219
	<div class="row form-group">
220
		<label class="col-sm-3 col-form-label" for="time-zone">
221
			<?= I18N::translate('Time zone') ?>
222
		</label>
223
		<div class="col-sm-9">
224
			<?= Bootstrap4::select(array_combine(DateTimeZone::listIdentifiers(), DateTimeZone::listIdentifiers()), Auth::user()->getPreference('TIMEZONE', 'UTC'), ['id' => 'time-zone', 'name', 'time-zone', 'aria-describedby' => 'time-zone-description']) ?>
225
			<p class="small text-muted" id="time-zone-description">
226
				<?= I18N::translate('The time zone is required for date calculations, such as knowing today’s date.') ?>
227
			</p>
228
		</div>
229
	</div>
230
231
	<div class="row form-group">
232
		<label class="col-sm-3 col-form-label" for="email">
233
			<?= I18N::translate('Email address') ?>
234
		</label>
235
		<div class="col-sm-9">
236
			<input class="form-control" type="email" id="email" name="email" value="<?= e(Auth::user()->getEmail()) ?>" aria-describedby="email-description">
237
			<p class="small text-muted" id="email-description">
238
				<?= I18N::translate('This email address will be used to send password reminders, website notifications, and messages from other family members who are registered on the website.') ?>
239
			</p>
240
		</div>
241
	</div>
242
243
	<?php if (Site::getPreference('ALLOW_USER_THEMES') === '1'): ?>
244
	<div class="row form-group">
245
		<label class="col-sm-3 col-form-label" for="theme">
246
			<?= I18N::translate('Theme') ?>
247
		</label>
248
		<div class="col-sm-9">
249
			<select class="form-control" id="theme" name="theme" aria-describedby="theme-description">
250
				<option value="">
251
					<?= e(/* I18N: default option in list of themes */ I18N::translate('<default theme>')) ?>
252
				</option>
253
				<?php foreach (Theme::themeNames() as $theme_id => $theme_name): ?>
254
					<option value="<?= $theme_id ?>" <?= $theme_id === Auth::user()->getPreference('theme') ? 'selected' : '' ?>>
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $theme_id (integer) and \Fisharebest\Webtrees\Au...>getPreference('theme') (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
255
						<?= $theme_name ?>
256
					</option>
257
				<?php endforeach ?>
258
			</select>
259
			<p class="small text-muted" id="theme-description">
260
				<?= /* I18N: Help text for the "Default theme" site configuration setting */ I18N::translate('You can change the appearance of webtrees using ā€œthemesā€. Each theme has a different style, layout, color scheme, etc.') ?>
261
			</p>
262
		</div>
263
	</div>
264
	<?php endif ?>
265
266
	<div class="row form-group">
267
		<label class="col-sm-3 col-form-label" for="contact-method">
268
			<?= I18N::translate('Contact method') ?>
269
		</label>
270
		<div class="col-sm-9">
271
			<?= Bootstrap4::select(FunctionsEdit::optionsContactMethods(), Auth::user()->getPreference('contactmethod'), ['id' => 'contact-method', 'name' => 'contact-method', 'aria-describedby' => 'contact-method-description']) ?>
272
			<p class="small text-muted" id="contact-method-description">
273
				<?= I18N::translate('Site members can send each other messages. You can choose to how these messages are sent to you, or choose not receive them at all.') ?>
274
			</p>
275
		</div>
276
	</div>
277
278
	<fieldset class="form-group">
279
		<div class="row">
280
			<legend  class="col-sm-3 col-form-label">
281
				<?= I18N::translate('Visible online') ?>
282
			</legend>
283
			<div class="col-sm-9">
284
				<?= Bootstrap4::checkbox(I18N::translate('Visible to other users when online'), false, ['name' => 'visible-online', 'checked' => (bool) Auth::user()->getPreference('visibleonline'), 'aria-describedby' => 'visible-online-description']) ?>
0 ignored issues
show
array('name' => 'visible...le-online-description') is of type array<string,string|bool...describedby":"string"}>, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
285
				<p class="small text-muted" id="visible-online-description">
286
					<?= I18N::translate('You can choose whether to appear in the list of users who are currently signed-in.') ?>
287
				</p>
288
			</div>
289
		</div>
290
	</fieldset>
291
292
	<div class="row form-group">
293
		<div class="col-sm-9 offset-sm-3">
294
			<input class="btn btn-primary" type="submit" value="<?= I18N::translate('save') ?>">
295
		</div>
296
	</div>
297
</form>
298
299
<?php if (!Auth::user()->getPreference('canadmin')): ?>
300
<form method="post">
301
	<input type="hidden" name="action" value="delete">
302
	<?= Filter::getCsrf() ?>
303
	<div class="row form-group">
304
		<div class="col-sm-9 offset-sm-3">
305
			<input class="btn btn-danger" type="submit" value="<?= I18N::translate('Delete your account') ?>" data-confirm="<?= I18N::translate('Are you sure you want to delete ā€œ%sā€?', e(Auth::user()->getUserName())) ?>" onclick="return confirm(this.dataset.confirm);">
306
		</div>
307
	</div>
308
</form>
309
<?php endif ?>
310