Patched to Drupal 8.4.8 level. See https://www.drupal.org/sa-core-2018-004 and patch...
[yaffs-website] / vendor / drush / drush / commands / core / drupal / update_7.inc
1 <?php
2 /**
3  * @file
4  *   Update.php for provisioned sites.
5  *   This file is a derivative of the standard drupal update.php,
6  *   which has been modified to allow being run from the command
7  *   line.
8  */
9
10 use Drush\Log\LogLevel;
11
12 /**
13  * Global flag to identify update.php run, and so avoid various unwanted
14  * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing
15  * and translation, and solve some theming issues. This flag is checked on several
16  * places in Drupal code (not just update.php).
17  */
18 define('MAINTENANCE_MODE', 'update');
19
20 /**
21  * Drupal's update.inc has functions that are in previous update_X.inc files
22  * for example, update_check_incompatibility() which can prove useful when
23  * enabling modules.
24  */
25 require_once DRUSH_DRUPAL_CORE . '/includes/update.inc';
26 /**
27  * Returns (and optionally stores) extra requirements that only apply during
28  * particular parts of the update.php process.
29  */
30 function update_extra_requirements($requirements = NULL) {
31   static $extra_requirements = array();
32   if (isset($requirements)) {
33     $extra_requirements += $requirements;
34   }
35   return $extra_requirements;
36 }
37
38 /**
39  * Perform one update and store the results which will later be displayed on
40  * the finished page.
41  *
42  * An update function can force the current and all later updates for this
43  * module to abort by returning a $ret array with an element like:
44  * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong');
45  * The schema version will not be updated in this case, and all the
46  * aborted updates will continue to appear on update.php as updates that
47  * have not yet been run.
48  *
49  * @param $module
50  *   The module whose update will be run.
51  * @param $number
52  *   The update number to run.
53  * @param $context
54  *   The batch context array
55  */
56 function drush_update_do_one($module, $number, $dependency_map,  &$context) {
57   $function = $module . '_update_' . $number;
58
59   // If this update was aborted in a previous step, or has a dependency that
60   // was aborted in a previous step, go no further.
61   if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
62     return;
63   }
64
65   $context['log'] = FALSE;
66
67   $ret = array();
68   if (function_exists($function)) {
69     try {
70       if ($context['log']) {
71         Database::startLog($function);
72       }
73
74       drush_log("Executing " . $function);
75       $ret['results']['query'] = $function($context['sandbox']);
76
77       // If the update hook returned a status message (common in batch updates),
78       // show it to the user.
79       if ($ret['results']['query']) {
80         drush_log($ret['results']['query'], LogLevel::OK);
81       }
82
83       $ret['results']['success'] = TRUE;
84     }
85     // @TODO We may want to do different error handling for different exception
86     // types, but for now we'll just print the message.
87     catch (Exception $e) {
88       $ret['#abort'] = array('success' => FALSE, 'query' => $e->getMessage());
89       drush_set_error('DRUPAL_EXCEPTION', $e->getMessage());
90     }
91
92     if ($context['log']) {
93       $ret['queries'] = Database::getLog($function);
94     }
95   }
96
97   if (isset($context['sandbox']['#finished'])) {
98     $context['finished'] = $context['sandbox']['#finished'];
99     unset($context['sandbox']['#finished']);
100   }
101
102   if (!isset($context['results'][$module])) {
103     $context['results'][$module] = array();
104   }
105   if (!isset($context['results'][$module][$number])) {
106     $context['results'][$module][$number] = array();
107   }
108   $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
109
110   if (!empty($ret['#abort'])) {
111     // Record this function in the list of updates that were aborted.
112     $context['results']['#abort'][] = $function;
113   }
114
115   // Record the schema update if it was completed successfully.
116   if ($context['finished'] == 1 && empty($ret['#abort'])) {
117     drupal_set_installed_schema_version($module, $number);
118   }
119
120   $context['message'] = 'Performed update: ' . $function;
121 }
122
123 /**
124  * Check update requirements and report any errors.
125  */
126 function update_check_requirements() {
127   $warnings = FALSE;
128
129   // Check the system module and update.php requirements only.
130   $requirements = system_requirements('update');
131   $requirements += update_extra_requirements();
132
133   // If there are issues, report them.
134   foreach ($requirements as $requirement) {
135     if (isset($requirement['severity']) && $requirement['severity'] > REQUIREMENT_OK) {
136       $message = isset($requirement['description']) ? $requirement['description'] : '';
137       if (isset($requirement['value']) && $requirement['value']) {
138         $message .= ' (Currently using ' . $requirement['title'] . ' ' . $requirement['value'] . ')';
139       }
140       $warnings = TRUE;
141       drupal_set_message($message, LogLevel::WARNING);
142     }
143   }
144   return $warnings;
145 }
146
147
148 function update_main_prepare() {
149   // Some unavoidable errors happen because the database is not yet up-to-date.
150   // Our custom error handler is not yet installed, so we just suppress them.
151   drush_errors_off();
152
153   // We prepare a minimal bootstrap for the update requirements check to avoid
154   // reaching the PHP memory limit.
155   $core = DRUSH_DRUPAL_CORE;
156   require_once $core . '/includes/bootstrap.inc';
157   require_once $core . '/includes/common.inc';
158   require_once $core . '/includes/file.inc';
159   require_once $core . '/includes/entity.inc';
160   include_once $core . '/includes/unicode.inc';
161
162   update_prepare_d7_bootstrap();
163   drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
164
165   require_once $core . '/includes/install.inc';
166   require_once $core . '/modules/system/system.install';
167
168   // Load module basics.
169   include_once $core . '/includes/module.inc';
170   $module_list['system']['filename'] = 'modules/system/system.module';
171   module_list(TRUE, FALSE, FALSE, $module_list);
172   drupal_load('module', 'system');
173
174   // Reset the module_implements() cache so that any new hook implementations
175   // in updated code are picked up.
176   module_implements('', FALSE, TRUE);
177
178   // Set up $language, since the installer components require it.
179   drupal_language_initialize();
180
181   // Set up theme system for the maintenance page.
182   drupal_maintenance_theme();
183
184   // Check the update requirements for Drupal.
185   update_check_requirements();
186
187   // update_fix_d7_requirements() needs to run before bootstrapping beyond path.
188   // So bootstrap to DRUPAL_BOOTSTRAP_LANGUAGE then include unicode.inc.
189   drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
190
191   update_fix_d7_requirements();
192
193   // Clear the module_implements() cache before the full bootstrap. The calls
194   // above to drupal_maintenance_theme() and update_check_requirements() have
195   // invoked hooks before all modules have actually been loaded by the full
196   // bootstrap. This means that the module_implements() results for any hooks
197   // that have been invoked, including hook_module_implements_alter(), is a
198   // smaller set of modules than should be returned normally.
199   // @see https://github.com/drush-ops/drush/pull/399
200   module_implements('', FALSE, TRUE);
201
202   // Now proceed with a full bootstrap.
203
204   drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL);
205   drupal_maintenance_theme();
206
207   drush_errors_on();
208
209   include_once DRUPAL_ROOT . '/includes/batch.inc';
210   drupal_load_updates();
211
212   update_fix_compatibility();
213
214    // Change query-strings on css/js files to enforce reload for all users.
215   _drupal_flush_css_js();
216   // Flush the cache of all data for the update status module.
217   if (db_table_exists('cache_update')) {
218     cache_clear_all('*', 'cache_update', TRUE);
219   }
220
221   module_list(TRUE, FALSE, TRUE);
222 }
223
224 function update_main() {
225   update_main_prepare();
226
227   list($pending, $start) = updatedb_status();
228   if ($pending) {
229     // @todo get table header working.
230     // $headers = array(dt('Module'), dt('ID'), dt('Description'));
231     drush_print_table($pending);
232     if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
233       return drush_user_abort();
234     }
235     drush_update_batch($start);
236   }
237   else {
238     drush_log(dt("No database updates required"), LogLevel::SUCCESS);
239   }
240
241   return count($pending);
242 }
243
244 function _update_batch_command($id) {
245   update_main_prepare();
246   drush_batch_command($id);
247 }
248
249 /**
250  * Start the database update batch process.
251  *
252  * @param $start
253  *   An array of all the modules and which update to start at.
254  * @param $redirect
255  *   Path to redirect to when the batch has finished processing.
256  * @param $url
257  *   URL of the batch processing page (should only be used for separate
258  *   scripts like update.php).
259  * @param $batch
260  *   Optional parameters to pass into the batch API.
261  * @param $redirect_callback
262  *   (optional) Specify a function to be called to redirect to the progressive
263  *   processing page.
264  */
265 function drush_update_batch($start) {
266   // Resolve any update dependencies to determine the actual updates that will
267   // be run and the order they will be run in.
268   $updates = update_resolve_dependencies($start);
269
270   // Store the dependencies for each update function in an array which the
271   // batch API can pass in to the batch operation each time it is called. (We
272   // do not store the entire update dependency array here because it is
273   // potentially very large.)
274   $dependency_map = array();
275   foreach ($updates as $function => $update) {
276     $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
277   }
278
279   $operations = array();
280   foreach ($updates as $update) {
281     if ($update['allowed']) {
282       // Set the installed version of each module so updates will start at the
283       // correct place. (The updates are already sorted, so we can simply base
284       // this on the first one we come across in the above foreach loop.)
285       if (isset($start[$update['module']])) {
286         drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
287         unset($start[$update['module']]);
288       }
289       // Add this update function to the batch.
290       $function = $update['module'] . '_update_' . $update['number'];
291       $operations[] = array('drush_update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
292     }
293   }
294
295   $batch['operations'] = $operations;
296   $batch += array(
297     'title' => 'Updating',
298     'init_message' => 'Starting updates',
299     '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.',
300     'finished' => 'drush_update_finished',
301     'file' => 'includes/update.inc',
302   );
303   batch_set($batch);
304   drush_backend_batch_process('updatedb-batch-process');
305 }
306
307
308
309 function drush_update_finished($success, $results, $operations) {
310   // Nothing to do here. All caches already cleared. Kept as documentation of 'finished' callback.
311 }
312
313 /**
314  * Return a 2 item array with
315  *  - an array where each item is a 3 item associative array describing a pending update.
316  *  - an array listing the first update to run, keyed by module.
317  */
318 function updatedb_status() {
319   $pending = update_get_update_list();
320
321   $return = array();
322   // Ensure system module's updates run first.
323   $start['system'] = array();
324
325   // Print a list of pending updates for this module and get confirmation.
326   foreach ($pending as $module => $updates) {
327     if (isset($updates['start']))  {
328       foreach ($updates['pending'] as $update_id => $description) {
329         // Strip cruft from front.
330         $description = str_replace($update_id . ' -   ', '', $description);
331         $return[] = array('module' => ucfirst($module), 'update_id' => $update_id, 'description' => $description);
332       }
333       if (isset($updates['start'])) {
334         $start[$module] = $updates['start'];
335       }
336     }
337   }
338   return array($return, $start);
339 }