Issue #1990544 by fubhy, Pancho, h3rj4n, dawehner: Convert system_modules() to a Controller.

8.0.x
Alex Pott 2013-07-19 11:17:03 +01:00
parent 862b4d6347
commit 8d693aded0
8 changed files with 678 additions and 515 deletions

View File

@ -114,6 +114,19 @@ class Connection extends DatabaseConnection {
return $pdo;
}
/**
* {@inheritdoc}
*/
public function serialize() {
// Cleanup the connection, much like __destruct() does it as well.
if ($this->needsCleanup) {
$this->nextIdDelete();
}
$this->needsCleanup = FALSE;
return parent::serialize();
}
public function __destruct() {
if ($this->needsCleanup) {
$this->nextIdDelete();

View File

@ -1,95 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\system\Form\ModulesInstallConfirmForm.
*/
namespace Drupal\system\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Builds a confirmation form for required modules.
*
* Used internally in system_modules().
*/
class ModulesInstallConfirmForm extends ConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Some required modules must be enabled');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Continue');
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
return 'admin/modules';
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return t('Would you like to continue with the above?');
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'system_modules_confirm_form';
}
/**
* {@inheritdoc}
* @param array $modules
* The array of modules.
* @param array $storage
* Temporary storage of module dependency information.
*/
public function buildForm(array $form, array &$form_state, $modules = array(), $storage = array(), Request $request = NULL) {
$items = array();
$form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
$form['status']['#tree'] = TRUE;
foreach ($storage['more_required'] as $info) {
$t_argument = array(
'@module' => $info['name'],
'@required' => implode(', ', $info['requires']),
);
$items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument);
}
foreach ($storage['missing_modules'] as $name => $info) {
$t_argument = array(
'@module' => $name,
'@depends' => implode(', ', $info['depends']),
);
$items[] = format_plural(count($info['depends']), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', $t_argument);
}
$form['modules'] = array('#theme' => 'item_list', '#items' => $items);
return parent::buildForm($form, $form_state, $request);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
}
}

View File

@ -0,0 +1,190 @@
<?php
/**
* @file
* Contains \Drupal\system\Form\ModulesListConfirmForm.
*/
namespace Drupal\system\Form;
use Drupal\Core\Controller\ControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\StringTranslation\TranslationManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Builds a confirmation form for enabling modules with dependencies.
*/
class ModulesListConfirmForm extends ConfirmFormBase implements ControllerInterface {
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The expirable key value store.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
*/
protected $keyValueExpirable;
/**
* The translation manager service.
*
* @var \Drupal\Core\StringTranslation\TranslationManager
*/
protected $translationManager;
/**
* The request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* An associative list of modules to enable or disable.
*
* @var array
*/
protected $modules = array();
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('module_handler'),
$container->get('keyvalue.expirable')->get('module_list'),
$container->get('string_translation')
);
}
/**
* Constructs a ModulesListConfirmForm object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
* The key value expirable factory.
* @param \Drupal\Core\StringTranslation\TranslationManager
* The translation manager.
*/
public function __construct(ModuleHandlerInterface $module_handler, KeyValueStoreExpirableInterface $key_value_expirable, TranslationManager $translation_manager) {
$this->moduleHandler = $module_handler;
$this->keyValueExpirable = $key_value_expirable;
$this->translationManager = $translation_manager;
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->translationManager->translate('Some required modules must be enabled');
}
/**
* {@inheritdoc}
*/
public function getCancelPath() {
return 'admin/modules';
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->translationManager->translate('Continue');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->translationManager->translate('Would you like to continue with the above?');
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'system_modules_confirm_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$form_state, Request $request = NULL) {
$account = $request->attributes->get('account')->id();
$this->modules = $this->keyValueExpirable->get($account);
// Redirect to the modules list page if the key value store is empty.
if (!$this->modules) {
return new RedirectResponse(url($this->getCancelPath(), array('absolute' => TRUE)));
}
// Store the request for use in the submit handler.
$this->request = $request;
$items = array();
// Display a list of required modules that have to be installed as well but
// were not manually selected.
foreach ($this->modules['dependencies'] as $module => $dependencies) {
$items[] = format_plural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', array(
'@module' => $this->modules['enable'][$module],
'@required' => implode(', ', $dependencies),
));
}
foreach ($this->modules['missing'] as $name => $dependents) {
$items[] = format_plural(count($dependents), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', array(
'@module' => $name,
'@depends' => implode(', ', $dependents),
));
}
$form['message'] = array(
'#theme' => 'item_list',
'#items' => $items,
);
return parent::buildForm($form, $form_state, $this->request);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
// Remove the key value store entry.
$account = $this->request->attributes->get('account')->id();
$this->keyValueExpirable->delete($account);
// Gets list of modules prior to install process.
$before = $this->moduleHandler->getModuleList();
// Installs, enables, and disables modules.
if (!empty($this->modules['enable'])) {
$this->moduleHandler->enable(array_keys($this->modules['enable']));
}
if (!empty($this->modules['disable'])) {
$this->moduleHandler->disable(array_keys($this->modules['disable']));
}
// Gets module list after install process, flushes caches and displays a
// message if there are changes.
if ($before != $this->moduleHandler->getModuleList()) {
drupal_flush_all_caches();
drupal_set_message($this->translationManager->translate('The configuration options have been saved.'));
}
$form_state['redirect'] = $this->getCancelPath();
}
}

