Adding Password Strength Meter Fixes #7366 (#7367)
[openemr.git] / setup.php
blob35e8be9a55ec18303beed0a52ba6ac9eec7f1f4e
1 <?php
3 /**
5 * Installation script.
7 * To make this script a bit more simpler to figure out, here is a listing of the "state"(s)
8 * which are basically the steps that this installation script goes through.
9 * state:
10 * empty or 0 - Ensures required file and directory permission are correctly set prior to starting installation.
11 * 1 - Step 1: Select database setup (ie. create the database or use a database already initialized)
12 * 2 - Step 2: Enter in database and openemr user information
13 * 3 - Step 3: Create database
14 * 4 - Step 4: Instructions on configuring PHP
15 * 5 - Step 5: Instructions on configuring Apache
16 * 6 - Step 6: Select a theme
17 * 7 - Final step: Several miscellaneous instruction, login credentials, and link to OpenEMR
19 * @package OpenEMR
20 * @link https://www.open-emr.org
21 * @author Roberto Vasquez <robertogagliotta@gmail.com>
22 * @author Scott Wakefield <scott@npclinics.com.au>
23 * @author Ranganath Pathak <pathak@scrs1.org>
24 * @author Brady Miller <brady.g.miller@gmail.com>
25 * @copyright Copyright (c) 2016 Roberto Vasquez <robertogagliotta@gmail.com>
26 * @copyright Copyright (c) 2016 Scott Wakefield <scott@npclinics.com.au>
27 * @copyright Copyright (c) 2019 Ranganath Pathak <pathak@scrs1.org>
28 * @copyright Copyright (c) 2019-2021 Brady Miller <brady.g.miller@gmail.com>
29 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
32 // Checks if the server's PHP version is compatible with OpenEMR:
33 require_once(dirname(__FILE__) . "/src/Common/Compatibility/Checker.php");
34 $response = OpenEMR\Common\Compatibility\Checker::checkPhpVersion();
35 if ($response !== true) {
36 die(htmlspecialchars($response));
39 // Set the maximum excution time and time limit to unlimited.
40 ini_set('max_execution_time', 0);
41 ini_set('display_errors', 0);
42 set_time_limit(0);
44 // Warning. If you set $allow_multisite_setup to true, this is a potential security vulnerability.
45 // Recommend setting it back to false (or removing this setup.php script entirely) after you
46 // are done with the multisite procedure.
47 $allow_multisite_setup = false;
49 // Warning. If you set $allow_cloning_setup to true, this is a potential security vulnerability.
50 // Recommend setting it back to false (or removing this setup.php script entirely) after you
51 // are done with the cloning setup procedure.
52 $allow_cloning_setup = false;
53 if (!$allow_cloning_setup && !empty($_REQUEST['clone_database'])) {
54 require_once(dirname(__FILE__) . "/src/Common/Session/SessionUtil.php");
55 OpenEMR\Common\Session\SessionUtil::setupScriptSessionStart();
56 OpenEMR\Common\Session\SessionUtil::setupScriptSessionCookieDestroy();
57 die("To turn on support for cloning setup, need to edit this script and change \$allow_cloning_setup to true. After you are done setting up the cloning, ensure you change \$allow_cloning_setup back to false or remove this script altogether");
60 function recursive_writable_directory_test($dir)
62 // first, collect the directory and subdirectories
63 $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
64 $dirNames = array();
65 foreach ($ri as $file) {
66 if ($file->isDir()) {
67 if (!preg_match("/\.\.$/", $file->getPathname())) {
68 $dirName = realpath($file->getPathname());
69 if (!in_array($dirName, $dirNames)) {
70 $dirNames[] = $dirName;
76 // second, flag the directories that are not writable
77 $resultsNegative = array();
78 foreach ($dirNames as $value) {
79 if (!is_writable($value)) {
80 $resultsNegative[] = $value;
84 // third, send the output and return if didn't pass the test
85 if (!empty($resultsNegative)) {
86 echo "<p>";
87 $mainDirTest = "";
88 $outputs = array();
89 foreach ($resultsNegative as $failedDir) {
90 if (basename($failedDir) == basename($dir)) {
91 // need to reorder output so the main directory is at the top of the list
92 $mainDirTest = "<span class='text-danger'>UNABLE</span> to open directory '" . text(realpath($failedDir)) . "' for writing by web server.<br />\r\n";
93 } else {
94 $outputs[] = "<span class='text-danger'>UNABLE</span> to open subdirectory '" . text(realpath($failedDir)) . "' for writing by web server.<br />\r\n";
97 if ($mainDirTest) {
98 // need to reorder output so the main directory is at the top of the list
99 array_unshift($outputs, $mainDirTest);
101 foreach ($outputs as $output) {
102 echo $output;
104 echo "(configure directory permissions; see below for further instructions)</p>\r\n";
105 return 1;
106 } else {
107 echo "<code class='ml-5'>" . text(realpath($dir)) . "</code> directory and its subdirectories are <span class='text-success font-weight-bold'>ready</span>.<br /><br />\r\n";
108 return 0;
112 // Include standard libraries/classes
113 require_once dirname(__FILE__) . "/vendor/autoload.php";
115 use OpenEMR\Common\Csrf\CsrfUtils;
116 use OpenEMR\Common\Session\SessionUtil;
117 use OpenEMR\Common\Utils\RandomGenUtils;
119 $state = isset($_POST["state"]) ? ($_POST["state"]) : '';
120 $installer = new Installer($_REQUEST);
121 // Make this true for IPPF.
122 $ippf_specific = false;
124 $error_page_end = <<<EPE
125 </div>
126 </div>
127 </div><!--end of container div-->
128 </body>
129 </html>
130 EPE;
132 // If this script was invoked with no site ID, then ask for one. (only if allow multisite is on, otherwise will assume default site)
133 if (empty($_REQUEST['site']) && $allow_multisite_setup && empty($state)) {
134 $site_id = <<<SITEID
135 <!DOCTYPE html>
136 <html>
137 <head>
138 <title>OpenEMR Setup Tool</title>
139 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css">
140 <script src="public/assets/jquery/dist/jquery.min.js"></script>
141 <script src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
142 <link rel="stylesheet" href="public/assets/@fortawesome/fontawesome-free/css/all.min.css">
143 <link rel="shortcut icon" href="public/images/favicon.ico" />
144 <style>
145 .oe-pull-away {
146 float:right;
148 </style>
149 </head>
150 <body>
151 <nav class="navbar navbar-expand navbar-light bg-light">
152 <div class="container">
153 <a class="navbar-brand" href="#">OpenEMR Setup</a>
154 <div class="collapse navbar-collapse justify-content-end">
155 <ul class="navbar-nav">
156 <li class="nav-item active">
157 <a class="nav-link" href="#" data-target="#myModal" data-toggle="modal" href="#" id="help-href" name="help-href" title="Click to view Help">Help</span></a>
158 </li>
159 </ul>
160 </div>
161 </div>
162 </nav>
164 <div class='container mt-3'>
165 <div class="row">
166 <div class="col-12">
167 <h3 class="mb-3 border-bottom">Optional Site ID Selection</h3>
168 <div class="jumbotron p-5">
170 Most OpenEMR installations support only one site. If that is
171 true for you then ignore the rest of this text and just click Continue.
172 </p>
173 <p class='p-2 bg-warning'>
174 If you are using the multisite setup module for the first time please read the
175 'Multi Site Installation' section of the help file before proceeding.
176 </p>
178 Otherwise please enter a unique Site ID here.
179 </p>
181 A Site ID is a short identifier with no spaces or special
182 characters other than periods or dashes. It is case-sensitive and we
183 suggest sticking to lower case letters for ease of use.
184 </p>
186 If each site will have its own host/domain name, then use that
187 name as the Site ID (e.g. www.example.com).
188 </p>
190 The site ID is used to identify which site you will log in to.
191 If it is a hostname then it is taken from the hostname in the URL.
192 Otherwise you must append "?site=<i>siteid</i>" to the URL used for
193 logging in.
194 </p>
196 It is OK for one of the sites to have "default" as its ID. This
197 is the ID that will be used if it cannot otherwise be determined.
198 </p>
199 <br />
200 <form method='post'>
201 <input type='hidden' name='state' value='0' />
202 <div class="form-row">
203 <div class="col-auto">
204 Site ID:
205 </div>
206 <div class="col">
207 <input type='text' class='form-control' name='site' value='default'>
208 </div>
209 <div class="col-12 mt-3">
210 <button type='submit' class='btn btn-primary' value='Continue'><i class="fas fa-chevron-right"></i> Continue</button>
211 </div>
212 </div>
213 </form>
214 </div>
215 </div>
216 </div>
217 </div><!--end of container div-->
218 SITEID;
219 echo $site_id . "\r\n";
220 $installer->setupHelpModal();
221 echo "</body>" . "\r\n";
222 echo "</html>" . "\r\n";
224 exit();
227 // Support "?site=siteid" in the URL, otherwise assume "default".
228 $site_id = 'default';
229 if (!empty($_REQUEST['site'])) {
230 $site_id = trim($_REQUEST['site']);
233 // Die if site ID is empty or has invalid characters.
234 if (empty($site_id) || preg_match('/[^A-Za-z0-9\\-.]/', $site_id)) {
235 SessionUtil::setupScriptSessionStart();
236 SessionUtil::setupScriptSessionCookieDestroy();
237 die("Site ID '" . text($site_id) . "' contains invalid characters.");
240 // If multisite is turned off, then only allow default for site.
241 if (!$allow_multisite_setup && $site_id != 'default') {
242 SessionUtil::setupScriptSessionStart();
243 SessionUtil::setupScriptSessionCookieDestroy();
244 die("To turn on support for multisite setup, need to edit this script and change \$allow_multisite_setup to true. After you are done setting up the cloning, ensure you change \$allow_multisite_setup back to false or remove this script altogether");
247 // Disable file and directory permissions check by setting to false
248 $checkPermissions = true;
250 global $OE_SITE_DIR; // The Installer sets this
252 $docsDirectory = "$OE_SITE_DIR/documents";
254 //These are files and dir checked before install for
255 // correct permissions.
256 if (is_dir($OE_SITE_DIR)) {
257 $writableFileList = array($installer->conffile);
258 $writableDirList = array($docsDirectory);
259 } else {
260 $writableFileList = array();
261 $writableDirList = array($OE_SITES_BASE);
264 // Include the sqlconf file if it exists yet.
265 $config = 0;
266 if (file_exists($installer->conffile)) {
267 require_once($installer->conffile);
268 } elseif ($state > 3) {
269 // State 3 should have created the site directory if it is missing.
270 SessionUtil::setupScriptSessionStart();
271 SessionUtil::setupScriptSessionCookieDestroy();
272 die("Internal error, site directory is missing.");
275 // Should never have $config set when state less than 4
276 // (this means already installed)
277 if (!empty($config) && (($state ?? 0) < 4)) {
278 SessionUtil::setupScriptSessionStart();
279 SessionUtil::setupScriptSessionCookieDestroy();
280 error_log("OpenEMR has already been installed. If you wish to force re-installation, then edit " . errorLogEscape($installer->conffile) . " (change the 'config' variable to 0), and re-run the setup.php script.");
281 die("OpenEMR has already been installed. If you wish to force re-installation, see log for details.<br />\n");
284 // This will effectively only allow entry into the setup.php script at the first step and will bar entry to the
285 // script if openemr has already been installed.
286 // 2 mechanisms are also in place to ensure go through script in chronological order
287 // - a session variable tracks the next state expected and kills script is not correct
288 // - only the next state csrf is handed out in the form
289 if (empty($state)) {
290 // ensure not already installed
291 if (!empty($config)) {
292 SessionUtil::setupScriptSessionStart();
293 SessionUtil::setupScriptSessionCookieDestroy();
294 error_log("OpenEMR has already been installed. If you wish to force re-installation, then edit " . errorLogEscape($installer->conffile) . " (change the 'config' variable to 0), and re-run the setup.php script.");
295 die("OpenEMR has already been installed. If you wish to force re-installation, see log for details.<br />\n");
297 // set up new blank session and csrf mechanism
298 SessionUtil::setupScriptSessionStart();
299 session_regenerate_id(true);
300 $_SESSION = [];
301 CsrfUtils::setupCsrfKey();
302 } else {
303 // start up session and check csrf
304 SessionUtil::setupScriptSessionStart();
305 $state = (int)$state;
306 $verifyCsrf = false;
307 if (($state > 0) && ($state < 8)) {
308 $verifyCsrf = CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"], "state" . $state);
309 } else {
310 SessionUtil::setupScriptSessionCookieDestroy();
311 die("Not authorized (invalid state)");
313 if (!$verifyCsrf) {
314 SessionUtil::setupScriptSessionCookieDestroy();
315 die("Not authorized (invalid csrf)");
318 // ensure correct state is going (ie. do not allow users to muck around with this)
319 if ($_SESSION['bootstrapStateInSetup'] != $state) {
320 SessionUtil::setupScriptSessionCookieDestroy();
321 die("Not authorized (incorrect state)");
324 // ensure that users are not mucking with database settings
325 if ($state > 3) {
326 if (
327 ($sqlconf["host"] != $installer->server) ||
328 ($sqlconf["port"] != $installer->port) ||
329 ($sqlconf["dbase"] != $installer->dbname) ||
330 ($sqlconf["login"] != $installer->login) ||
331 ($sqlconf["pass"] != $installer->pass)
333 SessionUtil::setupScriptSessionCookieDestroy();
334 die("Not authorized (database)");
338 // state 7 is last state so might as well kill the session now to ensure no funny business
339 if ($state == 7) {
340 SessionUtil::setupScriptSessionCookieDestroy();
345 <html>
346 <head>
347 <title>OpenEMR Setup Tool</title>
348 <!--<link rel=stylesheet href="interface/themes/style_blue.css">-->
349 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css">
350 <script src="public/assets/jquery/dist/jquery.min.js"></script>
351 <script src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
352 <link rel="stylesheet" href="public/assets/@fortawesome/fontawesome-free/css/all.min.css">
353 <link rel="shortcut icon" href="public/images/favicon.ico" />
355 <style>
356 .noclone { }
357 table.phpset {
358 border-collapse:collapse;
360 table.phpset td, table.phpset th {
361 font-size:9pt;
362 border:1px solid gray;
363 padding:2px;
365 .table.no-border tr td, .table.no-border tr th {
366 border-width: 0;
368 td {
369 font-size:10pt;
371 .inputtext {
372 padding-left:2px;
373 padding-right:2px;
376 .button {
377 font-family:sans-serif;
378 font-size:9pt;
379 font-weight:bold;
382 .label-div > a {
383 display:none;
385 .label-div:hover > a {
386 display:inline-block;
388 div[id$="_info"] {
389 background: #F7FAB3;
390 padding: 20px;
391 margin: 10px 15px 0px 15px;
393 div[id$="_info"] > a {
394 margin-left:10px;
396 .checkboxgroup {
397 display: inline-block;
398 text-align: center;
400 .checkboxgroup label {
401 display: block;
403 .oe-pull-away{
404 float:right;
406 .oe-help-x {
407 color: grey;
408 padding: 0 5px;
410 .oe-superscript {
411 position: relative;
412 top: -.5em;
413 font-size: 70%!important;
415 .oe-setup-legend{
416 background-color: #f5f5f5;
417 padding:0 10px;
419 .button-wait {
420 color: grey;
421 cursor: not-allowed;
422 opacity: 0.6;
424 @media only screen {
425 fieldset > [class*="col-"] {
426 width: 100%;
427 text-align:left!Important;
430 </style>
431 <script>
432 // onclick handler for "clone database" checkbox
433 function cloneClicked() {
434 var cb = document.forms[0].clone_database;
435 $('.noclone').css('display', cb.checked ? 'none' : 'block');
437 </script>
439 </head>
440 <body>
441 <nav class="navbar navbar-expand navbar-light bg-light">
442 <div class="container">
443 <a class="navbar-brand" href="#">OpenEMR Setup</a>
444 <div class="collapse navbar-collapse justify-content-end">
445 <ul class="navbar-nav">
446 <li class="nav-item active">
447 <a class="nav-link" href="#" data-target="#myModal" data-toggle="modal" href="#" id="help-href" name="help-href" title="Click to view Help">Help</span></a>
448 </li>
449 </ul>
450 </div>
451 </div>
452 </nav>
453 <div class='mt-3 container'>
454 <div class="row">
455 <div class="col-12">
456 <?php
457 $error = "<span class='text-danger font-weight-bold'>ERROR</span>";
458 $caution = "<span class='text-danger font-weight-bold'>CAUTION</span>";
459 $ok = "<span class='text-success font-weight-bold'>OK</span>";
460 $note = "<span class='text-primary font-weight-bold'>NOTE</span>";
462 if (strtolower(ini_get('register_globals')) != 'off' && (bool) ini_get('register_globals')) {
463 echo "$caution: It appears that you have register_globals enabled in your php.ini\n" .
464 "configuration file. This causes unacceptable security risks. You must\n" .
465 "turn it off before continuing with installation.\n";
466 exit(1);
469 if (!extension_loaded("xml")) {
470 echo "$error: PHP XML extension missing. To continue, install PHP XML extension, then restart web server.";
471 exit(1);
474 if (!(extension_loaded("mysql") || extension_loaded("mysqlnd") || extension_loaded("mysqli"))) {
475 echo "$error: PHP MySQL extension missing. To continue, install and enable MySQL extension, then restart web server.";
476 exit(1);
479 if (!(extension_loaded("mbstring") )) {
480 echo "$error: PHP mb_string extension missing. To continue, install and enable mb_string extension, then restart web server.";
481 exit(1);
484 if (!(extension_loaded("openssl") )) {
485 echo "$error: PHP openssl extension missing. To continue, install PHP openssl extension, then restart web server.";
486 exit(1);
490 <?php
491 if ($state == 7) {
493 <h3 class="mb-3 border-bottom">Final step - Success</h3>
494 <div class="jumbotron p-5">
495 <p>Congratulations! OpenEMR is now installed.</p>
496 <ul>
497 <li>Access controls (php-GACL) are installed for fine-grained security, and can be administered in
498 OpenEMR's admin->acl menu.</li>
499 <li>Reviewing <code> <?php echo text($OE_SITE_DIR); ?>/config.php </code> is a good idea. This file
500 contains some settings that you may want to change.</li>
501 <li>There's much information and many extra tools bundled within the OpenEMR installation directory.
502 Please refer to openemr/Documentation. Many forms and other useful scripts can be found at openemr/contrib.</li>
503 <li>To ensure a consistent look and feel throughout the application,
504 <a href='http://www.mozilla.org/products/firefox/'>Firefox</a> and <a href="https://www.google.com/chrome/browser/desktop/index.html">Chrome</a> are recommended. The OpenEMR development team exclusively tests with modern versions of these browsers.</li>
505 <li>The OpenEMR project home page, documentation, and forums can be found at <a href = "https://www.open-emr.org" rel='noopener' target="_blank">https://www.open-emr.org</a></li>
506 <li>We pursue grants to help fund the future development of OpenEMR. To apply for these grants, we need to estimate how many times this program is installed and how many practices are evaluating or using this software. It would be awesome if you would email us at <a href="mailto:hello@open-emr.org">hello@open-emr.org</a> if you have installed this software. The more details about your plans with this software, the better, but even just sending us an email stating you just installed it is very helpful.</li>
507 </ul>
508 <p>We recommend you print these instructions for future reference.</p>
509 <?php
510 echo "<p> The selected theme is :</p>";
511 $installer->displayNewThemeDiv();
512 if (empty($installer->clone_database)) {
513 echo "<p><b>The initial OpenEMR user is <span class='text-primary'>'" . text($installer->iuser) . "'</span> and the password is <span class='text-primary'>'" . text($installer->iuserpass) . "'</span></b></p>";
514 } else {
515 echo "<p>The initial OpenEMR user name and password is the same as that of source site <b>'" . text($installer->source_site_id) . "'</span></b></p>";
517 echo "<p>If you edited the PHP or Apache configuration files during this installation process, then we recommend you restart your Apache server before following below OpenEMR link.</p>";
518 echo "<p>In Linux use the following command:</p>";
519 echo "<p><code>sudo apachectl -k restart</code></p>";
522 <p>Click to start using OpenEMR.</p>
523 <div class="row">
524 <div class="col-12">
525 <a href='./?site=<?php echo attr_url($site_id); ?>' class='btn btn-primary'>
526 <i class="fas fa-chevron-right"></i> Start
527 </a>
528 </div>
529 </div>
531 </div>
532 <?php
533 $installer->setCurrentTheme();
535 $end_div = <<<ENDDIV
536 </div>
537 </div>
538 </div><!--end of container div-->
539 ENDDIV;
540 echo $end_div . "\r\n";
541 $installer->setupHelpModal();
542 echo "</body>" . "\r\n";
543 echo "</html>" . "\r\n";
545 exit();
549 <?php
551 $inst = isset($_POST["inst"]) ? ($_POST["inst"]) : '';
553 switch ($state) {
554 case 1:
555 $_SESSION['bootstrapStateInSetup'] = 2;
556 $state_esc = text($state);
557 $site_id_esc = attr($site_id);
558 $csrf_id_esc = attr(CsrfUtils::collectCsrfToken('state2'));
559 $step1 = <<<STP1
560 <h3 class="mb-3 border-bottom">Step $state_esc - Select Database Setup</h3>
561 <div class="jumbotron p-5">
562 <p>Now I need to know whether you want me to create the database on my own or if you have already created the database for me to use. For me to create the database, you will need to supply the MySQL root password.</p>
563 <br />
564 <p class='p-1 bg-warning'>$caution: clicking on <span class="font-weight-bold">Proceed to Step 2</span> may delete or cause damage to existing data on your system. Before you continue <span class="font-weight-bold">please backup your data</span>.</p>
565 <br />
566 <form method='post'>
567 <input name='state' type='hidden' value='2' />
568 <input name='site' type='hidden' value='$site_id_esc' />
569 <input name='csrf_token_form' type='hidden' value='$csrf_id_esc' />
570 <div class="form-check">
571 <input checked class='form-check-input' id='inst1' name='inst' type='radio' value='1' />
572 <label class="form-check-label" for="inst1">
573 Have setup create the database
574 </label>
575 </div>
576 <br />
577 <div class="form-check">
578 <input id='inst2' class='form-check-input' name='inst' type='radio' value='2' />
579 <label class="form-check-label" for="inst2">
580 I have already created the database
581 </label>
582 </div>
583 <div class="form-group">
584 <div class="col mt-3">
585 <button type='submit' class='btn btn-primary' value='Continue'>
586 <i class="fas fa-chevron-right"></i> Proceed to Step 2
587 </button>
588 </div>
589 </div>
590 </form>
591 <br />
592 </div>
593 STP1;
594 echo $step1 . "\r\n";
595 break;
597 case 2:
598 $_SESSION['bootstrapStateInSetup'] = 3;
599 $state_esc = text($state);
600 $site_id_esc = attr($site_id);
601 $inst_esc = attr($inst);
602 $csrf_id_esc = attr(CsrfUtils::collectCsrfToken('state3'));
603 $step2top = <<<STP2TOP
604 <h3 class="mb-3 border-bottom">Step $state_esc - Database and OpenEMR Initial User Setup Details</h3>
605 <div class="jumbotron p-5">
606 <p>Now you need to supply the MySQL server information and path information. Detailed instructions on each item can be found in the
607 <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u>
608 </a> manual file.
609 </p>
610 <form method='post' id='myform'>
611 <input name='state' type='hidden' value='3' />
612 <input name='site' type='hidden' value='$site_id_esc' />
613 <input name='inst' type='hidden' value='$inst_esc' />
614 <input name='csrf_token_form' type='hidden' value='$csrf_id_esc' />
615 STP2TOP;
616 echo $step2top . "\r\n";
619 $step2tabletop1 = <<<STP2TBLTOP1
620 <fieldset>
621 <legend name="form_legend" id="form_legend" class='oe-setup-legend'>MySQL Server Details<i id="enter-details-tooltip" class="fa fa-info-circle oe-text-black oe-superscript enter-details-tooltip" aria-hidden="true"></i></legend>
622 <div class="ml-2 row">
623 <div class="col-sm-4">
624 <div class="clearfix form-group">
625 <div class="label-div">
626 <label class="font-weight-bold" for="server">Server Host:</label>
627 <a href="#server_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
628 </div>
629 <div>
630 <input name='server' id='server' type='text' class='form-control' value='localhost' />
631 </div>
632 </div>
633 <div id="server_info" class="collapse">
634 <a href="#server_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
635 <p>If you run MySQL and Apache/PHP on the same computer, then leave this as 'localhost'.</p>
636 <p>If they are on separate computers, then enter the IP address of the computer running MySQL.</p>
637 </div>
638 </div>
639 <div class="col-sm-4">
640 <div class="clearfix form-group">
641 <div class="label-div">
642 <label class="font-weight-bold" for="port">Server Port:</label>
643 <a href="#port_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
644 </div>
645 <div>
646 <input name='port' id='port' type='text' class='form-control' value='3306' />
647 </div>
648 </div>
649 <div id="port_info" class="collapse">
650 <a href="#port_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
651 <p>This is the MySQL port.</p>
652 <p>The default port for MySQL is 3306.</p>
653 </div>
654 </div>
655 <div class="col-sm-4">
656 <div class="clearfix form-group">
657 <div class="label-div">
658 <label class="font-weight-bold" for="dbname">Database Name:</label> <a href="#dbname_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
659 </div>
660 <div>
661 <input name='dbname' id='dbname' type='text' class='form-control' value='openemr' />
662 </div>
663 </div>
664 <div id="dbname_info" class="collapse">
665 <a href="#dbname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
666 <p>This will be the name of the OpenEMR database in MySQL.</p>
667 <p>'openemr' is the recommended name.</p>
668 <p>This database will contain patient data as well as data pertaining to the OpenEMR installation.</p>
669 </div>
670 </div>
671 </div>
672 <div class="ml-2 row">
673 <div class="col-sm-4">
674 <div class="clearfix form-group">
675 <div class="label-div">
676 <label class="font-weight-bold" for="login">Login Name:</label> <a href="#login_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
677 </div>
678 <div>
679 <input name='login' ID='login' type='text' class='form-control' value='openemr' />
680 </div>
681 </div>
682 <div id="login_info" class="collapse">
683 <a href="#login_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
684 <p>This is the name that OpenEMR will use to login to the MySQL database.</p>
685 <p>'openemr' is the recommended name.</p>
686 </div>
687 </div>
688 <div class="col-sm-4">
689 <div class="clearfix form-group">
690 <div class="label-div">
691 <label class="font-weight-bold" for="pass">Password:</label>
692 <a href="#pass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
693 </div>
694 <div>
695 <input name='pass' id='pass' class='form-control' type='password' value='' required />
696 </div>
697 </div>
698 <div id="pass_info" class="collapse">
699 <a href="#pass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
700 <p>This is the Login Password that OpenEMR will use to accesses the MySQL database.</p>
701 <p>It should be at least 12 characters long and composed of both numbers and letters.</p>
702 <p>It cannot contain any of these characters: \ ; ( ) < > / ' "</p>
703 </div>
704 </div>
705 STP2TBLTOP1;
706 echo $step2tabletop1 . "\r\n";
707 if ($inst != 2) {
708 $step2tabletop2 = <<<STP2TBLTOP2
709 <div class="col-sm-4">
710 <div class="clearfix form-group">
711 <div class="label-div">
712 <label class="font-weight-bold" for="root">Name for Root Account:</label>
713 <a href="#root_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
714 </div>
715 <div>
716 <input name='root' id='root' type='text' class='form-control' value='root' />
717 </div>
718 </div>
719 <div id="root_info" class="collapse">
720 <a href="#root_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
721 <p>This is name for the MySQL root account.</p>
722 <p>For localhost, it is usually ok to leave it as 'root'.</p>
723 </div>
724 </div>
725 </div>
726 <div class="ml-2 row">
727 <div class="col-sm-4">
728 <div class="clearfix form-group">
729 <div class="label-div">
730 <label class="font-weight-bold" for="rootpass">Root Password:</label>
731 <a href="#rootpass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
732 </div>
733 <div>
734 <input name='rootpass' id='rootpass' type='password' class='form-control' value='' />
735 </div>
736 </div>
737 <div id="rootpass_info" class="collapse">
738 <a href="#rootpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
739 <p>This is your MySQL server root password.</p>
740 </div>
741 </div>
742 <div class="col-sm-4">
743 <div class="clearfix form-group">
744 <div class="label-div">
745 <label class="font-weight-bold" for="loginhost">User Hostname:</label>
746 <a href="#loginhost_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
747 </div>
748 <div>
749 <input name='loginhost' id='loginhost' type='text' class='form-control' value='localhost' />
750 </div>
751 </div>
752 <div id="loginhost_info" class="collapse">
753 <a href="#loginhost_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
754 <p>If you run Apache/PHP and MySQL on the same computer, then leave this as 'localhost'.</p>
755 <p>If they are on separate computers, then enter the IP address of the computer running Apache/PHP.</p>
756 </div>
757 </div>
758 <div class="col-sm-4">
759 <div class="clearfix form-group">
760 <div class="label-div">
761 <label class="font-weight-bold" for="collate">UTF-8 Collation:</label> <a href="#collate_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
762 </div>
763 <div>
764 <select name='collate' id=='collate' class='form-control'>
765 <option selected value='utf8mb4_general_ci'>
766 General (Recommended)
767 </option>
768 <option value='utf8mb4_unicode_ci'>
769 Unicode
770 </option>
771 <option value='utf8mb4_roman_ci'>
772 Classical Latin
773 </option>
774 <option value='utf8mb4_croatian_ci'>
775 Croatian
776 </option>
777 <option value='utf8mb4_czech_ci'>
778 Czech
779 </option>
780 <option value='utf8mb4_danish_ci'>
781 Danish
782 </option>
783 <option value='utf8mb4_esperanto_ci'>
784 Esperanto
785 </option>
786 <option value='utf8mb4_estonian_ci'>
787 Estonian
788 </option>
789 <option value='utf8mb4_german2_ci'>
790 German
791 </option>
792 <option value='utf8mb4_hungarian_ci'>
793 Hungarian
794 </option>
795 <option value='utf8mb4_icelandic_ci'>
796 Icelandic
797 </option>
798 <option value='utf8mb4_latvian_ci'>
799 Latvian
800 </option>
801 <option value='utf8mb4_lithuanian_ci'>
802 Lithuanian
803 </option>
804 <option value='utf8mb4_persian_ci'>
805 Persian
806 </option>
807 <option value='utf8mb4_polish_ci'>
808 Polish
809 </option>
810 <option value='utf8mb4_romanian_ci'>
811 Romanian
812 </option>
813 <option value='utf8mb4_sinhala_ci'>
814 Sinhala
815 </option>
816 <option value='utf8mb4_slovak_ci'>
817 Slovak
818 </option>
819 <option value='utf8mb4_slovenian_ci'>
820 Slovenian
821 </option>
822 <option value='utf8mb4_spanish_ci'>
823 Spanish (Modern)
824 </option>
825 <option value='utf8mb4_spanish2_ci'>
826 Spanish (Traditional)
827 </option>
828 <option value='utf8mb4_swedish_ci'>
829 Swedish
830 </option>
831 <option value='utf8mb4_turkish_ci'>
832 Turkish
833 </option>
834 <option value='utf8mb4_vietnamese_ci'>
835 Vietnamese
836 </option>
837 </select>
838 </div>
839 </div>
840 <div id="collate_info" class="collapse">
841 <a href="#collate_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
842 <p>This is the collation setting for MySQL.</p>
843 <p>Collation refers to a set of rules that determine how data is sorted and compared in a database.</p>
844 <p>Leave as 'General' if you are not sure.</p>
845 <p>If the language you are planning to use in OpenEMR is in the menu, then you can select it.</p>
846 <p>Otherwise, just select 'General'.</p>
847 </div>
848 </div>
849 </div>
850 STP2TBLTOP2;
851 echo $step2tabletop2 . "\r\n";
853 // Include a "source" site ID drop-list and a checkbox to indicate
854 // if cloning its database. When checked, do not display initial user
855 // and group stuff below.
856 $dh = opendir($OE_SITES_BASE);
857 if (!$dh) {
858 die("Cannot read directory '" . text($OE_SITES_BASE) . "'.");
861 $siteslist = array();
862 while (false !== ($sfname = readdir($dh))) {
863 if (substr($sfname, 0, 1) == '.') {
864 continue;
867 if ($sfname == 'CVS') {
868 continue;
871 if ($sfname == $site_id) {
872 continue;
875 $sitedir = "$OE_SITES_BASE/$sfname";
876 if (!is_dir($sitedir)) {
877 continue;
880 if (!is_file("$sitedir/sqlconf.php")) {
881 continue;
884 $siteslist[$sfname] = $sfname;
887 closedir($dh);
888 // If this is not the first site...
889 if (!empty($siteslist)) {
890 ksort($siteslist);
891 $source_site_top = <<<SOURCESITETOP
892 <div class="ml-2 row">
893 <div class="col-sm-4">
894 <div class="clearfix form-group">
895 <div class="label-div">
896 <label class="font-weight-bold" for="source_site_id">Source Site:</label>
897 <a href="#source_site_id_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
898 </div>
899 <div>
900 <select name='source_site_id'id='source_site_id' class='form-control'>
901 SOURCESITETOP;
902 echo $source_site_top . "\r\n";
903 foreach ($siteslist as $sfname) {
904 echo "<option value='" . attr($sfname) . "'";
905 if ($sfname == 'default') {
906 echo " selected";
909 echo ">" . text($sfname) . "</option>";
911 $source_site_bot = <<<SOURCESITEBOT
912 </select>
914 </div>
915 </div>
916 <div id="source_site_id_info" class="collapse">
917 <a href="#source_site_id_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
918 <p>The site directory that will be a model for the new site.</p>
919 </div>
920 </div>
921 <div class="col-sm-4">
922 <div class="clearfix form-group">
923 <div class="label-div">
924 <label class="font-weight-bold" for="clone_database">Clone Source Database:</label>
925 <a href="#clone_database_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
926 </div>
927 <div>
928 <input type='checkbox' name='clone_database' id='clone_database' onclick='cloneClicked()' />
929 </div>
930 </div>
931 <div id="clone_database_info" class="collapse">
932 <a href="#clone_database_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
933 <p>Clone the source site's database instead of creating a fresh one.</p>
934 </div>
935 </div>
936 </div>
937 SOURCESITEBOT;
938 echo $source_site_bot . "\r\n";
941 $randomusernamepre = RandomGenUtils::produceRandomString(3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
942 $randomusernamepost = RandomGenUtils::produceRandomString(2, "0123456789");
943 $randomusername = $randomusernamepre . "-admin-" . $randomusernamepost;
945 // App Based TOTP secret
946 // Shared key (per rfc6238 and rfc4226) should be 20 bytes (160 bits) and encoded in base32, which should
947 // be 32 characters in base32
948 // Would be nice to use the OpenEMR\Common\Utils\RandomGenUtils\produceRandomBytes() function and then encode to base32,
949 // but does not appear to be a standard way to encode binary to base32 in php.
950 $randomsecret = RandomGenUtils::produceRandomString(32, "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ");
951 if (empty($randomsecret) || empty($randomusernamepre) || empty($randomusernamepost)) {
952 error_log('OpenEMR Error : Random String error - exiting');
953 die();
955 $disableCheckbox = "";
956 if (empty($randomsecret)) {
957 $randomsecret = "";
958 $disableCheckbox = "disabled";
961 $randomsecret_esc = attr($randomsecret);
962 $randomusername_esc = attr($randomusername);
963 $step2tablebot = <<<STP2TBLBOT
964 </fieldset>
965 <br />
966 <fieldset class='noclone'>
967 <legend name="form_legend" id="form_legend" class='oe-setup-legend'>OpenEMR Initial User Details<i id="enter-details-tooltip" class="fa fa-info-circle oe-text-black oe-superscript enter-details-tooltip" aria-hidden="true"></i></legend>
968 <div class="ml-2 row">
969 <div class="col-sm-4">
970 <div class="clearfix form-group">
971 <div class="label-div">
972 <label class="font-weight-bold" for="iuser">Initial User Login Name:</label> <a href="#iuser_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
973 </div>
974 <div>
975 <input name='iuser' id='iuser' type='text' class='form-control' value='$randomusername_esc' minlength='12' />
976 </div>
977 </div>
978 <div id="iuser_info" class="collapse">
979 <a href="#iuser_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
980 <p>This is the login name of the first user that will be created for you.</p>
981 <p>Limit this to one word with at least 12 characters and composed of both numbers and letters.</p>
982 </div>
983 </div>
984 <div class="col-sm-4">
985 <div class="clearfix form-group">
986 <div class="label-div">
987 <label class="font-weight-bold" for="iuserpass">Initial User Password:</label>
988 <a href="#iuserpass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
989 </div>
990 <div>
991 <input name='iuserpass' id='iuserpass' type='password' class='form-control' value='' minlength='12' />
992 </div>
993 </div>
994 <div id="iuserpass_info" class="collapse">
995 <a href="#iuserpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
996 <p>This is the password for the initial user.
997 </div>
998 </div>
999 <div class="col-sm-4">
1000 <div class="clearfix form-group">
1001 <div class="label-div">
1002 <label class="font-weight-bold" for="iufname">Initial User's First Name:</label>
1003 <a href="#iufname_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1004 </div>
1005 <div>
1006 <input name='iufname' id='iufname 'type='text' class='form-control' value='Administrator' />
1007 </div>
1008 </div>
1009 <div id="iufname_info" class="collapse">
1010 <a href="#iufname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
1011 <p>This is the First name of the 'initial user'.</p>
1012 </div>
1013 </div>
1014 </div>
1015 <div class="ml-2 row">
1016 <div class="col-sm-4">
1017 <div class="clearfix form-group">
1018 <div class="label-div">
1019 <label class="font-weight-bold" for="iuname">Initial User's Last Name:</label>
1020 <a href="#iuname_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1021 </div>
1022 <div>
1023 <input name='iuname' id='iuname' type='text' class='form-control' value='Administrator' />
1024 </div>
1025 </div>
1026 <div id="iuname_info" class="collapse">
1027 <a href="#iuname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
1028 <p>This is the Last name of the 'initial user'.</p>
1029 </div>
1030 </div>
1031 <div class="col-sm-4">
1032 <div class="clearfix form-group">
1033 <div class="label-div">
1034 <label class="font-weight-bold" for="igroup">Initial Group:</label>
1035 <a href="#igroup_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1036 </div>
1037 <div>
1038 <input name='igroup' id='igroup' class='form-control' type='text' value='Default' />
1039 </div>
1040 </div>
1041 <div id="igroup_info" class="collapse">
1042 <a href="#igroup_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
1043 <p>This is the group that will be created for your users.</p>
1044 <p>This should be the name of your practice.</p>
1045 </div>
1046 </div>
1047 </div>
1048 </fieldset>
1049 <br />
1050 <fieldset class='noclone py-2 bg-warning'>
1051 <legend name="form_legend" id="form_legend" class='oe-setup-legend text-danger'>Enable 2 Factor Authentication for Initial User (more secure - optional) <i id="2fa-section" class="fa fa-info-circle oe-text-black oe-superscript 2fa-section-tooltip" aria-hidden="true"></i></legend>
1052 <div class="ml-2 row">
1053 <div class="col-sm-3">
1054 <div class="clearfix form-group">
1055 <div class="label-div">
1056 <label class="font-weight-bold" for="i2fa">Configure 2FA:</label>
1057 <a href="#i2fa_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1058 </div>
1059 <div>
1060 <input name='i2faenable' id='i2faenable' type='checkbox' $disableCheckbox/> Enable 2FA
1061 <input type='hidden' name='i2fasecret' id='i2fasecret' value='$randomsecret_esc' />
1062 </div>
1063 </div>
1064 <div id="i2fa_info" class="collapse">
1065 <a href="#i2fa_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
1066 <p>If selected will allow TOTP 2 factor authentication for the initial user.</p>
1067 <p>Click on the help file for more information.</p>
1068 </div>
1069 </div>
1070 <div class="col-sm-5">
1071 <div class="clearfix form-group">
1072 <p class="text-danger font-weight-bold">IMPORTANT IF ENABLED</p>
1073 <p>If enabled, you must have an authenticator app on your phone ready to scan the QR code displayed next.</p>
1074 </div>
1075 </div>
1076 <div class="col-sm-4">
1077 <div class="clearfix form-group">
1078 <p>Example authenticator apps include:</p>
1079 <ul>
1080 <li>Google Auth
1081 (<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8" target="_blank">iOS</a>, <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en">Android</a>)</li>
1082 <li>Authy
1083 (<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8">iOS</a>, <a href="https://play.google.com/store/apps/details?id=com.authy.authy&amp;hl=en">Android</a>)</li>
1084 </ul>
1085 </div>
1086 </div>
1087 </div>
1088 </fieldset>
1089 <p class='mt-4 mark'>Click the <b>Create DB and User</b> button below to create the database and first user <a href='#create_db_button' title='Click me'><i class="fa fa-arrow-circle-down" aria-hidden="true"></i></a>. $note: This process will take a few minutes.</p>
1090 <p class='p-1 bg-success text-white oe-spinner' style='visibility:hidden;'>Upon successful completion will automatically take you to the next step.<i class='fa fa-spinner fa-pulse fa-fw'></i></p>
1091 <div class="form-row">
1092 <div class="col-12">
1093 <button type='submit' id='create_db_button' value='Continue' class='wait btn btn-primary'>
1094 <i class="fas fa-chevron-right"></i> Create DB and User
1095 </button>
1096 </div>
1097 </div>
1098 </form>
1099 </div>
1100 STP2TBLBOT;
1101 echo $step2tablebot . "\r\n";
1102 break;
1104 case 3:
1105 // Form Validation
1106 // (applicable if not cloning from another database)
1108 $pass_step2_validation = true;
1109 $error_step2_message = "";
1111 if (! $installer->char_is_valid($_REQUEST['server'])) {
1112 $pass_step2_validation = false;
1113 $error_step2_message .= "$error - A database server host is required <br />\n";
1116 if (! $installer->char_is_valid($_REQUEST['port'])) {
1117 $pass_step2_validation = false;
1118 $error_step2_message .= "$error - A database server port value is required <br />\n";
1121 if (! $installer->databaseNameIsValid($_REQUEST['dbname'])) {
1122 $pass_step2_validation = false;
1123 $error_step2_message .= "$error - A database name is required <br />\n";
1126 if (! $installer->collateNameIsValid($_REQUEST['collate'])) {
1127 $pass_step2_validation = false;
1128 $error_step2_message .= "$error - A collation name is required <br />\n";
1131 if (! $installer->char_is_valid($_REQUEST['login'])) {
1132 $pass_step2_validation = false;
1133 $error_step2_message .= "$error - A database login name is required <br />\n";
1136 if (! $installer->char_is_valid($_REQUEST['pass'])) {
1137 $pass_step2_validation = false;
1138 $error_step2_message .= "$error - A database login password is required <br />\n";
1141 if (empty($installer->clone_database)) {
1142 if (! $installer->login_is_valid()) {
1143 $pass_step2_validation = false;
1144 $error_step2_message .= "$error - Please pick a proper 'Login Name'.<br />\n";
1147 if (! $installer->iuser_is_valid()) {
1148 $pass_step2_validation = false;
1149 $error_step2_message .= "$error - The 'Initial User' field can only contain one word and no spaces.<br />\n";
1152 if (! $installer->user_password_is_valid()) {
1153 $pass_step2_validation = false;
1154 $error_step2_message .= "$error - Please pick a proper 'Initial User Password'.<br />\n";
1158 if (! $installer->password_is_valid()) {
1159 $pass_step2_validation = false;
1160 $error_step2_message .= "$error - Please pick a proper 'Password'.<br />\n";
1163 if (! $installer->iuname_is_valid()) {
1164 $pass_step2_validation = false;
1165 $error_step2_message .= "$error - Please pick a proper 'Initial User Last Name'.<br />\n";
1168 if (!$pass_step2_validation) {
1169 $_SESSION['bootstrapStateInSetup'] = 2;
1170 echo $error_step2_message . '<br />';
1171 echo "
1172 <form method='post' id='validate-error'>
1173 <input name='state' type='hidden' value='2' />
1174 <input name='site' type='hidden' value='" . attr($site_id) . "' />
1175 <input name='inst' type='hidden' value='" . attr($inst) . "' />
1176 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state2')) . "' />
1177 <button type='submit' class='btn btn-primary'><i class='fas fa-chevron-left'></i> " . text("Back") . "</button>
1178 </form>";
1179 break;
1182 echo "<h3 class='mb-3 border-bottom'>Step " . text($state) . " - Creating Database and First User</h3>";
1183 echo "<div class='jumbotron p-5'>";
1185 // Skip below if database shell has already been created.
1186 if ($inst != 2) {
1187 echo "Connecting to MySQL Server...\n";
1188 flush();
1189 if (! $installer->root_database_connection()) {
1190 $_SESSION['bootstrapStateInSetup'] = 2;
1191 echo "$error. Check your login credentials.\n";
1192 echo text($installer->error_message);
1193 echo "<br /><br />";
1194 echo "
1195 <form method='post' id='validate-error'>
1196 <input name='state' type='hidden' value='2' />
1197 <input name='site' type='hidden' value='" . attr($site_id) . "' />
1198 <input name='inst' type='hidden' value='" . attr($inst) . "' />
1199 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state2')) . "' />
1200 <button type='submit' class='btn btn-primary'><i class='fas fa-chevron-left'></i> " . text("Back") . "</button>
1201 </form>";
1202 break;
1203 } else {
1204 echo "$ok.<br />\n";
1205 flush();
1209 // Only pertinent if cloning another installation database
1210 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1211 echo "Dumping source database...";
1212 flush();
1213 if (! $installer->create_dumpfiles()) {
1214 echo text($installer->error_message);
1215 break;
1216 } else {
1217 echo "$ok.<br />\n";
1218 flush();
1222 // Only pertinent if mirroring another installation directory
1223 if (! empty($installer->source_site_id)) {
1224 echo "Creating site directory...";
1225 if (! $installer->create_site_directory()) {
1226 echo text($installer->error_message);
1227 break;
1228 } else {
1229 echo "$ok.<br />";
1230 flush();
1234 // Skip below if database shell has already been created.
1235 if ($inst != 2) {
1236 echo "Creating database...\n";
1237 flush();
1238 if (! $installer->create_database()) {
1239 echo "$error. Check your login credentials.\n";
1240 echo text($installer->error_message);
1241 break;
1242 } else {
1243 echo "$ok.<br />\n";
1244 flush();
1247 echo "Creating user with permissions for database...\n";
1248 flush();
1249 $user_mysql_error = true;
1250 if (! $installer->create_database_user()) {
1251 echo "$error when creating specified user.\n";
1252 echo text($installer->error_message);
1253 break;
1254 } else {
1255 $user_mysql_error = false;
1257 if (! $installer->grant_privileges()) {
1258 echo "$error when granting privileges to the specified user.\n";
1259 echo text($installer->error_message);
1260 break;
1261 } else {
1262 $user_mysql_error = false;
1264 if (!$user_mysql_error) {
1265 echo "$ok.<br />\n";
1266 flush();
1269 echo "Reconnecting as new user...\n";
1270 flush();
1271 $installer->disconnect();
1272 } else {
1273 echo "Connecting to MySQL Server...\n";
1276 if (! $installer->user_database_connection()) {
1277 echo "$error. Check your login credentials.\n";
1278 echo text($installer->error_message);
1279 echo "<br />You will need to remove the database, correct the problem, and then restart the setup.php script.<br /><br />";
1280 break;
1281 } else {
1282 echo "$ok.<br />\n";
1283 flush();
1286 // Load the database files
1287 $dump_results = $installer->load_dumpfiles();
1288 if (! $dump_results) {
1289 echo "$error.\n";
1290 echo text($installer->error_message);
1291 break;
1292 } else {
1293 echo $dump_results;
1294 flush();
1297 echo "Writing SQL configuration...\n";
1298 flush();
1299 if (! $installer->write_configuration_file()) {
1300 echo "$error.\n";
1301 echo text($installer->error_message);
1302 break;
1303 } else {
1304 echo "$ok.<br />\n";
1305 flush();
1308 // Only pertinent if not cloning another installation database
1309 if (empty($installer->clone_database)) {
1310 echo "Setting version indicators...\n";
1311 flush();
1312 if (! $installer->add_version_info()) {
1313 echo "$error.\n";
1314 echo text($installer->error_message);
1316 break;
1317 } else {
1318 echo "$ok<br />\n";
1319 flush();
1322 echo "Writing global configuration defaults...\n";
1323 flush();
1324 if (! $installer->insert_globals()) {
1325 echo "$error.\n";
1326 echo text($installer->error_message);
1328 break;
1329 } else {
1330 echo "$ok<br />\n";
1331 flush();
1334 echo "Setting up Access Controls...\n";
1335 require("$OE_SITE_DIR/sqlconf.php");
1336 if (! $installer->install_gacl()) {
1337 echo "$error -.\n";
1338 echo text($installer->error_message);
1339 break;
1340 } else {
1341 echo "$ok<br />\n";
1342 flush();
1346 echo "Adding Initial User...\n";
1347 flush();
1348 if (! $installer->add_initial_user()) {
1349 echo "$error.\n";
1350 echo text($installer->error_message);
1351 break;
1353 echo "$ok<br />\n";
1354 flush();
1356 echo "Adding Additional Users...\n";
1357 flush();
1358 if (! $installer->install_additional_users()) {
1359 echo "$error.\n";
1360 echo text($installer->error_message);
1361 break;
1363 echo "$ok<br />\n";
1364 flush();
1366 echo "Configuring Care Coordination Module...\n";
1367 flush();
1368 if (! $installer->on_care_coordination()) {
1369 echo "$error.\n";
1370 echo text($installer->error_message);
1371 break;
1373 echo "$ok<br />\n";
1374 flush();
1377 // If user has selected to set MFA App Based 2FA, display QR code to scan
1378 $mfa = $installer->get_initial_user_mfa_totp();
1379 if ($mfa !== false) {
1380 $qr = $mfa->generateQrCode();
1381 $qr_esc = attr($qr);
1382 $sharedSecret = text($mfa->getSecret());
1383 $qrDisplay = <<<TOTP
1384 <br />
1385 <table>
1386 <tr>
1387 <td>
1388 <strong class='text-danger'>IMPORTANT!!</strong>
1389 <p><strong>You must scan the following QR code with your preferred authenticator app.</strong></p>
1390 <img src='$qr_esc' width="150" />
1391 <p>Or paste in the following code into your authenticator app</p>
1392 <p>$sharedSecret</p>
1393 </td>
1394 </tr>
1395 <tr>
1396 <td>
1397 Example authenticator apps include:
1398 <ul>
1399 <li>Google Auth
1400 (<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>, <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">Android</a>)</li>
1401 <li>Authy
1402 (<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8">iOS</a>, <a href="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en">Android</a>)</li>
1403 </ul>
1404 </td>
1405 </tr>
1406 </table>
1407 TOTP;
1408 echo $qrDisplay;
1411 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1412 // Database was cloned, skip ACL setup.
1413 $btn_text = 'Proceed to Select a Theme';
1414 echo "<br />";
1415 echo "<p>The database was cloned, access control list exists therefore skipping ACL setup</p>";
1416 echo "<p class='p-1 bg-warning'>Click <b>" . text($btn_text) . "</b> for further instructions.</p>";
1417 $next_state = 7;
1418 } else {
1419 $btn_text = 'Proceed to Step 4';
1420 echo "<br />";
1421 echo "<p><b>Granted user <span class='text-primary'>" . text($installer->iuser) . "</span> administrator access control (password is <span class='text-primary'>" . text($installer->iuserpass) . "</span>).</b></p>";
1422 echo "<p>The next step will configure php.</p>";
1423 echo "<p class='mark'>Click <strong>" . text($btn_text) . "</strong> to continue.</p>";
1424 $next_state = 4;
1427 $_SESSION['bootstrapStateInSetup'] = $next_state;
1428 echo "<form method='post'>
1429 <input name='state' type='hidden' value='" . attr($next_state) . "' />
1430 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state' . $next_state)) . "' />
1431 <input name='site' type='hidden' value='" . attr($site_id) . "' />
1432 <input name='iuser' type='hidden' value='" . attr($installer->iuser) . "' />
1433 <input name='iuserpass' type='hidden' value='" . attr($installer->iuserpass) . "' />
1434 <input name='iuname' type='hidden' value='" . attr($installer->iuname) . "' />
1435 <input name='iufname' type='hidden' value='" . attr($installer->iufname) . "' />
1436 <input name='login' type='hidden' value='" . attr($installer->login) . "' />
1437 <input name='pass' type='hidden' value='" . attr($installer->pass) . "' />
1438 <input name='server' type='hidden' value='" . attr($installer->server) . "' />
1439 <input name='port' type='hidden' value='" . attr($installer->port) . "' />
1440 <input name='loginhost' type='hidden' value='" . attr($installer->loginhost) . "' />
1441 <input name='dbname' type='hidden' value='" . attr($installer->dbname) . "' />\r\n";
1443 if ($allow_cloning_setup) {
1444 echo "<input type='hidden' name='clone_database' value='" . attr($installer->clone_database) . "' />";
1445 echo "<input name='source_site_id' type='hidden' value='" . attr($installer->source_site_id) . "' />";
1447 $btn_text_esc = text($btn_text);
1448 $form_bottom = <<<FRMBOT
1449 <div class="form-row">
1450 <div class="col-12">
1451 <button type='submit' id='step-4-btn' class="btn btn-primary" value='Continue'>
1452 <i class="fas fa-chevron-right"></i> $btn_text_esc
1453 </button>
1454 </div>
1455 </div>
1456 </form>
1457 </div>
1458 FRMBOT;
1459 echo $form_bottom . "\r\n";
1460 break;
1462 case 4:
1463 $_SESSION['bootstrapStateInSetup'] = 5;
1464 $state_esc = text($state);
1465 $step4_top = <<<STP4TOP
1466 <h3 class="mb-3 border-bottom">Step $state_esc - Configure PHP</h3>
1467 <div class="jumbotron p-5">
1468 <p>Configuration of PHP...</p>
1469 <p>We recommend making the following changes to your PHP installation, which can normally be done by editing the php.ini configuration file:</p>
1470 <ul>
1471 STP4TOP;
1472 echo $step4_top . "\r\n";
1474 $gotFileFlag = 0;
1475 $phpINIfile = php_ini_loaded_file();
1476 if ($phpINIfile) {
1477 echo "<li><span class='text-success'>Your php.ini file can be found at " . text($phpINIfile) . "</span></li>\n";
1478 $gotFileFlag = 1;
1481 $short_tag = ini_get('short_open_tag') ? 'On' : 'Off';
1482 $short_tag_style = (strcmp($short_tag, 'Off') === 0) ? '' : 'text-danger';
1483 $display_errors = ini_get('display_errors') ? 'On' : 'Off';
1484 $display_errors_style = (strcmp($display_errors, "Off") === 0) ? '' : 'text-danger';
1485 $register_globals = ini_get('register_globals') ? 'On' : 'Off';
1486 $register_globals_style = (strcmp($register_globals, 'Off') === 0) ? '' : 'text-danger';
1487 $max_input_vars = ini_get('max_input_vars');
1488 $max_input_vars_style = $max_input_vars < 3000 ? 'text-danger' : '';
1489 $max_execution_time = (int)ini_get('max_execution_time');
1490 $max_execution_time_style = $max_execution_time >= 60 || $max_execution_time === 0 ? '' : 'text-danger';
1491 $max_input_time = ini_get('max_input_time');
1492 $max_input_time_style = (strcmp($max_input_time, '-1') === 0) ? '' : 'text-danger';
1493 $post_max_size = ini_get('post_max_size');
1494 $post_max_size_style = $post_max_size < 30 ? 'text-danger' : '';
1495 $memory_limit = ini_get('memory_limit');
1496 $memory_limit_style = $memory_limit < 256 ? 'text-danger' : '';
1497 $mysqli_allow_local_infile = ini_get('mysqli.allow_local_infile') ? 'On' : 'Off';
1498 $mysqli_allow_local_infile_style = (strcmp($mysqli_allow_local_infile, 'On') === 0) ? '' : 'text-danger';
1500 echo "<li>To ensure proper functioning of OpenEMR you must make sure that PHP settings include:
1501 <table class='phpset'>
1502 <tr>
1503 <th>Setting</th>
1504 <th>Required value</th>
1505 <th>Current value</th>
1506 </tr>
1507 <tr>
1508 <td>short_open_tag</td>
1509 <td>Off</td>
1510 <td class='" . attr($short_tag_style) . "'>" . text($short_tag) . "</td>
1511 </tr>
1512 <tr>
1513 <td>display_errors</td>
1514 <td>Off</td>
1515 <td class='" . attr($display_errors_style) . "'>" . text($display_errors) . "</td>
1516 </tr>
1517 <tr>
1518 <td>register_globals</td>
1519 <td>Off</td>
1520 <td class='" . attr($register_globals_style) . "'>" . text($register_globals) . "</td>
1521 </tr>
1522 <tr>
1523 <td>max_input_vars</td>
1524 <td>at least 3000</td>
1525 <td class='" . attr($max_input_vars_style) . "'>" . text($max_input_vars) . "</td>
1526 </tr>
1527 <tr>
1528 <td>max_execution_time</td>
1529 <td>at least 60</td>
1530 <td class='" . attr($max_execution_time_style) . "'>" . text($max_execution_time) . "</td>
1531 </tr>
1532 <tr>
1533 <td>max_input_time</td>
1534 <td>-1</td>
1535 <td class='" . attr($max_input_time_style) . "'>" . text($max_input_time) . "</td>
1536 </tr>
1537 <tr>
1538 <td>post_max_size</td>
1539 <td>at least 30M</td>
1540 <td class='" . attr($post_max_size_style) . "'>" . text($post_max_size) . "</td>
1541 </tr>
1542 <tr>
1543 <td>memory_limit</td>
1544 <td>at least 256M</td>
1545 <td class='" . attr($memory_limit_style) . "'>" . text($memory_limit) . "</td>
1546 </tr>
1547 <tr>
1548 <td>mysqli.allow_local_infile</td>
1549 <td>On</td>
1550 <td class='" . attr($mysqli_allow_local_infile_style) . "'>" . text($mysqli_allow_local_infile) . "</td>
1551 </tr>
1552 </table>
1553 </li>
1554 <li>In order to take full advantage of the patient documents capability you must make sure that settings in php.ini file include \"file_uploads = On\", that \"upload_max_filesize\" is appropriate for your use and that \"upload_tmp_dir\" is set to a correct value that will work on your system.
1555 </li>\r\n";
1557 if (!$gotFileFlag) {
1558 echo "<li>If you are having difficulty finding your php.ini file, then refer to the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u></a> manual for suggestions.</li>\n";
1561 $btn_text = 'Proceed to Step 5';
1562 echo "</ul>
1564 <p>We recommend you print these instructions for future reference.</p>
1565 <p>The next step will configure the Apache web server.</p>
1566 <p class='mark'>Click <strong>" . text($btn_text) . "</strong> to continue.</p>
1567 <br />
1568 <form method='post'>
1569 <input type='hidden' name='state' value='5' />
1570 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state5')) . "' />
1571 <input type='hidden' name='site' value='" . attr($site_id) . "' />
1572 <input type='hidden' name='iuser' value='" . attr($installer->iuser) . "' />
1573 <input type='hidden' name='iuserpass' value='" . attr($installer->iuserpass) . "' />
1574 <input name='login' type='hidden' value='" . attr($installer->login) . "' />
1575 <input name='pass' type='hidden' value='" . attr($installer->pass) . "' />
1576 <input name='server' type='hidden' value='" . attr($installer->server) . "' />
1577 <input name='port' type='hidden' value='" . attr($installer->port) . "' />
1578 <input name='loginhost' type='hidden' value='" . attr($installer->loginhost) . "' />
1579 <input name='dbname' type='hidden' value='" . attr($installer->dbname) . "' />
1580 <div class='form-row'>
1581 <div class='col-12'>
1582 <button type='submit' class='btn btn-primary' value='Continue'>
1583 <i class='fas fa-chevron-right'></i> " . text($btn_text) . "
1584 </button>
1585 </div>
1586 </div>
1587 </form>
1588 </div>\r\n";
1589 break;
1591 case 5:
1592 $_SESSION['bootstrapStateInSetup'] = 6;
1593 echo "<h3 class='mb-3 border-bottom'>Step " . text($state) . " - Configure Apache Web Server</h3>";
1594 echo "<div class='jumbotron p-5'>";
1595 echo "<p>Configuration of Apache web server...</p><br />\n";
1596 echo "The <code>\"" . text(preg_replace("/{$site_id}/", "*", realpath($docsDirectory))) . "\"</code> directory contain patient information, and
1597 it is important to secure these directories. Additionally, some settings are required for the Zend Framework to work in OpenEMR. This can be done by pasting the below to end of your apache configuration file:<br /><br />
1598 &nbsp;&nbsp;<code>&lt;Directory \"" . text(realpath(dirname(__FILE__))) . "\"&gt;<br />
1599 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride FileInfo<br />
1600 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all granted<br />
1601 &nbsp;&nbsp;<code>&lt;/Directory&gt;</code><br />
1602 &nbsp;&nbsp;&lt;Directory \"" . text(realpath(dirname(__FILE__))) . "/sites\"&gt;<br />
1603 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride None<br />
1604 &nbsp;&nbsp;&lt;/Directory&gt;</code><br />
1605 &nbsp;&nbsp;<code>&lt;Directory \"" . text(preg_replace("/{$site_id}/", "*", realpath($docsDirectory))) . "\"&gt;<br />
1606 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all denied<br />
1607 &nbsp;&nbsp;&lt;/Directory&gt;</code><br /><br />";
1609 $btn_text = 'Proceed to Select a Theme';
1610 echo "<p>If you are having difficulty finding your apache configuration file, then refer to the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u></a> manual for suggestions.</p>
1611 <p>We recommend you print these instructions for future reference.</p>
1612 <p class='mark'>Click <strong>'" . text($btn_text) . "'</strong> to select a theme.</p>
1613 <br />
1614 <form method='post'>
1615 <input type='hidden' name='state' value='6' />
1616 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state6')) . "' />
1617 <input type='hidden' name='site' value='" . attr($site_id) . "' />
1618 <input type='hidden' name='iuser' value='" . attr($installer->iuser) . "' />
1619 <input type='hidden' name='iuserpass' value='" . attr($installer->iuserpass) . "' />
1620 <input name='login' type='hidden' value='" . attr($installer->login) . "' />
1621 <input name='pass' type='hidden' value='" . attr($installer->pass) . "' />
1622 <input name='server' type='hidden' value='" . attr($installer->server) . "' />
1623 <input name='port' type='hidden' value='" . attr($installer->port) . "' />
1624 <input name='loginhost' type='hidden' value='" . attr($installer->loginhost) . "' />
1625 <input name='dbname' type='hidden' value='" . attr($installer->dbname) . "' />
1626 <div class='form-row'>
1627 <div class='col-12'>
1628 <button type='submit' class='btn btn-primary' value='Continue'>
1629 <i class='fas fa-chevron-right'></i> " . text($btn_text) . "
1630 </button>
1631 </div>
1632 </div>
1633 </form>
1634 <div>\r\n";
1635 break;
1637 case 6:
1638 $_SESSION['bootstrapStateInSetup'] = 7;
1639 echo "<h3 class='mb-3 border-bottom'>Step " . text($state) . " - Select a Theme</h3>";
1640 echo "<div class='jumbotron p-5'>";
1641 echo "<p>Select a theme for OpenEMR...</p>\n";
1642 $btn_text = "Proceed to Final Step";
1643 $installer->displaySelectedThemeDiv();
1644 echo "<div class='row'>
1645 <div class='col-12'>
1646 <form method='post'>
1647 <input type='hidden' name='state' value='7' />
1648 <input name='csrf_token_form' type='hidden' value='" . attr(CsrfUtils::collectCsrfToken('state7')) . "' />
1649 <input type='hidden' name='site' value='" . attr($site_id) . "' />
1650 <input type='hidden' name='iuser' value='" . attr($installer->iuser) . "' />
1651 <input type='hidden' name='iuserpass' value='" . attr($installer->iuserpass) . "' />
1652 <input name='login' type='hidden' value='" . attr($installer->login) . "' />
1653 <input name='pass' type='hidden' value='" . attr($installer->pass) . "' />
1654 <input name='server' type='hidden' value='" . attr($installer->server) . "' />
1655 <input name='port' type='hidden' value='" . attr($installer->port) . "' />
1656 <input name='loginhost' type='hidden' value='" . attr($installer->loginhost) . "' />
1657 <input name='dbname' type='hidden' value='" . attr($installer->dbname) . "' />
1658 <input type='hidden' name='new_theme' id = 'new_theme' value='" . attr($installer->getCurrentTheme()) . "' />
1659 <input name='clone_database' type='hidden' value='" . attr($installer->clone_database) . "' />
1660 <input name='source_site_id' type='hidden' value='" . attr($installer->source_site_id) . "' />
1661 <h4>Select One:</h4>
1662 <div class='checkbox'>
1663 <label><input type='checkbox' class='check' value='show_theme' />Show More Themes</label>
1664 </div>
1665 <div class='checkbox'>
1666 <label><input type='checkbox' class='check' value='keep_current' />Keep Current</label>
1667 </div>
1668 <div class='hide_button' style='display:none;'>
1669 <button type='submit' class='btn btn-primary' value='Continue' id='continue'>
1670 <i class='fas fa-chevron-right'></i> " . text($btn_text) . "
1671 </button>
1672 </div>
1673 </form>
1674 </div>
1675 </div>\r\n";
1676 echo '<div class="row hideaway" style="display:none;">' . "\r\n";
1677 echo '<div class="col-12">' . "\r\n";
1678 echo ' <h4>Select New Theme: <h5>(scroll down to view all)</h5></h4>' . "\r\n";
1679 echo ' <br />' . "\r\n";
1680 $installer->displayThemesDivs();
1681 echo "</div>";
1682 break;
1684 case 0:
1685 default:
1686 $top = <<<TOP
1687 <h3 class="mb-3 border-bottom">Pre Install - Checking File and Directory Permissions</h3>
1688 <div class="jumbotron p-5">
1690 Welcome to OpenEMR. This utility will step you through the installation and configuration of OpenEMR for your practice.
1691 </p>
1692 <ul>
1693 <li>
1694 Before proceeding, be sure that you have a properly installed and configured MySQL server available, and a PHP configured webserver.
1695 </li>
1696 <li>
1697 <span class="text-highlight">Detailed installation instructions can be found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u></a> manual file.</span>
1698 </li>
1699 <li>
1700 If you are upgrading from a previous version, <strong>DO NOT</strong> use this script. Please read the <strong>'Upgrading'</strong> section found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u></a> manual file.
1701 </li>
1702 </ul>
1703 TOP;
1704 echo $top;
1705 if ($checkPermissions) {
1706 echo "<p>We will now ensure correct file and directory permissions before starting installation:</p>\n";
1707 echo "<p class='text-success m-0'>Ensuring following file is world-writable...</p>\n";
1708 $errorWritable = 0;
1709 foreach ($writableFileList as $tempFile) {
1710 if (is_writable($tempFile)) {
1711 echo "<code class='ml-5'>" . text(realpath($tempFile)) . "</code> file is <span class='text-success font-weight-bold'>ready</span><br /><br />\n";
1712 } else {
1713 echo "<p><span class='text-danger'>UNABLE</span> to open file '" . text(realpath($tempFile)) . "' for writing.<br />\n";
1714 echo "(configure file permissions; see below for further instructions)</p>\n";
1715 $errorWritable = 1;
1719 if ($errorWritable) {
1720 $site_id_esc = attr($site_id);
1721 $check_file = <<<CHKFILE
1722 <p class="text-danger">You can't proceed until all above files are ready (world-writable).</p>
1723 <p>In linux, recommend changing file permissions with the <strong>'chmod 666 filename'</strong> command.</p>
1724 <p class='p-1 bg-danger text-white'>Fix above file permissions and then click the <strong>'Check Again'</strong> button to re-check files.</p>
1725 <br />
1726 <form method='post'>
1727 <input type='hidden' name='site' value='$site_id_esc' />
1728 <button type='submit' class='btn btn-primary' value='check again'>Check Again</button>
1729 </form>
1730 CHKFILE;
1731 echo $check_file . "\r\n";
1732 break;
1735 $errorWritable = 0;
1736 foreach ($writableDirList as $tempDir) {
1737 echo "<p class='text-success m-0'>Ensuring the <code>" . text(realpath($tempDir)) . "</code> directory and its subdirectories have proper permissions...</p>\n";
1738 $errorWritable = recursive_writable_directory_test($tempDir);
1741 if ($errorWritable) {
1742 $site_id_esc = attr($site_id);
1743 $check_directory = <<<CHKDIR
1744 <p class="text-danger">You can't proceed until all directories and subdirectories are ready.</p>
1745 <p>In linux, recommend changing owners of these directories to the web server. For example, in many linux OS's the web server user is 'apache', 'nobody', or 'www-data'. So if 'apache' were the web server user name, could use the command <strong>'chown -R apache:apache directory_name'</strong> command.</p>
1746 <p class='p-1 bg-warning'>Fix above directory permissions and then click the <strong>'Check Again'</strong> button to re-check directories.</p>
1747 <br />
1748 <form method='post'>
1749 <input type='hidden' name='site' value='$site_id_esc' />
1750 <button type='submit' value='check again'><b>Check Again</b></button>
1751 </form>
1752 CHKDIR;
1753 echo $check_directory . "\r\n";
1754 break;
1757 //RP_CHECK_LOGIC
1758 $_SESSION['bootstrapStateInSetup'] = 1;
1759 $site_id_esc = attr($site_id);
1760 $csrf_id_esc = attr(CsrfUtils::collectCsrfToken('state1'));
1761 $form = <<<FRM
1762 <p>All required files and directories have been verified.</p>
1763 <p class='mark'>Click <span class="font-weight-bold">Proceed to Step 1</span> to continue with a new installation.</p>
1764 <p class='p-1 bg-warning'>$caution: If you are upgrading from a previous version, <strong>DO NOT</strong> use this script. Please read the <strong>'Upgrading'</strong> section found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u></a> manual file.</p>
1765 <br />
1766 <form method='post'>
1767 <input name='state' type='hidden' value='1' />
1768 <input name='site' type='hidden' value='$site_id_esc' />
1769 <input name='csrf_token_form' type='hidden' value='$csrf_id_esc' />
1770 <div class="form-group">
1771 <div class="col">
1772 <button type='submit' class='btn btn-primary' value='Continue'>
1773 <i class="fas fa-chevron-right"></i> Proceed to Step 1
1774 </button>
1775 </div>
1776 </div>
1777 </form>
1778 FRM;
1779 echo $form . "\r\n";
1780 } else {
1781 $_SESSION['bootstrapStateInSetup'] = 1;
1782 $site_id_esc = attr($site_id);
1783 $csrf_id_esc = attr(CsrfUtils::collectCsrfToken('state1'));
1784 $form = <<<FRM
1785 <br />
1786 <p class='p-1 bg-warning'>$caution: Permisssions checking has been disabled. All required files and directories have NOT been verified, please manually verify sites/$site_id_esc .</p>
1787 <p class='mark'>Click <b>Proceed to Step 1</b> to continue with a new installation.</p>
1788 <p class='p-1 bg-warning'>$caution: If you are upgrading from a previous version, <strong>DO NOT</strong> use this script. Please read the <strong>'Upgrading'</strong> section found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><span style='text-decoration: underline;'>'INSTALL'</span></a> manual file.</p>
1789 <br />
1790 <form method='post'>
1791 <input name='state' type='hidden' value='1'>
1792 <input name='site' type='hidden' value='$site_id_esc'>
1793 <input name='csrf_token_form' type='hidden' value='$csrf_id_esc' />
1794 <button type='submit' value='Continue'><b>Proceed to Step 1</b></button>
1795 </form>
1796 FRM;
1797 echo $form . "\r\n"; }
1799 $bot = <<<BOT
1800 </div>
1801 </div>
1802 </div>
1803 BOT;
1804 echo $bot . "\r\n";
1808 </div><!--end of container div -->
1809 <?php $installer->setupHelpModal();?>
1810 <script>
1811 //jquery-ui tooltip
1812 $(function () {
1813 $('.icon-tooltip').prop( "title", "Click to see more information").tooltip({
1814 show: {
1815 delay: 700,
1816 duration: 0
1819 $('.enter-details-tooltip').prop( "title", "Additional help to fill out this form is available by hovering over labels of each box and clicking on the dark blue help ? icon that is revealed. On mobile devices tap once on the label to reveal the help icon and tap on the icon to show the help section").tooltip();
1820 $('.2fa-section-tooltip').prop( "title", "Two factor authentication prevents unauthorized access to openEMR thus improves security. It is optional. More information is available in the help file under Step 2 Database and OpenEMR Initial User Setup Details.").tooltip();
1824 </script>
1825 <script>
1826 $(function () {
1827 $("input[type='radio']").click(function() {
1828 var radioValue = $("input[name='stylesheet']:checked").val();
1829 var imgPath = "public/images/stylesheets/";
1830 var currStyle = $("#current_theme_title").text();
1831 var currStyleTitle = currStyle;
1832 currStyle = currStyle.replace(/\b\w/g, l => l.toLowerCase());
1833 currStyle = currStyle.split(" ");
1834 currStyle = currStyle.join("_");
1835 currStyle = "style_" + currStyle + ".png";
1836 if (radioValue) {
1837 var currThemeText = radioValue.split("_");
1838 currThemeText = currThemeText.join(" ");
1839 currThemeText = currThemeText.replace(/\b\w/g, l => l.toUpperCase());
1840 var styleSelected = confirm("You have selected style - " + currThemeText + "\n" + "Click OK to apply selection");
1841 if (styleSelected) {
1842 $("#current_theme").attr("src", imgPath + "style_" + radioValue + ".png");
1843 $("#current_theme_title").text(currThemeText);
1844 $("#new_theme").val("style_" + radioValue + ".css");
1845 } else {
1846 $("#current_theme").attr("src", imgPath + currStyle);
1847 $("#current_theme_title").text(currStyleTitle);
1848 $(this).prop("checked", false);
1852 $('.check').click(function() {
1853 $('.check').not(this).prop('checked', false);
1854 if($('.check:checked').val() == 'show_theme'){
1855 $(".hideaway").show();
1856 } else if($('.check:checked').val() == 'keep_current'){
1857 $(".hideaway").hide();
1860 if($('.check').filter(':checked').length > 0) {
1861 $(".hide_button").show();
1862 } else {
1863 $(".hide_button").hide();
1864 $(".hideaway").hide();
1867 $('.wait').removeClass('button-wait');
1869 $( "#create_db_button" ).hover(
1870 function() {
1871 if (($('#iuserpass' ).val().length > 11 && $('#iuser' ).val().length > 11 ) || ($('#clone_database').prop('checked'))){
1873 $("button").click(function(){
1874 $(".oe-spinner").css("visibility", "visible");
1877 $('.wait').click(function(){
1878 $('.wait').addClass('button-wait');
1884 $("#step-4-btn").click(function(){
1885 $(".oe-spinner").css("visibility", "visible");
1886 $(this).addClass('button-wait');
1889 </script>
1890 </body>
1891 </html>