3 * Portal Registration Wizard
6 * @link http://www.open-emr.org
7 * @author Jerry Padgett <sjpadgett@gmail.com>
8 * @copyright Copyright (c) 2017-2018 Jerry Padgett <sjpadgett@gmail.com>
9 * @license https://www.gnu.org/licenses/agpl-3.0.en.html GNU Affero General Public License 3
13 session_regenerate_id(true);
15 unset($_SESSION['itsme']);
16 $_SESSION['patient_portal_onsite_two'] = true;
17 $_SESSION['authUser'] = 'portal-user';
18 $_SESSION['pid'] = true;
19 $_SESSION['register'] = true;
21 $_SESSION['site_id'] = isset($_SESSION['site_id']) ?
$_SESSION['site_id'] : 'default';
22 $landingpage = "index.php?site=" . $_SESSION['site_id'];
24 $ignoreAuth_onsite_portal_two = true;
26 require_once("../../interface/globals.php");
28 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array(
29 $GLOBALS['language_default']
31 for ($iter = 0; $row = sqlFetchArray($res2); $iter ++
) {
32 $result2[$iter] = $row;
34 if (count($result2) == 1) {
35 $defaultLangID = $result2[0]{"lang_id"};
36 $defaultLangName = $result2[0]{"lang_description"};
38 // default to english if any problems
40 $defaultLangName = "English";
43 if (! isset($_SESSION['language_choice'])) {
44 $_SESSION['language_choice'] = $defaultLangID;
46 // collect languages if showing language menu
47 if ($GLOBALS['language_menu_login']) {
48 // sorting order of language titles depends on language translation options.
49 $mainLangID = empty($_SESSION['language_choice']) ?
'1' : $_SESSION['language_choice'];
50 if ($mainLangID == '1' && ! empty($GLOBALS['skip_english_translation'])) {
51 $sql = "SELECT * FROM lang_languages ORDER BY lang_description, lang_id";
52 $res3 = SqlStatement($sql);
54 // Use and sort by the translated language name.
55 $sql = "SELECT ll.lang_id, " . "IF(LENGTH(ld.definition),ld.definition,ll.lang_description) AS trans_lang_description, " . "ll.lang_description " .
56 "FROM lang_languages AS ll " . "LEFT JOIN lang_constants AS lc ON lc.constant_name = ll.lang_description " .
57 "LEFT JOIN lang_definitions AS ld ON ld.cons_id = lc.cons_id AND " . "ld.lang_id = ? " .
58 "ORDER BY IF(LENGTH(ld.definition),ld.definition,ll.lang_description), ll.lang_id";
59 $res3 = SqlStatement($sql, array(
64 for ($iter = 0; $row = sqlFetchArray($res3); $iter ++
) {
65 $result3[$iter] = $row;
68 if (count($result3) == 1) {
69 // default to english if only return one language
70 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='1' />\n";
73 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='" . htmlspecialchars($defaultLangID, ENT_QUOTES
) . "' />\n";
80 <title
><?php
echo xlt('New Patient'); ?
> |
<?php
echo xlt('Register'); ?
></title
>
81 <meta content
='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name
='viewport'>
82 <meta name
="description" content
="Developed By sjpadgett@gmail.com">
84 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/font-awesome-4-6-3/css/font-awesome.min.css" rel
="stylesheet" type
="text/css" />
85 <link rel
="stylesheet" href
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-datetimepicker-2-5-4/build/jquery.datetimepicker.min.css">
86 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/css/bootstrap.min.css" rel
="stylesheet" type
="text/css" />
87 <link href
="./../assets/css/register.css" rel
="stylesheet" type
="text/css" />
89 <script src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-3-1-1/index.js" type
="text/javascript"></script
>
91 <script src
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/js/bootstrap.min.js" type
="text/javascript"></script
>
92 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-datetimepicker-2-5-4/build/jquery.datetimepicker.full.min.js"></script
>
93 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/emodal-1-2-65/dist/eModal.js"></script
>
100 $
(document
).ready(function () {
102 $("#emailInput").val("me@me.com");
103 $("#fname").val("Jerry");
104 $("#lname").val("Padgett");
105 $("#dob").val("1919-03-03");
107 var navListItems
= $
('div.setup-panel div a'),
108 allWells
= $
('.setup-content'),
109 allNextBtn
= $
('.nextBtn'),
110 allPrevBtn
= $
('.prevBtn');
114 navListItems
.click(function (e
) {
116 var $target = $
($
(this
).attr('href')),
119 if (!$item.hasClass('disabled')) {
120 navListItems
.removeClass('btn-primary').addClass('btn-default');
121 $item.addClass('btn-primary');
124 $target.find('input:eq(0)').focus();
128 allPrevBtn
.click(function () {
129 var curStep
= $
(this
).closest(".setup-content"),
130 curStepBtn
= curStep
.attr("id"),
131 prevstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().prev().children("a");
132 prevstepwiz
.removeAttr('disabled').trigger('click');
135 allNextBtn
.click(function () {
136 var profile
= $
("#profileFrame").contents();
139 profile.find("input#street").val("123 Some St.");
140 profile.find("input#city").val("Brandon");
141 //--------------------- */
143 var curStep
= $
(this
).closest(".setup-content"),
144 curStepBtn
= curStep
.attr("id"),
145 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
146 curInputs
= curStep
.find("input[type='text'],input[type='email'],select"),
149 $
(".form-group").removeClass("has-error");
150 for (var i
= 0; i
< curInputs
.length
; i++
) {
151 if (!curInputs
[i
].validity
.valid
) {
153 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
157 if (curStepBtn
== 'step-1') { // leaving step 1 setup profile frame. Prob not nec but in case
158 profile
.find('input#fname').val($
("#fname").val());
159 profile
.find('input#mname').val($
("#mname").val());
160 profile
.find('input#lname').val($
("#lname").val());
161 profile
.find('input#dob').val($
("#dob").val());
162 profile
.find('input#email').val($
("#emailInput").val());
163 profile
.find('input[name=allowPatientPortal]').val(['YES']);
164 // need these for validation.
165 profile
.find('select#providerid option:contains("Unassigned")').val('');
166 profile
.find('select#providerid').attr('required', true);
167 profile
.find('select#sex option:contains("Unassigned")').val('');
168 profile
.find('select#sex').attr('required', true);
170 var pid
= profile
.find('input#pid').val();
171 if (pid
< 1) { // form pid set in promise
172 callServer('get_newpid', '', $
("#dob").val(), $
("#lname").val(), $
("#fname").val()); // @TODO escape these
175 nextstepwiz
.removeAttr('disabled').trigger('click');
179 $
("#profileNext").click(function () {
180 var profile
= $
("#profileFrame").contents();
181 var curStep
= $
(this
).closest(".setup-content"),
182 curStepBtn
= curStep
.attr("id"),
183 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
184 curInputs
= $
("#profileFrame").contents().find("input[type='text'],input[type='email'],select"),
186 $
(".form-group").removeClass("has-error");
188 for (var i
= 0; i
< curInputs
.length
; i++
) {
189 if (!curInputs
[i
].validity
.valid
) {
192 curInputs
[i
].scrollIntoView();
193 curInputs
[i
].focus();
196 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
200 provider
= profile
.find('select#providerid').val();
201 nextstepwiz
.removeAttr('disabled').trigger('click');
205 $
("#submitPatient").click(function () {
206 var profile
= $
("#profileFrame").contents();
207 var pid
= profile
.find('input#pid').val();
209 if (pid
< 1) { // Just in case. Can never have too many pid checks!
210 callServer('get_newpid', '');
213 var isOk
= checkRegistration(newPid
);
215 // Use portals rest api. flag 1 is write to chart. flag 0 writes an audit record for review in dashboard.
216 // rest update will determine if new or existing pid for save. In register step-1 we catch existing pid but,
217 // we can still use update here if we want to allow changing passwords.
219 document
.getElementById('profileFrame').contentWindow
.page
.updateModel(1);
220 $
("#insuranceForm").submit();
221 // cleanup is in callServer done promise. This starts end session.
225 $
('div.setup-panel div a.btn-primary').trigger('click');
227 $
('.datepicker').datetimepicker({
228 <?php
$datetimepicker_timepicker = false; ?
>
229 <?php
$datetimepicker_showseconds = false; ?
>
230 <?php
$datetimepicker_formatInput = false; ?
>
231 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
234 $
("#insuranceForm").submit(function (e
) {
236 var url
= "account.php?action=new_insurance&pid=" + newPid
;
240 data
: $
("#insuranceForm").serialize(),
241 success
: function (serverResponse
) {
242 doCredentials(newPid
) // this is the end for session.
248 $
('#selLanguage').on('change', function () {
249 callServer("set_lang", this
.value
);
252 $
(document
.body
).on('hidden.bs.modal', function () { //@TODO maybe make a promise for wiz exit
253 callServer('cleanup');
256 $
('#inscompany').on('change', function () {
257 if ($
('#inscompany').val().toUpperCase() === 'SELF') {
258 $
("#insuranceForm input").removeAttr("required");
259 let message
= "<?php echo xls('You have chosen to be self insured or currently do not have insurance. Click next to continue registration.'); ?>";
266 function doCredentials(pid
) {
267 callServer('do_signup', pid
);
270 function checkRegistration(pid
) {
271 var profile
= $
("#profileFrame").contents();
272 var curStep
= $
("#step-2"),
273 curStepBtn
= curStep
.attr("id"),
274 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
275 curInputs
= $
("#profileFrame").contents().find("input[type='text'],input[type='email'],select"),
277 $
(".form-group").removeClass("has-error");
279 for (var i
= 0; i
< curInputs
.length
; i++
) {
280 if (!curInputs
[i
].validity
.valid
) {
283 curInputs
[i
].scrollIntoView();
284 curInputs
[i
].focus();
287 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
298 function callServer(action
, value
, value2
, last
, first
) {
306 if (action
== 'do_signup') {
312 else if (action
== 'notify_admin') {
319 else if (action
== 'cleanup') {
324 // The magic that is jquery ajax.
329 }).done(function (rtn
) {
330 if (action
== "cleanup") {
331 window
.location
.href
= "./../index.php" // Goto landing page.
333 else if (action
== "set_lang") {
334 window
.location
.href
= window
.location
.href
;
336 else if (action
== "get_newpid") {
337 if (parseInt(rtn
) > 0) {
339 $
("#profileFrame").contents().find('input#pubpid').val(newPid
);
340 $
("#profileFrame").contents().find('input#pid').val(newPid
);
343 // After error alert app exit to landing page.
344 // Existing user error. Error message is translated in account.lib.php.
348 else if (action
== 'do_signup') {
350 let message
= "<?php echo xls('Unable to either create credentials or send email.'); ?>";
354 // For production. Here we're finished so do signup closing alert and then cleanup.
355 callServer('notify_admin', newPid
, provider
); // pnote notify to selected provider
356 // alert below for ease of testing.
357 //alert(rtn); // sync alert.. rtn holds username and password for testing.
359 let message
= "<?php echo xls("Your
new credentials have been sent
. Check your email inbox
and also possibly your spam folder
. Once you log into your patient portal feel free to make an appointment
or send us a secure message
. We look forward to seeing you soon
."); ?>"
360 eModal
.alert(message
); // This is an async call. The modal close event exits us to portal landing page after cleanup.
362 }).fail(function (err
) {
363 let message
= "<?php echo xls('Something went wrong.') ?>";
369 <body
class="skin-blue">
370 <div
class="container">
371 <div
class="stepwiz col-md-offset-3">
372 <div
class="stepwiz-row setup-panel">
373 <div
class="stepwiz-step">
374 <a href
="#step-1" type
="button" class="btn btn-primary btn-circle">1</a
>
375 <p
><?php
echo xlt('Get Started') ?
></p
>
377 <div
class="stepwiz-step">
378 <a href
="#step-2" type
="button" class="btn btn-default btn-circle" disabled
="disabled">2</a
>
379 <p
><?php
echo xlt('Profile') ?
></p
>
381 <div
class="stepwiz-step">
382 <a href
="#step-3" type
="button" class="btn btn-default btn-circle" disabled
="disabled">3</a
>
383 <p
><?php
echo xlt('Insurance') ?
></p
>
385 <div
class="stepwiz-step">
386 <a href
="#step-4" type
="button" class="btn btn-default btn-circle" disabled
="disabled"><?php
echo xlt('Done') ?
></a
>
387 <p
><?php
echo xlt('Register') ?
></p
>
391 <!-- // Start Forms // -->
392 <form
class="form-inline" id
="startForm" role
="form" action
="" method
="post" onsubmit
="">
393 <div
class="row setup-content" id
="step-1">
394 <div
class="col-xs-7 col-md-offset-3 text-center">
396 <legend
class='bg-primary'><?php
echo xlt('Contact') ?
></legend
>
398 <?php
if ($GLOBALS['language_menu_login']) { ?
>
399 <?php
if (count($result3) != 1) { ?
>
400 <div
class="form-group row">
401 <label
for="selLanguage"><?php
echo xlt('Language'); ?
></label
>
402 <select
class="form-control" id
="selLanguage" name
="languageChoice">
404 echo "<option selected='selected' value='" . htmlspecialchars($defaultLangID, ENT_QUOTES
) . "'>" .
405 htmlspecialchars(xl('Default') . " - " . xl($defaultLangName), ENT_NOQUOTES
) . "</option>\n";
406 foreach ($result3 as $iter) {
407 if ($GLOBALS['language_menu_showall']) {
408 if (! $GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
409 continue; // skip the dummy language
411 echo "<option value='" . htmlspecialchars($iter['lang_id'], ENT_QUOTES
) . "'>" .
412 htmlspecialchars($iter['trans_lang_description'], ENT_NOQUOTES
) . "</option>\n";
414 if (in_array($iter['lang_description'], $GLOBALS['language_menu_show'])) {
415 if (! $GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
416 continue; // skip the dummy language
418 echo "<option value='" . htmlspecialchars($iter['lang_id'], ENT_QUOTES
) . "'>" .
419 htmlspecialchars($iter['trans_lang_description'], ENT_NOQUOTES
) . "</option>\n";
428 <div
class="col-sm-12">
429 <div
class="form-group inline">
430 <label
class="control-label" for="fname"><?php
echo xlt('First')?
></label
>
431 <div
class="controls inline-inputs">
432 <input type
="text" class="form-control" id
="fname" required placeholder
="<?php echo xla('First Name'); ?>">
435 <div
class="form-group inline">
436 <label
class="control-label" for="mname"><?php
echo xlt('Middle')?
></label
>
437 <div
class="controls inline-inputs">
438 <input type
="text" class="form-control" id
="mname" placeholder
="<?php echo xla('Full or Initial'); ?>">
441 <div
class="form-group inline">
442 <label
class="control-label" for="lname"><?php
echo xlt('Last Name')?
></label
>
443 <div
class="controls inline-inputs">
444 <input type
="text" class="form-control" id
="lname" required placeholder
="<?php echo xla('Enter Last'); ?>">
449 <div
class="form-group inline">
450 <label
class="control-label" for="dob"><?php
echo xlt('Birth Date')?
></label
>
451 <div
class="controls inline-inputs">
452 <div
class="input-group">
453 <input id
="dob" type
="text" required
class="form-control datepicker" placeholder
="<?php echo xla('YYYY-MM-DD'); ?>" />
458 <div
class="col-sm-12 form-group">
459 <label
class="control-label" for="email"><?php
echo xlt('Enter E-Mail Address')?
></label
>
460 <div
class="controls inline-inputs">
461 <input id
="emailInput" type
="email" class="form-control" style
="width: 100%" required
462 placeholder
="<?php echo xla('Enter email address to receive registration.'); ?>" maxlength
="100">
467 <button
class="btn btn-primary nextBtn btn-sm pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
472 <!-- Profile Form
-->
473 <form
class="form-inline" id
="profileForm" role
="form" action
="account.php" method
="post">
474 <div
class="row setup-content" id
="step-2" style
="display: none">
475 <div
class="col-md-9 col-md-offset-2 text-center">
477 <legend
class='bg-primary'><?php
echo xlt('Profile') ?
></legend
>
479 <div
class="embed-responsive embed-responsive-16by9">
480 <iframe
class="embed-responsive-item" src
="../patient/patientdata?pid=0®ister=true" id
="profileFrame" name
="demo"></iframe
>
483 <button
class="btn btn-primary prevBtn btn-sm pull-left" type
="button"><?php
echo xlt('Previous') ?
></button
>
484 <button
class="btn btn-primary btn-sm pull-right" type
="button" id
="profileNext"><?php
echo xlt('Next') ?
></button
>
489 <!-- Insurance Form
-->
490 <form
class="form-inline" id
="insuranceForm" role
="form" action
="" method
="post">
491 <div
class="row setup-content" id
="step-3" style
="display: none">
492 <div
class="col-xs-6 col-md-offset-3 text-center">
494 <legend
class='bg-primary'><?php
echo xlt('Insurance') ?
></legend
>
496 <div
class="form-group inline">
497 <label
class="control-label" for="provider"><?php
echo xlt('Insurance Company')?
></label
>
498 <div
class="controls inline-inputs">
499 <input type
="text" class="form-control" name
="provider" id
="inscompany" required placeholder
="<?php echo xla('Enter Self if None'); ?>">
502 <div
class="form-group inline">
503 <label
class="control-label" for=""><?php
echo xlt('Plan Name')?
></label
>
504 <div
class="controls inline-inputs">
505 <input type
="text" class="form-control" name
="plan_name" required placeholder
="<?php echo xla('Required'); ?>">
508 <div
class="form-group inline">
509 <label
class="control-label" for=""><?php
echo xlt('Policy Number')?
></label
>
510 <div
class="controls inline-inputs">
511 <input type
="text" class="form-control" name
="policy_number" required placeholder
="<?php echo xla('Required'); ?>">
514 <div
class="form-group inline">
515 <label
class="control-label" for=""><?php
echo xlt('Group Number')?
></label
>
516 <div
class="controls inline-inputs">
517 <input type
="text" class="form-control" name
="group_number" required placeholder
="<?php echo xla('Required'); ?>">
520 <div
class="form-group inline">
521 <label
class="control-label" for=""><?php
echo xlt('Policy Begin Date')?
></label
>
522 <div
class="controls inline-inputs">
523 <input type
="text" class="form-control datepicker" name
="date" placeholder
="<?php echo xla('Policy effective date'); ?>">
526 <div
class="form-group inline">
527 <label
class="control-label" for=""><?php
echo xlt('Co-Payment')?
></label
>
528 <div
class="controls inline-inputs">
529 <input type
="number" class="form-control" name
="copay" placeholder
="<?php echo xla('Plan copay if known'); ?>">
533 <button
class="btn btn-primary prevBtn btn-sm pull-left" type
="button"><?php
echo xlt('Previous') ?
></button
>
534 <button
class="btn btn-primary nextBtn btn-sm pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
539 <!-- End Insurance
. Next what we
've been striving towards..the end-->
540 <div class="row setup-content" id="step-4" style="display: none">
541 <div class="col-xs-6 col-md-offset-3 text-center">
542 <div class="col-md-12">
544 <legend class='bg
-success
'><?php echo xlt('Register
') ?></legend>
545 <div class="well" style="text-align: center">
546 <h4 class='bg
-success
'><?php echo xlt("All set. Click Send Request below to finish registration") ?></h4>
549 <?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. Note to be sure you have given your correct e-mail address. If after receiving credentials and you have trouble with access to the portal, please contact administration.") ?>
552 <button class="btn btn-primary prevBtn btn-sm pull-left" type="button"><?php echo xlt('Previous
') ?></button>
554 <button class="btn btn-success btn-sm pull-right" type="button" id="submitPatient"><?php echo xlt('Send Request
') ?></button>