View File

@ -0,0 +1,458 @@
<?php
/**
* @file
* Contains \Drupal\system\Form\ModulesListForm.
*/
namespace Drupal\system\Form;
use Drupal\Core\Controller\ControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\StringTranslation\TranslationManager;
use Drupal\Component\Utility\Unicode;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides module enable/disable interface.
*
* The list of modules gets populated by module.info.yml files, which contain
* each module's name, description, and information about which modules it
* requires. See drupal_parse_info_file() for info on module.info.yml
* descriptors.
*/
class ModulesListForm implements FormInterface, ControllerInterface {
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The expirable key value store.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
*/
protected $keyValueExpirable;
/**
* The translation manager service.
*
* @var \Drupal\Core\StringTranslation\TranslationManager
*/
protected $translationManager;
/**
* The request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('module_handler'),
$container->get('keyvalue.expirable')->get('module_list'),
$container->get('string_translation')
);
}
/**
* Constructs a ModulesListForm object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
* The key value expirable factory.
* @param \Drupal\Core\StringTranslation\TranslationManager
* The translation manager.
*/
public function __construct(ModuleHandlerInterface $module_handler, KeyValueStoreExpirableInterface $key_value_expirable, TranslationManager $translation_manager) {
$this->moduleHandler = $module_handler;
$this->keyValueExpirable = $key_value_expirable;
$this->translationManager = $translation_manager;
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'system_modules';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$form_state, Request $request = NULL) {
require_once DRUPAL_ROOT . '/core/includes/install.inc';
$distribution = check_plain(drupal_install_profile_distribution_name());
// Include system.admin.inc so we can use the sort callbacks.
$this->moduleHandler->loadInclude('system', 'inc', 'system.admin');
// Store the request for use in the submit handler.
$this->request = $request;
$form['filters'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('table-filter', 'js-show'),
),
);
$form['filters']['text'] = array(
'#type' => 'search',
'#title' => $this->translationManager->translate('Search'),
'#size' => 30,
'#placeholder' => $this->translationManager->translate('Enter module name'),
'#attributes' => array(
'class' => array('table-filter-text'),
'data-table' => '#system-modules',
'autocomplete' => 'off',
'title' => $this->translationManager->translate('Enter a part of the module name or description to filter by.'),
),
);
// Sort all modules by their names.
$modules = system_rebuild_module_data();
uasort($modules, 'system_sort_modules_by_info_name');
// Iterate over each of the modules.
$form['modules']['#tree'] = TRUE;
foreach ($modules as $filename => $module) {
if (empty($module->info['hidden'])) {
$package = $module->info['package'];
$form['modules'][$package][$filename] = $this->buildRow($modules, $module, $distribution);
}
}
// Add a wrapper around every package.
foreach (element_children($form['modules']) as $package) {
$form['modules'][$package] += array(
'#type' => 'details',
'#title' => $this->translationManager->translate($package),
'#theme' => 'system_modules_details',
'#header' => array(
array('data' => '<span class="element-invisible">' . $this->translationManager->translate('Enabled') . '</span>', 'class' => array('checkbox')),
array('data' => $this->translationManager->translate('Name'), 'class' => array('name')),
array('data' => $this->translationManager->translate('Description'), 'class' => array('description', RESPONSIVE_PRIORITY_LOW)),
),
// Ensure that the "Core" package comes first.
'#weight' => $package == 'Core' ? -10 : NULL,
);
}
// Lastly, sort all packages by title.
uasort($form['modules'], 'element_sort_by_title');
$form['#attached']['library'][] = array('system', 'drupal.system.modules');
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->translationManager->translate('Save configuration'),
);
return $form;
}
/**
* Builds a table row for the system modules page.
*
* @param array $modules
* The list existing modules.
* @param object $module
* The module for which to build the form row.
* @param $distribution
*
* @return array
* The form row for the given module.
*/
protected function buildRow(array $modules, $module, $distribution) {
// Set the basic properties.
$row['#required'] = array();
$row['#requires'] = array();
$row['#required_by'] = array();
$row['name']['#markup'] = $module->info['name'];
$row['description']['#markup'] = $this->translationManager->translate($module->info['description']);
$row['version']['#markup'] = $module->info['version'];
// Add links for each module.
// Used when checking if a module implements a help page.
$help = $this->moduleHandler->moduleExists('help') ? drupal_help_arg() : FALSE;
// Generate link for module's help page, if there is one.
$row['links']['help'] = array();
if ($help && $module->status && in_array($module->name, $this->moduleHandler->getImplementations('help'))) {
if ($this->moduleHandler->invoke($module->name, 'help', array("admin/help#$module->name", $help))) {
$row['links']['help'] = array(
'#type' => 'link',
'#title' => $this->translationManager->translate('Help'),
'#href' => "admin/help/$module->name",
'#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => $this->translationManager->translate('Help'))),
);
}
}
// Generate link for module's permission, if the user has access to it.
$row['links']['permissions'] = array();
if ($module->status && user_access('administer permissions') && in_array($module->name, $this->moduleHandler->getImplementations('permission'))) {
$row['links']['permissions'] = array(
'#type' => 'link',
'#title' => $this->translationManager->translate('Permissions'),
'#href' => 'admin/people/permissions',
'#options' => array('fragment' => 'module-' . $module->name, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => $this->translationManager->translate('Configure permissions'))),
);
}
// Generate link for module's configuration page, if it has one.
$row['links']['configure'] = array();
if ($module->status && isset($module->info['configure'])) {
if (($configure = menu_get_item($module->info['configure'])) && $configure['access']) {
$row['links']['configure'] = array(
'#type' => 'link',
'#title' => $this->translationManager->translate('Configure'),
'#href' => $configure['href'],
'#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure['description'])),
);
}
}
// Present a checkbox for installing and indicating the status of a module.
$row['enable'] = array(
'#type' => 'checkbox',
'#title' => $this->translationManager->translate('Enable'),
'#default_value' => (bool) $module->status,
);
// Disable the checkbox for required modules.
if (!empty($module->info['required'])) {
// Used when displaying modules that are required by the installation profile
$row['enable']['#disabled'] = TRUE;
$row['#required_by'][] = $distribution . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
}
// Check the compatibilities.
$compatible = TRUE;
$status = '';
// Check the core compatibility.
if ($module->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
$compatible = FALSE;
$status .= $this->translationManager->translate('This version is not compatible with Drupal !core_version and should be replaced.', array(
'!core_version' => DRUPAL_CORE_COMPATIBILITY,
));
}
// Ensure this module is compatible with the currently installed version of PHP.
if (version_compare(phpversion(), $module->info['php']) < 0) {
$compatible = FALSE;
$required = $module->info['php'] . (substr_count($module->info['php'], '.') < 2 ? '.*' : '');
$status .= $this->translationManager->translate('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array(
'@php_required' => $required,
'!php_version' => phpversion(),
));
}
// If this module is not compatible, disable the checkbox.
if (!$compatible) {
$row['enable']['#disabled'] = TRUE;
$row['description'] = array(
'#theme' => 'system_modules_incompatible',
'#message' => $status,
);
}
// If this module requires other modules, add them to the array.
foreach ($module->requires as $dependency => $version) {
if (!isset($modules[$dependency])) {
$row['#requires'][$dependency] = $this->translationManager->translate('@module (<span class="admin-missing">missing</span>)', array('@module' => Unicode::ucfirst($dependency)));
$row['enable']['#disabled'] = TRUE;
}
// Only display visible modules.
elseif (empty($modules[$dependency]->hidden)) {
$name = $modules[$dependency]->info['name'];
// Disable the module's checkbox if it is incompatible with the
// dependency's version.
if ($incompatible_version = drupal_check_incompatibility($version, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $modules[$dependency]->info['version']))) {
$row['#requires'][$dependency] = $this->translationManager->translate('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $name . $incompatible_version,
'@version' => $modules[$dependency]->info['version'],
));
$row['enable']['#disabled'] = TRUE;
}
// Disable the checkbox if the dependency is incompatible with this
// version of Drupal core.
elseif ($modules[$dependency]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
$row['#requires'][$dependency] = $this->translationManager->translate('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
'@module' => $name,
));
$row['enable']['#disabled'] = TRUE;
}
elseif ($modules[$dependency]->status) {
$row['#requires'][$dependency] = $this->translationManager->translate('@module', array('@module' => $name));
}
else {
$row['#requires'][$dependency] = $this->translationManager->translate('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $name));
}
}
}
// If this module is required by other modules, list those, and then make it
// impossible to disable this one.
foreach ($module->required_by as $dependent => $version) {
if (isset($modules[$dependent]) && empty($modules[$dependent]->info['hidden'])) {
if ($modules[$dependent]->status == 1 && $module->status == 1) {
$row['#required_by'][$dependent] = $this->translationManager->translate('@module', array('@module' => $modules[$dependent]->info['name']));
$row['enable']['#disabled'] = TRUE;
}
else {
$row['#required_by'][$dependent] = $this->translationManager->translate('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $modules[$dependent]->info['name']));
}
}
}
return $row;
}
/**
* Helper function for building a list of modules to enable or disable.
*
* @param array $form_state
* The form state array.
*
* @return array
* An array of modules to disable/enable and their dependencies.
*/
protected function buildModuleList(array $form_state) {
$packages = $form_state['values']['modules'];
// Build a list of modules to enable or disable.
$modules = array(
'enable' => array(),
'disable' => array(),
'dependencies' => array(),
'missing' => array(),
);
// Build a list of missing dependencies.
// @todo This should really not be handled here.
$data = system_rebuild_module_data();
foreach ($data as $name => $module) {
// Modules with missing dependencies have to be disabled.
if ($this->moduleHandler->moduleExists($name)) {
foreach (array_keys($module->requires) as $dependency) {
if (!isset($data[$dependency])) {
$modules['missing'][$dependency][$name] = $module->info['name'];
$modules['disable'][$name] = $module->info['name'];
}
}
}
elseif (!empty($module->required)) {
$modules['enable'][$name] = $module->info['name'];
}
}
// First, build a list of all modules that were selected.
foreach ($packages as $items) {
foreach ($items as $name => $checkbox) {
// Do not override modules that are forced to be enabled/disabled.
if (isset($modules['enable'][$name]) || isset($modules['disable'][$name])) {
continue;
}
$enabled = $this->moduleHandler->moduleExists($name);
if (!$checkbox['enable'] && $enabled) {
$modules['disable'][$name] = $data[$name]->info['name'];
}
elseif ($checkbox['enable'] && !$enabled) {
$modules['enable'][$name] = $data[$name]->info['name'];
}
}
}
// Add all dependencies to a list.
while (list($module) = each($modules['enable'])) {
foreach (array_keys($data[$module]->requires) as $dependency) {
if (!isset($modules['enable'][$dependency]) && !$this->moduleHandler->moduleExists($dependency)) {
$modules['dependencies'][$module][$dependency] = $data[$dependency]->info['name'];
$modules['enable'][$dependency] = $data[$dependency]->info['name'];
}
}
}
// Make sure the install API is available.
include_once DRUPAL_ROOT . '/core/includes/install.inc';
// Invoke hook_requirements('install'). If failures are detected, make
// sure the dependent modules aren't installed either.
foreach (array_keys($modules['enable']) as $module) {
if (drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED && !drupal_check_module($module)) {
unset($modules['enable'][$module]);
foreach (array_keys($data[$module]->required_by) as $dependent) {
unset($modules['enable'][$dependent]);
unset($modules['dependencies'][$dependent]);
}
}
}
return $modules;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, array &$form_state) {
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
// Retrieve a list of modules to enable/disable and their dependencies.
$modules = $this->buildModuleList($form_state);
// Check if we have to enable any dependencies. If there is one or more
// dependencies that are not enabled yet, redirect to the confirmation form.
if (!empty($modules['dependencies']) || !empty($modules['missing'])) {
// Write the list of changed module states into a key value store.
$account = $this->request->attributes->get('account')->id();
$this->keyValueExpirable->setWithExpire($account, $modules, 60);
// Redirect to the confirmation form.
$form_state['redirect'] = 'admin/modules/list/confirm';
// We can exit here because at least one modules has dependencies
// which we have to prompt the user for in a confirmation form.
return;
}
// Gets list of modules prior to install process.
$before = $this->moduleHandler->getModuleList();
// There seem to be no dependencies that would need approval.
if (!empty($modules['enable'])) {
$this->moduleHandler->enable(array_keys($modules['enable']));
}
if (!empty($modules['disable'])) {
$this->moduleHandler->disable(array_keys($modules['disable']));
}
// Gets module list after install process, flushes caches and displays a
// message if there are changes.
if ($before != $this->moduleHandler->getModuleList()) {
drupal_flush_all_caches();
drupal_set_message(t('The configuration options have been saved.'));
}
}
}

