Unverified Commit 8a1fbabe authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3098475 by Berdir, catch, TravisCarden, xjm, benjifisher, alexpott,...

Issue #3098475 by Berdir, catch, TravisCarden, xjm, benjifisher, alexpott, larowlan, webchick, dww: Add more strict checking of hook_update_last_removed() and better explanation
parent 857f4dc5
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -327,14 +327,6 @@ function update_get_update_list() {
    // Otherwise, get the list of updates defined by this module.
    $updates = drupal_get_schema_versions($module);
    if ($updates !== FALSE) {
      // \Drupal::moduleHandler()->invoke() returns NULL for non-existing hooks,
      // so if no updates are removed, it will == 0.
      $last_removed = \Drupal::moduleHandler()->invoke($module, 'update_last_removed');
      if ($schema_version < $last_removed) {
        $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="https://www.drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.';
        continue;
      }

      foreach ($updates as $update) {
        if ($update == \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
          $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. It contains an update numbered as ' . \Drupal::CORE_MINIMUM_SCHEMA_VERSION . ' which is reserved for the earliest installation of a module in Drupal ' . \Drupal::CORE_COMPATIBILITY . ', before any updates. In order to update <em>' . $module . '</em> module, you will need to install a version of the module with valid updates.';
+34 −0
Original line number Diff line number Diff line
@@ -1309,6 +1309,40 @@ function system_requirements($phase) {
    }
  }

  // Ensure that no module has a current schema version that is lower than the
  // one that was last removed.
  if ($phase == 'update') {
    $module_handler = \Drupal::moduleHandler();
    $module_list = [];
    foreach ($module_handler->getImplementations('update_last_removed') as $module) {
      $last_removed = $module_handler->invoke($module, 'update_last_removed');
      if ($last_removed && $last_removed > drupal_get_installed_schema_version($module)) {

        /** @var \Drupal\Core\Extension\Extension $module_info */
        $module_info = \Drupal::service('extension.list.module')->get($module);
        $module_list[$module_info->info['package']][$module] = [
          'info' => $module_info,
          'last_removed' => $last_removed,
          'installed_version' => drupal_get_installed_schema_version($module),
        ];
      }
    }

    foreach ($module_list as $package => $package_modules) {
      foreach ($package_modules as $module => $data) {
        $requirements[$module . '_update_last_removed'] = [
          'title' => t('Unsupported schema version: @module', ['@module' => $data['info']->info['name']]),
          'description' => t('The installed version of the %module module is too old to update. Update to an intermediate version first (last removed version: @last_removed_version, installed version: @installed_version).', [
            '%module' => $data['info']->info['name'],
            '@last_removed_version' => $data['last_removed'],
            '@installed_version' => $data['installed_version'],
          ]),
          'severity' => REQUIREMENT_ERROR,
        ];
      }
    }
  }

  return $requirements;
}

+5 −0
Original line number Diff line number Diff line
name: 'Update test with hook_update_last_removed() implementation'
type: module
description: 'Support module for update testing.'
package: Testing
version: VERSION
+20 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Update functions for the update_test_invalid_hook module.
 */

/**
 * Implements hook_update_last_removed().
 */
function update_test_last_removed_update_last_removed() {
  return 8002;
}

/**
 * Dummy update function to run during the tests.
 */
function update_test_last_removed_update_8003() {
  return 'The update_test_last_removed_update_8003() update was executed successfully.';
}
+72 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\system\Functional\UpdateSystem;

use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UpdatePathTestTrait;

/**
 * Modules can define their last removed update function.
 *
 * @group system
 */
class UpdatePathLastRemovedTest extends BrowserTestBase {
  use UpdatePathTestTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['update_test_last_removed'];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * URL for the upgrade script.
   *
   * @var string
   */
  protected $updateUrl;

  /**
   * A user account with upgrade permission.
   *
   * @var \Drupal\user\UserInterface
   */
  protected $updateUser;

  protected function setUp() {
    parent::setUp();
    require_once $this->root . '/core/includes/update.inc';

    $this->updateUrl = Url::fromRoute('system.db_update');
    $this->updateUser = $this->drupalCreateUser(['administer software updates']);
  }

  /**
   * Tests that a module with a too old schema version can not be updated.
   */
  public function testLastRemovedVersion() {
    drupal_set_installed_schema_version('update_test_last_removed', 8000);

    // Access the update page with a schema version that is too old for the test
    // module.
    $this->drupalLogin($this->updateUser);
    $this->drupalGet($this->updateUrl);
    $assert_session = $this->assertSession();
    $assert_session->pageTextContains('Requirements problem');
    $assert_session->pageTextContains('Unsupported schema version: Update test with hook_update_last_removed() implementation');
    $assert_session->pageTextContains('The installed version of the Update test with hook_update_last_removed() implementation module is too old to update. Update to an intermediate version first (last removed version: 8002, installed version: 8000).');
    $assert_session->linkNotExists('Continue');

    // Set the expected schema version, updates are successful now.
    drupal_set_installed_schema_version('update_test_last_removed', 8002);

    $this->runUpdates();
    $this->assertEquals(8003, drupal_get_installed_schema_version('update_test_last_removed'));
  }

}
Loading