upgrade.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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) 2005-2010 Regis Houssin <regis.houssin@capnetworks.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * Upgrade scripts can be ran from command line with syntax:
  20. *
  21. * cd htdocs/install
  22. * php upgrade.php 3.4.0 3.5.0
  23. * php upgrade2.php 3.4.0 3.5.0
  24. *
  25. * Return code is 0 if OK, >0 if error
  26. */
  27. /**
  28. * \file htdocs/install/upgrade.php
  29. * \brief Run migration script
  30. */
  31. include_once 'inc.php';
  32. if (! file_exists($conffile))
  33. {
  34. print 'Error: Dolibarr config file was not found. This may means that Dolibarr is not installed yet. Please call the page "/install/index.php" instead of "/install/upgrade.php").';
  35. }
  36. require_once $conffile; if (! isset($dolibarr_main_db_type)) $dolibarr_main_db_type='mysql'; // For backward compatibility
  37. require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
  38. $grant_query='';
  39. $etape = 2;
  40. $ok = 0;
  41. // Cette page peut etre longue. On augmente le delai autorise.
  42. // Ne fonctionne que si on est pas en safe_mode.
  43. $err=error_reporting();
  44. error_reporting(0);
  45. @set_time_limit(120);
  46. error_reporting($err);
  47. $setuplang=GETPOST("selectlang",'',3)?GETPOST("selectlang",'',3):'auto';
  48. $langs->setDefaultLang($setuplang);
  49. $versionfrom=GETPOST("versionfrom",'',3)?GETPOST("versionfrom",'',3):(empty($argv[1])?'':$argv[1]);
  50. $versionto=GETPOST("versionto",'',3)?GETPOST("versionto",'',3):(empty($argv[2])?'':$argv[2]);
  51. $versionmodule=GETPOST("versionmodule",'',3)?GETPOST("versionmodule",'',3):(empty($argv[3])?'':$argv[3]);
  52. $langs->load("admin");
  53. $langs->load("install");
  54. $langs->load("errors");
  55. if ($dolibarr_main_db_type == "mysql") $choix=1;
  56. if ($dolibarr_main_db_type == "mysqli") $choix=1;
  57. if ($dolibarr_main_db_type == "pgsql") $choix=2;
  58. if ($dolibarr_main_db_type == "mssql") $choix=3;
  59. dolibarr_install_syslog("upgrade: Entering upgrade.php page");
  60. if (! is_object($conf)) dolibarr_install_syslog("upgrade2: conf file not initialized",LOG_ERR);
  61. /*
  62. * View
  63. */
  64. pHeader('',"upgrade2",GETPOST('action'),'versionfrom='.$versionfrom.'&versionto='.$versionto);
  65. $actiondone=0;
  66. // Action to launch the migrate script
  67. if (! GETPOST("action") || preg_match('/upgrade/i',GETPOST('action')))
  68. {
  69. $actiondone=1;
  70. print '<h3>'.$langs->trans("DatabaseMigration").'</h3>';
  71. if (! $versionfrom && ! $versionto)
  72. {
  73. print '<div class="error">Parameter versionfrom or versionto missing. Upgrade is launched from page install/index.php (like a first install) instead of install/upgrade.php</div>';
  74. exit;
  75. }
  76. print '<table cellspacing="0" cellpadding="1" border="0" width="100%">';
  77. $error=0;
  78. // If password is encoded, we decode it
  79. if (preg_match('/crypted:/i',$dolibarr_main_db_pass) || ! empty($dolibarr_main_db_encrypted_pass))
  80. {
  81. require_once $dolibarr_main_document_root.'/core/lib/security.lib.php';
  82. if (preg_match('/crypted:/i',$dolibarr_main_db_pass))
  83. {
  84. $dolibarr_main_db_pass = preg_replace('/crypted:/i', '', $dolibarr_main_db_pass);
  85. $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_pass);
  86. $dolibarr_main_db_encrypted_pass = $dolibarr_main_db_pass; // We need to set this as it is used to know the password was initially crypted
  87. }
  88. else $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_encrypted_pass);
  89. }
  90. // $conf is already instancied inside inc.php
  91. $conf->db->type = $dolibarr_main_db_type;
  92. $conf->db->host = $dolibarr_main_db_host;
  93. $conf->db->port = $dolibarr_main_db_port;
  94. $conf->db->name = $dolibarr_main_db_name;
  95. $conf->db->user = $dolibarr_main_db_user;
  96. $conf->db->pass = $dolibarr_main_db_pass;
  97. // Load type and crypt key
  98. if (empty($dolibarr_main_db_encryption)) $dolibarr_main_db_encryption=0;
  99. $conf->db->dolibarr_main_db_encryption = $dolibarr_main_db_encryption;
  100. if (empty($dolibarr_main_db_cryptkey)) $dolibarr_main_db_cryptkey='';
  101. $conf->db->dolibarr_main_db_cryptkey = $dolibarr_main_db_cryptkey;
  102. $db=getDoliDBInstance($conf->db->type,$conf->db->host,$conf->db->user,$conf->db->pass,$conf->db->name,$conf->db->port);
  103. // Create the global $hookmanager object
  104. include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
  105. $hookmanager=new HookManager($db);
  106. if ($db->connected == 1)
  107. {
  108. print '<tr><td class="nowrap">';
  109. print $langs->trans("ServerConnection")." : $dolibarr_main_db_host</td><td align=\"right\">".$langs->trans("OK")."</td></tr>\n";
  110. dolibarr_install_syslog("upgrade: ".$langs->transnoentities("ServerConnection")." : $dolibarr_main_db_host ".$langs->transnoentities("OK"));
  111. $ok = 1;
  112. }
  113. else
  114. {
  115. print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase",$dolibarr_main_db_name)."</td><td align=\"right\">".$langs->transnoentities("Error")."</td></tr>\n";
  116. dolibarr_install_syslog("upgrade: ".$langs->transnoentities("ErrorFailedToConnectToDatabase",$dolibarr_main_db_name));
  117. $ok = 0;
  118. }
  119. if ($ok)
  120. {
  121. if($db->database_selected == 1)
  122. {
  123. print '<tr><td class="nowrap">';
  124. print $langs->trans("DatabaseConnection")." : ".$dolibarr_main_db_name."</td><td align=\"right\">".$langs->trans("OK")."</td></tr>\n";
  125. dolibarr_install_syslog("upgrade: Database connection successfull : $dolibarr_main_db_name");
  126. $ok=1;
  127. }
  128. else
  129. {
  130. print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase",$dolibarr_main_db_name)."</td><td align=\"right\">".$langs->trans("Error")."</td></tr>\n";
  131. dolibarr_install_syslog("upgrade: ".$langs->transnoentities("ErrorFailedToConnectToDatabase",$dolibarr_main_db_name));
  132. $ok=0;
  133. }
  134. }
  135. // Affiche version
  136. if ($ok)
  137. {
  138. $version=$db->getVersion();
  139. $versionarray=$db->getVersionArray();
  140. print '<tr><td>'.$langs->trans("ServerVersion").'</td>';
  141. print '<td align="right">'.$version.'</td></tr>';
  142. dolibarr_install_syslog("upgrade: ".$langs->transnoentities("ServerVersion")." : $version");
  143. // Test database version
  144. $versionmindb=getStaticMember(get_class($db),'versionmin');
  145. //print join('.',$versionarray).' - '.join('.',$versionmindb);
  146. if (count($versionmindb) && count($versionarray)
  147. && versioncompare($versionarray,$versionmindb) < 0)
  148. {
  149. // Warning: database version too low.
  150. print "<tr><td>".$langs->trans("ErrorDatabaseVersionTooLow",join('.',$versionarray),join('.',$versionmindb))."</td><td align=\"right\">".$langs->trans("Error")."</td></tr>\n";
  151. dolibarr_install_syslog("upgrade: ".$langs->transnoentities("ErrorDatabaseVersionTooLow",join('.',$versionarray),join('.',$versionmindb)));
  152. $ok=0;
  153. }
  154. }
  155. // Force l'affichage de la progression
  156. if ($ok)
  157. {
  158. print '<tr><td colspan="2">'.$langs->trans("PleaseBePatient").'</td></tr>';
  159. flush();
  160. }
  161. /*
  162. * Delete duplicates in table categorie_association
  163. */
  164. if ($ok)
  165. {
  166. $result = $db->DDLDescTable(MAIN_DB_PREFIX."categorie_association");
  167. if ($result) // result defined for version 3.2 or -
  168. {
  169. $obj = $db->fetch_object($result);
  170. if ($obj) // It table categorie_association exists
  171. {
  172. $couples=array();
  173. $filles=array();
  174. $sql = "SELECT fk_categorie_mere, fk_categorie_fille";
  175. $sql.= " FROM ".MAIN_DB_PREFIX."categorie_association";
  176. dolibarr_install_syslog("upgrade: search duplicate sql=".$sql);
  177. $resql = $db->query($sql);
  178. if ($resql)
  179. {
  180. $num=$db->num_rows($resql);
  181. while ($obj=$db->fetch_object($resql))
  182. {
  183. if (! isset($filles[$obj->fk_categorie_fille])) // Only one record as child (a child has only on parent).
  184. {
  185. if ($obj->fk_categorie_mere != $obj->fk_categorie_fille)
  186. {
  187. $filles[$obj->fk_categorie_fille]=1; // Set record for this child
  188. $couples[$obj->fk_categorie_mere.'_'.$obj->fk_categorie_fille]=array('mere'=>$obj->fk_categorie_mere, 'fille'=>$obj->fk_categorie_fille);
  189. }
  190. }
  191. }
  192. dolibarr_install_syslog("upgrade: result is num=".$num." count(couples)=".count($couples));
  193. // If there is duplicates couples or child with two parents
  194. if (count($couples) > 0 && $num > count($couples))
  195. {
  196. $error=0;
  197. $db->begin();
  198. // We delete all
  199. $sql="DELETE FROM ".MAIN_DB_PREFIX."categorie_association";
  200. dolibarr_install_syslog("upgrade: delete association sql=".$sql);
  201. $resqld=$db->query($sql);
  202. if ($resqld)
  203. {
  204. // And we insert only each record once
  205. foreach($couples as $key => $val)
  206. {
  207. $sql ="INSERT INTO ".MAIN_DB_PREFIX."categorie_association(fk_categorie_mere,fk_categorie_fille)";
  208. $sql.=" VALUES(".$val['mere'].", ".$val['fille'].")";
  209. dolibarr_install_syslog("upgrade: insert association sql=".$sql);
  210. $resqli=$db->query($sql);
  211. if (! $resqli) $error++;
  212. }
  213. }
  214. if (! $error)
  215. {
  216. print '<tr><td>'.$langs->trans("RemoveDuplicates").'</td>';
  217. print '<td align="right">'.$langs->trans("Success").' ('.$num.'=>'.count($couples).')</td></tr>';
  218. $db->commit();
  219. }
  220. else
  221. {
  222. print '<tr><td>'.$langs->trans("RemoveDuplicates").'</td>';
  223. print '<td align="right">'.$langs->trans("Failed").'</td></tr>';
  224. $db->rollback();
  225. }
  226. }
  227. }
  228. else
  229. {
  230. print '<div class="error">'.$langs->trans("Error").' '.$db->lasterror().'</div>';
  231. }
  232. }
  233. }
  234. }
  235. /*
  236. * Remove deprecated indexes and constraints for Mysql
  237. */
  238. if ($ok && preg_match('/mysql/',$db->type))
  239. {
  240. $versioncommande=array(4,0,0);
  241. if (count($versioncommande) && count($versionarray)
  242. && versioncompare($versioncommande,$versionarray) <= 0) // Si mysql >= 4.0
  243. {
  244. // Suppression vieilles contraintes sans noms et en doubles
  245. // Les contraintes indesirables ont un nom qui commence par 0_ ou se termine par ibfk_999
  246. $listtables=array(
  247. MAIN_DB_PREFIX.'adherent_options',
  248. MAIN_DB_PREFIX.'bank_class',
  249. MAIN_DB_PREFIX.'c_ecotaxe',
  250. MAIN_DB_PREFIX.'c_methode_commande_fournisseur', // table renamed
  251. MAIN_DB_PREFIX.'c_input_method'
  252. );
  253. $listtables = $db->DDLListTables($conf->db->name,'');
  254. foreach ($listtables as $val)
  255. {
  256. // Database prefix filter
  257. if (preg_match('/^'.MAIN_DB_PREFIX.'/', $val))
  258. {
  259. //print "x".$val."<br>";
  260. $sql = "SHOW CREATE TABLE ".$val;
  261. $resql = $db->query($sql);
  262. if ($resql)
  263. {
  264. $values=$db->fetch_array($resql);
  265. $i=0;
  266. $createsql=$values[1];
  267. while (preg_match('/CONSTRAINT `(0_[0-9a-zA-Z]+|[_0-9a-zA-Z]+_ibfk_[0-9]+)`/i',$createsql,$reg) && $i < 100)
  268. {
  269. $sqldrop="ALTER TABLE ".$val." DROP FOREIGN KEY ".$reg[1];
  270. $resqldrop = $db->query($sqldrop);
  271. if ($resqldrop)
  272. {
  273. print '<tr><td colspan="2">'.$sqldrop.";</td></tr>\n";
  274. }
  275. $createsql=preg_replace('/CONSTRAINT `'.$reg[1].'`/i','XXX',$createsql);
  276. $i++;
  277. }
  278. $db->free($resql);
  279. }
  280. else
  281. {
  282. if ($db->lasterrno() != 'DB_ERROR_NOSUCHTABLE')
  283. {
  284. print '<tr><td colspan="2"><font class="error">'.$sql.' : '.$db->lasterror()."</font></td></tr>\n";
  285. }
  286. }
  287. }
  288. }
  289. }
  290. }
  291. /*
  292. * Load sql files
  293. */
  294. if ($ok)
  295. {
  296. $dir = "mysql/migration/"; // We use mysql migration scripts whatever is database driver
  297. if (! empty($versionmodule)) $dir=dol_buildpath('/'.$versionmodule.'/sql/',0);
  298. // Clean last part to exclude minor version x.y.z -> x.y
  299. $newversionfrom=preg_replace('/(\.[0-9]+)$/i','.0',$versionfrom);
  300. $newversionto=preg_replace('/(\.[0-9]+)$/i','.0',$versionto);
  301. $filelist=array();
  302. $i = 0;
  303. $ok = 0;
  304. $from='^'.$newversionfrom;
  305. $to=$newversionto.'\.sql$';
  306. // Get files list
  307. $filesindir=array();
  308. $handle=opendir($dir);
  309. if (is_resource($handle))
  310. {
  311. while (($file = readdir($handle))!==false)
  312. {
  313. if (preg_match('/\.sql$/i',$file)) $filesindir[]=$file;
  314. }
  315. sort($filesindir);
  316. }
  317. else
  318. {
  319. print '<div class="error">'.$langs->trans("ErrorCanNotReadDir",$dir).'</div>';
  320. }
  321. // Define which file to run
  322. foreach($filesindir as $file)
  323. {
  324. if (preg_match('/'.$from.'/i',$file))
  325. {
  326. $filelist[]=$file;
  327. }
  328. else if (preg_match('/'.$to.'/i',$file)) // First test may be false if we migrate from x.y.* to x.y.*
  329. {
  330. $filelist[]=$file;
  331. }
  332. }
  333. if (count($filelist) == 0)
  334. {
  335. print '<div class="error">'.$langs->trans("ErrorNoMigrationFilesFoundForParameters").'</div>';
  336. }
  337. else
  338. {
  339. // Loop on each migrate files
  340. foreach($filelist as $file)
  341. {
  342. print '<tr><td colspan="2"><hr></td></tr>';
  343. print '<tr><td class="nowrap">'.$langs->trans("ChoosedMigrateScript").'</td><td align="right">'.$file.'</td></tr>'."\n";
  344. // Run sql script
  345. $ok=run_sql($dir.$file, 0, '', 1);
  346. // Scan if there is migration scripts for modules htdocs/module/sql or htdocs/custom/module/sql
  347. $modulesfile = array();
  348. foreach ($conf->file->dol_document_root as $type => $dirroot)
  349. {
  350. $handlemodule=@opendir($dirroot); // $dirroot may be '..'
  351. if (is_resource($handlemodule))
  352. {
  353. while (($filemodule = readdir($handlemodule))!==false)
  354. {
  355. if (! preg_match('/\./',$filemodule) && is_dir($dirroot.'/'.$filemodule.'/sql')) // We exclude filemodule that contains . (are not directories) and are not directories.
  356. {
  357. //print "Scan for ".$dirroot . '/' . $filemodule . '/sql/'.$file;
  358. if (is_file($dirroot . '/' . $filemodule . '/sql/'.$file))
  359. {
  360. $modulesfile[$dirroot . '/' . $filemodule . '/sql/'.$file] = '/' . $filemodule . '/sql/'.$file;
  361. }
  362. }
  363. }
  364. closedir($handlemodule);
  365. }
  366. }
  367. foreach ($modulesfile as $modulefilelong => $modulefileshort)
  368. {
  369. print '<tr><td colspan="2"><hr></td></tr>';
  370. print '<tr><td class="nowrap">'.$langs->trans("ChoosedMigrateScript").' (external modules)</td><td align="right">'.$modulefileshort.'</td></tr>'."\n";
  371. // Run sql script
  372. $okmodule=run_sql($modulefilelong, 0, '', 1); // Note: Result of migration of external module should not decide if we continue migration of Dolibarr or not.
  373. }
  374. }
  375. }
  376. }
  377. print '</table>';
  378. if ($db->connected) $db->close();
  379. }
  380. if (empty($actiondone))
  381. {
  382. print '<div class="error">'.$langs->trans("ErrorWrongParameters").'</div>';
  383. }
  384. $ret=0;
  385. if (! $ok && isset($argv[1])) $ret=1;
  386. dol_syslog("Exit ".$ret);
  387. pFooter(((! $ok && empty($_GET["ignoreerrors"])) || $versionmodule),$setuplang);
  388. if ($db->connected) $db->close();
  389. // Return code if ran from command line
  390. if ($ret) exit($ret);