Skip to content
Snippets Groups Projects
Commit 1b4c4238 authored by catch's avatar catch
Browse files

Issue #3123537 by tedbow, dww, DamienMcKenna, xjm, Berdir, tim.plunkett,...

Issue #3123537 by tedbow, dww, DamienMcKenna, xjm, Berdir, tim.plunkett, pingwin4eg: /admin/reports/status should not WSOD if it finds an extension missing core_version_requirement in its info.yml file
parent 15c4512b
No related branches found
No related tags found
8 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!1012Issue #3226887: Hreflang on non-canonical content pages,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10,!596Issue #3046532: deleting an entity reference field, used in a contextual view, makes the whole site unrecoverable,!496Issue #2463967: Use .user.ini file for PHP settings,!144Issue #2666286: Clean up menu_ui to conform to Drupal coding standards,!16Draft: Resolve #2081585 "History storage",!13Resolve #2903456
...@@ -68,7 +68,7 @@ public function parse($filename) { ...@@ -68,7 +68,7 @@ public function parse($filename) {
// easier for contrib to use test modules. // easier for contrib to use test modules.
$parsed_info['core_version_requirement'] = \Drupal::VERSION; $parsed_info['core_version_requirement'] = \Drupal::VERSION;
} }
else { elseif (!isset($parsed_info['core'])) {
// Non-core extensions must specify core compatibility. // Non-core extensions must specify core compatibility.
throw new InfoParserException("The 'core_version_requirement' key must be present in " . $filename); throw new InfoParserException("The 'core_version_requirement' key must be present in " . $filename);
} }
......
...@@ -209,11 +209,19 @@ public function themesPage() { ...@@ -209,11 +209,19 @@ public function themesPage() {
$theme_groups = ['installed' => [], 'uninstalled' => []]; $theme_groups = ['installed' => [], 'uninstalled' => []];
$admin_theme = $config->get('admin'); $admin_theme = $config->get('admin');
$admin_theme_options = []; $admin_theme_options = [];
$incompatible_installed = FALSE;
foreach ($themes as &$theme) { foreach ($themes as &$theme) {
if (!empty($theme->info['hidden'])) { if (!empty($theme->info['hidden'])) {
continue; continue;
} }
if (!$incompatible_installed && $theme->info['core_incompatible'] && $theme->status) {
$incompatible_installed = TRUE;
$this->messenger()->addWarning($this->t(
'There are errors with some installed themes. Visit the <a href=":link">status report page</a> for more information.',
[':link' => Url::fromRoute('system.status')->toString()]
));
}
$theme->is_default = ($theme->getName() == $theme_default); $theme->is_default = ($theme->getName() == $theme_default);
$theme->is_admin = ($theme->getName() == $admin_theme || ($theme->is_default && empty($admin_theme))); $theme->is_admin = ($theme->getName() == $admin_theme || ($theme->is_default && empty($admin_theme)));
$theme->is_experimental = isset($theme->info['experimental']) && $theme->info['experimental']; $theme->is_experimental = isset($theme->info['experimental']) && $theme->info['experimental'];
......
...@@ -171,12 +171,20 @@ public function buildForm(array $form, FormStateInterface $form_state) { ...@@ -171,12 +171,20 @@ public function buildForm(array $form, FormStateInterface $form_state) {
// Iterate over each of the modules. // Iterate over each of the modules.
$form['modules']['#tree'] = TRUE; $form['modules']['#tree'] = TRUE;
$incompatible_installed = FALSE;
foreach ($modules as $filename => $module) { foreach ($modules as $filename => $module) {
if (empty($module->info['hidden'])) { if (empty($module->info['hidden'])) {
$package = $module->info['package']; $package = $module->info['package'];
$form['modules'][$package][$filename] = $this->buildRow($modules, $module, $distribution); $form['modules'][$package][$filename] = $this->buildRow($modules, $module, $distribution);
$form['modules'][$package][$filename]['#parents'] = ['modules', $filename]; $form['modules'][$package][$filename]['#parents'] = ['modules', $filename];
} }
if (!$incompatible_installed && $module->status && $module->info['core_incompatible']) {
$incompatible_installed = TRUE;
$this->messenger()->addWarning($this->t(
'There are errors with some installed modules. Visit the <a href=":link">status report page</a> for more information.',
[':link' => Url::fromRoute('system.status')->toString()]
));
}
} }
// Add a wrapper around every package. // Add a wrapper around every package.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\Tests\system\Functional\Form; namespace Drupal\Tests\system\Functional\Form;
use Drupal\Core\Serialization\Yaml;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
/** /**
...@@ -73,9 +74,7 @@ public function testModulesListFormWithInvalidInfoFile() { ...@@ -73,9 +74,7 @@ public function testModulesListFormWithInvalidInfoFile() {
type: module type: module
core: 9.x core: 9.x
BROKEN, BROKEN,
// Checking for 'core_version_requirement' is done before checking 'expected_error' => "'core: 9.x' is not supported. Use 'core_version_requirement' to specify core compatibility. Only 'core: 8.x' is supported to provide backwards compatibility for Drupal 8 when needed in $file_path",
// for a valid 'core' value.
'expected_error' => "The 'core_version_requirement' key must be present in $file_path",
], ],
[ [
'yml' => <<<BROKEN 'yml' => <<<BROKEN
...@@ -123,4 +122,56 @@ public function testRequiredByThemeMessage() { ...@@ -123,4 +122,56 @@ public function testRequiredByThemeMessage() {
$this->assertSession()->pageTextContains('Test Theme Depending on Modules (Theme) (Disabled)'); $this->assertSession()->pageTextContains('Test Theme Depending on Modules (Theme) (Disabled)');
} }
/**
* Tests that incompatible modules message is shown.
*/
public function testInstalledIncompatibleModule() {
$incompatible_modules_message = 'There are errors with some installed modules. Visit the status report page for more information.';
$path = \Drupal::getContainer()->getParameter('site.path') . "/modules/changing_module";
mkdir($path, 0777, TRUE);
$file_path = "$path/changing_module.info.yml";
$info = [
'name' => 'Module that changes',
'type' => 'module',
];
$compatible_info = $info + ['core_version_requirement' => '*'];
file_put_contents($file_path, Yaml::encode($compatible_info));
$edit = ['modules[changing_module][enable]' => 'changing_module'];
$this->drupalGet('admin/modules');
$this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->assertText('Module Module that changes has been enabled.');
$incompatible_updates = [
[
'core_version_requirement' => '^1',
],
[
'core' => '8.x',
],
];
foreach ($incompatible_updates as $incompatible_update) {
$incompatible_info = $info + $incompatible_update;
file_put_contents($file_path, Yaml::encode($incompatible_info));
$this->drupalGet('admin/modules');
$this->assertText($incompatible_modules_message);
file_put_contents($file_path, Yaml::encode($compatible_info));
$this->drupalGet('admin/modules');
$this->assertNoText($incompatible_modules_message);
}
// Uninstall the module and ensure that incompatible modules message is not
// displayed for modules that are not installed.
$edit = ['uninstall[changing_module]' => 'changing_module'];
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText('The selected modules have been uninstalled.');
foreach ($incompatible_updates as $incompatible_update) {
$incompatible_info = $info + $incompatible_update;
file_put_contents($file_path, Yaml::encode($incompatible_info));
$this->drupalGet('admin/modules');
$this->assertNoText($incompatible_modules_message);
}
}
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\Tests\system\Functional\Theme; namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Core\Serialization\Yaml;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
/** /**
...@@ -318,4 +319,59 @@ public function testInstallModuleWithIncompatibleDependencies() { ...@@ -318,4 +319,59 @@ public function testInstallModuleWithIncompatibleDependencies() {
$this->assertStringContainsString('This theme requires the listed modules to operate correctly.', $theme_container->getText()); $this->assertStringContainsString('This theme requires the listed modules to operate correctly.', $theme_container->getText());
} }
/**
* Tests that incompatible themes message is shown.
*/
public function testInstalledIncompatibleTheme() {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$incompatitable_themes_message = 'There are errors with some installed themes. Visit the status report page for more information.';
$path = \Drupal::getContainer()->getParameter('site.path') . "/themes/changing_theme";
mkdir($path, 0777, TRUE);
$file_path = "$path/changing_theme.info.yml";
$theme_name = 'Theme that changes';
$info = [
'name' => $theme_name,
'type' => 'theme',
'base theme' => FALSE,
];
$compatible_info = $info + ['core_version_requirement' => '*'];
file_put_contents($file_path, Yaml::encode($compatible_info));
$this->drupalGet('admin/appearance');
$this->assertNoText($incompatitable_themes_message);
$page->clickLink("Install $theme_name theme");
$assert_session->addressEquals('admin/appearance');
$assert_session->pageTextContains("The $theme_name theme has been installed");
$incompatible_updates = [
[
'core_version_requirement' => '^1',
],
[
'core' => '8.x',
],
];
foreach ($incompatible_updates as $incompatible_update) {
$incompatible_info = $info + $incompatible_update;
file_put_contents($file_path, Yaml::encode($incompatible_info));
$this->drupalGet('admin/appearance');
$this->assertText($incompatitable_themes_message);
file_put_contents($file_path, Yaml::encode($compatible_info));
$this->drupalGet('admin/appearance');
$this->assertNoText($incompatitable_themes_message);
}
// Uninstall the theme and ensure that incompatible themes message is not
// displayed for themes that are not installed.
$this->uninstallTheme($theme_name);
foreach ($incompatible_updates as $incompatible_update) {
$incompatible_info = $info + $incompatible_update;
file_put_contents($file_path, Yaml::encode($incompatible_info));
$this->drupalGet('admin/appearance');
$this->assertNoText($incompatitable_themes_message);
}
}
} }
...@@ -323,6 +323,28 @@ public function providerExtensionCompatibilityChange() { ...@@ -323,6 +323,28 @@ public function providerExtensionCompatibilityChange() {
], ],
'The following theme is installed, but it is incompatible with PHP ' . phpversion() . ":", 'The following theme is installed, but it is incompatible with PHP ' . phpversion() . ":",
], ],
'module: core_version_requirement key missing' => [
[
'core_version_requirement' => '^8 || ^9',
'type' => 'module',
],
[
'core' => '8.x',
'type' => 'module',
],
$incompatible_module_message,
],
'theme: core_version_requirement key missing' => [
[
'core_version_requirement' => '^8 || ^9',
'type' => 'theme',
],
[
'core' => '8.x',
'type' => 'theme',
],
$incompatible_theme_message,
],
]; ];
} }
......
...@@ -377,7 +377,7 @@ public function testCoreWithoutCoreVersionRequirement($core) { ...@@ -377,7 +377,7 @@ public function testCoreWithoutCoreVersionRequirement($core) {
"core_without_core_version_requirement-$core-duplicate.info.txt" => $core_without_core_version_requirement, "core_without_core_version_requirement-$core-duplicate.info.txt" => $core_without_core_version_requirement,
], ],
]); ]);
$exception_message = "The 'core_version_requirement' key must be present in vfs://modules/fixtures/core_without_core_version_requirement-$core"; $exception_message = "'core: $core' is not supported. Use 'core_version_requirement' to specify core compatibility. Only 'core: 8.x' is supported to provide backwards compatibility for Drupal 8 when needed in vfs://modules/fixtures/core_without_core_version_requirement-$core";
// Set the expected exception for the 2nd call to parse(). // Set the expected exception for the 2nd call to parse().
$this->expectException('\Drupal\Core\Extension\InfoParserException'); $this->expectException('\Drupal\Core\Extension\InfoParserException');
$this->expectExceptionMessage("$exception_message-duplicate.info.txt"); $this->expectExceptionMessage("$exception_message-duplicate.info.txt");
...@@ -397,7 +397,6 @@ public function testCoreWithoutCoreVersionRequirement($core) { ...@@ -397,7 +397,6 @@ public function testCoreWithoutCoreVersionRequirement($core) {
public function providerCoreWithoutCoreVersionRequirement() { public function providerCoreWithoutCoreVersionRequirement() {
return [ return [
'7.x' => ['7.x'], '7.x' => ['7.x'],
'8.x' => ['8.x'],
'9.x' => ['9.x'], '9.x' => ['9.x'],
'10.x' => ['10.x'], '10.x' => ['10.x'],
]; ];
...@@ -663,4 +662,33 @@ public function testUnparsableCoreVersionRequirement() { ...@@ -663,4 +662,33 @@ public function testUnparsableCoreVersionRequirement() {
$this->infoParser->parse(vfsStream::url('modules/fixtures/unparsable_core_version_requirement.info.txt')); $this->infoParser->parse(vfsStream::url('modules/fixtures/unparsable_core_version_requirement.info.txt'));
} }
/**
* Tests an info file with 'core: 8.x' but without 'core_version_requirement'.
*
* @covers ::parse
*/
public function testCore8xNoCoreVersionRequirement() {
$info = <<<INFO
package: Core
core: 8.x
version: VERSION
type: module
name: Module for That
dependencies:
- field
INFO;
vfsStream::setup('modules');
foreach (['1', '2'] as $file_delta) {
$filename = "core_version_requirement-$file_delta.info.txt";
vfsStream::create([
'fixtures' => [
$filename => $info,
],
]);
$info_values = $this->infoParser->parse(vfsStream::url("modules/fixtures/$filename"));
$this->assertSame(TRUE, $info_values['core_incompatible'], "Expected 'core_incompatible's for file: $filename");
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment