diff --git a/modules/entity_form/src/Plugin/EntityBrowser/Widget/EntityForm.php b/modules/entity_form/src/Plugin/EntityBrowser/Widget/EntityForm.php index 771aeaa..509f07a 100644 --- a/modules/entity_form/src/Plugin/EntityBrowser/Widget/EntityForm.php +++ b/modules/entity_form/src/Plugin/EntityBrowser/Widget/EntityForm.php @@ -83,6 +83,25 @@ class EntityForm extends WidgetBase { ); } + /** + * {@inheritdoc} + */ + public function getTargetEntityTypeId() { + return $this->configuration['entity_type']; + } + + /** + * {@inheritdoc} + */ + public function getTargetBundles() { + + if (!empty($this->configuration['bundle'])) { + return [$this->configuration['bundle']]; + } + + return []; + } + /** * {@inheritdoc} */ @@ -102,6 +121,19 @@ class EntityForm extends WidgetBase { if (empty($this->configuration['entity_type']) || empty($this->configuration['bundle']) || empty($this->configuration['form_mode'])) { return ['#markup' => $this->t('The settings for this widget (Entity type, Bundle or Form mode) are not configured correctly.')]; } + elseif (!empty($form_state->getStorage()['entity_browser']['widget_context'])) { + $widget_context = $form_state->getStorage()['entity_browser']['widget_context']; + if (!empty($widget_context['target_entity_type']) && $widget_context['target_entity_type'] != $this->configuration['entity_type']) { + return [ + '#markup' => $this->getWrongEntityMessage($widget_context), + ]; + } + elseif (!empty($widget_context['target_bundles']) && !in_array($this->configuration['bundle'], $widget_context['target_bundles'])) { + return [ + '#markup' => $this->getWrongBundleMessage($widget_context), + ]; + } + } $form = parent::getForm($original_form, $form_state, $additional_widget_parameters); diff --git a/src/Plugin/EntityBrowser/Widget/MediaImageUpload.php b/src/Plugin/EntityBrowser/Widget/MediaImageUpload.php index c6516bd..be3f4ed 100644 --- a/src/Plugin/EntityBrowser/Widget/MediaImageUpload.php +++ b/src/Plugin/EntityBrowser/Widget/MediaImageUpload.php @@ -19,6 +19,13 @@ use Drupal\media\MediaInterface; */ class MediaImageUpload extends FileUpload { + /** + * {@inheritdoc} + */ + public function getTargetEntityTypeId() { + return 'media'; + } + /** * {@inheritdoc} */ @@ -33,8 +40,18 @@ class MediaImageUpload extends FileUpload { * {@inheritdoc} */ public function getForm(array &$original_form, FormStateInterface $form_state, array $aditional_widget_parameters) { + + if (!empty($form_state->getStorage()['entity_browser']['widget_context'])) { + $widget_context = $form_state->getStorage()['entity_browser']['widget_context']; + if (!empty($widget_context['target_entity_type']) && $widget_context['target_entity_type'] != 'media') { + return [ + '#markup' => $this->getWrongEntityMessage($widget_context), + ]; + } + } + /** @var \Drupal\media\MediaTypeInterface $media_type */ - if (!$this->configuration['media_type'] || !($media_type = $this->entityTypeManager->getStorage('media_type')->load($this->configuration['media_type']))) { + elseif (!$this->configuration['media_type'] || !($media_type = $this->entityTypeManager->getStorage('media_type')->load($this->configuration['media_type']))) { return ['#markup' => $this->t('The media type is not configured correctly.')]; } diff --git a/src/Plugin/EntityBrowser/Widget/Upload.php b/src/Plugin/EntityBrowser/Widget/Upload.php index ff4fe7a..5793fae 100644 --- a/src/Plugin/EntityBrowser/Widget/Upload.php +++ b/src/Plugin/EntityBrowser/Widget/Upload.php @@ -60,6 +60,16 @@ class Upload extends WidgetBase { * {@inheritdoc} */ public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) { + + if (!empty($form_state->getStorage()['entity_browser']['widget_context'])) { + $widget_context = $form_state->getStorage()['entity_browser']['widget_context']; + if (!empty($widget_context['target_entity_type']) && $widget_context['target_entity_type'] != 'file') { + return [ + '#markup' => $this->getWrongEntityMessage($widget_context), + ]; + } + } + $form = parent::getForm($original_form, $form_state, $additional_widget_parameters); $field_cardinality = $form_state->get(['entity_browser', 'validators', 'cardinality', 'cardinality']); $upload_validators = $form_state->has(['entity_browser', 'widget_context', 'upload_validators']) ? $form_state->get(['entity_browser', 'widget_context', 'upload_validators']) : []; @@ -79,6 +89,13 @@ class Upload extends WidgetBase { return $form; } + /** + * {@inheritdoc} + */ + public function getTargetEntityTypeId() { + return 'file'; + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/EntityBrowser/Widget/View.php b/src/Plugin/EntityBrowser/Widget/View.php index f445d45..636307c 100644 --- a/src/Plugin/EntityBrowser/Widget/View.php +++ b/src/Plugin/EntityBrowser/Widget/View.php @@ -63,16 +63,49 @@ class View extends WidgetBase { * {@inheritdoc} */ public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) { - $form = parent::getForm($original_form, $form_state, $additional_widget_parameters); - // TODO - do we need better error handling for view and view_display (in - // case either of those is nonexistent or display not of correct type)? - $form['#attached']['library'] = ['entity_browser/view']; - /** @var \Drupal\views\ViewExecutable $view */ + /** @var \Drupal\views\Entity\View $view */ $view = $this->entityTypeManager ->getStorage('view') - ->load($this->configuration['view']) - ->getExecutable(); + ->load($this->configuration['view']); + + /** @var \Drupal\views\ViewExecutable $view */ + $view_executable = $view->getExecutable(); + + /** @var \Drupal\entity_browser\Entity\EntityBrowser $entity_browser */ + $entity_browser = $this->entityTypeManager + ->getStorage('entity_browser') + ->load($this->configuration['entity_browser_id']); + + $widget_entity_type = $this->getTargetEntityTypeId(); + $widget_target_bundles = $this->getTargetBundles(); + if (!empty($form_state->getStorage()['entity_browser']['widget_context'])) { + $widget_context = $form_state->getStorage()['entity_browser']['widget_context']; + if (!empty($widget_context['target_entity_type']) && $widget_context['target_entity_type'] != $widget_entity_type) { + return [ + '#markup' => $this->getWrongEntityMessage($widget_context), + ]; + } + elseif (!empty($widget_context['target_bundles']) && !empty($widget_target_bundles)) { + foreach ($widget_target_bundles as $widget_target_bundle) { + if (empty($widget_context['target_bundles'][$widget_target_bundle])) { + return [ + '#markup' => $this->getWrongBundleMessage($widget_context), + ]; + } + } + } + } + + $form = parent::getForm($original_form, $form_state, $additional_widget_parameters); + $form['#attached']['library'] = ['entity_browser/view']; + + // Check if the current user has access to this view. + if (!$view_executable->access($this->configuration['view_display'])) { + return [ + '#markup' => $this->t('You do not have access to this View.'), + ]; + } if (!empty($this->configuration['arguments'])) { if (!empty($additional_widget_parameters['path_parts'])) { @@ -81,13 +114,13 @@ class View extends WidgetBase { foreach ($this->configuration['arguments'] as $argument) { $arguments[] = isset($additional_widget_parameters['path_parts'][$argument]) ? $additional_widget_parameters['path_parts'][$argument] : ''; } - $view->setArguments(array_values($arguments)); + $view_executable->setArguments(array_values($arguments)); } } - $form['view'] = $view->executeDisplay($this->configuration['view_display']); + $form['view'] = $view_executable->executeDisplay($this->configuration['view_display']); - if (empty($view->field['entity_browser_select'])) { + if (empty($view_executable->field['entity_browser_select'])) { $url = Url::fromRoute('entity.view.edit_form', ['view' => $this->configuration['view']])->toString(); if ($this->currentUser->hasPermission('administer views')) { return [ @@ -289,4 +322,49 @@ class View extends WidgetBase { return AccessResult::allowedIf($view->access($this->configuration['view_display'])); } + /** + * {@inheritdoc} + */ + public function getTargetEntityTypeId() { + + if (empty($this->configuration['view'])) { + return NULL; + } + + /** @var \Drupal\views\ViewExecutable $view */ + $view = $this->entityTypeManager + ->getStorage('view') + ->load($this->configuration['view']) + ->getExecutable(); + + return $view->getBaseEntityType()->id(); + } + + /** + * {@inheritdoc} + */ + public function getTargetBundles() { + + if (empty($this->configuration['view']) || empty($this->configuration['view_display'])) { + return []; + } + + /** @var \Drupal\views\ViewExecutable $view */ + $view = $this->entityTypeManager + ->getStorage('view') + ->load($this->configuration['view']) + ->getExecutable(); + + /** @var \Drupal\views\Plugin\views\display\DisplayPluginInterface $display */ + $display = $view->getDisplay($this->configuration['view_display']); + + $filters = $display->getOption('filters'); + if (!empty($filters['type']['value'])) { + return $filters['type']['value']; + } + + return []; + } + + } diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php index 13df19e..35c96fb 100644 --- a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php @@ -239,9 +239,10 @@ class EntityReferenceBrowserWidget extends WidgetBase { public static function validateSettingsForm($element, FormStateInterface $form_state, $form) { $values = $form_state->getValue($element['#parents']); + /** @var \Drupal\entity_browser\Entity\EntityBrowser $entity_browser */ + $entity_browser = EntityBrowser::load($values['entity_browser']); + if ($values['selection_mode'] === 'selection_edit') { - /** @var \Drupal\entity_browser\Entity\EntityBrowser $entity_browser */ - $entity_browser = EntityBrowser::load($values['entity_browser']); if (!$entity_browser->getSelectionDisplay()->supportsPreselection()) { $tparams = [ '%selection_mode' => EntityBrowserElement::getSelectionModeOptions()[EntityBrowserElement::SELECTION_MODE_EDIT], @@ -249,10 +250,48 @@ class EntityReferenceBrowserWidget extends WidgetBase { ]; $form_state->setError($element['entity_browser']); $form_state->setError($element['selection_mode'], t('The selection mode %selection_mode requires an entity browser with a selection display plugin that supports preselection. Either change the selection mode or update the @browser_link entity browser to use a selection display plugin that supports preselection.', $tparams)); + return; } } + + $entity_type = $form['#entity_type']; + $bundle = $form['#bundle']; + $parents = array_slice($element['#array_parents'], -4, 1); + $field_name = end($parents); + $field_config = FieldConfig::loadByName($entity_type, $bundle, $field_name); + $target_entity_type = $field_config->getFieldStorageDefinition()->getSetting('target_type'); + $handler_settings = $field_config->getSetting('handler_settings'); + $target_bundles = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : []; + foreach ($entity_browser->getWidgets() as $key => $widget) { + // Skip validation if widget doesn't provide entity info with + // ::getTargetEntityTypeId and ::getTargetBundles methods. + if (!$widget instanceof WidgetEntityInfoInterface) { + continue; + } + $widget_entity_type = $widget->getTargetEntityTypeId(); + if (!empty($widget_entity_type) && $widget_entity_type != $target_entity_type) { + $widget_context = [ + 'target_entity_type' => $target_entity_type, + ]; + $form_state->setError($element['entity_browser'], $widget->getWrongEntityMessage($widget_context)); + return; + } + elseif (!empty($target_bundles) && $widget_target_bundles = $widget->getTargetBundles()) { + foreach ($widget_target_bundles as $widget_target_bundle) { + if (empty($target_bundles[$widget_target_bundle])) { + $widget_context = [ + 'target_bundles' => $target_bundles, + ]; + $form_state->setError($element['entity_browser'], $widget->getWrongBundleMessage($widget_context)); + return; + } + } + } + } + } + /** * Ajax callback that updates field widget display settings fieldset. */ diff --git a/src/WidgetBase.php b/src/WidgetBase.php index cba78da..cade750 100644 --- a/src/WidgetBase.php +++ b/src/WidgetBase.php @@ -17,7 +17,7 @@ use Symfony\Component\Validator\ConstraintViolationList; /** * Base class for widget plugins. */ -abstract class WidgetBase extends PluginBase implements WidgetInterface, ContainerFactoryPluginInterface { +abstract class WidgetBase extends PluginBase implements WidgetInterface, WidgetEntityInfoInterface, ContainerFactoryPluginInterface { use PluginConfigurationFormTrait; @@ -107,6 +107,55 @@ abstract class WidgetBase extends PluginBase implements WidgetInterface, Contain ); } + /** + * {@inheritdoc} + */ + public function getTargetEntityTypeId() { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getTargetBundles() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getWrongEntityMessage(array $widget_context) { + /** @var \Drupal\entity_browser\Entity\EntityBrowser $entity_browser */ + $entity_browser = $this->entityTypeManager + ->getStorage('entity_browser') + ->load($this->configuration['entity_browser_id']); + + $tparams = [ + '%field_target_entity_type' => $this->entityTypeManager->getStorage($widget_context['target_entity_type'])->getEntityType()->getLabel(), + '@entity_browser_link' => $entity_browser->toLink($entity_browser->label(), 'edit-widgets')->toString(), + '@widget_type' => $this->getPluginDefinition()['label'], + '%widget_entity_target_type' => $this->entityTypeManager->getStorage($this->getTargetEntityTypeId())->getEntityType()->getLabel(), + ]; + + return $this->t('There is an entity type mismatch between the field target entity type %field_target_entity_type and the target entity type %widget_entity_target_type used by the @widget_type widget in the entity browser @entity_browser_link.', $tparams); + } + + /** + * {@inheritdoc} + */ + public function getWrongBundleMessage(array $widget_context) { + /** @var \Drupal\entity_browser\Entity\EntityBrowser $entity_browser */ + $entity_browser = $this->entityTypeManager + ->getStorage('entity_browser') + ->load($this->configuration['entity_browser_id']); + + $tparams = [ + '@entity_browser_link' => $entity_browser->toLink($entity_browser->label(), 'edit-widgets')->toString(), + '@widget_type' => $this->getPluginDefinition()['label'], + ]; + return $this->t('There is a mismatch between the field target bundles and the target bundles used by the @widget_type widget in the entity browser @entity_browser_link.', $tparams); + } + /** * {@inheritdoc} */ diff --git a/src/WidgetEntityInfoInterface.php b/src/WidgetEntityInfoInterface.php new file mode 100644 index 0000000..719f499 --- /dev/null +++ b/src/WidgetEntityInfoInterface.php @@ -0,0 +1,38 @@ += 8.4) - drupal:user - drupal:node - drupal:views diff --git a/tests/src/FunctionalJavascript/EntityBrowserTest.php b/tests/src/FunctionalJavascript/EntityBrowserTest.php index f2bc387..d8f5309 100644 --- a/tests/src/FunctionalJavascript/EntityBrowserTest.php +++ b/tests/src/FunctionalJavascript/EntityBrowserTest.php @@ -2,6 +2,10 @@ namespace Drupal\Tests\entity_browser\FunctionalJavascript; +use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; + /** * Tests the entity_browser. * @@ -339,6 +343,32 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { * Tests the EntityBrowserWidgetContext default argument plugin. */ public function testEntityBrowserWidgetContext() { + + FieldStorageConfig::create([ + 'field_name' => 'field_galaxy', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'node', + ], + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_galaxy', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Reference', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'shark' => 'shark', + 'jet' => 'jet', + ], + ], + ], + ])->save(); + $this->createNode(['type' => 'shark', 'title' => 'Luke']); $this->createNode(['type' => 'jet', 'title' => 'Leia']); $this->createNode(['type' => 'article', 'title' => 'Darth']); @@ -348,7 +378,8 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { ->getStorage('entity_form_display') ->load('node.article.default'); - $form_display->setComponent('field_reference', [ + $form_display->removeComponent('field_reference'); + $form_display->setComponent('field_galaxy', [ 'type' => 'entity_browser_entity_reference', 'settings' => [ 'entity_browser' => 'widget_context_default_value', @@ -357,18 +388,6 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { ], ])->save(); - /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */ - $field_config = $this->container->get('entity_type.manager') - ->getStorage('field_config') - ->load('node.article.field_reference'); - $handler_settings = $field_config->getSetting('handler_settings'); - $handler_settings['target_bundles'] = [ - 'shark' => 'shark', - 'jet' => 'jet', - ]; - $field_config->setSetting('handler_settings', $handler_settings); - $field_config->save(); - // Set auto open to false on the entity browser. $entity_browser = $this->container->get('entity_type.manager') ->getStorage('entity_browser') @@ -400,7 +419,7 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */ $field_config = $this->container->get('entity_type.manager') ->getStorage('field_config') - ->load('node.article.field_reference'); + ->load('node.article.field_galaxy'); $handler_settings = $field_config->getSetting('handler_settings'); $handler_settings['target_bundles'] = [ 'article' => 'article', @@ -427,6 +446,31 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { */ public function testContextualBundle() { + FieldStorageConfig::create([ + 'field_name' => 'field_galaxy', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'node', + ], + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_galaxy', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Reference', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'shark' => 'shark', + 'jet' => 'jet', + ], + ], + ], + ])->save(); + $this->createNode(['type' => 'shark', 'title' => 'Luke']); $this->createNode(['type' => 'jet', 'title' => 'Leia']); $this->createNode(['type' => 'article', 'title' => 'Darth']); @@ -436,7 +480,8 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { ->getStorage('entity_form_display') ->load('node.article.default'); - $form_display->setComponent('field_reference', [ + $form_display->removeComponent('field_reference'); + $form_display->setComponent('field_galaxy', [ 'type' => 'entity_browser_entity_reference', 'settings' => [ 'entity_browser' => 'bundle_filter', @@ -445,18 +490,6 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { ], ])->save(); - /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */ - $field_config = $this->container->get('entity_type.manager') - ->getStorage('field_config') - ->load('node.article.field_reference'); - $handler_settings = $field_config->getSetting('handler_settings'); - $handler_settings['target_bundles'] = [ - 'shark' => 'shark', - 'jet' => 'jet', - ]; - $field_config->setSetting('handler_settings', $handler_settings); - $field_config->save(); - // Set auto open to false on the entity browser. $entity_browser = $this->container->get('entity_type.manager') ->getStorage('entity_browser') @@ -488,7 +521,7 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */ $field_config = $this->container->get('entity_type.manager') ->getStorage('field_config') - ->load('node.article.field_reference'); + ->load('node.article.field_galaxy'); $handler_settings = $field_config->getSetting('handler_settings'); $handler_settings['target_bundles'] = [ 'article' => 'article', @@ -514,6 +547,31 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { */ public function testContextualBundleExposed() { + FieldStorageConfig::create([ + 'field_name' => 'field_galaxy', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'node', + ], + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_galaxy', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Reference', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'shark' => 'shark', + 'jet' => 'jet', + ], + ], + ], + ])->save(); + $this->config('entity_browser.browser.bundle_filter') ->set('widgets.b882a89d-9ce4-4dfe-9802-62df93af232a.settings.view', 'bundle_filter_exposed') ->save(); @@ -527,7 +585,8 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { ->getStorage('entity_form_display') ->load('node.article.default'); - $form_display->setComponent('field_reference', [ + $form_display->removeComponent('field_reference'); + $form_display->setComponent('field_galaxy', [ 'type' => 'entity_browser_entity_reference', 'settings' => [ 'entity_browser' => 'bundle_filter', @@ -607,7 +666,7 @@ class EntityBrowserTest extends EntityBrowserWebDriverTestBase { /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */ $field_config = $this->container->get('entity_type.manager') ->getStorage('field_config') - ->load('node.article.field_reference'); + ->load('node.article.field_galaxy'); $handler_settings = $field_config->getSetting('handler_settings'); $handler_settings['target_bundles'] = [ 'article' => 'article', diff --git a/tests/src/FunctionalJavascript/FieldWidgetConfigTest.php b/tests/src/FunctionalJavascript/FieldWidgetConfigTest.php index f542d1c..f8ebe58 100644 --- a/tests/src/FunctionalJavascript/FieldWidgetConfigTest.php +++ b/tests/src/FunctionalJavascript/FieldWidgetConfigTest.php @@ -31,6 +31,7 @@ class FieldWidgetConfigTest extends WebDriverTestBase { protected static $modules = [ 'entity_browser', 'entity_browser_test', + 'entity_browser_ief_test', 'block', 'node', 'taxonomy', @@ -179,18 +180,22 @@ class FieldWidgetConfigTest extends WebDriverTestBase { ]); $field->save(); + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.article.default'); + + $form_display->setComponent('field_dalek', [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => 'widget_context_default_value', + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + $this->drupalGet('/admin/structure/types/manage/article/form-display'); - // Drag to enabled. - $target = $this->assertSession() - ->elementExists('css', '#title'); - $this->assertSession() - ->elementExists('css', '#field-dalek') - ->find('css', '.handle') - ->dragTo($target); - $this->assertSession()->assertWaitOnAjaxRequest(); - // Set to Entity Browser Widget. - $this->assertSession()->selectExists('fields[field_dalek][type]')->selectOption('entity_browser_entity_reference'); - $this->assertSession()->assertWaitOnAjaxRequest(); + // Open settings form. $this->assertSession()->waitforButton('field_dalek_settings_edit')->press(); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -211,7 +216,7 @@ class FieldWidgetConfigTest extends WebDriverTestBase { $this->assertSession()->pageTextContains($error_message); // Switch to an entity browser that supports preselection. - $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('test_entity_browser_iframe_view'); + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('widget_context_default_value'); $this->assertSession()->buttonExists('field_dalek_plugin_settings_update')->press(); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -220,14 +225,328 @@ class FieldWidgetConfigTest extends WebDriverTestBase { $this->assertSession()->buttonExists('Save')->press(); - // Update selected entity browser so it will trigger a warning. - $entity_browser = EntityBrowser::load('test_entity_browser_iframe_view'); + // Update selected entity browser so it will trigger a warning when viewed. + $entity_browser = EntityBrowser::load('widget_context_default_value'); $entity_browser->setSelectionDisplay('no_display'); $entity_browser->save(); $this->drupalGet('/node/add/article'); // Error message should be shown. - $this->assertSession()->pageTextContains('There is a configuration problem with field "Seek! Locate! Exterminate!". The selection mode Edit selection requires an entity browser with a selection display plugin that supports preselection. Either change the selection mode or update the Test entity browser iframe with view widget entity browser to use a selection display plugin that supports preselection.'); + $this->assertSession()->pageTextContains('There is a configuration problem with field "Seek! Locate! Exterminate!". The selection mode Edit selection requires an entity browser with a selection display plugin that supports preselection. Either change the selection mode or update the Widget Context Default Value entity browser to use a selection display plugin that supports preselection.'); + + } + + /** + * Tests validation of entity type and bundle of the View widget. + * + * User should be prevented from selecting an entity browser using a view + * widget with a view targeting the wrong entity or wrong bundles. + */ + public function testViewValidation() { + + // Create an entity_reference field to test the widget. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_sharks', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'node', + ], + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_sharks', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Referenced Sharks', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'shark' => 'shark', + ], + ], + ], + ]); + $field->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.article.default'); + + $form_display->setComponent('field_sharks', [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => 'type_filter', + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + + $this->drupalGet('/admin/structure/types/manage/article/form-display'); + + // Open settings form. + $this->assertSession()->waitforButton('field_sharks_settings_edit')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $form_prefix = 'fields[field_sharks][settings_edit_form][settings]'; + + // Select entity browser with wrong entity type on a widget. + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('test_entity_browser_file'); + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertSession()->pageTextContains('There is an entity type mismatch between the field target entity type Content and the target entity type File used by the View widget in the entity browser Test entity browser file.'); + + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('type_filter'); + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $bundle_error = "There is a mismatch between the field target bundles and the target bundles used by the View widget in the entity browser Type Filter."; + + $this->assertSession()->pageTextContains($bundle_error); + + $field_config = FieldConfig::loadByName('node', 'article', 'field_sharks'); + $field_config->setSetting('handler_settings', [ + 'target_bundles' => [ + 'article' => 'article', + ], + ]); + $field_config->save(); + + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->assertSession()->pageTextNotContains($bundle_error); + + } + + /** + * Tests entity validation of upload widget. + * + * User should be prevented from selecting entity browser using this widget on a media reference field. + */ + public function testUploadValidation() { + + // Create an entity_reference field to test the widget. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_media_image', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'media', + ], + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_media_image', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Images', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'image' => 'image', + ], + ], + ], + ]); + $field->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.article.default'); + + $form_display->setComponent('field_media_image', [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => 'media_image', + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + + $this->drupalGet('/admin/structure/types/manage/article/form-display'); + + // Open settings form. + $this->assertSession()->waitforButton('field_media_image_settings_edit')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $form_prefix = 'fields[field_media_image][settings_edit_form][settings]'; + + // Select entity browser with wrong entity type on a widget. + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('test_entity_browser_iframe'); + $this->assertSession()->buttonExists('field_media_image_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $entity_error = 'There is an entity type mismatch between the field target entity type Media and the target entity type File used by the Upload widget in the entity browser Test entity browser iframe.'; + + $this->assertSession()->pageTextContains($entity_error); + + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('media_image'); + $this->assertSession()->buttonExists('field_media_image_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->assertSession()->pageTextNotContains($entity_error); + + } + + /** + * Tests entity validation of MediaImageUpload widget. + * + * User should be prevented from selecting entity browser using this widget on file field. + */ + public function testMediaUploadValidation() { + + // Create an entity_reference field to test the widget. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_file', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'file', + ], + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_file', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Files', + 'settings' => [], + ]); + $field->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.article.default'); + + $form_display->setComponent('field_file', [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => 'test_entity_browser_file', + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + + $this->drupalGet('/admin/structure/types/manage/article/form-display'); + + // Open settings form. + $this->assertSession()->waitforButton('field_file_settings_edit')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $form_prefix = 'fields[field_file][settings_edit_form][settings]'; + + // Select entity browser with wrong entity type on a widget. + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('media_image'); + $this->assertSession()->buttonExists('field_file_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $entity_error = 'There is an entity type mismatch between the field target entity type File and the target entity type Media used by the Upload images as media items widget in the entity browser Media Image.'; + + $this->assertSession()->pageTextContains($entity_error); + + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('test_entity_browser_file'); + $this->assertSession()->buttonExists('field_file_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->assertSession()->pageTextNotContains($entity_error); + + } + + /** + * Tests entity validation of EntityForm widget. + * + * User should be prevented from selecting entity browser using this widget + * configured for another entity type or bundle than the field widget's + * settings. + */ + public function testEntityFormValidation() { + + // Create an entity_reference field to test the widget. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_sharks', + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'node', + ], + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_sharks', + 'entity_type' => 'node', + 'bundle' => 'article', + 'label' => 'Referenced Sharks', + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + 'shark' => 'shark', + ], + ], + ], + ]); + $field->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.article.default'); + + $form_display->setComponent('field_sharks', [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => 'widget_context_default_value', + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + + $this->drupalGet('/admin/structure/types/manage/article/form-display'); + + // Open settings form. + $this->assertSession()->waitforButton('field_sharks_settings_edit')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $form_prefix = 'fields[field_sharks][settings_edit_form][settings]'; + + // Select entity browser with wrong entity type on a widget. + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('media_entity_form'); + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertSession()->pageTextContains('There is an entity type mismatch between the field target entity type Content and the target entity type Media used by the Entity form widget in the entity browser Media Entity Form.'); + + $this->assertSession()->selectExists($form_prefix . '[entity_browser]')->selectOption('article_entity_form'); + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $bundle_error = "There is a mismatch between the field target bundles and the target bundles used by the Entity form widget in the entity browser Article Entity Form."; + + $this->assertSession()->pageTextContains($bundle_error); + + $field_config = FieldConfig::loadByName('node', 'article', 'field_sharks'); + $field_config->setSetting('handler_settings', [ + 'target_bundles' => [ + 'article' => 'article', + ], + ]); + $field_config->save(); + + $this->assertSession()->buttonExists('field_sharks_plugin_settings_update')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->assertSession()->pageTextNotContains($bundle_error); + } } diff --git a/tests/src/FunctionalJavascript/WidgetFormValidationTest.php b/tests/src/FunctionalJavascript/WidgetFormValidationTest.php new file mode 100644 index 0000000..2be72fc --- /dev/null +++ b/tests/src/FunctionalJavascript/WidgetFormValidationTest.php @@ -0,0 +1,245 @@ +drupalPlaceBlock('local_tasks_block'); + $this->drupalPlaceBlock('local_actions_block'); + + $this->adminUser = $this->drupalCreateUser([ + 'administer entity browsers', + 'access administration pages', + 'administer nodes', + 'create article content', + 'access media_image entity browser pages', + 'access type_filter entity browser pages', + 'access test_entity_browser_iframe entity browser pages', + 'access media_entity_form entity browser pages', + 'access article_entity_form entity browser pages', + 'access content', + 'create media', + ]); + + $this->drupalLogin($this->adminUser); + } + + /** + * Helper function to add a field to a node type with an entity browser. + * + * @param string $bundle + * The target node bundle. + * @param string $field_name + * The new field's name. + * @param string $entity_browser_id + * The entity browser to set on the form display. + * @param string $target_entity_type + * The target entity type. + * @param array $target_bundles + * An array of target bundles. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ + protected function setUpField($bundle, $field_name, $entity_browser_id, $target_entity_type, array $target_bundles) { + + // Create an entity_reference field to test the widget. + $field_storage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'type' => 'entity_reference', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => $target_entity_type, + ], + ]); + $field_storage->save(); + + $field_config_settings = [ + 'field_name' => $field_name, + 'entity_type' => 'node', + 'bundle' => $bundle, + 'label' => $field_name, + 'settings' => [], + ]; + + if (!empty($target_bundles)) { + $field_config_settings['settings'] = [ + 'handler_settings' => [ + 'target_bundles' => $target_bundles, + ], + ]; + } + + $field = FieldConfig::create($field_config_settings); + $field->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $this->container->get('entity_type.manager') + ->getStorage('entity_form_display') + ->load('node.' . $bundle . '.default'); + + $form_display->setComponent($field_name, [ + 'type' => 'entity_browser_entity_reference', + 'settings' => [ + 'entity_browser' => $entity_browser_id, + 'field_widget_display' => 'label', + 'open' => TRUE, + ], + ])->save(); + + $browser = EntityBrowser::load($entity_browser_id); + + $browser->getDisplay() + ->setConfiguration([ + 'width' => 650, + 'height' => 500, + 'link_text' => 'Select entities', + 'auto_open' => TRUE, + ]); + $browser->setSelectionDisplay('no_display'); + $browser->save(); + } + + /** + * Tests View widget behavior when target entity type is wrong for widget context. + */ + public function testWrongViewEntity() { + + $entity_browser_id = 'media_image'; + $target_bundles = [ + 'shark' => 'shark', + ]; + $this->setUpField('article', 'field_sharks', $entity_browser_id, 'node', $target_bundles); + + $entity_browser = EntityBrowser::load($entity_browser_id); + $entity_browser->getWidgets()->removeInstanceId('3679f2e6-b798-46fa-9d0d-2583f0687037'); + $entity_browser->save(); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains('There is an entity type mismatch between the field target entity type Content and the target entity type Media used by the View widget in the entity browser Media Image.'); + + } + + /** + * Tests View widget behavior when target bundles are mismatch bundles in widget context. + */ + public function testWrongViewBundle() { + $entity_browser_id = 'type_filter'; + $target_bundles = [ + 'shark' => 'shark', + ]; + $this->setUpField('article', 'field_sharks', $entity_browser_id, 'node', $target_bundles); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains("There is a mismatch between the field target bundles and the target bundles used by the View widget in the entity browser Type Filter."); + } + + /** + * Tests Upload widget behavior when target entity type is not file. + */ + public function testWrongUploadEntity() { + $entity_browser_id = 'test_entity_browser_iframe'; + $target_bundles = [ + 'image' => 'image', + ]; + $this->setUpField('article', 'field_sharks', $entity_browser_id, 'media', $target_bundles); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains('There is an entity type mismatch between the field target entity type Media and the target entity type File used by the Upload widget in the entity browser Test entity browser iframe.'); + + } + + /** + * Tests MediaUpload widget behavior when target entity type is not media. + */ + public function testWrongMediaUploadEntity() { + + $entity_browser_id = 'media_image'; + $target_bundles = []; + $this->setUpField('article', 'field_files', $entity_browser_id, 'file', $target_bundles); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains('There is an entity type mismatch between the field target entity type File and the target entity type Media used by the Upload images as media items widget in the entity browser Media Image.'); + + } + + /** + * Tests EntityForm widget behavior when target entity type is wrong for widget context. + */ + public function testWrongEntityFormEntity() { + + $entity_browser_id = 'media_entity_form'; + $target_bundles = [ + 'shark' => 'shark', + ]; + $this->setUpField('article', 'field_sharks', $entity_browser_id, 'node', $target_bundles); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains("There is an entity type mismatch between the field target entity type Content and the target entity type Media used by the Entity form widget in the entity browser Media Entity Form."); + + } + + /** + * Tests EntityForm widget behavior when target bundle is wrong for widget context. + */ + public function testWrongEntityFormBundle() { + $entity_browser_id = 'article_entity_form'; + $target_bundles = [ + 'shark' => 'shark', + ]; + $this->setUpField('article', 'field_sharks', $entity_browser_id, 'node', $target_bundles); + + $this->drupalGet('/node/add/article'); + + $this->getSession()->switchToIFrame('entity_browser_iframe_' . $entity_browser_id); + + $this->assertSession()->pageTextContains("There is a mismatch between the field target bundles and the target bundles used by the Entity form widget in the entity browser Article Entity Form."); + } + +} diff --git a/tests/src/Kernel/Extension/EntityBrowserTest.php b/tests/src/Kernel/Extension/EntityBrowserTest.php index 0e0b492..d259c94 100644 --- a/tests/src/Kernel/Extension/EntityBrowserTest.php +++ b/tests/src/Kernel/Extension/EntityBrowserTest.php @@ -34,6 +34,8 @@ class EntityBrowserTest extends KernelTestBase { 'views', 'file', 'node', + 'media', + 'image', 'entity_browser', 'entity_browser_test', ];