View File

@ -61,7 +61,7 @@ class DependencyTest extends ModuleTestBase {
// Verify that the module is forced to be disabled when submitting
// the module page.
$this->drupalPost('admin/modules', array(), t('Save configuration'));
$this->assertText(t('The @module module is missing, so the following module will be disabled: @depends.', array('@module' => '_missing_dependency', '@depends' => 'system_dependencies_test')), 'The module missing dependencies will be disabled.');
$this->assertText(t('The @module module is missing, so the following module will be disabled: @depends.', array('@module' => '_missing_dependency', '@depends' => 'System dependency test')), 'The module missing dependencies will be disabled.');
// Confirm.
$this->drupalPost(NULL, NULL, t('Continue'));

View File

@ -6,11 +6,9 @@
*/
use Drupal\system\DateFormatInterface;
use Drupal\system\Form\ModulesInstallConfirmForm;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Drupal\Core\Datetime\DrupalDateTime;
/**
* Menu callback; Provide the administration overview page.
@ -331,203 +329,6 @@ function _system_is_incompatible(&$incompatible, $files, $file) {
}
}
/**
* Form constructor for the module enable/disable interface.
*
* The list of modules gets populated by module.info.yml files, which contain
* each module's name, description, and information about which modules it
* requires.
* See drupal_parse_info_file() for information on module.info.yml descriptors.
*
* Dependency checking is performed to ensure that a module:
* - can not be enabled if there are disabled modules it requires.
* - can not be disabled if there are enabled modules which depend on it.
*
* @see system_menu()
* @see theme_system_modules()
* @see system_modules_submit()
*
* @ingroup forms
*/
function system_modules($form, $form_state = array()) {
// Get current list of modules.
$files = system_rebuild_module_data();
// Remove hidden modules from display list.
$visible_files = $files;
foreach ($visible_files as $filename => $file) {
if (!empty($file->info['hidden'])) {
unset($visible_files[$filename]);
}
}
uasort($visible_files, 'system_sort_modules_by_info_name');
// If the modules form was submitted, then system_modules_submit() runs first
// and if there are unfilled required modules, then $form_state['storage'] is
// filled, triggering a rebuild. In this case we need to display a
// confirmation form.
if (!empty($form_state['storage'])) {
// Contents of confirm form is injected here because already in form
// building function.
$confirm_form = new ModulesInstallConfirmForm();
return $confirm_form->buildForm($form, $form_state, $visible_files, $form_state['storage'], Drupal::request());
}
// JS-only table filters.
$form['filters'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('table-filter', 'js-show'),
),
);
$form['filters']['text'] = array(
'#type' => 'search',
'#title' => t('Search'),
'#size' => 30,
'#placeholder' => t('Enter module name…'),
'#attributes' => array(
'class' => array('table-filter-text'),
'data-table' => '#system-modules',
'autocomplete' => 'off',
'title' => t('Enter a part of the module name or description to filter by.'),
),
);
$modules = array();
$form['modules'] = array('#tree' => TRUE);
// Used when checking if module implements a help page.
$help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
// Used when displaying modules that are required by the installation profile.
require_once DRUPAL_ROOT . '/core/includes/install.inc';
$distribution_name = check_plain(drupal_install_profile_distribution_name());
// Iterate through each of the modules.
foreach ($visible_files as $filename => $module) {
$extra = array();
$extra['enabled'] = (bool) $module->status;
if (!empty($module->info['required'] )) {
$extra['disabled'] = TRUE;
$extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
}
// If this module requires other modules, add them to the array.
foreach ($module->requires as $requires => $v) {
if (!isset($files[$requires])) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
$extra['disabled'] = TRUE;
}
// Only display visible modules.
elseif (isset($visible_files[$requires])) {
$requires_name = $files[$requires]->info['name'];
// Disable this module if it is incompatible with the dependency's version.
if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $requires_name . $incompatible_version,
'@version' => $files[$requires]->info['version'],
));
$extra['disabled'] = TRUE;
}
// Disable this module if the dependency is incompatible with this
// version of Drupal core.
elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
'@module' => $requires_name,
));
$extra['disabled'] = TRUE;
}
elseif ($files[$requires]->status) {
$extra['requires'][$requires] = t('@module', array('@module' => $requires_name));
}
else {
$extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
}
}
}
// Generate link for module's help page, if there is one.
if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
$extra['links']['help'] = array(
'#type' => 'link',
'#title' => t('Help'),
'#href' => "admin/help/$filename",
'#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => t('Help'))),
);
}
}
// Generate link for module's permission, if the user has access to it.
if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
$extra['links']['permissions'] = array(
'#type' => 'link',
'#title' => t('Permissions'),
'#href' => 'admin/people/permissions',
'#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))),
);
}
// Generate link for module's configuration page, if the module provides
// one.
if ($module->status && isset($module->info['configure'])) {
$configure_link = menu_get_item($module->info['configure']);
if ($configure_link['access']) {
$extra['links']['configure'] = array(
'#type' => 'link',
'#title' => t('Configure'),
'#href' => $configure_link['href'],
'#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])),
);
}
}
// If this module is required by other modules, list those, and then make it
// impossible to disable this one.
foreach ($module->required_by as $required_by => $v) {
// Hidden modules are unset already.
if (isset($visible_files[$required_by])) {
if ($files[$required_by]->status == 1 && $module->status == 1) {
$extra['required_by'][] = t('@module', array('@module' => $files[$required_by]->info['name']));
$extra['disabled'] = TRUE;
}
else {
$extra['required_by'][] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$required_by]->info['name']));
}
}
}
$form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
}
// Add basic information to the details.
foreach (element_children($form['modules']) as $package) {
$form['modules'][$package] += array(
'#type' => 'details',
'#title' => t($package),
'#theme' => 'system_modules_details',
'#header' => array(
array('data' => t('<span class="visually-hidden">Enabled</span>'), 'class' => array('checkbox')),
array('data' => t('Name'), 'class' => array('name')),
array('data' => t('Description'), 'class' => array('description', RESPONSIVE_PRIORITY_LOW)),
),
'#attributes' => array('class' => array('package-listing')),
// Ensure that the "Core" package comes first.
'#weight' => $package == 'Core' ? -10 : NULL,
);
}
// Lastly, sort all packages by title.
uasort($form['modules'], 'element_sort_by_title');
$form['#attached']['library'][] = array('system', 'drupal.system.modules');
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
$form['#action'] = url('admin/modules/list/confirm');
return $form;
}
/**
* Array sorting callback; sorts modules or themes by their name.
*/
@ -548,221 +349,6 @@ function system_sort_themes($a, $b) {
return strcasecmp($a->info['name'], $b->info['name']);
}
/**
* Build a table row for the system modules page.
*/
function _system_modules_build_row($info, $extra) {
// Add in the defaults.
$extra += array(
'requires' => array(),
'required_by' => array(),
'disabled' => FALSE,
'enabled' => FALSE,
'links' => array(),
);
$form = array(
'#tree' => TRUE,
);
// Set the basic properties.
$form['name'] = array(
'#markup' => $info['name'],
);
$form['description'] = array(
'#markup' => t($info['description']),
);
$form['version'] = array(
'#markup' => $info['version'],
);
$form['#requires'] = $extra['requires'];
$form['#required_by'] = $extra['required_by'];
// Check the compatibilities.
$compatible = TRUE;
$status_short = '';
$status_long = '';
// Check the core compatibility.
if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
$compatible = FALSE;
$status_short .= t('Incompatible with this version of Drupal core.');
$status_long .= t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
}
// Ensure this module is compatible with the currently installed version of PHP.
if (version_compare(phpversion(), $info['php']) < 0) {
$compatible = FALSE;
$status_short .= t('Incompatible with this version of PHP');
$php_required = $info['php'];
if (substr_count($info['php'], '.') < 2) {
$php_required .= '.*';
}
$status_long .= t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
}
// If this module is compatible, present a checkbox indicating
// this module may be installed. Otherwise, show a big red X.
if ($compatible) {
$form['enable'] = array(
'#type' => 'checkbox',
'#title' => t('Enable'),
'#default_value' => $extra['enabled'],
'#attributes' => array('role' => 'disabled'),
);
if ($extra['disabled']) {
$form['enable']['#disabled'] = TRUE;
}
}
else {
$form['enable'] = array(
'#markup' => theme('image', array('uri' => 'core/misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)),
);
$form['description']['#markup'] .= theme('system_modules_incompatible', array('message' => $status_long));
}
// Build operation links.
foreach (array('help', 'permissions', 'configure') as $key) {
$form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array());
}
return $form;
}
/**
* Submit callback; handles modules form submission.
*/
function system_modules_submit($form, &$form_state) {
include_once DRUPAL_ROOT . '/core/includes/install.inc';
// Builds list of modules.
$modules = array();
// If we're not coming from the confirmation form, build the list of modules.
if (empty($form_state['storage'])) {
// If we're not coming from the confirmation form, build the module list.
foreach ($form_state['values']['modules'] as $group_name => $group) {
foreach ($group as $module => $enabled) {
$modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
}
}
}
else {
// If we are coming from the confirmation form, fetch
// the modules out of $form_state.
$modules = $form_state['storage']['modules'];
}
// Collect data for all modules to be able to determine dependencies.
$files = system_rebuild_module_data();
// Sorts modules by weight.
$sort = array();
foreach (array_keys($modules) as $module) {
$sort[$module] = $files[$module]->sort;
}
array_multisort($sort, $modules);
// Makes sure all required modules are set to be enabled.
$more_required = array();
$missing_modules = array();
foreach ($modules as $name => $module) {
if ($module['enabled']) {
// Checks that all dependencies are set to be enabled. Stores the ones
// that are not in $dependencies variable so that the user can be alerted
// in the confirmation form that more modules need to be enabled.
$dependencies = array();
foreach (array_keys($files[$name]->requires) as $required) {
if (empty($modules[$required]['enabled'])) {
if (isset($files[$required])) {
$dependencies[] = $files[$required]->info['name'];
$modules[$required]['enabled'] = TRUE;
}
else {
$missing_modules[$required]['depends'][] = $name;
$modules[$name]['enabled'] = FALSE;
}
}
}
// Stores additional modules that need to be enabled in $more_required.
if (!empty($dependencies)) {
$more_required[$name] = array(
'name' => $files[$name]->info['name'],
'requires' => $dependencies,
);
}
}
}
// Redirects to confirmation form if more modules need to be enabled.
if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) {
$form_state['storage'] = array(
'more_required' => $more_required,
'modules' => $modules,
'missing_modules' => $missing_modules,
);
$form_state['rebuild'] = TRUE;
return;
}
// Invokes hook_requirements('install'). If failures are detected, makes sure
// the dependent modules aren't installed either.
foreach ($modules as $name => $module) {
// Only invoke hook_requirements() on modules that are going to be installed.
if ($module['enabled'] && drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
if (!drupal_check_module($name)) {
$modules[$name]['enabled'] = FALSE;
foreach (array_keys($files[$name]->required_by) as $required_by) {
$modules[$required_by]['enabled'] = FALSE;
}
}
}
}
// Initializes array of actions.
$actions = array(
'enable' => array(),
'disable' => array(),
'install' => array(),
);
// Builds arrays of modules that need to be enabled, disabled, and installed.
foreach ($modules as $name => $module) {
if ($module['enabled']) {
if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
$actions['install'][] = $name;
$actions['enable'][] = $name;
}
elseif (!module_exists($name)) {
$actions['enable'][] = $name;
}
}
elseif (module_exists($name)) {
$actions['disable'][] = $name;
}
}
// Gets list of modules prior to install process, unsets $form_state['storage']
// so we don't get redirected back to the confirmation form.
$pre_install_list = Drupal::moduleHandler()->getModuleList();
unset($form_state['storage']);
// Reverse the 'enable' list, to order dependencies before dependents.
krsort($actions['enable']);
// Installs, enables, and disables modules.
module_enable($actions['enable'], FALSE);
module_disable($actions['disable'], FALSE);
// Gets module list after install process, flushes caches and displays a
// message if there are changes.
$post_install_list = Drupal::moduleHandler()->getModuleList();
if ($pre_install_list != $post_install_list) {
drupal_flush_all_caches();
drupal_set_message(t('The configuration options have been saved.'));
}
$form_state['redirect'] = 'admin/modules';
}
/**
* Default page callback for batches.
*/

