' . "\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;
}
/**
Index: install/install_toolkit.php
===================================================================
--- install/install_toolkit.php (revision 13756)
+++ install/install_toolkit.php (working copy)
@@ -304,7 +304,7 @@
* @param mixed $replace_from
* @param mixed $replace_to
*/
- function RunSQLText(&$sqls, $replace_from = null, $replace_to = null, $start_from=0)
+ function RunSQLText(&$sqls, $replace_from = null, $replace_to = null, $start_from = 0)
{
$table_prefix = $this->getSystemConfig('Database', 'TablePrefix');
@@ -818,6 +818,8 @@
$this->Application->HandleEvent($event, 'adm:OnResetConfigsCache');
$this->Application->HandleEvent($event, 'c:OnResetCMSMenuCache');
+ $this->Conn->Query('DELETE FROM ' . TABLE_PREFIX . 'CachedUrls');
+
if ($refresh_permissions) {
if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) {
// refresh permission without progress bar
@@ -906,4 +908,129 @@
$permissions = fileperms($file);
return $permissions & 0x0010 || $permissions & 0x0002;
}
+
+ /**
+ * Upgrades primary skin to the latest version
+ *
+ * @param Array $module_info
+ * @return string
+ */
+ function upgradeSkin($module_info)
+ {
+ $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'css');
+ $data = file_get_contents($upgrades_file);
+
+ // get all versions with their positions in file
+ $versions = Array ();
+ preg_match_all('/(' . VERSION_MARK . ')/s', $data, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
+ $from_version_int = $this->ConvertModuleVersion($module_info['FromVersion']);
+
+ foreach ($matches as $index => $match) {
+ $version_int = $this->ConvertModuleVersion($match[2][0]);
+
+ if ($version_int < $from_version_int) {
+ // only process versions, that were released after currently used version
+ continue;
+ }
+
+ $start_pos = $match[0][1] + strlen($match[0][0]);
+ $end_pos = array_key_exists($index + 1, $matches) ? $matches[$index + 1][0][1] : mb_strlen($data);
+ $patch_data = str_replace("\r\n", "\n", substr($data, $start_pos, $end_pos - $start_pos));
+
+ $versions[] = Array (
+ 'Version' => $match[2][0],
+ // fixes trimmed leading spaces by modern text editor
+ 'Data' => ltrim( str_replace("\n\n", "\n \n", $patch_data) ),
+ );
+ }
+
+ if (!$versions) {
+ // not skin changes -> quit
+ return ;
+ }
+
+ $primary_skin =& $this->Application->recallObject('skin.primary', null, Array ('skip_autoload' => true));
+ /* @var $primary_skin kDBItem */
+
+ $primary_skin->Load(1, 'IsPrimary');
+
+ if (!$primary_skin->isLoaded()) {
+ // we always got primary skin, but just in case
+ return ;
+ }
+
+ $temp_handler =& $this->Application->recallObject('skin_TempHandler', 'kTempTablesHandler');
+ /* @var $temp_handler kTempTablesHandler */
+
+ // clone current skin
+ $cloned_ids = $temp_handler->CloneItems('skin', '', Array ($primary_skin->GetID()));
+
+ if (!$cloned_ids) {
+ // can't clone
+ return ;
+ }
+
+ $skin =& $this->Application->recallObject('skin.tmp', null, Array ('skip_autoload' => true));
+ /* @var $skin kDBItem */
+
+ $skin->Load( $cloned_ids[0] );
+
+ // save css to temp file (for patching)
+ $skin_file = tempnam('/tmp', 'skin_css_');
+ $fp = fopen($skin_file, 'w');
+ fwrite($fp, str_replace("\r\n", "\n", $skin->GetDBField('CSS')));
+ fclose($fp);
+
+ $output = Array ();
+ $patch_file = tempnam('/tmp', 'skin_patch_');
+
+ foreach ($versions as $version_info) {
+ // for each left version get it's patch and apply to temp file
+ $fp = fopen($patch_file, 'w');
+ fwrite($fp, $version_info['Data']);
+ fclose($fp);
+
+ $output[ $version_info['Version'] ] = shell_exec('patch ' . $skin_file . ' ' . $patch_file . ' 2>&1') . "\n";
+ }
+
+ // place temp file content into cloned skin
+ $skin->SetDBField('Name', 'Upgraded to ' . $module_info['ToVersion']);
+ $skin->SetDBField('CSS', file_get_contents($skin_file));
+ $skin->Update();
+
+ unlink($skin_file);
+ unlink($patch_file);
+
+ $has_errors = false;
+
+ foreach ($output as $version => $version_output) {
+ $version_errors = trim( preg_replace("/(^|\n)(patching file .*?|Hunk #.*?\.)(\n|$)/m", '', $version_output) );
+
+ if ($version_errors) {
+ $has_errors = true;
+ $output[$version] = trim( preg_replace("/(^|\n)(patching file .*?)(\n|$)/m", '', $output[$version]) );
+ }
+ else {
+ unset($output[$version]);
+ }
+ }
+
+ if (!$has_errors) {
+ // copy patched css back to primary skin
+ $primary_skin->SetDBField('CSS', $skin->GetDBField('CSS'));
+ $primary_skin->Update();
+
+ // delete temporary skin record
+ $temp_handler->DeleteItems('skin', '', Array ($skin->GetID()));
+
+ return true;
+ }
+
+ // put clean skin from new version
+ $skin->SetDBField('CSS', file_get_contents(FULL_PATH . '/core/admin_templates/incs/style_template.css'));
+ $skin->Update();
+
+ // return output in case of errors
+ return $output;
+ }
}
\ No newline at end of file
Index: install/step_templates/skin_upgrade.tpl
===================================================================
--- install/step_templates/skin_upgrade.tpl (revision 0)
+++ install/step_templates/skin_upgrade.tpl (revision 0)
@@ -0,0 +1,24 @@
+
+
+ Automatic upgrade of Administrative Console Skin failed. You can verify patching log errors below.
+
+
+
+
+
+
+
+ You must manually merge skin, created during upgrade with your current
+ primary skin to ensure, that Administrative Console interface isn't broken.
+