diff --git a/core/modules/media/config/schema/media.schema.yml b/core/modules/media/config/schema/media.schema.yml index dad518985a..7842248d22 100644 --- a/core/modules/media/config/schema/media.schema.yml +++ b/core/modules/media/config/schema/media.schema.yml @@ -58,3 +58,14 @@ media.source.field_aware: source_field: type: string label: 'Source field' + +field.widget.settings.media_image: + type: mapping + label: 'Media image field display format settings' + mapping: + progress_indicator: + type: string + label: 'Progress indicator' + preview_image_style: + type: string + label: 'Preview image style' diff --git a/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php b/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php index e5a907d590..a7d2beda24 100644 --- a/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php +++ b/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php @@ -6,6 +6,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\media\Entity\MediaType; +use Drupal\media\Entity\Media; /** * Plugin implementation of the 'media_image' widget. @@ -82,37 +83,111 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen } /** - * Assign the right preview_image_style based on what the user chose, and get - * the resulting image's height and width. + * If we have an image media entity to work with: add the alt and title fields + * if they are required, assign the right preview_image_style, and get the + * resulting image's height and width. * * This method is assigned as a #process callback in formElement() method. */ public static function process($element, FormStateInterface $form_state, $form) { $element = parent::process($element, $form_state, $form); $mid = FALSE; + $show_form = FALSE; if (!empty($element['#value']['target_id'])) { $mid = $element['#value']['target_id']; } else if (!empty($element['#value']['mids']) && count($element['#value']['mids']) === 1) { $mid = $element['#value']['mids'][0]; - } - if ($mid && $element['#preview_image_style']) { - $icon_element = &$element['media_' . $mid]['media_icon']; - $icon_element['#style_name'] = $element['#preview_image_style']; + // Don't show any kind of form (even for required elements!) if this media + // has already been successfully saved via this form. + if (empty($element['#value']['media_already_saved'])) { + $show_form = TRUE; + } + } - // Let ImageWidget do the work of figuring out height and width. (It will - // work on the target media entity's underlying File object, since we - // still have that stored in $element['#files'].) - $dummy_element = ImageWidget::process($element, $form_state, $form)['preview']; - $icon_element['#height'] = $dummy_element['#height']; - $icon_element['#width'] = $dummy_element['#width']; + if ($mid) { + $media_form_element = &$element['media_' . $mid]; + $entity = Media::load($mid); + /** @var \Drupal\Core\Field\FieldDefinitionInterface $source_field_definition */ + $source_field_definition = $entity->getSource()->getSourceFieldDefinition($entity->bundle->entity); + $alt_field = $source_field_definition->getSetting('alt_field') ?: FALSE; + $alt_field_required = $source_field_definition->getSetting('alt_field_required') ?: FALSE; + $title_field = $source_field_definition->getSetting('title_field') ?: FALSE; + $title_field_required = $source_field_definition->getSetting('title_field_required') ?: FALSE; + + // Get only _some_ logic from ImageWidget::process(), which complains + // without some kind of values for the alt and title field keys. + $element['#alt_field'] = $alt_field; + $element['#alt_field_required'] = $alt_field_required; + $element['#title_field'] = $title_field; + $element['#title_field_required'] = $title_field_required; + $dummy_element = ImageWidget::process($element, $form_state, $form); + + if ($show_form) { + // If the media source field is an image, and the alt field is both + // available and required, require it here too. + if ($alt_field && $alt_field_required) { + $media_form_element['alt'] = $dummy_element['alt']; + $media_form_element['alt']['#weight'] = -5; + } + + // Same as above, but for title field. + if ($title_field && $title_field_required) { + $media_form_element['title'] = $dummy_element['title']; + $media_form_element['title']['#weight'] = -3; + } + } + + // Only show an image preview if we have a style set for it. + if ($element['#preview_image_style']) { + $icon_element = $media_form_element['media_icon']; + $icon_element['#style_name'] = $element['#preview_image_style']; + + // Let ImageWidget do the work of figuring out height and width. (It + // will work on the target media entity's underlying File object, since + // we still have that stored in $element['#files'].) + $icon_element['#height'] = $dummy_element['preview']['#height']; + $icon_element['#width'] = $dummy_element['preview']['#width']; + } } return $element; } + /** + * {@inheritdoc} + */ + public static function value($element, $input, FormStateInterface $form_state) { + if (!empty($input['mids'])) { + $mid = $input['mids']; + $media_fields_key = 'media_' . $mid; + + // If the "alt" and/or "title" input values are here, it's because the + // site builder made them required for this media item's source field + // (i.e. an image field). + foreach (['alt', 'title'] as $extra_image_field_attribute) { + if (!empty($input[$media_fields_key][$extra_image_field_attribute])) { + $media_entity = Media::load($mid); + $source_field = $media_entity->getSource() + ->getSourceFieldDefinition($media_entity->bundle->entity) + ->getName(); + $source_val = $media_entity->get($source_field)->getValue(); + $source_val[0][$extra_image_field_attribute] = $input[$media_fields_key][$extra_image_field_attribute]; + $media_entity->set($source_field, $source_val); + unset($input[$media_fields_key][$extra_image_field_attribute]); + } + + if (isset($media_entity)) { + $media_entity->save(); + } + } + } + + return parent::value($element, $input, $form_state); + } + /** * {@inheritdoc} */ diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php new file mode 100644 index 0000000000..00912a0bf9 --- /dev/null +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php @@ -0,0 +1,128 @@ +createMediaType(['bundle' => $bundle], 'image'); + $this->mediaTypeId = $media_type->id(); + + // For the Image field, "alt required" is on by default. We'll also turn on + // "title" and "title required". + $media_type->getSource() + ->getSourceFieldDefinition($media_type) + ->getConfig($bundle) + ->setSetting('title_field', TRUE) + ->setSetting('title_field_required', TRUE) + ->save(); + + // Create the node type and add the media-reference field. + $node_type = $this->drupalCreateContentType(); + $this->nodeTypeId = $node_type->id(); + $field_storage = FieldStorageConfig::create([ + 'type' => 'entity_reference', + 'cardinality' => -1, + 'entity_type' => 'node', + 'field_name' => 'field_media_reference', + 'settings' => [ + 'target_type' => 'media', + ], + ]); + $field_storage->save(); + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $this->nodeTypeId, + 'settings' => [ + 'handler_settings' => [ + 'target_bundles' => [ + $this->mediaTypeId => $this->mediaTypeId, + ], + ], + ], + ])->save(); + + // Ensure that the field, with our widget, shows up on the default + // node-entry form, and also that its values appear on the default node + // display. + entity_get_form_display('node', $this->nodeTypeId, 'default') + ->setComponent('field_media_reference', [ + 'type' => 'media_image', + 'settings' => [ + 'progress_indicator' => 'throbber', + 'preview_image_syle' => 'thumbnail' + ] + ]) + ->save(); + entity_get_display('node', $this->nodeTypeId, 'default') + ->setComponent('field_media_reference', [ + 'type' => 'entity_reference_entity_view', + 'label' => 'hidden', + 'settings' => [ + 'view_mode' => 'full', + ], + ])->save(); + } + + /** + * Tests the file widget behavior. + */ + public function testInlineImageWidget() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + + $this->drupalGet('/node/add/' . $this->nodeTypeId); + + // Ensure the media field is present. + $media_field = $assert_session->elementExists('css', 'details[data-drupal-selector="edit-field-media-reference"]'); + + // Upload a file, and wait long enough for the form to be redrawn. + $upload_element_id = 'edit-field-media-reference-0-upload'; + $image_to_upload = drupal_get_path('module', 'media') . '/tests/fixtures/example_1.jpeg'; + $page->attachFileToField($upload_element_id, $image_to_upload); + $result = $assert_session->waitForButton('Remove'); + $this->assertNotEmpty($result); + + // Ensure that the image preview has appeared. + $assert_session->elementExists('css', 'img[data-drupal-selector$="-media-icon"]', $media_field); + + // The alt field should now be present and required. + $assert_session->elementExists('css', 'input[data-drupal-selector$="-alt"].required', $media_field); + + // The title field should also be present and required. + $assert_session->elementExists('css', 'input[data-drupal-selector$="-title"].required', $media_field); + } + +}