Index: branches/5.1.x/core/install.php =================================================================== diff -u -N -r13773 -r13780 --- branches/5.1.x/core/install.php (.../install.php) (revision 13773) +++ branches/5.1.x/core/install.php (.../install.php) (revision 13780) @@ -1,6 +1,6 @@ Array ('check_paths', 'clean_db', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'), 'already_installed' => Array ('check_paths', 'install_setup'), - 'upgrade' => Array ('check_paths', 'install_setup', 'upgrade_modules', 'security', 'finish'), + 'upgrade' => Array ('check_paths', 'install_setup', 'upgrade_modules', 'skin_upgrade', 'security', 'finish'), 'update_license' => Array ('check_paths', 'install_setup', 'select_license', /*'download_license',*/ 'select_domain', 'security', 'finish'), 'db_reconfig' => Array ('check_paths', 'install_setup', 'db_reconfig', 'security', 'finish'), 'fix_paths' => Array ('check_paths', 'install_setup', 'fix_paths', 'security', 'finish'), @@ -128,6 +128,20 @@ var $LastQueryNum = 0; /** + * Dependencies, that should be used in upgrade process + * + * @var Array + */ + var $upgradeDepencies = Array (); + + /** + * Log of upgrade - list of upgraded modules and their versions + * + * @var Array + */ + var $upgradeLog = Array (); + + /** * Common tools required for installation process * * @var kInstallToolkit @@ -312,6 +326,13 @@ } break; + case 'skin_upgrade': + if ($this->Application->RecallVar('SkinUpgradeLog') === false) { + // no errors during skin upgrade -> skip this step + $this->currentStep = $this->GetNextStep(); + } + break; + case 'install_setup': $next_preset = $this->Application->GetVar('next_preset'); if ($next_preset !== false) { @@ -838,51 +859,71 @@ case 'upgrade_modules': // get installed modules from db and compare their versions to upgrade script $modules = $this->Application->GetVar('modules'); + if ($modules) { $upgrade_data = $this->GetUpgradableModules(); + $start_from_query = $this->Application->GetVar('start_from_query'); + $this->upgradeDepencies = $this->getUpgradeDependencies($modules, $upgrade_data); - $start_from_module = $this->GetVar('continue_from_module'); - $start_from_query = $this->GetVar('continue_from_query'); - if (!$start_from_query) $start_from_query = 0; - foreach ($modules as $module_name) { - if ($start_from_module && $module_name != $start_from_module) { - continue; + if ($start_from_query !== false) { + $this->upgradeLog = unserialize( $this->Application->RecallVar('UpgradeLog') ); + } + else { + $start_from_query = 0; + $this->upgradeLog = Array ('ModuleVersions' => Array ()); + + // remember each module version, before upgrade scripts are executed + foreach ($modules as $module_name) { + $module_info = $upgrade_data[$module_name]; + $this->upgradeLog['ModuleVersions'][$module_name] = $module_info['FromVersion']; } - else { - $start_from_module = false; //otherwise it will skip all modules after the one we start with! - } - $module_info = $upgrade_data[$module_name]; - $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql'); - $sqls = file_get_contents($upgrades_file); - $version_mark = preg_replace('/(\(.*?\))/', $module_info['FromVersion'], VERSION_MARK); + $this->Application->RemoveVar('UpgradeLog'); + } - // get only sqls from next (relative to current) version to end of file - $start_pos = strpos($sqls, $version_mark); - $sqls = substr($sqls, $start_pos); + // 1. perform "php before", "sql", "php after" upgrades + foreach ($modules as $module_name) { + $module_info = $upgrade_data[$module_name]; - preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); + /*echo '

Upgrading "' . $module_info['Name'] . '" to "' . $module_info['ToVersion'] . '"