View File

@ -719,10 +719,7 @@ function system_menu() {
$items['admin/modules'] = array(
'title' => 'Extend',
'description' => 'Add and enable modules to extend site functionality.',
'page callback' => 'drupal_get_form',
'page arguments' => array('system_modules'),
'access arguments' => array('administer modules'),
'file' => 'system.admin.inc',
'route_name' => 'system_modules_list',
'weight' => -2,
);
$items['admin/modules/list'] = array(
@ -731,7 +728,7 @@ function system_menu() {
);
$items['admin/modules/list/confirm'] = array(
'title' => 'List',
'access arguments' => array('administer modules'),
'route_name' => 'system_modules_list_confirm',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
$items['admin/modules/uninstall'] = array(

View File

@ -116,6 +116,20 @@ date_format_localize_reset:
requirements:
_permission: 'administer site configuration'
system_modules_list:
pattern: 'admin/modules'
defaults:
_form: 'Drupal\system\Form\ModulesListForm'
requirements:
_permission: 'administer modules'
system_modules_list_confirm:
pattern: 'admin/modules/list/confirm'
defaults:
_form: 'Drupal\system\Form\ModulesListConfirmForm'
requirements:
_permission: 'administer modules'
system_theme_disable:
pattern: '/admin/appearance/disable'
defaults: