Commit c3232e96 authored by webchick's avatar webchick

Issue #3033653 by seanB, Pancho, phenaproxima, webchick, benjifisher,...

Issue #3033653 by seanB, Pancho, phenaproxima, webchick, benjifisher, rainbreaw, jrockow: InvalidArgumentException when adding reference field without Media type

(cherry picked from commit f8e7edb1)
parent f75cc99a
......@@ -206,7 +206,7 @@ protected function buildMediaTypeMenu(MediaLibraryState $state) {
// Add the menu for each type if we have more than 1 media type enabled for
// the field.
$allowed_type_ids = $state->getAllowedTypeIds();
if (count($allowed_type_ids) === 1) {
if (count($allowed_type_ids) <= 1) {
return [];
}
......
......@@ -9,12 +9,16 @@
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\field_ui\FieldUI;
use Drupal\media\Entity\Media;
use Drupal\media_library\MediaLibraryUiBuilder;
use Drupal\media_library\MediaLibraryState;
......@@ -48,6 +52,20 @@ class MediaLibraryWidget extends WidgetBase implements ContainerFactoryPluginInt
*/
protected $entityTypeManager;
/**
* The current active user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The prefix to use with a field ID for media library opener IDs.
*
......@@ -70,10 +88,24 @@ class MediaLibraryWidget extends WidgetBase implements ContainerFactoryPluginInt
* Any third party settings.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* (optional) The current active user.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* (optional) The module handler.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager) {
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager, AccountInterface $current_user = NULL, ModuleHandlerInterface $module_handler = NULL) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->entityTypeManager = $entity_type_manager;
if (!$current_user) {
@trigger_error('The current_user service must be passed to MediaLibraryWidget::__construct(), it is required before Drupal 9.0.0.', E_USER_DEPRECATED);
$current_user = \Drupal::currentUser();
}
$this->currentUser = $current_user;
if (!$module_handler) {
@trigger_error('The module_handler service must be passed to MediaLibraryWidget::__construct(), it is required before Drupal 9.0.0.', E_USER_DEPRECATED);
$module_handler = \Drupal::moduleHandler();
}
$this->moduleHandler = $module_handler;
}
/**
......@@ -86,7 +118,9 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration['field_definition'],
$configuration['settings'],
$configuration['third_party_settings'],
$container->get('entity_type.manager')
$container->get('entity_type.manager'),
$container->get('current_user'),
$container->get('module_handler')
);
}
......@@ -118,10 +152,15 @@ protected function getAllowedMediaTypeIdsSorted() {
// Get the configured media types from the field storage.
$handler_settings = $this->getFieldSetting('handler_settings');
$allowed_media_type_ids = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : [];
$allowed_media_type_ids = $handler_settings['target_bundles'];
// When there are no allowed media types, return the empty array.
if ($allowed_media_type_ids === []) {
return $allowed_media_type_ids;
}
// When no target bundles are configured for the field, all are allowed.
if (!$allowed_media_type_ids) {
if ($allowed_media_type_ids === NULL) {
$allowed_media_type_ids = $this->entityTypeManager->getStorage('media_type')->getQuery()->execute();
}
......@@ -287,6 +326,18 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
];
// When the list of allowed types in the field configuration is null,
// ::getAllowedMediaTypeIdsSorted() returns all existing media types. When
// the list of allowed types is an empty array, we show a message to users
// and ask them to configure the field if they have access.
$allowed_media_type_ids = $this->getAllowedMediaTypeIdsSorted();
if (!$allowed_media_type_ids) {
$element['no_types_message'] = [
'#markup' => $this->getNoMediaTypesAvailableMessage(),
];
return $element;
}
if (empty($referenced_entities)) {
$element['empty_selection'] = [
'#type' => 'html_tag',
......@@ -394,7 +445,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
}
// Create a new media library URL with the correct state parameters.
$allowed_media_type_ids = $this->getAllowedMediaTypeIdsSorted();
$selected_type_id = reset($allowed_media_type_ids);
$remaining = $cardinality_unlimited ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : $remaining;
// The opener ID is used by the select form and the upload form to add the
......@@ -473,6 +523,45 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
return $element;
}
/**
* Gets the message to display when there are no allowed media types.
*
* @return \Drupal\Component\Render\MarkupInterface
* The message to display when there are no allowed media types.
*/
protected function getNoMediaTypesAvailableMessage() {
$entity_type_id = $this->fieldDefinition->getTargetEntityTypeId();
$default_message = $this->t('There are no allowed media types configured for this field. Please contact the site administrator.');
// Show the default message if the user does not have the permissions to
// configure the fields for the entity type.
if (!$this->currentUser->hasPermission("administer $entity_type_id fields")) {
return $default_message;
}
// Show a message for privileged users to configure the field if the Field
// UI module is not enabled.
if (!$this->moduleHandler->moduleExists('field_ui')) {
return $this->t('There are no allowed media types configured for this field. Edit the field settings to select the allowed media types.');
}
// Add a link to the message to configure the field if the Field UI module
// is enabled.
$route_parameters = FieldUI::getRouteBundleParameter($this->entityTypeManager->getDefinition($entity_type_id), $this->fieldDefinition->getTargetBundle());
$route_parameters['field_config'] = $this->fieldDefinition->id();
$url = Url::fromRoute('entity.field_config.' . $entity_type_id . '_field_edit_form', $route_parameters);
if ($url->access($this->currentUser)) {
return $this->t('There are no allowed media types configured for this field. <a href=":url">Edit the field settings</a> to select the allowed media types.', [
':url' => $url->toString(),
]);
}
// If the user for some reason doesn't have access to the Field UI, fall
// back to the default message.
return $default_message;
}
/**
* {@inheritdoc}
*/
......
......@@ -49,12 +49,18 @@ content:
settings: { }
third_party_settings: { }
region: content
field_empty_types_media:
field_null_types_media:
type: media_library_widget
weight: 125
settings: { }
third_party_settings: { }
region: content
field_empty_types_media:
type: media_library_widget
weight: 126
settings: { }
third_party_settings: { }
region: content
promote:
type: boolean_checkbox
settings:
......
......@@ -51,7 +51,16 @@ content:
region: content
field_empty_types_media:
type: entity_reference_entity_view
weight: 103
weight: 104
label: above
settings:
view_mode: default
link: false
third_party_settings: { }
region: content
field_null_types_media:
type: entity_reference_entity_view
weight: 105
label: above
settings:
view_mode: default
......
......@@ -3,8 +3,6 @@ status: true
dependencies:
config:
- field.storage.node.field_empty_types_media
- media.type.type_one
- media.type.type_two
- node.type.basic_page
id: node.basic_page.field_empty_types_media
field_name: field_empty_types_media
......@@ -19,7 +17,7 @@ default_value_callback: ''
settings:
handler: 'default:media'
handler_settings:
target_bundles: null
target_bundles: { }
sort:
field: _none
auto_create: false
......
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_null_types_media
- node.type.basic_page
id: node.basic_page.field_null_types_media
field_name: field_null_types_media
entity_type: node
bundle: basic_page
label: 'Null types media'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:media'
handler_settings:
target_bundles: null
sort:
field: _none
auto_create: false
auto_create_bundle: file
field_type: entity_reference
langcode: en
status: true
dependencies:
module:
- media
- node
id: node.field_null_types_media
field_name: field_null_types_media
entity_type: node
type: entity_reference
settings:
target_type: media
module: core
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
......@@ -2,6 +2,8 @@
namespace Drupal\Tests\media_library\FunctionalJavascript;
use Drupal\Core\Url;
use Drupal\field_ui\FieldUI;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\media\Entity\Media;
use Drupal\media_library\MediaLibraryState;
......@@ -141,6 +143,135 @@ public function testAdministrationPage() {
$assert_session->linkExists('Add media');
}
/**
* Tests that the widget works as expected when media types are deleted.
*/
public function testWidgetWithoutMediaTypes() {
$assert_session = $this->assertSession();
$user = $this->drupalCreateUser([
'access administration pages',
'access content',
'create basic_page content',
'create media',
'view media',
]);
$this->drupalLogin($user);
$default_message = 'There are no allowed media types configured for this field. Please contact the site administrator.';
$this->drupalGet('node/add/basic_page');
// Assert a properly configured field does not show a message.
$assert_session->elementTextNotContains('css', '.field--name-field-twin-media', 'There are no allowed media types configured for this field.');
$assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is an empty array. No types are allowed in this
// case.
$assert_session->elementTextContains('css', '.field--name-field-empty-types-media', $default_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_empty_types_media"]');
// Assert that the message is not shown when the target_bundles setting for
// the entity reference field is null. All types are allowed in this case.
$assert_session->elementTextNotContains('css', '.field--name-field-null-types-media', 'There are no allowed media types configured for this field.');
$assert_session->elementExists('css', '.media-library-open-button[name^="field_null_types_media"]');
// Delete all media and media types.
$entity_type_manager = \Drupal::entityTypeManager();
$media_storage = $entity_type_manager->getStorage('media');
$media_type_storage = $entity_type_manager->getStorage('media_type');
$media_storage->delete($media_storage->loadMultiple());
$media_type_storage->delete($media_type_storage->loadMultiple());
// Visit a node create page.
$this->drupalGet('node/add/basic_page');
// Assert a properly configured field now shows a message.
$assert_session->elementTextContains('css', '.field--name-field-twin-media', $default_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_twin_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is an empty array.
$assert_session->elementTextContains('css', '.field--name-field-empty-types-media', $default_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_empty_types_media"]');
// Assert that the message is shown when the target_bundles setting for
// the entity reference field is null.
$assert_session->elementTextContains('css', '.field--name-field-null-types-media', $default_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_null_types_media"]');
// Assert a different message is shown when the user is allowed to
// administer the fields.
$user = $this->drupalCreateUser([
'access administration pages',
'access content',
'create basic_page content',
'view media',
'administer node fields',
]);
$this->drupalLogin($user);
$route_bundle_params = FieldUI::getRouteBundleParameter(\Drupal::entityTypeManager()->getDefinition('node'), 'basic_page');
$field_twin_url = new Url('entity.field_config.node_field_edit_form', [
'field_config' => 'node.basic_page.field_twin_media',
] + $route_bundle_params);
$field_twin_message = 'There are no allowed media types configured for this field. <a href="' . $field_twin_url->toString() . '">Edit the field settings</a> to select the allowed media types.';
$field_empty_types_url = new Url('entity.field_config.node_field_edit_form', [
'field_config' => 'node.basic_page.field_empty_types_media',
] + $route_bundle_params);
$field_empty_types_message = 'There are no allowed media types configured for this field. <a href="' . $field_empty_types_url->toString() . '">Edit the field settings</a> to select the allowed media types.';
$field_null_types_url = new Url('entity.field_config.node_field_edit_form', [
'field_config' => 'node.basic_page.field_null_types_media',
] + $route_bundle_params);
$field_null_types_message = 'There are no allowed media types configured for this field. <a href="' . $field_null_types_url->toString() . '">Edit the field settings</a> to select the allowed media types.';
// Visit a node create page.
$this->drupalGet('node/add/basic_page');
// Assert a properly configured field still shows a message.
$assert_session->elementContains('css', '.field--name-field-twin-media', $field_twin_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_twin_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is an empty array.
$assert_session->elementContains('css', '.field--name-field-empty-types-media', $field_empty_types_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_empty_types_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is null.
$assert_session->elementContains('css', '.field--name-field-null-types-media', $field_null_types_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_null_types_media"]');
// Assert the messages are also shown in the default value section of the
// field edit form.
$this->drupalGet($field_empty_types_url);
$assert_session->elementContains('css', '.field--name-field-empty-types-media', $field_empty_types_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_empty_types_media"]');
$this->drupalGet($field_null_types_url);
$assert_session->elementContains('css', '.field--name-field-null-types-media', $field_null_types_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_null_types_media"]');
// Uninstall the Field UI and check if the link is removed from the message.
\Drupal::service('module_installer')->uninstall(['field_ui']);
// Visit a node create page.
$this->drupalGet('node/add/basic_page');
$field_ui_uninstalled_message = 'There are no allowed media types configured for this field. Edit the field settings to select the allowed media types.';
// Assert the link is now longer part of the message.
$assert_session->elementNotExists('named', ['link', 'Edit the field settings']);
// Assert a properly configured field still shows a message.
$assert_session->elementContains('css', '.field--name-field-twin-media', $field_ui_uninstalled_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_twin_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is an empty array.
$assert_session->elementContains('css', '.field--name-field-empty-types-media', $field_ui_uninstalled_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_empty_types_media"]');
// Assert that the message is shown when the target_bundles setting for the
// entity reference field is null.
$assert_session->elementContains('css', '.field--name-field-null-types-media', $field_ui_uninstalled_message);
$assert_session->elementNotExists('css', '.media-library-open-button[name^="field_null_types_media"]');
}
/**
* Tests that the widget access works as expected.
*/
......@@ -245,9 +376,10 @@ public function testWidget() {
$page->find('css', '.ui-dialog-titlebar-close')->click();
$assert_session->assertWaitOnAjaxRequest();
// Assert that the media type menu is available when no types are configured
// for the field. All types should be available in this case.
$assert_session->elementExists('css', '.media-library-open-button[name^="field_empty_types_media"]')->click();
// Assert that the media type menu is available when the target_bundles
// setting for the entity reference field is null. All types should be
// allowed in this case.
$assert_session->elementExists('css', '.media-library-open-button[name^="field_null_types_media"]')->click();
$assert_session->assertWaitOnAjaxRequest();
$menu = $assert_session->elementExists('css', '.media-library-menu');
$this->assertTrue($menu->hasLink('Type One'));
......
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