1<?php
2/* Copyright (C) 2004       Rodolphe Quiedeville    <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2010  Laurent Destailleur     <eldy@users.sourceforge.net>
4 * Copyright (C) 2015       Cedric GROSS            <c.gross@kreiz-it.fr>
5 * Copyright (C) 2015-2016  Raphaël Doursenaud      <rdoursenaud@gpcsolutions.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21/**
22 *      \file       htdocs/install/step2.php
23 *      \ingroup    install
24 *      \brief      Create tables, primary keys, foreign keys, indexes and functions into database and then load reference data
25 */
26
27include 'inc.php';
28require_once $dolibarr_main_document_root.'/core/class/conf.class.php';
29require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
30
31global $langs;
32
33$step = 2;
34$ok = 0;
35
36
37// Cette page peut etre longue. On augmente le delai autorise.
38// Ne fonctionne que si on est pas en safe_mode.
39$err = error_reporting();
40error_reporting(0); // Disable all errors
41//error_reporting(E_ALL);
42@set_time_limit(1800); // Need 1800 on some very slow OS like Windows 7/64
43error_reporting($err);
44
45$action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : (empty($argv[1]) ? '' : $argv[1]);
46$setuplang = GETPOST('selectlang', 'aZ09', 3) ?GETPOST('selectlang', 'aZ09', 3) : (empty($argv[2]) ? 'auto' : $argv[2]);
47$langs->setDefaultLang($setuplang);
48
49$langs->loadLangs(array("admin", "install"));
50
51$choix = 0;
52if ($dolibarr_main_db_type == "mysqli") {
53	$choix = 1;
54}
55if ($dolibarr_main_db_type == "pgsql") {
56	$choix = 2;
57}
58if ($dolibarr_main_db_type == "mssql") {
59	$choix = 3;
60}
61if ($dolibarr_main_db_type == "sqlite") {
62	$choix = 4;
63}
64if ($dolibarr_main_db_type == "sqlite3") {
65	$choix = 5;
66}
67
68//if (empty($choix)) dol_print_error('','Database type '.$dolibarr_main_db_type.' not supported into step2.php page');
69
70// Now we load forced values from install.forced.php file.
71$useforcedwizard = false;
72$forcedfile = "./install.forced.php";
73if ($conffile == "/etc/dolibarr/conf.php") {
74	$forcedfile = "/etc/dolibarr/install.forced.php";
75}
76if (@file_exists($forcedfile)) {
77	$useforcedwizard = true;
78	include_once $forcedfile;
79	// test for travis
80	if (!empty($argv[1]) && $argv[1] == "set") {
81		$action = "set";
82	}
83}
84
85dolibarr_install_syslog("- step2: entering step2.php page");
86
87
88/*
89 *	View
90 */
91
92pHeader($langs->trans("CreateDatabaseObjects"), "step4");
93
94// Test if we can run a first install process
95if (!is_writable($conffile)) {
96	print $langs->trans("ConfFileIsNotWritable", $conffiletoshow);
97	pFooter(1, $setuplang, 'jscheckparam');
98	exit;
99}
100
101if ($action == "set") {
102	print '<h3><img class="valignmiddle inline-block paddingright" src="../theme/common/octicons/build/svg/database.svg" width="20" alt="Database"> '.$langs->trans("Database").'</h3>';
103
104	print '<table cellspacing="0" style="padding: 4px 4px 4px 0" border="0" width="100%">';
105	$error = 0;
106
107	$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, $conf->db->port);
108
109	if ($db->connected) {
110		print "<tr><td>";
111		print $langs->trans("ServerConnection")." : ".$conf->db->host.'</td><td><img src="../theme/eldy/img/tick.png" alt="Ok"></td></tr>';
112		$ok = 1;
113	} else {
114		print "<tr><td>Failed to connect to server : ".$conf->db->host.'</td><td><img src="../theme/eldy/img/error.png" alt="Error"></td></tr>';
115	}
116
117	if ($ok) {
118		if ($db->database_selected) {
119			dolibarr_install_syslog("step2: successful connection to database: ".$conf->db->name);
120		} else {
121			dolibarr_install_syslog("step2: failed connection to database :".$conf->db->name, LOG_ERR);
122			print "<tr><td>Failed to select database ".$conf->db->name.'</td><td><img src="../theme/eldy/img/error.png" alt="Error"></td></tr>';
123			$ok = 0;
124		}
125	}
126
127
128	// Affiche version
129	if ($ok) {
130		$version = $db->getVersion();
131		$versionarray = $db->getVersionArray();
132		print '<tr><td>'.$langs->trans("DatabaseVersion").'</td>';
133		print '<td>'.$version.'</td></tr>';
134		//print '<td class="right">'.join('.',$versionarray).'</td></tr>';
135
136		print '<tr><td>'.$langs->trans("DatabaseName").'</td>';
137		print '<td>'.$db->database_name.'</td></tr>';
138		//print '<td class="right">'.join('.',$versionarray).'</td></tr>';
139	}
140
141	$requestnb = 0;
142
143	// To disable some code, so you can call step2 with url like
144	// http://localhost/dolibarrnew/install/step2.php?action=set&createtables=0&createkeys=0&createfunctions=0&createdata=llx_20_c_departements
145	$createtables = isset($_GET['createtables']) ?GETPOST('createtables') : 1;
146	$createkeys = isset($_GET['createkeys']) ?GETPOST('createkeys') : 1;
147	$createfunctions = isset($_GET['createfunctions']) ?GETPOST('createfunction') : 1;
148	$createdata = isset($_GET['createdata']) ?GETPOST('createdata') : 1;
149
150
151	// To say sql requests are escaped for mysql so we need to unescape them
152	$db->unescapeslashquot = true;
153
154
155	/**************************************************************************************
156	 *
157	 * Chargement fichiers tables/*.sql (non *.key.sql)
158	 * A faire avant les fichiers *.key.sql
159	 *
160	 ***************************************************************************************/
161	if ($ok && $createtables) {
162		// We always choose in mysql directory (Conversion is done by driver to translate SQL syntax)
163		$dir = "mysql/tables/";
164
165		$ok = 0;
166		$handle = opendir($dir);
167		dolibarr_install_syslog("step2: open tables directory ".$dir." handle=".$handle);
168		$tablefound = 0;
169		$tabledata = array();
170		if (is_resource($handle)) {
171			while (($file = readdir($handle)) !== false) {
172				if (preg_match('/\.sql$/i', $file) && preg_match('/^llx_/i', $file) && !preg_match('/\.key\.sql$/i', $file)) {
173					$tablefound++;
174					$tabledata[] = $file;
175				}
176			}
177			closedir($handle);
178		}
179
180		// Sort list of sql files on alphabetical order (load order is important)
181		sort($tabledata);
182		foreach ($tabledata as $file) {
183			$name = substr($file, 0, dol_strlen($file) - 4);
184			$buffer = '';
185			$fp = fopen($dir.$file, "r");
186			if ($fp) {
187				while (!feof($fp)) {
188					$buf = fgets($fp, 4096);
189					if (substr($buf, 0, 2) <> '--') {
190						$buf = preg_replace('/--(.+)*/', '', $buf);
191						$buffer .= $buf;
192					}
193				}
194				fclose($fp);
195
196				$buffer = trim($buffer);
197				if ($conf->db->type == 'mysql' || $conf->db->type == 'mysqli') {	// For Mysql 5.5+, we must replace type=innodb with ENGINE=innodb
198					$buffer = preg_replace('/type=innodb/i', 'ENGINE=innodb', $buffer);
199				} else {
200					// Keyword ENGINE is MySQL-specific, so scrub it for
201					// other database types (mssql, pgsql)
202					$buffer = preg_replace('/type=innodb/i', '', $buffer);
203					$buffer = preg_replace('/ENGINE=innodb/i', '', $buffer);
204				}
205
206				// Replace the prefix tables
207				if ($dolibarr_main_db_prefix != 'llx_') {
208					$buffer = preg_replace('/llx_/i', $dolibarr_main_db_prefix, $buffer);
209				}
210
211				//print "<tr><td>Creation de la table $name/td>";
212				$requestnb++;
213
214				dolibarr_install_syslog("step2: request: ".$buffer);
215				$resql = $db->query($buffer, 0, 'dml');
216				if ($resql) {
217					// print "<td>OK requete ==== $buffer</td></tr>";
218					$db->free($resql);
219				} else {
220					if ($db->errno() == 'DB_ERROR_TABLE_ALREADY_EXISTS' ||
221					$db->errno() == 'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS') {
222						//print "<td>Deja existante</td></tr>";
223					} else {
224						print "<tr><td>".$langs->trans("CreateTableAndPrimaryKey", $name);
225						print "<br>\n".$langs->trans("Request").' '.$requestnb.' : '.$buffer.' <br>Executed query : '.$db->lastquery;
226						print "\n</td>";
227						print '<td><span class="error">'.$langs->trans("ErrorSQL")." ".$db->errno()." ".$db->error().'</span></td></tr>';
228						$error++;
229					}
230				}
231			} else {
232				print "<tr><td>".$langs->trans("CreateTableAndPrimaryKey", $name);
233				print "</td>";
234				print '<td><span class="error">'.$langs->trans("Error").' Failed to open file '.$dir.$file.'</span></td></tr>';
235				$error++;
236				dolibarr_install_syslog("step2: failed to open file ".$dir.$file, LOG_ERR);
237			}
238		}
239
240		if ($tablefound) {
241			if ($error == 0) {
242				print '<tr><td>';
243				print $langs->trans("TablesAndPrimaryKeysCreation").'</td><td><img src="../theme/eldy/img/tick.png" alt="Ok"></td></tr>';
244				$ok = 1;
245			}
246		} else {
247			print '<tr><td>'.$langs->trans("ErrorFailedToFindSomeFiles", $dir).'</td><td><img src="../theme/eldy/img/error.png" alt="Error"></td></tr>';
248			dolibarr_install_syslog("step2: failed to find files to create database in directory ".$dir, LOG_ERR);
249		}
250	}
251
252
253	/***************************************************************************************
254	 *
255	 * Chargement fichiers tables/*.key.sql
256	 * A faire apres les fichiers *.sql
257	 *
258	 ***************************************************************************************/
259	if ($ok && $createkeys) {
260		// We always choose in mysql directory (Conversion is done by driver to translate SQL syntax)
261		$dir = "mysql/tables/";
262
263		$okkeys = 0;
264		$handle = opendir($dir);
265		dolibarr_install_syslog("step2: open keys directory ".$dir." handle=".$handle);
266		$tablefound = 0;
267		$tabledata = array();
268		if (is_resource($handle)) {
269			while (($file = readdir($handle)) !== false) {
270				if (preg_match('/\.sql$/i', $file) && preg_match('/^llx_/i', $file) && preg_match('/\.key\.sql$/i', $file)) {
271					$tablefound++;
272					$tabledata[] = $file;
273				}
274			}
275			closedir($handle);
276		}
277
278		// Sort list of sql files on alphabetical order (load order is important)
279		sort($tabledata);
280		foreach ($tabledata as $file) {
281			$name = substr($file, 0, dol_strlen($file) - 4);
282			//print "<tr><td>Creation de la table $name</td>";
283			$buffer = '';
284			$fp = fopen($dir.$file, "r");
285			if ($fp) {
286				while (!feof($fp)) {
287					$buf = fgets($fp, 4096);
288
289					// Special case of lines allowed for some version only
290					if ($choix == 1 && preg_match('/^--\sV([0-9\.]+)/i', $buf, $reg)) {
291						$versioncommande = explode('.', $reg[1]);
292						//print var_dump($versioncommande);
293						//print var_dump($versionarray);
294						if (count($versioncommande) && count($versionarray)
295						&& versioncompare($versioncommande, $versionarray) <= 0) {
296							// Version qualified, delete SQL comments
297							$buf = preg_replace('/^--\sV([0-9\.]+)/i', '', $buf);
298							//print "Ligne $i qualifiee par version: ".$buf.'<br>';
299						}
300					}
301					if ($choix == 2 && preg_match('/^--\sPOSTGRESQL\sV([0-9\.]+)/i', $buf, $reg)) {
302						$versioncommande = explode('.', $reg[1]);
303						//print var_dump($versioncommande);
304						//print var_dump($versionarray);
305						if (count($versioncommande) && count($versionarray)
306						&& versioncompare($versioncommande, $versionarray) <= 0) {
307							// Version qualified, delete SQL comments
308							$buf = preg_replace('/^--\sPOSTGRESQL\sV([0-9\.]+)/i', '', $buf);
309							//print "Ligne $i qualifiee par version: ".$buf.'<br>';
310						}
311					}
312
313					// Ajout ligne si non commentaire
314					if (!preg_match('/^--/i', $buf)) {
315						$buffer .= $buf;
316					}
317				}
318				fclose($fp);
319
320				// Si plusieurs requetes, on boucle sur chaque
321				$listesql = explode(';', $buffer);
322				foreach ($listesql as $req) {
323					$buffer = trim($req);
324					if ($buffer) {
325						// Replace the prefix tables
326						if ($dolibarr_main_db_prefix != 'llx_') {
327							$buffer = preg_replace('/llx_/i', $dolibarr_main_db_prefix, $buffer);
328						}
329
330						//print "<tr><td>Creation des cles et index de la table $name: '$buffer'</td>";
331						$requestnb++;
332
333						dolibarr_install_syslog("step2: request: ".$buffer);
334						$resql = $db->query($buffer, 0, 'dml');
335						if ($resql) {
336							//print "<td>OK requete ==== $buffer</td></tr>";
337							$db->free($resql);
338						} else {
339							if ($db->errno() == 'DB_ERROR_KEY_NAME_ALREADY_EXISTS' ||
340							$db->errno() == 'DB_ERROR_CANNOT_CREATE' ||
341							$db->errno() == 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS' ||
342							$db->errno() == 'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS' ||
343							preg_match('/duplicate key name/i', $db->error())) {
344								//print "<td>Deja existante</td></tr>";
345								$key_exists = 1;
346							} else {
347								print "<tr><td>".$langs->trans("CreateOtherKeysForTable", $name);
348								print "<br>\n".$langs->trans("Request").' '.$requestnb.' : '.$db->lastqueryerror();
349								print "\n</td>";
350								print '<td><span class="error">'.$langs->trans("ErrorSQL")." ".$db->errno()." ".$db->error().'</span></td></tr>';
351								$error++;
352							}
353						}
354					}
355				}
356			} else {
357				print "<tr><td>".$langs->trans("CreateOtherKeysForTable", $name);
358				print "</td>";
359				print '<td><span class="error">'.$langs->trans("Error")." Failed to open file ".$dir.$file."</span></td></tr>";
360				$error++;
361				dolibarr_install_syslog("step2: failed to open file ".$dir.$file, LOG_ERR);
362			}
363		}
364
365		if ($tablefound && $error == 0) {
366			print '<tr><td>';
367			print $langs->trans("OtherKeysCreation").'</td><td><img src="../theme/eldy/img/tick.png" alt="Ok"></td></tr>';
368			$okkeys = 1;
369		}
370	}
371
372
373	/***************************************************************************************
374	 *
375	 * Chargement fichier functions.sql
376	 *
377	 ***************************************************************************************/
378	if ($ok && $createfunctions) {
379		// For this file, we use a directory according to database type
380		if ($choix == 1) {
381			$dir = "mysql/functions/";
382		} elseif ($choix == 2) {
383			$dir = "pgsql/functions/";
384		} elseif ($choix == 3) {
385			$dir = "mssql/functions/";
386		} elseif ($choix == 4) {
387			$dir = "sqlite3/functions/";
388		}
389
390		// Creation donnees
391		$file = "functions.sql";
392		if (file_exists($dir.$file)) {
393			$fp = fopen($dir.$file, "r");
394			dolibarr_install_syslog("step2: open function file ".$dir.$file." handle=".$fp);
395			if ($fp) {
396				$buffer = '';
397				while (!feof($fp)) {
398					$buf = fgets($fp, 4096);
399					if (substr($buf, 0, 2) <> '--') {
400						$buffer .= $buf."§";
401					}
402				}
403				fclose($fp);
404			}
405			//$buffer=preg_replace('/;\';/',";'§",$buffer);
406
407			// If several requests, we loop on each of them
408			$listesql = explode('§', $buffer);
409			foreach ($listesql as $buffer) {
410				$buffer = trim($buffer);
411				if ($buffer) {
412					// Replace the prefix in table names
413					if ($dolibarr_main_db_prefix != 'llx_') {
414						$buffer = preg_replace('/llx_/i', $dolibarr_main_db_prefix, $buffer);
415					}
416					dolibarr_install_syslog("step2: request: ".$buffer);
417					print "<!-- Insert line : ".$buffer."<br>-->\n";
418					$resql = $db->query($buffer, 0, 'dml');
419					if ($resql) {
420						$ok = 1;
421						$db->free($resql);
422					} else {
423						if ($db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS'
424						|| $db->errno() == 'DB_ERROR_KEY_NAME_ALREADY_EXISTS') {
425							//print "Insert line : ".$buffer."<br>\n";
426						} else {
427							$ok = 0;
428
429							print "<tr><td>".$langs->trans("FunctionsCreation");
430							print "<br>\n".$langs->trans("Request").' '.$requestnb.' : '.$buffer;
431							print "\n</td>";
432							print '<td><span class="error">'.$langs->trans("ErrorSQL")." ".$db->errno()." ".$db->error().'</span></td></tr>';
433							$error++;
434						}
435					}
436				}
437			}
438
439			print "<tr><td>".$langs->trans("FunctionsCreation")."</td>";
440			if ($ok) {
441				print '<td><img src="../theme/eldy/img/tick.png" alt="Ok"></td></tr>';
442			} else {
443				print '<td><img src="../theme/eldy/img/error.png" alt="Error"></td></tr>';
444				$ok = 1;
445			}
446		}
447	}
448
449
450	/***************************************************************************************
451	 *
452	 * Load files data/*.sql
453	 *
454	 ***************************************************************************************/
455	if ($ok && $createdata) {
456		// We always choose in mysql directory (Conversion is done by driver to translate SQL syntax)
457		$dir = "mysql/data/";
458
459		// Insert data
460		$handle = opendir($dir);
461		dolibarr_install_syslog("step2: open directory data ".$dir." handle=".$handle);
462		$tablefound = 0;
463		$tabledata = array();
464		if (is_resource($handle)) {
465			while (($file = readdir($handle)) !== false) {
466				if (preg_match('/\.sql$/i', $file) && preg_match('/^llx_/i', $file)) {
467					if (preg_match('/^llx_accounting_account_/', $file)) {
468						continue; // We discard data file of chart of account. Will be loaded when a chart is selected.
469					}
470
471					//print 'x'.$file.'-'.$createdata.'<br>';
472					if (is_numeric($createdata) || preg_match('/'.preg_quote($createdata).'/i', $file)) {
473						$tablefound++;
474						$tabledata[] = $file;
475					}
476				}
477			}
478			closedir($handle);
479		}
480
481		// Sort list of data files on alphabetical order (load order is important)
482		sort($tabledata);
483		foreach ($tabledata as $file) {
484			$name = substr($file, 0, dol_strlen($file) - 4);
485			$fp = fopen($dir.$file, "r");
486			dolibarr_install_syslog("step2: open data file ".$dir.$file." handle=".$fp);
487			if ($fp) {
488				$arrayofrequests = array();
489				$linefound = 0;
490				$linegroup = 0;
491				$sizeofgroup = 1; // Grouping request to have 1 query for several requests does not works with mysql, so we use 1.
492
493				// Load all requests
494				while (!feof($fp)) {
495					$buffer = fgets($fp, 4096);
496					$buffer = trim($buffer);
497					if ($buffer) {
498						if (substr($buffer, 0, 2) == '--') {
499							continue;
500						}
501
502						if ($linefound && ($linefound % $sizeofgroup) == 0) {
503							$linegroup++;
504						}
505						if (empty($arrayofrequests[$linegroup])) {
506							$arrayofrequests[$linegroup] = $buffer;
507						} else {
508							$arrayofrequests[$linegroup] .= " ".$buffer;
509						}
510
511						$linefound++;
512					}
513				}
514				fclose($fp);
515
516				dolibarr_install_syslog("step2: found ".$linefound." records, defined ".count($arrayofrequests)." group(s).");
517
518				$okallfile = 1;
519				$db->begin();
520
521				// We loop on each requests of file
522				foreach ($arrayofrequests as $buffer) {
523					// Replace the prefix tables
524					if ($dolibarr_main_db_prefix != 'llx_') {
525						$buffer = preg_replace('/llx_/i', $dolibarr_main_db_prefix, $buffer);
526					}
527
528					//dolibarr_install_syslog("step2: request: " . $buffer);
529					$resql = $db->query($buffer, 1);
530					if ($resql) {
531						//$db->free($resql);     // Not required as request we launch here does not return memory needs.
532					} else {
533						if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
534							//print "<tr><td>Insertion ligne : $buffer</td><td>";
535						} else {
536							$ok = 0;
537							$okallfile = 0;
538							print '<span class="error">'.$langs->trans("ErrorSQL")." : ".$db->lasterrno()." - ".$db->lastqueryerror()." - ".$db->lasterror()."</span><br>";
539						}
540					}
541				}
542
543				if ($okallfile) {
544					$db->commit();
545				} else {
546					$db->rollback();
547				}
548			}
549		}
550
551		print "<tr><td>".$langs->trans("ReferenceDataLoading")."</td>";
552		if ($ok) {
553			print '<td><img src="../theme/eldy/img/tick.png" alt="Ok"></td></tr>';
554		} else {
555			print '<td><img src="../theme/eldy/img/error.png" alt="Error"></td></tr>';
556			$ok = 1; // Data loading are not blocking errors
557		}
558	}
559	print '</table>';
560} else {
561	print 'Parameter action=set not defined';
562}
563
564
565$ret = 0;
566if (!$ok && isset($argv[1])) {
567	$ret = 1;
568}
569dolibarr_install_syslog("Exit ".$ret);
570
571dolibarr_install_syslog("- step2: end");
572
573
574$out  = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno" value="checked" checked="true"> ';
575$out .= '<label for="dolibarrpingno">'.$langs->trans("MakeAnonymousPing").'</label>';
576
577$out .= '<!-- Add js script to manage the uncheck of option to not send the ping -->';
578$out .= '<script type="text/javascript">';
579$out .= 'jQuery(document).ready(function(){';
580$out .= '  document.cookie = "DOLINSTALLNOPING_'.md5($dolibarr_main_instance_unique_id).'=0; path=/"'."\n";
581$out .= '  jQuery("#dolibarrpingno").click(function() {';
582$out .= '    if (! $(this).is(\':checked\')) {';
583$out .= '      console.log("We uncheck anonymous ping");';
584$out .= '      document.cookie = "DOLINSTALLNOPING_'.md5($dolibarr_main_instance_unique_id).'=1; path=/"'."\n";
585$out .= '    }';
586$out .= '  });';
587$out .= '});';
588$out .= '</script>';
589
590print $out;
591
592pFooter($ok ? 0 : 1, $setuplang);
593
594if (isset($db) && is_object($db)) {
595	$db->close();
596}
597
598// Return code if ran from command line
599if ($ret) {
600	exit($ret);
601}
602