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['authUser'] = 'portal-user';
17 $_SESSION['pid'] = true;
18 $_SESSION['register'] = true;
20 $_SESSION['site_id'] = isset($_SESSION['site_id']) ?
$_SESSION['site_id'] : 'default';
21 $landingpage = "index.php?site=" . $_SESSION['site_id'];
23 $ignoreAuth_onsite_portal_two = true;
25 require_once("../../interface/globals.php");
27 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array(
28 $GLOBALS['language_default']
30 for ($iter = 0; $row = sqlFetchArray($res2); $iter ++
) {
31 $result2[$iter] = $row;
33 if (count($result2) == 1) {
34 $defaultLangID = $result2[0]{"lang_id"};
35 $defaultLangName = $result2[0]{"lang_description"};
37 // default to english if any problems
39 $defaultLangName = "English";
42 if (! isset($_SESSION['language_choice'])) {
43 $_SESSION['language_choice'] = $defaultLangID;
45 // collect languages if showing language menu
46 if ($GLOBALS['language_menu_login']) {
47 // sorting order of language titles depends on language translation options.
48 $mainLangID = empty($_SESSION['language_choice']) ?
'1' : $_SESSION['language_choice'];
49 if ($mainLangID == '1' && ! empty($GLOBALS['skip_english_translation'])) {
50 $sql = "SELECT * FROM lang_languages ORDER BY lang_description, lang_id";
51 $res3 = SqlStatement($sql);
53 // Use and sort by the translated language name.
54 $sql = "SELECT ll.lang_id, " . "IF(LENGTH(ld.definition),ld.definition,ll.lang_description) AS trans_lang_description, " . "ll.lang_description " .
55 "FROM lang_languages AS ll " . "LEFT JOIN lang_constants AS lc ON lc.constant_name = ll.lang_description " .
56 "LEFT JOIN lang_definitions AS ld ON ld.cons_id = lc.cons_id AND " . "ld.lang_id = ? " .
57 "ORDER BY IF(LENGTH(ld.definition),ld.definition,ll.lang_description), ll.lang_id";
58 $res3 = SqlStatement($sql, array(
63 for ($iter = 0; $row = sqlFetchArray($res3); $iter ++
) {
64 $result3[$iter] = $row;
67 if (count($result3) == 1) {
68 // default to english if only return one language
69 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='1' />\n";
72 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='" . htmlspecialchars($defaultLangID, ENT_QUOTES
) . "' />\n";
79 <title
><?php
echo xlt('New Patient'); ?
> |
<?php
echo xlt('Register'); ?
></title
>
80 <meta content
='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name
='viewport'>
81 <meta name
="description" content
="Developed By sjpadgett@gmail.com">
83 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/font-awesome-4-6-3/css/font-awesome.min.css" rel
="stylesheet" type
="text/css" />
84 <link rel
="stylesheet" href
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-datetimepicker-2-5-4/build/jquery.datetimepicker.min.css">
85 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/css/bootstrap.min.css" rel
="stylesheet" type
="text/css" />
86 <link href
="./../assets/css/register.css" rel
="stylesheet" type
="text/css" />
88 <script src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-3-1-1/index.js" type
="text/javascript"></script
>
90 <script src
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/js/bootstrap.min.js" type
="text/javascript"></script
>
91 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-datetimepicker-2-5-4/build/jquery.datetimepicker.full.min.js"></script
>
92 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/emodal-1-2-65/dist/eModal.js"></script
>
99 $
(document
).ready(function () {
101 $("#emailInput").val("me@me.com");
102 $("#fname").val("Jerry");
103 $("#lname").val("Padgett");
104 $("#dob").val("1919-03-03");
106 var navListItems
= $
('div.setup-panel div a'),
107 allWells
= $
('.setup-content'),
108 allNextBtn
= $
('.nextBtn'),
109 allPrevBtn
= $
('.prevBtn');
113 navListItems
.click(function (e
) {
115 var $target = $
($
(this
).attr('href')),
118 if (!$item.hasClass('disabled')) {
119 navListItems
.removeClass('btn-primary').addClass('btn-default');
120 $item.addClass('btn-primary');
123 $target.find('input:eq(0)').focus();
127 allPrevBtn
.click(function () {
128 var curStep
= $
(this
).closest(".setup-content"),
129 curStepBtn
= curStep
.attr("id"),
130 prevstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().prev().children("a");
131 prevstepwiz
.removeAttr('disabled').trigger('click');
134 allNextBtn
.click(function () {
135 var profile
= $
("#profileFrame").contents();
138 profile.find("input#street").val("123 Some St.");
139 profile.find("input#city").val("Brandon");
140 //--------------------- */
142 var curStep
= $
(this
).closest(".setup-content"),
143 curStepBtn
= curStep
.attr("id"),
144 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
145 curInputs
= curStep
.find("input[type='text'],input[type='email'],select"),
148 $
(".form-group").removeClass("has-error");
149 for (var i
= 0; i
< curInputs
.length
; i++
) {
150 if (!curInputs
[i
].validity
.valid
) {
152 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
156 if (curStepBtn
== 'step-1') { // leaving step 1 setup profile frame. Prob not nec but in case
157 profile
.find('input#fname').val($
("#fname").val());
158 profile
.find('input#mname').val($
("#mname").val());
159 profile
.find('input#lname').val($
("#lname").val());
160 profile
.find('input#dob').val($
("#dob").val());
161 profile
.find('input#email').val($
("#emailInput").val());
162 profile
.find('input[name=allowPatientPortal]').val(['YES']);
163 // need these for validation.
164 profile
.find('select#providerid option:contains("Unassigned")').val('');
165 profile
.find('select#providerid').attr('required', true);
166 profile
.find('select#sex option:contains("Unassigned")').val('');
167 profile
.find('select#sex').attr('required', true);
169 var pid
= profile
.find('input#pid').val();
170 if (pid
< 1) { // form pid set in promise
171 callServer('get_newpid', '', $
("#dob").val(), $
("#lname").val(), $
("#fname").val()); // @TODO escape these
174 nextstepwiz
.removeAttr('disabled').trigger('click');
178 $
("#profileNext").click(function () {
179 var profile
= $
("#profileFrame").contents();
180 var curStep
= $
(this
).closest(".setup-content"),
181 curStepBtn
= curStep
.attr("id"),
182 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
183 curInputs
= $
("#profileFrame").contents().find("input[type='text'],input[type='email'],select"),
185 $
(".form-group").removeClass("has-error");
187 for (var i
= 0; i
< curInputs
.length
; i++
) {
188 if (!curInputs
[i
].validity
.valid
) {
191 curInputs
[i
].scrollIntoView();
192 curInputs
[i
].focus();
195 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
199 provider
= profile
.find('select#providerid').val();
200 nextstepwiz
.removeAttr('disabled').trigger('click');
204 $
("#submitPatient").click(function () {
205 var profile
= $
("#profileFrame").contents();
206 var pid
= profile
.find('input#pid').val();
208 if (pid
< 1) { // Just in case. Can never have too many pid checks!
209 callServer('get_newpid', '');
212 var isOk
= checkRegistration(newPid
);
214 // Use portals rest api. flag 1 is write to chart. flag 0 writes an audit record for review in dashboard.
215 // rest update will determine if new or existing pid for save. In register step-1 we catch existing pid but,
216 // we can still use update here if we want to allow changing passwords.
218 document
.getElementById('profileFrame').contentWindow
.page
.updateModel(1);
219 $
("#insuranceForm").submit();
220 // cleanup is in callServer done promise. This starts end session.
224 $
('div.setup-panel div a.btn-primary').trigger('click');
226 $
('.datepicker').datetimepicker({
227 <?php
$datetimepicker_timepicker = false; ?
>
228 <?php
$datetimepicker_showseconds = false; ?
>
229 <?php
$datetimepicker_formatInput = false; ?
>
230 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
233 $
("#insuranceForm").submit(function (e
) {
235 var url
= "account.php?action=new_insurance&pid=" + newPid
;
239 data
: $
("#insuranceForm").serialize(),
240 success
: function (serverResponse
) {
241 doCredentials(newPid
) // this is the end for session.
247 $
('#selLanguage').on('change', function () {
248 callServer("set_lang", this
.value
);
251 $
(document
.body
).on('hidden.bs.modal', function () { //@TODO maybe make a promise for wiz exit
252 callServer('cleanup');
255 $
('#inscompany').on('change', function () {
256 if ($
('#inscompany').val().toUpperCase() === 'SELF') {
257 $
("#insuranceForm input").removeAttr("required");
258 let message
= "<?php echo xls('You have chosen to be self insured or currently do not have insurance. Click next to continue registration.'); ?>";
265 function doCredentials(pid
) {
266 callServer('do_signup', pid
);
269 function checkRegistration(pid
) {
270 var profile
= $
("#profileFrame").contents();
271 var curStep
= $
("#step-2"),
272 curStepBtn
= curStep
.attr("id"),
273 nextstepwiz
= $
('div.setup-panel div a[href="#' + curStepBtn +
'"]').parent().next().children("a"),
274 curInputs
= $
("#profileFrame").contents().find("input[type='text'],input[type='email'],select"),
276 $
(".form-group").removeClass("has-error");
278 for (var i
= 0; i
< curInputs
.length
; i++
) {
279 if (!curInputs
[i
].validity
.valid
) {
282 curInputs
[i
].scrollIntoView();
283 curInputs
[i
].focus();
286 $
(curInputs
[i
]).closest(".form-group").addClass("has-error");
297 function callServer(action
, value
, value2
, last
, first
) {
305 if (action
== 'do_signup') {
311 else if (action
== 'notify_admin') {
318 else if (action
== 'cleanup') {
323 // The magic that is jquery ajax.
328 }).done(function (rtn
) {
329 if (action
== "cleanup") {
330 window
.location
.href
= "./../index.php" // Goto landing page.
332 else if (action
== "set_lang") {
333 window
.location
.href
= window
.location
.href
;
335 else if (action
== "get_newpid") {
336 if (parseInt(rtn
) > 0) {
338 $
("#profileFrame").contents().find('input#pubpid').val(newPid
);
339 $
("#profileFrame").contents().find('input#pid').val(newPid
);
342 // After error alert app exit to landing page.
343 // Existing user error. Error message is translated in account.lib.php.
347 else if (action
== 'do_signup') {
349 let message
= "<?php echo xls('Unable to either create credentials or send email.'); ?>";
353 // For production. Here we're finished so do signup closing alert and then cleanup.
354 callServer('notify_admin', newPid
, provider
); // pnote notify to selected provider
355 // alert below for ease of testing.
356 //alert(rtn); // sync alert.. rtn holds username and password for testing.
358 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
."); ?>"
359 eModal
.alert(message
); // This is an async call. The modal close event exits us to portal landing page after cleanup.
361 }).fail(function (err
) {
362 let message
= "<?php echo xls('Something went wrong.') ?>";
368 <body
class="skin-blue">
369 <div
class="container">
370 <div
class="stepwiz col-md-offset-3">
371 <div
class="stepwiz-row setup-panel">
372 <div
class="stepwiz-step">
373 <a href
="#step-1" type
="button" class="btn btn-primary btn-circle">1</a
>
374 <p
><?php
echo xlt('Get Started') ?
></p
>
376 <div
class="stepwiz-step">
377 <a href
="#step-2" type
="button" class="btn btn-default btn-circle" disabled
="disabled">2</a
>
378 <p
><?php
echo xlt('Profile') ?
></p
>
380 <div
class="stepwiz-step">
381 <a href
="#step-3" type
="button" class="btn btn-default btn-circle" disabled
="disabled">3</a
>
382 <p
><?php
echo xlt('Insurance') ?
></p
>
384 <div
class="stepwiz-step">
385 <a href
="#step-4" type
="button" class="btn btn-default btn-circle" disabled
="disabled"><?php
echo xlt('Done') ?
></a
>
386 <p
><?php
echo xlt('Register') ?
></p
>
390 <!-- // Start Forms // -->
391 <form
class="form-inline" id
="startForm" role
="form" action
="" method
="post" onsubmit
="">
392 <div
class="row setup-content" id
="step-1">
393 <div
class="col-xs-7 col-md-offset-3 text-center">
395 <legend
class='bg-primary'><?php
echo xlt('Contact') ?
></legend
>
397 <?php
if ($GLOBALS['language_menu_login']) { ?
>
398 <?php
if (count($result3) != 1) { ?
>
399 <div
class="form-group row">
400 <label
for="selLanguage"><?php
echo xlt('Language'); ?
></label
>
401 <select
class="form-control" id
="selLanguage" name
="languageChoice">
403 echo "<option selected='selected' value='" . htmlspecialchars($defaultLangID, ENT_QUOTES
) . "'>" .
404 htmlspecialchars(xl('Default') . " - " . xl($defaultLangName), ENT_NOQUOTES
) . "</option>\n";
405 foreach ($result3 as $iter) {
406 if ($GLOBALS['language_menu_showall']) {
407 if (! $GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
408 continue; // skip the dummy language
410 echo "<option value='" . htmlspecialchars($iter['lang_id'], ENT_QUOTES
) . "'>" .
411 htmlspecialchars($iter['trans_lang_description'], ENT_NOQUOTES
) . "</option>\n";
413 if (in_array($iter['lang_description'], $GLOBALS['language_menu_show'])) {
414 if (! $GLOBALS['allow_debug_language'] && $iter['lang_description'] == 'dummy') {
415 continue; // skip the dummy language
417 echo "<option value='" . htmlspecialchars($iter['lang_id'], ENT_QUOTES
) . "'>" .
418 htmlspecialchars($iter['trans_lang_description'], ENT_NOQUOTES
) . "</option>\n";
427 <div
class="col-sm-12">
428 <div
class="form-group inline">
429 <label
class="control-label" for="fname"><?php
echo xlt('First')?
></label
>
430 <div
class="controls inline-inputs">
431 <input type
="text" class="form-control" id
="fname" required placeholder
="<?php echo xla('First Name'); ?>">
434 <div
class="form-group inline">
435 <label
class="control-label" for="mname"><?php
echo xlt('Middle')?
></label
>
436 <div
class="controls inline-inputs">
437 <input type
="text" class="form-control" id
="mname" placeholder
="<?php echo xla('Full or Initial'); ?>">
440 <div
class="form-group inline">
441 <label
class="control-label" for="lname"><?php
echo xlt('Last Name')?
></label
>
442 <div
class="controls inline-inputs">
443 <input type
="text" class="form-control" id
="lname" required placeholder
="<?php echo xla('Enter Last'); ?>">
448 <div
class="form-group inline">
449 <label
class="control-label" for="dob"><?php
echo xlt('Birth Date')?
></label
>
450 <div
class="controls inline-inputs">
451 <div
class="input-group">
452 <input id
="dob" type
="text" required
class="form-control datepicker" placeholder
="<?php echo xla('YYYY-MM-DD'); ?>" />
457 <div
class="col-sm-12 form-group">
458 <label
class="control-label" for="email"><?php
echo xlt('Enter E-Mail Address')?
></label
>
459 <div
class="controls inline-inputs">
460 <input id
="emailInput" type
="email" class="form-control" style
="width: 100%" required
461 placeholder
="<?php echo xla('Enter email address to receive registration.'); ?>" maxlength
="100">
466 <button
class="btn btn-primary nextBtn btn-sm pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
471 <!-- Profile Form
-->
472 <form
class="form-inline" id
="profileForm" role
="form" action
="account.php" method
="post">
473 <div
class="row setup-content" id
="step-2" style
="display: none">
474 <div
class="col-md-9 col-md-offset-2 text-center">
476 <legend
class='bg-primary'><?php
echo xlt('Profile') ?
></legend
>
478 <div
class="embed-responsive embed-responsive-16by9">
479 <iframe
class="embed-responsive-item" src
="../patient/patientdata?pid=0®ister=true" id
="profileFrame" name
="demo"></iframe
>
482 <button
class="btn btn-primary prevBtn btn-sm pull-left" type
="button"><?php
echo xlt('Previous') ?
></button
>
483 <button
class="btn btn-primary btn-sm pull-right" type
="button" id
="profileNext"><?php
echo xlt('Next') ?
></button
>
488 <!-- Insurance Form
-->
489 <form
class="form-inline" id
="insuranceForm" role
="form" action
="" method
="post">
490 <div
class="row setup-content" id
="step-3" style
="display: none">
491 <div
class="col-xs-6 col-md-offset-3 text-center">
493 <legend
class='bg-primary'><?php
echo xlt('Insurance') ?
></legend
>
495 <div
class="form-group inline">
496 <label
class="control-label" for="provider"><?php
echo xlt('Insurance Company')?
></label
>
497 <div
class="controls inline-inputs">
498 <input type
="text" class="form-control" name
="provider" id
="inscompany" required placeholder
="<?php echo xla('Enter Self if None'); ?>">
501 <div
class="form-group inline">
502 <label
class="control-label" for=""><?php
echo xlt('Plan Name')?
></label
>
503 <div
class="controls inline-inputs">
504 <input type
="text" class="form-control" name
="plan_name" required placeholder
="<?php echo xla('Required'); ?>">
507 <div
class="form-group inline">
508 <label
class="control-label" for=""><?php
echo xlt('Policy Number')?
></label
>
509 <div
class="controls inline-inputs">
510 <input type
="text" class="form-control" name
="policy_number" required placeholder
="<?php echo xla('Required'); ?>">
513 <div
class="form-group inline">
514 <label
class="control-label" for=""><?php
echo xlt('Group Number')?
></label
>
515 <div
class="controls inline-inputs">
516 <input type
="text" class="form-control" name
="group_number" required placeholder
="<?php echo xla('Required'); ?>">
519 <div
class="form-group inline">
520 <label
class="control-label" for=""><?php
echo xlt('Policy Begin Date')?
></label
>
521 <div
class="controls inline-inputs">
522 <input type
="text" class="form-control datepicker" name
="date" placeholder
="<?php echo xla('Policy effective date'); ?>">
525 <div
class="form-group inline">
526 <label
class="control-label" for=""><?php
echo xlt('Co-Payment')?
></label
>
527 <div
class="controls inline-inputs">
528 <input type
="number" class="form-control" name
="copay" placeholder
="<?php echo xla('Plan copay if known'); ?>">
532 <button
class="btn btn-primary prevBtn btn-sm pull-left" type
="button"><?php
echo xlt('Previous') ?
></button
>
533 <button
class="btn btn-primary nextBtn btn-sm pull-right" type
="button"><?php
echo xlt('Next') ?
></button
>
538 <!-- End Insurance
. Next what we
've been striving towards..the end-->
539 <div class="row setup-content" id="step-4" style="display: none">
540 <div class="col-xs-6 col-md-offset-3 text-center">
541 <div class="col-md-12">
543 <legend class='bg
-success
'><?php echo xlt('Register
') ?></legend>
544 <div class="well" style="text-align: center">
545 <h4 class='bg
-success
'><?php echo xlt("All set. Click Send Request below to finish registration") ?></h4>
548 <?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.") ?>
551 <button class="btn btn-primary prevBtn btn-sm pull-left" type="button"><?php echo xlt('Previous
') ?></button>
553 <button class="btn btn-success btn-sm pull-right" type="button" id="submitPatient"><?php echo xlt('Send Request
') ?></button>