summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/media
diff options
context:
space:
mode:
authorDave Long <dave@longwaveconsulting.com>2024-11-14 18:16:42 +0000
committerDave Long <dave@longwaveconsulting.com>2024-11-14 18:16:42 +0000
commit8aeb2ca5992dc3aecfb654f098caeb26d794a960 (patch)
tree77483ed9f5b108b2cd545cd15338915a77db888f /core/modules/media
parent55bab3403cba9ae3797f3d2ca7482ae68040efae (diff)
downloaddrupal-8aeb2ca5992dc3aecfb654f098caeb26d794a960.tar.gz
drupal-8aeb2ca5992dc3aecfb654f098caeb26d794a960.zip
Issue #3483599 by nicxvan, ghost of drupal past, catch, longwave, fabianx: Convert all procedural hook implementations to Hook classes
Diffstat (limited to 'core/modules/media')
-rw-r--r--core/modules/media/media.api.php8
-rw-r--r--core/modules/media/media.module281
-rw-r--r--core/modules/media/src/Hook/MediaHooks.php299
-rw-r--r--core/modules/media/tests/modules/media_test_embed/media_test_embed.module20
-rw-r--r--core/modules/media/tests/modules/media_test_embed/src/Hook/MediaTestEmbedHooks.php37
-rw-r--r--core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module11
-rw-r--r--core/modules/media/tests/modules/media_test_oembed/src/Hook/MediaTestOembedHooks.php25
-rw-r--r--core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiAddTest.php2
-rw-r--r--core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiEditTest.php2
9 files changed, 370 insertions, 315 deletions
diff --git a/core/modules/media/media.api.php b/core/modules/media/media.api.php
index 93244f58a8a..1a170059613 100644
--- a/core/modules/media/media.api.php
+++ b/core/modules/media/media.api.php
@@ -2,6 +2,12 @@
/**
* @file
+ */
+
+use Drupal\media\OEmbed\Provider;
+
+/**
+ * @file
* Hooks related to Media and its plugins.
*/
@@ -30,7 +36,7 @@ function hook_media_source_info_alter(array &$sources) {
*
* @see \Drupal\media\OEmbed\UrlResolverInterface::getResourceUrl()
*/
-function hook_oembed_resource_url_alter(array &$parsed_url, \Drupal\media\OEmbed\Provider $provider) {
+function hook_oembed_resource_url_alter(array &$parsed_url, Provider $provider) {
// Always serve YouTube videos from youtube-nocookie.com.
if ($provider->getName() === 'YouTube') {
$parsed_url['path'] = str_replace('://youtube.com/', '://youtube-nocookie.com/', $parsed_url['path']);
diff --git a/core/modules/media/media.module b/core/modules/media/media.module
index 1bd372bdd71..d10dd860195 100644
--- a/core/modules/media/media.module
+++ b/core/modules/media/media.module
@@ -2,107 +2,15 @@
/**
* @file
- * Provides media items.
*/
use Drupal\Component\Plugin\DerivativeInspectionInterface;
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElementBase;
-use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
-use Drupal\field\FieldConfigInterface;
use Drupal\media\Plugin\media\Source\OEmbedInterface;
-use Drupal\views\ViewExecutable;
-
-/**
- * Implements hook_help().
- */
-function media_help($route_name, RouteMatchInterface $route_match) {
- switch ($route_name) {
- case 'help.page.media':
- $output = '<h2>' . t('About') . '</h2>';
- $output .= '<p>' . t('The Media module manages the creation, editing, deletion, settings, and display of media. Items are typically images, documents, slideshows, YouTube videos, tweets, Instagram photos, etc. You can reference media items from any other content on your site. For more information, see the <a href=":media">online documentation for the Media module</a>.', [':media' => 'https://www.drupal.org/docs/8/core/modules/media']) . '</p>';
- $output .= '<h2>' . t('Uses') . '</h2>';
- $output .= '<dl>';
- $output .= '<dt>' . t('Creating media items') . '</dt>';
- $output .= '<dd>' . t('When a new media item is created, the Media module records basic information about it, including the author, date of creation, and the <a href=":media-type">media type</a>. It also manages the <em>publishing options</em>, which define whether or not the item is published. Default settings can be configured for each type of media on your site.', [':media-type' => Url::fromRoute('entity.media_type.collection')->toString()]) . '</dd>';
- $output .= '<dt>' . t('Listing media items') . '</dt>';
- $output .= '<dd>' . t('Media items are listed at the <a href=":media-collection">media administration page</a>.', [
- ':media-collection' => Url::fromRoute('entity.media.collection')->toString(),
- ]) . '</dd>';
- $output .= '<dt>' . t('Creating custom media types') . '</dt>';
- $output .= '<dd>' . t('The Media module gives users with the <em>Administer media types</em> permission the ability to <a href=":media-new">create new media types</a> in addition to the default ones already configured. Each media type has an associated media source (such as the image source) which support thumbnail generation and metadata extraction. Fields managed by the <a href=":field">Field module</a> may be added for storing that metadata, such as width and height, as well as any other associated values.', [
- ':media-new' => Url::fromRoute('entity.media_type.add_form')->toString(),
- ':field' => Url::fromRoute('help.page', ['name' => 'field'])->toString(),
- ]) . '</dd>';
- $output .= '<dt>' . t('Creating revisions') . '</dt>';
- $output .= '<dd>' . t('The Media module also enables you to create multiple versions of any media item, and revert to older versions using the <em>Revision information</em> settings.') . '</dd>';
- $output .= '<dt>' . t('User permissions') . '</dt>';
- $output .= '<dd>' . t('The Media module makes a number of permissions available, which can be set by role on the <a href=":permissions">permissions page</a>.', [
- ':permissions' => Url::fromRoute('user.admin_permissions.module', ['modules' => 'media'])->toString(),
- ]) . '</dd>';
- $output .= '<dt>' . t('Adding media to other content') . '</dt>';
- $output .= '<dd>' . t('Users with permission to administer content types can add media support by adding a media reference field to the content type on the content type administration page. (The same is true of block types, taxonomy terms, user profiles, and other content that supports fields.) A media reference field can refer to any configured media type. It is possible to allow multiple media types in the same field.') . '</dd>';
- $output .= '</dl>';
- $output .= '<h2>' . t('Differences between Media, File, and Image reference fields') . '</h2>';
- $output .= '<p>' . t('<em>Media</em> reference fields offer several advantages over basic <em>File</em> and <em>Image</em> references:') . '</p>';
- $output .= '<ul>';
- $output .= '<li>' . t('Media reference fields can reference multiple media types in the same field.') . '</li>';
- $output .= '<li>' . t('Fields can also be added to media types themselves, which means that custom metadata like descriptions and taxonomy tags can be added for the referenced media. (Basic file and image fields do not support this.)') . '</li>';
- $output .= '<li>' . t('Media types for audio and video files are provided by default, so there is no need for additional configuration to upload these media.') . '</li>';
- $output .= '<li>' . t('Contributed or custom projects can provide additional media sources (such as third-party websites, Twitter, etc.).') . '</li>';
- $output .= '<li>' . t('Existing media items can be reused on any other content items with a media reference field.') . '</li>';
- $output .= '</ul>';
- $output .= '<p>' . t('Use <em>Media</em> reference fields for most files, images, audio, videos, and remote media. Use <em>File</em> or <em>Image</em> reference fields when creating your own media types, or for legacy files and images created before installing the Media module.') . '</p>';
- return $output;
- }
-}
-
-/**
- * Implements hook_theme().
- */
-function media_theme(): array {
- return [
- 'media' => [
- 'render element' => 'elements',
- ],
- 'media_reference_help' => [
- 'render element' => 'element',
- 'base hook' => 'field_multiple_value_form',
- ],
- 'media_oembed_iframe' => [
- 'variables' => [
- 'resource' => NULL,
- 'media' => NULL,
- 'placeholder_token' => '',
- ],
- ],
- 'media_embed_error' => [
- 'variables' => [
- 'message' => NULL,
- 'attributes' => [],
- ],
- ],
- ];
-}
-
-/**
- * Implements hook_entity_access().
- */
-function media_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
- if ($operation === 'delete' && $entity instanceof FieldConfigInterface && $entity->getTargetEntityTypeId() === 'media') {
- /** @var \Drupal\media\MediaTypeInterface $media_type */
- $media_type = \Drupal::entityTypeManager()->getStorage('media_type')->load($entity->getTargetBundle());
- return AccessResult::forbiddenIf($entity->id() === 'media.' . $media_type->id() . '.' . $media_type->getSource()->getConfiguration()['source_field']);
- }
- return AccessResult::neutral();
-}
/**
* Implements hook_theme_suggestions_HOOK().
@@ -169,135 +77,6 @@ function template_preprocess_media(array &$variables) {
}
/**
- * Implements hook_field_ui_preconfigured_options_alter().
- */
-function media_field_ui_preconfigured_options_alter(array &$options, $field_type) {
- // If the field is not an "entity_reference"-based field, bail out.
- /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
- $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
- $class = $field_type_manager->getPluginClass($field_type);
- if (!is_a($class, 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', TRUE)) {
- return;
- }
-
- // Set the default formatter for media in entity reference fields to be the
- // "Rendered entity" formatter.
- if (!empty($options['media'])) {
- $options['media']['description'] = t('Field to reference media. Allows uploading and selecting from uploaded media.');
- $options['media']['weight'] = -25;
- $options['media']['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
- $options['media']['entity_view_display']['type'] = 'entity_reference_entity_view';
- }
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function media_form_field_ui_field_storage_add_form_alter(&$form, FormStateInterface $form_state, $form_id): void {
- // Provide some help text to aid users decide whether they need a Media,
- // File, or Image reference field.
- $description_text = t('Use <em>Media</em> reference fields for most files, images, audio, videos, and remote media. Use <em>File</em> or <em>Image</em> reference fields when creating your own media types, or for legacy files and images created before installing the Media module.');
- if (\Drupal::moduleHandler()->moduleExists('help')) {
- $description_text .= ' ' . t('For more information, see the <a href="@help_url">Media help page</a>.', [
- '@help_url' => Url::fromRoute('help.page', ['name' => 'media'])->toString(),
- ]);
- }
- $field_types = [
- 'file_upload',
- 'field_ui:entity_reference:media',
- ];
- if (in_array($form_state->getValue('new_storage_type'), $field_types)) {
- $form['group_field_options_wrapper']['description_wrapper'] = [
- '#type' => 'item',
- '#markup' => $description_text,
- ];
- }
-}
-
-/**
- * Implements hook_field_widget_complete_form_alter().
- */
-function media_field_widget_complete_form_alter(array &$field_widget_complete_form, FormStateInterface $form_state, array $context) {
- $elements = &$field_widget_complete_form['widget'];
- // Do not alter the default settings form.
- if ($context['default']) {
- return;
- }
-
- // Only act on entity reference fields that reference media.
- $field_type = $context['items']->getFieldDefinition()->getType();
- $target_type = $context['items']->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type');
- if ($field_type !== 'entity_reference' || $target_type !== 'media') {
- return;
- }
-
- // Autocomplete widgets need different help text than options widgets.
- $widget_plugin_id = $context['widget']->getPluginId();
- if (in_array($widget_plugin_id, ['entity_reference_autocomplete', 'entity_reference_autocomplete_tags'])) {
- $is_autocomplete = TRUE;
- }
- else {
- // @todo We can't yet properly alter non-autocomplete fields. Resolve this
- // in https://www.drupal.org/node/2943020 and remove this condition.
- return;
- }
- $elements['#media_help'] = [];
-
- // Retrieve the media bundle list and add information for the user based on
- // which bundles are available to be created or referenced.
- $settings = $context['items']->getFieldDefinition()->getSetting('handler_settings');
- $allowed_bundles = !empty($settings['target_bundles']) ? $settings['target_bundles'] : [];
- $add_url = _media_get_add_url($allowed_bundles);
- if ($add_url) {
- $elements['#media_help']['#media_add_help'] = t('Create your media on the <a href=":add_page" target="_blank">media add page</a> (opens a new window), then add it by name to the field below.', [':add_page' => $add_url]);
- }
-
- $elements['#theme'] = 'media_reference_help';
- // @todo template_preprocess_field_multiple_value_form() assumes this key
- // exists, but it does not exist in the case of a single widget that
- // accepts multiple values. This is for some reason necessary to use
- // our template for the entity_autocomplete_tags widget.
- // Research and resolve this in https://www.drupal.org/node/2943020.
- if (empty($elements['#cardinality_multiple'])) {
- $elements['#cardinality_multiple'] = NULL;
- }
-
- // Use the title set on the element if it exists, otherwise fall back to the
- // field label.
- $elements['#media_help']['#original_label'] = $elements['#title'] ?? $context['items']->getFieldDefinition()->getLabel();
-
- // Customize the label for the field widget.
- // @todo Research a better approach https://www.drupal.org/node/2943024.
- $use_existing_label = t('Use existing media');
- if (!empty($elements[0]['target_id']['#title'])) {
- $elements[0]['target_id']['#title'] = $use_existing_label;
- }
- if (!empty($elements['#title'])) {
- $elements['#title'] = $use_existing_label;
- }
- if (!empty($elements['target_id']['#title'])) {
- $elements['target_id']['#title'] = $use_existing_label;
- }
-
- // This help text is only relevant for autocomplete widgets. When the user
- // is presented with options, they don't need to type anything or know what
- // types of media are allowed.
- if ($is_autocomplete) {
- $elements['#media_help']['#media_list_help'] = t('Type part of the media name.');
-
- $overview_url = Url::fromRoute('entity.media.collection');
- if ($overview_url->access()) {
- $elements['#media_help']['#media_list_link'] = t('See the <a href=":list_url" target="_blank">media list</a> (opens a new window) to help locate media.', [':list_url' => $overview_url->toString()]);
- }
- $all_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('media');
- $bundle_labels = array_map(function ($bundle) use ($all_bundles) {
- return $all_bundles[$bundle]['label'];
- }, $allowed_bundles);
- $elements['#media_help']['#allowed_types_help'] = t('Allowed media types: %types', ['%types' => implode(", ", $bundle_labels)]);
- }
-}
-
-/**
* Implements hook_preprocess_HOOK() for media reference widgets.
*/
function media_preprocess_media_reference_help(&$variables) {
@@ -352,35 +131,6 @@ function _media_get_add_url($allowed_bundles) {
}
/**
- * Implements hook_entity_type_alter().
- */
-function media_entity_type_alter(array &$entity_types): void {
- if (\Drupal::config('media.settings')->get('standalone_url')) {
- /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
- $entity_type = $entity_types['media'];
- $entity_type->setLinkTemplate('canonical', '/media/{media}');
- }
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function media_form_filter_format_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id): void {
- // Add an additional validate callback so we can ensure the order of filters
- // is correct.
- $form['#validate'][] = 'media_filter_format_edit_form_validate';
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function media_form_filter_format_add_form_alter(array &$form, FormStateInterface $form_state, $form_id): void {
- // Add an additional validate callback so we can ensure the order of filters
- // is correct.
- $form['#validate'][] = 'media_filter_format_edit_form_validate';
-}
-
-/**
* Validate callback to ensure filter order and allowed_html are compatible.
*/
function media_filter_format_edit_form_validate($form, FormStateInterface $form_state) {
@@ -498,34 +248,3 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
$form_state->setErrorByName('filters', $error_message);
}
}
-
-/**
- * Implements hook_field_widget_single_element_form_alter().
- */
-function media_field_widget_single_element_form_alter(&$element, FormStateInterface $form_state, $context) {
- // Add an attribute so that text editors plugins can pass the host entity's
- // language, allowing it to present entities in the same language.
- if (!empty($element['#type']) && $element['#type'] == 'text_format') {
- $element['#attributes']['data-media-embed-host-entity-langcode'] = $context['items']->getLangcode();
- }
-}
-
-/**
- * Implements hook_views_query_substitutions().
- */
-function media_views_query_substitutions(ViewExecutable $view) {
- $account = \Drupal::currentUser();
- return [
- '***VIEW_OWN_UNPUBLISHED_MEDIA***' => (int) $account->hasPermission('view own unpublished media'),
- '***ADMINISTER_MEDIA***' => (int) $account->hasPermission('administer media'),
- ];
-}
-
-/**
- * Implements hook_field_type_category_info_alter().
- */
-function media_field_type_category_info_alter(&$definitions) {
- // The `media` field type belongs in the `general` category, so the libraries
- // need to be attached using an alter hook.
- $definitions[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY]['libraries'][] = 'media/drupal.media-icon';
-}
diff --git a/core/modules/media/src/Hook/MediaHooks.php b/core/modules/media/src/Hook/MediaHooks.php
new file mode 100644
index 00000000000..b39aa8aa294
--- /dev/null
+++ b/core/modules/media/src/Hook/MediaHooks.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace Drupal\media\Hook;
+
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\field\FieldConfigInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for media.
+ */
+class MediaHooks {
+
+ /**
+ * Implements hook_help().
+ */
+ #[Hook('help')]
+ public function help($route_name, RouteMatchInterface $route_match) {
+ switch ($route_name) {
+ case 'help.page.media':
+ $output = '<h2>' . t('About') . '</h2>';
+ $output .= '<p>' . t('The Media module manages the creation, editing, deletion, settings, and display of media. Items are typically images, documents, slideshows, YouTube videos, tweets, Instagram photos, etc. You can reference media items from any other content on your site. For more information, see the <a href=":media">online documentation for the Media module</a>.', [':media' => 'https://www.drupal.org/docs/8/core/modules/media']) . '</p>';
+ $output .= '<h2>' . t('Uses') . '</h2>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Creating media items') . '</dt>';
+ $output .= '<dd>' . t('When a new media item is created, the Media module records basic information about it, including the author, date of creation, and the <a href=":media-type">media type</a>. It also manages the <em>publishing options</em>, which define whether or not the item is published. Default settings can be configured for each type of media on your site.', [
+ ':media-type' => Url::fromRoute('entity.media_type.collection')->toString(),
+ ]) . '</dd>';
+ $output .= '<dt>' . t('Listing media items') . '</dt>';
+ $output .= '<dd>' . t('Media items are listed at the <a href=":media-collection">media administration page</a>.', [
+ ':media-collection' => Url::fromRoute('entity.media.collection')->toString(),
+ ]) . '</dd>';
+ $output .= '<dt>' . t('Creating custom media types') . '</dt>';
+ $output .= '<dd>' . t('The Media module gives users with the <em>Administer media types</em> permission the ability to <a href=":media-new">create new media types</a> in addition to the default ones already configured. Each media type has an associated media source (such as the image source) which support thumbnail generation and metadata extraction. Fields managed by the <a href=":field">Field module</a> may be added for storing that metadata, such as width and height, as well as any other associated values.', [
+ ':media-new' => Url::fromRoute('entity.media_type.add_form')->toString(),
+ ':field' => Url::fromRoute('help.page', [
+ 'name' => 'field',
+ ])->toString(),
+ ]) . '</dd>';
+ $output .= '<dt>' . t('Creating revisions') . '</dt>';
+ $output .= '<dd>' . t('The Media module also enables you to create multiple versions of any media item, and revert to older versions using the <em>Revision information</em> settings.') . '</dd>';
+ $output .= '<dt>' . t('User permissions') . '</dt>';
+ $output .= '<dd>' . t('The Media module makes a number of permissions available, which can be set by role on the <a href=":permissions">permissions page</a>.', [
+ ':permissions' => Url::fromRoute('user.admin_permissions.module', [
+ 'modules' => 'media',
+ ])->toString(),
+ ]) . '</dd>';
+ $output .= '<dt>' . t('Adding media to other content') . '</dt>';
+ $output .= '<dd>' . t('Users with permission to administer content types can add media support by adding a media reference field to the content type on the content type administration page. (The same is true of block types, taxonomy terms, user profiles, and other content that supports fields.) A media reference field can refer to any configured media type. It is possible to allow multiple media types in the same field.') . '</dd>';
+ $output .= '</dl>';
+ $output .= '<h2>' . t('Differences between Media, File, and Image reference fields') . '</h2>';
+ $output .= '<p>' . t('<em>Media</em> reference fields offer several advantages over basic <em>File</em> and <em>Image</em> references:') . '</p>';
+ $output .= '<ul>';
+ $output .= '<li>' . t('Media reference fields can reference multiple media types in the same field.') . '</li>';
+ $output .= '<li>' . t('Fields can also be added to media types themselves, which means that custom metadata like descriptions and taxonomy tags can be added for the referenced media. (Basic file and image fields do not support this.)') . '</li>';
+ $output .= '<li>' . t('Media types for audio and video files are provided by default, so there is no need for additional configuration to upload these media.') . '</li>';
+ $output .= '<li>' . t('Contributed or custom projects can provide additional media sources (such as third-party websites, Twitter, etc.).') . '</li>';
+ $output .= '<li>' . t('Existing media items can be reused on any other content items with a media reference field.') . '</li>';
+ $output .= '</ul>';
+ $output .= '<p>' . t('Use <em>Media</em> reference fields for most files, images, audio, videos, and remote media. Use <em>File</em> or <em>Image</em> reference fields when creating your own media types, or for legacy files and images created before installing the Media module.') . '</p>';
+ return $output;
+ }
+ }
+
+ /**
+ * Implements hook_theme().
+ */
+ #[Hook('theme')]
+ public function theme() : array {
+ return [
+ 'media' => [
+ 'render element' => 'elements',
+ ],
+ 'media_reference_help' => [
+ 'render element' => 'element',
+ 'base hook' => 'field_multiple_value_form',
+ ],
+ 'media_oembed_iframe' => [
+ 'variables' => [
+ 'resource' => NULL,
+ 'media' => NULL,
+ 'placeholder_token' => '',
+ ],
+ ],
+ 'media_embed_error' => [
+ 'variables' => [
+ 'message' => NULL,
+ 'attributes' => [],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Implements hook_entity_access().
+ */
+ #[Hook('entity_access')]
+ public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+ if ($operation === 'delete' && $entity instanceof FieldConfigInterface && $entity->getTargetEntityTypeId() === 'media') {
+ /** @var \Drupal\media\MediaTypeInterface $media_type */
+ $media_type = \Drupal::entityTypeManager()->getStorage('media_type')->load($entity->getTargetBundle());
+ return AccessResult::forbiddenIf($entity->id() === 'media.' . $media_type->id() . '.' . $media_type->getSource()->getConfiguration()['source_field']);
+ }
+ return AccessResult::neutral();
+ }
+
+ /**
+ * Implements hook_field_ui_preconfigured_options_alter().
+ */
+ #[Hook('field_ui_preconfigured_options_alter')]
+ public function fieldUiPreconfiguredOptionsAlter(array &$options, $field_type) {
+ // If the field is not an "entity_reference"-based field, bail out.
+ /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
+ $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
+ $class = $field_type_manager->getPluginClass($field_type);
+ if (!is_a($class, 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', TRUE)) {
+ return;
+ }
+ // Set the default formatter for media in entity reference fields to be the
+ // "Rendered entity" formatter.
+ if (!empty($options['media'])) {
+ $options['media']['description'] = t('Field to reference media. Allows uploading and selecting from uploaded media.');
+ $options['media']['weight'] = -25;
+ $options['media']['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
+ $options['media']['entity_view_display']['type'] = 'entity_reference_entity_view';
+ }
+ }
+
+ /**
+ * Implements hook_form_FORM_ID_alter().
+ */
+ #[Hook('form_field_ui_field_storage_add_form_alter')]
+ public function formFieldUiFieldStorageAddFormAlter(&$form, FormStateInterface $form_state, $form_id) : void {
+ // Provide some help text to aid users decide whether they need a Media,
+ // File, or Image reference field.
+ $description_text = t('Use <em>Media</em> reference fields for most files, images, audio, videos, and remote media. Use <em>File</em> or <em>Image</em> reference fields when creating your own media types, or for legacy files and images created before installing the Media module.');
+ if (\Drupal::moduleHandler()->moduleExists('help')) {
+ $description_text .= ' ' . t('For more information, see the <a href="@help_url">Media help page</a>.', [
+ '@help_url' => Url::fromRoute('help.page', [
+ 'name' => 'media',
+ ])->toString(),
+ ]);
+ }
+ $field_types = ['file_upload', 'field_ui:entity_reference:media'];
+ if (in_array($form_state->getValue('new_storage_type'), $field_types)) {
+ $form['group_field_options_wrapper']['description_wrapper'] = ['#type' => 'item', '#markup' => $description_text];
+ }
+ }
+
+ /**
+ * Implements hook_field_widget_complete_form_alter().
+ */
+ #[Hook('field_widget_complete_form_alter')]
+ public function fieldWidgetCompleteFormAlter(array &$field_widget_complete_form, FormStateInterface $form_state, array $context) {
+ $elements =& $field_widget_complete_form['widget'];
+ // Do not alter the default settings form.
+ if ($context['default']) {
+ return;
+ }
+ // Only act on entity reference fields that reference media.
+ $field_type = $context['items']->getFieldDefinition()->getType();
+ $target_type = $context['items']->getFieldDefinition()->getFieldStorageDefinition()->getSetting('target_type');
+ if ($field_type !== 'entity_reference' || $target_type !== 'media') {
+ return;
+ }
+ // Autocomplete widgets need different help text than options widgets.
+ $widget_plugin_id = $context['widget']->getPluginId();
+ if (in_array($widget_plugin_id, ['entity_reference_autocomplete', 'entity_reference_autocomplete_tags'])) {
+ $is_autocomplete = TRUE;
+ }
+ else {
+ // @todo We can't yet properly alter non-autocomplete fields. Resolve this
+ // in https://www.drupal.org/node/2943020 and remove this condition.
+ return;
+ }
+ $elements['#media_help'] = [];
+ // Retrieve the media bundle list and add information for the user based on
+ // which bundles are available to be created or referenced.
+ $settings = $context['items']->getFieldDefinition()->getSetting('handler_settings');
+ $allowed_bundles = !empty($settings['target_bundles']) ? $settings['target_bundles'] : [];
+ $add_url = _media_get_add_url($allowed_bundles);
+ if ($add_url) {
+ $elements['#media_help']['#media_add_help'] = t('Create your media on the <a href=":add_page" target="_blank">media add page</a> (opens a new window), then add it by name to the field below.', [':add_page' => $add_url]);
+ }
+ $elements['#theme'] = 'media_reference_help';
+ // @todo template_preprocess_field_multiple_value_form() assumes this key
+ // exists, but it does not exist in the case of a single widget that
+ // accepts multiple values. This is for some reason necessary to use
+ // our template for the entity_autocomplete_tags widget.
+ // Research and resolve this in https://www.drupal.org/node/2943020.
+ if (empty($elements['#cardinality_multiple'])) {
+ $elements['#cardinality_multiple'] = NULL;
+ }
+ // Use the title set on the element if it exists, otherwise fall back to the
+ // field label.
+ $elements['#media_help']['#original_label'] = $elements['#title'] ?? $context['items']->getFieldDefinition()->getLabel();
+ // Customize the label for the field widget.
+ // @todo Research a better approach https://www.drupal.org/node/2943024.
+ $use_existing_label = t('Use existing media');
+ if (!empty($elements[0]['target_id']['#title'])) {
+ $elements[0]['target_id']['#title'] = $use_existing_label;
+ }
+ if (!empty($elements['#title'])) {
+ $elements['#title'] = $use_existing_label;
+ }
+ if (!empty($elements['target_id']['#title'])) {
+ $elements['target_id']['#title'] = $use_existing_label;
+ }
+ // This help text is only relevant for autocomplete widgets. When the user
+ // is presented with options, they don't need to type anything or know what
+ // types of media are allowed.
+ if ($is_autocomplete) {
+ $elements['#media_help']['#media_list_help'] = t('Type part of the media name.');
+ $overview_url = Url::fromRoute('entity.media.collection');
+ if ($overview_url->access()) {
+ $elements['#media_help']['#media_list_link'] = t('See the <a href=":list_url" target="_blank">media list</a> (opens a new window) to help locate media.', [':list_url' => $overview_url->toString()]);
+ }
+ $all_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('media');
+ $bundle_labels = array_map(function ($bundle) use ($all_bundles) {
+ return $all_bundles[$bundle]['label'];
+ }, $allowed_bundles);
+ $elements['#media_help']['#allowed_types_help'] = t('Allowed media types: %types', ['%types' => implode(", ", $bundle_labels)]);
+ }
+ }
+
+ /**
+ * Implements hook_entity_type_alter().
+ */
+ #[Hook('entity_type_alter')]
+ public function entityTypeAlter(array &$entity_types) : void {
+ if (\Drupal::config('media.settings')->get('standalone_url')) {
+ /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
+ $entity_type = $entity_types['media'];
+ $entity_type->setLinkTemplate('canonical', '/media/{media}');
+ }
+ }
+
+ /**
+ * Implements hook_form_FORM_ID_alter().
+ */
+ #[Hook('form_filter_format_edit_form_alter')]
+ public function formFilterFormatEditFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void {
+ // Add an additional validate callback so we can ensure the order of filters
+ // is correct.
+ $form['#validate'][] = 'media_filter_format_edit_form_validate';
+ }
+
+ /**
+ * Implements hook_form_FORM_ID_alter().
+ */
+ #[Hook('form_filter_format_add_form_alter')]
+ public function formFilterFormatAddFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void {
+ // Add an additional validate callback so we can ensure the order of filters
+ // is correct.
+ $form['#validate'][] = 'media_filter_format_edit_form_validate';
+ }
+
+ /**
+ * Implements hook_field_widget_single_element_form_alter().
+ */
+ #[Hook('field_widget_single_element_form_alter')]
+ public function fieldWidgetSingleElementFormAlter(&$element, FormStateInterface $form_state, $context) {
+ // Add an attribute so that text editors plugins can pass the host entity's
+ // language, allowing it to present entities in the same language.
+ if (!empty($element['#type']) && $element['#type'] == 'text_format') {
+ $element['#attributes']['data-media-embed-host-entity-langcode'] = $context['items']->getLangcode();
+ }
+ }
+
+ /**
+ * Implements hook_views_query_substitutions().
+ */
+ #[Hook('views_query_substitutions')]
+ public function viewsQuerySubstitutions(ViewExecutable $view) {
+ $account = \Drupal::currentUser();
+ return [
+ '***VIEW_OWN_UNPUBLISHED_MEDIA***' => (int) $account->hasPermission('view own unpublished media'),
+ '***ADMINISTER_MEDIA***' => (int) $account->hasPermission('administer media'),
+ ];
+ }
+
+ /**
+ * Implements hook_field_type_category_info_alter().
+ */
+ #[Hook('field_type_category_info_alter')]
+ public function fieldTypeCategoryInfoAlter(&$definitions) {
+ // The `media` field type belongs in the `general` category, so the libraries
+ // need to be attached using an alter hook.
+ $definitions[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY]['libraries'][] = 'media/drupal.media-icon';
+ }
+
+}
diff --git a/core/modules/media/tests/modules/media_test_embed/media_test_embed.module b/core/modules/media/tests/modules/media_test_embed/media_test_embed.module
index 260adb2a178..ec3eec8279b 100644
--- a/core/modules/media/tests/modules/media_test_embed/media_test_embed.module
+++ b/core/modules/media/tests/modules/media_test_embed/media_test_embed.module
@@ -7,29 +7,9 @@
declare(strict_types=1);
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Session\AccountInterface;
-
-/**
- * Implements hook_entity_view_alter().
- */
-function media_test_embed_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
- $build['#attributes']['data-media-embed-test-active-theme'] = \Drupal::theme()->getActiveTheme()->getName();
- $build['#attributes']['data-media-embed-test-view-mode'] = $display->getMode();
-}
-
/**
* Implements hook_preprocess_HOOK().
*/
function media_test_embed_preprocess_media_embed_error(&$variables) {
$variables['attributes']['class'][] = 'this-error-message-is-themeable';
}
-
-/**
- * Implements hook_entity_access().
- */
-function media_test_embed_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
- return AccessResult::neutral()->addCacheTags(['_media_test_embed_filter_access:' . $entity->getEntityTypeId() . ':' . $entity->id()]);
-}
diff --git a/core/modules/media/tests/modules/media_test_embed/src/Hook/MediaTestEmbedHooks.php b/core/modules/media/tests/modules/media_test_embed/src/Hook/MediaTestEmbedHooks.php
new file mode 100644
index 00000000000..b9783bbdc62
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_embed/src/Hook/MediaTestEmbedHooks.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\media_test_embed\Hook;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for media_test_embed.
+ */
+class MediaTestEmbedHooks {
+
+ /**
+ * Implements hook_entity_view_alter().
+ */
+ #[Hook('entity_view_alter')]
+ public function entityViewAlter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
+ $build['#attributes']['data-media-embed-test-active-theme'] = \Drupal::theme()->getActiveTheme()->getName();
+ $build['#attributes']['data-media-embed-test-view-mode'] = $display->getMode();
+ }
+
+ /**
+ * Implements hook_entity_access().
+ */
+ #[Hook('entity_access')]
+ public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+ return AccessResult::neutral()->addCacheTags([
+ '_media_test_embed_filter_access:' . $entity->getEntityTypeId() . ':' . $entity->id(),
+ ]);
+ }
+
+}
diff --git a/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
index 2084d3d5180..52b4be100c5 100644
--- a/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
+++ b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
@@ -7,8 +7,6 @@
declare(strict_types=1);
-use Drupal\media\OEmbed\Provider;
-
/**
* Implements hook_preprocess_media_oembed_iframe().
*/
@@ -20,12 +18,3 @@ function media_test_oembed_preprocess_media_oembed_iframe(array &$variables) {
$variables['#attached']['library'][] = 'media_test_oembed/frame';
$variables['#cache']['tags'][] = 'yo_there';
}
-
-/**
- * Implements hook_oembed_resource_url_alter().
- */
-function media_test_oembed_oembed_resource_url_alter(array &$parsed_url, Provider $provider) {
- if ($provider->getName() === 'Vimeo') {
- $parsed_url['query']['altered'] = 1;
- }
-}
diff --git a/core/modules/media/tests/modules/media_test_oembed/src/Hook/MediaTestOembedHooks.php b/core/modules/media/tests/modules/media_test_oembed/src/Hook/MediaTestOembedHooks.php
new file mode 100644
index 00000000000..ea4004e3359
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_oembed/src/Hook/MediaTestOembedHooks.php
@@ -0,0 +1,25 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\media_test_oembed\Hook;
+
+use Drupal\media\OEmbed\Provider;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for media_test_oembed.
+ */
+class MediaTestOembedHooks {
+
+ /**
+ * Implements hook_oembed_resource_url_alter().
+ */
+ #[Hook('oembed_resource_url_alter')]
+ public function oembedResourceUrlAlter(array &$parsed_url, Provider $provider) {
+ if ($provider->getName() === 'Vimeo') {
+ $parsed_url['query']['altered'] = 1;
+ }
+ }
+
+}
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiAddTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiAddTest.php
index 259f0831ed6..554fc35e37c 100644
--- a/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiAddTest.php
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiAddTest.php
@@ -12,7 +12,7 @@ namespace Drupal\Tests\media\FunctionalJavascript;
class MediaEmbedFilterConfigurationUiAddTest extends MediaEmbedFilterTestBase {
/**
- * @covers ::media_form_filter_format_add_form_alter
+ * @covers \Drupal\media\Hook\MediaHooks::formFilterFormatAddFormAlter
* @dataProvider providerTestValidations
*/
public function testValidationWhenAdding($filter_html_status, $filter_align_status, $filter_caption_status, $filter_html_image_secure_status, $media_embed, $allowed_html, $expected_error_message): void {
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiEditTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiEditTest.php
index 0b798f85533..bcc62c73621 100644
--- a/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiEditTest.php
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedFilterConfigurationUiEditTest.php
@@ -12,7 +12,7 @@ namespace Drupal\Tests\media\FunctionalJavascript;
class MediaEmbedFilterConfigurationUiEditTest extends MediaEmbedFilterTestBase {
/**
- * @covers ::media_form_filter_format_edit_form_alter
+ * @covers \Drupal\media\Hook\MediaHooks::formFilterFormatEditFormAlter
* @dataProvider providerTestValidations
*/
public function testValidationWhenEditing($filter_html_status, $filter_align_status, $filter_caption_status, $filter_html_image_secure_status, $media_embed, $allowed_html, $expected_error_message): void {