setuppage.class.inc.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * Web page used for displaying the login form
  18. *
  19. * @author Erwan Taloc <erwan.taloc@combodo.com>
  20. * @author Romain Quetiez <romain.quetiez@combodo.com>
  21. * @author Denis Flaven <denis.flaven@combodo.com>
  22. * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
  23. */
  24. require_once(APPROOT."/application/nicewebpage.class.inc.php");
  25. define('INSTALL_LOG_FILE', APPROOT.'/setup.log');
  26. define ('MODULE_ACTION_OPTIONAL', 1);
  27. define ('MODULE_ACTION_MANDATORY', 2);
  28. define ('MODULE_ACTION_IMPOSSIBLE', 3);
  29. define ('ROOT_MODULE', '_Root_'); // Convention to store IN MEMORY the name/version of the root module i.e. application
  30. date_default_timezone_set('Europe/Paris');
  31. class SetupWebPage extends NiceWebPage
  32. {
  33. public function __construct($sTitle)
  34. {
  35. parent::__construct($sTitle);
  36. $this->add_linked_script("../js/jquery.blockUI.js");
  37. $this->add_linked_script("./setup.js");
  38. $this->add_style("
  39. body {
  40. background-color: #eee;
  41. margin: 0;
  42. padding: 0;
  43. font-size: 10pt;
  44. overflow-y: auto;
  45. }
  46. #header {
  47. width: 600px;
  48. margin-left: auto;
  49. margin-right: auto;
  50. margin-top: 50px;
  51. padding: 20px;
  52. background: #f6f6f1;
  53. height: 54px;
  54. border-top: 1px solid #000;
  55. border-left: 1px solid #000;
  56. border-right: 1px solid #000;
  57. }
  58. #header img {
  59. border: 0;
  60. vertical-align: middle;
  61. margin-right: 20px;
  62. }
  63. #header h1 {
  64. vertical-align: middle;
  65. height: 54px;
  66. noline-height: 54px;
  67. margin: 0;
  68. }
  69. #setup {
  70. width: 600px;
  71. margin-left: auto;
  72. margin-right: auto;
  73. padding: 20px;
  74. background-color: #fff;
  75. border-left: 1px solid #000;
  76. border-right: 1px solid #000;
  77. border-bottom: 1px solid #000;
  78. }
  79. .center {
  80. text-align: center;
  81. }
  82. h1 {
  83. color: #1C94C4;
  84. font-size: 16pt;
  85. }
  86. h2 {
  87. color: #000;
  88. font-size: 14pt;
  89. }
  90. h3 {
  91. color: #1C94C4;
  92. font-size: 12pt;
  93. font-weight: bold;
  94. }
  95. .next {
  96. width: 100%;
  97. text-align: right;
  98. }
  99. .v-spacer {
  100. padding-top: 1em;
  101. }
  102. button {
  103. margin-top: 1em;
  104. padding-left: 1em;
  105. padding-right: 1em;
  106. }
  107. p.info {
  108. padding-left: 50px;
  109. background: url(../images/info-mid.png) no-repeat left -5px;
  110. min-height: 48px;
  111. }
  112. p.ok {
  113. padding-left: 50px;
  114. background: url(../images/clean-mid.png) no-repeat left -8px;
  115. min-height: 48px;
  116. }
  117. p.warning {
  118. padding-left: 50px;
  119. background: url(../images/messagebox_warning-mid.png) no-repeat left -5px;
  120. min-height: 48px;
  121. }
  122. p.error {
  123. padding-left: 50px;
  124. background: url(../images/stop-mid.png) no-repeat left -5px;
  125. min-height: 48px;
  126. }
  127. td.label {
  128. text-align: left;
  129. }
  130. label.read-only {
  131. color: #666;
  132. cursor: text;
  133. }
  134. td.input {
  135. text-align: left;
  136. }
  137. table.formTable {
  138. border: 0;
  139. cellpadding: 2px;
  140. cellspacing: 0;
  141. }
  142. .wizlabel, .wizinput {
  143. color: #000;
  144. font-size: 10pt;
  145. }
  146. .wizhelp {
  147. color: #333;
  148. font-size: 8pt;
  149. }
  150. #progress {
  151. border:1px solid #000000;
  152. width: 180px;
  153. height: 20px;
  154. line-height: 20px;
  155. text-align: center;
  156. margin: 5px;
  157. }
  158. h3.clickable {
  159. background: url(../images/plus.gif) no-repeat left;
  160. padding-left:16px;
  161. cursor: hand;
  162. }
  163. h3.clickable.open {
  164. background: url(../images/minus.gif) no-repeat left;
  165. padding-left:16px;
  166. cursor: hand;
  167. }
  168. ");
  169. }
  170. public function info($sText)
  171. {
  172. $this->add("<p class=\"info\">$sText</p>\n");
  173. $this->log_info($sText);
  174. }
  175. public function ok($sText)
  176. {
  177. $this->add("<p class=\"ok\">$sText</p>\n");
  178. $this->log_ok($sText);
  179. }
  180. public function warning($sText)
  181. {
  182. $this->add("<p class=\"warning\">$sText</p>\n");
  183. $this->log_warning($sText);
  184. }
  185. public function error($sText)
  186. {
  187. $this->add("<p class=\"error\">$sText</p>\n");
  188. $this->log_error($sText);
  189. }
  190. public function form($aData)
  191. {
  192. $this->add("<table class=\"formTable\">\n");
  193. foreach($aData as $aRow)
  194. {
  195. $this->add("<tr>\n");
  196. if (isset($aRow['label']) && isset($aRow['input']) && isset($aRow['help']))
  197. {
  198. $this->add("<td class=\"wizlabel\">{$aRow['label']}</td>\n");
  199. $this->add("<td class=\"wizinput\">{$aRow['input']}</td>\n");
  200. $this->add("<td class=\"wizhelp\">{$aRow['help']}</td>\n");
  201. }
  202. else if (isset($aRow['label']) && isset($aRow['help']))
  203. {
  204. $this->add("<td colspan=\"2\" class=\"wizlabel\">{$aRow['label']}</td>\n");
  205. $this->add("<td class=\"wizhelp\">{$aRow['help']}</td>\n");
  206. }
  207. else if (isset($aRow['label']) && isset($aRow['input']))
  208. {
  209. $this->add("<td class=\"wizlabel\">{$aRow['label']}</td>\n");
  210. $this->add("<td colspan=\"2\" class=\"wizinput\">{$aRow['input']}</td>\n");
  211. }
  212. else if (isset($aRow['label']))
  213. {
  214. $this->add("<td colspan=\"3\" class=\"wizlabel\">{$aRow['label']}</td>\n");
  215. }
  216. $this->add("</tr>\n");
  217. }
  218. $this->add("</table>\n");
  219. }
  220. public function collapsible($sId, $sTitle, $aItems, $bOpen = true)
  221. {
  222. $this->add("<h3 class=\"clickable open\" id=\"{$sId}\">$sTitle</h3>");
  223. $this->p('<ul id="'.$sId.'_list">');
  224. foreach($aItems as $sItem)
  225. {
  226. $this->p("<li>$sItem</li>\n");
  227. }
  228. $this->p('</ul>');
  229. $this->add_ready_script("$('#{$sId}').click( function() { $(this).toggleClass('open'); $('#{$sId}_list').toggle();} );\n");
  230. if (!$bOpen)
  231. {
  232. $this->add_ready_script("$('#{$sId}').toggleClass('open'); $('#{$sId}_list').toggle();\n");
  233. }
  234. }
  235. public function output()
  236. {
  237. $this->s_content = "<div id=\"header\"><h1><a href=\"http://www.combodo.com/itop\" target=\"_blank\"><img title=\"iTop by Combodo\" src=\"../images/itop-logo.png\"></a>&nbsp;{$this->s_title}</h1>\n</div><div id=\"setup\">{$this->s_content}\n</div>\n";
  238. return parent::output();
  239. }
  240. public static function log_error($sText)
  241. {
  242. self::log("Error - ".$sText);
  243. }
  244. public static function log_warning($sText)
  245. {
  246. self::log("Warning - ".$sText);
  247. }
  248. public static function log_info($sText)
  249. {
  250. self::log("Info - ".$sText);
  251. }
  252. public static function log_ok($sText)
  253. {
  254. self::log("Ok - ".$sText);
  255. }
  256. public static function log($sText)
  257. {
  258. $hLogFile = @fopen(INSTALL_LOG_FILE, 'a');
  259. if ($hLogFile !== false)
  260. {
  261. $sDate = date('Y-m-d H:i:s');
  262. fwrite($hLogFile, "$sDate - $sText\n");
  263. fclose($hLogFile);
  264. }
  265. }
  266. static $m_aModuleArgs = array(
  267. 'label' => 'One line description shown during the interactive setup',
  268. 'dependencies' => 'array of module ids',
  269. 'mandatory' => 'boolean',
  270. 'visible' => 'boolean',
  271. 'datamodel' => 'array of data model files',
  272. 'dictionary' => 'array of dictionary files',
  273. 'data.struct' => 'array of structural data files',
  274. 'data.sample' => 'array of sample data files',
  275. 'doc.manual_setup' => 'url',
  276. 'doc.more_information' => 'url',
  277. );
  278. static $m_aModules = array();
  279. // All the entries below are list of file paths relative to the module directory
  280. static $m_aFilesList = array('datamodel', 'webservice', 'dictionary', 'data.struct', 'data.sample');
  281. static $m_sModulePath = null;
  282. public static function SetModulePath($sModulePath)
  283. {
  284. self::$m_sModulePath = $sModulePath;
  285. }
  286. public static function AddModule($sFilePath, $sId, $aArgs)
  287. {
  288. if (!array_key_exists('itop_version', $aArgs))
  289. {
  290. // Assume 1.0.2
  291. $aArgs['itop_version'] = '1.0.2';
  292. }
  293. foreach (self::$m_aModuleArgs as $sArgName => $sArgDesc)
  294. {
  295. if (!array_key_exists($sArgName, $aArgs))
  296. {
  297. throw new Exception("Module '$sId': missing argument '$sArgName'");
  298. }
  299. }
  300. self::$m_aModules[$sId] = $aArgs;
  301. foreach(self::$m_aFilesList as $sAttribute)
  302. {
  303. if (isset(self::$m_aModules[$sId][$sAttribute]))
  304. {
  305. // All the items below are list of files, that are relative to the current file
  306. // being loaded, let's update their path to store path relative to the application directory
  307. foreach(self::$m_aModules[$sId][$sAttribute] as $idx => $sRelativePath)
  308. {
  309. self::$m_aModules[$sId][$sAttribute][$idx] = self::$m_sModulePath.'/'.$sRelativePath;
  310. }
  311. }
  312. }
  313. }
  314. public static function GetModules($oP = null)
  315. {
  316. // Order the modules to take into account their inter-dependencies
  317. $aDependencies = array();
  318. foreach(self::$m_aModules as $sId => $aModule)
  319. {
  320. $aDependencies[$sId] = $aModule['dependencies'];
  321. }
  322. $aOrderedModules = array();
  323. $iLoopCount = 1;
  324. while(($iLoopCount < count(self::$m_aModules)) && (count($aDependencies) > 0) )
  325. {
  326. foreach($aDependencies as $sId => $aRemainingDeps)
  327. {
  328. $bDependenciesSolved = true;
  329. foreach($aRemainingDeps as $sDepId)
  330. {
  331. if (!in_array($sDepId, $aOrderedModules))
  332. {
  333. $bDependenciesSolved = false;
  334. }
  335. }
  336. if ($bDependenciesSolved)
  337. {
  338. $aOrderedModules[] = $sId;
  339. unset($aDependencies[$sId]);
  340. }
  341. }
  342. $iLoopCount++;
  343. }
  344. if (count($aDependencies) >0)
  345. {
  346. $sHtml = "<ul><b>Warning: the following modules have unmet dependencies, and have been ignored:</b>\n";
  347. foreach($aDependencies as $sId => $aDeps)
  348. {
  349. $aModule = self::$m_aModules[$sId];
  350. $sHtml.= "<li>{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)."</li>";
  351. }
  352. $sHtml .= "</ul>\n";
  353. if (is_object($oP))
  354. {
  355. $oP->warning($sHtml);
  356. }
  357. else
  358. {
  359. self::log_warning($sHtml);
  360. }
  361. }
  362. // Return the ordered list, so that the dependencies are met...
  363. $aResult = array();
  364. foreach($aOrderedModules as $sId)
  365. {
  366. $aResult[$sId] = self::$m_aModules[$sId];
  367. }
  368. return $aResult;
  369. }
  370. } // End of class
  371. /**
  372. * Helper function to initialize the ORM and load the data model
  373. * from the given file
  374. * @param $sConfigFileName string The name of the configuration file to load
  375. * @param $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
  376. * @return none
  377. */
  378. function InitDataModel($sConfigFileName, $bModelOnly = true, $bUseCache = false)
  379. {
  380. require_once(APPROOT.'/core/log.class.inc.php');
  381. require_once(APPROOT.'/core/kpi.class.inc.php');
  382. require_once(APPROOT.'/core/coreexception.class.inc.php');
  383. require_once(APPROOT.'/core/dict.class.inc.php');
  384. require_once(APPROOT.'/core/attributedef.class.inc.php');
  385. require_once(APPROOT.'/core/filterdef.class.inc.php');
  386. require_once(APPROOT.'/core/stimulus.class.inc.php');
  387. require_once(APPROOT.'/core/MyHelpers.class.inc.php');
  388. require_once(APPROOT.'/core/expression.class.inc.php');
  389. require_once(APPROOT.'/core/cmdbsource.class.inc.php');
  390. require_once(APPROOT.'/core/sqlquery.class.inc.php');
  391. require_once(APPROOT.'/core/dbobject.class.php');
  392. require_once(APPROOT.'/core/dbobjectsearch.class.php');
  393. require_once(APPROOT.'/core/dbobjectset.class.php');
  394. require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
  395. require_once(APPROOT.'/core/userrights.class.inc.php');
  396. require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
  397. SetupWebPage::log_info("MetaModel::Startup from file '$sConfigFileName' (ModelOnly = $bModelOnly)");
  398. if ($bUseCache)
  399. {
  400. // Reset the cache for the first use !
  401. $oConfig = new Config($sConfigFileName, true /* Load Config */);
  402. MetaModel::ResetCache($oConfig);
  403. }
  404. MetaModel::Startup($sConfigFileName, $bModelOnly, $bUseCache);
  405. }
  406. /**
  407. * Search (on the disk) for all defined iTop modules, load them and returns the list (as an array)
  408. * of the possible iTop modules to install
  409. * @param none
  410. * @return Hash A big array moduleID => ModuleData
  411. */
  412. function GetAvailableModules($oP = null)
  413. {
  414. clearstatcache();
  415. ListModuleFiles('modules');
  416. return SetupWebPage::GetModules($oP);
  417. }
  418. /**
  419. * Analyzes the current installation and the possibilities
  420. *
  421. * @param $oP SetupWebPage For accessing the list of loaded modules
  422. * @param $sDBServer string Name/IP of the DB server
  423. * @param $sDBUser username for the DB server connection
  424. * @param $sDBPwd password for the DB server connection
  425. * @param $sDBName Name of the database instance
  426. * @param $sDBPrefix Prefix for the iTop tables in the DB instance
  427. * @return hash Array with the following format:
  428. * array =>
  429. * 'iTop' => array(
  430. * 'version_db' => ... (could be empty in case of a fresh install)
  431. * 'version_code => ...
  432. * )
  433. * <module_name> => array(
  434. * 'version_db' => ...
  435. * 'version_code' => ...
  436. * 'install' => array(
  437. * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
  438. * 'message' => ...
  439. * )
  440. * 'uninstall' => array(
  441. * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
  442. * 'message' => ...
  443. * )
  444. * 'label' => ...
  445. * 'dependencies' => array(<module1>, <module2>, ...)
  446. * 'visible' => true | false
  447. * )
  448. * )
  449. */
  450. function AnalyzeInstallation($oConfig)
  451. {
  452. $aRes = array(
  453. ROOT_MODULE => array(
  454. 'version_db' => '',
  455. 'name_db' => '',
  456. 'version_code' => ITOP_VERSION.'.'.ITOP_REVISION,
  457. 'name_code' => ITOP_APPLICATION,
  458. )
  459. );
  460. $aModules = GetAvailableModules();
  461. foreach($aModules as $sModuleId => $aModuleInfo)
  462. {
  463. list($sModuleName, $sModuleVersion) = GetModuleName($sModuleId);
  464. $sModuleAppVersion = $aModuleInfo['itop_version'];
  465. $aModuleInfo['version_db'] = '';
  466. $aModuleInfo['version_code'] = $sModuleVersion;
  467. if (!in_array($sModuleAppVersion, array('1.0.0', '1.0.1', '1.0.2')))
  468. {
  469. // This module is NOT compatible with the current version
  470. $aModuleInfo['install'] = array(
  471. 'flag' => MODULE_ACTION_IMPOSSIBLE,
  472. 'message' => 'the module is not compatible with the current version of the application'
  473. );
  474. }
  475. elseif ($aModuleInfo['mandatory'])
  476. {
  477. $aModuleInfo['install'] = array(
  478. 'flag' => MODULE_ACTION_MANDATORY,
  479. 'message' => 'the module is part of the application'
  480. );
  481. }
  482. else
  483. {
  484. $aModuleInfo['install'] = array(
  485. 'flag' => MODULE_ACTION_OPTIONAL,
  486. 'message' => ''
  487. );
  488. }
  489. $aRes[$sModuleName] = $aModuleInfo;
  490. }
  491. try
  492. {
  493. CMDBSource::Init($oConfig->GetDBHost(), $oConfig->GetDBUser(), $oConfig->GetDBPwd(), $oConfig->GetDBName());
  494. $aSelectInstall = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->GetDBSubname()."priv_module_install");
  495. }
  496. catch (MySQLException $e)
  497. {
  498. // No database or eroneous information
  499. $aSelectInstall = array();
  500. }
  501. // Build the list of installed module (get the latest installation)
  502. //
  503. $aInstallByModule = array(); // array of <module> => array ('installed' => timestamp, 'version' => <version>)
  504. foreach ($aSelectInstall as $aInstall)
  505. {
  506. //$aInstall['comment']; // unsused
  507. $iInstalled = strtotime($aInstall['installed']);
  508. $sModuleName = $aInstall['name'];
  509. $sModuleVersion = $aInstall['version'];
  510. if ($aInstall['parent_id'] == 0)
  511. {
  512. $sModuleName = ROOT_MODULE;
  513. }
  514. if (array_key_exists($sModuleName, $aInstallByModule))
  515. {
  516. if ($iInstalled < $aInstallByModule[$sModuleName]['installed'])
  517. {
  518. continue;
  519. }
  520. }
  521. if ($aInstall['parent_id'] == 0)
  522. {
  523. $aRes[$sModuleName]['version_db'] = $sModuleVersion;
  524. $aRes[$sModuleName]['name_db'] = $aInstall['name'];
  525. }
  526. $aInstallByModule[$sModuleName]['installed'] = $iInstalled;
  527. $aInstallByModule[$sModuleName]['version'] = $sModuleVersion;
  528. }
  529. // Adjust the list of proposed modules
  530. //
  531. foreach ($aInstallByModule as $sModuleName => $aModuleDB)
  532. {
  533. if ($sModuleName == ROOT_MODULE) continue; // Skip the main module
  534. if (!array_key_exists($sModuleName, $aRes))
  535. {
  536. // A module was installed, it is not proposed in the new build... skip
  537. continue;
  538. }
  539. $aRes[$sModuleName]['version_db'] = $aModuleDB['version'];
  540. if ($aRes[$sModuleName]['install']['flag'] == MODULE_ACTION_MANDATORY)
  541. {
  542. $aRes[$sModuleName]['uninstall'] = array(
  543. 'flag' => MODULE_ACTION_IMPOSSIBLE,
  544. 'message' => 'the module is part of the application'
  545. );
  546. }
  547. else
  548. {
  549. $aRes[$sModuleName]['uninstall'] = array(
  550. 'flag' => MODULE_ACTION_OPTIONAL,
  551. 'message' => ''
  552. );
  553. }
  554. }
  555. return $aRes;
  556. }
  557. /**
  558. * Helper function to interpret the name of a module
  559. * @param $sModuleId string Identifier of the module, in the form 'name/version'
  560. * @return array(name, version)
  561. */
  562. function GetModuleName($sModuleId)
  563. {
  564. if (preg_match('!^(.*)/(.*)$!', $sModuleId, $aMatches))
  565. {
  566. $sName = $aMatches[1];
  567. $sVersion = $aMatches[2];
  568. }
  569. else
  570. {
  571. $sName = $sModuleId;
  572. $sVersion = "";
  573. }
  574. return array($sName, $sVersion);
  575. }
  576. /**
  577. * Helper function to create the database structure
  578. * @return boolean true on success, false otherwise
  579. */
  580. function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
  581. {
  582. if (strlen($oConfig->GetDBSubname()) > 0)
  583. {
  584. SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBName()."' (table names prefixed by '".$oConfig->GetDBSubname()."').");
  585. }
  586. else
  587. {
  588. SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBSubname()."'.");
  589. }
  590. //MetaModel::CheckDefinitions();
  591. if ($sMode == 'install')
  592. {
  593. if (!MetaModel::DBExists(/* bMustBeComplete */ false))
  594. {
  595. MetaModel::DBCreate();
  596. SetupWebPage::log_ok("Database structure successfully created.");
  597. }
  598. else
  599. {
  600. if (strlen($oConfig->GetDBSubname()) > 0)
  601. {
  602. throw new Exception("Error: found iTop tables into the database '".$oConfig->GetDBName()."' (prefix: '".$oConfig->GetDBSubname()."'). Please, try selecting another database instance or specify another prefix to prevent conflicting table names.");
  603. }
  604. else
  605. {
  606. throw new Exception("Error: found iTop tables into the database '".$oConfig->GetDBName()."'. Please, try selecting another database instance or specify a prefix to prevent conflicting table names.");
  607. }
  608. }
  609. }
  610. else
  611. {
  612. if (MetaModel::DBExists(/* bMustBeComplete */ false))
  613. {
  614. MetaModel::DBCreate();
  615. SetupWebPage::log_ok("Database structure successfully created.");
  616. }
  617. else
  618. {
  619. if (strlen($oConfig->GetDBSubname()) > 0)
  620. {
  621. throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."' (prefix: '".$oConfig->GetDBSubname()."'). Please, try selecting another database instance.");
  622. }
  623. else
  624. {
  625. throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."'. Please, try selecting another database instance.");
  626. }
  627. }
  628. }
  629. return true;
  630. }
  631. function RecordInstallation(Config $oConfig, $aSelectedModules)
  632. {
  633. // Record main installation
  634. $oInstallRec = new ModuleInstallation();
  635. $oInstallRec->Set('name', ITOP_APPLICATION);
  636. $oInstallRec->Set('version', ITOP_VERSION.'.'.ITOP_REVISION);
  637. $oInstallRec->Set('comment', "Done by the setup program\nBuilt on ".ITOP_BUILD_DATE);
  638. $oInstallRec->Set('parent_id', 0); // root module
  639. $iMainItopRecord = $oInstallRec->DBInsertNoReload();
  640. // Record installed modules
  641. //
  642. $aAvailableModules = AnalyzeInstallation($oConfig);
  643. foreach($aSelectedModules as $sModuleId)
  644. {
  645. $aModuleData = $aAvailableModules[$sModuleId];
  646. $sName = $sModuleId;
  647. $sVersion = $aModuleData['version_code'];
  648. $aComments = array();
  649. $aComments[] = 'Done by the setup program';
  650. if ($aModuleData['mandatory'])
  651. {
  652. $aComments[] = 'Mandatory';
  653. }
  654. else
  655. {
  656. $aComments[] = 'Optional';
  657. }
  658. if ($aModuleData['visible'])
  659. {
  660. $aComments[] = 'Visible (during the setup)';
  661. }
  662. else
  663. {
  664. $aComments[] = 'Hidden (selected automatically)';
  665. }
  666. foreach ($aModuleData['dependencies'] as $sDependOn)
  667. {
  668. $aComments[] = "Depends on module: $sDependOn";
  669. }
  670. $sComment = implode("\n", $aComments);
  671. $oInstallRec = new ModuleInstallation();
  672. $oInstallRec->Set('name', $sName);
  673. $oInstallRec->Set('version', $sVersion);
  674. $oInstallRec->Set('comment', $sComment);
  675. $oInstallRec->Set('parent_id', $iMainItopRecord);
  676. $oInstallRec->DBInsertNoReload();
  677. }
  678. // Database is created, installation has been tracked into it
  679. return true;
  680. }
  681. function ListModuleFiles($sRelDir)
  682. {
  683. $sDirectory = APPROOT.'/'.$sRelDir;
  684. //echo "<p>$sDirectory</p>\n";
  685. if ($hDir = opendir($sDirectory))
  686. {
  687. // This is the correct way to loop over the directory. (according to the documentation)
  688. while (($sFile = readdir($hDir)) !== false)
  689. {
  690. $aMatches = array();
  691. if (is_dir($sDirectory.'/'.$sFile))
  692. {
  693. if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn'))
  694. {
  695. ListModuleFiles($sRelDir.'/'.$sFile);
  696. }
  697. }
  698. else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
  699. {
  700. SetupWebPage::SetModulePath($sRelDir);
  701. try
  702. {
  703. //echo "<p>Loading: $sDirectory/$sFile...</p>\n";
  704. require_once($sDirectory.'/'.$sFile);
  705. //echo "<p>Done.</p>\n";
  706. }
  707. catch(Exception $e)
  708. {
  709. // Continue...
  710. }
  711. }
  712. }
  713. closedir($hDir);
  714. }
  715. else
  716. {
  717. throw new Exception("Data directory (".$sDirectory.") not found or not readable.");
  718. }
  719. }
  720. ?>