4 * Portal Registration Wizard
7 * @link http://www.open-emr.org
8 * @author Jerry Padgett <sjpadgett@gmail.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2017-2019 Jerry Padgett <sjpadgett@gmail.com>
11 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
15 // script is brought in as require_once in index.php when applicable
17 use OpenEMR\Common\Logging\SystemLogger
;
18 use OpenEMR\Core\Header
;
20 if ($portalRegistrationAuthorization !== true) {
21 (new SystemLogger())->debug("attempted to use register.php directly, so failed");
22 OpenEMR\Common\Session\SessionUtil
::portalSessionCookieDestroy();
23 echo xlt("Not Authorized");
24 header('HTTP/1.1 401 Unauthorized');
28 if (empty($GLOBALS['portal_onsite_two_register']) ||
empty($GLOBALS['google_recaptcha_site_key']) ||
empty($GLOBALS['google_recaptcha_secret_key'])) {
29 (new SystemLogger())->debug("attempted to use register.php despite register feature being turned off, so failed");
30 OpenEMR\Common\Session\SessionUtil
::portalSessionCookieDestroy();
31 echo xlt("Not Authorized");
32 header('HTTP/1.1 401 Unauthorized');
36 unset($_SESSION['itsme']);
37 $_SESSION['authUser'] = 'portal-user';
38 $_SESSION['pid'] = true;
39 $_SESSION['register'] = true;
40 $_SESSION['register_silo_ajax'] = true;
42 $landingpage = "index.php?site=" . urlencode($_SESSION['site_id']);
48 <title
><?php
echo xlt('New Patient'); ?
> |
<?php
echo xlt('Register'); ?
></title
>
49 <meta name
="description" content
="Developed By sjpadgett@gmail.com" />
51 <?php Header
::setupHeader(['no_main-theme', 'portal-theme', 'datetime-picker']); ?
>
54 var webRoot
= <?php
echo js_escape($GLOBALS['web_root']); ?
>;
55 top
.webroot_url
= webRoot
;
58 function restoreSession() {
59 //dummy functions so the dlgopen function will work in the patient portal
64 var navListItems
= $
('div.setup-panel div a'),
65 allWells
= $
('.setup-content'),
66 allNextBtn
= $
('.nextBtn'),
67 allPrevBtn
= $
('.prevBtn');
71 navListItems
.click(function (e
) {
73 if (!$
(this
).hasClass('disabled')) {
74 navListItems
.removeClass('btn-primary').addClass('btn-light');
75 $
(this
).addClass('btn-primary').removeClass('btn-light');
77 $
($
(this
).attr('href')).show();
78 $
($
(this
).attr('href')).find('input:eq(0)').focus();
82 allPrevBtn
.click(function () {
83 var curStep
= $
(this
).closest(".setup-content"),
84 curStepBtn
= curStep
.attr("id"),
85 prevstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().prev().children("a");
86 prevstepwiz
.removeClass('disabled').trigger('click');
89 allNextBtn
.click(function () {
90 var profile
= $
("#profileFrame").contents();
92 // Fix for iFrame height
93 window
.addEventListener('message', function (e
) {
94 var scroll_height
= e
.data
;
95 document
.getElementById('profileFrame').style
.height
= scroll_height +
'px';
98 var curStep
= $
(this
).closest(".setup-content"),
99 curStepBtn
= curStep
.attr("id"),
100 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
101 curInputs
= curStep
.find("input[type='text'],input[type='email'],select"),
104 $
(".form-group").removeClass("has-error");
105 for (var i
= 0; i
< curInputs
.length
; i++
) {
106 if (!curInputs
[i
].validity
.valid
) {
108 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
112 if (curStepBtn
== 'step-1') { // leaving step 1 setup profile frame. Prob not nec but in case
113 let fn
= $
("#fname").val().replace(/^
./, $
("#fname").val()[0].toUpperCase());
114 let ln
= $
("#lname").val().replace(/^
./, $
("#lname").val()[0].toUpperCase());
115 profile
.find('input#fname').val(fn
);
116 profile
.find('input#mname').val($
("#mname").val());
117 profile
.find('input#lname').val(ln
);
118 profile
.find('input#dob').val($
("#dob").val());
119 profile
.find('input#email').val($
("#emailInput").val());
120 // disable to prevent already validated field changes.
121 profile
.find('input#fname').prop("disabled", true);
122 profile
.find('input#mname').prop("disabled", true);
123 profile
.find('input#lname').prop("disabled", true);
124 profile
.find('input#dob').prop("disabled", true);
125 profile
.find('input#email').prop("disabled", true);
126 profile
.find('input#emailDirect').prop("disabled", true);
127 profile
.find('input#pid').prop('disabled', true);
128 profile
.find('input#pid').val('');
130 profile
.find('input[name=allowPatientPortal]').val(['YES']);
131 profile
.find('input[name=hipaaAllowemail]').val(['YES']);
132 // need these for validation.
133 profile
.find('select#providerid option:contains("Unassigned")').val('');
134 // must have a provider for many reasons. w/o save won't work.
135 //profile.find('select#providerid').attr('required', true);
136 profile
.find('select#sex option:contains("Unassigned")').val('');
137 profile
.find('select#sex').attr('required', true);
139 nextstepwiz
.removeClass('disabled').trigger('click');
143 $
("#profileNext").click(function () {
144 var profile
= $
("#profileFrame").contents();
145 var curStep
= $
(this
).closest(".setup-content"),
146 curStepBtn
= curStep
.attr("id"),
147 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
148 curInputs
= profile
.find("input[type='text'],input[type='email'],select"),
150 $
(".form-group").removeClass("has-error");
152 for (var i
= 0; i
< curInputs
.length
; i++
) {
153 if (!curInputs
[i
].validity
.valid
) {
156 curInputs
[i
].scrollIntoView();
157 curInputs
[i
].focus();
160 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
164 provider
= profile
.find('select#providerid').val();
165 nextstepwiz
.removeClass('disabled').trigger('click');
169 $
("#submitPatient").click(function () {
170 let profile
= $
("#profileFrame").contents();
171 if (checkRegistration()) {
172 // Use portals rest api. flag 1 is write to chart. flag 0 writes an audit record for review in dashboard.
173 // (unclear what above means since no flags here and is being saved directly in chart)
174 // Save the new patient.
175 document
.getElementById('profileFrame').contentWindow
.postMessage({submitForm
: true}, window
.location
.origin
);
176 // lets force a second here to ensure above patient import is called to server prior than the insurance import
177 // (this order is critical because the patient import sets up the pid to be used on the backend, so that no
178 // pid adjustments can be made on the frontend)
179 delayPromise(1000).then(() => $
("#insuranceForm").submit());
183 $
('div.setup-panel div a.btn-primary').trigger('click');
185 $
('.datepicker').datetimepicker({
186 <?php
$datetimepicker_timepicker = false; ?
>
187 <?php
$datetimepicker_showseconds = false; ?
>
188 <?php
$datetimepicker_formatInput = false; ?
>
189 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
192 $
("#insuranceForm").submit(function (e
) {
194 let url
= "account/account.php?action=new_insurance";
198 data
: $
("#insuranceForm").serialize()
199 }).done(function (serverResponse
) {
200 doCredentials(provider
) // this is the end for session.
205 $
('#inscompany').on('change', function () {
206 if ($
('#inscompany').val().toUpperCase() === 'SELF') {
207 $
("#insuranceForm input").removeAttr("required");
208 let message
= <?php
echo xlj('You have chosen to be self insured or currently do not have insurance. Click next to continue registration.'); ?
>;
213 $
("#dob").on('blur', function () {
214 let bday
= $
(this
).val() ??
'';
215 let age
= Math
.round(Math
.abs((new Date().getTime() - new Date(bday
).getTime())));
216 age
= Math
.round(age
/ 1000 / 60 / 60 / 24);
217 // need to be at least 30 days old otherwise likely an error.
219 let msg
= <?php
echo(xlj("Invalid Date format or value! Type date as YYYY-MM-DD or use the calendar.")); ?
> ;
221 $
(this
).prop('placeholder', 'Invalid Date');
229 function delayPromise(time
) {
230 return new Promise(resolve
=> setTimeout(resolve
, time
));
233 function doCredentials(provider
) {
234 window
.location
.href
= "account/account.php?action=do_signup&provider=" +
encodeURIComponent(provider
);
237 function checkRegistration() {
238 var profile
= $
("#profileFrame").contents();
239 var curStep
= $
("#step-2"),
240 curStepBtn
= curStep
.attr("id"),
241 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
242 curInputs
= $
("#profileFrame").contents().find("input[type='text'],input[type='email'],select"),
244 $
(".form-group").removeClass("has-error");
246 for (let i
= 0; i
< curInputs
.length
; i++
) {
247 if (!curInputs
[i
].validity
.valid
) {
250 curInputs
[i
].scrollIntoView();
251 curInputs
[i
].focus();
254 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
268 border
-radius
: .9375rem
!important
273 width
: 100%
!important
282 @media
(max
-width
: 1024px
) {
292 .stepwiz
-row
::before
{
293 background
-color
: var(--gray400
);
319 .stepwiz
-step button
[disabled
] {
320 filter
: alpha(opacity
=100) !important
;
321 opacity
: 1 !important
329 line
-height
: 1.428571429;
335 fieldset
, input
[type
=date
], input
[type
=email
], input
[type
=text
], select
{
336 -webkit
-appearance
: none
;
337 -moz
-appearance
: none
;
339 box
-sizing
: border
-box
342 input
:focus
:invalid
, input
:required
:invalid
{
343 background
-image
: url(data
:image
/png
;base64
,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8
/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeVJREFUeNqkU01oE1EQ
/mazSTdRmqSxLVSJVKU9RYoHD8WfHr16kh5EFA8eSy6hXrwUPBSKZ6E9V1CU4tGf0DZWDEQrGkhprRDbCvlpavan3ezu+LLSUnADLZnHwHvzmJlvvpkhZkY7IqFNaTuAfPhhP
/8Uo
87SGSaDsP
27hgYM
/lUpy6lHdqsAtM+BPfvqKp3ufYKwcgmWCug6oKmrrG3PoaqngWjdd
/922hOBs5C
/jJA6x7AiUt8VYVUAVQXXShfIqCYRMZO8
/N1N+B8H1sOUwivpSUSVCJ2MAjtVwBAIdv+AQkHQqbOgc+fBvorjyQENDcch16
/BtkQdAlC4E6jrYHGgGU18Io3gmhzJuwub6
/fQJYNi
/YBpCifhbDaAPXFvCBVxXbvfbNGFeN8DkjogWAd8DljV3KRutcEAeHMN
/HXZ4p9bhncJHCyhNx52R0Kv
/XNuQvYBnM+CP7xddXL5KaJw0TMAF8qjnMvegeK
/SLHubhpKDKIrJDlvXoMX3y9xcSMZyBQ+tpyk5hzsa2Ns7LGdfWdbL6fZvHn92d7dgROH
/730YBLtiZmEdGPkFnhX4kxmjVe2xgPfCtrRd6GHRtEh9zsL8xVe+pwSzj+OtwvletZZ
/wLeKD71L+ZeHHWZ
/gowABkp7AwwnEjFAAAAAElFTkSuQmCC
);
344 background
-position
: right top
;
345 background
-repeat
: no
-repeat
;
349 input
:required
:valid
{
350 background
-image
: url(data
:image
/png
;base64
,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8
/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAepJREFUeNrEk79PFEEUx9
/uDDd7v
/AAQQnEQokmJCRGwc7
/QeM
/YGVxsZJQYI
/EhCChICYmUJigNBSGzobQaI5SaYRw6imne0d2D
/bYmZ3dGd+YQKEHYiyc5GUyb3Y+
77vfeWNpreFfhvXfAWAAJtbKi7dff1rWK9vPHx3mThP2Iaipk5EzTg8Qmru38H7izmkFHAF4WH1R52654PR0Oamzj2dKxYt
/Bbg1OPZuY3d9aU82VGem
/5LtnJscLxWzfzRxaWNqWJP
0XUadIbSzu
5DuvUJpzq
7sfYBKsP
1GJeLB+PWpt8cCXm4+
2+zLXx4guKiLXWA2Nc5ChOuacMEPv20FkT+dIawyenVi5VcAbcigWzXLeNiDRCdwId0LFm5IUMBIBgrp8wOEsFlfeCGm23
/zoBZWn9a4C314A1nCoM1OAVccuGyCkPs
/P+pIdVIOkG9pIh6YlyqCrwhRKD3GygK9PUBImIQQxRi4b2O+JcCLg8+e8NZiLVEygwCrWpYF0jQJziYU
/ho2TUuCPTn8hHcQNuZy1
/94sAMOzQHDeqaij7Cd8Dt8CatGhX3iWxgtFW
/m29pnUjR7TSQcRCIAVW1FSr6KAVYdi+
5Pj8yunviYHq7f72po3Y9dbi7CxzDO1+duzCXH9cEPAQYAhJELY
/AqBtwAAAAASUVORK5CYII
=);
351 background
-position
: right top
;
352 background
-repeat
: no
-repeat
356 <body
class="mt-4 skin-blue">
357 <div
class="container-lg">
358 <h1
class="text-center"><?php
echo xlt('Account Registration'); ?
></h1
>
359 <div
class="stepwiz">
360 <div
class="stepwiz-row setup-panel">
361 <div
class="stepwiz-step">
362 <a href
="#step-1" type
="button" class="btn btn-primary btn-circle">1</a
>
363 <p
><?php
echo xlt('Verify Email') ?
></p
>
365 <div
class="stepwiz-step">
366 <a href
="#step-2" type
="button" class="btn btn-light btn-circle disabled">2</a
>
367 <p
><?php
echo xlt('Profile') ?
></p
>
369 <div
class="stepwiz-step">
370 <a href
="#step-3" type
="button" class="btn btn-light btn-circle disabled">3</a
>
371 <p
><?php
echo xlt('Insurance') ?
></p
>
373 <div
class="stepwiz-step">
374 <a href
="#step-4" type
="button" class="btn btn-light btn-circle disabled">4</a
>
375 <p
><?php
echo xlt('Register') ?
></p
>
379 <!-- // Start Forms // -->
380 <form id
="startForm" role
="form">
381 <div
class="text-center setup-content" id
="step-1">
382 <div
class="jumbotron">
383 <input type
="hidden" name
="languageChoice" value
="<?php echo attr($languageRegistration); ?>" />
384 <input type
="hidden" id
="fname" name
="fname" value
="<?php echo attr($fnameRegistration); ?>" />
385 <input type
="hidden" id
="mname" name
="mname" value
="<?php echo attr($mnameRegistration); ?>" />
386 <input type
="hidden" id
="lname" name
="lname" value
="<?php echo attr($lnameRegistration); ?>" />
387 <input type
="hidden" id
="dob" name
="dob" value
="<?php echo attr($dobRegistration); ?>" />
388 <input type
="hidden" id
="emailInput" name
="email" value
="<?php echo attr($emailRegistration); ?>" />
389 <div
class="alert alert-success" role
="alert"><?php
echo xlt("Your email has been verified. Click Next."); ?
></div
>
391 <button
class="btn btn-primary nextBtn pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
394 <!-- Profile Form
-->
395 <form id
="profileForm" role
="form" action
="account/account.php" method
="post">
396 <div
class="text-center setup-content" id
="step-2" style
="display: none">
397 <legend
class="bg-primary text-white"><?php
echo xlt('Profile') ?
></legend
>
398 <div
class="jumbotron">
399 <iframe
class="embedded-content" src
="patient/patientdata?pid=0®ister=true" id
="profileFrame" name
="Profile Info"></iframe
>
401 <button
class="btn btn-primary pull-right" type
="button" id
="profileNext"><?php
echo xlt('Next') ?
></button
>
404 <!-- Insurance Form
-->
405 <form id
="insuranceForm" role
="form" action
="" method
="post">
406 <div
class="text-center setup-content" id
="step-3" style
="display: none">
407 <legend
class='bg-primary text-white'><?php
echo xlt('Insurance') ?
></legend
>
408 <div
class="jumbotron">
409 <div
class="form-row">
410 <div
class="col-12 col-md-6 col-lg-3 form-group">
411 <label
for="provider"><?php
echo xlt('Insurance Company') ?
></label
>
412 <div
class="controls inline-inputs">
413 <input type
="text" class="form-control" name
="provider" id
="inscompany" required placeholder
="<?php echo xla('Enter Self if None'); ?>">
416 <div
class="col-12 col-md-6 col-lg-3 form-group">
417 <label
for="plan_name"><?php
echo xlt('Plan Name') ?
></label
>
418 <div
class="controls inline-inputs">
419 <input type
="text" class="form-control" name
="plan_name" required placeholder
="<?php echo xla('required'); ?>">
422 <div
class="col-12 col-md-6 col-lg-3 form-group">
423 <label
for="policy_number"><?php
echo xlt('Policy Number') ?
></label
>
424 <div
class="controls inline-inputs">
425 <input type
="text" class="form-control" name
="policy_number" required placeholder
="<?php echo xla('required'); ?>">
429 <div
class="form-row">
430 <div
class="col-12 col-md-6 col-lg-3 form-group">
431 <label
for="group_number"><?php
echo xlt('Group Number') ?
></label
>
432 <div
class="controls inline-inputs">
433 <input type
="text" class="form-control" name
="group_number" required placeholder
="<?php echo xla('required'); ?>">
436 <div
class="col-12 col-md-6 col-lg-3 form-group">
437 <label
for="date"><?php
echo xlt('Policy Begin Date') ?
></label
>
438 <div
class="controls inline-inputs">
439 <input type
="text" class="form-control datepicker" name
="date" placeholder
="<?php echo xla('Policy effective date'); ?>">
442 <div
class="col-12 col-md-6 col-lg-3 form-group">
443 <label
for="copay"><?php
echo xlt('Co-Payment') ?
></label
>
444 <div
class="controls inline-inputs">
445 <input type
="number" class="form-control" name
="copay" placeholder
="<?php echo xla('Plan copay if known'); ?>">
450 <button
class="btn btn-primary prevBtn btn-sm pull-left" type
="button"><?php
echo xlt('Previous') ?
></button
>
451 <button
class="btn btn-primary nextBtn btn-sm pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
454 <!-- End Insurance
. Next what we
've been striving towards..the end-->
455 <div class="text-center setup-content" id="step-4" style="display: none">
456 <legend class='bg
-success text
-white
'><?php echo xlt('Register
') ?></legend>
457 <div class="jumbotron">
458 <h4 class='bg
-success
'><?php echo xlt("All set. Click Send Request below to finish registration.") ?></h4>
461 <?php echo xlt("An e-mail with your new account credentials will be sent to the e-mail address supplied earlier. You may still review or edit any part of your information by using the top step buttons to go to the appropriate panels. If after receiving credentials and you have trouble with access to the portal, please contact administration.") ?>
465 <button class="btn btn-primary prevBtn float-left" type="button"><?php echo xlt('Previous
') ?></button>
466 <button class="btn btn-success float-right" type="button" id="submitPatient"><?php echo xlt('Send Request
') ?></button>