fix: set default x12 partner for item in billing manager (#7502)
[openemr.git] / library / classes / Installer.class.php
blob319d14dad11e4025f5ed3ef89bbad36d462aede9
1 <?php
3 /**
5 * Installer class.
7 * @package OpenEMR
8 * @link https://www.open-emr.org
9 * @author Andrew Moore <amoore@cpan.org>
10 * @author Ranganath Pathak <pathak@scrs1.org>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @copyright Copyright (c) 2010 Andrew Moore <amoore@cpan.org>
13 * @copyright Copyright (c) 2019 Ranganath Pathak <pathak@scrs1.org>
14 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
15 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
18 use OpenEMR\Gacl\GaclApi;
20 class Installer
22 public $iuser;
23 public $iuserpass;
24 public $iuname;
25 public $iufname;
26 public $igroup;
27 public $i2faEnable;
28 public $i2faSecret;
29 public $server;
30 public $loginhost;
31 public $port;
32 public $root;
33 public $rootpass;
34 public $login;
35 public $pass;
36 public $dbname;
37 public $collate;
38 public $site;
39 public $source_site_id;
40 public $clone_database;
41 public $no_root_db_access;
42 public $development_translations;
43 public $new_theme;
44 public $ippf_specific;
45 public $conffile;
46 public $main_sql;
47 public $translation_sql;
48 public $devel_translation_sql;
49 public $ippf_sql;
50 public $icd9;
51 public $cvx;
52 public $additional_users;
53 public $dumpfiles;
54 public $error_message;
55 public $debug_message;
56 public $dbh;
58 public function __construct($cgi_variables)
60 // Installation variables
61 // For a good explanation of these variables, see documentation in
62 // the contrib/util/installScripts/InstallerAuto.php file.
63 $this->iuser = isset($cgi_variables['iuser']) ? ($cgi_variables['iuser']) : '';
64 $this->iuserpass = isset($cgi_variables['iuserpass']) ? ($cgi_variables['iuserpass']) : '';
65 $this->iuname = isset($cgi_variables['iuname']) ? ($cgi_variables['iuname']) : '';
66 $this->iufname = isset($cgi_variables['iufname']) ? ($cgi_variables['iufname']) : '';
67 $this->igroup = isset($cgi_variables['igroup']) ? ($cgi_variables['igroup']) : '';
68 $this->i2faEnable = isset($cgi_variables['i2faenable']) ? ($cgi_variables['i2faenable']) : '';
69 $this->i2faSecret = isset($cgi_variables['i2fasecret']) ? ($cgi_variables['i2fasecret']) : '';
70 $this->server = isset($cgi_variables['server']) ? ($cgi_variables['server']) : ''; // mysql server (usually localhost)
71 $this->loginhost = isset($cgi_variables['loginhost']) ? ($cgi_variables['loginhost']) : ''; // php/apache server (usually localhost)
72 $this->port = isset($cgi_variables['port']) ? ($cgi_variables['port']) : '';
73 $this->root = isset($cgi_variables['root']) ? ($cgi_variables['root']) : '';
74 $this->rootpass = isset($cgi_variables['rootpass']) ? ($cgi_variables['rootpass']) : '';
75 $this->login = isset($cgi_variables['login']) ? ($cgi_variables['login']) : '';
76 $this->pass = isset($cgi_variables['pass']) ? ($cgi_variables['pass']) : '';
77 $this->dbname = isset($cgi_variables['dbname']) ? ($cgi_variables['dbname']) : '';
78 $this->collate = isset($cgi_variables['collate']) ? ($cgi_variables['collate']) : '';
79 $this->site = isset($cgi_variables['site']) ? ($cgi_variables['site']) : 'default'; // set to default if not set in order for install script to work correctly
80 $this->source_site_id = isset($cgi_variables['source_site_id']) ? ($cgi_variables['source_site_id']) : '';
81 $this->clone_database = isset($cgi_variables['clone_database']) ? ($cgi_variables['clone_database']) : '';
82 $this->no_root_db_access = isset($cgi_variables['no_root_db_access']) ? ($cgi_variables['no_root_db_access']) : ''; // no root access to database. user/privileges pre-configured
83 $this->development_translations = isset($cgi_variables['development_translations']) ? ($cgi_variables['development_translations']) : '';
84 $this->new_theme = isset($cgi_variables['new_theme']) ? ($cgi_variables['new_theme']) : '';
85 // Make this true for IPPF.
86 $this->ippf_specific = false;
88 // Record name of sql access file
89 $GLOBALS['OE_SITES_BASE'] = dirname(__FILE__) . '/../../sites';
90 $GLOBALS['OE_SITE_DIR'] = $GLOBALS['OE_SITES_BASE'] . '/' . $this->site;
91 $this->conffile = $GLOBALS['OE_SITE_DIR'] . '/sqlconf.php';
93 // Record names of sql table files
94 $this->main_sql = dirname(__FILE__) . '/../../sql/database.sql';
95 $this->translation_sql = dirname(__FILE__) . '/../../contrib/util/language_translations/currentLanguage_utf8.sql';
96 $this->devel_translation_sql = "http://translations.openemr.io/languageTranslations_utf8.sql";
97 $this->ippf_sql = dirname(__FILE__) . "/../../sql/ippf_layout.sql";
98 $this->icd9 = dirname(__FILE__) . "/../../sql/icd9.sql";
99 $this->cvx = dirname(__FILE__) . "/../../sql/cvx_codes.sql";
100 $this->additional_users = dirname(__FILE__) . "/../../sql/official_additional_users.sql";
102 // Prepare the dumpfile list
103 $this->initialize_dumpfile_list();
105 // Entities to hold error and debug messages
106 $this->error_message = '';
107 $this->debug_message = '';
109 // Entity to hold sql connection
110 $this->dbh = false;
113 public function login_is_valid()
115 if (($this->login == '') || (! isset($this->login))) {
116 $this->error_message = "login is invalid: '$this->login'";
117 return false;
120 return true;
123 public function char_is_valid($input_text)
125 // to prevent php injection
126 trim($input_text);
127 if ($input_text == '') {
128 return false;
131 if (preg_match('@[\\\\;()<>/\'"]@', $input_text)) {
132 return false;
135 return true;
138 public function databaseNameIsValid($name)
140 if (preg_match('/[^A-Za-z0-9_-]/', $name)) {
141 return false;
143 return true;
146 public function collateNameIsValid($name)
148 if (preg_match('/[^A-Za-z0-9_-]/', $name)) {
149 return false;
151 return true;
154 public function iuser_is_valid()
156 if (strpos($this->iuser, " ")) {
157 $this->error_message = "Initial user is invalid: '$this->iuser'";
158 return false;
161 return true;
164 public function iuname_is_valid()
166 if ($this->iuname == "" || !isset($this->iuname)) {
167 $this->error_message = "Initial user last name is invalid: '$this->iuname'";
168 return false;
171 return true;
174 public function password_is_valid()
176 if ($this->pass == "" || !isset($this->pass)) {
177 $this->error_message = "The password for the new database account is invalid: '$this->pass'";
178 return false;
181 return true;
184 public function user_password_is_valid()
186 if ($this->iuserpass == "" || !isset($this->iuserpass)) {
187 $this->error_message = "The password for the user is invalid: '$this->iuserpass'";
188 return false;
191 return true;
196 public function root_database_connection()
198 $this->dbh = $this->connect_to_database($this->server, $this->root, $this->rootpass, $this->port);
199 if ($this->dbh) {
200 if (!$this->set_sql_strict()) {
201 $this->error_message = 'unable to set strict sql setting';
202 return false;
205 return true;
206 } else {
207 $this->error_message = 'unable to connect to database as root';
208 return false;
212 public function user_database_connection()
214 $this->dbh = $this->connect_to_database($this->server, $this->login, $this->pass, $this->port, $this->dbname);
215 if (! $this->dbh) {
216 $this->error_message = "unable to connect to database as user: '$this->login'";
217 return false;
220 if (! $this->set_sql_strict()) {
221 $this->error_message = 'unable to set strict sql setting';
222 return false;
225 if (! $this->set_collation()) {
226 $this->error_message = 'unable to set sql collation';
227 return false;
230 if (! mysqli_select_db($this->dbh, $this->dbname)) {
231 $this->error_message = "unable to select database: '$this->dbname'";
232 return false;
235 return true;
238 public function create_database()
240 $sql = "create database " . $this->escapeDatabaseName($this->dbname);
241 if (empty($this->collate) || ($this->collate == 'utf8_general_ci')) {
242 $this->collate = 'utf8mb4_general_ci';
244 $sql .= " character set utf8mb4 collate " . $this->escapeCollateName($this->collate);
245 $this->set_collation();
247 return $this->execute_sql($sql);
250 public function drop_database()
252 $sql = "drop database if exists " . $this->escapeDatabaseName($this->dbname);
253 return $this->execute_sql($sql);
256 public function create_database_user()
258 // First, check for database user in the mysql.user table (this works for all except mariadb 10.4+)
259 $checkUser = $this->execute_sql("SELECT user FROM mysql.user WHERE user = '" . $this->escapeSql($this->login) . "' AND host = '" . $this->escapeSql($this->loginhost) . "'", false);
260 if ($checkUser === false) {
261 // Above caused error, so is MariaDB 10.4+, and need to do below query instead in the mysql.global_priv table
262 $checkUser = $this->execute_sql("SELECT user FROM mysql.global_priv WHERE user = '" . $this->escapeSql($this->login) . "' AND host = '" . $this->escapeSql($this->loginhost) . "'");
265 if ($checkUser === false) {
266 // there was an error in the check database user query, so return false
267 return false;
268 } elseif ($checkUser->num_rows > 0) {
269 // the mysql user already exists, so do not need to create the user, but need to set the password
270 // Note need to try two different methods, first is for newer mysql versions and second is for older mysql versions (if the first method fails)
271 $returnSql = $this->execute_sql("ALTER USER '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "' IDENTIFIED BY '" . $this->escapeSql($this->pass) . "'", false);
272 if ($returnSql === false) {
273 error_log("Using older mysql version method to set password for the mysql user");
274 $returnSql = $this->execute_sql("SET PASSWORD FOR '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "' = PASSWORD('" . $this->escapeSql($this->pass) . "')");
276 return $returnSql;
277 } else {
278 // the mysql user does not yet exist, so create the user
279 if (getenv('FORCE_DATABASE_X509_CONNECT', true) == 1) {
280 // this use case is to allow enforcement of x509 database connection use in applicable docker and kubernetes auto installations
281 return $this->execute_sql("CREATE USER '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "' IDENTIFIED BY '" . $this->escapeSql($this->pass) . "' REQUIRE X509");
282 } elseif (getenv('FORCE_DATABASE_SSL_CONNECT', true) == 1) {
283 // this use case is to allow enforcement of ssl database connection use in applicable docker and kubernetes auto installations
284 return $this->execute_sql("CREATE USER '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "' IDENTIFIED BY '" . $this->escapeSql($this->pass) . "' REQUIRE SSL");
285 } else {
286 return $this->execute_sql("CREATE USER '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "' IDENTIFIED BY '" . $this->escapeSql($this->pass) . "'");
291 public function grant_privileges()
293 return $this->execute_sql("GRANT ALL PRIVILEGES ON " . $this->escapeDatabaseName($this->dbname) . ".* TO '" . $this->escapeSql($this->login) . "'@'" . $this->escapeSql($this->loginhost) . "'");
296 public function disconnect()
298 return mysqli_close($this->dbh);
302 * This method creates any dumpfiles necessary.
303 * This is actually only done if we're cloning an existing site
304 * and we need to dump their database into a file.
305 * @return bool indicating success
307 public function create_dumpfiles()
309 return $this->dumpSourceDatabase();
312 public function load_dumpfiles()
314 $sql_results = ''; // information string which is returned
315 foreach ($this->dumpfiles as $filename => $title) {
316 $sql_results_temp = '';
317 $sql_results_temp = $this->load_file($filename, $title);
318 if ($sql_results_temp == false) {
319 return false;
322 $sql_results .= $sql_results_temp;
325 return $sql_results;
328 public function load_file($filename, $title)
330 $sql_results = ''; // information string which is returned
331 $sql_results .= "Creating $title tables...\n";
332 $fd = fopen($filename, 'r');
333 if ($fd == false) {
334 $this->error_message = "ERROR. Could not open dumpfile '$filename'.\n";
335 return false;
338 $query = "";
339 $line = "";
341 // Settings to drastically speed up installation with InnoDB
342 if (! $this->execute_sql("SET autocommit=0;")) {
343 return false;
346 if (! $this->execute_sql("START TRANSACTION;")) {
347 return false;
350 while (!feof($fd)) {
351 $line = fgets($fd, 1024);
352 $line = rtrim($line);
353 if (substr($line, 0, 2) == "--") { // Kill comments
354 continue;
357 if (substr($line, 0, 1) == "#") { // Kill comments
358 continue;
361 if ($line == "") {
362 continue;
365 $query = $query . $line; // Check for full query
366 $chr = substr($query, strlen($query) - 1, 1);
367 if ($chr == ";") { // valid query, execute
368 $query = rtrim($query, ";");
369 if (! $this->execute_sql($query)) {
370 return false;
373 $query = "";
377 // Settings to drastically speed up installation with InnoDB
378 if (! $this->execute_sql("COMMIT;")) {
379 return false;
382 if (! $this->execute_sql("SET autocommit=1;")) {
383 return false;
386 $sql_results .= "<span class='text-success'><b>OK</b></span>.<br>\n";
387 fclose($fd);
388 return $sql_results;
391 public function add_version_info()
393 include dirname(__FILE__) . "/../../version.php";
394 if ($this->execute_sql("UPDATE version SET v_major = '" . $this->escapeSql($v_major) . "', v_minor = '" . $this->escapeSql($v_minor) . "', v_patch = '" . $this->escapeSql($v_patch) . "', v_realpatch = '" . $this->escapeSql($v_realpatch) . "', v_tag = '" . $this->escapeSql($v_tag) . "', v_database = '" . $this->escapeSql($v_database) . "', v_acl = '" . $this->escapeSql($v_acl) . "'") == false) {
395 $this->error_message = "ERROR. Unable insert version information into database\n" .
396 "<p>" . mysqli_error($this->dbh) . " (#" . mysqli_errno($this->dbh) . ")\n";
397 return false;
400 return true;
403 public function add_initial_user()
405 if ($this->execute_sql("INSERT INTO `groups` (id, name, user) VALUES (1,'" . $this->escapeSql($this->igroup) . "','" . $this->escapeSql($this->iuser) . "')") == false) {
406 $this->error_message = "ERROR. Unable to add initial user group\n" .
407 "<p>" . mysqli_error($this->dbh) . " (#" . mysqli_errno($this->dbh) . ")\n";
408 return false;
411 if ($this->execute_sql("INSERT INTO users (id, username, password, authorized, lname, fname, facility_id, calendar, cal_ui) VALUES (1,'" . $this->escapeSql($this->iuser) . "','NoLongerUsed',1,'" . $this->escapeSql($this->iuname) . "','" . $this->escapeSql($this->iufname) . "',3,1,3)") == false) {
412 $this->error_message = "ERROR. Unable to add initial user\n" .
413 "<p>" . mysqli_error($this->dbh) . " (#" . mysqli_errno($this->dbh) . ")\n";
414 return false;
417 $hash = password_hash($this->iuserpass, PASSWORD_DEFAULT);
418 if (empty($hash)) {
419 // Something is seriously wrong
420 error_log('OpenEMR Error : OpenEMR is not working because unable to create a hash.');
421 die("OpenEMR Error : OpenEMR is not working because unable to create a hash.");
423 if ($this->execute_sql("INSERT INTO users_secure (id, username, password, last_update_password) VALUES (1,'" . $this->escapeSql($this->iuser) . "','" . $this->escapeSql($hash) . "',NOW())") == false) {
424 $this->error_message = "ERROR. Unable to add initial user login credentials\n" .
425 "<p>" . mysqli_error($this->dbh) . " (#" . mysqli_errno($this->dbh) . ")\n";
426 return false;
429 // Create new 2fa if enabled
430 if (($this->i2faEnable) && (!empty($this->i2faSecret)) && (class_exists('Totp')) && (class_exists('OpenEMR\Common\Crypto\CryptoGen'))) {
431 // Encrypt the new secret with the hashed password
432 $cryptoGen = new OpenEMR\Common\Crypto\CryptoGen();
433 $secret = $cryptoGen->encryptStandard($this->i2faSecret, $hash);
434 if ($this->execute_sql("INSERT INTO login_mfa_registrations (user_id, name, method, var1, var2) VALUES (1, 'App Based 2FA', 'TOTP', '" . $this->escapeSql($secret) . "', '')") == false) {
435 $this->error_message = "ERROR. Unable to add initial user's 2FA credentials\n" .
436 "<p>" . mysqli_error($this->dbh) . " (#" . mysqli_errno($this->dbh) . ")\n";
437 return false;
441 return true;
445 * Handle the additional users now that our gacl's have finished installing.
446 * @return bool
448 public function install_additional_users()
450 // Add the official openemr users (services)
451 if ($this->load_file($this->additional_users, "Additional Official Users") == false) {
452 return false;
454 return true;
457 public function on_care_coordination()
459 $resource = $this->execute_sql("SELECT `mod_id` FROM `modules` WHERE `mod_name` = 'Carecoordination' LIMIT 1");
460 $resource_array = mysqli_fetch_array($resource, MYSQLI_ASSOC);
461 $modId = $resource_array['mod_id'];
462 if (empty($modId)) {
463 $this->error_message = "ERROR configuring Care Coordination module. Unable to get mod_id for Carecoordination module\n";
464 return false;
467 $resource = $this->execute_sql("SELECT `section_id` FROM `module_acl_sections` WHERE `section_identifier` = 'carecoordination' LIMIT 1");
468 $resource_array = mysqli_fetch_array($resource, MYSQLI_ASSOC);
469 $sectionId = $resource_array['section_id'];
470 if (empty($sectionId)) {
471 $this->error_message = "ERROR configuring Care Coordination module. Unable to get section_id for carecoordination module section\n";
472 return false;
475 $resource = $this->execute_sql("SELECT `id` FROM `gacl_aro_groups` WHERE `value` = 'admin' LIMIT 1");
476 $resource_array = mysqli_fetch_array($resource, MYSQLI_ASSOC);
477 $groupId = $resource_array['id'];
478 if (empty($groupId)) {
479 $this->error_message = "ERROR configuring Care Coordination module. Unable to get id for gacl_aro_groups admin section\n";
480 return false;
483 if ($this->execute_sql("INSERT INTO `module_acl_group_settings` (`module_id`, `group_id`, `section_id`, `allowed`) VALUES ('" . $this->escapeSql($modId) . "', '" . $this->escapeSql($groupId) . "', '" . $this->escapeSql($sectionId) . "', 1)") == false) {
484 $this->error_message = "ERROR configuring Care Coordination module. Unable to add the module_acl_group_settings acl entry\n";
485 return false;
488 return true;
492 * Generates the initial user's 2FA QR Code
493 * @deprecated Recommended to use get_initial_user_mfa_totp() instead
494 * @return bool|string|void
496 public function get_initial_user_2fa_qr()
498 if (($this->i2faEnable) && (!empty($this->i2faSecret)) && (class_exists('Totp'))) {
499 $adminTotp = new Totp($this->i2faSecret, $this->iuser);
500 $qr = $adminTotp->generateQrCode();
501 return $qr;
503 return false;
507 * Generates the initial user's 2FA QR Code
508 * @return bool|string|void
510 public function get_initial_user_mfa_totp()
512 if (($this->i2faEnable) && (!empty($this->i2faSecret)) && (class_exists('Totp'))) {
513 $adminTotp = new Totp($this->i2faSecret, $this->iuser);
514 return $adminTotp;
516 return false;
520 * Create site directory if it is missing.
521 * @global string $GLOBALS['OE_SITE_DIR'] contains the name of the site directory to create
522 * @return name of the site directory or False
524 public function create_site_directory()
526 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
527 $source_directory = $GLOBALS['OE_SITES_BASE'] . "/" . $this->source_site_id;
528 $destination_directory = $GLOBALS['OE_SITE_DIR'];
529 if (! $this->recurse_copy($source_directory, $destination_directory)) {
530 $this->error_message = "unable to copy directory: '$source_directory' to '$destination_directory'. " . $this->error_message;
531 return false;
533 // the new site will create it's own keys so okay to delete these copied from the source site
534 if (!$this->clone_database) {
535 array_map('unlink', glob($destination_directory . "/documents/logs_and_misc/methods/*"));
539 return true;
542 public function write_configuration_file()
544 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
545 $this->create_site_directory();
547 @touch($this->conffile); // php bug
548 $fd = @fopen($this->conffile, 'w');
549 if (! $fd) {
550 $this->error_message = 'unable to open configuration file for writing: ' . $this->conffile;
551 return false;
554 $string = '<?php
555 // OpenEMR
556 // MySQL Config
560 $it_died = 0; //fmg: variable keeps running track of any errors
562 fwrite($fd, $string) or $it_died++;
563 fwrite($fd, "global \$disable_utf8_flag;\n") or $it_died++;
564 fwrite($fd, "\$disable_utf8_flag = false;\n\n") or $it_died++;
565 fwrite($fd, "\$host\t= '$this->server';\n") or $it_died++;
566 fwrite($fd, "\$port\t= '$this->port';\n") or $it_died++;
567 fwrite($fd, "\$login\t= '$this->login';\n") or $it_died++;
568 fwrite($fd, "\$pass\t= '$this->pass';\n") or $it_died++;
569 fwrite($fd, "\$dbase\t= '$this->dbname';\n") or $it_died++;
570 fwrite($fd, "\$db_encoding\t= 'utf8mb4';\n") or $it_died++;
572 $string = '
573 $sqlconf = array();
574 global $sqlconf;
575 $sqlconf["host"]= $host;
576 $sqlconf["port"] = $port;
577 $sqlconf["login"] = $login;
578 $sqlconf["pass"] = $pass;
579 $sqlconf["dbase"] = $dbase;
580 $sqlconf["db_encoding"] = $db_encoding;
582 //////////////////////////
583 //////////////////////////
584 //////////////////////////
585 //////DO NOT TOUCH THIS///
586 $config = 1; /////////////
587 //////////////////////////
588 //////////////////////////
589 //////////////////////////
593 fwrite($fd, $string) or $it_died++;
594 fclose($fd) or $it_died++;
596 //it's rather irresponsible to not report errors when writing this file.
597 if ($it_died != 0) {
598 $this->error_message = "ERROR. Couldn't write $it_died lines to config file '$this->conffile'.\n";
599 return false;
602 // Tell PHP that its cached bytecode version of sqlconf.php is no longer usable.
603 if (function_exists('opcache_invalidate')) {
604 opcache_invalidate($this->conffile, true);
607 return true;
610 public function insert_globals()
612 if (!(function_exists('xl'))) {
613 function xl($s)
615 return $s;
617 } else {
618 $GLOBALS['temp_skip_translations'] = true;
620 $skipGlobalEvent = true; //use in globals.inc.php script to skip event stuff
621 require(dirname(__FILE__) . '/../globals.inc.php');
622 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
623 foreach ($grparr as $fldid => $fldarr) {
624 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
625 if (is_array($fldtype) || substr($fldtype, 0, 2) !== 'm_') {
626 $res = $this->execute_sql("SELECT count(*) AS count FROM globals WHERE gl_name = '" . $this->escapeSql($fldid) . "'");
627 $row = mysqli_fetch_array($res, MYSQLI_ASSOC);
628 if (empty($row['count'])) {
629 $this->execute_sql("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
630 "VALUES ( '" . $this->escapeSql($fldid) . "', '0', '" . $this->escapeSql($flddef) . "' )");
636 return true;
639 public function install_gacl()
642 $gacl = new GaclApi();
644 // Create the ACO sections. Every ACO must have a section.
646 if ($gacl->add_object_section('Accounting', 'acct', 10, 0, 'ACO') === false) {
647 $this->error_message = "ERROR, Unable to create the access controls for OpenEMR.";
648 return false;
650 // xl('Accounting')
651 $gacl->add_object_section('Administration', 'admin', 10, 0, 'ACO');
652 // xl('Administration')
653 $gacl->add_object_section('Encounters', 'encounters', 10, 0, 'ACO');
654 // xl('Encounters')
655 $gacl->add_object_section('Lists', 'lists', 10, 0, 'ACO');
656 // xl('Lists')
657 $gacl->add_object_section('Patients', 'patients', 10, 0, 'ACO');
658 // xl('Patients')
659 $gacl->add_object_section('Squads', 'squads', 10, 0, 'ACO');
660 // xl('Squads')
661 $gacl->add_object_section('Sensitivities', 'sensitivities', 10, 0, 'ACO');
662 // xl('Sensitivities')
663 $gacl->add_object_section('Placeholder', 'placeholder', 10, 0, 'ACO');
664 // xl('Placeholder')
665 $gacl->add_object_section('Nation Notes', 'nationnotes', 10, 0, 'ACO');
666 // xl('Nation Notes')
667 $gacl->add_object_section('Patient Portal', 'patientportal', 10, 0, 'ACO');
668 // xl('Patient Portal')
669 $gacl->add_object_section('Menus', 'menus', 10, 0, 'ACO');
670 // xl('Menus')
671 $gacl->add_object_section('Groups', 'groups', 10, 0, 'ACO');
672 // xl('Groups')
673 $gacl->add_object_section('Inventory', 'inventory', 10, 0, 'ACO');
674 // xl('Inventory')
676 // Create Accounting ACOs.
678 $gacl->add_object('acct', 'Billing (write optional)', 'bill', 10, 0, 'ACO');
679 // xl('Billing (write optional)')
680 $gacl->add_object('acct', 'Price Discounting', 'disc', 10, 0, 'ACO');
681 // xl('Price Discounting')
682 $gacl->add_object('acct', 'EOB Data Entry', 'eob', 10, 0, 'ACO');
683 // xl('EOB Data Entry')
684 $gacl->add_object('acct', 'Financial Reporting - my encounters', 'rep', 10, 0, 'ACO');
685 // xl('Financial Reporting - my encounters')
686 $gacl->add_object('acct', 'Financial Reporting - anything', 'rep_a', 10, 0, 'ACO');
687 // xl('Financial Reporting - anything')
689 // Create Administration ACOs.
691 $gacl->add_object('admin', 'Superuser', 'super', 10, 0, 'ACO');
692 // xl('Superuser')
693 $gacl->add_object('admin', 'Calendar Settings', 'calendar', 10, 0, 'ACO');
694 // xl('Calendar Settings')
695 $gacl->add_object('admin', 'Database Reporting', 'database', 10, 0, 'ACO');
696 // xl('Database Reporting')
697 $gacl->add_object('admin', 'Forms Administration', 'forms', 10, 0, 'ACO');
698 // xl('Forms Administration')
699 $gacl->add_object('admin', 'Practice Settings', 'practice', 10, 0, 'ACO');
700 // xl('Practice Settings')
701 $gacl->add_object('admin', 'Superbill Codes Administration', 'superbill', 10, 0, 'ACO');
702 // xl('Superbill Codes Administration')
703 $gacl->add_object('admin', 'Users/Groups/Logs Administration', 'users', 10, 0, 'ACO');
704 // xl('Users/Groups/Logs Administration')
705 $gacl->add_object('admin', 'Batch Communication Tool', 'batchcom', 10, 0, 'ACO');
706 // xl('Batch Communication Tool')
707 $gacl->add_object('admin', 'Language Interface Tool', 'language', 10, 0, 'ACO');
708 // xl('Language Interface Tool')
709 $gacl->add_object('admin', 'Inventory Administration', 'drugs', 10, 0, 'ACO');
710 // xl('Inventory Administration')
711 $gacl->add_object('admin', 'ACL Administration', 'acl', 10, 0, 'ACO');
712 // xl('ACL Administration')
713 $gacl->add_object('admin', 'Multipledb', 'multipledb', 10, 0, 'ACO');
714 // xl('Multipledb')
715 $gacl->add_object('admin', 'Menu', 'menu', 10, 0, 'ACO');
716 // xl('Menu')
717 $gacl->add_object('admin', 'Manage modules', 'manage_modules', 10, 0, 'ACO');
718 // xl('Manage modules')
721 // Create ACOs for encounters.
723 $gacl->add_object('encounters', 'Authorize - my encounters', 'auth', 10, 0, 'ACO');
724 // xl('Authorize - my encounters')
725 $gacl->add_object('encounters', 'Authorize - any encounters', 'auth_a', 10, 0, 'ACO');
726 // xl('Authorize - any encounters')
727 $gacl->add_object('encounters', 'Coding - my encounters (write,wsome optional)', 'coding', 10, 0, 'ACO');
728 // xl('Coding - my encounters (write,wsome optional)')
729 $gacl->add_object('encounters', 'Coding - any encounters (write,wsome optional)', 'coding_a', 10, 0, 'ACO');
730 // xl('Coding - any encounters (write,wsome optional)')
731 $gacl->add_object('encounters', 'Notes - my encounters (write,addonly optional)', 'notes', 10, 0, 'ACO');
732 // xl('Notes - my encounters (write,addonly optional)')
733 $gacl->add_object('encounters', 'Notes - any encounters (write,addonly optional)', 'notes_a', 10, 0, 'ACO');
734 // xl('Notes - any encounters (write,addonly optional)')
735 $gacl->add_object('encounters', 'Fix encounter dates - any encounters', 'date_a', 10, 0, 'ACO');
736 // xl('Fix encounter dates - any encounters')
737 $gacl->add_object('encounters', 'Less-private information (write,addonly optional)', 'relaxed', 10, 0, 'ACO');
738 // xl('Less-private information (write,addonly optional)')
740 // Create ACOs for lists.
742 $gacl->add_object('lists', 'Default List (write,addonly optional)', 'default', 10, 0, 'ACO');
743 // xl('Default List (write,addonly optional)')
744 $gacl->add_object('lists', 'State List (write,addonly optional)', 'state', 10, 0, 'ACO');
745 // xl('State List (write,addonly optional)')
746 $gacl->add_object('lists', 'Country List (write,addonly optional)', 'country', 10, 0, 'ACO');
747 // xl('Country List (write,addonly optional)')
748 $gacl->add_object('lists', 'Language List (write,addonly optional)', 'language', 10, 0, 'ACO');
749 // xl('Language List (write,addonly optional)')
750 $gacl->add_object('lists', 'Ethnicity-Race List (write,addonly optional)', 'ethrace', 10, 0, 'ACO');
751 // xl('Ethnicity-Race List (write,addonly optional)')
753 // Create ACOs for patientportal.
755 $gacl->add_object('patientportal', 'Patient Portal', 'portal', 10, 0, 'ACO');
756 // xl('Patient Portal')
758 // Create ACOs for modules.
760 $gacl->add_object('menus', 'Modules', 'modle', 10, 0, 'ACO');
761 // xl('Modules')
763 // Create ACOs for patients.
765 $gacl->add_object('patients', 'Appointments (write,wsome optional)', 'appt', 10, 0, 'ACO');
766 // xl('Appointments (write,wsome optional)')
767 $gacl->add_object('patients', 'Demographics (write,addonly optional)', 'demo', 10, 0, 'ACO');
768 // xl('Demographics (write,addonly optional)')
769 $gacl->add_object('patients', 'Medical/History (write,addonly optional)', 'med', 10, 0, 'ACO');
770 // xl('Medical/History (write,addonly optional)')
771 $gacl->add_object('patients', 'Transactions (write optional)', 'trans', 10, 0, 'ACO');
772 // xl('Transactions (write optional)')
773 $gacl->add_object('patients', 'Documents (write,addonly optional)', 'docs', 10, 0, 'ACO');
774 // xl('Documents (write,addonly optional)')
775 $gacl->add_object('patients', 'Documents Delete', 'docs_rm', 10, 0, 'ACO');
776 // xl('Documents Delete')
777 $gacl->add_object('patients', 'Patient Notes (write,addonly optional)', 'notes', 10, 0, 'ACO');
778 // xl('Patient Notes (write,addonly optional)')
779 $gacl->add_object('patients', 'Sign Lab Results (write,addonly optional)', 'sign', 10, 0, 'ACO');
780 // xl('Sign Lab Results (write,addonly optional)')
781 $gacl->add_object('patients', 'Patient Reminders (write,addonly optional)', 'reminder', 10, 0, 'ACO');
782 // xl('Patient Reminders (write,addonly optional)')
783 $gacl->add_object('patients', 'Clinical Reminders/Alerts (write,addonly optional)', 'alert', 10, 0, 'ACO');
784 // xl('Clinical Reminders/Alerts (write,addonly optional)')
785 $gacl->add_object('patients', 'Disclosures (write,addonly optional)', 'disclosure', 10, 0, 'ACO');
786 // xl('Disclosures (write,addonly optional)')
787 $gacl->add_object('patients', 'Prescriptions (write,addonly optional)', 'rx', 10, 0, 'ACO');
788 // xl('Prescriptions (write,addonly optional)')
789 $gacl->add_object('patients', 'Amendments (write,addonly optional)', 'amendment', 10, 0, 'ACO');
790 // xl('Amendments (write,addonly optional)')
791 $gacl->add_object('patients', 'Lab Results (write,addonly optional)', 'lab', 10, 0, 'ACO');
792 // xl('Lab Results (write,addonly optional)')
793 $gacl->add_object('patients', 'Patient Report', 'pat_rep', 10, 0, 'ACO');
794 // xl('Patient Report')
797 $gacl->add_object('groups', 'View/Add/Update groups', 'gadd', 10, 0, 'ACO');
798 // xl('View/Add/Update groups')
799 $gacl->add_object('groups', 'View/Create/Update groups appointment in calendar', 'gcalendar', 10, 0, 'ACO');
800 // xl('View/Create/Update groups appointment in calendar')
801 $gacl->add_object('groups', 'Group encounter log', 'glog', 10, 0, 'ACO');
802 // xl('Group encounter log')
803 $gacl->add_object('groups', 'Group detailed log of appointment in patient record', 'gdlog', 10, 0, 'ACO');
804 // xl('Group detailed log of appointment in patient record')
805 $gacl->add_object('groups', 'Send message from the permanent group therapist to the personal therapist', 'gm', 10, 0, 'ACO');
806 // xl('Send message from the permanent group therapist to the personal therapist')
808 // Create ACOs for sensitivities.
810 $gacl->add_object('sensitivities', 'Normal', 'normal', 10, 0, 'ACO');
811 // xl('Normal')
812 $gacl->add_object('sensitivities', 'High', 'high', 20, 0, 'ACO');
813 // xl('High')
815 // Create ACO for placeholder.
817 $gacl->add_object('placeholder', 'Placeholder (Maintains empty ACLs)', 'filler', 10, 0, 'ACO');
818 // xl('Placeholder (Maintains empty ACLs)')
820 // Create ACO for nationnotes.
822 $gacl->add_object('nationnotes', 'Nation Notes Configure', 'nn_configure', 10, 0, 'ACO');
823 // xl('Nation Notes Configure')
825 // Create ACOs for Inventory.
827 $gacl->add_object('inventory', 'Lots', 'lots', 10, 0, 'ACO');
828 // xl('Lots')
829 $gacl->add_object('inventory', 'Sales', 'sales', 20, 0, 'ACO');
830 // xl('Sales')
831 $gacl->add_object('inventory', 'Purchases', 'purchases', 30, 0, 'ACO');
832 // xl('Purchases')
833 $gacl->add_object('inventory', 'Transfers', 'transfers', 40, 0, 'ACO');
834 // xl('Transfers')
835 $gacl->add_object('inventory', 'Adjustments', 'adjustments', 50, 0, 'ACO');
836 // xl('Adjustments')
837 $gacl->add_object('inventory', 'Consumption', 'consumption', 60, 0, 'ACO');
838 // xl('Consumption')
839 $gacl->add_object('inventory', 'Destruction', 'destruction', 70, 0, 'ACO');
840 // xl('Destruction')
841 $gacl->add_object('inventory', 'Reporting', 'reporting', 80, 0, 'ACO');
842 // xl('Reporting')
844 // Create ARO groups.
846 $users = $gacl->add_group('users', 'OpenEMR Users', 0, 'ARO');
847 // xl('OpenEMR Users')
848 $admin = $gacl->add_group('admin', 'Administrators', $users, 'ARO');
849 // xl('Administrators')
850 $clin = $gacl->add_group('clin', 'Clinicians', $users, 'ARO');
851 // xl('Clinicians')
852 $doc = $gacl->add_group('doc', 'Physicians', $users, 'ARO');
853 // xl('Physicians')
854 $front = $gacl->add_group('front', 'Front Office', $users, 'ARO');
855 // xl('Front Office')
856 $back = $gacl->add_group('back', 'Accounting', $users, 'ARO');
857 // xl('Accounting')
858 $breakglass = $gacl->add_group('breakglass', 'Emergency Login', $users, 'ARO');
859 // xl('Emergency Login')
862 // Create a Users section for the AROs (humans).
864 $gacl->add_object_section('Users', 'users', 10, 0, 'ARO');
865 // xl('Users')
867 // Create the Administrator in the above-created "users" section
868 // and add him/her to the above-created "admin" group.
869 // If this script is being used by OpenEMR's setup, then will
870 // incorporate the installation values. Otherwise will
871 // hardcode the 'admin' user.
872 if (isset($this) && isset($this->iuser)) {
873 $gacl->add_object('users', $this->iuname, $this->iuser, 10, 0, 'ARO');
874 $gacl->add_group_object($admin, 'users', $this->iuser, 'ARO');
875 } else {
876 $gacl->add_object('users', 'Administrator', 'admin', 10, 0, 'ARO');
877 $gacl->add_group_object($admin, 'users', 'admin', 'ARO');
880 // Declare return terms for language translations
881 // xl('write') xl('wsome') xl('addonly') xl('view')
883 // Set permissions for administrators.
885 $gacl->add_acl(
886 array(
887 'acct' => array('bill', 'disc', 'eob', 'rep', 'rep_a'),
888 'admin' => array('calendar', 'database', 'forms', 'practice', 'superbill', 'users', 'batchcom', 'language', 'super', 'drugs', 'acl','multipledb','menu','manage_modules'),
889 'encounters' => array('auth_a', 'auth', 'coding_a', 'coding', 'notes_a', 'notes', 'date_a', 'relaxed'),
890 'inventory' => array('lots', 'sales', 'purchases', 'transfers', 'adjustments', 'consumption', 'destruction', 'reporting'),
891 'lists' => array('default','state','country','language','ethrace'),
892 'patients' => array('appt', 'demo', 'med', 'trans', 'docs', 'notes', 'sign', 'reminder', 'alert', 'disclosure', 'rx', 'amendment', 'lab', 'docs_rm','pat_rep'),
893 'sensitivities' => array('normal', 'high'),
894 'nationnotes' => array('nn_configure'),
895 'patientportal' => array('portal'),
896 'menus' => array('modle'),
897 'groups' => array('gadd','gcalendar','glog','gdlog','gm')
899 null,
900 array($admin),
901 null,
902 null,
905 'write',
906 'Administrators can do anything'
908 // xl('Administrators can do anything')
910 // Set permissions for physicians.
912 $gacl->add_acl(
913 array(
914 'patients' => array('pat_rep')
916 null,
917 array($doc),
918 null,
919 null,
922 'view',
923 'Things that physicians can only read'
925 // xl('Things that physicians can only read')
926 $gacl->add_acl(
927 array(
928 'placeholder' => array('filler')
930 null,
931 array($doc),
932 null,
933 null,
936 'addonly',
937 'Things that physicians can read and enter but not modify'
939 // xl('Things that physicians can read and enter but not modify')
940 $gacl->add_acl(
941 array(
942 'placeholder' => array('filler')
944 null,
945 array($doc),
946 null,
947 null,
950 'wsome',
951 'Things that physicians can read and partly modify'
953 // xl('Things that physicians can read and partly modify')
954 $gacl->add_acl(
955 array(
956 'acct' => array('disc', 'rep'),
957 'admin' => array('drugs'),
958 'encounters' => array('auth_a', 'auth', 'coding_a', 'coding', 'notes_a', 'notes', 'date_a', 'relaxed'),
959 'patients' => array('appt', 'demo', 'med', 'trans', 'docs', 'notes', 'sign', 'reminder', 'alert',
960 'disclosure', 'rx', 'amendment', 'lab'),
961 'sensitivities' => array('normal', 'high'),
962 'groups' => array('gcalendar','glog')
964 null,
965 array($doc),
966 null,
967 null,
970 'write',
971 'Things that physicians can read and modify'
973 // xl('Things that physicians can read and modify')
975 // Set permissions for clinicians.
977 $gacl->add_acl(
978 array(
979 'patients' => array('pat_rep')
981 null,
982 array($clin),
983 null,
984 null,
987 'view',
988 'Things that clinicians can only read'
990 // xl('Things that clinicians can only read')
991 $gacl->add_acl(
992 array(
993 'encounters' => array('notes', 'relaxed'),
994 'patients' => array('demo', 'med', 'docs', 'notes','trans', 'reminder', 'alert', 'disclosure', 'rx', 'amendment', 'lab'),
995 'sensitivities' => array('normal')
997 null,
998 array($clin),
999 null,
1000 null,
1003 'addonly',
1004 'Things that clinicians can read and enter but not modify'
1006 // xl('Things that clinicians can read and enter but not modify')
1007 $gacl->add_acl(
1008 array(
1009 'placeholder' => array('filler')
1011 null,
1012 array($clin),
1013 null,
1014 null,
1017 'wsome',
1018 'Things that clinicians can read and partly modify'
1020 // xl('Things that clinicians can read and partly modify')
1021 $gacl->add_acl(
1022 array(
1023 'admin' => array('drugs'),
1024 'encounters' => array('auth', 'coding', 'notes'),
1025 'patients' => array('appt'),
1026 'groups' => array('gcalendar', 'glog')
1028 null,
1029 array($clin),
1030 null,
1031 null,
1034 'write',
1035 'Things that clinicians can read and modify'
1037 // xl('Things that clinicians can read and modify')
1039 // Set permissions for front office staff.
1041 $gacl->add_acl(
1042 array(
1043 'patients' => array('alert')
1045 null,
1046 array($front),
1047 null,
1048 null,
1051 'view',
1052 'Things that front office can only read'
1054 // xl('Things that front office can only read')
1055 $gacl->add_acl(
1056 array(
1057 'placeholder' => array('filler')
1059 null,
1060 array($front),
1061 null,
1062 null,
1065 'addonly',
1066 'Things that front office can read and enter but not modify'
1068 // xl('Things that front office can read and enter but not modify')
1069 $gacl->add_acl(
1070 array(
1071 'placeholder' => array('filler')
1073 null,
1074 array($front),
1075 null,
1076 null,
1079 'wsome',
1080 'Things that front office can read and partly modify'
1082 // xl('Things that front office can read and partly modify')
1083 $gacl->add_acl(
1084 array(
1085 'patients' => array('appt', 'demo'),
1086 'groups' => array('gcalendar')
1088 null,
1089 array($front),
1090 null,
1091 null,
1094 'write',
1095 'Things that front office can read and modify'
1097 // xl('Things that front office can read and modify')
1099 // Set permissions for back office staff.
1101 $gacl->add_acl(
1102 array(
1103 'patients' => array('alert')
1105 null,
1106 array($back),
1107 null,
1108 null,
1111 'view',
1112 'Things that back office can only read'
1114 // xl('Things that back office can only read')
1115 $gacl->add_acl(
1116 array(
1117 'placeholder' => array('filler')
1119 null,
1120 array($back),
1121 null,
1122 null,
1125 'addonly',
1126 'Things that back office can read and enter but not modify'
1128 // xl('Things that back office can read and enter but not modify')
1129 $gacl->add_acl(
1130 array(
1131 'placeholder' => array('filler')
1133 null,
1134 array($back),
1135 null,
1136 null,
1139 'wsome',
1140 'Things that back office can read and partly modify'
1142 // xl('Things that back office can read and partly modify')
1143 $gacl->add_acl(
1144 array(
1145 'acct' => array('bill', 'disc', 'eob', 'rep', 'rep_a'),
1146 'admin' => array('practice', 'superbill'),
1147 'encounters' => array('auth_a', 'coding_a', 'date_a'),
1148 'patients' => array('appt', 'demo')
1150 null,
1151 array($back),
1152 null,
1153 null,
1156 'write',
1157 'Things that back office can read and modify'
1159 // xl('Things that back office can read and modify')
1161 // Set permissions for Emergency Login.
1163 $gacl->add_acl(
1164 array(
1165 'acct' => array('bill', 'disc', 'eob', 'rep', 'rep_a'),
1166 'admin' => array('calendar', 'database', 'forms', 'practice', 'superbill', 'users', 'batchcom', 'language', 'super', 'drugs', 'acl','multipledb','menu','manage_modules'),
1167 'encounters' => array('auth_a', 'auth', 'coding_a', 'coding', 'notes_a', 'notes', 'date_a', 'relaxed'),
1168 'inventory' => array('lots', 'sales', 'purchases', 'transfers', 'adjustments', 'consumption', 'destruction', 'reporting'),
1169 'lists' => array('default','state','country','language','ethrace'),
1170 'patients' => array('appt', 'demo', 'med', 'trans', 'docs', 'notes', 'sign', 'reminder', 'alert', 'disclosure', 'rx', 'amendment', 'lab', 'docs_rm','pat_rep'),
1171 'sensitivities' => array('normal', 'high'),
1172 'nationnotes' => array('nn_configure'),
1173 'patientportal' => array('portal'),
1174 'menus' => array('modle'),
1175 'groups' => array('gadd','gcalendar','glog','gdlog','gm')
1177 null,
1178 array($breakglass),
1179 null,
1180 null,
1183 'write',
1184 'Emergency Login user can do anything'
1186 // xl('Emergency Login user can do anything')
1188 return true;
1191 public function quick_install()
1193 // Validation of OpenEMR user settings
1194 // (applicable if not cloning from another database)
1195 if (empty($this->clone_database)) {
1196 if (! $this->login_is_valid()) {
1197 return false;
1200 if (! $this->iuser_is_valid()) {
1201 return false;
1204 if (! $this->user_password_is_valid()) {
1205 return false;
1209 // Validation of mysql database password
1210 if (! $this->password_is_valid()) {
1211 return false;
1214 if (! $this->no_root_db_access) {
1215 // Connect to mysql via root user
1216 if (! $this->root_database_connection()) {
1217 return false;
1220 // Create the dumpfile
1221 // (applicable if cloning from another database)
1222 if (! empty($this->clone_database)) {
1223 if (! $this->create_dumpfiles()) {
1224 return false;
1228 // Create the site directory
1229 // (applicable if mirroring another local site)
1230 if (! empty($this->source_site_id)) {
1231 if (! $this->create_site_directory()) {
1232 return false;
1236 $this->disconnect();
1237 // Using @ in below call to hide the php warning in cases where the
1238 // below connection does not work, which is expected behavior.
1239 // Using try in below call to catch the mysqli exception when the
1240 // below connection does not work, which is expected behavior (needed to
1241 // add this try/catch clause for PHP 8.1).
1242 try {
1243 $checkUserDatabaseConnection = @$this->user_database_connection();
1244 } catch (Exception $e) {
1245 $checkUserDatabaseConnection = false;
1247 if (! $checkUserDatabaseConnection) {
1248 // Re-connect to mysql via root user
1249 if (! $this->root_database_connection()) {
1250 return false;
1253 // Create the mysql database
1254 if (! $this->create_database()) {
1255 return false;
1258 // Create the mysql user
1259 if (! $this->create_database_user()) {
1260 return false;
1263 // Grant user privileges to the mysql database
1264 if (! $this->grant_privileges()) {
1265 return false;
1269 $this->disconnect();
1272 // Connect to mysql via created user
1273 if (! $this->user_database_connection()) {
1274 return false;
1277 // Build the database
1278 if (! $this->load_dumpfiles()) {
1279 return false;
1282 // Write the sql configuration file
1283 if (! $this->write_configuration_file()) {
1284 return false;
1287 // Load the version information, globals settings,
1288 // initial user, and set up gacl access controls.
1289 // (applicable if not cloning from another database)
1290 if (empty($this->clone_database)) {
1291 if (! $this->add_version_info()) {
1292 return false;
1295 if (! $this->insert_globals()) {
1296 return false;
1299 if (! $this->add_initial_user()) {
1300 return false;
1303 if (! $this->install_gacl()) {
1304 return false;
1307 if (! $this->install_additional_users()) {
1308 return false;
1311 if (! $this->on_care_coordination()) {
1312 return false;
1316 return true;
1319 private function escapeSql($sql)
1321 return mysqli_real_escape_string($this->dbh, $sql);
1324 private function escapeDatabaseName($name)
1326 if (preg_match('/[^A-Za-z0-9_-]/', $name)) {
1327 error_log("Illegal character(s) in database name");
1328 die("Illegal character(s) in database name");
1330 return $name;
1333 private function escapeCollateName($name)
1335 if (preg_match('/[^A-Za-z0-9_-]/', $name)) {
1336 error_log("Illegal character(s) in collation name");
1337 die("Illegal character(s) in collation name");
1339 return $name;
1342 private function execute_sql($sql, $showError = true)
1344 $this->error_message = '';
1345 if (! $this->dbh) {
1346 $this->user_database_connection();
1349 $results = mysqli_query($this->dbh, $sql);
1350 if ($results) {
1351 return $results;
1352 } else {
1353 if ($showError) {
1354 $error_mes = mysqli_error($this->dbh);
1355 $this->error_message = "unable to execute SQL: '$sql' due to: " . $error_mes;
1356 error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: " . htmlspecialchars($sql, ENT_QUOTES) . " due to: " . htmlspecialchars($error_mes, ENT_QUOTES));
1358 return false;
1362 private function connect_to_database($server, $user, $password, $port, $dbname = '')
1364 $pathToCerts = __DIR__ . "/../../sites/" . $this->site . "/documents/certificates/";
1365 $mysqlSsl = false;
1366 $mysqli = mysqli_init();
1367 if (defined('MYSQLI_CLIENT_SSL') && file_exists($pathToCerts . "mysql-ca")) {
1368 $mysqlSsl = true;
1369 if (
1370 file_exists($pathToCerts . "mysql-key") &&
1371 file_exists($pathToCerts . "mysql-cert")
1373 // with client side certificate/key
1374 mysqli_ssl_set(
1375 $mysqli,
1376 $pathToCerts . "mysql-key",
1377 $pathToCerts . "mysql-cert",
1378 $pathToCerts . "mysql-ca",
1379 null,
1380 null
1382 } else {
1383 // without client side certificate/key
1384 mysqli_ssl_set(
1385 $mysqli,
1386 null,
1387 null,
1388 $pathToCerts . "mysql-ca",
1389 null,
1390 null
1394 try {
1395 if ($mysqlSsl) {
1396 $ok = mysqli_real_connect($mysqli, $server, $user, $password, $dbname, (int)$port != 0 ? (int)$port : 3306, '', MYSQLI_CLIENT_SSL);
1397 } else {
1398 $ok = mysqli_real_connect($mysqli, $server, $user, $password, $dbname, (int)$port != 0 ? (int)$port : 3306);
1400 } catch (mysqli_sql_exception $e) {
1401 $this->error_message = "unable to connect to sql server because of mysql error: " . $e->getMessage();
1402 return false;
1404 if (!$ok) {
1405 $this->error_message = 'unable to connect to sql server because of: (' . mysqli_connect_errno() . ') ' . mysqli_connect_error();
1406 return false;
1408 return $mysqli;
1411 private function set_sql_strict()
1413 // Turn off STRICT SQL
1414 return $this->execute_sql("SET sql_mode = ''");
1417 private function set_collation()
1419 return $this->execute_sql("SET NAMES 'utf8mb4'");
1423 * innitialize $this->dumpfiles, an array of the dumpfiles that will
1424 * be loaded into the database, including the correct translation
1425 * dumpfile.
1426 * The keys are the paths of the dumpfiles, and the values are the titles
1427 * @return array
1429 private function initialize_dumpfile_list()
1431 if ($this->clone_database) {
1432 $this->dumpfiles = array( $this->get_backup_filename() => 'clone database' );
1433 } else {
1434 $dumpfiles = array( $this->main_sql => 'Main' );
1435 if (! empty($this->development_translations)) {
1436 // Use the online development translation set
1437 $dumpfiles[ $this->devel_translation_sql ] = "Online Development Language Translations (utf8)";
1438 } else {
1439 // Use the local translation set
1440 $dumpfiles[ $this->translation_sql ] = "Language Translation (utf8)";
1443 if ($this->ippf_specific) {
1444 $dumpfiles[ $this->ippf_sql ] = "IPPF Layout";
1447 // Load ICD-9 codes if present.
1448 if (file_exists($this->icd9)) {
1449 $dumpfiles[ $this->icd9 ] = "ICD-9";
1452 // Load CVX codes if present
1453 if (file_exists($this->cvx)) {
1454 $dumpfiles[ $this->cvx ] = "CVX Immunization Codes";
1457 $this->dumpfiles = $dumpfiles;
1460 return $this->dumpfiles;
1465 * Directory copy logic borrowed from a user comment at
1466 * http://www.php.net/manual/en/function.copy.php
1467 * @param string $src name of the directory to copy
1468 * @param string $dst name of the destination to copy to
1469 * @return bool indicating success
1471 private function recurse_copy($src, $dst)
1473 $dir = opendir($src);
1474 if (! @mkdir($dst)) {
1475 $this->error_message = "unable to create directory: '$dst'";
1476 return false;
1479 while (false !== ($file = readdir($dir))) {
1480 if ($file != '.' && $file != '..') {
1481 if (is_dir($src . '/' . $file)) {
1482 $this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
1483 } else {
1484 copy($src . '/' . $file, $dst . '/' . $file);
1489 closedir($dir);
1490 return true;
1495 * dump a site's database to a temporary file.
1496 * @param string $source_site_id the site_id of the site to dump
1497 * @return filename of the backup
1499 private function dumpSourceDatabase()
1501 global $OE_SITES_BASE;
1502 $source_site_id = $this->source_site_id;
1504 include("$OE_SITES_BASE/$source_site_id/sqlconf.php");
1506 if (empty($config)) {
1507 die("Source site $source_site_id has not been set up!");
1510 $backup_file = $this->get_backup_filename();
1511 $cmd = "mysqldump -u " . escapeshellarg($login) .
1512 " -h " . $host .
1513 " -p" . escapeshellarg($pass) .
1514 " --ignore-table=" . escapeshellarg($dbase . ".onsite_activity_view") . " --hex-blob --opt --skip-extended-insert --quote-names -r $backup_file " .
1515 escapeshellarg($dbase);
1517 $tmp1 = [];
1518 $tmp0 = exec($cmd, $tmp1, $tmp2);
1519 if ($tmp2) {
1520 die("Error $tmp2 running \"$cmd\": $tmp0 " . implode(' ', $tmp1));
1523 return $backup_file;
1527 * @return filename of the source backup database for cloning
1529 private function get_backup_filename()
1531 if (stristr(PHP_OS, 'WIN')) {
1532 $backup_file = 'C:/windows/temp/setup_dump.sql';
1533 } else {
1534 $backup_file = '/tmp/setup_dump.sql';
1537 return $backup_file;
1539 //RP_ADDED
1540 public function getCurrentTheme()
1542 $current_theme = $this->execute_sql("SELECT gl_value FROM globals WHERE gl_name LIKE '%css_header%'");
1543 $current_theme = mysqli_fetch_array($current_theme);
1544 return $current_theme[0];
1547 public function setCurrentTheme()
1549 $current_theme = $this->getCurrentTheme();
1550 // for cloned sites since they're not asked about a new theme
1551 if (!$this->new_theme) {
1552 $this->new_theme = $current_theme;
1554 return $this->execute_sql("UPDATE globals SET gl_value='" . $this->escapeSql($this->new_theme) . "' WHERE gl_name LIKE '%css_header%'");
1557 public function listThemes()
1559 $themes_img_dir = "public/images/stylesheets/";
1560 $arr_themes_img = array_values(array_filter(scandir($themes_img_dir), function ($item) {
1561 return $item[0] !== '.';
1562 }));
1563 return $arr_themes_img;
1566 private function extractFileName($theme_file_name = '')
1568 $this->theme_file_name = $theme_file_name;
1569 $under_score = strpos($theme_file_name, '_') + 1;
1570 $dot = strpos($theme_file_name, '.');
1571 $theme_value = substr($theme_file_name, $under_score, ($dot - $under_score));
1572 $theme_title = ucwords(str_replace("_", " ", $theme_value));
1573 return array('theme_value' => $theme_value, 'theme_title' => $theme_title);
1576 public function displayThemesDivs()
1578 $themes_number = count($this->listThemes());
1579 for ($i = 0; $i < $themes_number; $i++) {
1580 $id = $i + 1;
1581 $arr_theme_name = $this->listThemes();
1582 $theme_file_name = $arr_theme_name[$i];
1583 $arr_extracted_file_name = $this->extractFileName($theme_file_name);
1584 $theme_value = $arr_extracted_file_name['theme_value'];
1585 $theme_title = $arr_extracted_file_name['theme_title'];
1586 $img_path = "public/images/stylesheets/";
1587 $theme_file_path = $img_path . $theme_file_name;
1588 $div_start = " <div class='row'>";
1589 $div_end = " </div>";
1590 $img_div = " <div class='col-sm-2 checkboxgroup'>
1591 <label for='my_radio_button_id" . attr($id) . "'><img height='160px' src='" . attr($theme_file_path) . "' width='100%'></label>
1592 <p class='m-0'>" . text($theme_title) . "</p><input id='my_radio_button_id" . attr($id) . "' name='stylesheet' type='radio' value='" . attr($theme_value) . "'>
1593 </div>";
1594 $theme_img_number = $i % 6; //to ensure that last file in array will always generate 5 and will end the row
1595 switch ($theme_img_number) {
1596 case 0: //start row
1597 echo $div_start . "\r\n";
1598 echo $img_div . "\r\n";
1599 break;
1601 case 1:
1602 case 2:
1603 case 3:
1604 case 4:
1605 echo $img_div . "\r\n";
1606 break;
1608 case 5://end row
1609 echo $img_div . "\r\n";
1610 echo $div_end . "\r\n";
1611 echo "<br />" . "\r\n";
1612 break;
1614 default:
1615 echo $div_start . "\r\n";
1616 echo "<h5>Sorry no stylesheet images in directory</h5>";
1617 echo $div_end . "\r\n";
1618 break;
1621 return;
1624 public function displaySelectedThemeDiv()
1626 $theme_file_name = $this->getCurrentTheme();
1627 $arr_extracted_file_name = $this->extractFileName($theme_file_name);
1628 $theme_value = $arr_extracted_file_name['theme_value'];
1629 $theme_title = $arr_extracted_file_name['theme_title'];
1630 $img_path = "public/images/stylesheets/";
1631 $theme_file_path = $img_path . "style_" . $theme_value . ".png";
1633 $display_selected_theme_div = <<<DSTD
1634 <div class="row">
1635 <div class="col-sm-12">
1636 <h4>Current Theme:</h4>
1637 <div class="col-sm-4 offset-sm-4 checkboxgroup">
1638 <label for="nothing"><img id="current_theme" src="{$theme_file_path}" width="100%"></label>
1639 <p id="current_theme_title"style="margin:0">{$theme_title}</p>
1640 </div>
1641 </div>
1642 </div>
1643 <br />
1644 DSTD;
1645 echo $display_selected_theme_div . "\r\n";
1646 return;
1649 public function displayNewThemeDiv()
1651 // cloned sites don't get a chance to set a new theme
1652 if (!$this->new_theme) {
1653 $this->new_theme = $this->getCurrentTheme();
1655 $theme_file_name = $this->new_theme;
1656 $arr_extracted_file_name = $this->extractFileName($theme_file_name);
1657 $theme_value = $arr_extracted_file_name['theme_value'];
1658 $theme_title = $arr_extracted_file_name['theme_title'];
1659 $img_path = "public/images/stylesheets/";
1660 $theme_file_path = $img_path . "style_" . $theme_value . ".png";
1662 $display_selected_theme_div = <<<DSTD
1663 <div class="row">
1664 <div class="col-sm-12">
1665 <div class="col-sm-4 offset-sm-4 checkboxgroup">
1666 <label for="nothing"><img id="current_theme" src="{$theme_file_path}" width="75%"></label>
1667 <p id="current_theme_title"style="margin:0">{$theme_title}</p>
1668 </div>
1669 </div>
1670 </div>
1671 <br />
1672 DSTD;
1673 echo $display_selected_theme_div . "\r\n";
1674 return;
1677 public function setupHelpModal()
1679 $setup_help_modal = <<<SETHLP
1680 <div class="row">
1681 <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1682 <div class="modal-dialog modal-lg">
1683 <div class="modal-content oe-modal-content" style="height:700px">
1684 <div class="modal-header clearfix">
1685 <button type="button" class="close" data-dismiss="modal" aria-label=Close>
1686 <span aria-hidden="true" style="color:var(--black); font-size:1.5em;">×</span></button>
1687 </div>
1688 <div class="modal-body" style="height:80%;">
1689 <iframe src="" id="targetiframe" style="height:100%; width:100%; overflow-x: hidden; border:none"
1690 allowtransparency="true"></iframe>
1691 </div>
1692 <div class="modal-footer" style="margin-top:0px;">
1693 <button class="btn btn-link btn-cancel oe-pull-away" data-dismiss="modal" type="button">Close</button>
1694 <!--<button class="btn btn-secondary btn-print oe-pull-away" data-dismiss="modal" id="print-help-href" type="button">Print</button>-->
1695 </div>
1696 </div>
1697 </div>
1698 </div>
1699 </div>
1700 <script>
1701 $(function () {
1702 $('#help-href').click (function(){
1703 document.getElementById('targetiframe').src = "Documentation/help_files/openemr_installation_help.php";
1706 $(function () {
1707 $('#print-help-href').click (function(){
1708 $("#targetiframe").get(0).contentWindow.print();
1711 // Jquery draggable
1712 $(".modal-dialog").addClass('drag-action');
1713 $(".modal-content").addClass('resize-action');
1714 </script>
1715 SETHLP;
1716 echo $setup_help_modal . "\r\n";
1717 return;