Update NPM and Composer
[openemr.git] / setup.php
blob2434730eba005fb65c66b1940aecd8c078255c03
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 = "<FONT COLOR='red'>UNABLE</FONT> to open directory '" . realpath($failedDir) . "' for writing by web server.<br />\r\n";
78 } else {
79 $outputs[] = "<FONT COLOR='red'>UNABLE</FONT> 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 "'" . realpath($dir) . "' directory and its subdirectories are <FONT COLOR='green'><b>ready</b></FONT>.<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="interface/themes/style_blue.css">-->
125 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css" type="text/css">
126 <script type="text/javascript" src="public/assets/jquery/dist/jquery.min.js"></script>
127 <script type="text/javascript" src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
128 <link rel="stylesheet" href="public/assets/font-awesome/css/font-awesome.min.css" type="text/css">
129 <link rel="shortcut icon" href="public/images/favicon.ico" />
130 <style>
131 .oe-pull-away {
132 float:right;
134 </style>
135 </head>
136 <body>
137 <div class = 'mt-4 container'>
138 <div class="row">
139 <div class="row">
140 <div class="col-sm-12">
141 <div class="mb-3 border-bottom">
142 <h2>OpenEMR Setup <a class="oe-pull-away oe-help-redirect" data-target="#myModal" data-toggle="modal" href="#" id="help-href" name="help-href" style="color:#676666" title="Click to view Help"><i class="fa fa-question-circle" aria-hidden="true"></i></a></h2>
143 </div>
144 </div>
145 </div>
146 </div>
147 <div class="row">
148 <div class="col-sm-12">
149 <fieldset>
150 <legend class="mb-3 border-bottom">Optional Site ID Selection</legend>
151 <p>Most OpenEMR installations support only one site. If that is
152 true for you then ignore the rest of this text and just click Continue.</p>
153 <p class='p-1 bg-warning'>If you are using the multisite setup module for the first time please read the
154 'Multi Site Installation' section of the help file before proceeding.</p>
155 <p>Otherwise please enter a unique Site ID here.</p>
156 <p>A Site ID is a short identifier with no spaces or special
157 characters other than periods or dashes. It is case-sensitive and we
158 suggest sticking to lower case letters for ease of use.</p>
159 <p>If each site will have its own host/domain name, then use that
160 name as the Site ID (e.g. www.example.com).</p>
161 <p>The site ID is used to identify which site you will log in to.
162 If it is a hostname then it is taken from the hostname in the URL.
163 Otherwise you must append "?site=<i>siteid</i>" to the URL used for
164 logging in.</p>
165 <p>It is OK for one of the sites to have "default" as its ID. This
166 is the ID that will be used if it cannot otherwise be determined.</p>
167 <br />
168 <form method='post'>
169 <input type='hidden' name='state' value='0'>
170 Site ID: <input type='text' name='site' value='default'>
171 <button type='submit' value='Continue'>Continue</button>
172 </form>
173 </fieldset>
174 </div>
175 </div>
176 </div><!--end of container div-->
177 SITEID;
178 echo $site_id . "\r\n";
179 $installer->setupHelpModal();
180 echo "</body>" . "\r\n";
181 echo "</html>" . "\r\n";
183 exit();
186 // Support "?site=siteid" in the URL, otherwise assume "default".
187 $site_id = 'default';
188 if (!$COMMAND_LINE && !empty($_REQUEST['site'])) {
189 $site_id = trim($_REQUEST['site']);
192 // Die if site ID is empty or has invalid characters.
193 if (empty($site_id) || preg_match('/[^A-Za-z0-9\\-.]/', $site_id)) {
194 die("Site ID '" . htmlspecialchars($site_id, ENT_NOQUOTES) . "' contains invalid characters.");
197 // If multisite is turned off, then only allow default for site.
198 if (!$allow_multisite_setup && $site_id != 'default') {
199 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");
202 //If having problems with file and directory permission
203 // checking, then can be manually disabled here.
204 $checkPermissions = true;
206 global $OE_SITE_DIR; // The Installer sets this
208 $docsDirectory = "$OE_SITE_DIR/documents";
210 //These are files and dir checked before install for
211 // correct permissions.
212 if (is_dir($OE_SITE_DIR)) {
213 $writableFileList = array($installer->conffile);
214 $writableDirList = array($docsDirectory);
215 } else {
216 $writableFileList = array();
217 $writableDirList = array($OE_SITES_BASE);
220 // Include the sqlconf file if it exists yet.
221 $config = 0;
222 if (file_exists($OE_SITE_DIR)) {
223 include_once($installer->conffile);
224 } elseif ($state > 3) {
225 // State 3 should have created the site directory if it is missing.
226 die("Internal error, site directory is missing.");
229 <html>
230 <head>
231 <title>OpenEMR Setup Tool</title>
232 <!--<link rel=stylesheet href="interface/themes/style_blue.css">-->
233 <link rel="stylesheet" href="public/assets/bootstrap/dist/css/bootstrap.min.css" type="text/css">
234 <script type="text/javascript" src="public/assets/jquery/dist/jquery.min.js"></script>
235 <script type="text/javascript" src="public/assets/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
236 <link rel="stylesheet" href="public/assets/font-awesome/css/font-awesome.min.css" type="text/css">
237 <link rel="shortcut icon" href="public/images/favicon.ico" />
239 <style>
240 .noclone { }
241 table.phpset {
242 border-collapse:collapse;
244 table.phpset td, table.phpset th {
245 font-size:9pt;
246 border:1px solid gray;
247 padding:2px;
249 .table.no-border tr td, .table.no-border tr th {
250 border-width: 0;
252 td {
253 font-size:10pt;
255 .inputtext {
256 padding-left:2px;
257 padding-right:2px;
260 .button {
261 font-family:sans-serif;
262 font-size:9pt;
263 font-weight:bold;
266 .label-div > a {
267 display:none;
269 .label-div:hover > a {
270 display:inline-block;
272 div[id$="_info"] {
273 background: #F7FAB3;
274 padding: 20px;
275 margin: 10px 15px 0px 15px;
277 div[id$="_info"] > a {
278 margin-left:10px;
280 .checkboxgroup {
281 display: inline-block;
282 text-align: center;
284 .checkboxgroup label {
285 display: block;
287 .oe-pull-away{
288 float:right;
290 .oe-help-x {
291 color: grey;
292 padding: 0 5px;
294 .oe-superscript {
295 position: relative;
296 top: -.5em;
297 font-size: 70%!important;
299 .oe-setup-legend{
300 background-color: WHITESMOKE;
301 padding:0 10px;
303 button {
304 font-weight:bold;
306 .button-wait {
307 color: grey;
308 cursor: not-allowed;
309 opacity: 0.6;
311 @media only screen {
312 fieldset > [class*="col-"] {
313 width: 100%;
314 text-align:left!Important;
317 </style>
318 <script language="javascript">
319 // onclick handler for "clone database" checkbox
320 function cloneClicked() {
321 var cb = document.forms[0].clone_database;
322 $('.noclone').css('display', cb.checked ? 'none' : 'block');
324 </script>
326 </head>
327 <body>
328 <div class = 'mt-4 container'>
329 <div class="row">
330 <div class="col-sm-12">
331 <div class="mb-3 border-bottom">
332 <h2>OpenEMR Setup <a class="oe-pull-away oe-help-redirect" data-target="#myModal" data-toggle="modal" href="#" id="help-href" name="help-href" style="color:#676666" title="Click to view Help"><i class="fa fa-question-circle" aria-hidden="true"></i></a></h2>
333 </div>
334 </div>
335 </div>
336 <div class="row">
337 <div class="col-sm-12">
338 <?php
339 $error = "<span class='text-danger'><b>ERROR</b></span>";
340 $caution = "<span class='text-danger'><b>CAUTION</b></span>";
341 $ok = "<span class='text-success'><b>OK</b></span>";
342 $note = "<span class='text-primary'><b>NOTE</b></span>";
344 if (strtolower(ini_get('register_globals')) != 'off' && (bool) ini_get('register_globals')) {
345 echo "$caution: It appears that you have register_globals enabled in your php.ini\n" .
346 "configuration file. This causes unacceptable security risks. You must\n" .
347 "turn it off before continuing with installation.\n";
348 exit(1);
351 if (!extension_loaded("xml")) {
352 echo "$error: PHP XML extension missing. To continue, install PHP XML extension, then restart web server.";
353 exit(1);
356 if (!(extension_loaded("mysql") || extension_loaded("mysqlnd") || extension_loaded("mysqli"))) {
357 echo "$error: PHP MySQL extension missing. To continue, install and enable MySQL extension, then restart web server.";
358 exit(1);
361 if (!(extension_loaded("mbstring") )) {
362 echo "$error: PHP mb_string extension missing. To continue, install and enable mb_string extension, then restart web server.";
363 exit(1);
366 if (!(extension_loaded("openssl") )) {
367 echo "$error: PHP openssl extension missing. To continue, install PHP openssl extension, then restart web server.";
368 exit(1);
372 <?php
373 if ($state == 8) {
376 <fieldset>
377 <legend class="mb-3 border-bottom">Final step - Success</legend>
378 <p>Congratulations! OpenEMR is now installed.</p>
380 <ul>
381 <li>Access controls (php-GACL) are installed for fine-grained security, and can be administered in
382 OpenEMR's admin->acl menu.</li>
383 <li>Reviewing <?php echo $OE_SITE_DIR; ?>/config.php is a good idea. This file
384 contains some settings that you may want to change.</li>
385 <li>There's much information and many extra tools bundled within the OpenEMR installation directory.
386 Please refer to openemr/Documentation. Many forms and other useful scripts can be found at openemr/contrib.</li>
387 <li>To ensure a consistent look and feel throughout the application,
388 <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>
389 <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>
390 <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>
391 </ul>
392 <p>We recommend you print these instructions for future reference.</p>
393 <?php
394 echo "<p> The selected theme is :</p>";
395 $installer->displayNewThemeDiv();
396 if (empty($installer->clone_database)) {
397 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>";
398 } else {
399 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>";
401 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>";
402 echo "<p>In Linux use the following command:</p>";
403 echo "<p><code>sudo apachectl -k restart</code></p>";
407 <a href='./?site=<?php echo $site_id; ?>'>Click here to start using OpenEMR. </a>
408 </p>
409 </fieldset>
410 <?php
411 $installer->setCurrentTheme();
413 $end_div = <<<ENDDIV
414 </div>
415 </div>
416 </div><!--end of container div-->
417 ENDDIV;
418 echo $end_div . "\r\n";
419 $installer->setupHelpModal();
420 echo "</body>" . "\r\n";
421 echo "</html>" . "\r\n";
423 exit();
427 <?php
429 $inst = isset($_POST["inst"]) ? ($_POST["inst"]) : '';
431 if (($config == 1) && ($state < 4)) {
432 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";
433 } else {
434 switch ($state) {
435 case 1:
436 $step1 = <<<STP1
437 <fieldset>
438 <legend class="mb-3 border-bottom">Step $state - Select Database Setup</legend>
439 <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.
440 <br />
441 <p class='p-1 bg-warning'>$caution: clicking on <b>Proceed to Step 2</b> may delete or cause damage to existing data on your system. Before you continue <b>please backup your data</b>.
442 <br />
443 <form method='post'>
444 <input name='state' type='hidden' value='2'>
445 <input name='site' type='hidden' value='$site_id'>
446 <label for='inst1'>
447 <input checked id='inst1' name='inst' type='radio' value='1'>Have setup create the database
448 </label><br />
449 <label for='inst2'>
450 <input id='inst2' name='inst' type='radio' value='2'>I have already created the database
451 </label><br />
452 <br />
453 <button type='submit' value='Continue'><b>Proceed to Step 2</b></button>
454 </form><br />
455 </fieldset>
456 STP1;
457 echo $step1 . "\r\n";
458 break;
460 case 2:
461 $step2top = <<<STP2TOP
462 <fieldset>
463 <legend class="mb-3 border-bottom">Step $state - Database and OpenEMR Initial User Setup Details</legend>
464 <p>Now you need to supply the MySQL server information and path information. Detailed instructions on each item can be found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><span STYLE='text-decoration: underline;'>'INSTALL'</span></a> manual file.
465 <br /><br />
466 <form method='post' id='myform'>
467 <input name='state' type='hidden' value='3'>
468 <input name='site' type='hidden' value='$site_id'>
469 <input name='inst' type='hidden' value='$inst'>
470 STP2TOP;
471 echo $step2top . "\r\n";
474 $step2tabletop1 = <<<STP2TBLTOP1
475 <fieldset>
476 <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>
477 <div class="ml-2 row">
478 <div class="col-sm-4">
479 <div class="clearfix form-group">
480 <div class="label-div">
481 <label class="font-weight-bold" for="server">Server Host:</label> <a href="#server_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
482 </div>
483 <div>
484 <input name='server' id='server' type='text' class='form-control' value='localhost'>
486 </div>
487 </div>
488 <div id="server_info" class="collapse">
489 <a href="#server_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
490 <p>If you run MySQL and Apache/PHP on the same computer, then leave this as 'localhost'.
491 <p>If they are on separate computers, then enter the IP address of the computer running MySQL.
493 </div>
494 </div>
495 <div class="col-sm-4">
496 <div class="clearfix form-group">
497 <div class="label-div">
498 <label class="font-weight-bold" for="port">Server Port:</label> <a href="#port_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
499 </div>
500 <div>
501 <input name='port' id='port' type='text' class='form-control' value='3306'>
502 </div>
503 </div>
504 <div id="port_info" class="collapse">
505 <a href="#port_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
506 <p>This is the MySQL port.
507 <p>The default port for MySQL is 3306.
508 </div>
509 </div>
510 <div class="col-sm-4">
511 <div class="clearfix form-group">
512 <div class="label-div">
513 <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>
514 </div>
515 <div>
516 <input name='dbname' id='dbname' type='text' class='form-control' value='openemr'>
517 </div>
518 </div>
519 <div id="dbname_info" class="collapse">
520 <a href="#dbname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
521 <p>This will be the name of the OpenEMR database in MySQL.
522 <p>'openemr' is the recommended name.
523 <p>This database will contain patient data as well as data pertaining to the OpenEMR installation.
524 </div>
525 </div>
526 </div>
527 <div class="ml-2 row">
528 <div class="col-sm-4">
529 <div class="clearfix form-group">
530 <div class="label-div">
531 <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>
532 </div>
533 <div>
534 <input name='login' ID='login' type='text' class='form-control' value='openemr'>
536 </div>
537 </div>
538 <div id="login_info" class="collapse">
539 <a href="#login_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
540 <p>This is the name that OpenEMR will use to login to the MySQL database.
541 <p>'openemr' is the recommended name.
542 </div>
543 </div>
544 <div class="col-sm-4">
545 <div class="clearfix form-group">
546 <div class="label-div">
547 <label class="font-weight-bold" for="pass">Password:</label> <a href="#pass_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
548 </div>
549 <div>
550 <input name='pass' id='pass' class='form-control' type='password' value='' required>
551 </div>
552 </div>
553 <div id="pass_info" class="collapse">
554 <a href="#pass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
555 <p>This is the Login Password that OpenEMR will use to accesses the MySQL database.
556 <p>It should be at least 12 characters long and composed of both numbers and letters.
557 </div>
558 </div>
559 STP2TBLTOP1;
560 echo $step2tabletop1 . "\r\n";
561 if ($inst != 2) {
562 $step2tabletop2 = <<<STP2TBLTOP2
563 <div class="col-sm-4">
564 <div class="clearfix form-group">
565 <div class="label-div">
566 <label class="font-weight-bold" for="root">Name for Root Account:</label> <a href="#root_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
567 </div>
568 <div>
569 <input name='root' id='root' type='text' class='form-control' value='root'>
570 </div>
571 </div>
572 <div id="root_info" class="collapse">
573 <a href="#root_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
574 <p>This is name for the MySQL root account.
575 <p>For localhost, it is usually ok to leave it as 'root'.
576 </div>
577 </div>
578 </div>
579 <div class="ml-2 row">
580 <div class="col-sm-4">
581 <div class="clearfix form-group">
582 <div class="label-div">
583 <label class="font-weight-bold" for="rootpass">Root Password:</label> <a href="#rootpass_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
584 </div>
585 <div>
586 <input name='rootpass' id='rootpass' type='password' class='form-control' value=''>
588 </div>
589 </div>
590 <div id="rootpass_info" class="collapse">
591 <a href="#rootpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
592 <p>This is your MySQL server root password.
593 </div>
594 </div>
595 <div class="col-sm-4">
596 <div class="clearfix form-group">
597 <div class="label-div">
598 <label class="font-weight-bold" for="loginhost">User Hostname:</label> <a href="#loginhost_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
599 </div>
600 <div>
601 <input name='loginhost' id='loginhost' type='text' class='form-control' value='localhost'>
602 </div>
603 </div>
604 <div id="loginhost_info" class="collapse">
605 <a href="#loginhost_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
606 <p>If you run Apache/PHP and MySQL on the same computer, then leave this as 'localhost'.
607 <p>If they are on separate computers, then enter the IP address of the computer running Apache/PHP.
608 </div>
609 </div>
610 <div class="col-sm-4">
611 <div class="clearfix form-group">
612 <div class="label-div">
613 <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>
614 </div>
615 <div>
616 <select name='collate' id=='collate' class='form-control'>
617 <option value='utf8_bin'>
619 </option>
620 <option value='utf8_czech_ci'>
621 Czech
622 </option>
623 <option value='utf8_danish_ci'>
624 Danish
625 </option>
626 <option value='utf8_esperanto_ci'>
627 Esperanto
628 </option>
629 <option value='utf8_estonian_ci'>
630 Estonian
631 </option>
632 <option selected value='utf8_general_ci'>
633 General
634 </option>
635 <option value='utf8_hungarian_ci'>
636 Hungarian
637 </option>
638 <option value='utf8_icelandic_ci'>
639 Icelandic
640 </option>
641 <option value='utf8_latvian_ci'>
642 Latvian
643 </option>
644 <option value='utf8_lithuanian_ci'>
645 Lithuanian
646 </option>
647 <option value='utf8_persian_ci'>
648 Persian
649 </option>
650 <option value='utf8_polish_ci'>
651 Polish
652 </option>
653 <option value='utf8_roman_ci'>
654 Roman
655 </option>
656 <option value='utf8_romanian_ci'>
657 Romanian
658 </option>
659 <option value='utf8_slovak_ci'>
660 Slovak
661 </option>
662 <option value='utf8_slovenian_ci'>
663 Slovenian
664 </option>
665 <option value='utf8_spanish2_ci'>
666 Spanish2 (Traditional)
667 </option>
668 <option value='utf8_spanish_ci'>
669 Spanish (Modern)
670 </option>
671 <option value='utf8_swedish_ci'>
672 Swedish
673 </option>
674 <option value='utf8_turkish_ci'>
675 Turkish
676 </option>
677 <option value='utf8_unicode_ci'>
678 Unicode (German, French, Russian, Armenian, Greek)
679 </option>
680 <option value=''>
681 None (Do not force UTF-8)
682 </option>
683 </select>
684 </div>
685 </div>
686 <div id="collate_info" class="collapse">
687 <a href="#collate_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
688 <p>This is the collation setting for MySQL.
689 <p>Collation refers to a set of rules that determine how data is sorted and compared in a database.
690 <p>Leave as 'General' if you are not sure.
691 <p>If the language you are planning to use in OpenEMR is in the menu, then you can select it.
692 <p>Otherwise, just select 'General'.
693 </div>
694 </div>
695 </div>
696 STP2TBLTOP2;
697 echo $step2tabletop2 . "\r\n";
699 // Include a "source" site ID drop-list and a checkbox to indicate
700 // if cloning its database. When checked, do not display initial user
701 // and group stuff below.
702 $dh = opendir($OE_SITES_BASE);
703 if (!$dh) {
704 die("Cannot read directory '$OE_SITES_BASE'.");
707 $siteslist = array();
708 while (false !== ($sfname = readdir($dh))) {
709 if (substr($sfname, 0, 1) == '.') {
710 continue;
713 if ($sfname == 'CVS') {
714 continue;
717 if ($sfname == $site_id) {
718 continue;
721 $sitedir = "$OE_SITES_BASE/$sfname";
722 if (!is_dir($sitedir)) {
723 continue;
726 if (!is_file("$sitedir/sqlconf.php")) {
727 continue;
730 $siteslist[$sfname] = $sfname;
733 closedir($dh);
734 // If this is not the first site...
735 if (!empty($siteslist)) {
736 ksort($siteslist);
737 $source_site_top = <<<SOURCESITETOP
738 <div class="ml-2 row">
739 <div class="col-sm-4">
740 <div class="clearfix form-group">
741 <div class="label-div">
742 <label class="font-weight-bold" for="source_site_id">Source Site:</label> <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>
743 </div>
744 <div>
745 <select name='source_site_id'id='source_site_id' class='form-control'>
746 SOURCESITETOP;
747 echo $source_site_top . "\r\n";
748 foreach ($siteslist as $sfname) {
749 echo "<option value='$sfname'";
750 if ($sfname == 'default') {
751 echo " selected";
754 echo ">$sfname</option>";
756 $source_site_bot = <<<SOURCESITEBOT
757 </select>
759 </div>
760 </div>
761 <div id="source_site_id_info" class="collapse">
762 <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>
763 <p>The site directory that will be a model for the new site.
764 </div>
765 </div>
766 <div class="col-sm-4">
767 <div class="clearfix form-group">
768 <div class="label-div">
769 <label class="font-weight-bold" for="clone_database">Clone Source Database:</label> <a href="#clone_database_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
770 </div>
771 <div>
772 <input type='checkbox' name='clone_database' id='clone_database' onclick='cloneClicked()' />
773 </div>
774 </div>
775 <div id="clone_database_info" class="collapse">
776 <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>
777 <p>Clone the source site's database instead of creating a fresh one.
778 </div>
779 </div>
780 </div>
781 SOURCESITEBOT;
782 echo $source_site_bot . "\r\n";
785 $randomusernamepre = RandomGenUtils::produceRandomString(3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
786 $randomusernamepost = RandomGenUtils::produceRandomString(2, "0123456789");
787 $randomusername = $randomusernamepre . "-admin-" . $randomusernamepost;
789 // App Based TOTP secret
790 // Shared key (per rfc6238 and rfc4226) should be 20 bytes (160 bits) and encoded in base32, which should
791 // be 32 characters in base32
792 // Would be nice to use the OpenEMR\Common\Utils\RandomGenUtils\produceRandomBytes() function and then encode to base32,
793 // but does not appear to be a standard way to encode binary to base32 in php.
794 $randomsecret = RandomGenUtils::produceRandomString(32, "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ");
795 if (empty($randomsecret) || empty($randomusernamepre) || empty($randomusernamepost)) {
796 error_log('OpenEMR Error : Random String error - exiting');
797 die();
799 $disableCheckbox = "";
800 if (empty($randomsecret)) {
801 $randomsecret = "";
802 $disableCheckbox = "disabled";
805 $step2tablebot = <<<STP2TBLBOT
806 </fieldset>
807 <br />
808 <fieldset class='noclone'>
809 <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>
810 <div class="ml-2 row">
811 <div class="col-sm-4">
812 <div class="clearfix form-group">
813 <div class="label-div">
814 <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>
815 </div>
816 <div>
817 <input name='iuser' id='iuser' type='text' class='form-control' value='$randomusername' minlength='12'>
819 </div>
820 </div>
821 <div id="iuser_info" class="collapse">
822 <a href="#iuser_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
823 <p>This is the login name of the first user that will be created for you.
824 <p>Limit this to one word with at least 12 characters and composed of both numbers and letters.
826 </div>
827 </div>
828 <div class="col-sm-4">
829 <div class="clearfix form-group">
830 <div class="label-div">
831 <label class="font-weight-bold" for="iuserpass">Initial User Password:</label> <a href="#iuserpass_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
832 </div>
833 <div>
834 <input name='iuserpass' id='iuserpass' type='password' class='form-control' value='' minlength='12'>
835 </div>
836 </div>
837 <div id="iuserpass_info" class="collapse">
838 <a href="#iuserpass_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
839 <p>This is the password for the initial user.
840 </div>
841 </div>
842 <div class="col-sm-4">
843 <div class="clearfix form-group">
844 <div class="label-div">
845 <label class="font-weight-bold" for="iufname">Initial User's First Name:</label> <a href="#iufname_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
846 </div>
847 <div>
848 <input name='iufname' id='iufname 'type='text' class='form-control' value='Administrator'>
849 </div>
850 </div>
851 <div id="iufname_info" class="collapse">
852 <a href="#iufname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
853 <p>This is the First name of the 'initial user'.
854 </div>
855 </div>
856 </div>
857 <div class="ml-2 row">
858 <div class="col-sm-4">
859 <div class="clearfix form-group">
860 <div class="label-div">
861 <label class="font-weight-bold" for="iuname">Initial User's Last Name:</label> <a href="#iuname_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
862 </div>
863 <div>
864 <input name='iuname' id='iuname' type='text' class='form-control' value='Administrator'>
866 </div>
867 </div>
868 <div id="iuname_info" class="collapse">
869 <a href="#iuname_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
870 <p>This is the Last name of the 'initial user'.
871 </div>
872 </div>
873 <div class="col-sm-4">
874 <div class="clearfix form-group">
875 <div class="label-div">
876 <label class="font-weight-bold" for="igroup">Initial Group:</label> <a href="#igroup_info" class="info-anchor icon-tooltip" data-toggle="collapse" ><i class="fa fa-question-circle" aria-hidden="true"></i></a>
877 </div>
878 <div>
879 <input name='igroup' id='igroup' class='form-control' type='text' value='Default'>
880 </div>
881 </div>
882 <div id="igroup_info" class="collapse">
883 <a href="#igroup_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
884 <p>This is the group that will be created for your users.
885 <p>This should be the name of your practice.
886 </div>
887 </div>
888 </div>
889 </fieldset>
890 <br />
891 <fieldset class='noclone py-2 bg-warning'>
892 <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>
893 <div class="ml-2 row">
894 <div class="col-sm-3">
895 <div class="clearfix form-group">
896 <div class="label-div">
897 <label class="font-weight-bold" for="i2fa">Configure 2FA:</label> <a href="#i2fa_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 <input name='i2faenable' id='i2faenable' type='checkbox' $disableCheckbox/> Enable 2FA
901 <input type='hidden' name='i2fasecret' id='i2fasecret' value='$randomsecret' />
902 </div>
903 </div>
904 <div id="i2fa_info" class="collapse">
905 <a href="#i2fa_info" data-toggle="collapse" class="oe-pull-away"><i class="fa fa-times oe-help-x" aria-hidden="true"></i></a>
906 <p>If selected will allow TOTP 2 factor authentication for the initial user.</p>
907 <p>Click on the help file for more information.</p>
908 </div>
909 </div>
910 <div class="col-sm-5">
911 <div class="clearfix form-group">
912 <p class="text-danger font-weight-bold">IMPORTANT IF ENABLED</p>
913 <p>If enabled, you must have an authenticator app on your phone ready to scan the QR code displayed next.</p>
914 </div>
915 </div>
916 <div class="col-sm-4">
917 <div class="clearfix form-group">
918 <p>Example authenticator apps include:</p>
919 <ul>
920 <li>Google Auth
921 (<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>
922 <li>Authy
923 (<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>
924 </ul>
925 </div>
926 </div>
927 </div>
928 </fieldset>
929 <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>
930 <!--<p class='p-1 bg-success text-white'>Upon successful completion will automatically take you to the next step.</p>-->
931 <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>
932 <button type='submit' id='create_db_button' value='Continue' class='wait'><b>Create DB and User</b></button>
933 </form>
934 </fieldset>
935 STP2TBLBOT;
936 echo $step2tablebot . "\r\n";
937 break;
939 case 3:
940 // Form Validation
941 // (applicable if not cloning from another database)
943 $pass_step2_validation = true;
944 $error_step2_message = "$error - ";
946 if (! $installer->char_is_valid($_REQUEST['server'])) {
947 $pass_step2_validation = false;
948 $error_step2_message .= "A database server host is required <br />\n";
951 if (! $installer->char_is_valid($_REQUEST['port'])) {
952 $pass_step2_validation = false;
953 $error_step2_message .= "A database server port value is required <br />\n";
956 if (! $installer->databaseNameIsValid($_REQUEST['dbname'])) {
957 $pass_step2_validation = false;
958 $error_step2_message .= "A database name is required <br />\n";
961 if (! $installer->collateNameIsValid($_REQUEST['collate'])) {
962 $pass_step2_validation = false;
963 $error_step2_message .= "A collation name is required <br />\n";
966 if (! $installer->char_is_valid($_REQUEST['login'])) {
967 $pass_step2_validation = false;
968 $error_step2_message .= "A database login name is required <br />\n";
971 if (! $installer->char_is_valid($_REQUEST['pass'])) {
972 $pass_step2_validation = false;
973 $error_step2_message .= "A database login password is required <br />\n";
976 if (!$pass_step2_validation) {
977 $error_step2_message .= $error_page_end . "\r\n";
978 die($error_step2_message);
982 if (empty($installer->clone_database)) {
983 if (! $installer->login_is_valid()) {
984 echo "$error. Please pick a proper 'Login Name'.<br />\n";
985 echo "Click Back in browser to re-enter.<br />\n";
986 break;
989 if (! $installer->iuser_is_valid()) {
990 echo "$error. The 'Initial User' field can only contain one word and no spaces.<br />\n";
991 echo "Click Back in browser to re-enter.<br />\n";
992 break;
995 if (! $installer->user_password_is_valid()) {
996 echo "$error. Please pick a proper 'Initial User Password'.<br />\n";
997 echo "Click Back in browser to re-enter.<br />\n";
998 break;
1002 if (! $installer->password_is_valid()) {
1003 echo "$error. Please pick a proper 'Password'.<br />\n";
1004 echo "Click Back in browser to re-enter.<br />\n";
1005 break;
1008 echo "<fieldset>";
1009 echo "<legend class='mb-3 border-bottom'>Step $state - Creating Database and First User</legend>";
1011 // Skip below if database shell has already been created.
1012 if ($inst != 2) {
1013 echo "Connecting to MySQL Server...\n";
1014 flush();
1015 if (! $installer->root_database_connection()) {
1016 echo "$error. Check your login credentials.\n";
1017 echo $installer->error_message;
1018 break;
1019 } else {
1020 echo "$ok.<br />\n";
1021 flush();
1025 // Only pertinent if cloning another installation database
1026 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1027 echo "Dumping source database...";
1028 flush();
1029 if (! $installer->create_dumpfiles()) {
1030 echo $installer->error_message;
1031 break;
1032 } else {
1033 echo "$ok.<br />\n";
1034 flush();
1038 // Only pertinent if mirroring another installation directory
1039 if (! empty($installer->source_site_id)) {
1040 echo "Creating site directory...";
1041 if (! $installer->create_site_directory()) {
1042 echo $installer->error_message;
1043 break;
1044 } else {
1045 echo "$ok.<br />";
1046 flush();
1050 // Skip below if database shell has already been created.
1051 if ($inst != 2) {
1052 echo "Creating database...\n";
1053 flush();
1054 if (! $installer->create_database()) {
1055 echo "$error. Check your login credentials.\n";
1056 echo $installer->error_message;
1057 break;
1058 } else {
1059 echo "$ok.<br />\n";
1060 flush();
1063 echo "Creating user with permissions for database...\n";
1064 flush();
1065 $user_mysql_error = true;
1066 if (! $installer->create_database_user()) {
1067 echo "$error when creating specified user.\n";
1068 echo $installer->error_message;
1069 break;
1070 } else {
1071 $user_mysql_error = false;
1073 if (! $installer->grant_privileges()) {
1074 echo "$error when granting privileges to the specified user.\n";
1075 echo $installer->error_message;
1076 break;
1077 } else {
1078 $user_mysql_error = false;
1080 if (!$user_mysql_error) {
1081 echo "$ok.<br />\n";
1082 flush();
1085 echo "Reconnecting as new user...\n";
1086 flush();
1087 $installer->disconnect();
1088 } else {
1089 echo "Connecting to MySQL Server...\n";
1092 if (! $installer->user_database_connection()) {
1093 echo "$error. Check your login credentials.\n";
1094 echo $installer->error_message;
1095 break;
1096 } else {
1097 echo "$ok.<br />\n";
1098 flush();
1101 // Load the database files
1102 $dump_results = $installer->load_dumpfiles();
1103 if (! $dump_results) {
1104 echo "$error.\n";
1105 echo $installer->error_message;
1106 break;
1107 } else {
1108 echo $dump_results;
1109 flush();
1112 echo "Writing SQL configuration...\n";
1113 flush();
1114 if (! $installer->write_configuration_file()) {
1115 echo "$error.\n";
1116 echo $installer->error_message;
1117 break;
1118 } else {
1119 echo "$ok.<br />\n";
1120 flush();
1123 // Only pertinent if not cloning another installation database
1124 if (empty($installer->clone_database)) {
1125 echo "Setting version indicators...\n";
1126 flush();
1127 if (! $installer->add_version_info()) {
1128 echo "$error.\n";
1129 echo $installer->error_message;
1131 break;
1132 } else {
1133 echo "$ok<br />\n";
1134 flush();
1137 echo "Writing global configuration defaults...\n";
1138 flush();
1139 if (! $installer->insert_globals()) {
1140 echo "$error.\n";
1141 echo $installer->error_message;
1143 break;
1144 } else {
1145 echo "$ok<br />\n";
1146 flush();
1149 echo "Adding Initial User...\n";
1150 flush();
1151 if (! $installer->add_initial_user()) {
1152 echo "$error.\n";
1153 echo $installer->error_message;
1154 break;
1157 echo "$ok<br />\n";
1158 flush();
1162 // If user has selected to set MFA App Based 2FA, display QR code to scan
1163 $qr = $installer->get_initial_user_2fa_qr();
1164 if ($qr) {
1165 $qrDisplay = <<<TOTP
1166 <br />
1167 <table>
1168 <tr>
1169 <td>
1170 <strong><font color='RED'>IMPORTANT!!</font></strong>
1171 <p><strong>You must scan the following QR code with your preferred authenticator app.</strong></p>
1172 <img src='$qr' width="150" />
1173 </td>
1174 </tr>
1175 <tr>
1176 <td>
1177 Example authenticator apps include:
1178 <ul>
1179 <li>Google Auth
1180 (<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>
1181 <li>Authy
1182 (<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>
1183 </ul>
1184 </td>
1185 </tr>
1186 </table>
1187 TOTP;
1188 echo $qrDisplay;
1191 if ($allow_cloning_setup && !empty($installer->clone_database)) {
1192 // Database was cloned, skip ACL setup.
1193 $btn_text = 'Proceed to Select a Theme';
1194 echo "<br />";
1195 echo "<p>The database was cloned, access control list exists therefore skipping ACL setup</p>";
1196 echo "<p class='p-1 bg-warning'>Click <b>$btn_text</b> for further instructions.</p>";
1197 $next_state = 7;
1198 } else {
1199 $btn_text = 'Proceed to Step 4';
1200 echo "<br />";
1201 echo "<p class='mark'>Click <b>$btn_text</b> to install and configure access controls (php-GACL). $note: This process can take a few minutes.</p>";
1202 echo "<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>";
1203 $next_state = 4;
1206 $form_top = <<<FRMTOP
1207 <form method='post'>
1208 <input name='state' type='hidden' value='$next_state'>
1209 <input name='site' type='hidden' value='$site_id'>
1210 <input name='iuser' type='hidden' value='{$installer->iuser}'>
1211 <input name='iuserpass' type='hidden' value='{$installer->iuserpass}'>
1212 <input name='iuname' type='hidden' value='{$installer->iuname}'>
1213 <input name='iufname' type='hidden' value='{$installer->iufname}'>
1214 <input name='login' type='hidden' value='{$installer->login}'>
1215 <input name='pass' type='hidden' value='{$installer->pass}'>
1216 <input name='server' type='hidden' value='{$installer->server}'>
1217 <input name='port' type='hidden' value='{$installer->port}'>
1218 <input name='loginhost' type='hidden' value='{$installer->loginhost}'>
1219 <input name='dbname' type='hidden' value='{$installer->dbname}'>
1220 FRMTOP;
1221 echo $form_top . "\r\n";
1222 if ($allow_cloning_setup) {
1223 echo "<input type='hidden' name='clone_database' value='$installer->clone_database'>";
1224 echo "<input name='source_site_id' type='hidden' value='$installer->source_site_id'>";
1226 $form_bottom = <<<FRMBOT
1227 <button type='submit' id='step-4-btn' value='Continue' class='wait'><b>$btn_text</b></button>
1228 <br />
1229 </form>
1230 </fieldset>
1231 FRMBOT;
1232 echo $form_bottom . "\r\n";
1233 break;
1234 case 4:
1235 $step4_top = <<<STP4TOP
1236 <fieldset>
1237 <legend class="mb-3 border-bottom">Step $state - Creating and Configuring Access Control List</legend>
1238 <p>Installing and Configuring Access Controls (php-GACL)...</p><br />
1239 STP4TOP;
1240 echo $step4_top . "\r\n";
1241 if (! $installer->install_gacl()) {
1242 echo "$error -.\n";
1243 echo $installer->error_message;
1244 break;
1245 } else {
1246 // display the status information for gacl setup
1247 echo $installer->debug_message;
1249 $btn_text = 'Proceed to Step 5';
1250 $step4_bottom = <<<STP4BOT
1251 <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>
1252 <p>Done installing and configuring access controls (php-gacl).</p>
1253 <p>The next step will configure php.</p>
1254 <p class='mark'>Click <strong>$btn_text</strong> to continue.</p>
1255 <br />
1256 <form method='post'>
1257 <input name='state' type='hidden' value='5'>
1258 <input name='site' type='hidden' value='$site_id'>
1259 <input name='iuser' type='hidden' value='{$installer->iuser}'>
1260 <input name='iuserpass' type='hidden' value='{$installer->iuserpass}'>
1261 <input name='login' type='hidden' value='{$installer->login}'>
1262 <input name='pass' type='hidden' value='{$installer->pass}'>
1263 <input name='server' type='hidden' value='{$installer->server}'>
1264 <input name='port' type='hidden' value='{$installer->port}'>
1265 <input name='loginhost' type='hidden' value='{$installer->loginhost}'>
1266 <input name='dbname' type='hidden' value='{$installer->dbname}'>
1267 <button type='submit' value='Continue'><b>$btn_text</b></button>
1268 </form>
1269 </fieldset>
1270 STP4BOT;
1271 echo $step4_bottom . "\r\n";
1272 break;
1274 case 5:
1275 $step5_top = <<<STP5TOP
1276 <fieldset>
1277 <legend class="mb-3 border-bottom">Step $state - Configure PHP</legend>
1278 <p>Configuration of PHP...</p><br />
1279 <p>We recommend making the following changes to your PHP installation, which can normally be done by editing the php.ini configuration file:</p>
1280 <ul>
1281 STP5TOP;
1282 echo $step5_top . "\r\n";
1284 $gotFileFlag = 0;
1285 $phpINIfile = php_ini_loaded_file();
1286 if ($phpINIfile) {
1287 echo "<li><font color='green'>Your php.ini file can be found at " . $phpINIfile . "</font></li>\n";
1288 $gotFileFlag = 1;
1291 $short_tag = ini_get('short_open_tag') ? 'On' : 'Off';
1292 $display_errors = ini_get('display_errors') ? 'On' : 'Off';
1293 $register_globals = ini_get('register_globals') ? 'On' : 'Off';
1294 $max_input_vars = ini_get('max_input_vars');
1295 $max_execution_time = ini_get('max_execution_time');
1296 $max_input_time = ini_get('max_input_time');
1297 $post_max_size = ini_get('post_max_size');
1298 $memory_limit = ini_get('memory_limit');
1299 $mysqli_allow_local_infile = ini_get('mysqli.allow_local_infile') ? 'On' : 'Off';
1301 $step5_table = <<<STP5TAB
1302 <li>To ensure proper functioning of OpenEMR you must make sure that PHP settings include:
1303 <table class='phpset'>
1304 <tr>
1305 <th>Setting</th>
1306 <th>Required value</th>
1307 <th>Current value</th>
1308 </tr>
1309 <tr>
1310 <td>short_open_tag</td>
1311 <td>Off</td>
1312 <td>$short_tag</td>
1313 </tr>
1314 <tr>
1315 <td>display_errors</td>
1316 <td>Off</td>
1317 <td>$display_errors</td>
1318 </tr>
1319 <tr>
1320 <td>register_globals</td>
1321 <td>Off</td>
1322 <td>$register_globals</td>
1323 </tr>
1324 <tr>
1325 <td>max_input_vars</td>
1326 <td>at least 3000</td>
1327 <td>$max_input_vars</td>
1328 </tr>
1329 <tr>
1330 <td>max_execution_time</td>
1331 <td>at least 60</td>
1332 <td>$max_execution_time</td>
1333 </tr>
1334 <tr>
1335 <td>max_input_time</td>
1336 <td>-1</td>
1337 <td>$max_input_time</td>
1338 </tr>
1339 <tr>
1340 <td>post_max_size</td>
1341 <td>at least 30M</td>
1342 <td>$post_max_size</td>
1343 </tr>
1344 <tr>
1345 <td>memory_limit</td>
1346 <td>at least 256M</td>
1347 <td>$memory_limit</td>
1348 </tr>
1349 <tr>
1350 <td>mysqli.allow_local_infile</td>
1351 <td>On</td>
1352 <td>$mysqli_allow_local_infile</td>
1353 </tr>
1354 </table>
1355 </li>
1356 <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.
1357 </li>
1358 STP5TAB;
1359 echo $step5_table . "\r\n";
1361 if (!$gotFileFlag) {
1362 echo "<li>If you are having difficulty finding your php.ini file, then refer to the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><span STYLE='text-decoration: underline;'>'INSTALL'</span></a> manual for suggestions.</li>\n";
1365 $btn_text = 'Proceed to Step 6';
1366 $step5_bottom = <<<STP5BOT
1367 </ul>
1369 <p>We recommend you print these instructions for future reference.</p>
1370 <p>The next step will configure the Apache web server.</p>
1371 <p class='mark'>Click <strong>$btn_text</strong> to continue.</p>
1372 <br />
1373 <form method='post'>
1374 <input type='hidden' name='state' value='6'>
1375 <input type='hidden' name='site' value='$site_id'>
1376 <input type='hidden' name='iuser' value='{$installer->iuser}'>
1377 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}'>
1378 <input name='login' type='hidden' value='{$installer->login}'>
1379 <input name='pass' type='hidden' value='{$installer->pass}'>
1380 <input name='server' type='hidden' value='{$installer->server}'>
1381 <input name='port' type='hidden' value='{$installer->port}'>
1382 <input name='loginhost' type='hidden' value='{$installer->loginhost}'>
1383 <input name='dbname' type='hidden' value='{$installer->dbname}'>
1384 <button type='submit' value='Continue'><b>$btn_text</b></button>
1385 </form>
1386 </fieldset>
1387 STP5BOT;
1388 echo $step5_bottom . "\r\n";
1389 break;
1391 case 6:
1392 echo "<fieldset>";
1393 echo "<legend class='mb-3 border-bottom'>Step $state - Configure Apache Web Server</legend>";
1394 echo "<p>Configuration of Apache web server...</p><br />\n";
1395 echo "The <strong>\"" . preg_replace("/${site_id}/", "*", realpath($docsDirectory)) . "\"</strong> directory contain patient information, and
1396 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 />
1397 &nbsp;&nbsp;&lt;Directory \"" . realpath(dirname(__FILE__)) . "\"&gt;<br />
1398 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride FileInfo<br />
1399 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all granted<br />
1400 &nbsp;&nbsp;&lt;/Directory&gt;<br />
1401 &nbsp;&nbsp;&lt;Directory \"" . realpath(dirname(__FILE__)) . "/sites\"&gt;<br />
1402 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AllowOverride None<br />
1403 &nbsp;&nbsp;&lt;/Directory&gt;<br />
1404 &nbsp;&nbsp;&lt;Directory \"" . preg_replace("/${site_id}/", "*", realpath($docsDirectory)) . "\"&gt;<br />
1405 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Require all denied<br />
1406 &nbsp;&nbsp;&lt;/Directory&gt;<br /><br />";
1408 $btn_text = 'Proceed to Select a Theme';
1409 $step6_bottom = <<<STP6BOT
1410 <p>If you are having difficulty finding your apache configuration file, then refer to the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><span style='text-decoration: underline;'>'INSTALL'</span></a> manual for suggestions.</p>
1411 <p>We recommend you print these instructions for future reference.</p>
1412 <p class='mark'>Click <strong>'$btn_text'</strong> to select a theme.</p>
1413 <br />
1414 <form method='post'>
1415 <input type='hidden' name='state' value='7'>
1416 <input type='hidden' name='site' value='$site_id'>
1417 <input type='hidden' name='iuser' value='{$installer->iuser}'>
1418 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}'>
1419 <input name='login' type='hidden' value='{$installer->login}'>
1420 <input name='pass' type='hidden' value='{$installer->pass}'>
1421 <input name='server' type='hidden' value='{$installer->server}'>
1422 <input name='port' type='hidden' value='{$installer->port}'>
1423 <input name='loginhost' type='hidden' value='{$installer->loginhost}'>
1424 <input name='dbname' type='hidden' value='{$installer->dbname}'>
1425 <button type='submit' value='Continue'><b>$btn_text</b></button>
1426 </form>
1427 <fieldset>
1428 STP6BOT;
1429 echo $step6_bottom . "\r\n";
1430 break;
1432 case 7:
1433 echo "<fieldset>";
1434 echo "<legend class='mb-3 border-bottom'>Step $state - Select a Theme</legend>";
1435 echo "<p>Select a theme for OpenEMR...</p><br />\n";
1436 $btn_text = "Proceed to Final Step";
1437 $installer->displaySelectedThemeDiv();
1438 $theme_form = <<<TMF
1439 <div class='row'>
1440 <div class="col-sm-4 offset-sm-4">
1441 <form method='post'>
1442 <input type='hidden' name='state' value='8'>
1443 <input type='hidden' name='site' value='$site_id'>
1444 <input type='hidden' name='iuser' value='{$installer->iuser}'>
1445 <input type='hidden' name='iuserpass' value='{$installer->iuserpass}'>
1446 <input name='login' type='hidden' value='{$installer->login}'>
1447 <input name='pass' type='hidden' value='{$installer->pass}'>
1448 <input name='server' type='hidden' value='{$installer->server}'>
1449 <input name='port' type='hidden' value='{$installer->port}'>
1450 <input name='loginhost' type='hidden' value='{$installer->loginhost}'>
1451 <input name='dbname' type='hidden' value='{$installer->dbname}'>
1452 <input type='hidden' name='new_theme' id = 'new_theme' value='{$installer->getCurrentTheme()}'>
1453 <input name='clone_database' type='hidden' value='{$installer->clone_database}'>
1454 <input name='source_site_id' type='hidden' value='{$installer->source_site_id}'>
1455 <h4>Select One:</h4>
1456 <div class="checkbox">
1457 <label><input type="checkbox" class="check" value="show_theme">Show More Themes</label>
1458 </div>
1459 <div class="checkbox">
1460 <label><input type="checkbox" class="check" value="keep_current">Keep Current</label>
1461 </div>
1462 <div class='hide_button' style="display:none;">
1463 <button type='submit' value='Continue' id='continue'>{$btn_text}</button>
1464 </div>
1465 </form>
1466 </div>
1467 </div>
1468 </fieldset>
1469 TMF;
1470 echo $theme_form . "\r\n";
1471 echo '<div class="row hideaway" style="display:none;">' . "\r\n";
1472 echo '<div class="col-sm-12">' . "\r\n";
1473 echo ' <h4>Select New Theme: <h5>(scroll down to view all)</h5></h4>' . "\r\n";
1474 echo ' <br />' . "\r\n";
1475 $installer->displayThemesDivs();
1476 break;
1478 case 0:
1479 default:
1480 $top = <<<TOP
1481 <fieldset>
1482 <legend class="mb-3 border-bottom">Pre Install - Checking File and Directory Permissions</legend>
1483 <p><span class="text">Welcome to OpenEMR. This utility will step you through the installation and configuration of OpenEMR for your practice.</span></p>
1484 <ul>
1485 <li><span class="text">Before proceeding, be sure that you have a properly installed and configured MySQL server available, and a PHP configured webserver.</span></li>
1486 <li><span class="mark">Detailed installation instructions can be found in the <a href='Documentation/INSTALL' rel='noopener' target='_blank'><span style='text-decoration: underline;'>'INSTALL'</span></a> manual file.</span></li>
1487 <li>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.
1488 </li>
1489 </ul>
1490 TOP;
1491 echo $top;
1492 if ($checkPermissions) {
1493 echo "<p>We will now ensure correct file and directory permissions before starting installation:</p>\n";
1494 echo "<FONT COLOR='green'>Ensuring following file is world-writable...</FONT><br />\n";
1495 $errorWritable = 0;
1496 foreach ($writableFileList as $tempFile) {
1497 if (is_writable($tempFile)) {
1498 echo "'" . realpath($tempFile) . "' file is <FONT COLOR='green'><b>ready</b></FONT>.<br />\n";
1499 } else {
1500 echo "<p><FONT COLOR='red'>UNABLE</FONT> to open file '" . realpath($tempFile) . "' for writing.<br />\n";
1501 echo "(configure file permissions; see below for further instructions)</p>\n";
1502 $errorWritable = 1;
1506 if ($errorWritable) {
1507 $check_file = <<<CHKFILE
1508 <p style="font-color:red;">You can't proceed until all above files are ready (world-writable).</p>
1509 <p>In linux, recommend changing file permissions with the <strong>'chmod 666 filename'</strong> command.</p>
1510 <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>
1511 <br />
1512 <form method='post'>
1513 <input type='hidden' name='site' value='$site_id'>
1514 <button type='submit' value='check again'><b>Check Again</b></button>
1515 </form>
1516 CHKFILE;
1517 echo $check_file . "\r\n";
1518 break;
1521 $errorWritable = 0;
1522 foreach ($writableDirList as $tempDir) {
1523 echo "<br /><FONT COLOR='green'>Ensuring the '" . realpath($tempDir) . "' directory and its subdirectories have proper permissions...</FONT><br />\n";
1524 $errorWritable = recursive_writable_directory_test($tempDir);
1527 if ($errorWritable) {
1528 $check_directory = <<<CHKDIR
1529 <p style="font-color:red;">You can't proceed until all directories and subdirectories are ready.</p>
1530 <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>
1531 <p class='p-1 bg-warning'>Fix above directory permissions and then click the <strong>'Check Again'</strong> button to re-check directories.</p>
1532 <br />
1533 <form method='post'>
1534 <input type='hidden' name='site' value='$site_id'>
1535 <button type='submit' value='check again'><b>Check Again</b></button>
1536 </form>
1537 CHKDIR;
1538 echo $check_directory . "\r\n";
1539 break;
1542 //RP_CHECK_LOGIC
1543 $form = <<<FRM
1544 <br />
1545 <p>All required files and directories have been verified.</p>
1546 <p class='mark'>Click <b>Proceed to Step 1</b> to continue with a new installation.</p>
1547 <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>
1548 <br />
1549 <form method='post'>
1550 <input name='state' type='hidden' value='1'>
1551 <input name='site' type='hidden' value='$site_id'>
1552 <button type='submit' value='Continue'><b>Proceed to Step 1</b></button>
1553 </form>
1554 FRM;
1555 echo $form . "\r\n";
1556 } else {
1557 echo "<br />Click to continue installation.<br />\n";
1561 $bot = <<<BOT
1562 </div>
1563 </div>
1564 BOT;
1565 echo $bot . "\r\n";
1569 </div><!--end of container div -->
1570 <?php $installer->setupHelpModal();?>
1571 <script>
1572 //jquery-ui tooltip
1573 $(function () {
1574 $('.icon-tooltip').prop( "title", "Click to see more information").tooltip({
1575 show: {
1576 delay: 700,
1577 duration: 0
1580 $('.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();
1581 $('.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();
1585 </script>
1586 <script type = "text/javascript" >
1587 $(function () {
1588 $("input[type='radio']").click(function() {
1589 var radioValue = $("input[name='stylesheet']:checked").val();
1590 var imgPath = "public/images/stylesheets/";
1591 var currStyle = $("#current_theme_title").text();
1592 var currStyleTitle = currStyle;
1593 currStyle = currStyle.replace(/\b\w/g, l => l.toLowerCase());
1594 currStyle = currStyle.split(" ");
1595 currStyle = currStyle.join("_");
1596 currStyle = "style_" + currStyle + ".png";
1597 if (radioValue) {
1598 var currThemeText = radioValue.split("_");
1599 currThemeText = currThemeText.join(" ");
1600 currThemeText = currThemeText.replace(/\b\w/g, l => l.toUpperCase());
1601 var styleSelected = confirm("You have selected style - " + currThemeText + "\n" + "Click OK to apply selection");
1602 if (styleSelected) {
1603 $("#current_theme").attr("src", imgPath + "style_" + radioValue + ".png");
1604 $("#current_theme_title").text(currThemeText);
1605 $("#new_theme").val("style_" + radioValue + ".css");
1606 } else {
1607 $("#current_theme").attr("src", imgPath + currStyle);
1608 $("#current_theme_title").text(currStyleTitle);
1609 $(this).prop("checked", false);
1613 $('.check').click(function() {
1614 $('.check').not(this).prop('checked', false);
1615 if($('.check:checked').val() == 'show_theme'){
1616 $(".hideaway").show();
1617 } else if($('.check:checked').val() == 'keep_current'){
1618 $(".hideaway").hide();
1621 if($('.check').filter(':checked').length > 0) {
1622 $(".hide_button").show();
1623 } else {
1624 $(".hide_button").hide();
1625 $(".hideaway").hide();
1628 $('.wait').removeClass('button-wait');
1630 $( "#create_db_button" ).hover(
1631 function() {
1632 if (($('#iuserpass' ).val().length > 11 && $('#iuser' ).val().length > 11 ) || ($('#clone_database').prop('checked'))){
1634 $("button").click(function(){
1635 $(".oe-spinner").css("visibility", "visible");
1638 $('.wait').click(function(){
1639 $('.wait').addClass('button-wait');
1645 $("#step-4-btn").click(function(){
1646 $(".oe-spinner").css("visibility", "visible");
1647 $(this).addClass('button-wait');
1650 </script>
1651 </body>
1652 </html>