PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/drush/commands/core/drupal/update_7.inc

#
Pascal | 317 lines | 118 code | 34 blank | 165 comment | 14 complexity | d60957359b16ad9b18f7ba4fb4ccbf67 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. // $Id: update_7.inc,v 1.30 2010/12/30 18:28:08 greg1anderson Exp $
  3. /**
  4. * @file
  5. * Update.php for provisioned sites.
  6. * This file is a derivative of the standard drupal update.php,
  7. * which has been modified to allow being run from the command
  8. * line.
  9. */
  10. /**
  11. * Global flag to identify update.php run, and so avoid various unwanted
  12. * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing
  13. * and translation, and solve some theming issues. This flag is checked on several
  14. * places in Drupal code (not just update.php).
  15. */
  16. define('MAINTENANCE_MODE', 'update');
  17. /**
  18. * Returns (and optionally stores) extra requirements that only apply during
  19. * particular parts of the update.php process.
  20. */
  21. function update_extra_requirements($requirements = NULL) {
  22. static $extra_requirements = array();
  23. if (isset($requirements)) {
  24. $extra_requirements += $requirements;
  25. }
  26. return $extra_requirements;
  27. }
  28. /**
  29. * Perform one update and store the results which will later be displayed on
  30. * the finished page.
  31. *
  32. * An update function can force the current and all later updates for this
  33. * module to abort by returning a $ret array with an element like:
  34. * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong');
  35. * The schema version will not be updated in this case, and all the
  36. * aborted updates will continue to appear on update.php as updates that
  37. * have not yet been run.
  38. *
  39. * @param $module
  40. * The module whose update will be run.
  41. * @param $number
  42. * The update number to run.
  43. * @param $context
  44. * The batch context array
  45. */
  46. function drush_update_do_one($module, $number, $dependency_map, &$context) {
  47. $function = $module . '_update_' . $number;
  48. // If this update was aborted in a previous step, or has a dependency that
  49. // was aborted in a previous step, go no further.
  50. if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
  51. return;
  52. }
  53. $context['log'] = FALSE;
  54. $ret = array();
  55. if (function_exists($function)) {
  56. try {
  57. if ($context['log']) {
  58. Database::startLog($function);
  59. }
  60. drush_log("Executing " . $function);
  61. $ret['results']['query'] = $function($context['sandbox']);
  62. $ret['results']['success'] = TRUE;
  63. }
  64. // @TODO We may want to do different error handling for different exception
  65. // types, but for now we'll just print the message.
  66. catch (Exception $e) {
  67. $ret['#abort'] = array('success' => FALSE, 'query' => $e->getMessage());
  68. drush_set_error('DRUPAL_EXCEPTION', $e->getMessage());
  69. }
  70. if ($context['log']) {
  71. $ret['queries'] = Database::getLog($function);
  72. }
  73. }
  74. if (isset($context['sandbox']['#finished'])) {
  75. $context['finished'] = $context['sandbox']['#finished'];
  76. unset($context['sandbox']['#finished']);
  77. }
  78. if (!isset($context['results'][$module])) {
  79. $context['results'][$module] = array();
  80. }
  81. if (!isset($context['results'][$module][$number])) {
  82. $context['results'][$module][$number] = array();
  83. }
  84. $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
  85. if (!empty($ret['#abort'])) {
  86. // Record this function in the list of updates that were aborted.
  87. $context['results']['#abort'][] = $function;
  88. }
  89. // Record the schema update if it was completed successfully.
  90. if ($context['finished'] == 1 && empty($ret['#abort'])) {
  91. drupal_set_installed_schema_version($module, $number);
  92. }
  93. $context['message'] = 'Updating ' . check_plain($module) . ' module';
  94. }
  95. /**
  96. * Check update requirements and report any errors.
  97. */
  98. function update_check_requirements() {
  99. $warnings = FALSE;
  100. // Check the system module and update.php requirements only.
  101. $requirements = system_requirements('update');
  102. $requirements += update_extra_requirements();
  103. // If there are issues, report them.
  104. foreach ($requirements as $requirement) {
  105. if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) {
  106. $message = isset($requirement['description']) ? $requirement['description'] : '';
  107. if (isset($requirement['value']) && $requirement['value']) {
  108. $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')';
  109. }
  110. $warnings = TRUE;
  111. drupal_set_message($message, 'warning');
  112. }
  113. }
  114. return $warnings;
  115. }
  116. function update_main_prepare() {
  117. // Some unavoidable errors happen because the database is not yet up-to-date.
  118. // Our custom error handler is not yet installed, so we just suppress them.
  119. drush_errors_off();
  120. // We prepare a minimal bootstrap for the update requirements check to avoid
  121. // reaching the PHP memory limit.
  122. require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
  123. require_once DRUPAL_ROOT . '/includes/update.inc';
  124. require_once DRUPAL_ROOT . '/includes/common.inc';
  125. require_once DRUPAL_ROOT . '/includes/file.inc';
  126. require_once DRUPAL_ROOT . '/includes/entity.inc';
  127. include_once DRUPAL_ROOT . '/includes/unicode.inc';
  128. update_prepare_d7_bootstrap();
  129. drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
  130. require_once DRUPAL_ROOT . '/includes/install.inc';
  131. require_once DRUPAL_ROOT . '/modules/system/system.install';
  132. // Load module basics.
  133. include_once DRUPAL_ROOT . '/includes/module.inc';
  134. $module_list['system']['filename'] = 'modules/system/system.module';
  135. module_list(TRUE, FALSE, FALSE, $module_list);
  136. drupal_load('module', 'system');
  137. // Reset the module_implements() cache so that any new hook implementations
  138. // in updated code are picked up.
  139. module_implements('', FALSE, TRUE);
  140. // Set up $language, since the installer components require it.
  141. drupal_language_initialize();
  142. // Set up theme system for the maintenance page.
  143. drupal_maintenance_theme();
  144. // Check the update requirements for Drupal.
  145. update_check_requirements();
  146. // update_fix_d7_requirements() needs to run before bootstrapping beyond path.
  147. // So bootstrap to DRUPAL_BOOTSTRAP_LANGUAGE then include unicode.inc.
  148. drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
  149. update_fix_d7_requirements();
  150. // Now proceed with a full bootstrap.
  151. drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL);
  152. drupal_maintenance_theme();
  153. drush_errors_on();
  154. include_once DRUPAL_ROOT . '/includes/batch.inc';
  155. drupal_load_updates();
  156. update_fix_compatibility();
  157. // Change query-strings on css/js files to enforce reload for all users.
  158. _drupal_flush_css_js();
  159. // Flush the cache of all data for the update status module.
  160. if (db_table_exists('cache_update')) {
  161. cache_clear_all('*', 'cache_update', TRUE);
  162. }
  163. module_list(TRUE, FALSE, TRUE);
  164. }
  165. function update_main() {
  166. update_main_prepare();
  167. $pending = update_get_update_list();
  168. $start = array();
  169. // Ensure system module's updates run first
  170. $start['system'] = array();
  171. // Print a list of pending updates for this module and get confirmation.
  172. if (sizeof($pending)) {
  173. drush_print(dt('The following updates are pending:'));
  174. drush_print();
  175. foreach ($pending as $module => $updates) {
  176. if (isset($updates['start'])) {
  177. drush_print($module . ' module : ');
  178. if (isset($updates['start'])) {
  179. $start[$module] = $updates['start'];
  180. foreach ($updates['pending'] as $update) {
  181. drush_print($update, 2);
  182. }
  183. }
  184. drush_print();
  185. }
  186. }
  187. if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
  188. return drush_user_abort();
  189. }
  190. drush_update_batch($start);
  191. }
  192. else {
  193. drush_log(dt("No database updates required"), 'success');
  194. }
  195. }
  196. function _update_batch_command($id) {
  197. update_main_prepare();
  198. drush_batch_command($id);
  199. }
  200. /**
  201. * Start the database update batch process.
  202. *
  203. * @param $start
  204. * An array of all the modules and which update to start at.
  205. * @param $redirect
  206. * Path to redirect to when the batch has finished processing.
  207. * @param $url
  208. * URL of the batch processing page (should only be used for separate
  209. * scripts like update.php).
  210. * @param $batch
  211. * Optional parameters to pass into the batch API.
  212. * @param $redirect_callback
  213. * (optional) Specify a function to be called to redirect to the progressive
  214. * processing page.
  215. */
  216. function drush_update_batch($start) {
  217. // Resolve any update dependencies to determine the actual updates that will
  218. // be run and the order they will be run in.
  219. $updates = update_resolve_dependencies($start);
  220. // Store the dependencies for each update function in an array which the
  221. // batch API can pass in to the batch operation each time it is called. (We
  222. // do not store the entire update dependency array here because it is
  223. // potentially very large.)
  224. $dependency_map = array();
  225. foreach ($updates as $function => $update) {
  226. $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
  227. }
  228. $operations = array();
  229. foreach ($updates as $update) {
  230. if ($update['allowed']) {
  231. // Set the installed version of each module so updates will start at the
  232. // correct place. (The updates are already sorted, so we can simply base
  233. // this on the first one we come across in the above foreach loop.)
  234. if (isset($start[$update['module']])) {
  235. drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
  236. unset($start[$update['module']]);
  237. }
  238. // Add this update function to the batch.
  239. $function = $update['module'] . '_update_' . $update['number'];
  240. $operations[] = array('drush_update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
  241. }
  242. }
  243. $batch['operations'] = $operations;
  244. $batch += array(
  245. 'title' => 'Updating',
  246. 'init_message' => 'Starting updates',
  247. 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.',
  248. 'finished' => 'drush_update_finished',
  249. 'file' => 'includes/update.inc',
  250. );
  251. batch_set($batch);
  252. drush_backend_batch_process('updatedb-batch-process');
  253. }
  254. function drush_update_finished($success, $results, $operations) {
  255. // Nothing to do here. All caches already cleared. Kept as documentation of 'finished' callback.
  256. }