Commit 1f577875 authored by catch's avatar catch
Browse files

Issue #3066801 by catch, WidgetsBurritos, alexpott, pobster, jungle, tedbow,...

Issue #3066801 by catch, WidgetsBurritos, alexpott, pobster, jungle, tedbow, tim.plunkett, xjm, dww, benjifisher, webchick, longwave, worldlinemine, Berdir, lauriii
parent eecb5a21
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -295,10 +295,13 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
        }
        drupal_set_installed_schema_version($module, $version);

        // Ensure that all post_update functions are registered already.
        // Ensure that all post_update functions are registered already. This
        // should include existing post-updates, as well as any specified as
        // having been previously removed, to ensure that newly installed and
        // updated sites have the same entries in the registry.
        /** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
        $post_update_registry = \Drupal::service('update.post_update_registry');
        $post_update_registry->registerInvokedUpdates($post_update_registry->getModuleUpdateFunctions($module));
        $post_update_registry->registerInvokedUpdates(array_merge($post_update_registry->getModuleUpdateFunctions($module), array_keys($post_update_registry->getRemovedPostUpdates($module))));

        // Record the fact that it was installed.
        $modules_installed[] = $module;
+25 −0
Original line number Diff line number Diff line
@@ -714,6 +714,7 @@ function hook_update_N(&$sandbox) {
 * @ingroup update_api
 *
 * @see hook_update_N()
 * @see hook_removed_post_updates()
 */
function hook_post_update_NAME(&$sandbox) {
  // Example of updating some content.
@@ -747,6 +748,30 @@ function hook_post_update_NAME(&$sandbox) {
  return $result;
}

/**
 * Return an array of removed hook_post_update_NAME() function names.
 *
 * This should be used to indicate post-update functions that have existed in
 * some previous version of the module, but are no longer available.
 *
 * This implementation has to be placed in a MODULE.post_update.php file.
 *
 * @return string[]
 *   An array where the keys are removed post-update function names, and the
 *   values are the first stable version in which the update was removed.
 *
 * @ingroup update_api
 *
 * @see hook_post_update_NAME()
 */
function hook_removed_post_updates() {
  return [
    'mymodule_post_update_foo' => '8.x-2.0',
    'mymodule_post_update_bar' => '8.x-3.0',
    'mymodule_post_update_baz' => '8.x-3.0',
  ];
}

/**
 * Return an array of information about module update dependencies.
 *
+12 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Core\Update;

/**
 * An exception thrown for removed post-update functions.
 *
 * Occurs when a module defines hook_post_update_NAME() implementations
 * that are listed as removed in hook_removed_post_updates().
 */
class RemovedPostUpdateNameException extends \LogicException {
}
+23 −2
Original line number Diff line number Diff line
@@ -86,6 +86,21 @@ public function __construct($root, $site_path, array $enabled_modules, KeyValueS
    $this->includeTests = $include_tests;
  }

  /**
   * Gets removed hook_post_update_NAME() implementations for a module.
   *
   * @return string[]
   *   A list of post-update functions that have been removed.
   */
  public function getRemovedPostUpdates($module) {
    $this->scanExtensionsAndLoadUpdateFiles();
    $function = "{$module}_removed_post_updates";
    if (function_exists($function)) {
      return $function();
    }
    return [];
  }

  /**
   * Gets all available update functions.
   *
@@ -102,11 +117,17 @@ protected function getAvailableUpdateFunctions() {
      // module updates.
      if (preg_match($regexp, $function, $matches)) {
        if (in_array($matches['module'], $this->enabledModules)) {
          $updates[] = $matches['module'] . '_' . $this->updateType . '_' . $matches['name'];
          $function_name = $matches['module'] . '_' . $this->updateType . '_' . $matches['name'];
          if ($this->updateType === 'post_update') {
            $removed = array_keys($this->getRemovedPostUpdates($matches['module']));
            if (array_search($function_name, $removed) !== FALSE) {
              throw new RemovedPostUpdateNameException(sprintf('The following update is specified as removed in hook_removed_post_updates() but still exists in the code base: %s', $function_name));
            }
          }
          $updates[] = $function_name;
        }
      }
    }

    // Ensure that the update order is deterministic.
    sort($updates);
    return $updates;
+30 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PrivateStream;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\Request;
@@ -1266,6 +1267,35 @@ function system_requirements($phase) {
    }
  }

  // Check all the expected post-updates have been run.
  if ($phase === 'update') {
    $existing_updates = \Drupal::service('keyvalue')->get('post_update')->get('existing_updates', []);
    $post_update_registry = \Drupal::service('update.post_update_registry');
    $modules = \Drupal::moduleHandler()->getModuleList();
    $module_extension_list = \Drupal::service('extension.list.module');
    foreach ($modules as $module => $extension) {
      $module_info = $module_extension_list->get($module);
      $removed_post_updates = $post_update_registry->getRemovedPostUpdates($module);
      if ($missing_updates = array_diff(array_keys($removed_post_updates), $existing_updates)) {
        $versions = array_unique(array_intersect_key($removed_post_updates, array_flip($missing_updates)));
        $description = new PluralTranslatableMarkup(count($versions),
          'The installed version of the %module module is too old to update. Update to a version prior to @versions first (missing updates: @missing_updates).',
          'The installed version of the %module module is too old to update. Update first to a version prior to all of the following: @versions (missing updates: @missing_updates).',
          [
            '%module' => $module_info->info['name'],
            '@missing_updates' => implode(', ', $missing_updates),
            '@versions' => implode(', ', $versions),
          ]
        );
        $requirements[$module . '_post_update_removed'] = [
          'title' => t('Missing updates for: @module', ['@module' => $module_info->info['name']]),
          'description' => $description,
          'severity' => REQUIREMENT_ERROR,
        ];
      }
    }
  }

  return $requirements;
}

Loading