diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php index ecd464a..f8c681f 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php @@ -70,19 +70,19 @@ function testCommentEnable() { // Disable the comment module. $edit = array(); - $edit['modules[Core][comment][enable]'] = FALSE; + $edit['modules[comment][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->rebuildContainer(); $this->assertFalse(module_exists('comment'), 'Comment module disabled.'); // Enable core content type module (book). $edit = array(); - $edit['modules[Core][book][enable]'] = 'book'; + $edit['modules[book][enable]'] = 'book'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); // Now enable the comment module. $edit = array(); - $edit['modules[Core][comment][enable]'] = 'comment'; + $edit['modules[comment][enable]'] = 'comment'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->rebuildContainer(); $this->assertTrue(module_exists('comment'), 'Comment module enabled.'); diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php index 9cd6ae9..1dcb5ea 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php @@ -114,7 +114,7 @@ function testEnableForumField() { // Disable the Forum module. $edit = array(); - $edit['modules[Core][forum][enable]'] = FALSE; + $edit['modules[forum][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->rebuildContainer(); @@ -123,7 +123,7 @@ function testEnableForumField() { // Attempt to re-enable the Forum module and ensure it does not try to // recreate the taxonomy_forums field. $edit = array(); - $edit['modules[Core][forum][enable]'] = 'forum'; + $edit['modules[forum][enable]'] = 'forum'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->rebuildContainer(); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index 951fc88..95d6af9 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -95,7 +95,7 @@ function testConfigTranslation() { $this->assertFalse($string, 'Configuration strings have been created upon installation.'); // Enable the image module. - $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => "1"), t('Save configuration')); + $this->drupalPost('admin/modules', array('modules[image][enable]' => "1"), t('Save configuration')); $this->resetAll(); $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration')); @@ -139,7 +139,7 @@ function testConfigTranslation() { $this->assertEqual(config('locale.config.xx.image.style.medium')->get('label'), $image_style_label); // Disable and uninstall the module. - $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => FALSE), t('Save configuration')); + $this->drupalPost('admin/modules', array('modules[image][enable]' => FALSE), t('Save configuration')); $this->drupalPost('admin/modules/uninstall', array('uninstall[image]' => "image"), t('Uninstall')); $this->drupalPost(NULL, array(), t('Uninstall')); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php index 22aedb4..f072b35 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php @@ -27,7 +27,7 @@ public static function getInfo() { function testNodeTypeCustomizationPersistence() { $web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types', 'administer modules')); $this->drupalLogin($web_user); - $forum_key = 'modules[Core][forum][enable]'; + $forum_key = 'modules[forum][enable]'; $forum_enable = array($forum_key => "1"); $forum_disable = array($forum_key => FALSE); diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php index 23d3ada..9450ae7 100644 --- a/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ModulesInstallConfirmForm.php @@ -7,7 +7,12 @@ namespace Drupal\system\Form; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\ConfirmFormBase; +use Drupal\user\TempStoreFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; /** @@ -15,20 +20,57 @@ * * Used internally in system_modules(). */ -class ModulesInstallConfirmForm extends ConfirmFormBase { +class ModulesInstallConfirmForm extends ConfirmFormBase implements ControllerInterface { + + /** + * The array of modules to install. + * + * @var array + */ + protected $modules = array(); + + /** + * The tempstore. + * + * @var \Drupal\user\TempStore + */ + protected $tempStore; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Constructs a ModulesInstallConfirmForm form object. + * + * @param \Drupal\user\TempStoreFactory $temp_store_factory + * The tempstore factory. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + */ + public function __construct(TempStoreFactory $temp_store_factory, ModuleHandlerInterface $module_handler) { + $this->tempStore = $temp_store_factory->get('modules_install_confirm'); + $this->moduleHandler = $module_handler; + } /** * {@inheritdoc} */ - public function getQuestion() { - return t('Some required modules must be enabled'); + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.tempstore'), + $container->get('module_handler') + ); } /** * {@inheritdoc} */ - public function getConfirmText() { - return t('Continue'); + public function getQuestion() { + return t('Some required modules must be enabled'); } /** @@ -54,34 +96,50 @@ public function getFormID() { /** * {@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; + public function buildForm(array $form, array &$form_state, Request $request = NULL) { + if (!$this->modules = $this->tempStore->get('modules')) { + return new RedirectResponse(url($this->getCancelPath(), array('absolute' => 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); + // Make sure that we do not try to enable or disable any modules that, for + // whatever reason, disappeared in the meantime or lacks a dependency. + $modules = system_rebuild_module_data(); + foreach (array('enable', 'disable') as $action) { + if (!empty($this->modules[$action])) { + $this->modules[$action] = array_filter($this->modules['enable'], function ($module) use ($modules) { + $missing_dependencies = array_diff_key($modules[$module]->requires, $modules); + return isset($modules[$module]) && empty($missing_dependencies); + }); + } } - 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); + $items = array(); + // Display a list of required modules that have to be installed as well but + // were not manually selected. + // @todo Add the same for modules to be disabled. + if (!empty($this->modules['enable'])) { + foreach ($this->modules['enable'] as $module) { + $dependencies = array(); + foreach (array_keys($modules[$module]->requires) as $dependency) { + if (!in_array($dependency, $this->modules['enable']) && !$this->moduleHandler->moduleExists($dependency)) { + $dependencies[$dependency] = $modules[$dependency]->info['name']; + } + } + + if (!empty($dependencies)) { + $items['dependencies'] = format_plural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', array( + '@module' => $modules[$module]->info['name'], + '@required' => implode(', ', $dependencies), + )); + } + } } - $form['modules'] = array('#theme' => 'item_list', '#items' => $items); + $form['message'] = array( + '#theme' => 'item_list', + '#items' => $items, + ); return parent::buildForm($form, $form_state, $request); } @@ -90,6 +148,54 @@ public function buildForm(array $form, array &$form_state, $modules = array(), $ * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { + // Remove the tempstore entry. + $this->tempStore->delete('modules'); + + if ($form_state['values']['confirm'] && $this->modules) { + // Make sure the install API is available. + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + + // Make sure that we do not try to enable or disable any modules that, for + // whatever reason, disappeared in the meantime. + $modules = system_rebuild_module_data(); + foreach (array('enable', 'disable') as $action) { + if (!empty($this->modules[$action])) { + $this->modules[$action] = array_filter($this->modules['enable'], function ($module) use ($modules) { + return isset($modules[$module]); + }); + } + } + + // Invoke hook_requirements('install'). If failures are detected, make + // sure the dependent modules aren't installed either. + foreach ($this->modules['enable'] as $name) { + // Only invoke hook_requirements() on modules that are going to be + // installed. + if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) { + if (!drupal_check_module($name)) { + unset($this->modules['enable'][$name]); + foreach (array_keys($modules[$name]->required_by) as $dependent) { + unset($this->modules['enable'][$dependent]); + } + } + } + } + + // Installs, enables, and disables modules. + if (!empty($this->modules['enable'])) { + $this->moduleHandler->enable($this->modules['enable']); + } + if (!empty($this->modules['disable'])) { + $this->moduleHandler->disable($this->modules['disable']); + } + + // Gets module list after install process, flushes caches and displays a + // message if there are changes. + drupal_flush_all_caches(); + drupal_set_message(t('The configuration options have been saved.')); + + $form_state['redirect'] = 'admin/modules'; + } } } 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..12065eb --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php @@ -0,0 +1,318 @@ +get('module_handler'), + $container->get('user.tempstore') + ); + } + + /** + * Constructs a ModulesListForm object. + * + * @param \Drupal\user\TempStoreFactory $temp_store_factory + * @param ModuleHandlerInterface $module_handler + * The module handler to use. + */ + public function __construct(ModuleHandlerInterface $module_handler, TempStoreFactory $temp_store_factory) { + $this->tempStore = $temp_store_factory->get('modules_install_confirm'); + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public function getFormID() { + return 'system_modules'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, array &$form_state) { + // Include system.admin.inc so we can use the sort callbacks. + $this->moduleHandler->loadInclude('system', 'inc', 'system.admin'); + + $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.'), + ), + ); + + // 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. + foreach ($modules as $filename => $module) { + if (empty($module->info['hidden'])) { + $package = $module->info['package']; + $form['modules'][$package][$filename] = $this->buildRow($modules, $module); + $form['modules'][$package][$filename]['#tree'] = TRUE; + $form['modules'][$package][$filename]['enable']['#parents'] = array('modules', $filename); + } + } + + // Add a wrapper around every package. + 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('Enabled') . '', 'class' => array('checkbox')), + array('data' => t('Name'), 'class' => array('name')), + array('data' => t('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' => t('Save configuration'), + ); + + return $form; + } + + /** + * Build a table row for the system modules page. + */ + protected function buildRow($modules, $module) { + // Set the basic properties. + $row['#required'] = array(); + $row['#dependencies'] = array(); + $row['#dependents'] = array(); + + $row['name']['#markup'] = $module->info['name']; + $row['description']['#markup'] = t($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' => t('Help'), + '#href' => "admin/help/$module->name", + '#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. + $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' => t('Permissions'), + '#href' => 'admin/people/permissions', + '#options' => array('fragment' => 'module-' . $module->name, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('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' => t('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' => t('Enable'), + '#default_value' => (bool) $module->status, + ); + + // Disable the checkbox for required modules. + if (!empty($module->info['required'])) { + $row['enable']['#disabled'] = TRUE; + $row['#required'] = (!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 .= 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(), $module->info['php']) < 0) { + $compatible = FALSE; + $required = $module->info['php'] . (substr_count($module->info['php'], '.') < 2 ? '.*' : ''); + $status .= t('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']['#markup'] .= theme('system_modules_incompatible', array( + 'message' => $status, + )); + } + + // If this module requires other modules, add them to the array. + foreach ($module->requires as $dependency => $version) { + if (!isset($modules[$dependency])) { + $required[$dependency] = t('@module (missing)', array('@module' => Unicode::ucfirst($dependency))); + } + // Only display visible modules. + elseif (empty($modules[$dependency]->info['hidden'])) { + $name = $modules[$dependency]->info['name']; + + // Disable the checkbox module if it is incompatible with the + // dependency's version. + if (drupal_check_incompatibility($version, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $modules[$dependency]->info['version']))) { + $row['#dependencies'][$dependency] = t('@module (incompatible with version @version)', array( + '@module' => "$name $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['#dependencies'][$dependency] = t('@module (incompatible with this version of Drupal core)', array( + '@module' => $name, + )); + $row['enable']['#disabled'] = TRUE; + } + elseif ($modules[$dependency]->status) { + $row['#dependencies'][$dependency] = t('@module', array('@module' => $name)); + } + else { + $row['#dependencies'][$dependency] = t('@module (disabled)', 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['#dependents'][$dependent] = t('@module', array('@module' => $modules[$dependent]->info['name'])); + $row['enable']['#disabled'] = TRUE; + } + else { + $row['#dependents'][$dependent] = t('@module (disabled)', array('@module' => $modules[$dependent]->info['name'])); + } + } + } + + return $row; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + $modules = array(); + foreach ($form_state['values']['modules'] as $name => $checked) { + if ($checked && !$this->moduleHandler->moduleExists($name)) { + $modules['enable'][] = $name; + } + elseif (!$checked && $this->moduleHandler->moduleExists($name)) { + $modules['disable'][] = $name; + } + } + + if ($modules) { + // Write the list of changed module states into temp store. + $this->tempStore->set('modules', $modules); + + // Redirect to the confirmation form. + $form_state['redirect'] = 'admin/modules/list/confirm'; + } + } + +} 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 0143d1a..3b80429 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -144,18 +144,18 @@ function testModuleEnableOrder() { // Enable the modules through the UI, verifying that the dependency chain // is correct. $edit = array(); - $edit['modules[Core][forum][enable]'] = 'forum'; + $edit['modules[forum][enable]'] = 'forum'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertModules(array('forum'), FALSE); $this->assertText(t('You must enable the History, Taxonomy, Options, Number, Comment, Datetime, Ban, PHP Filter modules to install Forum.')); - $edit['modules[Core][history][enable]'] = 'history'; - $edit['modules[Core][options][enable]'] = 'options'; - $edit['modules[Core][number][enable]'] = 'number'; - $edit['modules[Core][taxonomy][enable]'] = 'taxonomy'; - $edit['modules[Core][comment][enable]'] = 'comment'; - $edit['modules[Core][datetime][enable]'] = 'datetime'; - $edit['modules[Core][ban][enable]'] = 'ban'; - $edit['modules[Core][php][enable]'] = 'php'; + $edit['modules[history][enable]'] = 'history'; + $edit['modules[options][enable]'] = 'options'; + $edit['modules[number][enable]'] = 'number'; + $edit['modules[taxonomy][enable]'] = 'taxonomy'; + $edit['modules[comment][enable]'] = 'comment'; + $edit['modules[datetime][enable]'] = 'datetime'; + $edit['modules[ban][enable]'] = 'ban'; + $edit['modules[php][enable]'] = 'php'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertModules(array('forum', 'ban', 'php', 'datetime', 'comment', 'history', 'taxonomy', 'options', 'number'), TRUE); @@ -169,16 +169,16 @@ function testModuleEnableOrder() { */ function testUninstallDependents() { // Enable the forum module. - $edit = array('modules[Core][forum][enable]' => 'forum'); + $edit = array('modules[forum][enable]' => 'forum'); $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->drupalPost(NULL, array(), t('Continue')); $this->assertModules(array('forum'), TRUE); // Disable forum and comment. Both should now be installed but disabled. - $edit = array('modules[Core][forum][enable]' => FALSE); + $edit = array('modules[forum][enable]' => FALSE); $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertModules(array('forum'), FALSE); - $edit = array('modules[Core][comment][enable]' => FALSE); + $edit = array('modules[comment][enable]' => FALSE); $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertModules(array('comment'), FALSE); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php index 8458bc7..33502f5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php @@ -95,11 +95,7 @@ function testEnableDisable() { $package = $module->info['package']; $edit['modules[' . $package . '][' . $name . '][enable]'] = $name; $this->drupalPost('admin/modules', $edit, t('Save configuration')); - // Handle the case where modules were installed along with this one and - // where we therefore hit a confirmation screen. - if (count($modules_to_enable) > 1) { - $this->drupalPost(NULL, array(), t('Continue')); - } + $this->drupalPost(NULL, array(), t('Continue')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); // Check that hook_modules_installed() and hook_modules_enabled() were @@ -181,10 +177,10 @@ function testEnableDisable() { * (optional) The package of the module to disable and uninstall. Defaults * to 'Core'. */ - function assertSuccessfulDisableAndUninstall($module, $package = 'Core') { + function assertSuccessfulDisableAndUninstall($module) { // Disable the module. $edit = array(); - $edit['modules[' . $package . '][' . $module . '][enable]'] = FALSE; + $edit['modules[' . $module . '][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->assertModules(array($module), FALSE); diff --git a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php index 4e79ae9..11750b1 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php @@ -53,7 +53,7 @@ function setUp() { function testMainContentFallback() { $edit = array(); // Disable the block module. - $edit['modules[Core][block][enable]'] = FALSE; + $edit['modules[block][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->rebuildContainer(); @@ -87,7 +87,7 @@ function testMainContentFallback() { // Enable the block module again. $this->drupalLogin($this->admin_user); $edit = array(); - $edit['modules[Core][block][enable]'] = 'block'; + $edit['modules[block][enable]'] = 'block'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->rebuildContainer(); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 957db86..8e9433a 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -333,202 +333,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 (missing)', 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 (incompatible with 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 (incompatible with 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 (disabled)', 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 (disabled)', 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('Enabled'), 'class' => array('checkbox')), - array('data' => t('Name'), 'class' => array('name')), - array('data' => t('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' => 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 +353,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'; -} - -/** * Uninstall functions */ @@ -1146,47 +735,52 @@ function theme_system_modules_details($variables) { $module = $form[$key]; // Create the row for the table. $row = array(); - // Add the checkbox into the first cell. - unset($module['enable']['#title']); - $module['#requires'] = array_filter($module['#requires']); - $module['#required_by'] = array_filter($module['#required_by']); + $module['#dependencies'] = array_filter($module['#dependencies']); + $module['#dependents'] = array_filter($module['#dependents']); - $requires = !empty($module['#requires']); - $required_by = !empty($module['#required_by']); + $dependencies = !empty($module['#dependencies']); + $dependents = !empty($module['#dependents']); $version = !empty($module['version']['#markup']); - $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable'])); + // Add the checkbox into the first cell. + unset($module['enable']['#title']); + $row[] = array( + 'class' => array('checkbox'), + 'data' => drupal_render($module['enable']), + ); - // Add the module label and expand/collapse functionalty. - $col2 = ''; - $row[] = array('class' => array('module'), 'data' => $col2); + // Add the module label and expand/collapse functionality. + $row[] = array( + 'class' => array('module'), + 'data' => '', + ); // Add the description, along with any modules it requires. $description = '' . drupal_render($module['description']) . ''; - if ($version || $requires || $required_by) { - $description .= '
'; + if ($version || $dependencies || $dependents) { + $description .= '
'; if ($version) { $description .= '
' . t('Version: !module-version', array('!module-version' => drupal_render($module['version']))) . '
'; } - if ($requires) { - $description .= '
' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '
'; + if ($dependencies) { + $description .= '
' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#dependencies']))) . '
'; } - if ($required_by) { - $description .= '
' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '
'; + if ($dependents) { + $description .= '
' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#dependents']))) . '
'; } $description .= '
'; } - $links = ''; - foreach (array('help', 'permissions', 'configure') as $key) { - $links .= drupal_render($module['links'][$key]); - } - if ($links) { - $description .= '