diff --git a/core/lib/Drupal/Core/Recipe/InputConfigurator.php b/core/lib/Drupal/Core/Recipe/InputConfigurator.php index c9f1347a836..9b324ed29aa 100644 --- a/core/lib/Drupal/Core/Recipe/InputConfigurator.php +++ b/core/lib/Drupal/Core/Recipe/InputConfigurator.php @@ -72,7 +72,7 @@ final class InputConfigurator { $definition['constraints'], ); $data_definition->setSettings($definition); - $this->data[$name] = $typedDataManager->create($data_definition, name: "$prefix.$name"); + $this->data[$name] = $typedDataManager->create($data_definition); } } @@ -112,9 +112,9 @@ final class InputConfigurator { foreach ($this->dependencies->recipes as $dependency) { $descriptions = array_merge($descriptions, $dependency->input->describeAll()); } - foreach ($this->data as $data) { - $name = $data->getName(); - $descriptions[$name] = $data->getDataDefinition()->getDescription(); + foreach ($this->getDataDefinitions() as $key => $definition) { + $name = $this->prefix . '.' . $key; + $descriptions[$name] = $definition->getDescription(); } return $descriptions; } @@ -153,7 +153,7 @@ final class InputConfigurator { $definition = $data->getDataDefinition(); $value = $collector->collectValue( - $data->getName(), + $this->prefix . '.' . $key, $definition, $this->getDefaultValue($definition), ); @@ -161,7 +161,7 @@ final class InputConfigurator { $violations = $data->validate(); if (count($violations) > 0) { - throw new ValidationFailedException($data, $violations); + throw new ValidationFailedException($value, $violations); } $this->values[$key] = $data->getCastedValue(); } diff --git a/core/lib/Drupal/Core/Recipe/Recipe.php b/core/lib/Drupal/Core/Recipe/Recipe.php index a09dc17de66..5e0d55b2e13 100644 --- a/core/lib/Drupal/Core/Recipe/Recipe.php +++ b/core/lib/Drupal/Core/Recipe/Recipe.php @@ -9,7 +9,6 @@ use Drupal\Core\Extension\Dependency; use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Component\Serialization\Yaml; -use Drupal\Core\Render\Element; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\Validation\Plugin\Validation\Constraint\RegexConstraint; use Symfony\Component\Validator\Constraints\All; @@ -204,8 +203,8 @@ final class Recipe { 'interface' => PrimitiveInterface::class, ]), ], - // The `prompt` and `form` elements, though optional, have their - // own sets of constraints, + // If there is a `prompt` element, it has its own set of + // constraints. 'prompt' => new Optional([ new Collection([ 'method' => [ @@ -216,19 +215,6 @@ final class Recipe { ]), ]), ]), - 'form' => new Optional([ - new Sequentially([ - new Type('associative_array'), - // Every element in the `form` array has to be a form API - // property, prefixed with `#`. Because recipe inputs can only - // be primitive data types, child elements aren't allowed. - new Callback(function (array $element, ExecutionContextInterface $context): void { - if (Element::children($element)) { - $context->addViolation('Form elements for recipe inputs cannot have child elements.'); - } - }), - ]), - ]), // Every input must define a default value. 'default' => new Required([ new Collection([ diff --git a/core/lib/Drupal/Core/Recipe/RecipeInputFormTrait.php b/core/lib/Drupal/Core/Recipe/RecipeInputFormTrait.php deleted file mode 100644 index 5327c9e315c..00000000000 --- a/core/lib/Drupal/Core/Recipe/RecipeInputFormTrait.php +++ /dev/null @@ -1,151 +0,0 @@ - [ - * 'input_1' => [ - * '#type' => 'textfield', - * '#title' => 'Some input value', - * ], - * 'input_2' => [ - * '#type' => 'checkbox', - * '#title' => 'Enable some feature or other?', - * ], - * ], - * 'dependency_recipe' => [ - * 'input_1' => [ - * '#type' => 'textarea', - * '#title' => 'An input defined by a dependency of recipe_1', - * ], - * ], - * '#tree' => TRUE, - * ]; - * @endcode - * - * The `#tree` property will always be set to TRUE. - * - * @var array - */ - // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable - public array $form = []; - - /** - * {@inheritdoc} - */ - public function collectValue(string $name, DataDefinitionInterface $definition, mixed $default_value): mixed { - $element = $definition->getSetting('form'); - if ($element) { - $element += [ - '#description' => $definition->getDescription(), - '#default_value' => $default_value, - ]; - // Recipe inputs are always required. - $element['#required'] = TRUE; - NestedArray::setValue($this->form, explode('.', $name, 2), $element); - - // Always return the input elements as a tree. - $this->form['#tree'] = TRUE; - } - return $default_value; - } - - }; - $recipe->input->collectAll($collector); - return $collector->form; - } - - /** - * Validates user-inputted values to a recipe and its dependencies. - * - * @param \Drupal\Core\Recipe\Recipe $recipe - * A recipe. - * @param array $form - * The form being validated, which should include the tree of elements - * returned by ::buildRecipeInputForm(). - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current form state. The values should be organized in the tree - * structure that was returned by ::buildRecipeInputForm(). - */ - protected function validateRecipeInput(Recipe $recipe, array &$form, FormStateInterface $form_state): void { - try { - $this->setRecipeInput($recipe, $form_state); - } - catch (ValidationFailedException $e) { - $data = $e->getValue(); - - if ($data instanceof TypedDataInterface) { - $element = NestedArray::getValue($form, explode('.', $data->getName(), 2)); - $form_state->setError($element, $e->getMessage()); - } - else { - // If the data isn't a typed data object, we have no idea how to handle - // the situation, so just re-throw the exception. - throw $e; - } - } - } - - /** - * Supplies user-inputted values to a recipe and its dependencies. - * - * @param \Drupal\Core\Recipe\Recipe $recipe - * A recipe. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current form state. The values should be organized in the tree - * structure that was returned by ::buildRecipeInputForm(). - */ - protected function setRecipeInput(Recipe $recipe, FormStateInterface $form_state): void { - $recipe->input->collectAll(new class ($form_state) implements InputCollectorInterface { - - public function __construct(private readonly FormStateInterface $formState) { - } - - /** - * {@inheritdoc} - */ - public function collectValue(string $name, DataDefinitionInterface $definition, mixed $default_value): mixed { - return $this->formState->getValue(explode('.', $name, 2), $default_value); - } - - }); - } - -} diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml index 54b3d115323..be0a1a34a8b 100644 --- a/core/modules/system/tests/modules/form_test/form_test.routing.yml +++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml @@ -564,10 +564,3 @@ form_test.incorrect_config_target: _admin_route: TRUE requirements: _access: 'TRUE' - -form_test.recipe_input: - path: '/form-test/recipe-input' - defaults: - _form: '\Drupal\form_test\Form\FormTestRecipeInputForm' - requirements: - _access: 'TRUE' diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRecipeInputForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRecipeInputForm.php deleted file mode 100644 index 0619e4afd3a..00000000000 --- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRecipeInputForm.php +++ /dev/null @@ -1,63 +0,0 @@ -buildRecipeInputForm($this->getRecipe()); - - $form['apply'] = [ - '#type' => 'submit', - '#value' => $this->t('Apply recipe'), - ]; - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state): void { - $this->validateRecipeInput($this->getRecipe(), $form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - $recipe = $this->getRecipe(); - $this->setRecipeInput($recipe, $form_state); - RecipeRunner::processRecipe($recipe); - } - -} diff --git a/core/modules/system/tests/src/Functional/Form/RecipeFormInputTest.php b/core/modules/system/tests/src/Functional/Form/RecipeFormInputTest.php deleted file mode 100644 index 35303b740cd..00000000000 --- a/core/modules/system/tests/src/Functional/Form/RecipeFormInputTest.php +++ /dev/null @@ -1,52 +0,0 @@ -drupalGet('/form-test/recipe-input'); - - $assert_session = $this->assertSession(); - // There should only be one nested input element on the page: the one - // defined by the input_test recipe. - $assert_session->elementsCount('css', 'input[name*="["]', 1); - // The default value and description should be visible. - $assert_session->fieldValueEquals('input_test[owner]', 'Dries Buytaert'); - $assert_session->pageTextContains('The name of the site owner.'); - // All recipe inputs are required. - $this->submitForm(['input_test[owner]' => ''], 'Apply recipe'); - $assert_session->statusMessageContains("Site owner's name field is required.", 'error'); - // All inputs should be validated with their own constraints. - $this->submitForm(['input_test[owner]' => 'Hacker Joe'], 'Apply recipe'); - $assert_session->statusMessageContains("I don't think you should be owning sites.", 'error'); - // The correct element should be flagged as invalid. - $assert_session->elementAttributeExists('named', ['field', 'input_test[owner]'], 'aria-invalid'); - // Submit the form with a valid value and apply the recipe, to prove that - // it was passed through correctly. - $this->submitForm(['input_test[owner]' => 'Legitimate Human'], 'Apply recipe'); - $this->assertSame("Legitimate Human's Turf", $this->config('system.site')->get('name')); - } - -} diff --git a/core/recipes/feedback_contact_form/recipe.yml b/core/recipes/feedback_contact_form/recipe.yml index c6bf74cf40c..084f2712537 100644 --- a/core/recipes/feedback_contact_form/recipe.yml +++ b/core/recipes/feedback_contact_form/recipe.yml @@ -13,9 +13,6 @@ input: method: ask arguments: question: 'What email address should receive website feedback?' - form: - '#type': email - '#title': 'Feedback form email address' default: source: config config: ['system.site', 'mail'] diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php index cc60172a960..c366883e290 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php @@ -11,7 +11,6 @@ use Drupal\Core\Recipe\InputCollectorInterface; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeRunner; use Drupal\Core\TypedData\DataDefinitionInterface; -use Drupal\Core\TypedData\TypedDataInterface; use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait; use Drupal\KernelTests\KernelTestBase; use Symfony\Component\Console\Input\InputInterface; @@ -80,9 +79,7 @@ class InputTest extends KernelTestBase { $this->fail('Expected an exception due to validation failure, but none was thrown.'); } catch (ValidationFailedException $e) { - $value = $e->getValue(); - $this->assertInstanceOf(TypedDataInterface::class, $value); - $this->assertSame('not-an-email-address', $value->getValue()); + $this->assertSame('not-an-email-address', $e->getValue()); $this->assertSame('This value is not a valid email address.', (string) $e->getViolations()->get(0)->getMessage()); } } diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeValidationTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeValidationTest.php index 5829898f176..fe736e90062 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeValidationTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeValidationTest.php @@ -505,88 +505,6 @@ YAML, '[input][foo][prompt][arguments]' => ['This value should be of type array.'], ], ]; - yield 'form element is not an array' => [ - << ['This value should be of type associative_array.'], - ], - ]; - yield 'form element is an indexed array' => [ - << ['This value should be of type associative_array.'], - ], - ]; - yield 'form element is an empty array' => [ - << ['This value should be of type associative_array.'], - ], - ]; - yield 'form element has children' => [ - << ['Form elements for recipe inputs cannot have child elements.'], - ], - ]; - yield 'Valid form element' => [ - << [ <<