core/modules/media/src/Entity/MediaType.php | 5 +- core/modules/media/src/MediaStorageProxyBase.php | 2 +- .../media/src/MediaStorageProxyInterface.php | 108 +++++++++++++-------- core/modules/media/src/SourceFieldInterface.php | 21 ---- 4 files changed, 70 insertions(+), 66 deletions(-) diff --git a/core/modules/media/src/Entity/MediaType.php b/core/modules/media/src/Entity/MediaType.php index abab0d1..d4cf5ee 100644 --- a/core/modules/media/src/Entity/MediaType.php +++ b/core/modules/media/src/Entity/MediaType.php @@ -8,7 +8,6 @@ use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; use Drupal\field\FieldStorageConfigInterface; use Drupal\media\MediaTypeInterface; -use Drupal\media\SourceFieldInterface; /** * Defines the Media type configuration entity. @@ -230,7 +229,7 @@ public function preSave(EntityStorageInterface $storage) { // its name before saving. We'd need to double-save if we did // this in postSave(). $handler = $this->getHandler(); - if ($this->auto_create_source_field && $handler instanceof SourceFieldInterface) { + if ($this->auto_create_source_field) { $field_storage = $handler->getSourceField($this)->getFieldStorageDefinition(); // If the field storage is a new (unsaved) config entity, save it. if ($field_storage instanceof FieldStorageConfigInterface && $field_storage->isNew()) { @@ -251,7 +250,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) { // it if it's new. The field storage is guaranteed to exist already // because preSave() took care of that. $handler = $this->getHandler(); - if ($this->auto_create_source_field && $handler instanceof SourceFieldInterface) { + if ($this->auto_create_source_field) { $field = $handler->getSourceField($this); // If the field is new, save it and add it to this media type's view diff --git a/core/modules/media/src/MediaStorageProxyBase.php b/core/modules/media/src/MediaStorageProxyBase.php index 4d9b278..69e90a3 100644 --- a/core/modules/media/src/MediaStorageProxyBase.php +++ b/core/modules/media/src/MediaStorageProxyBase.php @@ -15,7 +15,7 @@ /** * Base implementation of media storage proxy plugin. */ -abstract class MediaStorageProxyBase extends PluginBase implements SourceFieldInterface, MediaStorageProxyMetadataHandlerInterface, MediaStorageProxyThumbnailHandlerInterface, ContainerFactoryPluginInterface { +abstract class MediaStorageProxyBase extends PluginBase implements MediaStorageProxyInterface, MediaStorageProxyMetadataHandlerInterface, MediaStorageProxyThumbnailHandlerInterface, ContainerFactoryPluginInterface { /** * Plugin label. diff --git a/core/modules/media/src/MediaStorageProxyInterface.php b/core/modules/media/src/MediaStorageProxyInterface.php index dbb289f..9e2af21 100644 --- a/core/modules/media/src/MediaStorageProxyInterface.php +++ b/core/modules/media/src/MediaStorageProxyInterface.php @@ -7,51 +7,66 @@ use Drupal\Core\Plugin\PluginFormInterface; /** - * Defines the interface for media handler plugins. + * Defines the interface for media storage proxy plugins. * - * Media handlers are the essential part of the media type as they implement the - * knowledge and business logic related to a given sort of media. Media entity, - * without a handler, doesn't have any knowledge about media it represents. + * Media storage proxy plugins are the essential part of a MediaType config + * entity, since they provide the logic to be able to bring (proxy) a certain + * kind of media from some storage (local or remote) into Drupal. Drupal is then + * able to treat Media entities as any other entity. * - * Each Media type needs exactly one handler. A single handler can be used on - * many media types. + * This interface only covers the essentials of MediaStorageProxy plugins' + * capabilities. It covers 4 crucial tasks: + * 1. it determines which field type can be used as "the source field". That is + * where the association/link between a Media entity and the media it proxies + * in from a media storage (local or remote) is stored. + * 2. it provides entity validation constraints to ensure that: + * - for remote media: the URL or some other identifier is correct (in the + * aforementioned source field) + * - for local media: the MIME type is correct + * 3. it is able to provide a default name for a given media item: + * - for remote media: usually a good enough default can be retrieved + * - for local media: usually the file name, which may not be good enough + * 4. it is able to store configuration and provide a configuration form. This + * is valuable for e.g. API keys. * - * Examples of possible handlers are: - * - File: handles local files, - * - Image: handles local images, - * - oEmbed: handles resources that are exposed through the oEmbed standard, - * - YouTube: handles YouTube videos, - * - SoundCould: handles SoundCould audio, - * - Instagram: handles Instagram posts, - * - Twitter: handles Tweets, - * - ... + * To go beyond these 3 raw essentials, there are handlers that enrich the + * proxying from a media storage, and hence improve the integration. See: + * - \Drupal\media\MediaStorageProxyMetadataHandlerInterface + * - \Drupal\media\MediaStorageProxyThumbnailHandlerInterface * - * Their responsibilities are: - * - Defining how media is represented (stored). Handlers are not responsible to - * actually store the media. They only define how it is represented on a media - * entity (usually using some kind of a field). - * - Providing thumbnails. Handlers that are responsible for remote media will - * generally fetch the image from the 3rd party API and make it available for - * the local usage. Handlers that represent local resources (such as images) - * will usually use some locally provided image. Both will also fall back to - * a pre-defined default thumbnail if everything else fails. - * - Validating a media entity before it is saved. Entity constraint system will - * be used to ensure the valid structure of the media entity. Handlers that - * represent remote media will usually ensure that the URL or and other - * identifier is correct. Handlers that are responsible for local files could - * ensure the mime type of the file is correct or something along those lines. - * - Providing a default value for the media name field. This will save users - * from manually entering the name where it can be reliably set automatically. - * Handlers for local files will generally use filename. Handlers for remote - * resources might use something like a video/photo title or something similar - * to that. Name can be always overridden by the user. - * - Providing metadata specific to the given media type. Remote media handlers - * will generally get information available through the 3rd party API and make - * it available to Drupal. Local media handlers can expose things such as EXIF - * or ID3. - * - Handling mapping metadata to the media entity fields. Metadata that handler - * exposes can be automatically mapped to the fields on the media entity. - * Handler will be able to define how this is done. + * The complete architecture: + * 1. the installed MediaStorageProxy plugins determine which media can be + * proxied into Drupal from a particular kind of storage (e.g. local files, + * YouTube, oEmbed, Twitter, and so on). Beyond this primary purpose, these + * plugins can have "handlers" associated of various kinds. Only two handlers + * are required: + * 1. 'metadata': for introspection of media metadata, and reflection down + * from the proxied media storage into Media entity fields + * 2. 'thumbnail': for a unified visual representation of all media + * Note that more handlers may be added in the future, enabling advanced use + * cases. Those would be optional. For example: + * - 'metadata_attribute_field_type': allows specifying in which entity field + * type a particular metadata attribute must be stored. + * - 'metatadata_field_mapper': allows specifying a completely custom mapping + * from metadata attributes to entity fields. + * - 'accessible_thumbnail': alternative text, through an image recognition + * service or by fetching a remote description + * - 'oembed_provider': allows any Drupal site to become an oEmbed provider + * - 'twitter_card': tweets linking to Drupal media having rich displays + * - … + * 2. a MediaStorageProxy plugin can be used by one or more MediaType config + * entities; these config entities determine how the data provided by a + * MediaStorageProxy plugin are mapped to Media entities + * 3. a Media entity stores: + * 1. the identifier for a particular piece of media, so that the + * MediaStorageProxy can talk to the storage (local or remote) and track + * the association/link between a Media entity and this media. This is + * what is stored in "the source field". + * 2. any metadata attributes (associated with a particular piece of media in + * the media storage, local or remote) that have been configured in the + * MediaType to be mapped (and hence synced/reflected) to a Media entity + * field, hence allowing us to have rich media metadata in Drupal too, and + * enabling f.e. complex queries and listings with the Views module. * * @see \Drupal\media\Annotation\MediaStorageProxy * @see \Drupal\media\MediaStorageProxyBase @@ -62,6 +77,17 @@ interface MediaStorageProxyInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface { /** + * Returns the source field for a MediaType using this media storage proxy. + * + * @param \Drupal\media\MediaTypeInterface $type + * A media type. + * + * @return \Drupal\field\FieldConfigInterface + * The source field definition. + */ + public function getSourceField(MediaTypeInterface $type); + + /** * Gets media storage proxy plugin-specific media item validation constraints. * * @return \Symfony\Component\Validator\Constraint[] diff --git a/core/modules/media/src/SourceFieldInterface.php b/core/modules/media/src/SourceFieldInterface.php deleted file mode 100644 index 5c5b27b..0000000 --- a/core/modules/media/src/SourceFieldInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -