diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php index dba7a94..ec0bcb4 100644 --- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -345,7 +345,7 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { foreach ($reasons as $reason) { $reason_message[] = implode(', ', $reason); } - throw new ModuleUninstallValidatorException(format_string('The following reasons prevents the modules from being uninstalled: @reasons', array( + throw new ModuleUninstallValidatorException(format_string('The following reasons prevent the modules from being uninstalled: @reasons', array( '@reasons' => implode(', ', $reason_message), ))); } diff --git a/core/modules/system/css/system.admin.css b/core/modules/system/css/system.admin.css index 7f23aab..959a908 100644 --- a/core/modules/system/css/system.admin.css +++ b/core/modules/system/css/system.admin.css @@ -157,7 +157,8 @@ small .admin-link:after { margin-bottom: 0; } .admin-requirements, -.admin-required { +.admin-required, +.validation-reasons { font-size: 0.9em; color: #666; } diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php index 2e80482..532f20f 100644 --- a/core/modules/system/src/Form/ModulesListForm.php +++ b/core/modules/system/src/Form/ModulesListForm.php @@ -7,6 +7,7 @@ namespace Drupal\system\Form; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\PreExistingConfigException; @@ -208,19 +209,22 @@ public function buildForm(array $form, FormStateInterface $form_state) { // Add a wrapper around every package. foreach (Element::children($form['modules']) as $package) { - $form['modules'][$package] += array( - '#type' => 'details', - '#title' => $this->t($package), - '#open' => TRUE, - '#theme' => 'system_modules_details', + $table = array( + '#type' => 'table', '#header' => array( array('data' => $this->t('Installed'), 'class' => array('checkbox', 'visually-hidden')), array('data' => $this->t('Name'), 'class' => array('name', 'visually-hidden')), array('data' => $this->t('Description'), 'class' => array('description', 'visually-hidden', RESPONSIVE_PRIORITY_LOW)), ), - '#attributes' => array('class' => array('package-listing')), + ) + $form['modules'][$package]; + $form['modules'][$package] = array( + '#type' => 'details', + '#title' => $this->t($package), + '#open' => TRUE, // Ensure that the "Core" package comes first. '#weight' => $package == 'Core' ? -10 : NULL, + '#attributes' => ['class' => ['package-listing']], + 'table' => $table, ); } @@ -257,31 +261,33 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ protected function buildRow(array $modules, Extension $module, $distribution) { // Set the basic properties. - $row['#required'] = array(); + $row['#version'] = $module->info['version']; $row['#requires'] = array(); $row['#required_by'] = array(); - $row['name']['#markup'] = $module->info['name']; - $row['description']['#markup'] = $this->t($module->info['description']); - $row['version']['#markup'] = $module->info['version']; - // Generate link for module's help page, if there is one. - $row['links']['help'] = array(); + $row_links['help'] = array(); if ($this->moduleHandler->moduleExists('help') && $module->status && in_array($module->getName(), $this->moduleHandler->getImplementations('help'))) { if ($this->moduleHandler->invoke($module->getName(), 'help', array('help.page.' . $module->getName(), $this->routeMatch))) { - $row['links']['help'] = array( + $row_links['help'] = array( '#type' => 'link', '#title' => $this->t('Help'), '#url' => Url::fromRoute('help.page', ['name' => $module->getName()]), - '#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => $this->t('Help'))), + '#options' => array( + 'attributes' => array( + 'id' => 'edit-modules-core-' . $module->getName() . '-links-help', + 'class' => ['module-link', 'module-link-help'], + 'title' => $this->t('Help'), + ), + ), ); } } // Generate link for module's permission, if the user has access to it. - $row['links']['permissions'] = array(); + $row_links['permissions'] = array(); if ($module->status && \Drupal::currentUser()->hasPermission('administer permissions') && in_array($module->getName(), $this->moduleHandler->getImplementations('permission'))) { - $row['links']['permissions'] = array( + $row_links['permissions'] = array( '#type' => 'link', '#title' => $this->t('Permissions'), '#url' => Url::fromRoute('user.admin_permissions'), @@ -290,7 +296,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) { } // Generate link for module's configuration page, if it has one. - $row['links']['configure'] = array(); + $row_links['configure'] = array(); if ($module->status && isset($module->info['configure'])) { $route_parameters = isset($module->info['configure_parameters']) ? $module->info['configure_parameters'] : array(); if ($this->accessManager->checkNamedRoute($module->info['configure'], $route_parameters, $this->currentUser)) { @@ -312,7 +318,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) { $description = $this->titleResolver->getTitle($request, $route_object); } - $row['links']['configure'] = array( + $row_links['configure'] = array( '#type' => 'link', '#title' => $this->t('Configure'), '#url' => Url::fromRoute($module->info['configure'], $route_parameters), @@ -329,7 +335,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) { // Present a checkbox for installing and indicating the status of a module. $row['enable'] = array( '#type' => 'checkbox', - '#title' => $this->t('Install'), + '#wrapper_attributes' => ['class' => ['checkbox']], '#default_value' => (bool) $module->status, '#disabled' => (bool) $module->status, ); @@ -423,6 +429,62 @@ protected function buildRow(array $modules, Extension $module, $distribution) { } } + // Set id for checkbox here so module label can reference it. + $id = Html::getClass('edit-modules-' . $module->info['package'] . '-' . $module->getName() . '-enable'); + + $row['enable'] += array( + '#id' => $id, + '#parents' => ['modules', $module->info['package'], $module->getName(), 'enable'], + ); + + // Add the module label and expand/collapse functionality. + $row['name'] = array( + '#type' => 'inline_template', + '#template' => '', + '#context' => array( + 'machine_name' => $module->getName(), + 'label_id' => $id, + 'module_name' => $module->info['name'], + ), + '#wrapper_attributes' => ['class' => ['module']], + ); + + // Add the description, along with any modules it requires. + $description = '
'; + $description .= '
' . $this->t('Machine name: !key', array('!key' => $module->getName())) . '
'; + if ($module->info['version'] || $row['#requires'] || $row['#required_by']) { + if ($module->info['version']) { + $description .= '
' . $this->t('Version: !module-version', array('!module-version' => $module->info['version'])) . '
'; + } + if ($row['#requires']) { + $description .= '
' . $this->t('Requires: !module-list', array('!module-list' => implode(', ', $row['#requires']))) . '
'; + } + if ($row['#required_by']) { + $description .= '
' . $this->t('Required by: !module-list', array('!module-list' => implode(', ', $row['#required_by']))) . '
'; + } + } + $description .= '
'; + + $links = ''; + foreach (['help', 'permissions', 'configure'] as $key) { + $links .= (isset($row_links[$key]) ? drupal_render($row_links[$key]) : ''); + } + + if ($links) { + $description .= ' '; + } + + $row['description'] = array( + '#type' => 'details', + '#title' => $this->t(' ' . $module->info['description'] . ''), + '#attributes' => array('id' => $id . '-description'), + '#description' => $description, + '#collapsed' => TRUE, + '#wrapper_attributes' => ['class' => ['description', 'expand']] + ); + return $row; } @@ -455,6 +517,7 @@ protected function buildModuleList(FormStateInterface $form_state) { // First, build a list of all modules that were selected. foreach ($packages as $items) { + unset($items['table']); foreach ($items as $name => $checkbox) { if ($checkbox['enable'] && !$this->moduleHandler->moduleExists($name)) { $modules['install'][$name] = $data[$name]->info['name']; diff --git a/core/modules/system/src/Form/ModulesUninstallForm.php b/core/modules/system/src/Form/ModulesUninstallForm.php index 153e2ca..e8fb208 100644 --- a/core/modules/system/src/Form/ModulesUninstallForm.php +++ b/core/modules/system/src/Form/ModulesUninstallForm.php @@ -110,7 +110,23 @@ public function buildForm(array $form, FormStateInterface $form_state) { ), ); - $form['modules'] = array(); + $profile = drupal_get_profile(); + + // Sort all modules by their name. + if (!empty($uninstallable)) { + uasort($uninstallable, 'system_sort_modules_by_info_name'); + $validation_reasons = $this->moduleInstaller->validateUninstall(array_keys($uninstallable)); + } + + $form['uninstall'] = array( + '#type' => 'table', + '#header' => array( + 'title' => ['data' => $this->t('Name')], + 'description' => ['data' => $this->t('Description')], + ), + '#empty' => $this->t('There are no items available to uninstall.'), + '#tableselect' => TRUE, + ); // Only build the rest of the form if there are any modules available to // uninstall; @@ -118,41 +134,53 @@ public function buildForm(array $form, FormStateInterface $form_state) { return $form; } - $profile = drupal_get_profile(); - - // Sort all modules by their name. - uasort($uninstallable, 'system_sort_modules_by_info_name'); - $validation_reasons = $this->moduleInstaller->validateUninstall(array_keys($uninstallable)); - - $form['uninstall'] = array('#tree' => TRUE); foreach ($uninstallable as $module_key => $module) { - $name = $module->info['name'] ?: $module->getName(); - $form['modules'][$module->getName()]['#module_name'] = $name; - $form['modules'][$module->getName()]['name']['#markup'] = $name; - $form['modules'][$module->getName()]['description']['#markup'] = $this->t($module->info['description']); - - $form['uninstall'][$module->getName()] = array( - '#type' => 'checkbox', - '#title' => $this->t('Uninstall @module module', array('@module' => $name)), - '#title_display' => 'invisible', + // Define