minor psr12 fix
[openemr.git] / setup.php
blob114717389dae0b9936cb1717ce9bb7420ab2c2c6
1 <?php
3 /**
5 * Installation script.
7 * @package OpenEMR
8 * @link https://www.open-emr.org
9 * @author Roberto Vasquez <robertogagliotta@gmail.com>
10 * @author Scott Wakefield <scott@npclinics.com.au>
11 * @author Ranganath Pathak <pathak@scrs1.org>
12 * @author Brady Miller <brady.g.miller@gmail.com>
13 * @copyright Copyright (c) 2016 Roberto Vasquez <robertogagliotta@gmail.com>
14 * @copyright Copyright (c) 2016 Scott Wakefield <scott@npclinics.com.au>
15 * @copyright Copyright (c) 2019 Ranganath Pathak <pathak@scrs1.org>
16 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
17 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
20 // Checks if the server's PHP version is compatible with OpenEMR:
21 require_once(dirname(__FILE__) . "/src/Common/Compatibility/Checker.php");
22 $response = OpenEMR\Common\Compatibility\Checker::checkPhpVersion();
23 if ($response !== true) {
24 die(htmlspecialchars($response));
27 // Set the maximum excution time and time limit to unlimited.
28 ini_set('max_execution_time', 0);
29 ini_set('display_errors', 0);
30 set_time_limit(0);
32 // Warning. If you set $allow_multisite_setup to true, this is a potential security vulnerability.
33 // Recommend setting it back to false (or removing this setup.php script entirely) after you
34 // are done with the multisite procedure.
35 $allow_multisite_setup = false;
37 // Warning. If you set $allow_cloning_setup to true, this is a potential security vulnerability.
38 // Recommend setting it back to false (or removing this setup.php script entirely) after you
39 // are done with the cloning setup procedure.
40 $allow_cloning_setup = false;
41 if (!$allow_cloning_setup && !empty($_REQUEST['clone_database'])) {
42 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");
45 function recursive_writable_directory_test($dir)
47 // first, collect the directory and subdirectories
48 $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
49 $dirNames = array();
50 foreach ($ri as $file) {
51 if ($file->isDir()) {
52 if (!preg_match("/\.\.$/", $file->getPathname())) {
53 $dirName = realpath($file->getPathname());
54 if (!in_array($dirName, $dirNames)) {
55 $dirNames[] = $dirName;
61 // second, flag the directories that are not writable
62 $resultsNegative = array();
63 foreach ($dirNames as $value) {
64 if (!is_writable($value)) {
65 $resultsNegative[] = $value;
69 // third, send the output and return if didn't pass the test
70 if (!empty($resultsNegative)) {
71 echo "<p>";
72 $mainDirTest = "";
73 $outputs = array();
74 foreach ($resultsNegative as $failedDir) {
75 if (basename($failedDir) == basename($dir)) {
76 // need to reorder output so the main directory is at the top of the list
77 $mainDirTest = "<span class='text-danger'>UNABLE</span> to open directory '" . realpath($failedDir) . "' for writing by web server.<br />\r\n";
78 } else {
79 $outputs[] = "<span class='text-danger'>UNABLE</span> to open subdirectory '" . realpath($failedDir) . "' for writing by web server.<br />\r\n";
82 if ($mainDirTest) {
83 // need to reorder output so the main directory is at the top of the list
84 array_unshift($outputs, $mainDirTest);
86 foreach ($outputs as $output) {
87 echo $output;
89 echo "(configure directory permissions; see below for further instructions)</p>\r\n";
90 return 1;
91 } else {
92 echo "<code class='ml-5'>" . realpath($dir) . "</code> directory and its subdirectories are <span class='text-success font-weight-bold'>ready</span>.<br /><br />\r\n";
93 return 0;
97 // Include standard libraries/classes
98 require_once dirname(__FILE__) . "/vendor/autoload.php";
100 use OpenEMR\Common\Utils\RandomGenUtils;
102 $COMMAND_LINE = php_sapi_name() == 'cli';
104 $state = isset($_POST["state"]) ? ($_POST["state"]) : '';
105 $installer = new Installer($_REQUEST);
106 // Make this true for IPPF.
107 $ippf_specific = false;
109 $error_page_end = <<<EPE
110 </div>
111 </div>
112 </div><!--end of container div-->
113 </body>
114 </html>
115 EPE;
117 // If this script was invoked with no site ID, then ask for one.
118 if (!$COMMAND_LINE && empty($_REQUEST['site'])) {
119 $site_id = <<<SITEID
120 <!DOCTYPE html>
121 <html>
122 <head>
123 <title>OpenEMR Setup Tool</title>
124 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css">
125 <script src="public/assets/jquery/dist/jquery.min.js"></script>
126 <script src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
127 <link rel="stylesheet" href="public/assets/@fortawesome/fontawesome-free/css/all.min.css">
128 <link rel="shortcut icon" href="public/images/favicon.ico" />
129 <style>
130 .oe-pull-away {
131 float:right;
133 </style>
134 </head>
135 <body>
136 <nav class="navbar navbar-expand navbar-light bg-light">
137 <div class="container">
138 <a class="navbar-brand" href="#">OpenEMR Setup</a>
139 <div class="collapse navbar-collapse justify-content-end">
140 <ul class="navbar-nav">
141 <li class="nav-item active">
142 <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>
143 </li>
144 </ul>
145 </div>
146 </div>
147 </nav>
149 <div class='container mt-3'>
150 <div class="row">
151 <div class="col-12">
152 <h3 class="mb-3 border-bottom">Optional Site ID Selection</h3>
153 <div class="jumbotron p-5">
155 Most OpenEMR installations support only one site. If that is
156 true for you then ignore the rest of this text and just click Continue.
157 </p>
158 <p class='p-2 bg-warning'>
159 If you are using the multisite setup module for the first time please read the
160 'Multi Site Installation' section of the help file before proceeding.
161 </p>
163 Otherwise please enter a unique Site ID here.
164 </p>
166 A Site ID is a short identifier with no spaces or special
167 characters other than periods or dashes. It is case-sensitive and we
168 suggest sticking to lower case letters for ease of use.
169 </p>
171 If each site will have its own host/domain name, then use that
172 name as the Site ID (e.g. www.example.com).
173 </p>
175 The site ID is used to identify which site you will log in to.
176 If it is a hostname then it is taken from the hostname in the URL.
177 Otherwise you must append "?site=<i>siteid</i>" to the URL used for
178 logging in.
179 </p>
181 It is OK for one of the sites to have "default" as its ID. This
182 is the ID that will be used if it cannot otherwise be determined.
183 </p>
184 <br />
185 <form method='post'>
186 <input type='hidden' name='state' value='0' />
187 <div class="form-row">
188 <div class="col-auto">
189 Site ID:
190 </div>
191 <div class="col">
192 <input type='text' class='form-control' name='site' value='default'>
193 </div>
194 <div class="col-12 mt-3">
195 <button type='submit' class='btn btn-primary' value='Continue'><i class="fas fa-chevron-right"></i> Continue</button>
196 </div>
197 </div>
198 </form>
199 </div>
200 </div>
201 </div>
202 </div><!--end of container div-->
203 SITEID;
204 echo $site_id . "\r\n";
205 $installer->setupHelpModal();
206 echo "</body>" . "\r\n";
207 echo "</html>" . "\r\n";
209 exit();
212 // Support "?site=siteid" in the URL, otherwise assume "default".
213 $site_id = 'default';
214 if (!$COMMAND_LINE && !empty($_REQUEST['site'])) {
215 $site_id = trim($_REQUEST['site']);
218 // Die if site ID is empty or has invalid characters.
219 if (empty($site_id) || preg_match('/[^A-Za-z0-9\\-.]/', $site_id)) {
220 die("Site ID '" . htmlspecialchars($site_id, ENT_NOQUOTES) . "' contains invalid characters.");
223 // If multisite is turned off, then only allow default for site.
224 if (!$allow_multisite_setup && $site_id != 'default') {
225 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");
228 //If having problems with file and directory permission
229 // checking, then can be manually disabled here.
230 $checkPermissions = true;
232 global $OE_SITE_DIR; // The Installer sets this
234 $docsDirectory = "$OE_SITE_DIR/documents";
236 //These are files and dir checked before install for
237 // correct permissions.
238 if (is_dir($OE_SITE_DIR)) {
239 $writableFileList = array($installer->conffile);
240 $writableDirList = array($docsDirectory);
241 } else {
242 $writableFileList = array();
243 $writableDirList = array($OE_SITES_BASE);
246 // Include the sqlconf file if it exists yet.
247 $config = 0;
248 if (file_exists($OE_SITE_DIR)) {
249 include_once($installer->conffile);
250 } elseif ($state > 3) {
251 // State 3 should have created the site directory if it is missing.
252 die("Internal error, site directory is missing.");
255 <html>
256 <head>
257 <title>OpenEMR Setup Tool</title>
258 <!--<link rel=stylesheet href="interface/themes/style_blue.css">-->
259 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css">
260 <script src="public/assets/jquery/dist/jquery.min.js"></script>
261 <script src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
262 <link rel="stylesheet" href="public/assets/@fortawesome/fontawesome-free/css/all.min.css">
263 <link rel="shortcut icon" href="public/images/favicon.ico" />
265 <style>
266 .noclone { }
267 table.phpset {
268 border-collapse:collapse;
270 table.phpset td, table.phpset th {
271 font-size:9pt;
272 border:1px solid gray;
273 padding:2px;
275 .table.no-border tr td, .table.no-border tr th {
276 border-width: 0;
278 td {
279 font-size:10pt;
281 .inputtext {
282 padding-left:2px;
283 padding-right:2px;
286 .button {
287 font-family:sans-serif;
288 font-size:9pt;
289 font-weight:bold;
292 .label-div > a {
293 display:none;
295 .label-div:hover > a {
296 display:inline-block;
298 div[id$="_info"] {
299 background: #F7FAB3;
300 padding: 20px;
301 margin: 10px 15px 0px 15px;
303 div[id$="_info"] > a {
304 margin-left:10px;
306 .checkboxgroup {
307 display: inline-block;
308 text-align: center;
310 .checkboxgroup label {
311 display: block;
313 .oe-pull-away{
314 float:right;
316 .oe-help-x {
317 color: grey;
318 padding: 0 5px;
320 .oe-superscript {
321 position: relative;
322 top: -.5em;
323 font-size: 70%!important;
325 .oe-setup-legend{
326 background-color: #f5f5f5;
327 padding:0 10px;
329 .button-wait {
330 color: grey;
331 cursor: not-allowed;
332 opacity: 0.6;
334 @media only screen {
335 fieldset > [class*="col-"] {
336 width: 100%;
337 text-align:left!Important;
340 </style>
341 <script>
342 // onclick handler for "clone database" checkbox
343 function cloneClicked() {
344 var cb = document.forms[0].clone_database;
345 $('.noclone').css('display', cb.checked ? 'none' : 'block');
347 </script>
349 </head>
350 <body>
351 <nav class="navbar navbar-expand navbar-light bg-light">
352 <div class="container">
353 <a class="navbar-brand" href="#">OpenEMR Setup</a>
354 <div class="collapse navbar-collapse justify-content-end">
355 <ul class="navbar-nav">
356 <li class="nav-item active">
357 <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>
358 </li>
359 </ul>
360 </div>
361 </div>
362 </nav>
363 <div class='mt-3 container'>
364 <div class="row">
365 <div class="col-12">
366 <?php
367 $error = "<span class='text-danger font-weight-bold'>ERROR</span>";
368 $caution = "<span class='text-danger font-weight-bold'>CAUTION</span>";
369 $ok = "<span class='text-success font-weight-bold'>OK</span>";
370 $note = "<span class='text-primary font-weight-bold'>NOTE</span>";
372 if (strtolower(ini_get('register_globals')) != 'off' && (bool) ini_get('register_globals')) {
373 echo "$caution: It appears that you have register_globals enabled in your php.ini\n" .
374 "configuration file. This causes unacceptable security risks. You must\n" .
375 "turn it off before continuing with installation.\n";
376 exit(1);
379 if (!extension_loaded("xml")) {
380 echo "$error: PHP XML extension missing. To continue, install PHP XML extension, then restart web server.";
381 exit(1);
384 if (!(extension_loaded("mysql") || extension_loaded("mysqlnd") || extension_loaded("mysqli"))) {
385 echo "$error: PHP MySQL extension missing. To continue, install and enable MySQL extension, then restart web server.";
386 exit(1);
389 if (!(extension_loaded("mbstring") )) {
390 echo "$error: PHP mb_string extension missing. To continue, install and enable mb_string extension, then restart web server.";
391 exit(1);
394 if (!(extension_loaded("openssl") )) {
395 echo "$error: PHP openssl extension missing. To continue, install PHP openssl extension, then restart web server.";
396 exit(1);
400 <?php
401 if ($state == 7) {
403 <h3 class="mb-3 border-bottom">Final step - Success</h3>
404 <div class="jumbotron p-5">
405 <p>Congratulations! OpenEMR is now installed.</p>
406 <ul>
407 <li>Access controls (php-GACL) are installed for fine-grained security, and can be administered in
408 OpenEMR's admin->acl menu.</li>
409 <li>Reviewing <code> <?php echo $OE_SITE_DIR; ?>/config.php </code> is a good idea. This file
410 contains some settings that you may want to change.</li>
411 <li>There's much information and many extra tools bundled within the OpenEMR installation directory.
412 Please refer to openemr/Documentation. Many forms and other useful scripts can be found at openemr/contrib.</li>
413 <li>To ensure a consistent look and feel throughout the application,
414 <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>
415 <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>
416 <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>
417 </ul>
418 <p>We recommend you print these instructions for future reference.</p>
419 <?php
420 echo "<p> The selected theme is :</p>";
421 $installer->displayNewThemeDiv();
422 if (empty($installer->clone_database)) {
423 echo "<p><b>The initial OpenEMR user is <span class='text-primary'>'" . $installer->iuser . "'</span> and the password is <span class='text-primary'>'" . $installer->iuserpass . "'</span></b></p>";
424 } else {
425 echo "<p>The initial OpenEMR user name and password is the same as that of source site <b>'" . $installer->source_site_id . "'</span></b></p>";
427 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>";
428 echo "<p>In Linux use the following command:</p>";
429 echo "<p><code>sudo apachectl -k restart</code></p>";
432 <p>Click to start using OpenEMR.</p>
433 <div class="row">
434 <div class="col-12">
435 <a href='./?site=<?php echo $site_id; ?>' class='btn btn-primary'>
436 <i class="fas fa-chevron-right"></i> Start
437 </a>
438 </div>
439 </div>
441 </div>
442 <?php
443 $installer->setCurrentTheme();
445 $end_div = <<<ENDDIV
446 </div>
447 </div>
448 </div><!--end of container div-->
449 ENDDIV;
450 echo $end_div . "\r\n";
451 $installer->setupHelpModal();
452 echo "</body>" . "\r\n";
453 echo "</html>" . "\r\n";
455 exit();
459 <?php
461 $inst = isset($_POST["inst"]) ? ($_POST["inst"]) : '';
463 if (($config == 1) && ($state < 4)) {
464 echo "OpenEMR has already been installed. If you wish to force re-installation, then edit $installer->conffile (change the 'config' variable to 0), and re-run this script.<br />\n";
465 } else {
466 switch ($state) {
467 case 1:
468 $step1 = <<<STP1
469 <h3 class="mb-3 border-bottom">Step $state - Select Database Setup</h3>
470 <div class="jumbotron p-5">
471 <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>
472 <br />
473 <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>
474 <br />
475 <form method='post'>
476 <input name='state' type='hidden' value='2' />
477 <input name='site' type='hidden' value='$site_id' />
478 <div class="form-check">
479 <input checked class='form-check-input' id='inst1' name='inst' type='radio' value='1' />
480 <label class="form-check-label" for="inst1">
481 Have setup create the database
482 </label>
483 </div>
484 <br />
485 <div class="form-check">
486 <input id='inst2' class='form-check-input' name='inst' type='radio' value='2' />
487 <label class="form-check-label" for="inst2">
488 I have already created the database
489 </label>
490 </div>
491 <div class="form-group">
492 <div class="col mt-3">
493 <button type='submit' class='btn btn-primary' value='Continue'>
494 <i class="fas fa-chevron-right"></i> Proceed to Step 2
495 </button>
496 </div>
497 </div>
498 </form>
499 <br />
500 </div>
501 STP1;
502 echo $step1 . "\r\n";
503 break;
505 case 2:
506 $step2top = <<<STP2TOP
507 <h3 class="mb-3 border-bottom">Step $state - Database and OpenEMR Initial User Setup Details</h3>
508 <div class="jumbotron p-5">
509 <p>Now you need to supply the MySQL server information and path information. Detailed instructions on each item can be found in the
510 <a href='Documentation/INSTALL' rel='noopener' target='_blank'><u>'INSTALL'</u>
511 </a> manual file.
512 </p>
513 <form method='post' id='myform'>
514 <input name='state' type='hidden' value='3' />
515 <input name='site' type='hidden' value='$site_id' />
516 <input name='inst' type='hidden' value='$inst' />
517 STP2TOP;
518 echo $step2top . "\r\n";
521 $step2tabletop1 = <<<STP2TBLTOP1
522 <fieldset>
523 <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>
524 <div class="ml-2 row">
525 <div class="col-sm-4">
526 <div class="clearfix form-group">
527 <div class="label-div">
528 <label class="font-weight-bold" for="server">Server Host:</label>
529 <a href="#server_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
530 </div>
531 <div>
532 <input name='server' id='server' type='text' class='form-control' value='localhost' />
533 </div>
534 </div>
535 <div id="server_info" class="collapse">
536 <a href="#server_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
537 <p>If you run MySQL and Apache/PHP on the same computer, then leave this as 'localhost'.</p>
538 <p>If they are on separate computers, then enter the IP address of the computer running MySQL.</p>
539 </div>
540 </div>
541 <div class="col-sm-4">
542 <div class="clearfix form-group">
543 <div class="label-div">
544 <label class="font-weight-bold" for="port">Server Port:</label>
545 <a href="#port_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
546 </div>
547 <div>
548 <input name='port' id='port' type='text' class='form-control' value='3306' />
549 </div>
550 </div>
551 <div id="port_info" class="collapse">
552 <a href="#port_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
553 <p>This is the MySQL port.</p>
554 <p>The default port for MySQL is 3306.</p>
555 </div>
556 </div>
557 <div class="col-sm-4">
558 <div class="clearfix form-group">
559 <div class="label-div">
560 <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>
561 </div>
562 <div>
563 <input name='dbname' id='dbname' type='text' class='form-control' value='openemr' />
564 </div>
565 </div>
566 <div id="dbname_info" class="collapse">
567 <a href="#dbname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
568 <p>This will be the name of the OpenEMR database in MySQL.</p>
569 <p>'openemr' is the recommended name.</p>
570 <p>This database will contain patient data as well as data pertaining to the OpenEMR installation.</p>
571 </div>
572 </div>
573 </div>
574 <div class="ml-2 row">
575 <div class="col-sm-4">
576 <div class="clearfix form-group">
577 <div class="label-div">
578 <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>
579 </div>
580 <div>
581 <input name='login' ID='login' type='text' class='form-control' value='openemr' />
582 </div>
583 </div>
584 <div id="login_info" class="collapse">
585 <a href="#login_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
586 <p>This is the name that OpenEMR will use to login to the MySQL database.</p>
587 <p>'openemr' is the recommended name.</p>
588 </div>
589 </div>
590 <div class="col-sm-4">
591 <div class="clearfix form-group">
592 <div class="label-div">
593 <label class="font-weight-bold" for="pass">Password:</label>
594 <a href="#pass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
595 </div>
596 <div>
597 <input name='pass' id='pass' class='form-control' type='password' value='' required />
598 </div>
599 </div>
600 <div id="pass_info" class="collapse">
601 <a href="#pass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
602 <p>This is the Login Password that OpenEMR will use to accesses the MySQL database.</p>
603 <p>It should be at least 12 characters long and composed of both numbers and letters.</p>
604 </div>
605 </div>
606 STP2TBLTOP1;
607 echo $step2tabletop1 . "\r\n";
608 if ($inst != 2) {
609 $step2tabletop2 = <<<STP2TBLTOP2
610 <div class="col-sm-4">
611 <div class="clearfix form-group">
612 <div class="label-div">
613 <label class="font-weight-bold" for="root">Name for Root Account:</label>
614 <a href="#root_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
615 </div>
616 <div>
617 <input name='root' id='root' type='text' class='form-control' value='root' />
618 </div>
619 </div>
620 <div id="root_info" class="collapse">
621 <a href="#root_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
622 <p>This is name for the MySQL root account.</p>
623 <p>For localhost, it is usually ok to leave it as 'root'.</p>
624 </div>
625 </div>
626 </div>
627 <div class="ml-2 row">
628 <div class="col-sm-4">
629 <div class="clearfix form-group">
630 <div class="label-div">
631 <label class="font-weight-bold" for="rootpass">Root Password:</label>
632 <a href="#rootpass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
633 </div>
634 <div>
635 <input name='rootpass' id='rootpass' type='password' class='form-control' value='' />
636 </div>
637 </div>
638 <div id="rootpass_info" class="collapse">
639 <a href="#rootpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
640 <p>This is your MySQL server root password.</p>
641 </div>
642 </div>
643 <div class="col-sm-4">
644 <div class="clearfix form-group">
645 <div class="label-div">
646 <label class="font-weight-bold" for="loginhost">User Hostname:</label>
647 <a href="#loginhost_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
648 </div>
649 <div>
650 <input name='loginhost' id='loginhost' type='text' class='form-control' value='localhost' />
651 </div>
652 </div>
653 <div id="loginhost_info" class="collapse">
654 <a href="#loginhost_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
655 <p>If you run Apache/PHP and MySQL on the same computer, then leave this as 'localhost'.</p>
656 <p>If they are on separate computers, then enter the IP address of the computer running Apache/PHP.</p>
657 </div>
658 </div>
659 <div class="col-sm-4">
660 <div class="clearfix form-group">
661 <div class="label-div">
662 <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>
663 </div>
664 <div>
665 <select name='collate' id=='collate' class='form-control'>
666 <option selected value='utf8mb4_general_ci'>
667 General (Recommended)
668 </option>
669 <option value='utf8mb4_unicode_ci'>
670 Unicode
671 </option>
672 <option value='utf8mb4_roman_ci'>
673 Classical Latin
674 </option>
675 <option value='utf8mb4_croatian_ci'>
676 Croatian
677 </option>
678 <option value='utf8mb4_czech_ci'>
679 Czech
680 </option>
681 <option value='utf8mb4_danish_ci'>
682 Danish
683 </option>
684 <option value='utf8mb4_esperanto_ci'>
685 Esperanto
686 </option>
687 <option value='utf8mb4_estonian_ci'>
688 Estonian
689 </option>
690 <option value='utf8mb4_german2_ci'>
691 German
692 </option>
693 <option value='utf8mb4_hungarian_ci'>
694 Hungarian
695 </option>
696 <option value='utf8mb4_icelandic_ci'>
697 Icelandic
698 </option>
699 <option value='utf8mb4_latvian_ci'>
700 Latvian
701 </option>
702 <option value='utf8mb4_lithuanian_ci'>
703 Lithuanian
704 </option>
705 <option value='utf8mb4_persian_ci'>
706 Persian
707 </option>
708 <option value='utf8mb4_polish_ci'>
709 Polish
710 </option>
711 <option value='utf8mb4_romanian_ci'>
712 Romanian
713 </option>
714 <option value='utf8mb4_sinhala_ci'>
715 Sinhala
716 </option>
717 <option value='utf8mb4_slovak_ci'>
718 Slovak
719 </option>
720 <option value='utf8mb4_slovenian_ci'>
721 Slovenian
722 </option>
723 <option value='utf8mb4_spanish_ci'>
724 Spanish (Modern)
725 </option>
726 <option value='utf8mb4_spanish2_ci'>
727 Spanish (Traditional)
728 </option>
729 <option value='utf8mb4_swedish_ci'>
730 Swedish
731 </option>
732 <option value='utf8mb4_turkish_ci'>
733 Turkish
734 </option>
735 <option value='utf8mb4_vietnamese_ci'>
736 Vietnamese
737 </option>
738 </select>
739 </div>
740 </div>
741 <div id="collate_info" class="collapse">
742 <a href="#collate_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
743 <p>This is the collation setting for MySQL.</p>
744 <p>Collation refers to a set of rules that determine how data is sorted and compared in a database.</p>
745 <p>Leave as 'General' if you are not sure.</p>
746 <p>If the language you are planning to use in OpenEMR is in the menu, then you can select it.</p>
747 <p>Otherwise, just select 'General'.</p>
748 </div>
749 </div>
750 </div>
751 STP2TBLTOP2;
752 echo $step2tabletop2 . "\r\n";
754 // Include a "source" site ID drop-list and a checkbox to indicate
755 // if cloning its database. When checked, do not display initial user
756 // and group stuff below.
757 $dh = opendir($OE_SITES_BASE);
758 if (!$dh) {
759 die("Cannot read directory '$OE_SITES_BASE'.");
762 $siteslist = array();
763 while (false !== ($sfname = readdir($dh))) {
764 if (substr($sfname, 0, 1) == '.') {
765 continue;
768 if ($sfname == 'CVS') {
769 continue;
772 if ($sfname == $site_id) {
773 continue;
776 $sitedir = "$OE_SITES_BASE/$sfname";
777 if (!is_dir($sitedir)) {
778 continue;
781 if (!is_file("$sitedir/sqlconf.php")) {
782 continue;
785 $siteslist[$sfname] = $sfname;
788 closedir($dh);
789 // If this is not the first site...
790 if (!empty($siteslist)) {
791 ksort($siteslist);
792 $source_site_top = <<<SOURCESITETOP
793 <div class="ml-2 row">
794 <div class="col-sm-4">
795 <div class="clearfix form-group">
796 <div class="label-div">
797 <label class="font-weight-bold" for="source_site_id">Source Site:</label>
798 <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>
799 </div>
800 <div>
801 <select name='source_site_id'id='source_site_id' class='form-control'>
802 SOURCESITETOP;
803 echo $source_site_top . "\r\n";
804 foreach ($siteslist as $sfname) {
805 echo "<option value='$sfname'";
806 if ($sfname == 'default') {
807 echo " selected";
810 echo ">$sfname</option>";
812 $source_site_bot = <<<SOURCESITEBOT
813 </select>
815 </div>
816 </div>
817 <div id="source_site_id_info" class="collapse">
818 <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>
819 <p>The site directory that will be a model for the new site.</p>
820 </div>
821 </div>
822 <div class="col-sm-4">
823 <div class="clearfix form-group">
824 <div class="label-div">
825 <label class="font-weight-bold" for="clone_database">Clone Source Database:</label>
826 <a href="#clone_database_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
827 </div>
828 <div>
829 <input type='checkbox' name='clone_database' id='clone_database' onclick='cloneClicked()' />
830 </div>
831 </div>
832 <div id="clone_database_info" class="collapse">
833 <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>
834 <p>Clone the source site's database instead of creating a fresh one.</p>
835 </div>
836 </div>
837 </div>
838 SOURCESITEBOT;
839 echo $source_site_bot . "\r\n";
842 $randomusernamepre = RandomGenUtils::produceRandomString(3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
843 $randomusernamepost = RandomGenUtils::produceRandomString(2, "0123456789");
844 $randomusername = $randomusernamepre . "-admin-" . $randomusernamepost;
846 // App Based TOTP secret
847 // Shared key (per rfc6238 and rfc4226) should be 20 bytes (160 bits) and encoded in base32, which should
848 // be 32 characters in base32
849 // Would be nice to use the OpenEMR\Common\Utils\RandomGenUtils\produceRandomBytes() function and then encode to base32,
850 // but does not appear to be a standard way to encode binary to base32 in php.
851 $randomsecret = RandomGenUtils::produceRandomString(32, "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ");
852 if (empty($randomsecret) || empty($randomusernamepre) || empty($randomusernamepost)) {
853 error_log('OpenEMR Error : Random String error - exiting');
854 die();
856 $disableCheckbox = "";
857 if (empty($randomsecret)) {
858 $randomsecret = "";
859 $disableCheckbox = "disabled";
862 $step2tablebot = <<<STP2TBLBOT
863 </fieldset>
864 <br />
865 <fieldset class='noclone'>
866 <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>
867 <div class="ml-2 row">
868 <div class="col-sm-4">
869 <div class="clearfix form-group">
870 <div class="label-div">
871 <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>
872 </div>
873 <div>
874 <input name='iuser' id='iuser' type='text' class='form-control' value='$randomusername' minlength='12' />
875 </div>
876 </div>
877 <div id="iuser_info" class="collapse">
878 <a href="#iuser_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
879 <p>This is the login name of the first user that will be created for you.</p>
880 <p>Limit this to one word with at least 12 characters and composed of both numbers and letters.</p>
881 </div>
882 </div>
883 <div class="col-sm-4">
884 <div class="clearfix form-group">
885 <div class="label-div">
886 <label class="font-weight-bold" for="iuserpass">Initial User Password:</label>
887 <a href="#iuserpass_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
888 </div>
889 <div>
890 <input name='iuserpass' id='iuserpass' type='password' class='form-control' value='' minlength='12' />
891 </div>
892 </div>
893 <div id="iuserpass_info" class="collapse">
894 <a href="#iuserpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
895 <p>This is the password for the initial user.
896 </div>
897 </div>
898 <div class="col-sm-4">
899 <div class="clearfix form-group">
900 <div class="label-div">
901 <label class="font-weight-bold" for="iufname">Initial User's First Name:</label>
902 <a href="#iufname_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
903 </div>
904 <div>
905 <input name='iufname' id='iufname 'type='text' class='form-control' value='Administrator' />
906 </div>
907 </div>
908 <div id="iufname_info" class="collapse">
909 <a href="#iufname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
910 <p>This is the First name of the 'initial user'.</p>
911 </div>
912 </div>
913 </div>
914 <div class="ml-2 row">
915 <div class="col-sm-4">
916 <div class="clearfix form-group">
917 <div class="label-div">
918 <label class="font-weight-bold" for="iuname">Initial User's Last Name:</label>
919 <a href="#iuname_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
920 </div>
921 <div>
922 <input name='iuname' id='iuname' type='text' class='form-control' value='Administrator' />
923 </div>
924 </div>
925 <div id="iuname_info" class="collapse">
926 <a href="#iuname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
927 <p>This is the Last name of the 'initial user'.</p>
928 </div>
929 </div>
930 <div class="col-sm-4">
931 <div class="clearfix form-group">
932 <div class="label-div">
933 <label class="font-weight-bold" for="igroup">Initial Group:</label>
934 <a href="#igroup_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
935 </div>
936 <div>
937 <input name='igroup' id='igroup' class='form-control' type='text' value='Default' />
938 </div>
939 </div>
940 <div id="igroup_info" class="collapse">
941 <a href="#igroup_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
942 <p>This is the group that will be created for your users.</p>
943 <p>This should be the name of your practice.</p>
944 </div>
945 </div>
946 </div>
947 </fieldset>
948 <br />
949 <fieldset class='noclone py-2 bg-warning'>
950 <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>
951 <div class="ml-2 row">
952 <div class="col-sm-3">
953 <div class="clearfix form-group">
954 <div class="label-div">
955 <label class="font-weight-bold" for="i2fa">Configure 2FA:</label>
956 <a href="#i2fa_info" class="info-anchor icon-tooltip" data-toggle="collapse"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
957 </div>
958 <div>
959 <input name='i2faenable' id='i2faenable' type='checkbox' $disableCheckbox/> Enable 2FA
960 <input type='hidden' name='i2fasecret' id='i2fasecret' value='$randomsecret' />
961 </div>
962 </div>
963 <div id="i2fa_info" class="collapse">
964 <a href="#i2fa_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
965 <p>If selected will allow TOTP 2 factor authentication for the initial user.</p>
966 <p>Click on the help file for more information.</p>
967 </div>
968 </div>
969 <div class="col-sm-5">
970 <div class="clearfix form-group">
971 <p class="text-danger font-weight-bold">IMPORTANT IF ENABLED</p>
972 <p>If enabled, you must have an authenticator app on your phone ready to scan the QR code displayed next.</p>
973 </div>
974 </div>
975 <div class="col-sm-4">
976 <div class="clearfix form-group">
977 <p>Example authenticator apps include:</p>
978 <ul>
979 <li>Google Auth
980 (<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>
981 <li>Authy
982 (<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>
983 </ul>
984 </div>
985 </div>
986 </div>
987 </fieldset>
988 <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>
989 <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>
990 <div class="form-row">
991 <div class="col-12">
992 <button type='submit' id='create_db_button' value='Continue' class='wait btn btn-primary'>
993 <i class="fas fa-chevron-right"></i> Create DB and User
994 </button>
995 </div>
996 </div>
997 </form>
998 </div>
999 STP2TBLBOT;
1000 echo $step2tablebot . "\r\n";
1001 break;
1003 case 3:
1004 // Form Validation
1005 // (applicable if not cloning from another database)
1007 $pass_step2_validation = true;
1008 $error_step2_message = "$error - ";
1010 if (! $installer->char_is_valid($_REQUEST['server'])) {
1011 $pass_step2_validation = false;
1012 $error_step2_message .= "A database server host is required <br />\n";
1015 if (! $installer->char_is_valid($_REQUEST['port'])) {
1016 $pass_step2_validation = false;
1017 $error_step2_message .= "A database server port value is required <br />\n";
1020 if (! $installer->databaseNameIsValid($_REQUEST['dbname'])) {
1021 $pass_step2_validation = false;
1022 $error_step2_message .= "A database name is required <br />\n";
1025 if (! $installer->collateNameIsValid($_REQUEST['collate'])) {
1026 $pass_step2_validation = false;
1027 $error_step2_message .= "A collation name is required <br />\n";
1030 if (! $installer->char_is_valid($_REQUEST['login'])) {
1031 $pass_step2_validation = false;
1032 $error_step2_message .= "A database login name is required <br />\n";
1035 if (! $installer->char_is_valid($_REQUEST['pass'])) {
1036 $pass_step2_validation = false;
1037 $error_step2_message .= "A database login password is required <br />\n";
1040 if (!$pass_step2_validation) {
1041 $error_step2_message .= $error_page_end . "\r\n";
1042 die($error_step2_message);
1046 if (empty($installer->clone_database)) {
1047 if (! $installer->login_is_valid()) {
1048 echo "$error. Please pick a proper 'Login Name'.<br />\n";
1049 echo "Click Back in browser to re-enter.<br />\n";
1050 break;
1053 if (! $installer->iuser_is_valid()) {
1054 echo "$error. The 'Initial User' field can only contain one word and no spaces.<br />\n";
1055 echo "Click Back in browser to re-enter.<br />\n";
1056 break;
1059 if (! $installer->user_password_is_valid()) {
1060 echo "$error. Please pick a proper 'Initial User Password'.<br />\n";
1061 echo "Click Back in browser to re-enter.<br />\n";
1062 break;
1066 if (! $installer->password_is_valid()) {
1067 echo "$error. Please pick a proper 'Password'.<br />\n";
1068 echo "Click Back in browser to re-enter.<br />\n";
1069 break;
1072 echo "<h3 class='mb-3 border-bottom'>Step $state - Creating Database and First User</h3>";
1073 echo "<div class='jumbotron p-5'>";
1075 // Skip below if database shell has already been created.
1076 if ($inst != 2) {
1077 echo "Connecting to MySQL Server...\n";
1078 flush();
1079 if (! $installer->root_database_connection()) {
1080 echo "$error. Check your login credentials.\n";
1081 echo $installer->error_message;
1082 break;
1083 } else {
1084 echo "$ok.<br />\n";
1085 flush();
1089 // Only pertinent if cloning another installation database
1090 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1091 echo "Dumping source database...";
1092 flush();
1093 if (! $installer->create_dumpfiles()) {
1094 echo $installer->error_message;
1095 break;
1096 } else {
1097 echo "$ok.<br />\n";
1098 flush();
1102 // Only pertinent if mirroring another installation directory
1103 if (! empty($installer->source_site_id)) {
1104 echo "Creating site directory...";
1105 if (! $installer->create_site_directory()) {
1106 echo $installer->error_message;
1107 break;
1108 } else {
1109 echo "$ok.<br />";
1110 flush();
1114 // Skip below if database shell has already been created.
1115 if ($inst != 2) {
1116 echo "Creating database...\n";
1117 flush();
1118 if (! $installer->create_database()) {
1119 echo "$error. Check your login credentials.\n";
1120 echo $installer->error_message;
1121 break;
1122 } else {
1123 echo "$ok.<br />\n";
1124 flush();
1127 echo "Creating user with permissions for database...\n";
1128 flush();
1129 $user_mysql_error = true;
1130 if (! $installer->create_database_user()) {
1131 echo "$error when creating specified user.\n";
1132 echo $installer->error_message;
1133 break;
1134 } else {
1135 $user_mysql_error = false;
1137 if (! $installer->grant_privileges()) {
1138 echo "$error when granting privileges to the specified user.\n";
1139 echo $installer->error_message;
1140 break;
1141 } else {
1142 $user_mysql_error = false;
1144 if (!$user_mysql_error) {
1145 echo "$ok.<br />\n";
1146 flush();
1149 echo "Reconnecting as new user...\n";
1150 flush();
1151 $installer->disconnect();
1152 } else {
1153 echo "Connecting to MySQL Server...\n";
1156 if (! $installer->user_database_connection()) {
1157 echo "$error. Check your login credentials.\n";
1158 echo $installer->error_message;
1159 break;
1160 } else {
1161 echo "$ok.<br />\n";
1162 flush();
1165 // Load the database files
1166 $dump_results = $installer->load_dumpfiles();
1167 if (! $dump_results) {
1168 echo "$error.\n";
1169 echo $installer->error_message;
1170 break;
1171 } else {
1172 echo $dump_results;
1173 flush();
1176 echo "Writing SQL configuration...\n";
1177 flush();
1178 if (! $installer->write_configuration_file()) {
1179 echo "$error.\n";
1180 echo $installer->error_message;
1181 break;
1182 } else {
1183 echo "$ok.<br />\n";
1184 flush();
1187 // Only pertinent if not cloning another installation database
1188 if (empty($installer->clone_database)) {
1189 echo "Setting version indicators...\n";
1190 flush();
1191 if (! $installer->add_version_info()) {
1192 echo "$error.\n";
1193 echo $installer->error_message;
1195 break;
1196 } else {
1197 echo "$ok<br />\n";
1198 flush();
1201 echo "Writing global configuration defaults...\n";
1202 flush();
1203 if (! $installer->insert_globals()) {
1204 echo "$error.\n";
1205 echo $installer->error_message;
1207 break;
1208 } else {
1209 echo "$ok<br />\n";
1210 flush();
1213 echo "Adding Initial User...\n";
1214 flush();
1215 if (! $installer->add_initial_user()) {
1216 echo "$error.\n";
1217 echo $installer->error_message;
1218 break;
1220 echo "$ok<br />\n";
1221 flush();
1223 echo "Setting up Access Controls...\n";
1224 require("$OE_SITE_DIR/sqlconf.php");
1225 if (! $installer->install_gacl()) {
1226 echo "$error -.\n";
1227 echo $installer->error_message;
1228 break;
1229 } else {
1230 echo "$ok<br />\n";
1231 flush();
1235 // If user has selected to set MFA App Based 2FA, display QR code to scan
1236 $qr = $installer->get_initial_user_2fa_qr();
1237 if ($qr) {
1238 $qrDisplay = <<<TOTP
1239 <br />
1240 <table>
1241 <tr>
1242 <td>
1243 <strong class='text-danger'>IMPORTANT!!</strong>
1244 <p><strong>You must scan the following QR code with your preferred authenticator app.</strong></p>
1245 <img src='$qr' width="150" />
1246 </td>
1247 </tr>
1248 <tr>
1249 <td>
1250 Example authenticator apps include:
1251 <ul>
1252 <li>Google Auth
1253 (<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>
1254 <li>Authy
1255 (<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>
1256 </ul>
1257 </td>
1258 </tr>
1259 </table>
1260 TOTP;
1261 echo $qrDisplay;
1264 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1265 // Database was cloned, skip ACL setup.
1266 $btn_text = 'Proceed to Select a Theme';
1267 echo "<br />";
1268 echo "<p>The database was cloned, access control list exists therefore skipping ACL setup</p>";
1269 echo "<p class='p-1 bg-warning'>Click <b>$btn_text</b> for further instructions.</p>";
1270 $next_state = 7;
1271 } else {
1272 $btn_text = 'Proceed to Step 4';
1273 echo "<br />";
1274 echo "<p><b>Gave the <span class='text-primary'>$installer->iuser</span> user (password is <span class='text-primary'>$installer->iuserpass</span>) administrator access.</b></p>";
1275 echo "<p>The next step will configure php.</p>";
1276 echo "<p class='mark'>Click <strong>$btn_text</strong> to continue.</p>";
1277 $next_state = 4;
1280 $form_top = <<<FRMTOP
1281 <form method='post'>
1282 <input name='state' type='hidden' value='$next_state' />
1283 <input name='site' type='hidden' value='$site_id' />
1284 <input name='iuser' type='hidden' value='{$installer->iuser}' />
1285 <input name='iuserpass' type='hidden' value='{$installer->iuserpass}' />
1286 <input name='iuname' type='hidden' value='{$installer->iuname}' />
1287 <input name='iufname' type='hidden' value='{$installer->iufname}' />
1288 <input name='login' type='hidden' value='{$installer->login}' />
1289 <input name='pass' type='hidden' value='{$installer->pass}' />
1290 <input name='server' type='hidden' value='{$installer->server}' />
1291 <input name='port' type='hidden' value='{$installer->port}' />
1292 <input name='loginhost' type='hidden' value='{$installer->loginhost}' />
1293 <input name='dbname' type='hidden' value='{$installer->dbname}' />
1294 FRMTOP;
1295 echo $form_top . "\r\n";
1296 if ($allow_cloning_setup) {
1297 echo "<input type='hidden' name='clone_database' value='$installer->clone_database' />";
1298 echo "<input name='source_site_id' type='hidden' value='$installer->source_site_id' />";
1300 $form_bottom = <<<FRMBOT
1301 <div class="form-row">
1302 <div class="col-12">
1303 <button type='submit' id='step-4-btn' class="btn btn-primary" value='Continue'>
1304 <i class="fas fa-chevron-right"></i> $btn_text
1305 </button>
1306 </div>
1307 </div>
1308 </form>
1309 </div>
1310 FRMBOT;
1311 echo $form_bottom . "\r\n";
1312 break;
1314 case 4:
1315 $step4_top = <<<STP4TOP
1316 <h3 class="mb-3 border-bottom">Step $state - Configure PHP</h3>
1317 <div class="jumbotron p-5">
1318 <p>Configuration of PHP...</p>
1319 <p>We recommend making the following changes to your PHP installation, which can normally be done by editing the php.ini configuration file:</p>
1320 <ul>
1321 STP4TOP;
1322 echo $step4_top . "\r\n";
1324 $gotFileFlag = 0;
1325 $phpINIfile = php_ini_loaded_file();
1326 if ($phpINIfile) {
1327 echo "<li><span class='text-success'>Your php.ini file can be found at " . $phpINIfile . "</span></li>\n";
1328 $gotFileFlag = 1;
1331 $short_tag = ini_get('short_open_tag') ? 'On' : 'Off';
1332 $short_tag_style = (strcmp($short_tag, 'Off') === 0) ? '' : 'text-danger';
1333 $display_errors = ini_get('display_errors') ? 'On' : 'Off';
1334 $display_errors_style = (strcmp($display_errors, "Off") === 0) ? '' : 'text-danger';
1335 $register_globals = ini_get('register_globals') ? 'On' : 'Off';
1336 $register_globals_style = (strcmp($register_globals, 'Off') === 0) ? '' : 'text-danger';
1337 $max_input_vars = ini_get('max_input_vars');
1338 $max_input_vars_style = $max_input_vars < 3000 ? 'text-danger' : '';
1339 $max_execution_time = (int)ini_get('max_execution_time');
1340 $max_execution_time_style = $max_execution_time >= 60 || $max_execution_time === 0 ? '' : 'text-danger';
1341 $max_input_time = ini_get('max_input_time');
1342 $max_input_time_style = (strcmp($max_input_time, '-1') === 0) ? '' : 'text-danger';
1343 $post_max_size = ini_get('post_max_size');
1344 $post_max_size_style = $post_max_size < 30 ? 'text-danger' : '';
1345 $memory_limit = ini_get('memory_limit');
1346 $memory_limit_style = $memory_limit < 256 ? 'text-danger' : '';
1347 $mysqli_allow_local_infile = ini_get('mysqli.allow_local_infile') ? 'On' : 'Off';
1348 $mysqli_allow_local_infile_style = (strcmp($mysqli_allow_local_infile, 'On') === 0) ? '' : 'text-danger';
1350 $step4_table = <<<STP4TAB
1351 <li>To ensure proper functioning of OpenEMR you must make sure that PHP settings include:
1352 <table class='phpset'>
1353 <tr>
1354 <th>Setting</th>
1355 <th>Required value</th>
1356 <th>Current value</th>
1357 </tr>
1358 <tr>
1359 <td>short_open_tag</td>
1360 <td>Off</td>
1361 <td class='$short_tag_style'>$short_tag</td>
1362 </tr>
1363 <tr>
1364 <td>display_errors</td>
1365 <td>Off</td>
1366 <td class='$display_errors_style'>$display_errors</td>
1367 </tr>
1368 <tr>
1369 <td>register_globals</td>
1370 <td>Off</td>
1371 <td class='$register_globals_style'>$register_globals</td>
1372 </tr>
1373 <tr>
1374 <td>max_input_vars</td>
1375 <td>at least 3000</td>
1376 <td class='$max_input_vars_style'>$max_input_vars</td>
1377 </tr>
1378 <tr>
1379 <td>max_execution_time</td>
1380 <td>at least 60</td>
1381 <td class='$max_execution_time_style'>$max_execution_time</td>
1382 </tr>
1383 <tr>
1384 <td>max_input_time</td>
1385 <td>-1</td>
1386 <td class='$max_input_time_style'>$max_input_time</td>
1387 </tr>
1388 <tr>
1389 <td>post_max_size</td>
1390 <td>at least 30M</td>
1391 <td class='$post_max_size_style'>$post_max_size</td>
1392 </tr>
1393 <tr>
1394 <td>memory_limit</td>
1395 <td>at least 256M</td>
1396 <td class='$memory_limit_style'>$memory_limit</td>
1397 </tr>
1398 <tr>
1399 <td>mysqli.allow_local_infile</td>
1400 <td>On</td>
1401 <td class='$mysqli_allow_local_infile_style'>$mysqli_allow_local_infile</td>
1402 </tr>
1403 </table>
1404 </li>
1405 <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.
1406 </li>
1407 STP4TAB;
1408 echo $step4_table . "\r\n";
1410 if (!$gotFileFlag) {
1411 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";
1414 $btn_text = 'Proceed to Step 5';
1415 $step4_bottom = <<<STP4BOT
1416 </ul>
1418 <p>We recommend you print these instructions for future reference.</p>
1419 <p>The next step will configure the Apache web server.</p>
1420 <p class='mark'>Click <strong>$btn_text</strong> to continue.</p>
1421 <br />
1422 <form method='post'>
1423 <input type='hidden' name='state' value='5' />
1424 <input type='hidden' name='site' value='$site_id' />
1425 <input type='hidden' name='iuser' value='{$installer->iuser}' />
1426 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}' />
1427 <input name='login' type='hidden' value='{$installer->login}' />
1428 <input name='pass' type='hidden' value='{$installer->pass}' />
1429 <input name='server' type='hidden' value='{$installer->server}' />
1430 <input name='port' type='hidden' value='{$installer->port}' />
1431 <input name='loginhost' type='hidden' value='{$installer->loginhost}' />
1432 <input name='dbname' type='hidden' value='{$installer->dbname}' />
1433 <div class="form-row">
1434 <div class="col-12">
1435 <button type='submit' class='btn btn-primary' value='Continue'>
1436 <i class="fas fa-chevron-right"></i> $btn_text
1437 </button>
1438 </div>
1439 </div>
1440 </form>
1441 </div>
1442 STP4BOT;
1443 echo $step4_bottom . "\r\n";
1444 break;
1446 case 5:
1447 echo "<h3 class='mb-3 border-bottom'>Step $state - Configure Apache Web Server</h3>";
1448 echo "<div class='jumbotron p-5'>";
1449 echo "<p>Configuration of Apache web server...</p><br />\n";
1450 echo "The <code>\"" . preg_replace("/${site_id}/", "*", realpath($docsDirectory)) . "\"</code> directory contain patient information, and
1451 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 />
1452 &nbsp;&nbsp;<code>&lt;Directory \"" . realpath(dirname(__FILE__)) . "\"&gt;<br />
1453 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride FileInfo<br />
1454 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all granted<br />
1455 &nbsp;&nbsp;<code>&lt;/Directory&gt;</code><br />
1456 &nbsp;&nbsp;&lt;Directory \"" . realpath(dirname(__FILE__)) . "/sites\"&gt;<br />
1457 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride None<br />
1458 &nbsp;&nbsp;&lt;/Directory&gt;</code><br />
1459 &nbsp;&nbsp;<code>&lt;Directory \"" . preg_replace("/${site_id}/", "*", realpath($docsDirectory)) . "\"&gt;<br />
1460 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all denied<br />
1461 &nbsp;&nbsp;&lt;/Directory&gt;</code><br /><br />";
1463 $btn_text = 'Proceed to Select a Theme';
1464 $step5_bottom = <<<STP5BOT
1465 <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>
1466 <p>We recommend you print these instructions for future reference.</p>
1467 <p class='mark'>Click <strong>'$btn_text'</strong> to select a theme.</p>
1468 <br />
1469 <form method='post'>
1470 <input type='hidden' name='state' value='6' />
1471 <input type='hidden' name='site' value='$site_id' />
1472 <input type='hidden' name='iuser' value='{$installer->iuser}' />
1473 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}' />
1474 <input name='login' type='hidden' value='{$installer->login}' />
1475 <input name='pass' type='hidden' value='{$installer->pass}' />
1476 <input name='server' type='hidden' value='{$installer->server}' />
1477 <input name='port' type='hidden' value='{$installer->port}' />
1478 <input name='loginhost' type='hidden' value='{$installer->loginhost}' />
1479 <input name='dbname' type='hidden' value='{$installer->dbname}' />
1480 <div class="form-row">
1481 <div class="col-12">
1482 <button type='submit' class='btn btn-primary' value='Continue'>
1483 <i class="fas fa-chevron-right"></i> $btn_text
1484 </button>
1485 </div>
1486 </div>
1487 </form>
1488 <div>
1489 STP5BOT;
1490 echo $step5_bottom . "\r\n";
1491 break;
1493 case 6:
1494 echo "<h3 class='mb-3 border-bottom'>Step $state - Select a Theme</h3>";
1495 echo "<div class='jumbotron p-5'>";
1496 echo "<p>Select a theme for OpenEMR...</p>\n";
1497 $btn_text = "Proceed to Final Step";
1498 $installer->displaySelectedThemeDiv();
1499 $theme_form = <<<TMF
1500 <div class='row'>
1501 <div class="col-12">
1502 <form method='post'>
1503 <input type='hidden' name='state' value='7' />
1504 <input type='hidden' name='site' value='$site_id' />
1505 <input type='hidden' name='iuser' value='{$installer->iuser}' />
1506 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}' />
1507 <input name='login' type='hidden' value='{$installer->login}' />
1508 <input name='pass' type='hidden' value='{$installer->pass}' />
1509 <input name='server' type='hidden' value='{$installer->server}' />
1510 <input name='port' type='hidden' value='{$installer->port}' />
1511 <input name='loginhost' type='hidden' value='{$installer->loginhost}' />
1512 <input name='dbname' type='hidden' value='{$installer->dbname}' />
1513 <input type='hidden' name='new_theme' id = 'new_theme' value='{$installer->getCurrentTheme()}' />
1514 <input name='clone_database' type='hidden' value='{$installer->clone_database}' />
1515 <input name='source_site_id' type='hidden' value='{$installer->source_site_id}' />
1516 <h4>Select One:</h4>
1517 <div class="checkbox">
1518 <label><input type="checkbox" class="check" value="show_theme" />Show More Themes</label>
1519 </div>
1520 <div class="checkbox">
1521 <label><input type="checkbox" class="check" value="keep_current" />Keep Current</label>
1522 </div>
1523 <div class='hide_button' style="display:none;">
1524 <button type='submit' class='btn btn-primary' value='Continue' id='continue'>
1525 <i class="fas fa-chevron-right"></i> {$btn_text}
1526 </button>
1527 </div>
1528 </form>
1529 </div>
1530 </div>
1531 TMF;
1532 echo $theme_form . "\r\n";
1533 echo '<div class="row hideaway" style="display:none;">' . "\r\n";
1534 echo '<div class="col-12">' . "\r\n";
1535 echo ' <h4>Select New Theme: <h5>(scroll down to view all)</h5></h4>' . "\r\n";
1536 echo ' <br />' . "\r\n";
1537 $installer->displayThemesDivs();
1538 echo "</div>";
1539 break;
1541 case 0:
1542 default:
1543 $top = <<<TOP
1544 <h3 class="mb-3 border-bottom">Pre Install - Checking File and Directory Permissions</h3>
1545 <div class="jumbotron p-5">
1547 Welcome to OpenEMR. This utility will step you through the installation and configuration of OpenEMR for your practice.
1548 </p>
1549 <ul>
1550 <li>
1551 Before proceeding, be sure that you have a properly installed and configured MySQL server available, and a PHP configured webserver.
1552 </li>
1553 <li>
1554 <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>
1555 </li>
1556 <li>
1557 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.
1558 </li>
1559 </ul>
1560 TOP;
1561 echo $top;
1562 if ($checkPermissions) {
1563 echo "<p>We will now ensure correct file and directory permissions before starting installation:</p>\n";
1564 echo "<p class='text-success m-0'>Ensuring following file is world-writable...</p>\n";
1565 $errorWritable = 0;
1566 foreach ($writableFileList as $tempFile) {
1567 if (is_writable($tempFile)) {
1568 echo "<code class='ml-5'>" . realpath($tempFile) . "</code> file is <span class='text-success font-weight-bold'>ready</span><br /><br />\n";
1569 } else {
1570 echo "<p><span class='text-danger'>UNABLE</span> to open file '" . realpath($tempFile) . "' for writing.<br />\n";
1571 echo "(configure file permissions; see below for further instructions)</p>\n";
1572 $errorWritable = 1;
1576 if ($errorWritable) {
1577 $check_file = <<<CHKFILE
1578 <p class="text-danger">You can't proceed until all above files are ready (world-writable).</p>
1579 <p>In linux, recommend changing file permissions with the <strong>'chmod 666 filename'</strong> command.</p>
1580 <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>
1581 <br />
1582 <form method='post'>
1583 <input type='hidden' name='site' value='$site_id' />
1584 <button type='submit' class='btn btn-primary' value='check again'>Check Again</button>
1585 </form>
1586 CHKFILE;
1587 echo $check_file . "\r\n";
1588 break;
1591 $errorWritable = 0;
1592 foreach ($writableDirList as $tempDir) {
1593 echo "<p class='text-success m-0'>Ensuring the <code>" . realpath($tempDir) . "</code> directory and its subdirectories have proper permissions...</p>\n";
1594 $errorWritable = recursive_writable_directory_test($tempDir);
1597 if ($errorWritable) {
1598 $check_directory = <<<CHKDIR
1599 <p class="text-danger">You can't proceed until all directories and subdirectories are ready.</p>
1600 <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>
1601 <p class='p-1 bg-warning'>Fix above directory permissions and then click the <strong>'Check Again'</strong> button to re-check directories.</p>
1602 <br />
1603 <form method='post'>
1604 <input type='hidden' name='site' value='$site_id' />
1605 <button type='submit' value='check again'><b>Check Again</b></button>
1606 </form>
1607 CHKDIR;
1608 echo $check_directory . "\r\n";
1609 break;
1612 //RP_CHECK_LOGIC
1613 $form = <<<FRM
1614 <p>All required files and directories have been verified.</p>
1615 <p class='mark'>Click <span class="font-weight-bold">Proceed to Step 1</span> to continue with a new installation.</p>
1616 <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>
1617 <br />
1618 <form method='post'>
1619 <input name='state' type='hidden' value='1' />
1620 <input name='site' type='hidden' value='$site_id' />
1621 <div class="form-group">
1622 <div class="col">
1623 <button type='submit' class='btn btn-primary' value='Continue'>
1624 <i class="fas fa-chevron-right"></i> Proceed to Step 1
1625 </button>
1626 </div>
1627 </div>
1628 </form>
1629 FRM;
1630 echo $form . "\r\n";
1631 } else {
1632 echo "<br />Click to continue installation.<br />\n";
1636 $bot = <<<BOT
1637 </div>
1638 </div>
1639 </div>
1640 BOT;
1641 echo $bot . "\r\n";
1645 </div><!--end of container div -->
1646 <?php $installer->setupHelpModal();?>
1647 <script>
1648 //jquery-ui tooltip
1649 $(function () {
1650 $('.icon-tooltip').prop( "title", "Click to see more information").tooltip({
1651 show: {
1652 delay: 700,
1653 duration: 0
1656 $('.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();
1657 $('.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();
1661 </script>
1662 <script>
1663 $(function () {
1664 $("input[type='radio']").click(function() {
1665 var radioValue = $("input[name='stylesheet']:checked").val();
1666 var imgPath = "public/images/stylesheets/";
1667 var currStyle = $("#current_theme_title").text();
1668 var currStyleTitle = currStyle;
1669 currStyle = currStyle.replace(/\b\w/g, l => l.toLowerCase());
1670 currStyle = currStyle.split(" ");
1671 currStyle = currStyle.join("_");
1672 currStyle = "style_" + currStyle + ".png";
1673 if (radioValue) {
1674 var currThemeText = radioValue.split("_");
1675 currThemeText = currThemeText.join(" ");
1676 currThemeText = currThemeText.replace(/\b\w/g, l => l.toUpperCase());
1677 var styleSelected = confirm("You have selected style - " + currThemeText + "\n" + "Click OK to apply selection");
1678 if (styleSelected) {
1679 $("#current_theme").attr("src", imgPath + "style_" + radioValue + ".png");
1680 $("#current_theme_title").text(currThemeText);
1681 $("#new_theme").val("style_" + radioValue + ".css");
1682 } else {
1683 $("#current_theme").attr("src", imgPath + currStyle);
1684 $("#current_theme_title").text(currStyleTitle);
1685 $(this).prop("checked", false);
1689 $('.check').click(function() {
1690 $('.check').not(this).prop('checked', false);
1691 if($('.check:checked').val() == 'show_theme'){
1692 $(".hideaway").show();
1693 } else if($('.check:checked').val() == 'keep_current'){
1694 $(".hideaway").hide();
1697 if($('.check').filter(':checked').length > 0) {
1698 $(".hide_button").show();
1699 } else {
1700 $(".hide_button").hide();
1701 $(".hideaway").hide();
1704 $('.wait').removeClass('button-wait');
1706 $( "#create_db_button" ).hover(
1707 function() {
1708 if (($('#iuserpass' ).val().length > 11 && $('#iuser' ).val().length > 11 ) || ($('#clone_database').prop('checked'))){
1710 $("button").click(function(){
1711 $(".oe-spinner").css("visibility", "visible");
1714 $('.wait').click(function(){
1715 $('.wait').addClass('button-wait');
1721 $("#step-4-btn").click(function(){
1722 $(".oe-spinner").css("visibility", "visible");
1723 $(this).addClass('button-wait');
1726 </script>
1727 </body>
1728 </html>