Unverified Commit f31214d7 authored by larowlan's avatar larowlan
Browse files

Issue #3003150 by bnjmnm, oknate, seanB, hoebekewim, BR0kEN, a.hover,...

Issue #3003150 by bnjmnm, oknate, seanB, hoebekewim, BR0kEN, a.hover, guschilds, phenaproxima, larowlan: Media library causes validation errors when it is used in a required field of a nested form
parent 0671ed8a
......@@ -547,8 +547,12 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
'#validate' => [[static::class, 'validateItems']],
'#submit' => [[static::class, 'addItems']],
// Prevent errors in other widgets from preventing updates.
'#limit_validation_errors' => $limit_validation_errors,
// We need to prevent the widget from being validated when no media items
// are selected. When a media field is added in a subform, entity
// validation is triggered in EntityFormDisplay::validateFormValues().
// Since the media item is not added to the form yet, this triggers errors
// for required media fields.
'#limit_validation_errors' => !empty($referenced_entities) ? $limit_validation_errors : [],
];
return $element;
......@@ -839,8 +843,11 @@ public static function addItems(array $form, FormStateInterface $form_state) {
* An array of selected media items.
*/
protected static function getNewMediaItems(array $element, FormStateInterface $form_state) {
// Get the new media IDs passed to our hidden button.
$values = $form_state->getValues();
// Get the new media IDs passed to our hidden button. We need to use the
// actual user input, since when #limit_validation_errors is used, the
// unvalidated user input is not added to the form state.
// @see FormValidator::handleErrorsWithLimitedValidation()
$values = $form_state->getUserInput();
$path = $element['#parents'];
$value = NestedArray::getValue($values, $path);
......@@ -872,7 +879,10 @@ protected static function getNewMediaItems(array $element, FormStateInterface $f
protected static function getFieldState(array $element, FormStateInterface $form_state) {
// Default to using the current selection if the form is new.
$path = $element['#parents'];
$values = NestedArray::getValue($form_state->getValues(), $path);
// We need to use the actual user input, since when #limit_validation_errors
// is used, the unvalidated user input is not added to the form state.
// @see FormValidator::handleErrorsWithLimitedValidation()
$values = NestedArray::getValue($form_state->getUserInput(), $path);
$selection = isset($values['selection']) ? $values['selection'] : [];
$widget_state = static::getWidgetState($element['#field_parents'], $element['#field_name'], $form_state);
......
field.widget.settings.media_library_inception_widget:
type: mapping
label: 'Media library inception widget settings'
mapping:
media_types:
type: sequence
label: 'Allowed media types, in display order'
sequence:
type: string
label: 'Media type ID'
name: 'Media Library test widget'
type: module
description: 'Test widget that has a nested media library widget'
package: Testing
core: 8.x
dependencies:
- drupal:image
- drupal:media_library
- drupal:media_test_source
<?php
namespace Drupal\media_library_test_widget\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget;
/**
* Plugin implementation of the 'media_library_inception_widget' widget.
*
* This widget is used to simulate the media library widget nested inside
* another widget that performs validation of required fields before there is
* an opportunity to add media.
*
* @FieldWidget(
* id = "media_library_inception_widget",
* label = @Translation("Media library inception widget"),
* description = @Translation("Puts a widget in a widget for testing purposes."),
* field_types = {
* "entity_reference"
* },
* multiple_values = TRUE,
* )
*/
class MediaLibraryInceptionWidget extends MediaLibraryWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
if (empty($element['#element_validate'])) {
$element['#element_validate'] = [];
}
$element['#element_validate'][] = [$this, 'elementValidate'];
return parent::formElement($items, $delta, $element, $form, $form_state);
}
/**
* {@inheritdoc}
*/
public function elementValidate($element, FormStateInterface $form_state, $form) {
$field_name = $element['#field_name'];
$entity = $form_state->getFormObject()->getEntity();
$input = $form_state->getUserInput();
if (!empty($input['_triggering_element_name']) && strpos($input['_triggering_element_name'], 'media-library-update') !== FALSE) {
// This will validate a required field before an upload is completed.
$display = EntityFormDisplay::collectRenderDisplay($entity, 'edit');
$display->extractFormValues($entity, $form, $form_state);
$display->validateFormValues($entity, $form, $form_state);
}
$form_value = $form_state->getValue($field_name);
if (!empty($form_value['media_library_selection'])) {
$entity->set($field_name, $form_value['media_library_selection']);
}
}
}
<?php
namespace Drupal\Tests\media_library\FunctionalJavascript;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests media widget nested inside another widget.
*
* @group media_library
*/
class EmbeddedFormWidgetTest extends WebDriverTestBase {
use TestFileCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'media_library',
'media_library_test',
'media_library_test_widget',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$display_repository = $this->container->get('entity_display.repository');
FieldStorageConfig::create([
'field_name' => 'media_image_field',
'entity_type' => 'node',
'type' => 'entity_reference',
'settings' => [
'target_type' => 'media',
'required' => TRUE,
],
])->save();
FieldConfig::create([
'label' => 'A Media Image Field',
'field_name' => 'media_image_field',
'entity_type' => 'node',
'bundle' => 'basic_page',
'field_type' => 'entity_reference',
'required' => TRUE,
'settings' => [
'handler_settings' => [
'target_bundles' => [
'type_three' => 'type_three',
],
],
],
])->save();
$display_repository->getFormDisplay('node', 'basic_page')
->setComponent('media_image_field', [
'type' => 'media_library_widget',
'region' => 'content',
'settings' => [
'media_types' => ['type_three'],
],
])
->save();
$user = $this->drupalCreateUser([
'access content',
'access media overview',
'edit own basic_page content',
'create basic_page content',
'create media',
'view media',
]);
$this->drupalLogin($user);
}
/**
* Test media inside another widget that validates too enthusiastically.
*
* @dataProvider insertionReselectionProvider
*/
public function testInsertionAndReselection($widget) {
$this->container
->get('entity_display.repository')
->getFormDisplay('node', 'basic_page')
->setComponent('media_image_field', [
'type' => $widget,
'region' => 'content',
'settings' => [
'media_types' => ['type_three'],
],
])
->save();
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
foreach ($this->getTestFiles('image') as $image) {
$extension = pathinfo($image->filename, PATHINFO_EXTENSION);
if ($extension === 'jpg') {
$jpg_image = $image;
break;
}
}
$this->drupalGet('node/add/basic_page');
$wrapper = $assert_session->elementExists('css', '#media_image_field-media-library-wrapper');
$wrapper->pressButton('Add media');
$this->assertTrue($assert_session->waitForText('Add or select media'));
$page->attachFileToField('Add file', $this->container->get('file_system')->realpath($jpg_image->uri));
$this->assertTrue($assert_session->waitForText('Alternative text'));
$page->fillField('Alternative text', $this->randomString());
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and insert');
$first_item_locator = "(//div[@data-drupal-selector='edit-media-image-field-selection-0'])[1]";
$this->assertTrue($assert_session->waitForElementVisible('xpath', $first_item_locator));
$assert_session->elementExists('css', '.media-library-item__remove')->click();
$assert_session->waitForElementRemoved('xpath', $first_item_locator);
$page->waitFor(10, function () use ($wrapper) {
return $wrapper->hasButton('Add media');
});
// Test reinserting the same selection.
$wrapper->pressButton('Add media');
$this->assertTrue($assert_session->waitForText('Add or select media'));
$assert_session->elementExists('xpath', "(//div[contains(@class, 'media-library-item')])[1]")->click();
$assert_session->checkboxChecked('media_library_select_form[0]');
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
$this->assertTrue($assert_session->waitForElementVisible('xpath', $first_item_locator));
}
/**
* Data provider for ::testInsertionAndReselection().
*
* @return array
* Test data.
*/
public function insertionReselectionProvider() {
return [
'using media_library_widget' => [
'widget' => 'media_library_widget',
],
'using media_library_inception_widget' => [
'widget' => 'media_library_inception_widget',
],
];
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment