1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 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 */
16namespace Fisharebest\Webtrees;
17
18use Fisharebest\Webtrees\Controller\PageController;
19use Fisharebest\Webtrees\Functions\Functions;
20use Rhumsaa\Uuid\Uuid;
21
22/**
23 * Defined in session.php
24 *
25 * @global Tree $WT_TREE
26 */
27global $WT_TREE;
28
29define('WT_SCRIPT_NAME', 'login.php');
30require './includes/session.php';
31
32// If we are already logged in, then go to the “Home page”
33if (Auth::check() && $WT_TREE) {
34    header('Location: ' . WT_BASE_URL);
35
36    return;
37}
38
39$controller = new PageController;
40
41$action          = Filter::post('action');
42$user_realname   = Filter::post('user_realname');
43$user_name       = Filter::post('user_name');
44$user_email      = Filter::postEmail('user_email');
45$user_password01 = Filter::post('user_password01', WT_REGEX_PASSWORD);
46$user_password02 = Filter::post('user_password02', WT_REGEX_PASSWORD);
47$user_comments   = Filter::post('user_comments');
48$user_password   = Filter::post('user_password');
49$user_hashcode   = Filter::post('user_hashcode');
50$url             = Filter::post('url'); // Not actually a URL - just a path
51$username        = Filter::post('username');
52$password        = Filter::post('password');
53
54// These parameters may come from the URL which is emailed to users.
55if (!$action) {
56    $action = Filter::get('action');
57}
58if (!$user_name) {
59    $user_name = Filter::get('user_name');
60}
61if (!$user_hashcode) {
62    $user_hashcode = Filter::get('user_hashcode');
63}
64if (!$url) {
65    $url = Filter::get('url');
66}
67
68$message = '';
69
70switch ($action) {
71    case 'login':
72        try {
73            if (!$_COOKIE) {
74                Log::addAuthenticationLog('Login failed (no session cookies): ' . $username);
75                throw new \Exception(I18N::translate('You cannot sign in because your browser does not accept cookies.'));
76            }
77
78            $user = User::findByIdentifier($username);
79
80            if (!$user) {
81                Log::addAuthenticationLog('Login failed (no such user/email): ' . $username);
82                throw new \Exception(I18N::translate('The username or password is incorrect.'));
83            }
84
85            if (!$user->checkPassword($password)) {
86                Log::addAuthenticationLog('Login failed (incorrect password): ' . $username);
87                throw new \Exception(I18N::translate('The username or password is incorrect.'));
88            }
89
90            if (!$user->getPreference('verified')) {
91                Log::addAuthenticationLog('Login failed (not verified by user): ' . $username);
92                throw new \Exception(I18N::translate('This account has not been verified. Please check your email for a verification message.'));
93            }
94
95            if (!$user->getPreference('verified_by_admin')) {
96                Log::addAuthenticationLog('Login failed (not approved by admin): ' . $username);
97                throw new \Exception(I18N::translate('This account has not been approved. Please wait for an administrator to approve it.'));
98            }
99
100            Auth::login($user);
101            Log::addAuthenticationLog('Login: ' . Auth::user()->getUserName() . '/' . Auth::user()->getRealName());
102            Auth::user()->setPreference('sessiontime', WT_TIMESTAMP);
103
104            Session::put('locale', Auth::user()->getPreference('language'));
105            Session::put('theme_id', Auth::user()->getPreference('theme'));
106            I18N::init(Auth::user()->getPreference('language'));
107
108            // We're logging in as an administrator
109            if (Auth::isAdmin()) {
110                // Check for updates
111                $latest_version_txt = Functions::fetchLatestVersion();
112                if (preg_match('/^[0-9.]+\|[0-9.]+\|/', $latest_version_txt)) {
113                    list($latest_version, $earliest_version, $download_url) = explode('|', $latest_version_txt);
114                    if (version_compare(WT_VERSION, $latest_version) < 0) {
115                        FlashMessages::addMessage(
116                        I18N::translate('A new version of webtrees is available.') .
117                        ' <a href="admin_site_upgrade.php"><b>' .
118                        I18N::translate('Upgrade to webtrees %s.', '<span dir="ltr">' . $latest_version . '</span>') .
119                        '</b></a>'
120                        );
121                    }
122                }
123            }
124
125            // If we were on a "home page", redirect to "my page"
126            if ($url === '' || strpos($url, 'index.php?ctype=gedcom') === 0) {
127                $url = 'index.php?ctype=user';
128                // Switch to a tree where we have a genealogy record (or keep to the current/default).
129                $tree = Database::prepare(
130                "SELECT gedcom_name FROM `##gedcom` JOIN `##user_gedcom_setting` USING (gedcom_id)" .
131                " WHERE setting_name = 'gedcomid' AND user_id = :user_id" .
132                " ORDER BY gedcom_id = :tree_id DESC"
133                )->execute(array(
134                    'user_id' => Auth::user()->getUserId(),
135                    'tree_id' => $WT_TREE->getTreeId(),
136                ))->fetchOne();
137                $url .= '&ged=' . Filter::escapeUrl($tree);
138            }
139
140            // Redirect to the target URL
141            header('Location: ' . WT_BASE_URL . $url);
142
143            return;
144        } catch (\Exception $ex) {
145            $message = $ex->getMessage();
146        }
147        // No break;
148
149    default:
150        $controller
151        ->setPageTitle(I18N::translate('Sign in'))
152        ->pageHeader()
153        ->addInlineJavascript('
154			jQuery("#new_passwd_form").hide();
155			jQuery("#passwd_click").click(function() {
156				jQuery("#new_passwd_form").slideToggle(100, function() {
157					jQuery("#new_passwd_username").focus()
158				});
159				return false;
160			});
161		');
162
163        echo '<div id="login-page">';
164        echo '<div id="login-text">';
165
166        echo '<p class="center"><strong>' . I18N::translate('Welcome to this genealogy website') . '</strong></p>';
167
168        switch (Site::getPreference('WELCOME_TEXT_AUTH_MODE')) {
169            case 1:
170                echo '<p>' . I18N::translate('Anyone with a user account can access this website.') . ' ' . I18N::translate('You can apply for an account using the link below.') . '</p>';
171                break;
172            case 2:
173                echo '<p>' . I18N::translate('You need to be an authorized user to access this website.') . ' ' . I18N::translate('You can apply for an account using the link below.') . '</p>';
174                break;
175            case 3:
176                echo '<p>' . I18N::translate('You need to be a family member to access this website.') . ' ' . I18N::translate('You can apply for an account using the link below.') . '</p>';
177            break;
178            case 4:
179                echo '<p style="white-space: pre-wrap;">', Site::getPreference('WELCOME_TEXT_AUTH_MODE_' . WT_LOCALE), '</p>';
180            break;
181        }
182
183        echo '</div>';
184        echo '<div id="login-box">';
185        if ($message) {
186            echo '<p class="error">', $message, '</p>';
187        }
188        echo '<form id="login-form" name="login-form" method="post" action="', WT_LOGIN_URL, '">
189		<input type="hidden" name="action" value="login">
190		<input type="hidden" name="url" value="', Filter::escapeHtml($url), '">';
191        echo '<div>
192			<label for="username">', I18N::translate('Username'),
193            '<input type="text" id="username" name="username" value="', Filter::escapeHtml($username), '" class="formField" autofocus>
194			</label>
195		</div>
196		<div>
197			<label for="password">', I18N::translate('Password'),
198                '<input type="password" id="password" name="password" class="formField">
199			</label>
200		</div>
201		<div>
202			<input type="submit" value="', /* I18N: A button label. */ I18N::translate('sign in'), '">
203		</div>
204		';
205        // Emails are sent from a TREE, not from a SITE. Therefore if there is no
206        // tree available (initial setup or all trees private), then we can't send email.
207        if ($WT_TREE) {
208            echo '
209			<div>
210				<a href="#" id="passwd_click">', I18N::translate('Forgot password?'), '</a>
211			</div>';
212            if (Site::getPreference('USE_REGISTRATION_MODULE')) {
213                echo '<div><a href="' . WT_LOGIN_URL . '?action=register">', I18N::translate('Request a new user account'), '</a></div>';
214            }
215        }
216        echo '</form>';
217
218        // hidden New Password block
219        echo '<div id="new_passwd">
220		<form id="new_passwd_form" name="new_passwd_form" action="' . WT_LOGIN_URL . '" method="post">
221		<input type="hidden" name="action" value="requestpw">
222		<h4>', I18N::translate('Request a new password'), '</h4>
223		<div>
224			<label for="new_passwd_username">', I18N::translate('Username or email address'),
225                '<input type="text" id="new_passwd_username" name="new_passwd_username" value="">
226			</label>
227		</div>
228		<div><input type="submit" value="', /* I18N: A button label. */ I18N::translate('continue'), '"></div>
229		</form>
230	</div>';
231        echo '</div>';
232
233        echo '</div>';
234        break;
235
236    case 'requestpw':
237        $user_name = Filter::post('new_passwd_username');
238        $user      = User::findByIdentifier($user_name);
239
240        if ($user) {
241            $passchars   = 'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
242            $user_new_pw = '';
243            $max         = strlen($passchars) - 1;
244            for ($i = 0; $i < 8; $i++) {
245                $index = rand(0, $max);
246                $user_new_pw .= $passchars[$index];
247            }
248
249            $user->setPassword($user_new_pw);
250            Log::addAuthenticationLog('Password request was sent to user: ' . $user->getUserName());
251
252            Mail::systemMessage(
253            $WT_TREE,
254            $user,
255            I18N::translate('Lost password request'),
256            I18N::translate('Hello %s…', $user->getRealNameHtml()) . Mail::EOL . Mail::EOL .
257            I18N::translate('A new password has been requested for your username.') . Mail::EOL . Mail::EOL .
258            I18N::translate('Username') . ": " . Filter::escapeHtml($user->getUserName()) . Mail::EOL .
259            I18N::translate('Password') . ": " . $user_new_pw . Mail::EOL . Mail::EOL .
260            I18N::translate('After you have signed in, select the “My account” link under the “My pages” menu and fill in the password fields to change your password.') . Mail::EOL . Mail::EOL .
261            '<a href="' . WT_BASE_URL . 'login.php?ged=' . $WT_TREE->getNameUrl() . '">' . WT_BASE_URL . 'login.php?ged=' . $WT_TREE->getNameUrl() . '</a>'
262            );
263
264            FlashMessages::addMessage(I18N::translate('A new password has been created and emailed to %s. You can change this password after you sign in.', Filter::escapeHtml($user_name)), 'success');
265        } else {
266            FlashMessages::addMessage(I18N::translate('There is no account with the username or email “%s”.', Filter::escapeHtml($user_name)), 'danger');
267        }
268        header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
269
270        return;
271    break;
272
273    case 'register':
274        if (!Site::getPreference('USE_REGISTRATION_MODULE')) {
275            header('Location: ' . WT_BASE_URL);
276
277            return;
278        }
279
280        $controller->setPageTitle(I18N::translate('Request a new user account'));
281
282        // The form parameters are mandatory, and the validation errors are shown in the client.
283        if (Session::get('good_to_send') && $user_name && $user_password01 && $user_password01 == $user_password02 && $user_realname && $user_email && $user_comments) {
284
285            // These validation errors cannot be shown in the client.
286            if (User::findByUserName($user_name)) {
287                FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
288            } elseif (User::findByEmail($user_email)) {
289                FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
290            } elseif (preg_match('/(?!' . preg_quote(WT_BASE_URL, '/') . ')(((?:ftp|http|https):\/\/)[a-zA-Z0-9.-]+)/', $user_comments, $match)) {
291                FlashMessages::addMessage(
292                I18N::translate('You are not allowed to send messages that contain external links.') . ' ' .
293                I18N::translate('You should delete the “%1$s” from “%2$s” and try again.', $match[2], $match[1])
294                );
295                Log::addAuthenticationLog('Possible spam registration from "' . $user_name . '"/"' . $user_email . '" comments="' . $user_comments . '"');
296            } else {
297                // Everything looks good - create the user
298                $controller->pageHeader();
299                Log::addAuthenticationLog('User registration requested for: ' . $user_name);
300
301                $user = User::create($user_name, $user_realname, $user_email, $user_password01);
302                $user
303                ->setPreference('language', WT_LOCALE)
304                ->setPreference('verified', '0')
305                ->setPreference('verified_by_admin', 0)
306                ->setPreference('reg_timestamp', date('U'))
307                ->setPreference('reg_hashcode', md5(Uuid::uuid4()))
308                ->setPreference('contactmethod', 'messaging2')
309                ->setPreference('comment', $user_comments)
310                ->setPreference('visibleonline', '1')
311                ->setPreference('auto_accept', '0')
312                ->setPreference('canadmin', '0')
313                ->setPreference('sessiontime', '0');
314
315                // Generate an email in the admin’s language
316                $webmaster = User::find($WT_TREE->getPreference('WEBMASTER_USER_ID'));
317                I18N::init($webmaster->getPreference('language'));
318
319                $mail1_body =
320                I18N::translate('Hello administrator…') . Mail::EOL . Mail::EOL .
321                /* I18N: %s is a server name/URL */
322                I18N::translate('A prospective user has registered with webtrees at %s.', WT_BASE_URL . ' ' . $WT_TREE->getTitleHtml()) . Mail::EOL . Mail::EOL .
323                I18N::translate('Username') . ' ' . Filter::escapeHtml($user->getUserName()) . Mail::EOL .
324                I18N::translate('Real name') . ' ' . $user->getRealNameHtml() . Mail::EOL .
325                I18N::translate('Email address') . ' ' . Filter::escapeHtml($user->getEmail()) . Mail::EOL .
326                I18N::translate('Comments') . ' ' . Filter::escapeHtml($user_comments) . Mail::EOL . Mail::EOL .
327                I18N::translate('The user has been sent an email with the information necessary to confirm the access request.') . Mail::EOL . Mail::EOL .
328                I18N::translate('You will be informed by email when this prospective user has confirmed the request. You can then complete the process by activating the username. The new user will not be able to sign in until you activate the account.');
329
330                $mail1_subject = /* I18N: %s is a server name/URL */ I18N::translate('New registration at %s', WT_BASE_URL . ' ' . $WT_TREE->getTitle());
331                I18N::init(WT_LOCALE);
332
333                echo '<div id="login-register-page">';
334
335                // Generate an email in the user’s language
336                $mail2_body =
337                I18N::translate('Hello %s…', $user->getRealNameHtml()) .
338                Mail::EOL . Mail::EOL .
339                /* I18N: %1$s is the site URL and %2$s is an email address */
340                I18N::translate('You (or someone claiming to be you) has requested an account at %1$s using the email address %2$s.', WT_BASE_URL . ' ' . $WT_TREE->getTitleHtml(), $user->getEmail()) .
341                Mail::EOL . Mail::EOL .
342                I18N::translate('Follow this link to verify your email address.') .
343                Mail::EOL . Mail::EOL .
344                '<a href="' . WT_LOGIN_URL . '?user_name=' . Filter::escapeUrl($user->getUserName()) . '&amp;user_hashcode=' . $user->getPreference('reg_hashcode') . '&amp;action=userverify&amp;ged=' . $WT_TREE->getNameUrl() . '">' .
345                WT_LOGIN_URL . "?user_name=" . Filter::escapeHtml($user->getUserName()) . "&amp;user_hashcode=" . urlencode($user->getPreference('reg_hashcode')) . '&amp;action=userverify&amp;ged=' . $WT_TREE->getNameHtml() .
346                '</a>' . Mail::EOL . Mail::EOL .
347                I18N::translate('Username') . " - " . Filter::escapeHtml($user->getUserName()) . Mail::EOL .
348                I18N::translate('Comments') . " - " . $user->getPreference('comment') . Mail::EOL .
349                I18N::translate('If you didn’t request an account, you can just delete this message.') . Mail::EOL;
350                $mail2_subject = /* I18N: %s is a server name/URL */ I18N::translate('Your registration at %s', WT_BASE_URL);
351                $mail2_to      = $user->getEmail();
352                $mail2_from    = $WT_TREE->getPreference('WEBTREES_EMAIL');
353
354                // Send user message by email only
355                Mail::send(
356                // “From:” header
357                $WT_TREE,
358                // “To:” header
359                $mail2_to,
360                $mail2_to,
361                // “Reply-To:” header
362                $mail2_from,
363                $mail2_from,
364                // Message body
365                $mail2_subject,
366                $mail2_body
367                );
368
369                // Send admin message by email and/or internal messaging
370                Mail::send(
371                // “From:” header
372                $WT_TREE,
373                // “To:” header
374                $webmaster->getEmail(),
375                $webmaster->getRealName(),
376                // “Reply-To:” header
377                $user->getEmail(),
378                $user->getRealName(),
379                // Message body
380                $mail1_subject,
381                $mail1_body
382                );
383                $mail1_method = $webmaster->getPreference('contact_method');
384                if ($mail1_method != 'messaging3' && $mail1_method != 'mailto' && $mail1_method != 'none') {
385                    Database::prepare("INSERT INTO `##message` (sender, ip_address, user_id, subject, body) VALUES (? ,? ,? ,? ,?)")
386                    ->execute(array($user->getEmail(), WT_CLIENT_IP, $webmaster->getUserId(), $mail1_subject, Filter::unescapeHtml($mail1_body)));
387                }
388
389                echo '<div class="confirm"><p>', I18N::translate('Hello %s…<br>Thank you for your registration.', $user->getRealNameHtml()), '</p>';
390                echo '<p>', I18N::translate('We will now send a confirmation email to the address <b>%s</b>. You must verify your account request by following instructions in the confirmation email. If you do not confirm your account request within seven days, your application will be rejected automatically. You will have to apply again.<br><br>After you have followed the instructions in the confirmation email, the administrator still has to approve your request before your account can be used.<br><br>To sign in to this website, you will need to know your username and password.', $user->getEmail()), '</p>';
391                echo '</div>';
392                echo '</div>';
393
394                return;
395            }
396        }
397
398        Session::put('good_to_send', true);
399        $controller
400        ->pageHeader()
401        ->addInlineJavascript('function regex_quote(str) {return str.replace(/[\\\\.?+*()[\](){}|]/g, "\\\\$&");}');
402
403        ?>
404    <div id="login-register-page">
405        <h2><?php echo $controller->getPageTitle(); ?></h2>
406
407        <?php if (Site::getPreference('SHOW_REGISTER_CAUTION')): ?>
408        <div id="register-text">
409            <?php echo I18N::translate('<div class="largeError">Notice:</div><div class="error">By completing and submitting this form, you agree:<ul><li>to protect the privacy of living individuals listed on our site;</li><li>and in the text box below, to explain to whom you are related, or to provide us with information on someone who should be listed on our website.</li></ul></div>'); ?>
410        </div>
411        <?php endif; ?>
412        <div id="register-box">
413            <form id="register-form" name="register-form" method="post" onsubmit="return checkform(this);" autocomplete="off">
414                <input type="hidden" name="action" value="register">
415                <h4><?php echo I18N::translate('All fields must be completed.'); ?></h4>
416                <hr>
417
418                <div>
419                    <label for="user_realname">
420                        <?php echo I18N::translate('Real name'); ?>
421                        <input type="text" id="user_realname" name="user_realname" required maxlength="64" value="<?php echo Filter::escapeHtml($user_realname); ?>" autofocus>
422                    </label>
423                    <p class="small text-muted">
424                        <?php echo I18N::translate('This is your real name, as you would like it displayed on screen.'); ?>
425                    </p>
426                </div>
427
428                <div>
429                    <label for="user_email">
430                        <?php echo I18N::translate('Email address'); ?>
431                        <input type="email" id="user_email" name="user_email" required maxlength="64" value="<?php echo Filter::escapeHtml($user_email); ?>" pattern=".*@[^.]+\..*">
432                    </label>
433                    <p class="small text-muted">
434                        <?php echo 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.'); ?>
435                    </p>
436                </div>
437
438                <div>
439                    <label for="username">
440                        <?php echo I18N::translate('Username'); ?>
441                        <input type="text" id="username" name="user_name" required maxlength="32" value="<?php Filter::escapeHtml($user_name); ?>">
442                    </label>
443                    <p class="small text-muted">
444                        <?php echo I18N::translate('Usernames are case-insensitive and ignore accented letters, so that “chloe”, “chloë”, and “Chloe” are considered to be the same.'); ?>
445                    </p>
446                </div>
447
448                <div>
449                    <label for="user_password01">
450                        <?php echo I18N::translate('Password'); ?>
451                        <input required
452                            type="password"
453                            id="user_password01" name="user_password01"
454                            value="<?php echo Filter::escapeHtml($user_password01); ?>"
455                            placeholder="<?php echo /* I18N: placeholder text for new-password field */ I18N::plural('Use at least %s character.', 'Use at least %s characters.', WT_MINIMUM_PASSWORD_LENGTH, I18N::number(WT_MINIMUM_PASSWORD_LENGTH)); ?>"
456                            pattern="<?php echo  WT_REGEX_PASSWORD; ?>"
457                            onchange="form.user_password02.pattern = regex_quote(this.value);"
458                        >
459                    </label>
460                    <p class="small text-muted">
461                        <?php echo I18N::translate('Passwords must be at least 6 characters long and are case-sensitive, so that “secret” is different from “SECRET”.'); ?>
462                    </p>
463                </div>
464
465                <div>
466                    <label for="user_password02">
467                        <?php echo I18N::translate('Confirm password'); ?>
468                        <input required
469                            type="password"
470                            id="user_password02" name="user_password02"
471                            value="<?php echo Filter::escapeHtml($user_password02); ?>"
472                            placeholder="<?php echo /* I18N: placeholder text for repeat-password field */ I18N::translate('Type the password again.'); ?>"
473                            pattern="<?php echo WT_REGEX_PASSWORD; ?>"
474                        >
475                    </label>
476                    <p class="small text-muted">
477                        <?php echo I18N::translate('Type your password again, to make sure you have typed it correctly.'); ?>
478                    </p>
479                </div>
480
481                <div>
482                    <label for="user_comments">
483                        <?php echo I18N::translate('Comments'); ?>
484                        <textarea required
485                            cols="50" rows="5"
486                            id="user_comments" name="user_comments"
487                            placeholder="<?php /* I18N: placeholder text for registration-comments field */ I18N::translate('Explain why you are requesting an account.'); ?>"
488                        ><?php echo Filter::escapeHtml($user_comments); ?></textarea>
489                    </label>
490                    <p class="small text-muted">
491                        <?php echo I18N::translate('Use this field to tell the site administrator why you are requesting an account and how you are related to the genealogy displayed on this site. You can also use this to enter any other comments you may have for the site administrator.'); ?>
492                    </p>
493                </div>
494
495                <hr>
496
497                <div id="registration-submit">
498                    <input type="submit" value="<?php echo I18N::translate('continue'); ?>">
499                </div>
500            </form>
501        </div>
502    </div>
503        <?php
504        break;
505
506    case 'userverify':
507        if (!Site::getPreference('USE_REGISTRATION_MODULE')) {
508            header('Location: ' . WT_BASE_URL);
509
510            return;
511        }
512
513        // Change to the new user’s language
514        $user = User::findByUserName($user_name);
515
516        I18N::init($user->getPreference('language'));
517
518        $controller->setPageTitle(I18N::translate('User verification'));
519        $controller->pageHeader();
520
521        echo '<div id="login-register-page">
522		<form id="verify-form" name="verify-form" method="post" action="', WT_LOGIN_URL, '">
523			<input type="hidden" name="action" value="verify_hash">
524			<h4>', I18N::translate('User verification'), '</h4>
525			<div>
526				<label for="username">', I18N::translate('Username'), '</label>
527				<input type="text" id="username" name="user_name" value="', $user_name, '">
528			</div>
529			<div>
530			<label for="user_password">', I18N::translate('Password'), '</label>
531			<input type="password" id="user_password" name="user_password" value="" autofocus>
532			</div>
533			<div>
534			<label for="user_hashcode">', I18N::translate('Verification code'), '</label>
535			<input type="text" id="user_hashcode" name="user_hashcode" value="', $user_hashcode, '">
536			</div>
537			<div>
538				<input type="submit" value="', I18N::translate('Send'), '">
539			</div>
540		</form>
541	</div>';
542        break;
543
544    case 'verify_hash':
545        if (!Site::getPreference('USE_REGISTRATION_MODULE')) {
546            header('Location: ' . WT_BASE_URL);
547
548            return;
549        }
550
551        // switch language to webmaster settings
552        $webmaster = User::find($WT_TREE->getPreference('WEBMASTER_USER_ID'));
553        I18N::init($webmaster->getPreference('language'));
554
555        $user          = User::findByUserName($user_name);
556        $edit_user_url = WT_BASE_URL . "admin_users.php?action=edit&amp;user_id=" . $user->getUserId();
557        $mail1_body    =
558        I18N::translate('Hello administrator…') .
559        Mail::EOL . Mail::EOL .
560        /* I18N: %1$s is a real-name, %2$s is a username, %3$s is an email address */ I18N::translate(
561            'A new user (%1$s) has requested an account (%2$s) and verified an email address (%3$s).',
562            $user->getRealNameHtml(),
563            Filter::escapeHtml($user->getUserName()),
564            Filter::escapeHtml($user->getEmail())
565            ) .
566            Mail::EOL . Mail::EOL .
567            I18N::translate('You need to review the account details.') .
568            Mail::EOL . Mail::EOL .
569            '<a href="' . $edit_user_url . '">' . $edit_user_url . '</a>' .
570            Mail::EOL . Mail::EOL .
571            /* I18N: You need to: */ I18N::translate('Set the status to “approved”.') .
572            Mail::EOL .
573            /* I18N: You need to: */ I18N::translate('Set the access level for each tree.') .
574            Mail::EOL .
575            /* I18N: You need to: */ I18N::translate('Link the user account to an individual.');
576
577        $mail1_subject = /* I18N: %s is a server name/URL */ I18N::translate('New user at %s', WT_BASE_URL . ' ' . $WT_TREE->getTitle());
578
579        // Change to the new user’s language
580        I18N::init($user->getPreference('language'));
581
582        $controller->setPageTitle(I18N::translate('User verification'));
583        $controller->pageHeader();
584
585        echo '<div id="login-register-page">';
586        echo '<h2>' . I18N::translate('User verification') . '</h2>';
587        echo '<div id="user-verify">';
588        if ($user && $user->checkPassword($user_password) && $user->getPreference('reg_hashcode') === $user_hashcode) {
589            Mail::send(
590            // “From:” header
591                $WT_TREE,
592                // “To:” header
593                $webmaster->getEmail(),
594                $webmaster->getRealName(),
595                // “Reply-To:” header
596                $WT_TREE->getPreference('WEBTREES_EMAIL'),
597                $WT_TREE->getPreference('WEBTREES_EMAIL'),
598                // Message body
599                $mail1_subject,
600                $mail1_body
601            );
602            $mail1_method = $webmaster->getPreference('CONTACT_METHOD');
603            if ($mail1_method != 'messaging3' && $mail1_method != 'mailto' && $mail1_method != 'none') {
604                    Database::prepare("INSERT INTO `##message` (sender, ip_address, user_id, subject, body) VALUES (? ,? ,? ,? ,?)")
605                        ->execute(array($user_name, WT_CLIENT_IP, $webmaster->getUserId(), $mail1_subject, Filter::unescapeHtml($mail1_body)));
606            }
607
608            $user
609                ->setPreference('verified', '1')
610                ->setPreference('reg_timestamp', date('U'))
611                ->deletePreference('reg_hashcode');
612
613            Log::addAuthenticationLog('User ' . $user_name . ' verified their email address');
614
615            echo '<p>', I18N::translate('You have confirmed your request to become a registered user.'), '</p>';
616            echo '<p>', I18N::translate('The administrator has been informed. As soon as they give you permission to sign in, you can sign in with your username and password.'), '</p>';
617        } else {
618            echo '<p class="warning">';
619            echo I18N::translate('Could not verify the information you entered. Please try again or contact the site administrator for more information.');
620            echo '</p>';
621        }
622        echo '</div>';
623        echo '</div>';
624        break;
625}
626