PP2 Update (#1758)
[openemr.git] / portal / account / register.php
blobf21917e1930be11f326cd91a08408f120b425cc4
1 <?php
2 /**
3 * Portal Registration Wizard
5 * @package OpenEMR
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
12 session_start();
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']
29 ));
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"};
36 } else {
37 // default to english if any problems
38 $defaultLangID = 1;
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);
52 } else {
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(
59 $mainLangID
60 ));
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";
71 } else {
72 $hiddenLanguageField = "<input type='hidden' name='languageChoice' value='" . htmlspecialchars($defaultLangID, ENT_QUOTES) . "' />\n";
76 <!DOCTYPE html>
77 <html>
78 <head>
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>
94 <script>
95 var newPid = 0;
96 var curPid = 0;
97 var provider = 0;
99 $(document).ready(function () {
100 /* // test data
101 $("#emailInput").val("me@me.com");
102 $("#fname").val("Jerry");
103 $("#lname").val("Padgett");
104 $("#dob").val("1919-03-03");
105 // ---------- */
106 var navListItems = $('div.setup-panel div a'),
107 allWells = $('.setup-content'),
108 allNextBtn = $('.nextBtn'),
109 allPrevBtn = $('.prevBtn');
111 allWells.hide();
113 navListItems.click(function (e) {
114 e.preventDefault();
115 var $target = $($(this).attr('href')),
116 $item = $(this);
118 if (!$item.hasClass('disabled')) {
119 navListItems.removeClass('btn-primary').addClass('btn-default');
120 $item.addClass('btn-primary');
121 allWells.hide();
122 $target.show();
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();
137 /* // test data
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"),
146 isValid = true;
148 $(".form-group").removeClass("has-error");
149 for (var i = 0; i < curInputs.length; i++) {
150 if (!curInputs[i].validity.valid) {
151 isValid = false;
152 $(curInputs[i]).closest(".form-group").addClass("has-error");
155 if (isValid) {
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"),
184 isValid = true;
185 $(".form-group").removeClass("has-error");
186 var flg = 0;
187 for (var i = 0; i < curInputs.length; i++) {
188 if (!curInputs[i].validity.valid) {
189 isValid = false;
190 if (!flg) {
191 curInputs[i].scrollIntoView();
192 curInputs[i].focus();
193 flg = 1;
195 $(curInputs[i]).closest(".form-group").addClass("has-error");
198 if (isValid) {
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);
213 if (isOk) {
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) {
234 e.preventDefault();
235 var url = "account.php?action=new_insurance&pid=" + newPid;
236 $.ajax({
237 url: url,
238 type: 'post',
239 data: $("#insuranceForm").serialize(),
240 success: function (serverResponse) {
241 doCredentials(newPid) // this is the end for session.
242 return false;
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.'); ?>";
259 alert(message);
263 }); // ready end
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"),
275 isValid = true;
276 $(".form-group").removeClass("has-error");
277 var flg = 0;
278 for (var i = 0; i < curInputs.length; i++) {
279 if (!curInputs[i].validity.valid) {
280 isValid = false;
281 if (!flg) {
282 curInputs[i].scrollIntoView();
283 curInputs[i].focus();
284 flg = 1;
286 $(curInputs[i]).closest(".form-group").addClass("has-error");
290 if (!isValid) {
291 return false;
294 return true;
297 function callServer(action, value, value2, last, first) {
298 var data = {
299 'action' : action,
300 'value' : value,
301 'dob' : value2,
302 'last' : last,
303 'first' : first
305 if (action == 'do_signup') {
306 data = {
307 'action': action,
308 'pid': value
311 else if (action == 'notify_admin') {
312 data = {
313 'action': action,
314 'pid': value,
315 'provider': value2
318 else if (action == 'cleanup') {
319 data = {
320 'action': action
323 // The magic that is jquery ajax.
324 $.ajax({
325 type : 'GET',
326 url : 'account.php',
327 data : data
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) {
337 newPid = rtn;
338 $("#profileFrame").contents().find('input#pubpid').val(newPid);
339 $("#profileFrame").contents().find('input#pid').val(newPid);
341 else {
342 // After error alert app exit to landing page.
343 // Existing user error. Error message is translated in account.lib.php.
344 eModal.alert(rtn);
347 else if (action == 'do_signup') {
348 if (rtn == "") {
349 let message = "<?php echo xls('Unable to either create credentials or send email.'); ?>";
350 alert(message);
351 return false;
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.') ?>";
363 alert(message);
366 </script>
367 </head>
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>
375 </div>
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>
379 </div>
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>
383 </div>
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>
387 </div>
388 </div>
389 </div>
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">
394 <fieldset>
395 <legend class='bg-primary'><?php echo xlt('Contact') ?></legend>
396 <div class="well">
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">
402 <?php
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";
412 } else {
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";
423 </select>
424 </div>
425 <?php } } ?>
426 <div class="row">
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'); ?>">
432 </div>
433 </div>
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'); ?>">
438 </div>
439 </div>
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'); ?>">
444 </div>
445 </div>
446 </div>
447 </div>
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'); ?>" />
453 </div>
454 </div>
455 </div>
456 <div class="row">
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">
462 </div>
463 </div>
464 </div>
465 </div>
466 <button class="btn btn-primary nextBtn btn-sm pull-right" type="button"><?php echo xlt('Next') ?></button>
467 </fieldset>
468 </div>
469 </div>
470 </form>
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">
475 <fieldset>
476 <legend class='bg-primary'><?php echo xlt('Profile') ?></legend>
477 <div class="well">
478 <div class="embed-responsive embed-responsive-16by9">
479 <iframe class="embed-responsive-item" src="../patient/patientdata?pid=0&register=true" id="profileFrame" name="demo"></iframe>
480 </div>
481 </div>
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>
484 </fieldset>
485 </div>
486 </div>
487 </form>
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">
492 <fieldset>
493 <legend class='bg-primary'><?php echo xlt('Insurance') ?></legend>
494 <div class="well">
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'); ?>">
499 </div>
500 </div>
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'); ?>">
505 </div>
506 </div>
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'); ?>">
511 </div>
512 </div>
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'); ?>">
517 </div>
518 </div>
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'); ?>">
523 </div>
524 </div>
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'); ?>">
529 </div>
530 </div>
531 </div>
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>
534 </fieldset>
535 </div>
536 </div>
537 </form>
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">
542 <fieldset>
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>
546 <hr>
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.") ?>
549 </p>
550 </div>
551 <button class="btn btn-primary prevBtn btn-sm pull-left" type="button"><?php echo xlt('Previous') ?></button>
552 <hr>
553 <button class="btn btn-success btn-sm pull-right" type="button" id="submitPatient"><?php echo xlt('Send Request') ?></button>
554 </fieldset>
555 </div>
556 </div>
557 </div>
558 </div>
559 </body>
560 </html>