Verified Commit 63740f3d authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3410938 by kim.pepper, dww, nicxvan, smustgrave, andypost, dpi,...

Issue #3410938 by kim.pepper, dww, nicxvan, smustgrave, andypost, dpi, quietone: Create enums for RequirementSeverity and deprecate drupal_requirements_severity() constants
parent 0bae16bb
Loading
Loading
Loading
Loading
Loading
+18 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Installer\Exception\AlreadyInstalledException;
@@ -2018,7 +2019,7 @@ function install_check_translations($langcode, $server_pattern): array {
    $requirements['translations directory exists'] = [
      'title'       => t('Translations directory'),
      'value'       => t('The translations directory does not exist.'),
      'severity'    => REQUIREMENT_ERROR,
      'severity'    => RequirementSeverity::Error,
      'description' => t('The installer requires that you create a translations directory as part of the installation process. Create the directory %translations_directory . More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>.', ['%translations_directory' => $translations_directory, ':install_txt' => base_path() . 'core/INSTALL.txt']),
    ];
  }
@@ -2032,7 +2033,7 @@ function install_check_translations($langcode, $server_pattern): array {
      $requirements['translations directory readable'] = [
        'title'       => t('Translations directory'),
        'value'       => t('The translations directory is not readable.'),
        'severity'    => REQUIREMENT_ERROR,
        'severity'    => RequirementSeverity::Error,
        'description' => t('The installer requires read permissions to %translations_directory at all times. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', ['%translations_directory' => $translations_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']),
      ];
    }
@@ -2041,7 +2042,7 @@ function install_check_translations($langcode, $server_pattern): array {
      $requirements['translations directory writable'] = [
        'title'       => t('Translations directory'),
        'value'       => t('The translations directory is not writable.'),
        'severity'    => REQUIREMENT_ERROR,
        'severity'    => RequirementSeverity::Error,
        'description' => t('The installer requires write permissions to %translations_directory during the installation process. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', ['%translations_directory' => $translations_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']),
      ];
    }
@@ -2058,7 +2059,7 @@ function install_check_translations($langcode, $server_pattern): array {
    $requirements['online'] = [
      'title'       => t('Internet'),
      'value'       => t('The translation server is offline.'),
      'severity'    => REQUIREMENT_ERROR,
      'severity'    => RequirementSeverity::Error,
      'description' => t('The installer requires to contact the translation server to download a translation file. Check your internet connection and verify that your website can reach the translation server at <a href=":server_url">@server_url</a>.', [':server_url' => $server_url, '@server_url' => $server_url]),
    ];
  }
@@ -2073,7 +2074,7 @@ function install_check_translations($langcode, $server_pattern): array {
      $requirements['translation available'] = [
        'title'       => t('Translation'),
        'value'       => t('The %language translation is not available.', ['%language' => $language]),
        'severity'    => REQUIREMENT_ERROR,
        'severity'    => RequirementSeverity::Error,
        'description' => t('The %language translation file is not available at the translation server. <a href=":url">Choose a different language</a> or select English and translate your website later.', ['%language' => $language, ':url' => $_SERVER['SCRIPT_NAME']]),
      ];
    }
@@ -2092,7 +2093,7 @@ function install_check_translations($langcode, $server_pattern): array {
      $requirements['translation downloaded'] = [
        'title'       => t('Translation'),
        'value'       => t('The %language translation could not be downloaded.', ['%language' => $language]),
        'severity'    => REQUIREMENT_ERROR,
        'severity'    => RequirementSeverity::Error,
        'description' => t('The %language translation file could not be downloaded. <a href=":url">Choose a different language</a> or select English and translate your website later.', ['%language' => $language, ':url' => $_SERVER['SCRIPT_NAME']]),
      ];
    }
@@ -2152,7 +2153,7 @@ function install_check_requirements($install_state) {
      $requirements["default $file file exists"] = [
        'title' => $default_file_info['title_default'],
        'value' => $default_file_info['description_default'],
        'severity' => REQUIREMENT_ERROR,
        'severity' => RequirementSeverity::Error,
        'description' => t('The @drupal installer requires that the %default-file file must not be deleted or modified from the original download.', [
          '@drupal' => drupal_install_profile_distribution_name(),
          '%default-file' => $default_file,
@@ -2211,7 +2212,7 @@ function install_check_requirements($install_state) {
      $requirements["$file file exists"] = [
        'title' => $default_file_info['title'],
        'value' => t('The %file does not exist.', ['%file' => $default_file_info['title']]),
        'severity' => REQUIREMENT_ERROR,
        'severity' => RequirementSeverity::Error,
        'description' => t('The @drupal installer requires that you create a %file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>.', [
          '@drupal' => drupal_install_profile_distribution_name(),
          '%file' => $file,
@@ -2230,7 +2231,7 @@ function install_check_requirements($install_state) {
        $requirements["$file file readable"] = [
          'title' => $default_file_info['title'],
          'value' => t('The %file is not readable.', ['%file' => $default_file_info['title']]),
          'severity' => REQUIREMENT_ERROR,
          'severity' => RequirementSeverity::Error,
          'description' => t('@drupal requires read permissions to %file at all times. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
            '@drupal' => drupal_install_profile_distribution_name(),
            '%file' => $file,
@@ -2243,7 +2244,7 @@ function install_check_requirements($install_state) {
        $requirements["$file file writable"] = [
          'title' => $default_file_info['title'],
          'value' => t('The %file is not writable.', ['%file' => $default_file_info['title']]),
          'severity' => REQUIREMENT_ERROR,
          'severity' => RequirementSeverity::Error,
          'description' => t('The @drupal installer requires write permissions to %file during the installation process. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
            '@drupal' => drupal_install_profile_distribution_name(),
            '%file' => $file,
@@ -2261,7 +2262,7 @@ function install_check_requirements($install_state) {
        $requirements["$file file ownership"] = [
          'title' => $default_file_info['title'],
          'value' => t('The @file is owned by the web server.', ['@file' => $default_file_info['title']]),
          'severity' => REQUIREMENT_ERROR,
          'severity' => RequirementSeverity::Error,
          'description' => t('The @drupal installer failed to create a %file file with proper file ownership. Log on to your web server, remove the existing %file file, and create a new one by copying the %default_file file to %file. More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
            '@drupal' => drupal_install_profile_distribution_name(),
            '%file' => $file,
@@ -2287,7 +2288,7 @@ function install_check_requirements($install_state) {
        $requirements['database_install_errors'] = [
          'title' => t('Database settings'),
          'description' => $error_message,
          'severity' => REQUIREMENT_ERROR,
          'severity' => RequirementSeverity::Error,
        ];
      }
    }
@@ -2315,18 +2316,18 @@ function install_check_requirements($install_state) {
 */
function install_display_requirements($install_state, $requirements) {
  // Check the severity of the requirements reported.
  $severity = drupal_requirements_severity($requirements);
  $severity = RequirementSeverity::maxSeverityFromRequirements($requirements);

  // If there are errors, always display them. If there are only warnings, skip
  // them if the user has provided a URL parameter acknowledging the warnings
  // and indicating a desire to continue anyway. See drupal_requirements_url().
  if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
  if ($severity === RequirementSeverity::Error || ($severity === RequirementSeverity::Warning && empty($install_state['parameters']['continue']))) {
    if ($install_state['interactive']) {
      $build['report']['#type'] = 'status_report';
      $build['report']['#requirements'] = $requirements;
      if ($severity == REQUIREMENT_WARNING) {
      if ($severity == RequirementSeverity::Warning) {
        $build['#title'] = t('Requirements review');
        $build['#suffix'] = t('Check the messages and <a href=":retry">retry</a>, or you may choose to <a href=":cont">continue anyway</a>.', [':retry' => drupal_requirements_url(REQUIREMENT_ERROR), ':cont' => drupal_requirements_url($severity)]);
        $build['#suffix'] = t('Check the messages and <a href=":retry">retry</a>, or you may choose to <a href=":cont">continue anyway</a>.', [':retry' => drupal_requirements_url(RequirementSeverity::Error), ':cont' => drupal_requirements_url($severity)]);
      }
      else {
        $build['#title'] = t('Requirements problem');
@@ -2341,7 +2342,7 @@ function install_display_requirements($install_state, $requirements) {
        // Skip warnings altogether for non-interactive installations; these
        // proceed in a single request so there is no good opportunity (and no
        // good method) to warn the user anyway.
        if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
        if (isset($requirement['severity']) && $requirement['severity'] === RequirementSeverity::Error) {
          $render_array = [
            '#type' => 'inline_template',
            '#template' => '{{ title }}:{{ value }}<br /><br />{{ description }}',
+42 −13
Original line number Diff line number Diff line
@@ -12,26 +12,47 @@
use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Extension\InstallRequirementsInterface;
use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Installer\InstallerKernel;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Requirement severity -- Informational message only.
 *
 * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
 * \Drupal\Core\Extension\Requirement\RequirementSeverity::Info instead.
 *
 * @see https://www.drupal.org/node/3410939
 */
const REQUIREMENT_INFO = -1;

/**
 * Requirement severity -- Requirement successfully met.
 *
 * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
 *   \Drupal\Core\Extension\Requirement\RequirementSeverity::OK instead.
 *
 * @see https://www.drupal.org/node/3410939
 */
const REQUIREMENT_OK = 0;

/**
 * Requirement severity -- Warning condition; proceed but flag warning.
 *
 * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
 * \Drupal\Core\Extension\Requirement\RequirementSeverity::Warning instead.
 *
 * @see https://www.drupal.org/node/3410939
 */
const REQUIREMENT_WARNING = 1;

/**
 * Requirement severity -- Error condition; abort installation.
 *
 * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
 * \Drupal\Core\Extension\Requirement\RequirementSeverity::Error instead.
 *
 * @see https://www.drupal.org/node/3410939
 */
const REQUIREMENT_ERROR = 2;

@@ -190,7 +211,7 @@ function drupal_verify_profile($install_state): array {
    $requirements['required_modules'] = [
      'title' => t('Required modules'),
      'value' => t('Required modules not found.'),
      'severity' => REQUIREMENT_ERROR,
      'severity' => RequirementSeverity::Error,
      'description' => t('The following modules are required but were not found. Move them into the appropriate modules subdirectory, such as <em>/modules</em>. Missing modules: @modules', ['@modules' => $modules_list]),
    ];
  }
@@ -560,7 +581,7 @@ function drupal_current_script_url($query = []) {
 * update.php) and returns a URL that can be used to attempt to proceed to the
 * next step of the script.
 *
 * @param int $severity
 * @param int|\Drupal\Core\Extension\Requirement\RequirementSeverity $severity
 *   The severity of the requirements problem, as returned by
 *   drupal_requirements_severity().
 *
@@ -573,11 +594,18 @@ function drupal_current_script_url($query = []) {
 * @see drupal_current_script_url()
 * @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
 */
function drupal_requirements_url($severity) {
function drupal_requirements_url(/* int|RequirementSeverity */ $severity): string {
  if (!$severity instanceof RequirementSeverity) {
    @trigger_error('Passing a type other than ' . RequirementSeverity::class . ' to ' . __FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Pass a ' . RequirementSeverity::class . ' enum instead. See https://www.drupal.org/node/3410939', E_USER_DEPRECATED);
    $severity = RequirementSeverity::from($severity);
  }
  if (is_null($severity)) {
    $severity = RequirementSeverity::Info;
  }
  $query = [];
  // If there are no errors, only warnings, append 'continue=1' to the URL so
  // the user can bypass this screen on the next page load.
  if ($severity == REQUIREMENT_WARNING) {
  if ($severity === RequirementSeverity::Warning) {
    $query['continue'] = 1;
  }
  return drupal_current_script_url($query);
@@ -644,15 +672,16 @@ function drupal_check_profile($profile): array {
 *
 * @return int
 *   The highest severity in the array.
 *
 * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
 *   \Drupal\Core\Extension\Requirement\RequirementSeverity::getMaxSeverity()
 *   instead.
 *
 * @see https://www.drupal.org/node/3410939
 */
function drupal_requirements_severity(&$requirements) {
  $severity = REQUIREMENT_OK;
  foreach ($requirements as $requirement) {
    if (isset($requirement['severity'])) {
      $severity = max($severity, $requirement['severity']);
    }
  }
  return $severity;
  @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use ' . RequirementSeverity::class . '::maxSeverityFromRequirements() instead. See https://www.drupal.org/node/3410939', E_USER_DEPRECATED);
  return RequirementSeverity::maxSeverityFromRequirements($requirements)->value;
}

/**
@@ -676,10 +705,10 @@ function drupal_check_module($module) {
  $requirements = \Drupal::moduleHandler()->invoke($module, 'requirements', ['install']) ?? [];
  $requirements = array_merge($requirements, install_check_class_requirements($extension));

  if (!empty($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
  if (!empty($requirements) && RequirementSeverity::maxSeverityFromRequirements($requirements) === RequirementSeverity::Error) {
    // Print any error messages
    foreach ($requirements as $requirement) {
      if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
      if (isset($requirement['severity']) && $requirement['severity'] === RequirementSeverity::Error) {
        $message = $requirement['description'];
        if (isset($requirement['value']) && $requirement['value']) {
          $message = t('@requirements_message (Currently using @item version @version)', ['@requirements_message' => $requirement['description'], '@item' => $requirement['title'], '@version' => $requirement['value']]);
+2 −1
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@

use Drupal\Component\Graph\Graph;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Utility\Error;
use Drupal\Core\Update\EquivalentUpdate;

@@ -34,7 +35,7 @@ function update_system_schema_requirements(): array {
  else {
    $requirements['minimum schema'] += [
      'value' => 'The installed schema version does not meet the minimum.',
      'severity' => REQUIREMENT_ERROR,
      'severity' => RequirementSeverity::Error,
      'description' => 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must upgrade your site to Drupal 8 first, see https://www.drupal.org/docs/8/upgrade.',
    ];
  }
+5 −8
Original line number Diff line number Diff line
@@ -20,8 +20,8 @@ interface InstallRequirementsInterface {
   * hand. As a consequence, install-time requirements must be checked without
   * access to the full Drupal API, because it is not available during
   * install.php.
   * If a requirement has a severity of REQUIREMENT_ERROR, install.php will
   * abort or at least the module will not install.
   * If a requirement has a severity of RequirementSeverity::Error, install.php
   * will abort or at least the module will not install.
   * Other severity levels have no effect on the installation.
   * Module dependencies do not belong to these installation requirements,
   * but should be defined in the module's .info.yml file.
@@ -37,12 +37,9 @@ interface InstallRequirementsInterface {
   *   - value: This should only be used for version numbers, do not set it if
   *     not applicable.
   *   - description: The description of the requirement/status.
   *   - severity: (optional) The requirement's result/severity level, one of:
   *     - REQUIREMENT_INFO: For info only.
   *     - REQUIREMENT_OK: The requirement is satisfied.
   *     - REQUIREMENT_WARNING: The requirement failed with a warning.
   *     - REQUIREMENT_ERROR: The requirement failed with an error.
   *     Defaults to REQUIREMENT_OK when installing.
   *   - severity: (optional) An instance of
   *     \Drupal\Core\Extension\Requirement\RequirementSeverity enum. Defaults
   *     to RequirementSeverity::OK when installing.
   */
  public static function getRequirements(): array;

+120 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Core\Extension\Requirement;

use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * The requirements severity enum.
 */
enum RequirementSeverity: int {

  /*
   * Informational message only.
   */
  case Info = -1;

  /*
   * Requirement successfully met.
   */
  case OK = 0;

  /*
   * Warning condition; proceed but flag warning.
   */
  case Warning = 1;

  /*
   * Error condition; abort installation.
   */
  case Error = 2;

  /**
   * Returns the translated title of the severity.
   */
  public function title(): TranslatableMarkup {
    return match ($this) {
      self::Info => new TranslatableMarkup('Checked'),
      self::OK => new TranslatableMarkup('OK'),
      self::Warning => new TranslatableMarkup('Warnings found'),
      self::Error => new TranslatableMarkup('Errors found'),
    };
  }

  /**
   * Returns the status of the severity.
   *
   * This string representation can be used as an array key when grouping
   * requirements checks by severity, or in other places where the int-backed
   * value is not appropriate.
   */
  public function status(): string {
    return match ($this) {
      self::Info => 'checked',
      self::OK => 'ok',
      self::Warning => 'warning',
      self::Error => 'error',
    };

  }

  /**
   * Determines the most severe requirement in a list of requirements.
   *
   * @param array<string, array{'title': \Drupal\Core\StringTranslation\TranslatableMarkup, 'value': mixed, description: \Drupal\Core\StringTranslation\TranslatableMarkup, 'severity': \Drupal\Core\Extension\Requirement\RequirementSeverity}> $requirements
   *   An array of requirements, in the same format as is returned by
   *   hook_requirements(), hook_runtime_requirements(),
   *   hook_update_requirements(), and
   *   \Drupal\Core\Extension\InstallRequirementsInterface.
   *
   * @return \Drupal\Core\Extension\Requirement\RequirementSeverity
   *   The most severe requirement.
   *
   * @see \Drupal\Core\Extension\InstallRequirementsInterface::getRequirements()
   * @see \hook_requirements()
   * @see \hook_runtime_requirements()
   * @see \hook_update_requirements()
   */
  public static function maxSeverityFromRequirements(array $requirements): RequirementSeverity {
    RequirementSeverity::convertLegacyIntSeveritiesToEnums($requirements, __METHOD__);
    return array_reduce(
      $requirements,
      function (RequirementSeverity $severity, $requirement) {
        $requirementSeverity = $requirement['severity'] ?? RequirementSeverity::OK;
        return RequirementSeverity::from(max($severity->value, $requirementSeverity->value));
      },
      RequirementSeverity::OK
    );
  }

  /**
   * Converts legacy int value severities to enums.
   *
   * @param array<string, array{'title': \Drupal\Core\StringTranslation\TranslatableMarkup, 'value': mixed, description: \Drupal\Core\StringTranslation\TranslatableMarkup, 'severity': \Drupal\Core\Extension\Requirement\RequirementSeverity}> $requirements
   *   An array of requirements, in the same format as is returned by
   *   hook_requirements(), hook_runtime_requirements(),
   *   hook_update_requirements(), and
   *   \Drupal\Core\Extension\InstallRequirementsInterface.
   * @param string $deprecationMethod
   *   The method name to pass to the deprecation message.
   *
   * @see \Drupal\Core\Extension\InstallRequirementsInterface::getRequirements()
   * @see \hook_requirements()
   * @see \hook_runtime_requirements()
   * @see \hook_update_requirements()
   */
  public static function convertLegacyIntSeveritiesToEnums(array &$requirements, string $deprecationMethod): void {
    foreach ($requirements as &$requirement) {
      if (isset($requirement['severity'])) {
        $severity = $requirement['severity'];
        if (!$severity instanceof RequirementSeverity) {
          @trigger_error("Calling {$deprecationMethod}() with an array of \$requirements with 'severity' with values not of type " . RequirementSeverity::class . " enums is deprecated in drupal:11.2.0 and is required in drupal:12.0.0. See https://www.drupal.org/node/3410939", \E_USER_DEPRECATED);
          $requirement['severity'] = RequirementSeverity::from($requirement['severity']);
        }
      }
    }
  }

}
Loading