public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false) { if (!defined('MODULESROOT')) { define('MODULESROOT', APPROOT . 'env-' . utils::GetCurrentEnvironment() . '/'); self::$m_bTraceSourceFiles = $bTraceSourceFiles; // $config can be either a filename, or a Configuration object (volatile!) if ($config instanceof Config) { self::LoadConfig($config, $bAllowCache); } else { self::LoadConfig(new Config($config), $bAllowCache); } if ($bModelOnly) { return; } } CMDBSource::SelectDB(self::$m_sDBName); foreach (get_declared_classes() as $sPHPClass) { if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI')) { $aCallSpec = array($sPHPClass, 'OnMetaModelStarted'); call_user_func_array($aCallSpec, array()); } } if (false) { echo "Debug<br/>\n"; self::static_var_dump(); } }
protected static function DoUpdateDBSchema($sMode, $aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false) { SetupPage::log_info("Update Database Schema for environment '{$sTargetEnvironment}'."); $oConfig = new Config(); $aParamValues = array('mode' => $sMode, 'db_server' => $sDBServer, 'db_user' => $sDBUser, 'db_pwd' => $sDBPwd, 'db_name' => $sDBName, 'db_prefix' => $sDBPrefix); $oConfig->UpdateFromParams($aParamValues, $sModulesDir); if ($bOldAddon) { // Old version of the add-on for backward compatibility with pre-2.0 data models $oConfig->SetAddons(array('user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php')); } $oProductionEnv = new RunTimeEnvironment($sTargetEnvironment); $oProductionEnv->InitDataModel($oConfig, true); // load data model only // Migrate application data format // // priv_internalUser caused troubles because MySQL transforms table names to lower case under Windows // This becomes an issue when moving your installation data to/from Windows // Starting 2.0, all table names must be lowercase if ($sMode != 'install') { SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' into '{$sDBPrefix}priv_internaluser' (lowercase)"); // This command will have no effect under Windows... // and it has been written in two steps so as to make it work under windows! CMDBSource::SelectDB($sDBName); try { $sRepair = "RENAME TABLE `{$sDBPrefix}priv_internalUser` TO `{$sDBPrefix}priv_internaluser_other`, `{$sDBPrefix}priv_internaluser_other` TO `{$sDBPrefix}priv_internaluser`"; CMDBSource::Query($sRepair); } catch (Exception $e) { SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' failed (already done in a previous upgrade?)"); } // let's remove the records in priv_change which have no counterpart in priv_changeop SetupPage::log_info("Cleanup of '{$sDBPrefix}priv_change' to remove orphan records"); CMDBSource::SelectDB($sDBName); try { $sTotalCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_change`"; $iTotalCount = (int) CMDBSource::QueryToScalar($sTotalCount); SetupPage::log_info("There is a total of {$iTotalCount} records in {$sDBPrefix}priv_change."); $sOrphanCount = "SELECT COUNT(c.id) FROM `{$sDBPrefix}priv_change` AS c left join `{$sDBPrefix}priv_changeop` AS o ON c.id = o.changeid WHERE o.id IS NULL"; $iOrphanCount = (int) CMDBSource::QueryToScalar($sOrphanCount); SetupPage::log_info("There are {$iOrphanCount} useless records in {$sDBPrefix}priv_change (" . sprintf('%.2f', 100.0 * $iOrphanCount / $iTotalCount) . "%)"); if ($iOrphanCount > 0) { SetupPage::log_info("Removing the orphan records..."); $sCleanup = "DELETE FROM `{$sDBPrefix}priv_change` USING `{$sDBPrefix}priv_change` LEFT JOIN `{$sDBPrefix}priv_changeop` ON `{$sDBPrefix}priv_change`.id = `{$sDBPrefix}priv_changeop`.changeid WHERE `{$sDBPrefix}priv_changeop`.id IS NULL;"; CMDBSource::Query($sCleanup); SetupPage::log_info("Cleanup completed successfully."); } else { SetupPage::log_info("Ok, nothing to cleanup."); } } catch (Exception $e) { SetupPage::log_info("Cleanup of orphan records in `{$sDBPrefix}priv_change` failed: " . $e->getMessage()); } } // Module specific actions (migrate the data) // $aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT . $sModulesDir); foreach ($aAvailableModules as $sModuleId => $aModule) { if ($sModuleId != ROOT_MODULE && in_array($sModuleId, $aSelectedModules) && isset($aAvailableModules[$sModuleId]['installer'])) { $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer']; SetupPage::log_info("Calling Module Handler: {$sModuleInstallerClass}::BeforeDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})"); $aCallSpec = array($sModuleInstallerClass, 'BeforeDatabaseCreation'); call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code'])); } } if (!$oProductionEnv->CreateDatabaseStructure(MetaModel::GetConfig(), $sMode)) { throw new Exception("Failed to create/upgrade the database structure for environment '{$sTargetEnvironment}'"); } // priv_change now has an 'origin' field to distinguish between the various input sources // Let's initialize the field with 'interactive' for all records were it's null // Then check if some records should hold a different value, based on a pattern matching in the userinfo field CMDBSource::SelectDB($sDBName); try { $sCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_change` WHERE `origin` IS NULL"; $iCount = (int) CMDBSource::QueryToScalar($sCount); if ($iCount > 0) { SetupPage::log_info("Initializing '{$sDBPrefix}priv_change.origin' ({$iCount} records to update)"); // By default all uninitialized values are considered as 'interactive' $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'interactive' WHERE `origin` IS NULL"; CMDBSource::Query($sInit); // CSV Import was identified by the comment at the end $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-import.php' WHERE `userinfo` LIKE '%Web Service (CSV)'"; CMDBSource::Query($sInit); // CSV Import was identified by the comment at the end $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-interactive' WHERE `userinfo` LIKE '%(CSV)' AND origin = 'interactive'"; CMDBSource::Query($sInit); // Syncho data sources were identified by the comment at the end // Unfortunately the comment is localized, so we have to search for all possible patterns $sCurrentLanguage = Dict::GetUserLanguage(); foreach (Dict::GetLanguages() as $sLangCode => $aLang) { Dict::SetUserLanguage($sLangCode); $sSuffix = CMDBSource::Quote('%' . Dict::S('Core:SyncDataExchangeComment')); $aSuffixes[$sSuffix] = true; } Dict::SetUserLanguage($sCurrentLanguage); $sCondition = "`userinfo` LIKE " . implode(" OR `userinfo` LIKE ", array_keys($aSuffixes)); $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'synchro-data-source' WHERE ({$sCondition})"; CMDBSource::Query($sInit); SetupPage::log_info("Initialization of '{$sDBPrefix}priv_change.origin' completed."); } else { SetupPage::log_info("'{$sDBPrefix}priv_change.origin' already initialized, nothing to do."); } } catch (Exception $e) { SetupPage::log_error("Initializing '{$sDBPrefix}priv_change.origin' failed: " . $e->getMessage()); } // priv_async_task now has a 'status' field to distinguish between the various statuses rather than just relying on the date columns // Let's initialize the field with 'planned' or 'error' for all records were it's null CMDBSource::SelectDB($sDBName); try { $sCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_async_task` WHERE `status` IS NULL"; $iCount = (int) CMDBSource::QueryToScalar($sCount); if ($iCount > 0) { SetupPage::log_info("Initializing '{$sDBPrefix}priv_async_task.status' ({$iCount} records to update)"); $sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'planned' WHERE (`status` IS NULL) AND (`started` IS NULL)"; CMDBSource::Query($sInit); $sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'error' WHERE (`status` IS NULL) AND (`started` IS NOT NULL)"; CMDBSource::Query($sInit); SetupPage::log_info("Initialization of '{$sDBPrefix}priv_async_task.status' completed."); } else { SetupPage::log_info("'{$sDBPrefix}priv_async_task.status' already initialized, nothing to do."); } } catch (Exception $e) { SetupPage::log_error("Initializing '{$sDBPrefix}priv_async_task.status' failed: " . $e->getMessage()); } SetupPage::log_info("Database Schema Successfully Updated for environment '{$sTargetEnvironment}'."); }