diff options
Diffstat (limited to 'core/modules')
6 files changed, 127 insertions, 15 deletions
diff --git a/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php b/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php index 16cd486ad750..18b63b8376bd 100644 --- a/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php +++ b/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php @@ -441,6 +441,8 @@ END; $requirements['symfony/polyfill-php81'], $requirements['symfony/polyfill-php82'], $requirements['symfony/polyfill-php83'], + // Needed for PHP 8.4 features while PHP 8.3 is the minimum. + $requirements['symfony/polyfill-php84'], ); // If this package requires any Drupal core packages, ensure it allows // any version. diff --git a/core/modules/system/tests/src/Kernel/Block/StubPathMatcher.php b/core/modules/system/tests/src/Kernel/Block/StubPathMatcher.php new file mode 100644 index 000000000000..c2bd82782b05 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Block/StubPathMatcher.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\system\Kernel\Block; + +use Drupal\Core\Path\PathMatcher; + +/** + * A class extending PatchMatcher for testing purposes. + */ +class StubPathMatcher extends PathMatcher { + + /** + * {@inheritdoc} + */ + public function isFrontPage(): bool { + return FALSE; + } + +} diff --git a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php index e2ceead3475d..6d4ff90d5f46 100644 --- a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php +++ b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php @@ -341,6 +341,13 @@ class SystemMenuBlockTest extends KernelTestBase { * @dataProvider configExpandedTestCases */ public function testConfigExpanded($active_route, $menu_block_level, $expected_items): void { + // Replace the path.matcher service so it always returns FALSE when + // checking whether a route is the front page. Otherwise, the default + // service throws an exception when checking routes because all of these + // are mocked. + $service_definition = $this->container->getDefinition('path.matcher'); + $service_definition->setClass(StubPathMatcher::class); + $block = $this->blockManager->createInstance('system_menu_block:' . $this->menu->id(), [ 'region' => 'footer', 'id' => 'machine_name', diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 08e2dc70cc89..f193795cc410 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -297,7 +297,7 @@ class EntityOperations { * * Alters entity forms to disallow concurrent editing in multiple workspaces. */ - #[Hook('form_alter')] + #[Hook('form_alter', order: Order::First)] public function entityFormAlter(array &$form, FormStateInterface $form_state, string $form_id): void { if (!$form_state->getFormObject() instanceof EntityFormInterface) { return; diff --git a/core/modules/workspaces/src/Hook/FormOperations.php b/core/modules/workspaces/src/Hook/FormOperations.php index 85f374582398..61b775ea3cd0 100644 --- a/core/modules/workspaces/src/Hook/FormOperations.php +++ b/core/modules/workspaces/src/Hook/FormOperations.php @@ -29,20 +29,24 @@ class FormOperations { return; } - // Add a validation step for every form if we are in a workspace. - $this->addWorkspaceValidation($form); + // If a form hasn't already been marked as safe or not to submit in a + // workspace, check the generic interfaces. + if (!$form_state->has('workspace_safe')) { + $form_object = $form_state->getFormObject(); + $workspace_safe = $form_object instanceof WorkspaceSafeFormInterface + || ($form_object instanceof WorkspaceDynamicSafeFormInterface && $form_object->isWorkspaceSafeForm($form, $form_state)); - // If a form has already been marked as safe or not to submit in a - // workspace, we don't have anything else to do. - if ($form_state->has('workspace_safe')) { - return; + $form_state->set('workspace_safe', $workspace_safe); } - $form_object = $form_state->getFormObject(); - $workspace_safe = $form_object instanceof WorkspaceSafeFormInterface - || ($form_object instanceof WorkspaceDynamicSafeFormInterface && $form_object->isWorkspaceSafeForm($form, $form_state)); - - $form_state->set('workspace_safe', $workspace_safe); + // Add a validation step for every other form. + if ($form_state->get('workspace_safe') !== TRUE) { + $form['workspace_safe'] = [ + '#type' => 'value', + '#value' => FALSE, + ]; + $this->addWorkspaceValidation($form); + } } /** @@ -59,8 +63,14 @@ class FormOperations { } } - if (isset($element['#validate'])) { + if (isset($element['#submit'])) { $element['#validate'][] = [static::class, 'validateDefaultWorkspace']; + + // Ensure that the workspace validation is always shown, even when the + // form element is limiting validation errors. + if (isset($element['#limit_validation_errors']) && $element['#limit_validation_errors'] !== FALSE) { + $element['#limit_validation_errors'][] = ['workspace_safe']; + } } } @@ -68,8 +78,8 @@ class FormOperations { * Validation handler which sets a validation error for all unsupported forms. */ public static function validateDefaultWorkspace(array &$form, FormStateInterface $form_state): void { - if ($form_state->get('workspace_safe') !== TRUE) { - $form_state->setError($form, new TranslatableMarkup('This form can only be submitted in the default workspace.')); + if ($form_state->get('workspace_safe') !== TRUE && isset($form_state->getCompleteForm()['workspace_safe'])) { + $form_state->setErrorByName('workspace_safe', new TranslatableMarkup('This form can only be submitted in the default workspace.')); } } diff --git a/core/modules/workspaces/tests/src/Functional/WorkspaceFormValidationTest.php b/core/modules/workspaces/tests/src/Functional/WorkspaceFormValidationTest.php new file mode 100644 index 000000000000..efd3bef34c21 --- /dev/null +++ b/core/modules/workspaces/tests/src/Functional/WorkspaceFormValidationTest.php @@ -0,0 +1,72 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\workspaces\Functional; + +use Drupal\Tests\BrowserTestBase; +use Drupal\workspaces\Entity\Workspace; + +/** + * Tests Workspaces form validation. + * + * @group workspaces + */ +class WorkspaceFormValidationTest extends BrowserTestBase { + + use WorkspaceTestUtilities; + + /** + * {@inheritdoc} + */ + protected static $modules = ['block', 'form_test', 'workspaces']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->drupalLogin($this->drupalCreateUser(['administer workspaces'])); + $this->setupWorkspaceSwitcherBlock(); + } + + /** + * Tests partial form validation through #limit_validation_errors. + */ + public function testValidateLimitErrors(): void { + $stage = Workspace::load('stage'); + $this->switchToWorkspace($stage); + + $edit = [ + 'test' => 'test1', + 'test_numeric_index[0]' => 'test2', + 'test_substring[foo]' => 'test3', + ]; + $path = 'form-test/limit-validation-errors'; + + // Submit the form by pressing all the 'Partial validate' buttons. + $this->drupalGet($path); + $this->submitForm($edit, 'Partial validate'); + $this->assertSession()->pageTextContains('This form can only be submitted in the default workspace.'); + + $this->drupalGet($path); + $this->submitForm($edit, 'Partial validate (numeric index)'); + $this->assertSession()->pageTextContains('This form can only be submitted in the default workspace.'); + + $this->drupalGet($path); + $this->submitForm($edit, 'Partial validate (substring)'); + $this->assertSession()->pageTextContains('This form can only be submitted in the default workspace.'); + + // Now test full form validation. + $this->drupalGet($path); + $this->submitForm($edit, 'Full validate'); + $this->assertSession()->pageTextContains('This form can only be submitted in the default workspace.'); + } + +} |