Development Appliance/Demo - Added feature to use the online development translation...
[openemr.git] / library / classes / Installer.class.php
blob4936b59876c25746ac811f49f6c5f77afd252a8a
1 <?php
2 /* Copyright © 2010 by Andrew Moore */
3 /* Licensing information appears at the end of this file. */
5 class Installer
8 public function __construct( $cgi_variables )
10 // Installation variables
11 // For a good explanation of these variables, see documentation in
12 // the contrib/util/installScripts/InstallerAuto.php file.
13 $this->iuser = $cgi_variables['iuser'];
14 $this->iuserpass = $cgi_variables['iuserpass'];
15 $this->iuname = $cgi_variables['iuname'];
16 $this->igroup = $cgi_variables['igroup'];
17 $this->server = $cgi_variables['server']; // mysql server (usually localhost)
18 $this->loginhost = $cgi_variables['loginhost']; // php/apache server (usually localhost)
19 $this->port = $cgi_variables['port'];
20 $this->root = $cgi_variables['root'];
21 $this->rootpass = $cgi_variables['rootpass'];
22 $this->login = $cgi_variables['login'];
23 $this->pass = $cgi_variables['pass'];
24 $this->dbname = $cgi_variables['dbname'];
25 $this->collate = $cgi_variables['collate'];
26 $this->site = $cgi_variables['site'];
27 $this->source_site_id = $cgi_variables['source_site_id'];
28 $this->clone_database = $cgi_variables['clone_database'];
29 $this->development_translations = $cgi_variables['development_translations'];
31 // Make this true for IPPF.
32 $this->ippf_specific = false;
34 // Record name of sql access file
35 $GLOBALS['OE_SITES_BASE'] = dirname(__FILE__) . '/../../sites';
36 $GLOBALS['OE_SITE_DIR'] = $GLOBALS['OE_SITES_BASE'] . '/' . $this->site;
37 $this->conffile = $GLOBALS['OE_SITE_DIR'] . '/sqlconf.php';
39 // Record names of sql table files
40 $this->main_sql = dirname(__FILE__) . '/../../sql/database.sql';
41 $this->translation_sql = dirname(__FILE__) . '/../../contrib/util/language_translations/currentLanguage_utf8.sql';
42 $this->devel_translation_sql = "http://github.com/openemr/translations_development_openemr/raw/master/languageTranslations_utf8.sql";
43 $this->ippf_sql = dirname(__FILE__) . "/../../sql/ippf_layout.sql";
44 $this->icd9 = dirname(__FILE__) . "/../../sql/icd9.sql";
46 // Record name of php-gacl installation files
47 $this->gaclSetupScript1 = dirname(__FILE__) . "/../../gacl/setup.php";
48 $this->gaclSetupScript2 = dirname(__FILE__) . "/../../acl_setup.php";
50 // Prepare the dumpfile list
51 $this->initialize_dumpfile_list();
53 // Entities to hold error and debug messages
54 $this->error_message = '';
55 $this->debug_message = '';
57 // Entity to hold sql connection
58 $this->dbh = false;
61 public function login_is_valid()
63 if ( ($this->login == '') || (! isset( $this->login )) ) {
64 $this->error_message = "login is invalid: '$this->login'";
65 return FALSE;
67 return TRUE;
70 public function iuser_is_valid()
72 if ( strpos($this->iuser, " ") ) {
73 $this->error_message = "Initial user is invalid: '$this->iuser'";
74 return FALSE;
76 return TRUE;
79 public function password_is_valid()
81 if ( $this->pass == "" || !isset($this->pass) ) {
82 $this->error_message = "The password for the new database account is invalid: '$this->pass'";
83 return FALSE;
85 return TRUE;
88 public function user_password_is_valid()
90 if ( $this->iuserpass == "" || !isset($this->iuserpass) ) {
91 $this->error_message = "The password for the user is invalid: '$this->iuserpass'";
92 return FALSE;
94 return TRUE;
97 public function root_database_connection()
99 $this->dbh = $this->connect_to_database( $this->server, $this->root, $this->rootpass, $this->port );
100 if ( $this->dbh ) {
101 return TRUE;
102 } else {
103 $this->error_message = 'unable to connect to database as root';
104 return FALSE;
108 public function user_database_connection()
110 $this->dbh = $this->connect_to_database( $this->server, $this->login, $this->pass, $this->port );
111 if ( ! $this->dbh ) {
112 $this->error_message = "unable to connect to database as user: '$this->login'";
113 return FALSE;
115 if ( ! $this->set_collation() ) {
116 return FALSE;
118 if ( ! mysql_select_db($this->dbname, $this->dbh) ) {
119 $this->error_message = "unable to select database: '$this->dbname'";
120 return FALSE;
122 return TRUE;
125 public function create_database() {
126 $sql = "create database $this->dbname";
127 if ($this->collate) {
128 $sql .= " character set utf8 collate $this->collate";
129 $this->set_collation();
131 return $this->execute_sql($sql);
134 public function drop_database() {
135 $sql = "drop database if exists $this->dbname";
136 return $this->execute_sql($sql);
139 public function grant_privileges() {
140 return $this->execute_sql( "GRANT ALL PRIVILEGES ON $this->dbname.* TO '$this->login'@'$this->loginhost' IDENTIFIED BY '$this->pass'" );
143 public function disconnect() {
144 return mysql_close($this->dbh);
148 * This method creates any dumpfiles necessary.
149 * This is actually only done if we're cloning an existing site
150 * and we need to dump their database into a file.
151 * @return bool indicating success
153 public function create_dumpfiles() {
154 return $this->dumpSourceDatabase();
157 public function load_dumpfiles() {
158 $sql_results = ''; // information string which is returned
159 foreach ($this->dumpfiles as $filename => $title) {
160 $sql_results .= "Creating $title tables...\n";
161 $fd = fopen($filename, 'r');
162 if ($fd == FALSE) {
163 $this->error_message = "ERROR. Could not open dumpfile '$filename'.\n";
164 return FALSE;
166 $query = "";
167 $line = "";
168 while (!feof ($fd)){
169 $line = fgets($fd,1024);
170 $line = rtrim($line);
171 if (substr($line,0,2) == "--") // Kill comments
172 continue;
173 if (substr($line,0,1) == "#") // Kill comments
174 continue;
175 if ($line == "")
176 continue;
177 $query = $query.$line; // Check for full query
178 $chr = substr($query,strlen($query)-1,1);
179 if ($chr == ";") { // valid query, execute
180 $query = rtrim($query,";");
181 $this->execute_sql( $query );
182 $query = "";
185 $sql_results .= "OK<br>\n";
186 fclose($fd);
188 return $sql_results;
191 public function add_version_info() {
192 include dirname(__FILE__) . "/../../version.php";
193 if ($this->execute_sql("UPDATE version SET v_major = '$v_major', v_minor = '$v_minor', v_patch = '$v_patch', v_tag = '$v_tag', v_database = '$v_database'") == FALSE) {
194 $this->error_message = "ERROR. Unable insert version information into database\n" .
195 "<p>".mysql_error()." (#".mysql_errno().")\n";
196 return FALSE;
198 return TRUE;
201 public function add_initial_user() {
202 if ($this->execute_sql("INSERT INTO groups (id, name, user) VALUES (1,'$this->igroup','$this->iuser')") == FALSE) {
203 $this->error_message = "ERROR. Unable to add initial user group\n" .
204 "<p>".mysql_error()." (#".mysql_errno().")\n";
205 return FALSE;
207 $password_hash = md5( $this->iuserpass );
208 if ($this->execute_sql("INSERT INTO users (id, username, password, authorized, lname, fname, facility_id, calendar, cal_ui) VALUES (1,'$this->iuser','$password_hash',1,'$this->iuname','',3,1,3)") == FALSE) {
209 $this->error_message = "ERROR. Unable to add initial user\n" .
210 "<p>".mysql_error()." (#".mysql_errno().")\n";
211 return FALSE;
213 return TRUE;
217 * Create site directory if it is missing.
218 * @global string $GLOBALS['OE_SITE_DIR'] contains the name of the site directory to create
219 * @return name of the site directory or False
221 public function create_site_directory() {
222 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
223 $source_directory = $GLOBALS['OE_SITES_BASE'] . "/" . $this->source_site_id;
224 $destination_directory = $GLOBALS['OE_SITE_DIR'];
225 if ( ! $this->recurse_copy( $source_directory, $destination_directory ) ) {
226 $this->error_message = "unable to copy directory: '$source_directory' to '$destination_directory'. " . $this->error_message;
227 return False;
230 return True;
233 public function write_configuration_file() {
234 @touch($this->conffile); // php bug
235 $fd = @fopen($this->conffile, 'w');
236 if ( ! $fd ) {
237 $this->error_message = 'unable to open configuration file for writing: ' . $this->conffile;
238 return False;
240 $string = '<?php
241 // OpenEMR
242 // MySQL Config
246 $it_died = 0; //fmg: variable keeps running track of any errors
248 fwrite($fd,$string) or $it_died++;
249 fwrite($fd,"\$host\t= '$this->server';\n") or $it_died++;
250 fwrite($fd,"\$port\t= '$this->port';\n") or $it_died++;
251 fwrite($fd,"\$login\t= '$this->login';\n") or $it_died++;
252 fwrite($fd,"\$pass\t= '$this->pass';\n") or $it_died++;
253 fwrite($fd,"\$dbase\t= '$this->dbname';\n\n") or $it_died++;
254 fwrite($fd,"//Added ability to disable\n") or $it_died++;
255 fwrite($fd,"//utf8 encoding - bm 05-2009\n") or $it_died++;
256 fwrite($fd,"global \$disable_utf8_flag;\n") or $it_died++;
257 fwrite($fd,"\$disable_utf8_flag = false;\n") or $it_died++;
259 $string = '
260 $sqlconf = array();
261 global $sqlconf;
262 $sqlconf["host"]= $host;
263 $sqlconf["port"] = $port;
264 $sqlconf["login"] = $login;
265 $sqlconf["pass"] = $pass;
266 $sqlconf["dbase"] = $dbase;
267 //////////////////////////
268 //////////////////////////
269 //////////////////////////
270 //////DO NOT TOUCH THIS///
271 $config = 1; /////////////
272 //////////////////////////
273 //////////////////////////
274 //////////////////////////
277 ?><?php // done just for coloring
279 fwrite($fd,$string) or $it_died++;
280 fclose($fd) or $it_died++;
282 //it's rather irresponsible to not report errors when writing this file.
283 if ($it_died != 0) {
284 $this->error_message = "ERROR. Couldn't write $it_died lines to config file '$this->conffile'.\n";
285 return FALSE;
288 return TRUE;
291 public function insert_globals() {
292 function xl($s) { return $s; }
293 require(dirname(__FILE__) . '/../globals.inc.php');
294 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
295 foreach ($grparr as $fldid => $fldarr) {
296 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
297 if (substr($fldtype, 0, 2) !== 'm_') {
298 $res = $this->execute_sql("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
299 $row = @mysql_fetch_array($res, MYSQL_ASSOC);
300 if (empty($row['count'])) {
301 $this->execute_sql("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
302 "VALUES ( '$fldid', '0', '$flddef' )");
307 return TRUE;
310 public function install_gacl()
312 $install_results_1 = $this->get_require_contents($this->gaclSetupScript1);
313 if (! $install_results_1 ) {
314 $this->error_message = "install_gacl failed: unable to require gacl script 1";
315 return FALSE;
318 $install_results_2 = $this->get_require_contents($this->gaclSetupScript2);
319 if (! $install_results_2 ) {
320 $this->error_message = "install_gacl failed: unable to require gacl script 2";
321 return FALSE;
323 $this->debug_message .= $install_results_1 . $install_results_2;
324 return TRUE;
327 public function quick_install() {
328 // Validation of OpenEMR user settings
329 // (applicable if not cloning from another database)
330 if (empty($this->clone_database)) {
331 if ( ! $this->login_is_valid() ) {
332 return False;
334 if ( ! $this->iuser_is_valid() ) {
335 return False;
337 if ( ! $this->user_password_is_valid() ) {
338 return False;
341 // Validation of mysql database password
342 if ( ! $this->password_is_valid() ) {
343 return False;
345 // Connect to mysql via root user
346 if (! $this->root_database_connection() ) {
347 return False;
349 // Create the dumpfile
350 // (applicable if cloning from another database)
351 if (! empty($this->clone_database)) {
352 if ( ! $this->create_dumpfiles() ) {
353 return False;
356 // Create the site directory
357 // (applicable if mirroring another local site)
358 if ( ! empty($this->source_site_id) ) {
359 if ( ! $this->create_site_directory() ) {
360 return False;
363 // Create the mysql database
364 if ( ! $this->create_database()) {
365 return False;
367 // Grant user privileges to the mysql database
368 if ( ! $this->grant_privileges() ) {
369 return False;
371 // Connect to mysql via created user
372 $this->disconnect();
373 if ( ! $this->user_database_connection() ) {
374 return False;
376 // Build the database
377 if ( ! $this->load_dumpfiles() ) {
378 return False;
380 // Write the sql configuration file
381 if ( ! $this->write_configuration_file() ) {
382 return False;
384 // Load the version information, globals settings,
385 // initial user, and set up gacl access controls.
386 // (applicable if not cloning from another database)
387 if (empty($this->clone_database)) {
388 if ( ! $this->add_version_info() ) {
389 return False;
391 if ( ! $this->insert_globals() ) {
392 return False;
394 if ( ! $this->add_initial_user() ) {
395 return False;
397 if ( ! $this->install_gacl()) {
398 return False;
402 return True;
405 private function execute_sql( $sql ) {
406 $this->error_message = '';
407 if ( ! $this->dbh ) {
408 $this->user_database_connection();
410 $results = mysql_query($sql, $this->dbh);
411 if ( $results ) {
412 return $results;
413 } else {
414 $this->error_message = "unable to execute SQL: '$sql' due to: " . mysql_error();
415 return False;
419 private function connect_to_database( $server, $user, $password, $port )
421 if ($server == "localhost")
422 $dbh = mysql_connect($server, $user, $password);
423 else
424 $dbh = mysql_connect("$server:$port", $user, $password);
425 return $dbh;
428 private function set_collation()
430 if ($this->collate) {
431 return $this->execute_sql("SET NAMES 'utf8'");
433 return TRUE;
437 * innitialize $this->dumpfiles, an array of the dumpfiles that will
438 * be loaded into the database, including the correct translation
439 * dumpfile.
440 * The keys are the paths of the dumpfiles, and the values are the titles
441 * @return array
443 private function initialize_dumpfile_list() {
444 if ( $this->clone_database ) {
445 $this->dumpfiles = array( $this->get_backup_filename() => 'clone database' );
446 } else {
447 $dumpfiles = array( $this->main_sql => 'Main' );
448 if (! empty($this->development_translations)) {
449 // Use the online development translation set
450 $dumpfiles[ $this->devel_translation_sql ] = "Online Development Language Translations (utf8)";
452 else {
453 // Use the local translation set
454 $dumpfiles[ $this->translation_sql ] = "Language Translation (utf8)";
456 if ($this->ippf_specific) {
457 $dumpfiles[ $this->ippf_sql ] = "IPPF Layout";
459 // Load ICD-9 codes if present.
460 if (file_exists( $this->icd9 )) {
461 $dumpfiles[ $this->icd9 ] = "ICD-9";
463 $this->dumpfiles = $dumpfiles;
465 return $this->dumpfiles;
468 // http://www.php.net/manual/en/function.include.php
469 private function get_require_contents($filename) {
470 if (is_file($filename)) {
471 ob_start();
472 require $filename;
473 $contents = ob_get_contents();
474 ob_end_clean();
475 return $contents;
477 return false;
482 * Directory copy logic borrowed from a user comment at
483 * http://www.php.net/manual/en/function.copy.php
484 * @param string $src name of the directory to copy
485 * @param string $dst name of the destination to copy to
486 * @return bool indicating success
488 private function recurse_copy($src, $dst) {
489 $dir = opendir($src);
490 if ( ! @mkdir($dst) ) {
491 $this->error_message = "unable to create directory: '$dst'";
492 return False;
494 while(false !== ($file = readdir($dir))) {
495 if ($file != '.' && $file != '..') {
496 if (is_dir($src . '/' . $file)) {
497 $this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
499 else {
500 copy($src . '/' . $file, $dst . '/' . $file);
504 closedir($dir);
505 return True;
510 * dump a site's database to a temporary file.
511 * @param string $source_site_id the site_id of the site to dump
512 * @return filename of the backup
514 private function dumpSourceDatabase() {
515 global $OE_SITES_BASE;
516 $source_site_id = $this->source_site_id;
518 include("$OE_SITES_BASE/$source_site_id/sqlconf.php");
520 if (empty($config)) die("Source site $source_site_id has not been set up!");
522 $backup_file = $this->get_backup_filename();
523 $cmd = "mysqldump -u " . escapeshellarg($login) .
524 " -p" . escapeshellarg($pass) .
525 " --opt --skip-extended-insert --quote-names -r $backup_file " .
526 escapeshellarg($dbase);
528 $tmp0 = exec($cmd, $tmp1=array(), $tmp2);
529 if ($tmp2) die("Error $tmp2 running \"$cmd\": $tmp0 " . implode(' ', $tmp1));
531 return $backup_file;
535 * @return filename of the source backup database for cloning
537 private function get_backup_filename() {
538 if (stristr(PHP_OS, 'WIN')) {
539 $backup_file = 'C:/windows/temp/setup_dump.sql';
541 else {
542 $backup_file = '/tmp/setup_dump.sql';
544 return $backup_file;
550 This file is free software: you can redistribute it and/or modify it under the
551 terms of the GNU General Public License as publish by the Free Software
552 Foundation.
554 This file is distributed in the hope that it will be useful, but WITHOUT ANY
555 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
556 PARTICULAR PURPOSE. See the GNU Gneral Public License for more details.
558 You should have received a copy of the GNU General Public Licence along with
559 this file. If not see <http://www.gnu.org/licenses/>.