From 99a43be156783cbcc24cec7e940108e9d882cf54 Mon Sep 17 00:00:00 2001 From: Adrian Rossouw <adrian@developmentseed.org> Date: Fri, 3 Apr 2009 23:05:45 +0000 Subject: [PATCH] Added back some basic d7 support. updates are still broken tho --- platform/cvs_deploy.inc | 1 + platform/drupal_7_update.inc | 462 +++++++++++++++++++++++++++- platform/provision_drupal.drush.inc | 23 +- 3 files changed, 474 insertions(+), 12 deletions(-) diff --git a/platform/cvs_deploy.inc b/platform/cvs_deploy.inc index 4852d8a9d..a3b73342a 100644 --- a/platform/cvs_deploy.inc +++ b/platform/cvs_deploy.inc @@ -12,6 +12,7 @@ * Used by provision to hook into the cvs_deploy system. */ function _provision_cvs_deploy(&$file) { + $file->filename = realpath($file->filename); $project = $file->info['project']; $project = ($project) ? $project : _provision_cvs_deploy_get_project_name($file); $file->project = $project; diff --git a/platform/drupal_7_update.inc b/platform/drupal_7_update.inc index 85ecae6fd..aa0c53b1e 100644 --- a/platform/drupal_7_update.inc +++ b/platform/drupal_7_update.inc @@ -8,20 +8,452 @@ * line. */ -ob_start(); -$_REQUEST['op'] = 'info'; -include_once("update.php"); -ob_end_clean(); +/** + * Global flag to identify update.php run, and so avoid various unwanted + * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing + * and translation, and solve some theming issues. This flag is checked on several + * places in Drupal code (not just update.php). + */ +define('MAINTENANCE_MODE', 'update'); + +/** + * Add a column to a database using syntax appropriate for PostgreSQL. + * Save result of SQL commands in $ret array. + * + * Note: when you add a column with NOT NULL and you are not sure if there are + * already rows in the table, you MUST also add DEFAULT. Otherwise PostgreSQL + * won't work when the table is not empty, and db_add_column() will fail. + * To have an empty string as the default, you must use: 'default' => "''" + * in the $attributes array. If NOT NULL and DEFAULT are set the PostgreSQL + * version will set values of the added column in old rows to the + * DEFAULT value. + * + * @param $ret + * Array to which results will be added. + * @param $table + * Name of the table, without {} + * @param $column + * Name of the column + * @param $type + * Type of column + * @param $attributes + * Additional optional attributes. Recognized attributes: + * not null => TRUE|FALSE + * default => NULL|FALSE|value (the value must be enclosed in '' marks) + * @return + * nothing, but modifies $ret parameter. + */ +function db_add_column(&$ret, $table, $column, $type, $attributes = array()) { + if (array_key_exists('not null', $attributes) and $attributes['not null']) { + $not_null = 'NOT NULL'; + } + if (array_key_exists('default', $attributes)) { + if (is_null($attributes['default'])) { + $default_val = 'NULL'; + $default = 'default NULL'; + } + elseif ($attributes['default'] === FALSE) { + $default = ''; + } + else { + $default_val = "$attributes[default]"; + $default = "default $attributes[default]"; + } + } + + $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column $type"); + if (!empty($default)) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET $default"); + } + if (!empty($not_null)) { + if (!empty($default)) { + $ret[] = update_sql("UPDATE {" . $table . "} SET $column = $default_val"); + } + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column SET NOT NULL"); + } +} + +/** + * Change a column definition using syntax appropriate for PostgreSQL. + * Save result of SQL commands in $ret array. + * + * Remember that changing a column definition involves adding a new column + * and dropping an old one. This means that any indices, primary keys and + * sequences from serial-type columns are dropped and might need to be + * recreated. + * + * @param $ret + * Array to which results will be added. + * @param $table + * Name of the table, without {} + * @param $column + * Name of the column to change + * @param $column_new + * New name for the column (set to the same as $column if you don't want to change the name) + * @param $type + * Type of column + * @param $attributes + * Additional optional attributes. Recognized attributes: + * not null => TRUE|FALSE + * default => NULL|FALSE|value (with or without '', it won't be added) + * @return + * nothing, but modifies $ret parameter. + */ +function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) { + if (array_key_exists('not null', $attributes) and $attributes['not null']) { + $not_null = 'NOT NULL'; + } + if (array_key_exists('default', $attributes)) { + if (is_null($attributes['default'])) { + $default_val = 'NULL'; + $default = 'default NULL'; + } + elseif ($attributes['default'] === FALSE) { + $default = ''; + } + else { + $default_val = "$attributes[default]"; + $default = "default $attributes[default]"; + } + } + + $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME $column TO " . $column . "_old"); + $ret[] = update_sql("ALTER TABLE {" . $table . "} ADD $column_new $type"); + $ret[] = update_sql("UPDATE {" . $table . "} SET $column_new = " . $column . "_old"); + if ($default) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET $default"); + } + if ($not_null) { + $ret[] = update_sql("ALTER TABLE {" . $table . "} ALTER $column_new SET NOT NULL"); + } + $ret[] = update_sql("ALTER TABLE {" . $table . "} DROP " . $column . "_old"); +} + +/** + * Perform one update and store the results which will later be displayed on + * the finished page. + * + * An update function can force the current and all later updates for this + * module to abort by returning a $ret array with an element like: + * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong'); + * The schema version will not be updated in this case, and all the + * aborted updates will continue to appear on update.php as updates that + * have not yet been run. + * + * @param $module + * The module whose update will be run. + * @param $number + * The update number to run. + * @param $context + * The batch context array + */ +function update_do_one($module, $number, &$context) { + // If updates for this module have been aborted + // in a previous step, go no further. + if (!empty($context['results'][$module]['#abort'])) { + return; + } + + try { + $function = $module . '_update_' . $number; + if (function_exists($function)) { + $ret = $function($context['sandbox']); + } + } + catch (Exception $e) { + drush_set_error('DRUPAL_EXCEPTION', var_export($e, TRUE)); + } + + if (isset($ret['#finished'])) { + $context['finished'] = $ret['#finished']; + unset($ret['#finished']); + } + + if (!isset($context['results'][$module])) { + $context['results'][$module] = array(); + } + if (!isset($context['results'][$module][$number])) { + $context['results'][$module][$number] = array(); + } + $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret); + + if (!empty($ret['#abort'])) { + $context['results'][$module]['#abort'] = TRUE; + } + // Record the schema update if it was completed successfully. + if ($context['finished'] == 1 && empty($context['results'][$module]['#abort'])) { + drupal_set_installed_schema_version($module, $number); + } + + $context['message'] = 'Updating ' . check_plain($module) . ' module'; +} + +/** + * Create the batch table. + * + * This is part of the Drupal 5.x to 6.x migration. + */ +function update_create_batch_table() { + + // If batch table exists, update is not necessary + if (db_table_exists('batch')) { + return; + } + + $schema['batch'] = array( + 'fields' => array( + 'bid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'token' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), + 'timestamp' => array('type' => 'int', 'not null' => TRUE), + 'batch' => array('type' => 'text', 'not null' => FALSE, 'size' => 'big') + ), + 'primary key' => array('bid'), + 'indexes' => array('token' => array('token')), + ); + + $ret = array(); + db_create_table($ret, 'batch', $schema['batch']); + return $ret; +} + +/** + * Disable anything in the {system} table that is not compatible with the + * current version of Drupal core. + */ +function update_fix_compatibility() { + $ret = array(); + $incompatible = array(); + $query = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')"); + while ($result = db_fetch_object($query)) { + if (update_check_incompatibility($result->name, $result->type)) { + $incompatible[] = $result->name; + } + } + if (!empty($incompatible)) { + $ret[] = update_sql("UPDATE {system} SET status = 0 WHERE name IN ('" . implode("','", $incompatible) . "')"); + } + return $ret; +} + +/** + * Helper function to test compatibility of a module or theme. + */ +function update_check_incompatibility($name, $type = 'module') { + static $themes, $modules; + + // Store values of expensive functions for future use. + if (empty($themes) || empty($modules)) { + $themes = _system_theme_data(); + $modules = module_rebuild_cache(); + } + + if ($type == 'module' && isset($modules[$name])) { + $file = $modules[$name]; + } + elseif ($type == 'theme' && isset($themes[$name])) { + $file = $themes[$name]; + } + if (!isset($file) + || !isset($file->info['core']) + || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY + || version_compare(phpversion(), $file->info['php']) < 0 + || ($type == 'module' && empty($file->info['files']))) { + return TRUE; + } + return FALSE; +} + +/** + * Perform Drupal 5.x to 6.x updates that are required for update.php + * to function properly. + * + * This function runs when update.php is run the first time for 6.x, + * even before updates are selected or performed. It is important + * that if updates are not ultimately performed that no changes are + * made which make it impossible to continue using the prior version. + * Just adding columns is safe. However, renaming the + * system.description column to owner is not. Therefore, we add the + * system.owner column and leave it to system_update_6008() to copy + * the data from description and remove description. The same for + * renaming locales_target.locale to locales_target.language, which + * will be finished by locale_update_6002(). + */ +function update_fix_d6_requirements() { + $ret = array(); + + if (drupal_get_installed_schema_version('system') < 6000 && !variable_get('update_d6_requirements', FALSE)) { + $spec = array('type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE); + db_add_field($ret, 'cache', 'serialized', $spec); + db_add_field($ret, 'cache_filter', 'serialized', $spec); + db_add_field($ret, 'cache_page', 'serialized', $spec); + db_add_field($ret, 'cache_menu', 'serialized', $spec); + + db_add_field($ret, 'system', 'info', array('type' => 'text')); + db_add_field($ret, 'system', 'owner', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); + if (db_table_exists('locales_target')) { + db_add_field($ret, 'locales_target', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '')); + } + if (db_table_exists('locales_source')) { + db_add_field($ret, 'locales_source', 'textgroup', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default')); + db_add_field($ret, 'locales_source', 'version', array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => 'none')); + } + variable_set('update_d6_requirements', TRUE); + + // Create the cache_block table. See system_update_6027() for more details. + $schema['cache_block'] = array( + 'fields' => array( + 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'), + 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'headers' => array('type' => 'text', 'not null' => FALSE), + 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0) + ), + 'indexes' => array('expire' => array('expire')), + 'primary key' => array('cid'), + ); + db_create_table($ret, 'cache_block', $schema['cache_block']); + } + + return $ret; +} + +/** + * Add the update task list to the current page. + */ +function update_task_list($active = NULL) { + // Default list of tasks. + $tasks = array( + 'info' => 'Overview', + 'select' => 'Review updates', + 'run' => 'Run updates', + 'finished' => 'Review log', + ); + + drupal_set_content('left', theme('task_list', $tasks, $active)); +} + +/** + * Check update requirements and report any errors. + */ +function update_check_requirements() { + global $db_url, $databases; + $requirements = array(); + + // If we will rewrite the settings.php then we need to make sure it is + // writeable. + if (empty($databases) && !empty($db_url) && is_string($db_url)) { + $requirements = install_check_requirements('', FALSE); + } + $warnings = FALSE; + + // Check the system module requirements only. + $requirements += module_invoke('system', 'requirements', 'update'); + $severity = drupal_requirements_severity($requirements); + + // If there are issues, report them. + if ($severity != REQUIREMENT_OK) { + foreach ($requirements as $requirement) { + if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) { + $message = isset($requirement['description']) ? $requirement['description'] : ''; + if (isset($requirement['value']) && $requirement['value']) { + $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')'; + } + $warnings = TRUE; + drupal_set_message($message, 'warning'); + } + } + } + return $warnings; +} + +/** + * Converts Drupal 6 $db_url to Drupal 7 $databases array. + */ +function update_check_d7_settings() { + global $db_url, $databases; + + if (empty($databases) && !empty($db_url) && is_string($db_url)) { + $url = parse_url($db_url); + $driver = substr($db_url, 0, strpos($db_url, '://')); + if ($driver == 'mysqli') { + $driver = 'mysql'; + } + $databases['default']['default']['driver'] = $driver; + $databases['default']['default']['database'] = substr($url['path'], 1); + foreach (array('user' => 'username', 'pass' => 'password', 'host' => 'host', 'port' => 'port') as $old_key => $new_key) { + $databases['default']['default'][$new_key] = isset($url[$old_key]) ? urldecode($url[$old_key]) : ''; + } + $conf_path = conf_path(); + file_put_contents($conf_path .'/settings.php', "\n" . '$databases = '. var_export($databases, TRUE) . ';', FILE_APPEND); + } +} + function update_main() { - include_once './includes/install.inc'; - include_once './includes/batch.inc'; + // Some unavoidable errors happen because the database is not yet up-to-date. + // Our custom error handler is not yet installed, so we just suppress them. + _provision_errors_off(); + + require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; + + // We only load DRUPAL_BOOTSTRAP_CONFIGURATION for the update requirements + // check to avoid reaching the PHP memory limit. + // Minimum load of components. + drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION); + + require_once DRUPAL_ROOT . '/includes/install.inc'; + require_once DRUPAL_ROOT . '/includes/file.inc'; + require_once DRUPAL_ROOT . '/modules/system/system.install'; + + // Load module basics. + include_once DRUPAL_ROOT . '/includes/module.inc'; + $module_list['system']['filename'] = 'modules/system/system.module'; + $module_list['filter']['filename'] = 'modules/filter/filter.module'; + module_list(TRUE, FALSE, $module_list); + drupal_load('module', 'system'); + drupal_load('module', 'filter'); + + // Set up $language, since the installer components require it. + drupal_init_language(); + + // Set up theme system for the maintenance page. + drupal_maintenance_theme(); + + // Check the update requirements for Drupal. + $warnings = update_check_requirements(); + + // Allow the database system to work even though the registry has not + // been created yet. + drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_DATABASE); + include_once DRUPAL_ROOT . '/includes/install.inc'; + drupal_install_init_database(); + spl_autoload_unregister('drupal_autoload_class'); + spl_autoload_unregister('drupal_autoload_interface'); + // The new {blocked_ips} table is used in Drupal 7 to store a list of + // banned IP addresses. If this table doesn't exist then we are still + // running on a Drupal 6 database, so suppress the unavoidable errors + // that occur. + try { + drupal_bootstrap(DRUPAL_BOOTSTRAP_ACCESS); + } + catch (Exception $e) { + if (db_table_exists('blocked_ips')) { + throw $e; + } + } + + drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL); + drupal_maintenance_theme(); + _provision_errors_on(); + drupal_load_updates(); provision_drupal_install_log(update_fix_d6_requirements()); provision_drupal_install_log(update_fix_compatibility()); $start = array(); + $start['system'] = array(); $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE); foreach ($modules as $module => $schema_version) { $updates = drupal_get_schema_versions($module); @@ -97,6 +529,7 @@ function _update_do_one($module, $number, &$context) { $function = $module .'_update_'. $number; if (function_exists($function)) { + drush_log("Running $function"); $ret = $function($context['sandbox']); } provision_drupal_install_log($ret); @@ -112,4 +545,21 @@ function _update_do_one($module, $number, &$context) { } + +function update_finished($success, $results, $operations) { + // clear the caches in case the data has been updated. + drupal_flush_all_caches(); + + drupal_set_session('update_results', $results); + drupal_set_session('update_success', $success); + drupal_set_session('updates_remaining', $operations); + + // Now that the update is done, we can disable site maintenance if it was + // previously turned off. + if (isset($_SESSION['site_offline']) && $_SESSION['site_offline'] == FALSE) { + variable_set('site_offline', FALSE); + unset($_SESSION['site_offline']); + } +} + update_main(); diff --git a/platform/provision_drupal.drush.inc b/platform/provision_drupal.drush.inc index a73c1dd94..0fac35341 100644 --- a/platform/provision_drupal.drush.inc +++ b/platform/provision_drupal.drush.inc @@ -218,19 +218,19 @@ function _provision_drupal_create_directories($url, $profile = NULL) { provision_path("mkdir", $path, TRUE, dt("Created <code>@path</code>"), dt("Could not create <code>@path</code>"), - DRUSH_PERM_ERROR); + 'DRUSH_PERM_ERROR'); } provision_path("chmod", $path, $perm, dt("Changed permissions of <code>@path</code> to @confirm"), dt("Could not change permissions <code>@path</code> to @confirm"), - DRUSH_PERM_ERROR); + 'DRUSH_PERM_ERROR'); } foreach ($grps as $path) { provision_path("chown", $path, drush_get_option('script_user'), dt("Changed ownership of <code>@path</code>"), dt("Could not change ownership <code>@path</code>"), - DRUSH_PERM_ERROR ); + 'DRUSH_PERM_ERROR' ); provision_path("chgrp", $path, drush_get_option('web_group'), dt("Changed group ownership of <code>@path</code>"), dt("Could not change group ownership <code>@path</code>")); @@ -392,10 +392,14 @@ function provision_find_packages() { // Find install profiles. $profiles = _provision_find_profiles(); drush_set_option('profiles', array_keys((array) $profiles), 'drupal'); - $packages['base']['profiles'] = _scrub_object($profiles); // Iterate through the install profiles, finding the profile specific packages foreach ($profiles as $profile => $info) { + _provision_cvs_deploy($info); + if (!$info->version) { + $info->version = drush_drupal_version(); + } + $packages['base']['profiles'][$profile] = $info; $packages['profiles'][$profile] = _provision_find_packages('profiles', $profile); } @@ -438,10 +442,17 @@ function provision_drupal_system_map() { // Load the version specific include files. provision_platform_include(dirname(__FILE__), 'packages'); - $profile = drush_get_option('profile'); $profiles = _provision_find_profiles(); - + foreach ($profiles as $profile => $info) { + _provision_cvs_deploy($info); + if (!$info->version) { + $info->version = drush_drupal_version(); + } + $profiles[$profile] = $info; + } $packages['platforms'] = _provision_find_platforms(); + + $profile = drush_get_option('profile'); $packages['profiles'][$profile] = $profiles[$profile]; $packages['profiles'][$profile]->status = 1; -- GitLab