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;
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');
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.
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'] ??
''
94 } elseif (!empty($_POST['service_auth'] ??
null)) {
95 $token = $_POST['service_auth'];
96 $redirect_token = $_POST['target'] ??
null;
97 $csrfToken = $_POST['csrf_token'] ??
null;
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');
106 } catch (OneTimeAuthExpiredException
$exception) {
108 'onetime login attempt',
109 $exception->getPid() ??
'',
114 // do we want a separate message that their token has expired?
115 OpenEMR\Common\Session\SessionUtil
::portalSessionCookieDestroy();
116 header('Location: ' . $landingpage . '&oe');
118 } catch (OneTimeAuthException
$exception) {
120 'onetime login attempt',
121 $exception->getPid() ??
'',
126 OpenEMR\Common\Session\SessionUtil
::portalSessionCookieDestroy();
127 header('Location: ' . $landingpage . '&oi');
131 (new SystemLogger())->errorLogCaller("Invalid service_auth request - should never reach here");
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');
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');
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');
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');
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');
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');
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");
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');
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');
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');
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'];
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"];
279 //default to english if any problems
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 " .
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";
308 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='" . attr($defaultLangID) . "' />\n";
315 <title
><?php
echo xlt('Patient Portal Login'); ?
></title
>
317 Header
::setupHeader(['no_main-theme', 'portal-theme', 'datetime-picker']);
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!'); ?
>);
328 'action': 'userIsUnique',
330 'loginUname': vsuname
334 url
: './account/account.php',
336 }).done(function (rtn
) {
340 alert(<?php
echo xlj('Log In Name is unavailable. Try again!'); ?
>);
347 alert(<?php
echo xlj('Field(s) are missing!'); ?
>);
353 function validate() {
356 if (document
.getElementById('uname').value
== "") {
357 $
('#uname').addClass('is-invalid');
360 if (document
.getElementById('pass').value
== "") {
361 $
('#pass').addClass('is-invalid');
367 function process_new_pass() {
368 if (!(validate_new_pass())) {
369 alert(<?php
echo xlj('Field(s) are missing!'); ?
>);
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.'); ?
>);
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.'); ?
>);
382 function validate_new_pass() {
384 if (document
.getElementById('uname').value
== "") {
385 $
('#uname').addClass('is-invalid');
388 if (document
.getElementById('pass').value
== "") {
389 $
('#pass').addClass('is-invalid');
392 if (document
.getElementById('pass_new').value
== "") {
393 $
('#pass_new').addClass('is-invalid');
396 if (document
.getElementById('pass_new_confirm').value
== "") {
397 $
('#pass_new_confirm').addClass('is-invalid');
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
>
407 function enableVerifyBtn() {
408 document
.getElementById("submitRequest").disabled
= false;
411 <?php
// add csrf mechanism for the password reset ui
412 CsrfUtils
::setupCsrfKey();
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); ?>" />
429 <div
class="form-row my-3">
430 <label
class="col-md-2 col-form-label" for="uname"><?php
echo xlt('Account Name'); ?
></label
>
432 <input
class="form-control" name
="uname" id
="uname" type
="text" readonly autocomplete
="none" value
="<?php echo attr($_SESSION['portal_username']); ?>" />
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
>
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()" />
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
>
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
/>
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
>
453 <input
class="form-control" name
="token_pin" id
="token_pin" type
="password" autocomplete
="none" value
="" required pattern
=".{6,20}" />
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
>
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,}" />
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
>
466 <input
class="form-control" name
="pass_new_confirm" id
="pass_new_confirm" type
="password" required pattern
="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
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
>
473 <input
class="form-control" name
="passaddon" id
="passaddon" required placeholder
="<?php echo xla('Current on record trusted email'); ?>" type
="email" autocomplete
="none" value
="" />
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'); ?>" />
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); ?>" />
487 <div
class="text-center">
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
>
494 <input type
="text" class="form-control" id
="fname" required placeholder
="<?php echo xla('First Name'); ?>" />
497 <div
class="form-row my-3">
498 <label
class="col-md-2 col-form-label" for="lname"><?php
echo xlt('Last Name') ?
></label
>
500 <input type
="text" class="form-control" id
="lname" required placeholder
="<?php echo xla('Last Name'); ?>" />
503 <div
class="form-row my-3">
504 <label
class="col-md-2 col-form-label" for="dob"><?php
echo xlt('Birth Date') ?
></label
>
506 <input id
="dob" type
="text" required
class="form-control datepicker" placeholder
="<?php echo xla('YYYY-MM-DD'); ?>" />
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
>
512 <input id
="emailInput" type
="email" class="form-control" required placeholder
="<?php echo xla('Current trusted email address on record.'); ?>" maxlength
="100" />
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
>
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
>
528 ?
> <!-- Main logon
-->
530 <img
class="img-fluid login-logo" src
='<?php echo $logoSrc; ?>'>
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); ?>" />
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
/>
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" />
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" />
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">
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";
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";
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
>
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
>
591 <button
class="btn btn-success float-right" type
="submit"><?php
echo xlt('Log In'); ?
></button
>
594 <?php
if (!(empty($hiddenLanguageField))) {
595 echo $hiddenLanguageField;
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">×
;</span
>
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
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
) {
639 callServer('reset_password');
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.'); ?
>);
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.'); ?
>);
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.'); ?
>);
655 <?php
// if successfully logged out
656 if (isset($_GET['logout'])) { ?
>
657 bsAlert(<?php
echo xlj('You have been successfully logged out.'); ?
>);
663 function callServer(action
) {
665 if (action
=== 'reset_password') {
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') {
683 url
: './account/account.php',
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();
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.") ?
>);
698 dialog
.alert(<?php
echo xlj("Something went wrong. Recommend contacting the clinic.") ?
>);
702 }).fail(function (err
) {
703 var message
= <?php
echo xlj('Something went wrong.') ?
>;
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
);
715 document
.querySelector("div.alert").remove();