summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2013-07-19 10:17:03 (GMT)
committerAlex Pott2013-07-19 10:17:03 (GMT)
commit8d693aded0f69de6fd05a4ff45657231608078e3 (patch)
tree8109c7eb7bcdcb9a8b0e9c6f09ac667d12ef33e7
parent862b4d63478a2ba1344b4f192c3fefeaff28abed (diff)
Issue #1990544 by fubhy, Pancho, h3rj4n, dawehner: Convert system_modules() to a Controller.
-rw-r--r--core/lib/Drupal/Core/Database/Driver/mysql/Connection.php13
-rw-r--r--core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php95
-rw-r--r--core/modules/system/lib/Drupal/system/Form/ModulesListConfirmForm.php190
-rw-r--r--core/modules/system/lib/Drupal/system/Form/ModulesListForm.php458
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php2
-rw-r--r--core/modules/system/system.admin.inc414
-rw-r--r--core/modules/system/system.module7
-rw-r--r--core/modules/system/system.routing.yml14
8 files changed, 678 insertions, 515 deletions
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
index 92142e8..1922e6d 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
@@ -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();
diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php
deleted file mode 100644
index 23d3ada..0000000
--- a/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php
+++ /dev/null
@@ -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) {
- }
-
-}
diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesListConfirmForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesListConfirmForm.php
new file mode 100644
index 0000000..3b69f8c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Form/ModulesListConfirmForm.php
@@ -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();
+ }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php
new file mode 100644
index 0000000..e8595ca
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php
@@ -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.'));
+ }
+ }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
index 6b3383b..59dc6a2 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
@@ -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'));
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 9b484ce..aa36ba7 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -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.
@@ -332,203 +330,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.
*/
function system_sort_modules_by_info_name($a, $b) {
@@ -549,221 +350,6 @@ function system_sort_themes($a, $b) {
}
/**
- * 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.
*/
function system_batch_page() {
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 72cb3bf..e771d4e 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -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(
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 1360262..0785e5c 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -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: