setuppage.class.inc.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  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\">".htmlentities($sText, ENT_COMPAT, 'UTF-8')."</p>\n");
  173. $this->log_info($sText);
  174. }
  175. public function ok($sText)
  176. {
  177. $this->add("<p class=\"ok\">".htmlentities($sText, ENT_COMPAT, 'UTF-8')."</p>\n");
  178. $this->log_ok($sText);
  179. }
  180. public function warning($sText)
  181. {
  182. $this->add("<p class=\"warning\">".htmlentities($sText, ENT_COMPAT, 'UTF-8')."</p>\n");
  183. $this->log_warning($sText);
  184. }
  185. public function error($sText)
  186. {
  187. $this->add("<p class=\"error\">".htmlentities($sText, ENT_COMPAT, 'UTF-8')."</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', // No longer mandatory, now automated
  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. // Populate automatically the list of dictionary files
  314. if(preg_match('|^([^/]+)|', $sId, $aMatches)) // ModuleName = everything before the first forward slash
  315. {
  316. $sModuleName = $aMatches[1];
  317. $sDir = dirname($sFilePath);
  318. if ($hDir = opendir($sDir))
  319. {
  320. while (($sFile = readdir($hDir)) !== false)
  321. {
  322. $aMatches = array();
  323. if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically
  324. {
  325. self::$m_aModules[$sId]['dictionary'][] = self::$m_sModulePath.'/'.$sFile;
  326. }
  327. }
  328. closedir($hDir);
  329. }
  330. }
  331. }
  332. public static function GetModules($oP = null)
  333. {
  334. // Order the modules to take into account their inter-dependencies
  335. $aDependencies = array();
  336. foreach(self::$m_aModules as $sId => $aModule)
  337. {
  338. $aDependencies[$sId] = $aModule['dependencies'];
  339. }
  340. $aOrderedModules = array();
  341. $iLoopCount = 1;
  342. while(($iLoopCount < count(self::$m_aModules)) && (count($aDependencies) > 0) )
  343. {
  344. foreach($aDependencies as $sId => $aRemainingDeps)
  345. {
  346. $bDependenciesSolved = true;
  347. foreach($aRemainingDeps as $sDepId)
  348. {
  349. if (!in_array($sDepId, $aOrderedModules))
  350. {
  351. $bDependenciesSolved = false;
  352. }
  353. }
  354. if ($bDependenciesSolved)
  355. {
  356. $aOrderedModules[] = $sId;
  357. unset($aDependencies[$sId]);
  358. }
  359. }
  360. $iLoopCount++;
  361. }
  362. if (count($aDependencies) >0)
  363. {
  364. $sHtml = "<ul><b>Warning: the following modules have unmet dependencies, and have been ignored:</b>\n";
  365. foreach($aDependencies as $sId => $aDeps)
  366. {
  367. $aModule = self::$m_aModules[$sId];
  368. $sHtml.= "<li>{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)."</li>";
  369. }
  370. $sHtml .= "</ul>\n";
  371. if (is_object($oP))
  372. {
  373. $oP->warning($sHtml);
  374. }
  375. else
  376. {
  377. self::log_warning($sHtml);
  378. }
  379. }
  380. // Return the ordered list, so that the dependencies are met...
  381. $aResult = array();
  382. foreach($aOrderedModules as $sId)
  383. {
  384. $aResult[$sId] = self::$m_aModules[$sId];
  385. }
  386. return $aResult;
  387. }
  388. } // End of class
  389. /**
  390. * Helper function to initialize the ORM and load the data model
  391. * from the given file
  392. * @param $sConfigFileName string The name of the configuration file to load
  393. * @param $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
  394. * @return none
  395. */
  396. function InitDataModel($sConfigFileName, $bModelOnly = true, $bUseCache = false)
  397. {
  398. require_once(APPROOT.'/core/log.class.inc.php');
  399. require_once(APPROOT.'/core/kpi.class.inc.php');
  400. require_once(APPROOT.'/core/coreexception.class.inc.php');
  401. require_once(APPROOT.'/core/dict.class.inc.php');
  402. require_once(APPROOT.'/core/attributedef.class.inc.php');
  403. require_once(APPROOT.'/core/filterdef.class.inc.php');
  404. require_once(APPROOT.'/core/stimulus.class.inc.php');
  405. require_once(APPROOT.'/core/MyHelpers.class.inc.php');
  406. require_once(APPROOT.'/core/expression.class.inc.php');
  407. require_once(APPROOT.'/core/cmdbsource.class.inc.php');
  408. require_once(APPROOT.'/core/sqlquery.class.inc.php');
  409. require_once(APPROOT.'/core/dbobject.class.php');
  410. require_once(APPROOT.'/core/dbobjectsearch.class.php');
  411. require_once(APPROOT.'/core/dbobjectset.class.php');
  412. require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
  413. require_once(APPROOT.'/core/userrights.class.inc.php');
  414. require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
  415. SetupWebPage::log_info("MetaModel::Startup from file '$sConfigFileName' (ModelOnly = $bModelOnly)");
  416. if ($bUseCache)
  417. {
  418. // Reset the cache for the first use !
  419. $oConfig = new Config($sConfigFileName, true /* Load Config */);
  420. MetaModel::ResetCache($oConfig);
  421. }
  422. MetaModel::Startup($sConfigFileName, $bModelOnly, $bUseCache);
  423. }
  424. /**
  425. * Search (on the disk) for all defined iTop modules, load them and returns the list (as an array)
  426. * of the possible iTop modules to install
  427. * @param none
  428. * @return Hash A big array moduleID => ModuleData
  429. */
  430. function GetAvailableModules($oP = null)
  431. {
  432. clearstatcache();
  433. ListModuleFiles('modules');
  434. return SetupWebPage::GetModules($oP);
  435. }
  436. /**
  437. * Analyzes the current installation and the possibilities
  438. *
  439. * @param $oP SetupWebPage For accessing the list of loaded modules
  440. * @param $sDBServer string Name/IP of the DB server
  441. * @param $sDBUser username for the DB server connection
  442. * @param $sDBPwd password for the DB server connection
  443. * @param $sDBName Name of the database instance
  444. * @param $sDBPrefix Prefix for the iTop tables in the DB instance
  445. * @return hash Array with the following format:
  446. * array =>
  447. * 'iTop' => array(
  448. * 'version_db' => ... (could be empty in case of a fresh install)
  449. * 'version_code => ...
  450. * )
  451. * <module_name> => array(
  452. * 'version_db' => ...
  453. * 'version_code' => ...
  454. * 'install' => array(
  455. * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
  456. * 'message' => ...
  457. * )
  458. * 'uninstall' => array(
  459. * 'flag' => SETUP_NEVER | SETUP_OPTIONAL | SETUP_MANDATORY
  460. * 'message' => ...
  461. * )
  462. * 'label' => ...
  463. * 'dependencies' => array(<module1>, <module2>, ...)
  464. * 'visible' => true | false
  465. * )
  466. * )
  467. */
  468. function AnalyzeInstallation($oConfig)
  469. {
  470. $aRes = array(
  471. ROOT_MODULE => array(
  472. 'version_db' => '',
  473. 'name_db' => '',
  474. 'version_code' => ITOP_VERSION.'.'.ITOP_REVISION,
  475. 'name_code' => ITOP_APPLICATION,
  476. )
  477. );
  478. $aModules = GetAvailableModules();
  479. foreach($aModules as $sModuleId => $aModuleInfo)
  480. {
  481. list($sModuleName, $sModuleVersion) = GetModuleName($sModuleId);
  482. $sModuleAppVersion = $aModuleInfo['itop_version'];
  483. $aModuleInfo['version_db'] = '';
  484. $aModuleInfo['version_code'] = $sModuleVersion;
  485. if (!in_array($sModuleAppVersion, array('1.0.0', '1.0.1', '1.0.2')))
  486. {
  487. // This module is NOT compatible with the current version
  488. $aModuleInfo['install'] = array(
  489. 'flag' => MODULE_ACTION_IMPOSSIBLE,
  490. 'message' => 'the module is not compatible with the current version of the application'
  491. );
  492. }
  493. elseif ($aModuleInfo['mandatory'])
  494. {
  495. $aModuleInfo['install'] = array(
  496. 'flag' => MODULE_ACTION_MANDATORY,
  497. 'message' => 'the module is part of the application'
  498. );
  499. }
  500. else
  501. {
  502. $aModuleInfo['install'] = array(
  503. 'flag' => MODULE_ACTION_OPTIONAL,
  504. 'message' => ''
  505. );
  506. }
  507. $aRes[$sModuleName] = $aModuleInfo;
  508. }
  509. try
  510. {
  511. CMDBSource::Init($oConfig->GetDBHost(), $oConfig->GetDBUser(), $oConfig->GetDBPwd(), $oConfig->GetDBName());
  512. $aSelectInstall = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->GetDBSubname()."priv_module_install");
  513. }
  514. catch (MySQLException $e)
  515. {
  516. // No database or eroneous information
  517. $aSelectInstall = array();
  518. }
  519. // Build the list of installed module (get the latest installation)
  520. //
  521. $aInstallByModule = array(); // array of <module> => array ('installed' => timestamp, 'version' => <version>)
  522. foreach ($aSelectInstall as $aInstall)
  523. {
  524. //$aInstall['comment']; // unsused
  525. $iInstalled = strtotime($aInstall['installed']);
  526. $sModuleName = $aInstall['name'];
  527. $sModuleVersion = $aInstall['version'];
  528. if ($aInstall['parent_id'] == 0)
  529. {
  530. $sModuleName = ROOT_MODULE;
  531. }
  532. if (array_key_exists($sModuleName, $aInstallByModule))
  533. {
  534. if ($iInstalled < $aInstallByModule[$sModuleName]['installed'])
  535. {
  536. continue;
  537. }
  538. }
  539. if ($aInstall['parent_id'] == 0)
  540. {
  541. $aRes[$sModuleName]['version_db'] = $sModuleVersion;
  542. $aRes[$sModuleName]['name_db'] = $aInstall['name'];
  543. }
  544. $aInstallByModule[$sModuleName]['installed'] = $iInstalled;
  545. $aInstallByModule[$sModuleName]['version'] = $sModuleVersion;
  546. }
  547. // Adjust the list of proposed modules
  548. //
  549. foreach ($aInstallByModule as $sModuleName => $aModuleDB)
  550. {
  551. if ($sModuleName == ROOT_MODULE) continue; // Skip the main module
  552. if (!array_key_exists($sModuleName, $aRes))
  553. {
  554. // A module was installed, it is not proposed in the new build... skip
  555. continue;
  556. }
  557. $aRes[$sModuleName]['version_db'] = $aModuleDB['version'];
  558. if ($aRes[$sModuleName]['install']['flag'] == MODULE_ACTION_MANDATORY)
  559. {
  560. $aRes[$sModuleName]['uninstall'] = array(
  561. 'flag' => MODULE_ACTION_IMPOSSIBLE,
  562. 'message' => 'the module is part of the application'
  563. );
  564. }
  565. else
  566. {
  567. $aRes[$sModuleName]['uninstall'] = array(
  568. 'flag' => MODULE_ACTION_OPTIONAL,
  569. 'message' => ''
  570. );
  571. }
  572. }
  573. return $aRes;
  574. }
  575. /**
  576. * Helper function to interpret the name of a module
  577. * @param $sModuleId string Identifier of the module, in the form 'name/version'
  578. * @return array(name, version)
  579. */
  580. function GetModuleName($sModuleId)
  581. {
  582. if (preg_match('!^(.*)/(.*)$!', $sModuleId, $aMatches))
  583. {
  584. $sName = $aMatches[1];
  585. $sVersion = $aMatches[2];
  586. }
  587. else
  588. {
  589. $sName = $sModuleId;
  590. $sVersion = "";
  591. }
  592. return array($sName, $sVersion);
  593. }
  594. /**
  595. * Helper function to create the database structure
  596. * @return boolean true on success, false otherwise
  597. */
  598. function CreateDatabaseStructure(Config $oConfig, $aSelectedModules, $sMode)
  599. {
  600. if (strlen($oConfig->GetDBSubname()) > 0)
  601. {
  602. SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBName()."' (table names prefixed by '".$oConfig->GetDBSubname()."').");
  603. }
  604. else
  605. {
  606. SetupWebPage::log_info("Creating the structure in '".$oConfig->GetDBSubname()."'.");
  607. }
  608. //MetaModel::CheckDefinitions();
  609. if ($sMode == 'install')
  610. {
  611. if (!MetaModel::DBExists(/* bMustBeComplete */ false))
  612. {
  613. MetaModel::DBCreate();
  614. SetupWebPage::log_ok("Database structure successfully created.");
  615. }
  616. else
  617. {
  618. if (strlen($oConfig->GetDBSubname()) > 0)
  619. {
  620. 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.");
  621. }
  622. else
  623. {
  624. 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.");
  625. }
  626. }
  627. }
  628. else
  629. {
  630. if (MetaModel::DBExists(/* bMustBeComplete */ false))
  631. {
  632. MetaModel::DBCreate();
  633. SetupWebPage::log_ok("Database structure successfully created.");
  634. // Check (and update only if it seems needed) the hierarchical keys
  635. MetaModel::CheckHKeys(false /* bDiagnosticsOnly */, false /* bVerbose*/, false /* bForceUpdate */);
  636. }
  637. else
  638. {
  639. if (strlen($oConfig->GetDBSubname()) > 0)
  640. {
  641. throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."' (prefix: '".$oConfig->GetDBSubname()."'). Please, try selecting another database instance.");
  642. }
  643. else
  644. {
  645. throw new Exception("Error: No previous instance of iTop found into the database '".$oConfig->GetDBName()."'. Please, try selecting another database instance.");
  646. }
  647. }
  648. }
  649. return true;
  650. }
  651. function RecordInstallation(Config $oConfig, $aSelectedModules)
  652. {
  653. // Record main installation
  654. $oInstallRec = new ModuleInstallation();
  655. $oInstallRec->Set('name', ITOP_APPLICATION);
  656. $oInstallRec->Set('version', ITOP_VERSION.'.'.ITOP_REVISION);
  657. $oInstallRec->Set('comment', "Done by the setup program\nBuilt on ".ITOP_BUILD_DATE);
  658. $oInstallRec->Set('parent_id', 0); // root module
  659. $iMainItopRecord = $oInstallRec->DBInsertNoReload();
  660. // Record installed modules
  661. //
  662. $aAvailableModules = AnalyzeInstallation($oConfig);
  663. foreach($aSelectedModules as $sModuleId)
  664. {
  665. $aModuleData = $aAvailableModules[$sModuleId];
  666. $sName = $sModuleId;
  667. $sVersion = $aModuleData['version_code'];
  668. $aComments = array();
  669. $aComments[] = 'Done by the setup program';
  670. if ($aModuleData['mandatory'])
  671. {
  672. $aComments[] = 'Mandatory';
  673. }
  674. else
  675. {
  676. $aComments[] = 'Optional';
  677. }
  678. if ($aModuleData['visible'])
  679. {
  680. $aComments[] = 'Visible (during the setup)';
  681. }
  682. else
  683. {
  684. $aComments[] = 'Hidden (selected automatically)';
  685. }
  686. foreach ($aModuleData['dependencies'] as $sDependOn)
  687. {
  688. $aComments[] = "Depends on module: $sDependOn";
  689. }
  690. $sComment = implode("\n", $aComments);
  691. $oInstallRec = new ModuleInstallation();
  692. $oInstallRec->Set('name', $sName);
  693. $oInstallRec->Set('version', $sVersion);
  694. $oInstallRec->Set('comment', $sComment);
  695. $oInstallRec->Set('parent_id', $iMainItopRecord);
  696. $oInstallRec->DBInsertNoReload();
  697. }
  698. // Database is created, installation has been tracked into it
  699. return true;
  700. }
  701. function ListModuleFiles($sRelDir)
  702. {
  703. $sDirectory = APPROOT.'/'.$sRelDir;
  704. //echo "<p>$sDirectory</p>\n";
  705. if ($hDir = opendir($sDirectory))
  706. {
  707. // This is the correct way to loop over the directory. (according to the documentation)
  708. while (($sFile = readdir($hDir)) !== false)
  709. {
  710. $aMatches = array();
  711. if (is_dir($sDirectory.'/'.$sFile))
  712. {
  713. if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn'))
  714. {
  715. ListModuleFiles($sRelDir.'/'.$sFile);
  716. }
  717. }
  718. else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
  719. {
  720. SetupWebPage::SetModulePath($sRelDir);
  721. try
  722. {
  723. //echo "<p>Loading: $sDirectory/$sFile...</p>\n";
  724. require_once($sDirectory.'/'.$sFile);
  725. //echo "<p>Done.</p>\n";
  726. }
  727. catch(Exception $e)
  728. {
  729. // Continue...
  730. }
  731. }
  732. }
  733. closedir($hDir);
  734. }
  735. else
  736. {
  737. throw new Exception("Data directory (".$sDirectory.") not found or not readable.");
  738. }
  739. }
  740. ?>