feat: Fixes #6634 CORS content-encoding (#6636)
[openemr.git] / portal / index.php
blobd530de6d0c2c8259d1444f2d2dfaa797f37ee26c
1 <?php
3 /**
4 * import_template.php
6 * @package OpenEMR
7 * @link https://www.open-emr.org
8 * @author Cassian LUP <cassi.lup@gmail.com>
9 * @author Jerry Padgett <sjpadgett@gmail.com>
10 * @author Brady Miller <brady.g.miller@gmail.com>
11 * @author Tyler Wrenn <tyler@tylerwrenn.com>
12 * @copyright Copyright (c) 2011 Cassian LUP <cassi.lup@gmail.com>
13 * @copyright Copyright (c) 2016-2023 Jerry Padgett <sjpadgett@gmail.com>
14 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2020 Tyler Wrenn <tyler@tylerwrenn.com>
16 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
19 // prevent UI redressing
20 Header("X-Frame-Options: DENY");
21 Header("Content-Security-Policy: frame-ancestors 'none'");
23 //setting the session & other config options
25 // Will start the (patient) portal OpenEMR session/cookie.
27 require_once __DIR__ . "/../src/Common/Session/SessionUtil.php";
28 OpenEMR\Common\Session\SessionUtil::portalSessionStart();
30 //don't require standard openemr authorization in globals.php
31 $ignoreAuth_onsite_portal = true;
33 //includes
35 require_once '../interface/globals.php';
36 require_once __DIR__ . "/lib/appsql.class.php";
37 $logit = new ApplicationTable();
39 use OpenEMR\Common\Auth\Exception\OneTimeAuthException;
40 use OpenEMR\Common\Auth\Exception\OneTimeAuthExpiredException;
41 use OpenEMR\Common\Auth\OneTimeAuth;
42 use OpenEMR\Common\Crypto\CryptoGen;
43 use OpenEMR\Common\Csrf\CsrfUtils;
44 use OpenEMR\Common\Logging\EventAuditLogger;
45 use OpenEMR\Common\Logging\SystemLogger;
46 use OpenEMR\Common\Twig\TwigContainer;
47 use OpenEMR\Core\Header;
48 use OpenEMR\Services\LogoService;
50 //For redirect if the site on session does not match
51 $landingpage = "index.php?site=" . urlencode($_SESSION['site_id']);
52 $logoService = new LogoService();
53 $logoSrc = $logoService->getLogo("portal/login/primary");
55 // allow both get and post redirect params here... everything will be sanitized in get_patient_info.php before we
56 // actually do anything with the redirect
57 // this value should already be url encoded.
58 $redirectUrl = $_REQUEST['redirect'] ?? '';
60 //exit if portal is turned off
61 if (!(isset($GLOBALS['portal_onsite_two_enable'])) || !($GLOBALS['portal_onsite_two_enable'])) {
62 echo xlt('Patient Portal is turned off');
63 exit;
65 $auth['portal_pwd'] = '';
66 if (isset($_GET['woops'])) {
67 unset($_GET['woops']);
68 unset($_SESSION['password_update']);
72 * Patient for onetime is verified when token redirect is decoded.
73 * The embedded pid in token is compared to the token looked up result pid.
74 * Also verified as the portal account id is rebuilt from patient data
75 * and compared to portal credential account id lookup.
76 * */
77 if (!empty($_REQUEST['service_auth'] ?? null)) {
78 if (!empty($_GET['service_auth'] ?? null)) {
79 // we have to setup the csrf key to preven CSRF Login attacks
80 // we also implement this mechanism in order to handle Same-Site cookie blocking when being referred by
81 // an external site domain. We used to auto process via GET but now we submit via the POST in order to make it
82 // a same site cookie origin request. This is a workaround for the Same-Site cookie blocking.
83 CsrfUtils::setupCsrfKey();
84 $twig = new TwigContainer(null, $GLOBALS['kernel']);
85 echo $twig->getTwig()->render('portal/login/autologin.html.twig', [
86 'action' => $GLOBALS['web_root'] . '/portal/index.php',
87 'service_auth' => $_GET['service_auth'],
88 'target' => $_GET['target'] ?? null,
89 'csrf_token' => CsrfUtils::collectCsrfToken('autologin'),
90 'pagetitle' => xl("OpenEMR Patient Portal"),
91 'images_static_relative' => $GLOBALS['images_static_relative'] ?? ''
92 ]);
93 exit;
94 } elseif (!empty($_POST['service_auth'] ?? null)) {
95 $token = $_POST['service_auth'];
96 $redirect_token = $_POST['target'] ?? null;
97 $csrfToken = $_POST['csrf_token'] ?? null;
98 try {
99 if (!CsrfUtils::verifyCsrfToken($csrfToken, 'autologin')) {
100 throw new OneTimeAuthException('Invalid CSRF token');
102 $oneTime = new OneTimeAuth();
103 $auth = $oneTime->processOnetime($token, $redirect_token);
104 $logit->portalLog('onetime login attempt', $auth['pid'], 'patient logged in and redirecting', '', '1');
105 exit();
106 } catch (OneTimeAuthExpiredException $exception) {
107 $logit->portalLog(
108 'onetime login attempt',
109 $exception->getPid() ?? '',
110 ':invalid one time',
114 // do we want a separate message that their token has expired?
115 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
116 header('Location: ' . $landingpage . '&oe');
117 exit();
118 } catch (OneTimeAuthException $exception) {
119 $logit->portalLog(
120 'onetime login attempt',
121 $exception->getPid() ?? '',
122 ':invalid one time',
126 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
127 header('Location: ' . $landingpage . '&oi');
128 exit();
130 } else {
131 (new SystemLogger())->errorLogCaller("Invalid service_auth request - should never reach here");
132 exit();
136 if (!empty($_GET['forward_email_verify'])) {
137 if (empty($GLOBALS['portal_onsite_two_register']) || empty($GLOBALS['google_recaptcha_site_key']) || empty($GLOBALS['google_recaptcha_secret_key'])) {
138 (new SystemLogger())->debug("registration not supported, so stopped attempt to use forward_email_verify token");
139 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
140 header('Location: ' . $landingpage . '&w&u');
141 exit();
144 $crypto = new CryptoGen();
145 if (!$crypto->cryptCheckStandard($_GET['forward_email_verify'])) {
146 (new SystemLogger())->debug("illegal token, so stopped attempt to use forward_email_verify token");
147 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
148 header('Location: ' . $landingpage . '&w&u');
149 exit();
152 $token_one_time = $crypto->decryptStandard($_GET['forward_email_verify'], null, 'drive', 6);
153 if (empty($token_one_time)) {
154 (new SystemLogger())->debug("unable to decrypt token, so stopped attempt to use forward_email_verify token");
155 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
156 header('Location: ' . $landingpage . '&w&u');
157 exit();
160 $sqlResource = sqlStatementNoLog("SELECT `id`, `token_onetime`, `fname`, `mname`, `lname`, `dob`, `email`, `language` FROM `verify_email` WHERE `active` = 1 AND `token_onetime` LIKE BINARY ?", [$token_one_time . '%']);
161 if (sqlNumRows($sqlResource) > 1) {
162 (new SystemLogger())->debug("active token (" . $token_one_time . ") found more than once, so stopped attempt to use forward_email_verify token");
163 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "active token (" . $token_one_time . ") found more than once, so stopped attempt to use forward_email_verify token");
164 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
165 header('Location: ' . $landingpage . '&w&u');
166 exit();
168 if (!sqlNumRows($sqlResource)) {
169 (new SystemLogger())->debug("active token (" . $token_one_time . ") not found, so stopped attempt to use forward_email_verify token");
170 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "active token (" . $token_one_time . ") not found, so stopped attempt to use forward_email_verify token");
171 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
172 header('Location: ' . $landingpage . '&w&u');
173 exit();
175 $sqlVerify = sqlFetchArray($sqlResource);
176 if (empty($sqlVerify['id']) || empty($sqlVerify['token_onetime'])) {
177 (new SystemLogger())->debug("active token (" . $token_one_time . ") not properly set up, so stopped attempt to use forward_email_verify token");
178 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "active token (" . $token_one_time . ") not properly set up, so stopped attempt to use forward_email_verify token");
179 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
180 header('Location: ' . $landingpage . '&w&u');
181 exit();
183 // have "used" token, so now make it inactive
184 sqlStatementNoLog("UPDATE `verify_email` SET `active` = 0 WHERE `id` = ?", [$sqlVerify['id']]);
186 $validateTime = hex2bin(str_replace($token_one_time, '', $sqlVerify['token_onetime']));
187 if ($validateTime <= time()) {
188 (new SystemLogger())->debug("active token (" . $token_one_time . ") has expired, so stopped attempt to use forward_email_verify token");
189 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "active token (" . $token_one_time . ") has expired, so stopped attempt to use forward_email_verify token");
190 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
191 die(xlt("Your email verification link has expired. Reset and try again."));
194 if (!empty($sqlVerify['fname']) && !empty($sqlVerify['lname']) && !empty($sqlVerify['dob']) && !empty($sqlVerify['email']) && !empty($sqlVerify['language'])) {
195 // token has passed and have all needed data
196 $fnameRegistration = $sqlVerify['fname'];
197 $_SESSION['fnameRegistration'] = $fnameRegistration;
198 $mnameRegistration = $sqlVerify['mname'] ?? '';
199 $_SESSION['mnameRegistration'] = $mnameRegistration;
200 $lnameRegistration = $sqlVerify['lname'];
201 $_SESSION['lnameRegistration'] = $lnameRegistration;
202 $dobRegistration = $sqlVerify['dob'];
203 $_SESSION['dobRegistration'] = $dobRegistration;
204 $emailRegistration = $sqlVerify['email'];
205 $_SESSION['emailRegistration'] = $emailRegistration;
206 $languageRegistration = $sqlVerify['language'];
207 $_SESSION['language_choice'] = (int)($languageRegistration ?? 1);
208 $portalRegistrationAuthorization = true;
209 $_SESSION['token_id_holder'] = $sqlVerify['id'];
210 (new SystemLogger())->debug("token worked for forward_email_verify token, now on to registration");
211 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 1, "token (" . $token_one_time . ") was successful for forward_email_verify token");
212 require_once(__DIR__ . "/account/register.php");
213 exit();
214 } else {
215 (new SystemLogger())->debug("active token (" . $token_one_time . ") did not have all required data, so stopped attempt to use forward_email_verify token");
216 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "active token (" . $token_one_time . ") did not have all required data, so stopped attempt to use forward_email_verify token");
217 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
218 header('Location: ' . $landingpage . '&w&u');
219 exit();
221 } elseif (isset($_GET['forward'])) {
222 if ((empty($GLOBALS['portal_two_pass_reset']) && empty($GLOBALS['portal_onsite_two_register'])) || empty($GLOBALS['google_recaptcha_site_key']) || empty($GLOBALS['google_recaptcha_secret_key'])) {
223 (new SystemLogger())->debug("reset password and registration not supported, so stopped attempt to use forward token");
224 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
225 header('Location: ' . $landingpage . '&w&u');
226 exit();
228 $auth = false;
229 if (strlen($_GET['forward']) >= 64) {
230 $crypto = new CryptoGen();
231 if ($crypto->cryptCheckStandard($_GET['forward'])) {
232 $one_time = $crypto->decryptStandard($_GET['forward'], null, 'drive', 6);
233 if (!empty($one_time)) {
234 $auth = sqlQueryNoLog("Select * From patient_access_onsite Where portal_onetime Like BINARY ?", array($one_time . '%'));
238 if ($auth === false) {
239 error_log("PORTAL ERROR: " . errorLogEscape('One time reset:' . $_GET['forward']), 0);
240 $logit->portalLog('login attempt', '', ($_GET['forward'] . ':invalid one time'), '', '0');
241 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
242 header('Location: ' . $landingpage . '&w&u');
243 exit();
245 $parse = str_replace($one_time, '', $auth['portal_onetime']);
246 $validate = hex2bin(substr($parse, 6));
247 if ($validate <= time()) {
248 error_log("PORTAL ERROR: " . errorLogEscape('One time reset link expired. Dying.'), 0);
249 $logit->portalLog('password reset attempt', '', ($_POST['uname'] . ':link expired'), '', '0');
250 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
251 die(xlt("Your one time credential reset link has expired. Reset and try again.") . "time:$validate time:" . time());
253 $_SESSION['pin'] = substr($parse, 0, 6);
254 $_SESSION['forward'] = $auth['portal_onetime'];
255 $_SESSION['portal_username'] = $auth['portal_username'];
256 $_SESSION['portal_login_username'] = $auth['portal_login_username'];
257 $_SESSION['password_update'] = 2;
258 $_SESSION['onetime'] = $auth['portal_pwd'];
259 unset($auth);
261 // security measure -- will check on next page.
262 $_SESSION['itsme'] = 1;
266 // Deal with language selection
268 // collect default language id (skip this if this is a password update or reset)
269 if (!(isset($_SESSION['password_update']) || (!empty($GLOBALS['portal_two_pass_reset']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key']) && isset($_GET['requestNew'])))) {
270 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array($GLOBALS['language_default']));
271 for ($iter = 0; $row = sqlFetchArray($res2); $iter++) {
272 $result2[$iter] = $row;
275 if (count($result2) == 1) {
276 $defaultLangID = $result2[0]["lang_id"];
277 $defaultLangName = $result2[0]["lang_description"];
278 } else {
279 //default to english if any problems
280 $defaultLangID = 1;
281 $defaultLangName = "English";
284 // set session variable to default so login information appears in default language
285 $_SESSION['language_choice'] = $defaultLangID;
286 // collect languages if showing language menu
287 if ($GLOBALS['language_menu_login']) {
288 // sorting order of language titles depends on language translation options.
289 $mainLangID = empty($_SESSION['language_choice']) ? '1' : $_SESSION['language_choice'];
290 // Use and sort by the translated language name.
291 $sql = "SELECT ll.lang_id, " .
292 "IF(LENGTH(ld.definition),ld.definition,ll.lang_description) AS trans_lang_description, " .
293 "ll.lang_description " .
294 "FROM lang_languages AS ll " .
295 "LEFT JOIN lang_constants AS lc ON lc.constant_name = ll.lang_description " .
296 "LEFT JOIN lang_definitions AS ld ON ld.cons_id = lc.cons_id AND " .
297 "ld.lang_id = ? " .
298 "ORDER BY IF(LENGTH(ld.definition),ld.definition,ll.lang_description), ll.lang_id";
299 $res3 = SqlStatement($sql, array($mainLangID));
300 for ($iter = 0; $row = sqlFetchArray($res3); $iter++) {
301 $result3[$iter] = $row;
303 if (count($result3) == 1) {
304 //default to english if only return one language
305 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='1' />\n";
307 } else {
308 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='" . attr($defaultLangID) . "' />\n";
312 <!DOCTYPE html>
313 <html>
314 <head>
315 <title><?php echo xlt('Patient Portal Login'); ?></title>
316 <?php
317 Header::setupHeader(['no_main-theme', 'portal-theme', 'datetime-picker']);
319 <script>
320 function checkUserName() {
321 let vacct = document.getElementById('uname').value;
322 let vsuname = document.getElementById('login_uname').value;
323 if (vsuname.length < 12) {
324 alert(<?php echo xlj('User Name must be at least 12 characters!'); ?>);
325 return false;
327 let data = {
328 'action': 'userIsUnique',
329 'account': vacct,
330 'loginUname': vsuname
332 $.ajax({
333 type: 'GET',
334 url: './account/account.php',
335 data: data
336 }).done(function (rtn) {
337 if (rtn === '1') {
338 return true;
340 alert(<?php echo xlj('Log In Name is unavailable. Try again!'); ?>);
341 return false;
345 function process() {
346 if (!(validate())) {
347 alert(<?php echo xlj('Field(s) are missing!'); ?>);
348 return false;
350 return true;
353 function validate() {
354 let pass = true;
356 if (document.getElementById('uname').value == "") {
357 $('#uname').addClass('is-invalid');
358 pass = false;
360 if (document.getElementById('pass').value == "") {
361 $('#pass').addClass('is-invalid');
362 pass = false;
364 return pass;
367 function process_new_pass() {
368 if (!(validate_new_pass())) {
369 alert(<?php echo xlj('Field(s) are missing!'); ?>);
370 return false;
372 if (document.getElementById('pass_new').value != document.getElementById('pass_new_confirm').value) {
373 alert(<?php echo xlj('The new password fields are not the same.'); ?>);
374 return false;
376 if (document.getElementById('pass').value == document.getElementById('pass_new').value) {
377 alert(<?php echo xlj('The new password can not be the same as the current password.'); ?>);
378 return false;
382 function validate_new_pass() {
383 var pass = true;
384 if (document.getElementById('uname').value == "") {
385 $('#uname').addClass('is-invalid');
386 pass = false;
388 if (document.getElementById('pass').value == "") {
389 $('#pass').addClass('is-invalid');
390 pass = false;
392 if (document.getElementById('pass_new').value == "") {
393 $('#pass_new').addClass('is-invalid');
394 pass = false;
396 if (document.getElementById('pass_new_confirm').value == "") {
397 $('#pass_new_confirm').addClass('is-invalid');
398 pass = false;
400 return pass;
402 </script>
404 <?php if (!empty($GLOBALS['portal_two_pass_reset']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key']) && isset($_GET['requestNew'])) { ?>
405 <script src="https://www.google.com/recaptcha/api.js" async defer></script>
406 <script>
407 function enableVerifyBtn() {
408 document.getElementById("submitRequest").disabled = false;
410 </script>
411 <?php // add csrf mechanism for the password reset ui
412 CsrfUtils::setupCsrfKey();
414 <?php } ?>
416 </head>
417 <body class="login">
418 <div id="wrapper" class="row mx-auto">
419 <?php if (isset($_SESSION['password_update']) || isset($_GET['password_update'])) {
420 $_SESSION['password_update'] = 1;
422 <h2 class="title"><?php echo xlt('Please Enter New Credentials'); ?></h2>
423 <form class="form pb-5" action="get_patient_info.php" method="POST" onsubmit="return process_new_pass()">
424 <input style="display: none" type="text" name="dummyuname" />
425 <input style="display: none" type="password" name="dummypass" />
426 <?php if (isset($redirectUrl)) { ?>
427 <input id="redirect" type="hidden" name="redirect" value="<?php echo attr($redirectUrl); ?>" />
428 <?php } ?>
429 <div class="form-row my-3">
430 <label class="col-md-2 col-form-label" for="uname"><?php echo xlt('Account Name'); ?></label>
431 <div class="col-md">
432 <input class="form-control" name="uname" id="uname" type="text" readonly autocomplete="none" value="<?php echo attr($_SESSION['portal_username']); ?>" />
433 </div>
434 </div>
435 <div class="form-row my-3">
436 <label class="col-md-2 col-form-label" for="login_uname"><?php echo xlt('Use Username'); ?></label>
437 <div class="col-md">
438 <input class="form-control" name="login_uname" id="login_uname" type="text" autofocus autocomplete="none" title="<?php echo xla('Please enter a username of 12 to 80 characters. Recommended to include symbols and numbers but not required.'); ?>" placeholder="<?php echo xla('Must be 12 to 80 characters'); ?>" pattern=".{12,80}" value="<?php echo attr($_SESSION['portal_login_username']); ?>" onblur="checkUserName()" />
439 </div>
440 </div>
441 <div class="form-row my-3">
442 <label class="col-md-2 col-form-label" for="pass"><?php echo empty($_SESSION['onetime'] ?? null) ? xlt('Current Password') : ''; ?></label>
443 <div class="col-md">
444 <input class="form-control" name="pass" id="pass" <?php echo ($_SESSION['onetime'] ?? null) ? 'type="hidden" ' : 'type="password" '; ?> autocomplete="none" value="<?php echo attr($_SESSION['onetime'] ?? '');
445 $_SESSION['password_update'] = ($_SESSION['onetime'] ?? null) ? 2 : 1;
446 unset($_SESSION['onetime']); ?>" required />
447 </div>
448 </div>
449 <?php if ($_SESSION['pin'] ?? null) { ?>
450 <div class="form-row my-3">
451 <label class="col-md-2 col-form-label" for="token_pin"><?php echo xlt('One Time PIN'); ?></label>
452 <div class="col-md">
453 <input class="form-control" name="token_pin" id="token_pin" type="password" autocomplete="none" value="" required pattern=".{6,20}" />
454 </div>
455 </div>
456 <?php } ?>
457 <div class="form-row my-3">
458 <label class="col-md-2 col-form-label" for="pass_new"><?php echo xlt('New Password'); ?></label>
459 <div class="col-md">
460 <input class="form-control" name="pass_new" id="pass_new" type="password" required placeholder="<?php echo xla('Min length is 8 with upper,lowercase,numbers mix'); ?>" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
461 </div>
462 </div>
463 <div class="form-row my-3">
464 <label class="col-md-2 col-form-label" for="pass_new_confirm"><?php echo xlt('Confirm New Password'); ?></label>
465 <div class="col-md">
466 <input class="form-control" name="pass_new_confirm" id="pass_new_confirm" type="password" required pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
467 </div>
468 </div>
469 <?php if ($GLOBALS['enforce_signin_email']) { ?>
470 <div class="form-row my-3">
471 <label class="col-md-2 col-form-label" for="passaddon"><?php echo xlt('Confirm Email Address'); ?></label>
472 <div class="col-md">
473 <input class="form-control" name="passaddon" id="passaddon" required placeholder="<?php echo xla('Current on record trusted email'); ?>" type="email" autocomplete="none" value="" />
474 </div>
475 </div>
476 <?php } ?>
477 <input class="btn btn-secondary float-left" type="button" onclick="document.location.replace('./index.php?woops=1&site=<?php echo attr_url($_SESSION['site_id']); ?><?php if (!empty($redirectUrl)) {
478 echo "&redirect=" . attr_url($redirectUrl); } ?>');" value="<?php echo xla('Cancel'); ?>" />
479 <input class="btn btn-primary float-right" type="submit" value="<?php echo xla('Log In'); ?>" />
480 </form>
481 <?php } elseif (!empty($GLOBALS['portal_two_pass_reset']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key']) && isset($_GET['requestNew'])) { ?>
482 <form id="resetPass" action="#" method="post">
483 <input type='hidden' id='csrf_token_form' name='csrf_token_form' value='<?php echo attr(CsrfUtils::collectCsrfToken('passwordResetCsrf')); ?>' />
484 <?php if (isset($redirectUrl)) { ?>
485 <input id="redirect" type="hidden" name="redirect" value="<?php echo attr($redirectUrl); ?>" />
486 <?php } ?>
487 <div class="text-center">
488 <fieldset>
489 <legend class='bg-primary text-white pt-2 py-1'><h3><?php echo xlt('Patient Credentials Reset') ?></h3></legend>
490 <div class="jumbotron jumbotron-fluid px-5 py-3">
491 <div class="form-row my-3">
492 <label class="col-md-2 col-form-label" for="fname"><?php echo xlt('First Name') ?></label>
493 <div class="col-md">
494 <input type="text" class="form-control" id="fname" required placeholder="<?php echo xla('First Name'); ?>" />
495 </div>
496 </div>
497 <div class="form-row my-3">
498 <label class="col-md-2 col-form-label" for="lname"><?php echo xlt('Last Name') ?></label>
499 <div class="col-md">
500 <input type="text" class="form-control" id="lname" required placeholder="<?php echo xla('Last Name'); ?>" />
501 </div>
502 </div>
503 <div class="form-row my-3">
504 <label class="col-md-2 col-form-label" for="dob"><?php echo xlt('Birth Date') ?></label>
505 <div class="col-md">
506 <input id="dob" type="text" required class="form-control datepicker" placeholder="<?php echo xla('YYYY-MM-DD'); ?>" />
507 </div>
508 </div>
509 <div class="form-row my-3">
510 <label class="col-md-2 col-form-label" for="emailInput"><?php echo xlt('Enter E-Mail Address') ?></label>
511 <div class="col-md">
512 <input id="emailInput" type="email" class="form-control" required placeholder="<?php echo xla('Current trusted email address on record.'); ?>" maxlength="100" />
513 </div>
514 </div>
515 </div>
516 <div class="form-group">
517 <div class="d-flex justify-content-center">
518 <div class="g-recaptcha" data-sitekey="<?php echo attr($GLOBALS['google_recaptcha_site_key']); ?>" data-callback="enableVerifyBtn"></div>
519 </div>
520 </div>
521 <input class="btn btn-secondary float-left" type="button" onclick="document.location.replace('./index.php?woops=1&site=<?php echo attr_url($_SESSION['site_id']); ?><?php if (!empty($redirectUrl)) {
522 echo "&redirect=" . attr_url($redirectUrl); } ?>');" value="<?php echo xla('Cancel'); ?>" />
523 <button id="submitRequest" class="btn btn-primary nextBtn float-right" type="submit" disabled="disabled"><?php echo xlt('Verify') ?></button>
524 </fieldset>
525 </div>
526 </form>
527 <?php } else {
528 ?> <!-- Main logon -->
529 <div class="row">
530 <img class="img-fluid login-logo" src='<?php echo $logoSrc; ?>'>
531 </div>
532 <div class="container-xl p-1">
533 <form class="text-center mx-1" action="get_patient_info.php" method="POST" onsubmit="return process()">
534 <?php if (isset($redirectUrl)) { ?>
535 <input id="redirect" type="hidden" name="redirect" value="<?php echo attr($redirectUrl); ?>" />
536 <?php } ?>
537 <fieldset>
538 <legend class="bg-primary text-white pt-2 py-1"><h3><?php echo xlt('Patient Portal Login'); ?></h3></legend>
539 <div class="form-group my-1">
540 <label class="col-form-label" for="uname"><?php echo xlt('Username') ?></label>
541 <input type="text" class="form-control" name="uname" id="uname" autocomplete="none" required />
542 </div>
543 <div class="form-group mt-1">
544 <label class="col-form-label" for="pass"><?php echo xlt('Password') ?></label>
545 <input class="form-control" name="pass" id="pass" type="password" required autocomplete="none" />
546 </div>
547 <?php if ($GLOBALS['enforce_signin_email']) { ?>
548 <div class="form-group mt-1">
549 <label class="col-form-label" for="passaddon"><?php echo xlt('E-Mail Address') ?></label>
550 <input class="form-control" name="passaddon" id="passaddon" type="email" autocomplete="none" />
551 </div>
552 <?php } ?>
553 <?php if ($GLOBALS['language_menu_login']) { ?>
554 <?php if (count($result3) != 1) { ?>
555 <div class="form-group mt-1">
556 <label class="col-form-label" for="selLanguage"><?php echo xlt('Language'); ?></label>
557 <select class="form-control" id="selLanguage" name="languageChoice">
558 <?php
559 echo "<option selected='selected' value='" . attr($defaultLangID) . "'>" .
560 text(xl('Default') . " - " . xl($defaultLangName)) . "</option>\n";
561 foreach ($result3 as $iter) {
562 if ($GLOBALS['language_menu_showall']) {
563 if (!$GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
564 continue; // skip the dummy language
566 echo "<option value='" . attr($iter['lang_id']) . "'>" .
567 text($iter['trans_lang_description']) . "</option>\n";
568 } else {
569 if (in_array($iter['lang_description'], $GLOBALS['language_menu_show'])) {
570 if (!$GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
571 continue; // skip the dummy language
573 echo "<option value='" . attr($iter['lang_id']) . "'>" .
574 text($iter['trans_lang_description']) . "</option>\n";
579 </select>
580 <?php }
581 } ?>
582 </div>
583 <div class="col">
584 <?php if (!empty($GLOBALS['portal_onsite_two_register']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key'])) { ?>
585 <button class="btn btn-secondary float-left" onclick="location.replace('./account/verify.php?site=<?php echo attr_url($_SESSION['site_id']); ?>')"><?php echo xlt('Register'); ?></button>
586 <?php } ?>
587 <?php if (!empty($GLOBALS['portal_two_pass_reset']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key']) && isset($_GET['w']) && (isset($_GET['u']) || isset($_GET['p']))) { ?>
588 <button class="btn btn-danger ml-2" onclick="location.replace('./index.php?requestNew=1&site=<?php echo attr_url($_SESSION['site_id']); ?><?php if (!empty($redirectUrl)) {
589 echo "&redirect=" . attr_url($redirectUrl); } ?>')"><?php echo xlt('Reset Credentials'); ?></button>
590 <?php } ?>
591 <button class="btn btn-success float-right" type="submit"><?php echo xlt('Log In'); ?></button>
592 </div>
593 </fieldset>
594 <?php if (!(empty($hiddenLanguageField))) {
595 echo $hiddenLanguageField;
596 } ?>
597 </form>
598 </div>
599 </div><!-- div wrapper -->
600 <?php } ?> <!-- logon wrapper -->
602 <div id="alertStore" class="d-none">
603 <div class="h6 alert alert-warning alert-dismissible fade show my-1 py-1" role="alert">
604 <button type="button" class="close my-1 py-0" data-dismiss="alert" aria-label="Close">
605 <span aria-hidden="true">&times;</span>
606 </button>
607 </div>
608 </div>
610 <script>
611 var tab_mode = true;
612 var webroot_url = <?php echo js_escape($GLOBALS['web_root']) ?>;
614 function restoreSession() {
615 //dummy functions so the dlgopen function will work in the patient portal
616 return true;
619 var isPortal = 1;
621 $(function () {
622 <?php // if something went wrong
623 if (!empty($GLOBALS['portal_two_pass_reset']) && !empty($GLOBALS['google_recaptcha_site_key']) && !empty($GLOBALS['google_recaptcha_secret_key']) && isset($_GET['requestNew'])) {
624 $_SESSION['register'] = true;
625 $_SESSION['authUser'] = 'portal-user';
626 $_SESSION['pid'] = true;
628 $('.datepicker').datetimepicker({
629 <?php $datetimepicker_timepicker = false; ?>
630 <?php $datetimepicker_showseconds = false; ?>
631 <?php $datetimepicker_formatInput = false; ?>
632 <?php require $GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'; ?>
634 $(document.body).on('hidden.bs.modal', function () {
635 callServer('cleanup');
637 $("#resetPass").on('submit', function (e) {
638 e.preventDefault();
639 callServer('reset_password');
640 return false;
642 <?php } ?>
643 <?php if (isset($_GET['w'])) { ?>
644 // mdsupport - Would be good to include some clue about what went wrong!
645 bsAlert(<?php echo xlj('Something went wrong. Please try again.'); ?>);
646 <?php } ?>
647 <?php if (isset($_GET['oe'])) { ?>
648 // mdsupport - Would be good to include some clue about what went wrong!
649 bsAlert(<?php echo xlj('Something went wrong. Onetime Authentication! Expired.'); ?>);
650 <?php } ?>
651 <?php if (isset($_GET['oi'])) { ?>
652 // mdsupport - Would be good to include some clue about what went wrong!
653 bsAlert(<?php echo xlj('Something went wrong. Onetime Authentication! Invalid.'); ?>);
654 <?php } ?>
655 <?php // if successfully logged out
656 if (isset($_GET['logout'])) { ?>
657 bsAlert(<?php echo xlj('You have been successfully logged out.'); ?>);
658 <?php } ?>
660 return false;
663 function callServer(action) {
664 var data = {};
665 if (action === 'reset_password') {
666 data = {
667 'action': action,
668 'dob': $("#dob").val(),
669 'last': $("#lname").val(),
670 'first': $("#fname").val(),
671 'email': $("#emailInput").val(),
672 'g-recaptcha-response': grecaptcha.getResponse(),
673 'csrf_token_form': $("#csrf_token_form").val()
676 if (action === 'cleanup') {
677 data = {
678 'action': action
681 $.ajax({
682 type: 'GET',
683 url: './account/account.php',
684 data: data
685 }).done(function (rtn) {
686 if (action === "cleanup") {
687 let url = "./index.php?site=" + <?php echo js_url($_SESSION['site_id']); ?>; // Goto landing page.
688 let redirectUrl = $("#redirect").val();
689 if (redirectUrl) {
690 url += "&redirect=" + encodeURIComponent(redirectUrl);
692 window.location.href = url;
693 } else if (action === "reset_password") {
694 if (JSON.parse(rtn) === 1) {
695 dialog.alert(<?php echo xlj("Check your email inbox (and possibly your spam folder) for further instructions to reset your password. If you have not received an email, then recommend contacting the clinic.") ?>);
696 return false;
697 } else {
698 dialog.alert(<?php echo xlj("Something went wrong. Recommend contacting the clinic.") ?>);
699 return false;
702 }).fail(function (err) {
703 var message = <?php echo xlj('Something went wrong.') ?>;
704 alert(message);
708 function bsAlert(msg) {
709 let divAlert = document.getElementById("alertStore").querySelector("div.alert").cloneNode(true);
710 document.querySelector("form").prepend(divAlert);
711 let strongMsg = document.createElement("strong");
712 strongMsg.innerHTML = msg;
713 divAlert.prepend(strongMsg);
714 setTimeout(() => {
715 document.querySelector("div.alert").remove();
716 }, 6000);
718 </script>
719 </body>
720 </html>