applicationinstaller.class.inc.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. <?php
  2. // Copyright (C) 2012 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. require_once(APPROOT.'setup/parameters.class.inc.php');
  17. require_once(APPROOT.'setup/xmldataloader.class.inc.php');
  18. /**
  19. * The base class for the installation process.
  20. * The installation process is split into a sequence of unitary steps
  21. * for performance reasons (i.e; timeout, memory usage) and also in order
  22. * to provide some feedback about the progress of the installation.
  23. *
  24. * This class can be used for a step by step interactive installation
  25. * while displaying a progress bar, or in an unattended manner
  26. * (for example from the command line), to run all the steps
  27. * in one go.
  28. * @author Erwan Taloc <erwan.taloc@combodo.com>
  29. * @author Romain Quetiez <romain.quetiez@combodo.com>
  30. * @author Denis Flaven <denis.flaven@combodo.com>
  31. * @license http://www.opensource.org/licenses/gpl-3.0.html GPL
  32. */
  33. class ApplicationInstaller
  34. {
  35. const OK = 1;
  36. const ERROR = 2;
  37. const WARNING = 3;
  38. const INFO = 4;
  39. protected $sXMLResponseFile;
  40. public function __construct($sXMLResponseFile)
  41. {
  42. $this->sXMLResponseFile = $sXMLResponseFile;
  43. }
  44. /**
  45. * Runs all the installation steps in one go and directly outputs
  46. * some information about the progress and the success of the various
  47. * sequential steps.
  48. * @return boolean True if the installation was successful, false otherwise
  49. */
  50. public function ExecuteAllSteps()
  51. {
  52. $sStep = '';
  53. $sStepLabel = '';
  54. do
  55. {
  56. if($sStep != '')
  57. {
  58. echo "$sStepLabel\n";
  59. echo "Executing '$sStep'\n";
  60. }
  61. else
  62. {
  63. echo "Starting the installation...\n";
  64. }
  65. $aRes = $this->ExecuteStep($sStep);
  66. $sStep = $aRes['next-step'];
  67. $sStepLabel = $aRes['next-step-label'];
  68. switch($aRes['status'])
  69. {
  70. case self::OK;
  71. echo "Ok. ".$aRes['percentage-completed']." % done.\n";
  72. break;
  73. case self::ERROR:
  74. echo "Error: ".$aRes['message']."\n";
  75. break;
  76. case self::WARNING:
  77. echo "Warning: ".$aRes['message']."\n";
  78. echo $aRes['percentage-completed']." % done.\n";
  79. break;
  80. case self::INFO:
  81. echo "Info: ".$aRes['message']."\n";
  82. echo $aRes['percentage-completed']." % done.\n";
  83. break;
  84. }
  85. }
  86. while(($aRes['status'] != self::ERROR) && ($aRes['next-step'] != ''));
  87. return ($aRes['status'] == self::OK);
  88. }
  89. /**
  90. * Executes the next step of the installation and reports about the progress
  91. * and the next step to perform
  92. * @param string $sStep The identifier of the step to execute
  93. * @return hash An array of (status => , message => , percentage-completed => , next-step => , next-step-label => )
  94. */
  95. public function ExecuteStep($sStep = '')
  96. {
  97. try
  98. {
  99. switch($sStep)
  100. {
  101. case '':
  102. $aResult = array(
  103. 'status' => self::OK,
  104. 'message' => '',
  105. 'percentage-completed' => 0,
  106. 'next-step' => 'copy',
  107. 'next-step-label' => 'Copying data model files',
  108. );
  109. break;
  110. case 'copy':
  111. $aResult = array(
  112. 'status' => self::WARNING,
  113. 'message' => 'Dummy setup - Nothing to copy',
  114. 'next-step' => 'compile',
  115. 'next-step-label' => 'Compiling the data model',
  116. 'percentage-completed' => 20,
  117. );
  118. break;
  119. case 'compile':
  120. $oParams = new XMLParameters($this->sXMLResponseFile);
  121. $aSelectedModules = $oParams->Get('selected_modules');
  122. $sSourceDir = $oParams->Get('source_dir', 'datamodel');
  123. $sTargetDir = $oParams->Get('target_dir', 'env-setup-test');
  124. $sWorkspaceDir = $oParams->Get('workspace_dir', 'workspace');
  125. self::DoCompile($aSelectedModules, $sSourceDir, $sTargetDir, $sWorkspaceDir);
  126. $aResult = array(
  127. 'status' => self::OK,
  128. 'message' => '',
  129. 'next-step' => 'db-schema',
  130. 'next-step-label' => 'Updating database schema',
  131. 'percentage-completed' => 40,
  132. );
  133. break;
  134. case 'db-schema':
  135. $oParams = new XMLParameters($this->sXMLResponseFile);
  136. $sMode = $oParams->Get('mode');
  137. $sTargetDir = $oParams->Get('target_dir', 'env-setup-test');
  138. $sDBServer = $oParams->Get('db_server', '');
  139. $sDBUser = $oParams->Get('db_user', '');
  140. $sDBPwd = $oParams->Get('db_pwd', '');
  141. $sDBName = $oParams->Get('db_name', '');
  142. $sDBNewName = $oParams->Get('db_new_name', '');
  143. $sDBPrefix = $oParams->Get('db_prefix', '');
  144. $sTargetEnvironment = $oParams->Get('target_env', '');
  145. self::DoUpdateDBSchema($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBNewName, $sDBPrefix, $sTargetEnvironment);
  146. $aResult = array(
  147. 'status' => self::OK,
  148. 'message' => '',
  149. 'next-step' => 'after-db-create',
  150. 'next-step-label' => 'Creating Profiles',
  151. 'percentage-completed' => 60,
  152. );
  153. break;
  154. case 'after-db-create':
  155. $oParams = new XMLParameters($this->sXMLResponseFile);
  156. $sMode = $oParams->Get('mode');
  157. $sTargetDir = $oParams->Get('target_dir', 'env-setup-test');
  158. $sDBServer = $oParams->Get('db_server', '');
  159. $sDBUser = $oParams->Get('db_user', '');
  160. $sDBPwd = $oParams->Get('db_pwd', '');
  161. $sDBName = $oParams->Get('db_name', '');
  162. $sDBNewName = $oParams->Get('db_new_name', '');
  163. $sDBPrefix = $oParams->Get('db_prefix', '');
  164. $sAdminUser = $oParams->Get('admin_user', '');
  165. $sAdminPwd = $oParams->Get('admin_pwd', '');
  166. $sLanguage = $oParams->Get('language', '');
  167. $aSelectedModules = $oParams->Get('selected_modules', array());
  168. $sTargetEnvironment = $oParams->Get('target_env', '');
  169. self::AfterDBCreate($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBNewName, $sDBPrefix, $sAdminUser, $sAdminPwd, $sLanguage, $aSelectedModules, $sTargetEnvironment);
  170. $aResult = array(
  171. 'status' => self::OK,
  172. 'message' => '',
  173. 'next-step' => 'sample-data',
  174. 'next-step-label' => 'Loading Sample Data',
  175. 'percentage-completed' => 80,
  176. );
  177. break;
  178. case 'sample-data':
  179. $oParams = new XMLParameters($this->sXMLResponseFile);
  180. $sMode = $oParams->Get('mode');
  181. $sTargetDir = $oParams->Get('target_dir', 'env-setup-test');
  182. $sDBServer = $oParams->Get('db_server', '');
  183. $sDBUser = $oParams->Get('db_user', '');
  184. $sDBPwd = $oParams->Get('db_pwd', '');
  185. $sDBName = $oParams->Get('db_name', '');
  186. $sDBNewName = $oParams->Get('db_new_name', '');
  187. $sDBPrefix = $oParams->Get('db_prefix', '');
  188. $aFiles = $oParams->Get('files', array());
  189. $sTargetEnvironment = $oParams->Get('target_env', '');
  190. self::DoLoadFiles($aFiles, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment);
  191. $aResult = array(
  192. 'status' => self::INFO,
  193. 'message' => 'All data loaded',
  194. 'next-step' => 'create-config',
  195. 'next-step-label' => 'Creating the Configuration File',
  196. 'percentage-completed' => 99,
  197. );
  198. break;
  199. case 'create-config':
  200. $oParams = new XMLParameters($this->sXMLResponseFile);
  201. $sMode = $oParams->Get('mode');
  202. $sTargetDir = $oParams->Get('target_dir', 'env-setup-test');
  203. $sDBServer = $oParams->Get('db_server', '');
  204. $sDBUser = $oParams->Get('db_user', '');
  205. $sDBPwd = $oParams->Get('db_pwd', '');
  206. $sDBName = $oParams->Get('db_name', '');
  207. $sDBNewName = $oParams->Get('db_new_name', '');
  208. $sDBPrefix = $oParams->Get('db_prefix', '');
  209. $sUrl = $oParams->Get('url', '');
  210. $sLanguage = $oParams->Get('language', '');
  211. $aSelectedModules = $oParams->Get('selected_modules', array());
  212. $sTargetEnvironment = $oParams->Get('target_env', '');
  213. self::DoCreateConfig($sMode, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment);
  214. $aResult = array(
  215. 'status' => self::INFO,
  216. 'message' => 'Configuration file created',
  217. 'next-step' => '',
  218. 'next-step-label' => 'Completed',
  219. 'percentage-completed' => 100,
  220. );
  221. break;
  222. default:
  223. $aResult = array(
  224. 'status' => self::ERROR,
  225. 'message' => '',
  226. 'next-step' => '',
  227. 'next-step-label' => "Unknown setup step '$sStep'.",
  228. 'percentage-completed' => 100,
  229. );
  230. }
  231. }
  232. catch(Exception $e)
  233. {
  234. $aResult = array(
  235. 'status' => self::ERROR,
  236. 'message' => $e->getMessage(),
  237. 'next-step' => '',
  238. 'next-step-label' => '',
  239. 'percentage-completed' => 100,
  240. );
  241. }
  242. return $aResult;
  243. }
  244. protected static function DoCompile($aSelectedModules, $sSourceDir, $sTargetDir, $sWorkspaceDir = '')
  245. {
  246. SetupPage::log_info("Compiling data model.");
  247. require_once(APPROOT.'setup/modulediscovery.class.inc.php');
  248. require_once(APPROOT.'setup/modelfactory.class.inc.php');
  249. require_once(APPROOT.'setup/compiler.class.inc.php');
  250. if (empty($sSourceDir) || empty($sTargetDir))
  251. {
  252. throw new Exception("missing parameter source_dir and/or target_dir");
  253. }
  254. $sSourcePath = APPROOT.$sSourceDir;
  255. $sTargetPath = APPROOT.$sTargetDir;
  256. if (!is_dir($sSourcePath))
  257. {
  258. throw new Exception("Failed to find the source directory '$sSourcePath', please check the rights of the web server");
  259. }
  260. if (!is_dir($sTargetPath) && !mkdir($sTargetPath))
  261. {
  262. throw new Exception("Failed to create directory '$sTargetPath', please check the rights of the web server");
  263. }
  264. // owner:rwx user/group:rx
  265. chmod($sTargetPath, 0755);
  266. $oFactory = new ModelFactory($sSourcePath);
  267. $aModules = $oFactory->FindModules();
  268. foreach($aModules as $foo => $oModule)
  269. {
  270. $sModule = $oModule->GetName();
  271. if (in_array($sModule, $aSelectedModules))
  272. {
  273. $oFactory->LoadModule($oModule);
  274. }
  275. }
  276. if (strlen($sWorkspaceDir) > 0)
  277. {
  278. $oWorkspace = new MFWorkspace(APPROOT.$sWorkspaceDir);
  279. if (file_exists($oWorkspace->GetWorkspacePath()))
  280. {
  281. $oFactory->LoadModule($oWorkspace);
  282. }
  283. }
  284. //$oFactory->Dump();
  285. if ($oFactory->HasLoadErrors())
  286. {
  287. foreach($oFactory->GetLoadErrors() as $sModuleId => $aErrors)
  288. {
  289. SetupPage::log_error("Data model source file (xml) could not be loaded - found errors in module: $sModuleId");
  290. foreach($aErrors as $oXmlError)
  291. {
  292. SetupPage::log_error("Load error: File: ".$oXmlError->file." Line:".$oXmlError->line." Message:".$oXmlError->message);
  293. }
  294. }
  295. throw new Exception("The data model could not be compiled. Please check the setup error log");
  296. }
  297. else
  298. {
  299. $oMFCompiler = new MFCompiler($oFactory, $sSourcePath);
  300. $oMFCompiler->Compile($sTargetPath);
  301. SetupPage::log_info("Data model successfully compiled to '$sTargetPath'.");
  302. }
  303. }
  304. protected static function DoUpdateDBSchema($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBNewName, $sDBPrefix, $sTargetEnvironment = '')
  305. {
  306. SetupPage::log_info("Update Database Schema for environment '$sTargetEnvironment'.");
  307. $oConfig = new Config();
  308. $aParamValues = array(
  309. 'db_server' => $sDBServer,
  310. 'db_user' => $sDBUser,
  311. 'db_pwd' => $sDBPwd,
  312. 'db_name' => $sDBName,
  313. 'new_db_name' => $sDBNewName,
  314. 'db_prefix' => $sDBPrefix,
  315. );
  316. $oConfig->UpdateFromParams($aParamValues, $sModulesDir);
  317. $oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
  318. $oProductionEnv->InitDataModel($oConfig, true); // load data model only
  319. if(!$oProductionEnv->CreateDatabaseStructure(MetaModel::GetConfig(), $sMode))
  320. {
  321. throw(new Exception("Failed to create/upgrade the database structure for environment '$sTargetEnvironment'"));
  322. }
  323. SetupPage::log_info("Database Schema Successfully Updated for environment '$sTargetEnvironment'.");
  324. }
  325. protected static function AfterDBCreate($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBNewName, $sDBPrefix, $sAdminUser, $sAdminPwd, $sLanguage, $aSelectedModules, $sTargetEnvironment = '')
  326. {
  327. SetupPage::log_info('After Database Creation');
  328. $oConfig = new Config();
  329. $aParamValues = array(
  330. 'db_server' => $sDBServer,
  331. 'db_user' => $sDBUser,
  332. 'db_pwd' => $sDBPwd,
  333. 'db_name' => $sDBName,
  334. 'new_db_name' => $sDBNewName,
  335. 'db_prefix' => $sDBPrefix,
  336. );
  337. $oConfig->UpdateFromParams($aParamValues, $sModulesDir);
  338. $oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
  339. $oProductionEnv->InitDataModel($oConfig, false); // load data model and connect to the database
  340. // Perform here additional DB setup... profiles, etc...
  341. //
  342. $aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), $sModulesDir);
  343. foreach($aAvailableModules as $sModuleId => $aModule)
  344. {
  345. if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
  346. isset($aAvailableModules[$sModuleId]['installer']) )
  347. {
  348. $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
  349. SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
  350. // The validity of the sModuleInstallerClass has been established in BuildConfig()
  351. $aCallSpec = array($sModuleInstallerClass, 'AfterDatabaseCreation');
  352. call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
  353. }
  354. }
  355. // Constant classes (e.g. User profiles)
  356. //
  357. foreach (MetaModel::GetClasses() as $sClass)
  358. {
  359. $aPredefinedObjects = call_user_func(array($sClass, 'GetPredefinedObjects'));
  360. if ($aPredefinedObjects != null)
  361. {
  362. // Temporary... until this get really encapsulated as the default and transparent behavior
  363. $oMyChange = MetaModel::NewObject("CMDBChange");
  364. $oMyChange->Set("date", time());
  365. $sUserString = CMDBChange::GetCurrentUserName();
  366. $oMyChange->Set("userinfo", $sUserString);
  367. $iChangeId = $oMyChange->DBInsert();
  368. // Create/Delete/Update objects of this class,
  369. // according to the given constant values
  370. //
  371. $aDBIds = array();
  372. $oAll = new DBObjectSet(new DBObjectSearch($sClass));
  373. while ($oObj = $oAll->Fetch())
  374. {
  375. if (array_key_exists($oObj->GetKey(), $aPredefinedObjects))
  376. {
  377. $aObjValues = $aPredefinedObjects[$oObj->GetKey()];
  378. foreach ($aObjValues as $sAttCode => $value)
  379. {
  380. $oObj->Set($sAttCode, $value);
  381. }
  382. $oObj->DBUpdateTracked($oMyChange);
  383. $aDBIds[$oObj->GetKey()] = true;
  384. }
  385. else
  386. {
  387. $oObj->DBDeleteTracked($oMyChange);
  388. }
  389. }
  390. foreach ($aPredefinedObjects as $iRefId => $aObjValues)
  391. {
  392. if (!array_key_exists($iRefId, $aDBIds))
  393. {
  394. $oNewObj = MetaModel::NewObject($sClass);
  395. $oNewObj->SetKey($iRefId);
  396. foreach ($aObjValues as $sAttCode => $value)
  397. {
  398. $oNewObj->Set($sAttCode, $value);
  399. }
  400. $oNewObj->DBInsertTracked($oMyChange);
  401. }
  402. }
  403. }
  404. }
  405. if (!$oProductionEnv->RecordInstallation($oConfig, $aSelectedModules, $sModulesDir))
  406. {
  407. throw(new Exception("Failed to record the installation information"));
  408. }
  409. if($sMode == 'install')
  410. {
  411. if (!self::CreateAdminAccount(MetaModel::GetConfig(), $sAdminUser, $sAdminPwd, $sLanguage))
  412. {
  413. throw(new Exception("Failed to create the administrator account '$sAdminUser'"));
  414. }
  415. else
  416. {
  417. SetupPage::log_info("Administrator account '$sAdminUser' created.");
  418. }
  419. }
  420. }
  421. /**
  422. * Helper function to create and administrator account for iTop
  423. * @return boolean true on success, false otherwise
  424. */
  425. protected static function CreateAdminAccount(Config $oConfig, $sAdminUser, $sAdminPwd, $sLanguage)
  426. {
  427. SetupPage::log_info('CreateAdminAccount');
  428. if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage))
  429. {
  430. return true;
  431. }
  432. else
  433. {
  434. return false;
  435. }
  436. }
  437. protected static function DoLoadFiles($aFiles, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '')
  438. {
  439. $aParamValues = array(
  440. 'db_server' => $sDBServer,
  441. 'db_user' => $sDBUser,
  442. 'db_pwd' => $sDBPwd,
  443. 'db_name' => $sDBName,
  444. 'new_db_name' => $sDBName,
  445. 'db_prefix' => $sDBPrefix,
  446. );
  447. $oConfig = new Config();
  448. $oConfig->UpdateFromParams($aParamValues, $sModulesDir);
  449. //TODO: load the MetaModel if needed (async mode)
  450. //$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
  451. //$oProductionEnv->InitDataModel($oConfig, false); // load data model and connect to the database
  452. $oDataLoader = new XMLDataLoader();
  453. $oChange = MetaModel::NewObject("CMDBChange");
  454. $oChange->Set("date", time());
  455. $oChange->Set("userinfo", "Initialization");
  456. $iChangeId = $oChange->DBInsert();
  457. SetupPage::log_info("starting data load session");
  458. $oDataLoader->StartSession($oChange);
  459. foreach($aFiles as $sFileRelativePath)
  460. {
  461. $sFileName = APPROOT.'env-'.(($sTargetEnvironment == '') ? 'production' : $sTargetEnvironment).'/'.$sFileRelativePath;
  462. SetupPage::log_info("Loading file: $sFileName");
  463. if (empty($sFileName) || !file_exists($sFileName))
  464. {
  465. throw(new Exception("File $sFileName does not exist"));
  466. }
  467. $oDataLoader->LoadFile($sFileName);
  468. $sResult = sprintf("loading of %s done.", basename($sFileName));
  469. SetupPage::log_info($sResult);
  470. }
  471. $oDataLoader->EndSession();
  472. SetupPage::log_info("ending data load session");
  473. }
  474. protected static function DoCreateConfig($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModules, $sTargetEnvironment = '')
  475. {
  476. $aParamValues = array(
  477. 'db_server' => $sDBServer,
  478. 'db_user' => $sDBUser,
  479. 'db_pwd' => $sDBPwd,
  480. 'db_name' => $sDBName,
  481. 'new_db_name' => $sDBName,
  482. 'db_prefix' => $sDBPrefix,
  483. 'application_path' => $sUrl,
  484. 'mode' => $sMode,
  485. 'language' => $sLanguage,
  486. 'selected_modules' => implode(',', $aSelectedModules),
  487. );
  488. $oConfig = new Config();
  489. // Migration: force utf8_unicode_ci as the collation to make the global search
  490. // NON case sensitive
  491. $oConfig->SetDBCollation('utf8_unicode_ci');
  492. // Final config update: add the modules
  493. $oConfig->UpdateFromParams($aParamValues, $sModulesDir);
  494. // Make sure the root configuration directory exists
  495. if (!file_exists(APPCONF))
  496. {
  497. mkdir(APPCONF);
  498. chmod(APPCONF, 0770); // RWX for owner and group, nothing for others
  499. SetupPage::log_info("Created configuration directory: ".APPCONF);
  500. }
  501. // Write the final configuration file
  502. $sConfigFile = APPCONF.(($sTargetEnvironment == '') ? 'production' : $sTargetEnvironment).'/'.ITOP_CONFIG_FILE;
  503. $sConfigDir = dirname($sConfigFile);
  504. @mkdir($sConfigDir);
  505. @chmod($sConfigDir, 0770); // RWX for owner and group, nothing for others
  506. $oConfig->WriteToFile($sConfigFile);
  507. // try to make the final config file read-only
  508. @chmod($sConfigFile, 0444); // Read-only for owner and group, nothing for others
  509. }
  510. }