diff -u b/core/modules/media/media.libraries.yml b/core/modules/media/media.libraries.yml --- b/core/modules/media/media.libraries.yml +++ b/core/modules/media/media.libraries.yml @@ -12,7 +12,7 @@ dependencies: - core/drupal.form -media_file_widget: +file_widget: version: VERSION js: 'js/media_file_widget.js': {} diff -u b/core/modules/media/src/Plugin/Field/FieldWidget/MediaFileWidget.php b/core/modules/media/src/Plugin/Field/FieldWidget/MediaFileWidget.php --- b/core/modules/media/src/Plugin/Field/FieldWidget/MediaFileWidget.php +++ b/core/modules/media/src/Plugin/Field/FieldWidget/MediaFileWidget.php @@ -15,6 +15,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\ElementInfoManagerInterface; +use Drupal\Core\Render\RendererInterface; use Drupal\Core\Url; use Drupal\file\Element\ManagedFile; use Drupal\file\Entity\File; @@ -31,7 +32,7 @@ * @FieldWidget( * id = "media_file", * label = @Translation("File"), - * media_handlers = { + * media_sources = { * "file" * }, * field_types = { @@ -56,6 +57,13 @@ protected $entityTypeManager; /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** * Constructs a MediaFileWidget object. * * @param string $plugin_id @@ -72,11 +80,14 @@ * Any third party settings. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ - public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, ElementInfoManagerInterface $element_info, EntityTypeManagerInterface $entity_type_manager) { + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, ElementInfoManagerInterface $element_info, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); $this->elementInfo = $element_info; $this->entityTypeManager = $entity_type_manager; + $this->renderer = $renderer; } /** @@ -90,7 +101,8 @@ $configuration['settings'], $configuration['third_party_settings'], $container->get('element_info'), - $container->get('entity_type.manager') + $container->get('entity_type.manager'), + $container->get('renderer') ); } @@ -135,21 +147,20 @@ * {@inheritdoc} */ public static function isApplicable(FieldDefinitionInterface $field_definition) { - if (parent::isApplicable($field_definition) && $field_definition->getSetting('target_type') == 'media') { - /** @var \Drupal\media\Entity\MediaType[] $bundles */ - $allowed_types = MediaType::loadMultiple($field_definition->getSetting('handler_settings')['target_bundles']); - - $media_handlers = []; - $definitions = \Drupal::service('plugin.manager.field.widget')->getDefinitions(); - foreach ($definitions as $definition) { - if ($definition['class'] == static::class) { - $media_handlers = $definition['media_handlers']; - } - } + $settings = $field_definition->getSettings(); + if (parent::isApplicable($field_definition) && $settings['target_type'] == 'media') { + /** @var \Drupal\media\MediaTypeInterface[] $allowed_types */ + $allowed_types = MediaType::loadMultiple($settings['handler_settings']['target_bundles']); + + $plugin_definition = \Drupal::service('plugin.manager.field.widget') + ->getDefinition('media_file'); + + // The widget applies if any of the media types that can be referenced by + // the field are using the media sources listed in the plugin's + // media_sources array. foreach ($allowed_types as $type) { - /** @var \Drupal\media\Entity\MediaType $type */ - if (in_array($type->getSource()->getPluginId(), $media_handlers)) { + if (in_array($type->get('source'), $plugin_definition['media_sources'])) { return TRUE; } } @@ -158,22 +169,10 @@ } /** - * Override form method. - * - * Special handling for updating items from the field state and for - * providing a ajax wrapper. - * - * @param \Drupal\Core\Field\FieldItemListInterface $items - * The fields items for the widget. - * @param array $form - * The widget form elements. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form_state of the widget form. - * @param int $get_delta - * Used to get only a specific delta value of a multiple value field. + * {@inheritdoc} * - * @return array - * The basic form structure. + * Special handling for updating items from the field state and for providing + * an Ajax wrapper. */ public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) { $field_name = $this->fieldDefinition->getName(); @@ -188,8 +187,8 @@ } $build = parent::form($items, $form, $form_state); - // Add a hidden textfield to the form, which is used to pass values - // from the modal to the widget. This is similar to how the + // Add a hidden textfield to the form, which is used to pass values from the + // modal to the widget. This is similar to how the // EntityReferenceAutocompleteWidget stores values. $build[$field_name . '_media_file_selection'] = [ '#type' => 'textfield', @@ -199,11 +198,14 @@ 'class' => ['visually-hidden'], ], ]; + // @TODO: Document this element. $build[$field_name . '_media_file_update_widget'] = [ '#type' => 'submit', '#name' => $field_name . '-media-file-update-button', '#value' => $this->t('Add selection'), - '#submit' => [[static::class, 'addItems']], + '#submit' => [ + [static::class, 'addItems'], + ], '#ajax' => [ 'callback' => [static::class, 'updateWidget'], 'wrapper' => $wrapper_id, @@ -216,42 +218,29 @@ '#limit_validation_errors' => [array_merge($parents, [$field_name])], ]; - // Wrap the widget in a div so that it can be re-loaded via AJAX. + // Wrap the widget in a DIV so that it can be re-loaded via AJAX. $build['#prefix'] = '
'; $build['#suffix'] = '
'; return $build; } /** - * Create a multivalue reference field. + * {@inheritdoc} * * Special handling for draggable multiple widgets and upload field. - * - * @param \Drupal\Core\Field\FieldItemListInterface $items - * The fields items for the widget. - * @param array $form - * The widget form elements. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form_state of the widget form. - * - * @return array - * The widget element. */ protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getName(); // Determine the number of widgets to display. $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); - switch ($cardinality) { - case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED: - $max = count($items); - $is_multiple = TRUE; - break; - - default: - $max = $cardinality - 1; - $is_multiple = ($cardinality > 1); - break; + if ($cardinality === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { + $max = count($items); + $is_multiple = TRUE; + } + else { + $max = $cardinality - 1; + $is_multiple = ($cardinality > 1); } $title = $this->fieldDefinition->getLabel(); @@ -294,20 +283,28 @@ } $empty_single_allowed = ($cardinality == 1 && $delta == 0); + // @TODO: Explain why programmed forms cannot be $empty_multiple_allowed. $empty_multiple_allowed = ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || $delta < $cardinality) && !$form_state->isProgrammed(); // Add a managed file field for new uploads except when this is a programmed // multiple form. if ($empty_single_allowed || $empty_multiple_allowed) { - // Fetch the upload validators from the source field. $upload_validators = []; - $allowed_types = MediaType::loadMultiple($this->fieldDefinition->getSetting('handler_settings')['target_bundles']); + + $allowed_types = $this->fieldDefinition->getSetting('handler_settings')['target_bundles']; + /** @var \Drupal\media\MediaTypeInterface[] $allowed_types */ + $allowed_types = $this->entityTypeManager + ->getStorage('media_type') + ->loadMultiple($allowed_types); + foreach ($allowed_types as $type) { - /** @var \Drupal\media\Entity\MediaType $type */ $source_field = $type->getSource()->getSourceFieldDefinition($type); $source_data_definition = FieldItemDataDefinition::create($source_field); $file_item = new FileItem($source_data_definition); + // @TODO: The upload validation should be smarter and as permissive as + // possible, since we don't know which media type will apply to the + // uploaded file. $upload_validators = array_merge($upload_validators, $file_item->getUploadValidators()); } @@ -324,9 +321,9 @@ '#type' => 'managed_file', '#required' => $delta == 0 && $this->fieldDefinition->isRequired(), '#upload_validators' => $upload_validators, - '#description' => \Drupal::service('renderer')->renderPlain($file_upload_help), - '#value_callback' => [get_class($this), 'uploadFieldValueCallback'], - '#process' => array_merge($element_info['#process'], [[get_class($this), 'uploadFieldProcess']]), + '#description' => $this->renderer->renderPlain($file_upload_help), + '#value_callback' => [static::class, 'uploadFieldValueCallback'], + '#process' => array_merge($element_info['#process'], [[static::class, 'uploadFieldProcess']]), '#progress_indicator' => $this->getSetting('progress_indicator'), '#extended' => TRUE, '#element_validate' => $element_info['#element_validate'], @@ -337,11 +334,11 @@ '#cardinality' => $cardinality, '#attached' => array_merge_recursive( $element_info['#attached'], - ['library' => ['media/media_file_widget']] + ['library' => ['media/file_widget']] ), - '#multiple' => $cardinality != 1 ? TRUE : FALSE, + '#multiple' => $cardinality != 1, ]; - if ($cardinality != 1 && $cardinality != -1) { + if ($cardinality > 1) { $elements['upload'][$delta][$field_name . '_media_file_upload']['#element_validate'] = [ [static::class, 'validateMultipleCount'], ]; @@ -357,7 +354,9 @@ $elements['#open'] = TRUE; $elements['#theme'] = 'media_file_widget_multiple'; $elements['#theme_wrappers'] = ['details']; - $elements['#process'] = [[get_class($this), 'processMultiple']]; + $elements['#process'] = [ + [static::class, 'processMultiple'], + ]; $elements['#title'] = $title; $elements['#description'] = $description; $elements['#field_name'] = $field_name; @@ -378,15 +377,15 @@ /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */ $referenced_entities = $items->referencedEntities(); if (isset($referenced_entities[$delta])) { - /** @var \Drupal\media\Entity\Media $media */ + /** @var \Drupal\media\MediaInterface $media */ $media = $referenced_entities[$delta]; /** @var \Drupal\field\FieldConfigInterface $source_field */ $source_field = $media->getSource()->getSourceFieldDefinition($media->bundle->entity); - /** @var \Drupal\file\Entity\File $file */ + /** @var \Drupal\file\FileInterface $file */ $file = $media->get($source_field->getName())->entity; - // We use a hidden field weith the media id and show a managed file - // to show the source field value for the media item. + // We use a hidden field with the media ID and show a managed file for the + // media item's source field value. $element += [ '#field_name' => $field_name, // Add media entity values for storage. @@ -413,7 +412,9 @@ '#remove_delta' => $element['#delta'], '#attributes' => ['class' => ['remove-button']], '#validate' => [], - '#submit' => [[static::class, 'removeItem']], + '#submit' => [ + [static::class, 'removeItem'], + ], '#limit_validation_errors' => [array_merge($form['#parents'], [$field_name])], '#ajax' => [ 'callback' => [static::class, 'updateWidget'], @@ -539,8 +540,8 @@ array_pop($array_parents); $previously_uploaded_count = count(Element::children(NestedArray::getValue($form, $array_parents))) - 1; - $field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($element['#entity_type']); - $field_storage = $field_storage_definitions[$element['#field_name']]; + /** @var FieldStorageDefinitionInterface $field_storage */ + $field_storage = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($element['#entity_type'])[$element['#field_name']]; $newly_uploaded_count = count($values['fids']); $total_uploaded_count = $newly_uploaded_count + $previously_uploaded_count; if ($total_uploaded_count > $field_storage->getCardinality()) { @@ -567,7 +568,7 @@ /** * Process callback for the upload field. * - * Change the ajax functionality for the upload button. + * Change the Ajax functionality for the upload button. * * @param array $element * The file field element array. @@ -614,7 +615,7 @@ /** * Ajax callback for the upload field. * - * This ajax callback takes care of the following things: + * This callback takes care of the following things: * - Add commands from ManagedFile::uploadAjaxCallback() to handle * file uploads. * - Pass the file IDs to a modal.