' . "\n"; + flush();*/ - if (!$start_from_module) { - $this->RunUpgrades($module_info['Path'], $regs[1], 'before'); - } - if (!$this->toolkit->RunSQLText($sqls, null, null, $start_from_query)) { - $this->errorMessage .= ''; - $this->errorMessage .= ''; - $this->errorMessage .= '
Click Continue button below to skip this query and go further
'; + if (!$this->RunUpgrade($module_info['Name'], $module_info['ToVersion'], $upgrade_data, $start_from_query)) { + $this->Application->StoreVar('UpgradeLog', serialize($this->upgradeLog)); $this->Done(); } - $start_from_query = 0; // so that next module start from the beggining + // restore upgradable module version (makes sense after sql error processing) + $upgrade_data[$module_name]['FromVersion'] = $this->upgradeLog['ModuleVersions'][$module_name]; + } + + // 2. import language pack, perform "languagepack" upgrade for all upgraded versions + foreach ($modules as $module_name) { + $module_info = $upgrade_data[$module_name]; + $sqls =& $this->getUpgradeQueriesFromVersion($module_info['Path'], $module_info['FromVersion']); + preg_match_all('/' . VERSION_MARK . '/s', $sqls, $regs); + + // import module language pack $this->toolkit->ImportLanguage('/' . $module_info['Path'] . 'install/english', true); - $this->RunUpgrades($module_info['Path'], $regs[1], 'after'); // upgrade script could operate resulting language pack - // after upgrade sqls are executed update version and upgrade language pack - $this->toolkit->SetModuleVersion($module_name, false, $module_info['ToVersion']); + // perform advanced language pack upgrade + foreach ($regs[1] as $version) { + $this->RunUpgradeScript($module_info['Path'], $version, 'languagepack'); + } } - // for now we set "In-Portal" module version to "Core" module version (during upgrade) + // 3. upgrade admin skin if (in_array('core', $modules)) { + $skin_upgrade_log = $this->toolkit->upgradeSkin($upgrade_data['core']); + + if ($skin_upgrade_log === true) { + $this->Application->RemoveVar('SkinUpgradeLog'); + } + else { + $this->Application->StoreVar('SkinUpgradeLog', serialize($skin_upgrade_log)); + } + + // for now we set "In-Portal" module version to "Core" module version (during upgrade) $this->toolkit->SetModuleVersion('In-Portal', false, $upgrade_data['core']['ToVersion']); } } @@ -934,40 +975,187 @@ } } + function getUpgradeDependencies($modules, &$upgrade_data) + { + $dependencies = Array (); + + foreach ($modules as $module_name) { + $module_info = $upgrade_data[$module_name]; + $upgrade_object =& $this->getUpgradeObject($module_info['Path']); + + if (!is_object($upgrade_object)) { + continue; + } + + foreach ($upgrade_object->dependencies as $dependent_version => $version_dependencies) { + if (!$version_dependencies) { + // module is independent -> skip + continue; + } + + list ($parent_name, $parent_version) = each($version_dependencies); + + if (!array_key_exists($parent_name, $dependencies)) { + // parent module + $dependencies[$parent_name] = Array (); + } + + if (!array_key_exists($parent_version, $dependencies[$parent_name])) { + // parent module versions, that are required by other module versions + $dependencies[$parent_name][$parent_version] = Array (); + } + + $dependencies[$parent_name][$parent_version][] = Array ($module_info['Name'] => $dependent_version); + } + } + + return $dependencies; + } + /** + * Returns database queries, that should be executed to perform upgrade from given to lastest version of given module path + * + * @param string $module_path + * @param string $from_version + * @return string + */ + function &getUpgradeQueriesFromVersion($module_path, $from_version) + { + $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'sql'); + + $sqls = file_get_contents($upgrades_file); + $version_mark = preg_replace('/(\(.*?\))/', $from_version, VERSION_MARK); + + // get only sqls from next (relative to current) version to end of file + $start_pos = strpos($sqls, $version_mark); + $sqls = substr($sqls, $start_pos); + + return $sqls; + } + + function RunUpgrade($module_name, $to_version, &$upgrade_data, &$start_from_query = 0) + { + $module_info = $upgrade_data[ strtolower($module_name) ]; + + $sqls =& $this->getUpgradeQueriesFromVersion($module_info['Path'], $module_info['FromVersion']); + preg_match_all('/(' . VERSION_MARK . ')/s', $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); + + foreach ($matches as $index => $match) { + // upgrade version + $version = $match[2][0]; + + if ($this->toolkit->ConvertModuleVersion($version) > $this->toolkit->ConvertModuleVersion($to_version)) { + // only upgrade to $to_version, not further + break; + } + + if (!in_array($module_name . ':' . $version, $this->upgradeLog)) { + if ($this->Application->isDebugMode()) { + $this->Application->Debugger->appendHTML('Upgrading "' . $module_name . '" to "' . $version . '" version: BEGIN.'); + } + + /*echo 'Upgrading "' . $module_name . '" to "' . $version . '".
' . "\n"; + flush();*/ + + // don't upgrade same version twice + $start_pos = $match[0][1] + strlen($match[0][0]); + $end_pos = array_key_exists($index + 1, $matches) ? $matches[$index + 1][0][1] : mb_strlen($sqls); + $version_sqls = substr($sqls, $start_pos, $end_pos - $start_pos); + + if ($start_from_query == 0) { + $this->RunUpgradeScript($module_info['Path'], $version, 'before'); + } + + if (!$this->toolkit->RunSQLText($version_sqls, null, null, $start_from_query)) { + $this->errorMessage .= ''; + $this->errorMessage .= '
Module "' . $module_name . '" upgrade to "' . $version . '" failed.'; + $this->errorMessage .= '
Click Continue button below to skip this query and go further
'; + + return false; + } + else { + // reset query counter, when all queries were processed + $start_from_query = 0; + } + + $this->RunUpgradeScript($module_info['Path'], $version, 'after'); + + if ($this->Application->isDebugMode()) { + $this->Application->Debugger->appendHTML('Upgrading "' . $module_name . '" to "' . $version . '" version: END.'); + } + + // remember, that we've already upgraded given version + $this->upgradeLog[] = $module_name . ':' . $version; + } + + if (array_key_exists($module_name, $this->upgradeDepencies) && array_key_exists($version, $this->upgradeDepencies[$module_name])) { + foreach ($this->upgradeDepencies[$module_name][$version] as $dependency_info) { + list ($dependent_module, $dependent_version) = each($dependency_info); + + if (!$this->RunUpgrade($dependent_module, $dependent_version, $upgrade_data, $start_from_query)) { + return false; + } + } + } + + // only mark module as updated, when all it's dependent modules are upgraded + $this->toolkit->SetModuleVersion($module_name, false, $version); + } + + return true; + } + + /** * Run upgrade PHP scripts for module with specified path * * @param string $module_path - * @param Array $versions - * @param string $mode upgrade mode = {before,after} + * @param Array $version + * @param string $mode upgrade mode = {before,after,languagepack} */ - function RunUpgrades($module_path, $versions, $mode) + function RunUpgradeScript($module_path, $version, $mode) { - static $upgrade_classes = Array (); + $upgrade_object =& $this->getUpgradeObject($module_path); - $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php'); - if (!file_exists($upgrades_file) || !$versions) { + if (!is_object($upgrade_object)) { return ; } + $upgrade_method = 'Upgrade_' . str_replace(Array ('.', '-'), '_', $version); + + if (method_exists($upgrade_object, $upgrade_method)) { + $upgrade_object->$upgrade_method($mode); + } + } + + /** + * Returns upgrade class for given module path + * + * @param string $module_path + * @return kUpgradeHelper + */ + function &getUpgradeObject($module_path) + { + static $upgrade_classes = Array (); + $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php'); + + if (!file_exists($upgrades_file)) { + $false = false; + return $false; + } + if (!isset($upgrade_classes[$module_path])) { - // save class name, because 2nd time - // (in after call $upgrade_class variable will not be present) + require_once(FULL_PATH . REL_PATH . '/install/upgrade_helper.php'); + + // save class name, because 2nd time (in after call) + // $upgrade_class variable will not be present include_once $upgrades_file; $upgrade_classes[$module_path] = $upgrade_class; } $upgrade_object = new $upgrade_classes[$module_path](); - if (method_exists($upgrade_object, 'setToolkit')) { - $upgrade_object->setToolkit($this->toolkit); - } + $upgrade_object->setToolkit($this->toolkit); - foreach ($versions as $version) { - $upgrade_method = 'Upgrade_'.str_replace(Array ('.', '-'), '_', $version); - if (method_exists($upgrade_object, $upgrade_method)) { - $upgrade_object->$upgrade_method($mode); - } - } + return $upgrade_object; } /**