Commit ed951b05 authored by catch's avatar catch
Browse files

Issue #3215043 by Spokje, larowlan, quietone, dww, srilakshmier, paulocs,...

Issue #3215043 by Spokje, larowlan, quietone, dww, srilakshmier, paulocs, yogeshmpawar, catch, Gábor Hojtsy, benjifisher, AaronMcHale, phenaproxima, kim.pepper, fubarhouse: Indicate the non-stable statuses in admin/modules page

(cherry picked from commit 24893484)
parent ab0ec7c0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -195,6 +195,15 @@ small .admin-link:after {
[dir="rtl"] .module-link-configure {
  background-position: top 50% right 0;
}
.module-link--non-stable {
  padding-left: 18px;
  background: url(../../../misc/icons/e29700/warning.svg) 0 50% no-repeat; /* LTR */
}
[dir="rtl"] .module-link--non-stable {
  padding-right: 18px;
  padding-left: 0;
  background-position: top 50% right 0;
}

/* Status report. */
.system-status-report__status-title {
+0 −39
Original line number Diff line number Diff line
<?php

namespace Drupal\system\Form;

/**
 * Builds a confirmation form for enabling experimental modules.
 *
 * @internal
 */
class ModulesListExperimentalConfirmForm extends ModulesListConfirmForm {

  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    return $this->t('Are you sure you wish to enable experimental modules?');
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'system_modules_experimental_confirm_form';
  }

  /**
   * {@inheritdoc}
   */
  protected function buildMessageList() {
    $this->messenger()->addWarning($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']));

    $items = parent::buildMessageList();
    // Add the list of experimental modules after any other messages.
    $items[] = $this->t('The following modules are experimental: @modules', ['@modules' => implode(', ', array_values($this->modules['experimental']))]);

    return $items;
  }

}
+34 −14
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PermissionHandlerInterface;
@@ -249,7 +250,22 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
    $row['#requires'] = [];
    $row['#required_by'] = [];

    $lifecycle = $module->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER];
    $row['name']['#markup'] = $module->info['name'];
    if ($lifecycle !== ExtensionLifecycle::STABLE && !empty($module->info[ExtensionLifecycle::LIFECYCLE_LINK_IDENTIFIER])) {
      $row['name']['#markup'] .= ' ' . Link::fromTextAndUrl('(' . $this->t('@lifecycle', ['@lifecycle' => ucfirst($lifecycle)]) . ')',
          Url::fromUri($module->info[ExtensionLifecycle::LIFECYCLE_LINK_IDENTIFIER], [
            'attributes' =>
              [
                'class' => 'module-link--non-stable',
                'aria-label' => $this->t('View information on the @lifecycle status of the module @module', [
                  '@lifecycle' => ucfirst($lifecycle),
                  '@module' => $module->info['name'],
                ]),
              ],
          ])
        )->toString();
    }
    $row['description']['#markup'] = $this->t($module->info['description']);
    $row['version']['#markup'] = $module->info['version'];

@@ -390,7 +406,7 @@ protected function buildModuleList(FormStateInterface $form_state) {
    $modules = [
      'install' => [],
      'dependencies' => [],
      'experimental' => [],
      'non_stable' => [],
    ];

    $data = $this->moduleExtensionList->getList();
@@ -405,10 +421,12 @@ protected function buildModuleList(FormStateInterface $form_state) {
      }
      // Selected modules should be installed.
      elseif (($checkbox = $form_state->getValue(['modules', $name], FALSE)) && $checkbox['enable']) {
        $modules['install'][$name] = $data[$name]->info['name'];
        // Identify experimental modules.
        if ($data[$name]->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
          $modules['experimental'][$name] = $data[$name]->info['name'];
        $info = $data[$name]->info;
        $modules['install'][$name] = $info['name'];
        // Identify non-stable modules.
        $lifecycle = $info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER];
        if ($lifecycle !== ExtensionLifecycle::STABLE) {
          $modules['non_stable'][$name] = $info['name'];
        }
      }
    }
@@ -417,12 +435,14 @@ protected function buildModuleList(FormStateInterface $form_state) {
    foreach ($modules['install'] as $module => $value) {
      foreach (array_keys($data[$module]->requires) as $dependency) {
        if (!isset($modules['install'][$dependency]) && !$this->moduleHandler->moduleExists($dependency)) {
          $modules['dependencies'][$module][$dependency] = $data[$dependency]->info['name'];
          $modules['install'][$dependency] = $data[$dependency]->info['name'];
          $dependency_info = $data[$dependency]->info;
          $modules['dependencies'][$module][$dependency] = $dependency_info['name'];
          $modules['install'][$dependency] = $dependency_info['name'];

          // Identify experimental modules.
          if ($data[$dependency]->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
            $modules['experimental'][$dependency] = $data[$dependency]->info['name'];
          // Identify non-stable modules.
          $lifecycle = $dependency_info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER];
          if ($lifecycle !== ExtensionLifecycle::STABLE) {
            $modules['non_stable'][$dependency] = $dependency_info['name'];
          }
        }
      }
@@ -436,7 +456,7 @@ protected function buildModuleList(FormStateInterface $form_state) {
    foreach (array_keys($modules['install']) as $module) {
      if (!drupal_check_module($module)) {
        unset($modules['install'][$module]);
        unset($modules['experimental'][$module]);
        unset($modules['non_stable'][$module]);
        foreach (array_keys($data[$module]->required_by) as $dependent) {
          unset($modules['install'][$dependent]);
          unset($modules['dependencies'][$dependent]);
@@ -455,9 +475,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    $modules = $this->buildModuleList($form_state);

    // Redirect to a confirmation form if needed.
    if (!empty($modules['experimental']) || !empty($modules['dependencies'])) {
    if (!empty($modules['non_stable']) || !empty($modules['dependencies'])) {

      $route_name = !empty($modules['experimental']) ? 'system.modules_list_experimental_confirm' : 'system.modules_list_confirm';
      $route_name = !empty($modules['non_stable']) ? 'system.modules_list_non_stable_confirm' : 'system.modules_list_confirm';
      // Write the list of changed module states into a key value store.
      $account = $this->currentUser()->id();
      $this->keyValueExpirable->setWithExpire($account, $modules, 60);
+200 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\system\Form;

use Drupal\Core\Extension\ExtensionLifecycle;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Link;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Builds a confirmation form for enabling non-stable modules.
 *
 * @internal
 */
class ModulesListNonStableConfirmForm extends ModulesListConfirmForm {

  /**
   * Module extension list.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected ModuleExtensionList $moduleExtensionList;

  /**
   * An array of module names to be enabled, keyed by lifecycle.
   *
   * @var array
   */
  protected array $groupedModuleInfo;

  /**
   * Boolean indicating a core deprecated module is being enabled.
   *
   * @var bool
   */
  protected bool $coreDeprecatedModules;

  /**
   * Boolean indicating a contrib deprecated module is being enabled.
   *
   * @var bool
   */
  protected bool $contribDeprecatedModules;

  /**
   * Constructs a new ModulesListNonStableConfirmForm.
   *
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
   *   The module installer.
   * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
   *   The key value expirable factory.
   * @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList
   *   The module extension list.
   */
  public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable, ModuleExtensionList $moduleExtensionList) {
    parent::__construct($module_handler, $module_installer, $key_value_expirable);
    $this->moduleExtensionList = $moduleExtensionList;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('module_handler'),
      $container->get('module_installer'),
      $container->get('keyvalue.expirable')->get('module_list'),
      $container->get('extension.list.module')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    $hasExperimentalModulesToEnable = !empty($this->groupedModuleInfo[ExtensionLifecycle::EXPERIMENTAL]);
    $hasDeprecatedModulesToEnable = !empty($this->groupedModuleInfo[ExtensionLifecycle::DEPRECATED]);

    if ($hasExperimentalModulesToEnable && $hasDeprecatedModulesToEnable) {
      return $this->t('Are you sure you wish to enable experimental and deprecated modules?');
    }

    if ($hasExperimentalModulesToEnable) {
      return $this->formatPlural(
        count($this->groupedModuleInfo[ExtensionLifecycle::EXPERIMENTAL]),
        'Are you sure you wish to enable an experimental module?',
        'Are you sure you wish to enable experimental modules?'
      );
    }

    if ($hasDeprecatedModulesToEnable) {
      return $this->formatPlural(
        count($this->groupedModuleInfo[ExtensionLifecycle::DEPRECATED]),
        'Are you sure you wish to enable a deprecated module?',
        'Are you sure you wish to enable deprecated modules?'
      );
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'system_modules_non_stable_confirm_form';
  }

  /**
   * {@inheritdoc}
   */
  protected function buildMessageList() {
    $this->buildNonStableInfo();

    $items = parent::buildMessageList();
    if (!empty($this->groupedModuleInfo[ExtensionLifecycle::EXPERIMENTAL])) {
      $this->messenger()->addWarning($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']));
      // Add the list of experimental modules after any other messages.
      $items[] = $this->formatPlural(
        count($this->groupedModuleInfo[ExtensionLifecycle::EXPERIMENTAL]),
        'The following module is experimental: @modules.',
        'The following modules are experimental: @modules.',
        ['@modules' => implode(', ', $this->groupedModuleInfo[ExtensionLifecycle::EXPERIMENTAL])]
      );
    }
    if (!empty($this->groupedModuleInfo[ExtensionLifecycle::DEPRECATED])) {
      $this->messenger()->addWarning($this->buildDeprecatedMessage($this->coreDeprecatedModules, $this->contribDeprecatedModules));
      $items = array_merge($items, $this->groupedModuleInfo[ExtensionLifecycle::DEPRECATED]);
    }

    return $items;
  }

  /**
   * Builds a message to be displayed to the user enabling deprecated modules.
   *
   * @param bool $core_deprecated_modules
   *   TRUE if a core deprecated module is being enabled.
   * @param bool $contrib_deprecated_modules
   *   TRUE if a contrib deprecated module is being enabled.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
   *   The relevant message.
   */
  protected function buildDeprecatedMessage(bool $core_deprecated_modules, bool $contrib_deprecated_modules): TranslatableMarkup {
    if ($contrib_deprecated_modules && $core_deprecated_modules) {
      return $this->t('<a href=":url">Deprecated modules</a> are modules that may be removed from the next major release of Drupal core and the relevant contributed module. Use at your own risk.', [':url' => 'https://www.drupal.org/about/core/policies/core-change-policies/deprecated-modules-and-themes']);
    }
    if ($contrib_deprecated_modules) {
      return $this->t('<a href=":url">Deprecated modules</a> are modules that may be removed from the next major release of this project. Use at your own risk.', [':url' => 'https://www.drupal.org/about/core/policies/core-change-policies/deprecated-modules-and-themes']);
    }

    return $this->t('<a href=":url">Deprecated modules</a> are modules that may be removed from the next major release of Drupal core. Use at your own risk.', [':url' => 'https://www.drupal.org/about/core/policies/core-change-policies/deprecated-modules-and-themes']);
  }

  /**
   * Sets properties with information about non-stable modules being enabled.
   */
  protected function buildNonStableInfo(): void {
    $non_stable = $this->modules['non_stable'];
    $data = $this->moduleExtensionList->getList();
    $grouped = [];
    $core_deprecated_modules = FALSE;
    $contrib_deprecated_modules = FALSE;
    foreach ($non_stable as $machine_name => $name) {
      $lifecycle = $data[$machine_name]->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER];
      if ($lifecycle === ExtensionLifecycle::EXPERIMENTAL) {
        // We just show the extension name if it is experimental.
        $grouped[$lifecycle][] = $name;
        continue;
      }
      $core_deprecated_modules = $core_deprecated_modules || $data[$machine_name]->origin === 'core';
      $contrib_deprecated_modules = $contrib_deprecated_modules || $data[$machine_name]->origin !== 'core';
      // If the extension is deprecated we show links to more information.
      $grouped[$lifecycle][] = Link::fromTextAndUrl(
        $this->t('The @name module is deprecated. (more information)', [
          '@name' => $name,
        ]),
        Url::fromUri($data[$machine_name]->info[ExtensionLifecycle::LIFECYCLE_LINK_IDENTIFIER], [
          'attributes' =>
            [
              'aria-label' => ' ' . $this->t('about the status of the @name module', [
                  '@name' => $name,
                ]),
            ],
        ])
      )->toString();
    }

    $this->groupedModuleInfo = $grouped;
    $this->coreDeprecatedModules = $core_deprecated_modules;
    $this->contribDeprecatedModules = $contrib_deprecated_modules;
  }

}
+4 −4
Original line number Diff line number Diff line
@@ -281,11 +281,11 @@ system.modules_list_confirm:
  requirements:
    _permission: 'administer modules'

system.modules_list_experimental_confirm:
  path: '/admin/modules/list/confirm-experimental'
system.modules_list_non_stable_confirm:
  path: '/admin/modules/list/confirm-non-stable'
  defaults:
    _form: '\Drupal\system\Form\ModulesListExperimentalConfirmForm'
    _title: 'Experimental modules'
    _form: '\Drupal\system\Form\ModulesListNonStableConfirmForm'
    _title: 'Non-stable modules'
  requirements:
    _permission: 'administer modules'

Loading