Add new modules loaded event (#7463)
[openemr.git] / portal / account / register.php
blob429c75943793aa4b9949ac66cce77b355022258b
1 <?php
3 /**
4 * Portal Registration Wizard
6 * @package OpenEMR
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');
25 die();
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');
33 die();
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']);
45 <!DOCTYPE html>
46 <html>
47 <head>
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']); ?>
53 <script>
54 var webRoot = <?php echo js_escape($GLOBALS['web_root']); ?>;
55 top.webroot_url = webRoot;
56 var provider = 0;
58 function restoreSession() {
59 //dummy functions so the dlgopen function will work in the patient portal
60 return true;
63 $(function () {
64 var navListItems = $('div.setup-panel div a'),
65 allWells = $('.setup-content'),
66 allNextBtn = $('.nextBtn'),
67 allPrevBtn = $('.prevBtn');
69 allWells.hide();
71 navListItems.click(function (e) {
72 e.preventDefault();
73 if (!$(this).hasClass('disabled')) {
74 navListItems.removeClass('btn-primary').addClass('btn-light');
75 $(this).addClass('btn-primary').removeClass('btn-light');
76 allWells.hide();
77 $($(this).attr('href')).show();
78 $($(this).attr('href')).find('input:eq(0)').focus();
80 });
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');
87 });
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';
96 }, false);
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"),
102 isValid = true;
104 $(".form-group").removeClass("has-error");
105 for (var i = 0; i < curInputs.length; i++) {
106 if (!curInputs[i].validity.valid) {
107 isValid = false;
108 $(curInputs[i]).closest(".form-group").addClass("has-error");
111 if (isValid) {
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"),
149 isValid = true;
150 $(".form-group").removeClass("has-error");
151 let flg = 0;
152 for (var i = 0; i < curInputs.length; i++) {
153 if (!curInputs[i].validity.valid) {
154 isValid = false;
155 if (!flg) {
156 curInputs[i].scrollIntoView();
157 curInputs[i].focus();
158 flg = 1;
160 $(curInputs[i]).closest(".form-group").addClass("has-error");
163 if (isValid) {
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) {
193 e.preventDefault();
194 let url = "account/account.php?action=new_insurance";
195 $.ajax({
196 url: url,
197 type: 'post',
198 data: $("#insuranceForm").serialize()
199 }).done(function (serverResponse) {
200 doCredentials(provider) // this is the end for session.
201 return false;
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.'); ?>;
209 //alert(message);
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.
218 if (age < 30) {
219 let msg = <?php echo(xlj("Invalid Date format or value! Type date as YYYY-MM-DD or use the calendar.")); ?> ;
220 $(this).val('');
221 $(this).prop('placeholder', 'Invalid Date');
222 alert(msg);
223 return false;
227 }); // ready end
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"),
243 isValid = true;
244 $(".form-group").removeClass("has-error");
245 let flg = 0;
246 for (let i = 0; i < curInputs.length; i++) {
247 if (!curInputs[i].validity.valid) {
248 isValid = false;
249 if (!flg) {
250 curInputs[i].scrollIntoView();
251 curInputs[i].focus();
252 flg = 1;
254 $(curInputs[i]).closest(".form-group").addClass("has-error");
257 return isValid;
260 </script>
261 </head>
262 <style>
263 body {
264 margin-top: 20px
267 .btn-circle {
268 border-radius: .9375rem !important
271 .embedded-content {
272 border: 0;
273 width: 100% !important
276 .reg-email {
277 margin-left: auto;
278 margin-right: auto;
279 width: 50%
282 @media (max-width: 1024px) {
283 .reg-email {
284 width: 100%
288 .stepwiz-row {
289 display: table-row
292 .stepwiz-row::before {
293 background-color: var(--gray400);
294 bottom: 0;
295 content: " ";
296 height: 1px;
297 position: absolute;
298 top: 14px;
299 width: 100%
302 .stepwiz {
303 display: table;
304 margin-top: 20px;
305 position: relative;
306 width: 100%
309 .stepwiz-step {
310 display: table-cell;
311 position: relative;
312 text-align: center
315 .stepwiz-step p {
316 margin-top: 10px
319 .stepwiz-step button[disabled] {
320 filter: alpha(opacity=100) !important;
321 opacity: 1 !important
324 .btn-circle {
325 border-radius: 16px;
326 font-size: 12px;
327 font-weight: 700;
328 height: 35px;
329 line-height: 1.428571429;
330 padding: 6px 0;
331 text-align: center;
332 width: 35px
335 fieldset, input[type=date], input[type=email], input[type=text], select {
336 -webkit-appearance: none;
337 -moz-appearance: none;
338 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/8Uo87SGSaDsP27hgYM/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;
346 box-shadow: none
349 input:required:valid {
350 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAepJREFUeNrEk79PFEEUx9/uDDd7v/AAQQnEQokmJCRGwc7/QeM/YGVxsZJQYI/EhCChICYmUJigNBSGzobQaI5SaYRw6imne0d2D/bYmZ3dGd+YQKEHYiyc5GUyb3Y+77vfeWNpreFfhvXfAWAAJtbKi7dff1rWK9vPHx3mThP2Iaipk5EzTg8Qmru38H7izmkFHAF4WH1R52654PR0Oamzj2dKxYt/Bbg1OPZuY3d9aU82VGem/5LtnJscLxWzfzRxaWNqWJP0XUadIbSzu5DuvUJpzq7sfYBKsP1GJeLB+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
354 </style>
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>
364 </div>
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>
368 </div>
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>
372 </div>
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>
376 </div>
377 </div>
378 </div>
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>
390 </div>
391 <button class="btn btn-primary nextBtn pull-right" type="button"><?php echo xlt('Next') ?></button>
392 </div>
393 </form>
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&register=true" id="profileFrame" name="Profile Info"></iframe>
400 </div>
401 <button class="btn btn-primary pull-right" type="button" id="profileNext"><?php echo xlt('Next') ?></button>
402 </div>
403 </form>
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'); ?>">
414 </div>
415 </div>
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'); ?>">
420 </div>
421 </div>
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'); ?>">
426 </div>
427 </div>
428 </div>
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'); ?>">
434 </div>
435 </div>
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'); ?>">
440 </div>
441 </div>
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'); ?>">
446 </div>
447 </div>
448 </div>
449 </div>
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>
452 </div>
453 </form>
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>
459 <hr />
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.") ?>
462 </p>
463 </div>
464 <hr />
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>
467 </div>
468 </div>
469 </body>
470 </html>