updated core from 8.4 to 8.5 : bug with login_destination

This commit is contained in:
Bachir Soussi Chiadmi
2018-03-13 14:01:21 +01:00
parent 0d78da249b
commit e668535a4e
3486 changed files with 89604 additions and 33283 deletions
@@ -1,51 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.field.media.file.field_media_file
- media.type.file
module:
- file
id: media.file.default
targetEntityType: media
bundle: file
mode: default
content:
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_media_file:
settings:
progress_indicator: throbber
third_party_settings: { }
type: file_generic
weight: 26
region: content
name:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
status:
type: boolean_checkbox
settings:
display_label: true
weight: 100
region: content
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
region: content
third_party_settings: { }
hidden: { }
@@ -1,53 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.field.media.image.field_media_image
- image.style.thumbnail
- media.type.image
module:
- image
id: media.image.default
targetEntityType: media
bundle: image
mode: default
content:
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_media_image:
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
type: image_image
weight: 26
region: content
name:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
status:
type: boolean_checkbox
settings:
display_label: true
weight: 100
region: content
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
region: content
third_party_settings: { }
hidden: { }
@@ -1,32 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.field.media.file.field_media_file
- media.type.file
module:
- file
id: media.file.default
targetEntityType: media
bundle: file
mode: default
content:
field_media_file:
label: visually_hidden
settings: { }
third_party_settings: { }
type: file_default
weight: 1
region: content
name:
label: hidden
type: string
weight: 0
region: content
settings:
link_to_entity: false
third_party_settings: { }
hidden:
created: true
thumbnail: true
uid: true
@@ -1,35 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.field.media.image.field_media_image
- image.style.medium
- media.type.image
module:
- image
id: media.image.default
targetEntityType: media
bundle: image
mode: default
content:
field_media_image:
label: visually_hidden
settings:
image_style: medium
image_link: file
third_party_settings: { }
type: image
weight: 1
region: content
name:
label: hidden
type: string
weight: 0
region: content
settings:
link_to_entity: false
third_party_settings: { }
hidden:
created: true
thumbnail: true
uid: true
@@ -1,29 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.storage.media.field_media_file
- media.type.file
enforced:
module:
- media
module:
- file
id: media.file.field_media_file
field_name: field_media_file
entity_type: media
bundle: file
label: File
description: ''
required: true
translatable: true
default_value: { }
default_value_callback: ''
settings:
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'txt rtf doc docx ppt pptx xls xlsx pdf odf odg odp ods odt fodt fods fodp fodg key numbers pages'
max_filesize: ''
handler: 'default:file'
handler_settings: { }
description_field: false
field_type: file
@@ -1,40 +0,0 @@
langcode: en
status: true
dependencies:
config:
- field.storage.media.field_media_image
- media.type.image
enforced:
module:
- media
module:
- image
id: media.image.field_media_image
field_name: field_media_image
entity_type: media
bundle: image
label: Image
description: ''
required: true
translatable: true
default_value: { }
default_value_callback: ''
settings:
alt_field: true
alt_field_required: true
title_field: false
title_field_required: false
max_resolution: ''
min_resolution: ''
default_image:
uuid: null
alt: ''
title: ''
width: null
height: null
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'png gif jpg jpeg'
max_filesize: ''
handler: 'default:file'
handler_settings: { }
field_type: image
@@ -1,25 +0,0 @@
langcode: en
status: true
dependencies:
enforced:
module:
- media
module:
- file
- media
id: media.field_media_file
field_name: field_media_file
entity_type: media
type: file
settings:
uri_scheme: public
target_type: file
display_field: false
display_default: false
module: file
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
@@ -1,32 +0,0 @@
langcode: en
status: true
dependencies:
enforced:
module:
- media
module:
- file
- image
- media
id: media.field_media_image
field_name: field_media_image
entity_type: media
type: image
settings:
default_image:
uuid: null
alt: ''
title: ''
width: null
height: null
target_type: file
display_field: false
display_default: false
uri_scheme: public
module: image
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
@@ -1,12 +0,0 @@
langcode: en
status: true
dependencies: { }
id: file
label: File
description: "Use local files for reusable media."
source: file
queue_thumbnail_downloads: false
new_revision: true
source_configuration:
source_field: field_media_file
field_map: { }
@@ -1,12 +0,0 @@
langcode: en
status: true
dependencies: { }
id: image
label: Image
description: "Use local images for reusable media."
source: image
queue_thumbnail_downloads: false
new_revision: true
source_configuration:
source_field: field_media_image
field_map: { }
@@ -252,7 +252,7 @@ display:
relationship: none
group_type: group
admin_label: ''
label: Source
label: Type
exclude: false
alter:
alter_text: false
@@ -614,11 +614,11 @@ display:
exposed: true
expose:
operator_id: bundle_op
label: Source
label: Type
description: ''
use_operator: false
operator: bundle_op
identifier: source
identifier: type
required: false
remember: false
multiple: false
@@ -52,6 +52,14 @@ media.source.image:
type: media.source.field_aware
label: '"Image" media source configuration'
media.source.audio_file:
type: media.source.field_aware
label: '"Audio" media source configuration'
media.source.video_file:
type: media.source.field_aware
label: '"Video" media source configuration'
media.source.field_aware:
type: mapping
mapping:
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+3 -4
View File
@@ -4,14 +4,13 @@ type: module
package: Core
# version: VERSION
# core: 8.x
hidden: true
dependencies:
- file
- image
- user
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457825
+43
View File
@@ -6,6 +6,7 @@
*/
use Drupal\user\RoleInterface;
use Drupal\user\Entity\Role;
/**
* Implements hook_install().
@@ -77,3 +78,45 @@ function media_requirements($phase) {
return $requirements;
}
/**
* Introduce per-bundle permissions.
*/
function media_update_8500() {
$media_types = \Drupal::entityQuery('media_type')->execute();
/** @var \Drupal\user\RoleInterface $role */
foreach (Role::loadMultiple() as $role) {
if ($role->hasPermission('update media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("edit own $media_type media");
}
}
if ($role->hasPermission('update any media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("edit any $media_type media");
}
}
if ($role->hasPermission('delete media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("delete own $media_type media");
}
}
if ($role->hasPermission('delete any media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("delete any $media_type media");
}
}
if ($role->hasPermission('create media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("create $media_type media");
}
}
$role->save();
}
}
+1 -1
View File
@@ -9,4 +9,4 @@ media.add:
title: 'Add media'
weight: 10
appears_on:
- view.media.media_page_list
- entity.media.collection
+6
View File
@@ -23,3 +23,9 @@ entity.media_type.collection:
title: List
route_name: entity.media_type.collection
base_route: entity.media_type.collection
entity.media.collection:
title: Media
route_name: entity.media.collection
base_route: system.admin_content
weight: 10
+212 -5
View File
@@ -6,12 +6,15 @@
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\FieldConfigInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElement;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\field\FieldConfigInterface;
/**
* Implements hook_help().
@@ -20,18 +23,39 @@ function media_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.media':
$output = '<h3>' . t('About') . '</h3>';
$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 .= '<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 .= '<h3>' . t('Uses') . '</h3>';
$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 .= '<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', [], ['fragment' => 'module-media'])->toString()]) . '</dd>';
$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', [], ['fragment' => 'module-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 .= '<h3>' . t('Differences between Media, File, and Image reference fields') . '</h3>';
$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 enabling the Media module.') . '</p>';
return $output;
}
}
@@ -44,6 +68,10 @@ function media_theme() {
'media' => [
'render element' => 'elements',
],
'media_reference_help' => [
'render element' => 'element',
'base hook' => 'field_multiple_value_form',
],
];
}
@@ -96,3 +124,182 @@ function template_preprocess_media(array &$variables) {
$variables['content'][$key] = $variables['elements'][$key];
}
}
/**
* 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']['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) {
// 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 enabling 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(),
]);
}
$form['add']['description_wrapper'] = [
'#type' => 'container',
];
$field_types = [
'file',
'image',
'field_ui:entity_reference:media',
];
foreach ($field_types as $field_name) {
$form['add']['description_wrapper']["description_{$field_name}"] = [
'#type' => 'item',
'#markup' => $description_text,
'#states' => [
'visible' => [
':input[name="new_storage_type"]' => ['value' => $field_name],
],
],
];
}
$form['add']['new_storage_type']['#weight'] = 0;
$form['add']['description_wrapper']['#weight'] = 1;
}
/**
* Implements hook_field_widget_multivalue_form_alter().
*/
function media_field_widget_multivalue_form_alter(array &$elements, FormStateInterface $form_state, array $context) {
// 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 = isset($settings['target_bundles']) ? $settings['target_bundles'] : [];
$access_handler = \Drupal::entityTypeManager()->getAccessControlHandler('media');
$all_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('media');
$bundle_labels = [];
$create_bundles = [];
foreach ($allowed_bundles as $bundle) {
$bundle_labels[] = $all_bundles[$bundle]['label'];
if ($access_handler->createAccess($bundle)) {
$create_bundles[] = $bundle;
if (count($create_bundles) > 1) {
// If the user has access to create more than 1 bundle then the
// individual media type form can not be used.
break;
}
}
}
// Add a section about how to create media if the user has access to do so.
if (!empty($create_bundles)) {
if (count($create_bundles) === 1) {
$add_url = Url::fromRoute('entity.media.add_form', ['media_type' => $create_bundles[0]])->toString();
}
elseif (count($create_bundles) > 1) {
$add_url = Url::fromRoute('entity.media.add_page')->toString();
}
$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'] = isset($elements['#title']) ? $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()]);
}
$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) {
// Most of these attribute checks are copied from
// template_preprocess_fieldset(). Our template extends
// field-multiple-value-form.html.twig to provide our help text, but also
// groups the information within a semantic fieldset with a legend. So, we
// incorporate parity for both.
$element = $variables['element'];
Element::setAttributes($element, ['id']);
RenderElement::setAttributes($element);
$variables['attributes'] = isset($element['#attributes']) ? $element['#attributes'] : [];
$variables['legend_attributes'] = new Attribute();
$variables['header_attributes'] = new Attribute();
$variables['description']['attributes'] = new Attribute();
$variables['legend_span_attributes'] = new Attribute();
if (!empty($element['#media_help'])) {
foreach ($element['#media_help'] as $key => $text) {
$variables[substr($key, 1)] = $text;
}
}
}
+4
View File
@@ -9,6 +9,7 @@ administer media types:
view media:
title: 'View media'
# @todo: Deprecate some permissions in https://www.drupal.org/project/drupal/issues/2925459
update media:
title: 'Update own media'
@@ -31,3 +32,6 @@ view all media revisions:
access media overview:
title: 'Access media overview'
description: 'Users with this permission can access the media overview page.'
permission_callbacks:
- \Drupal\media\MediaPermissions::mediaTypePermissions
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* @file
* Post update functions for Media.
*/
/**
* Clear caches due to changes in local tasks and action links.
*/
function media_post_update_collection_route() {
// Empty post-update hook.
}
@@ -91,7 +91,7 @@ class MediaSource extends Plugin {
/**
* (optional) The metadata attribute name to provide the thumbnail title.
*
* The name of the media entity will be used if the attribute name is not
* The name of the media item will be used if the attribute name is not
* provided.
*
* @var string|null
+4 -8
View File
@@ -31,7 +31,7 @@ use Drupal\user\UserInterface;
* handlers = {
* "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\Core\Entity\EntityListBuilder",
* "list_builder" = "Drupal\media\MediaListBuilder",
* "access" = "Drupal\media\MediaAccessControlHandler",
* "form" = {
* "default" = "Drupal\media\MediaForm",
@@ -66,7 +66,7 @@ use Drupal\user\UserInterface;
* "revision_log_message" = "revision_log_message",
* },
* bundle_entity_type = "media_type",
* permission_granularity = "entity_type",
* permission_granularity = "bundle",
* admin_permission = "administer media",
* field_ui_base_route = "entity.media_type.edit_form",
* common_reference_target = TRUE,
@@ -74,6 +74,7 @@ use Drupal\user\UserInterface;
* "add-page" = "/media/add",
* "add-form" = "/media/add/{media_type}",
* "canonical" = "/media/{media}",
* "collection" = "/admin/content/media",
* "delete-form" = "/media/{media}/delete",
* "edit-form" = "/media/{media}/edit",
* "revision" = "/media/{media}/revisions/{media_revision}/view",
@@ -404,12 +405,7 @@ class Media extends EditorialContentEntityBase implements MediaInterface {
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
]);
->setDisplayConfigurable('view', TRUE);
$fields['thumbnail'] = BaseFieldDefinition::create('image')
->setLabel(t('Thumbnail'))
@@ -21,6 +21,7 @@ use Drupal\media\MediaTypeInterface;
* plural = "@count media types"
* ),
* handlers = {
* "access" = "Drupal\media\MediaTypeAccessControlHandler",
* "form" = {
* "add" = "Drupal\media\MediaTypeForm",
* "edit" = "Drupal\media\MediaTypeForm",
@@ -6,12 +6,14 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides a confirmation form to delete multiple media items at once.
*
* @internal
*/
class MediaDeleteMultipleConfirmForm extends ConfirmFormBase {
@@ -25,7 +27,7 @@ class MediaDeleteMultipleConfirmForm extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
@@ -39,7 +41,7 @@ class MediaDeleteMultipleConfirmForm extends ConfirmFormBase {
/**
* Constructs a MediaDeleteMultipleConfirmForm form object.
*
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $manager
* The entity type manager.
@@ -54,7 +56,7 @@ class MediaDeleteMultipleConfirmForm extends ConfirmFormBase {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.private_tempstore'),
$container->get('tempstore.private'),
$container->get('entity_type.manager')
);
}
@@ -9,6 +9,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for media type deletion.
*
* @internal
*/
class MediaTypeDeleteConfirmForm extends EntityDeleteForm {
@@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines an access control handler for the media entity.
* Defines an access control handler for media items.
*/
class MediaAccessControlHandler extends EntityAccessControlHandler {
@@ -20,6 +20,7 @@ class MediaAccessControlHandler extends EntityAccessControlHandler {
return AccessResult::allowed()->cachePerPermissions();
}
$type = $entity->bundle();
$is_owner = ($account->id() && $account->id() === $entity->getOwnerId());
switch ($operation) {
case 'view':
@@ -32,22 +33,38 @@ class MediaAccessControlHandler extends EntityAccessControlHandler {
return $access_result;
case 'update':
if ($account->hasPermission('edit any ' . $type . ' media')) {
return AccessResult::allowed()->cachePerPermissions();
}
if ($account->hasPermission('edit own ' . $type . ' media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
// @todo Deprecate this permission in
// https://www.drupal.org/project/drupal/issues/2925459.
if ($account->hasPermission('update any media')) {
return AccessResult::allowed()->cachePerPermissions();
}
return AccessResult::allowedIf($account->hasPermission('update media') && $is_owner)
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($entity);
if ($account->hasPermission('update media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
return AccessResult::neutral()->cachePerPermissions();
case 'delete':
if ($account->hasPermission('delete any ' . $type . ' media')) {
return AccessResult::allowed()->cachePerPermissions();
}
if ($account->hasPermission('delete own ' . $type . ' media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
// @todo Deprecate this permission in
// https://www.drupal.org/project/drupal/issues/2925459.
if ($account->hasPermission('delete any media')) {
return AccessResult::allowed()->cachePerPermissions();
}
return AccessResult::allowedIf($account->hasPermission('delete media') && $is_owner)
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($entity);
if ($account->hasPermission('delete media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
return AccessResult::neutral()->cachePerPermissions();
default:
return AccessResult::neutral()->cachePerPermissions();
@@ -58,7 +75,12 @@ class MediaAccessControlHandler extends EntityAccessControlHandler {
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermissions($account, ['administer media', 'create media'], 'OR');
$permissions = [
'administer media',
'create media',
'create ' . $entity_bundle . ' media',
];
return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
}
}
+2
View File
@@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for the media edit forms.
*
* @internal
*/
class MediaForm extends ContentEntityForm {
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace Drupal\media;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a listing of media items.
*/
class MediaListBuilder extends EntityListBuilder {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The language manager service.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* Indicates whether the 'thumbnail' image style exists.
*
* @var bool
*/
protected $thumbnailStyleExists = FALSE;
/**
* Constructs a new MediaListBuilder object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage class.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager service.
* @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage
* The entity storage class for image styles.
*/
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager, EntityStorageInterface $image_style_storage) {
parent::__construct($entity_type, $storage);
$this->dateFormatter = $date_formatter;
$this->languageManager = $language_manager;
$this->thumbnailStyleExists = !empty($image_style_storage->load('thumbnail'));
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('entity.manager')->getStorage($entity_type->id()),
$container->get('date.formatter'),
$container->get('language_manager'),
$container->get('entity_type.manager')->getStorage('image_style')
);
}
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header = [];
if ($this->thumbnailStyleExists) {
$header['thumbnail'] = [
'data' => $this->t('Thumbnail'),
'class' => [RESPONSIVE_PRIORITY_LOW],
];
}
$header += [
'name' => $this->t('Media Name'),
'type' => [
'data' => $this->t('Type'),
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
],
'author' => [
'data' => $this->t('Author'),
'class' => [RESPONSIVE_PRIORITY_LOW],
],
'status' => $this->t('Status'),
'changed' => [
'data' => $this->t('Updated'),
'class' => [RESPONSIVE_PRIORITY_LOW],
],
];
// Enable language column if multiple languages are added.
if ($this->languageManager->isMultilingual()) {
$header['language'] = [
'data' => $this->t('Language'),
'class' => [RESPONSIVE_PRIORITY_LOW],
];
}
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/** @var \Drupal\media\MediaInterface $entity */
if ($this->thumbnailStyleExists) {
$row['thumbnail'] = [];
if ($thumbnail_url = $entity->getSource()->getMetadata($entity, 'thumbnail_uri')) {
$row['thumbnail']['data'] = [
'#theme' => 'image_style',
'#style_name' => 'thumbnail',
'#uri' => $thumbnail_url,
'#height' => 50,
];
}
}
$row['name']['data'] = [
'#type' => 'link',
'#title' => $entity->label(),
'#url' => $entity->toUrl(),
];
$row['type'] = $entity->bundle->entity->label();
$row['author']['data'] = [
'#theme' => 'username',
'#account' => $entity->getOwner(),
];
$row['status'] = $entity->isPublished() ? $this->t('Published') : $this->t('Unpublished');
$row['changed'] = $this->dateFormatter->format($entity->getChangedTime(), 'short');
if ($this->languageManager->isMultilingual()) {
$row['language'] = $this->languageManager->getLanguageName($entity->language()->getId());
}
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
protected function getEntityIds() {
$query = $this->getStorage()->getQuery()
->sort('changed', 'DESC');
// Only add the pager if a limit is specified.
if ($this->limit) {
$query->pager($this->limit);
}
return $query->execute();
}
}
@@ -0,0 +1,92 @@
<?php
namespace Drupal\media;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides dynamic permissions for each media type.
*/
class MediaPermissions implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* MediaPermissions constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity_type.manager'));
}
/**
* Returns an array of media type permissions.
*
* @return array
* The media type permissions.
*
* @see \Drupal\user\PermissionHandlerInterface::getPermissions()
*/
public function mediaTypePermissions() {
$perms = [];
// Generate media permissions for all media types.
$media_types = $this->entityTypeManager
->getStorage('media_type')->loadMultiple();
foreach ($media_types as $type) {
$perms += $this->buildPermissions($type);
}
return $perms;
}
/**
* Returns a list of media permissions for a given media type.
*
* @param \Drupal\media\MediaTypeInterface $type
* The media type.
*
* @return array
* An associative array of permission names and descriptions.
*/
protected function buildPermissions(MediaTypeInterface $type) {
$type_id = $type->id();
$type_params = ['%type_name' => $type->label()];
return [
"create $type_id media" => [
'title' => $this->t('%type_name: Create new media', $type_params),
],
"edit own $type_id media" => [
'title' => $this->t('%type_name: Edit own media', $type_params),
],
"edit any $type_id media" => [
'title' => $this->t('%type_name: Edit any media', $type_params),
],
"delete own $type_id media" => [
'title' => $this->t('%type_name: Delete own media', $type_params),
],
"delete any $type_id media" => [
'title' => $this->t('%type_name: Delete any media', $type_params),
],
];
}
}
@@ -3,6 +3,8 @@
namespace Drupal\media;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
@@ -317,4 +319,37 @@ abstract class MediaSourceBase extends PluginBase implements MediaSourceInterfac
return $id;
}
/**
* {@inheritdoc}
*/
public function getSourceFieldValue(MediaInterface $media) {
$source_field = $this->configuration['source_field'];
if (empty($source_field)) {
throw new \RuntimeException('Source field for media source is not defined.');
}
/** @var \Drupal\Core\Field\FieldItemInterface $field_item */
$field_item = $media->get($source_field)->first();
return $field_item->{$field_item->mainPropertyName()};
}
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
$display->setComponent($this->getSourceFieldDefinition($type)->getName());
}
/**
* {@inheritdoc}
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
// Make sure the source field is placed just after the "name" basefield.
$name_component = $display->getComponent('name');
$source_field_weight = ($name_component && isset($name_component['weight'])) ? $name_component['weight'] + 5 : -50;
$display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
'weight' => $source_field_weight,
]);
}
}
@@ -4,6 +4,8 @@ namespace Drupal\media;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
@@ -23,7 +25,7 @@ use Drupal\Core\Plugin\PluginFormInterface;
* - Image: handles local images,
* - oEmbed: handles resources that are exposed through the oEmbed standard,
* - YouTube: handles YouTube videos,
* - SoundCould: handles SoundCloud audio,
* - SoundCloud: handles SoundCloud audio,
* - Instagram: handles Instagram posts,
* - Twitter: handles tweets,
* - ...
@@ -139,4 +141,54 @@ interface MediaSourceInterface extends PluginInspectionInterface, ConfigurablePl
*/
public function createSourceField(MediaTypeInterface $type);
/**
* Prepares the media type fields for this source in the view display.
*
* This method should normally call
* \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent() or
* \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent() to
* configure the media type fields in the view display.
*
* @param \Drupal\media\MediaTypeInterface $type
* The media type which is using this source.
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
* The display which should be prepared.
*
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent()
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent()
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display);
/**
* Prepares the media type fields for this source in the form display.
*
* This method should normally call
* \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent() or
* \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent() to
* configure the media type fields in the form display.
*
* @param \Drupal\media\MediaTypeInterface $type
* The media type which is using this source.
* @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display
* The display which should be prepared.
*
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent()
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent()
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display);
/**
* Get the primary value stored in the source field.
*
* @param MediaInterface $media
* A media item.
*
* @return mixed
* The source value.
*
* @throws \RuntimeException
* If the source field for the media source is not defined.
*/
public function getSourceFieldValue(MediaInterface $media);
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\media;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines the access control handler for the "Media Type" entity type.
*
* @see \Drupal\media\Entity\MediaType
*/
class MediaTypeAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected $viewLabelOperation = TRUE;
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
if ($operation === 'view label') {
return AccessResult::allowedIfHasPermission($account, 'view media');
}
else {
return parent::checkAccess($entity, $operation, $account);
}
}
}
+39 -20
View File
@@ -15,6 +15,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for media type forms.
*
* @internal
*/
class MediaTypeForm extends EntityForm {
@@ -116,14 +118,30 @@ class MediaTypeForm extends EntityForm {
'#attributes' => ['id' => 'source-dependent'],
];
if ($source) {
$source_description = $this->t('<em>The media source cannot be changed after the media type is created.</em>');
}
else {
$source_description = $this->t('Media source that is responsible for additional logic related to this media type.');
}
$form['source_dependent']['source'] = [
'#type' => 'select',
'#title' => $this->t('Media source'),
'#default_value' => $source ? $source->getPluginId() : NULL,
'#options' => $options,
'#description' => $this->t('Media source that is responsible for additional logic related to this media type.'),
'#description' => $source_description,
'#ajax' => ['callback' => '::ajaxHandlerData'],
// Rebuilding the form as part of the AJAX request is a workaround to
// enforce machine_name validation.
// @todo This was added as part of #2932226 and it should be removed once
// https://www.drupal.org/project/drupal/issues/2557299 solves it in a
// more generic way.
'#executes_submit_callback' => TRUE,
'#submit' => [[static::class, 'rebuildSubmit']],
'#required' => TRUE,
// Once the media type is created, its source plugin cannot be changed
// anymore.
'#disabled' => !empty($source),
];
if (!$source) {
@@ -222,6 +240,18 @@ class MediaTypeForm extends EntityForm {
return $form;
}
/**
* Form submission handler to rebuild the form on select submit.
*
* @param array $form
* Full form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Current form state.
*/
public static function rebuildSubmit(array &$form, FormStateInterface $form_state) {
$form_state->setRebuild();
}
/**
* Prepares workflow options to be used in the 'checkboxes' form element.
*
@@ -262,7 +292,7 @@ class MediaTypeForm extends EntityForm {
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
if ($form['source_dependent']['source_configuration']) {
if (isset($form['source_dependent']['source_configuration'])) {
// Let the selected plugin validate its settings.
$this->entity->getSource()->validateConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
}
@@ -285,7 +315,7 @@ class MediaTypeForm extends EntityForm {
->setStatus((bool) $form_state->getValue(['options', 'status']))
->setNewRevision((bool) $form_state->getValue(['options', 'new_revision']));
if ($form['source_dependent']['source_configuration']) {
if (isset($form['source_dependent']['source_configuration'])) {
// Let the selected plugin save its settings.
$this->entity->getSource()->submitConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
}
@@ -325,30 +355,19 @@ class MediaTypeForm extends EntityForm {
// Add the new field to the default form and view displays for this
// media type.
$field_name = $source_field->getName();
$field_type = $source_field->getType();
if ($source_field->isDisplayConfigurable('form')) {
// Use the default widget and settings.
$component = \Drupal::service('plugin.manager.field.widget')
->prepareConfiguration($field_type, []);
// @todo Replace entity_get_form_display() when #2367933 is done.
// https://www.drupal.org/node/2872159.
entity_get_form_display('media', $media_type->id(), 'default')
->setComponent($field_name, $component)
->save();
$display = entity_get_form_display('media', $media_type->id(), 'default');
$source->prepareFormDisplay($media_type, $display);
$display->save();
}
if ($source_field->isDisplayConfigurable('view')) {
// Use the default formatter and settings.
$component = \Drupal::service('plugin.manager.field.formatter')
->prepareConfiguration($field_type, []);
// @todo Replace entity_get_display() when #2367933 is done.
// https://www.drupal.org/node/2872159.
entity_get_display('media', $media_type->id(), 'default')
->setComponent($field_name, $component)
->save();
$display = entity_get_display('media', $media_type->id(), 'default');
$source->prepareViewDisplay($media_type, $display);
$display->save();
}
}
@@ -100,7 +100,7 @@ class MediaThumbnailFormatter extends ImageFormatter {
$link_types = [
'content' => $this->t('Content'),
'media' => $this->t('Media entity'),
'media' => $this->t('Media item'),
];
$element['image_link']['#options'] = $link_types;
@@ -0,0 +1,39 @@
<?php
namespace Drupal\media\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Media source wrapping around an audio file.
*
* @see \Drupal\file\FileInterface
*
* @MediaSource(
* id = "audio_file",
* label = @Translation("Audio file"),
* description = @Translation("Use audio files for reusable media."),
* allowed_field_types = {"file"},
* default_thumbnail_filename = "audio.png"
* )
*/
class AudioFile extends File {
/**
* {@inheritdoc}
*/
public function createSourceField(MediaTypeInterface $type) {
return parent::createSourceField($type)->set('settings', ['file_extensions' => 'mp3 wav aac']);
}
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
$display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
'type' => 'file_audio',
]);
}
}
@@ -22,11 +22,29 @@ use Drupal\media\MediaSourceBase;
*/
class File extends MediaSourceBase {
/**
* Key for "MIME type" metadata attribute.
*
* @var string
*/
const METADATA_ATTRIBUTE_MIME = 'mimetype';
/**
* Key for "File size" metadata attribute.
*
* @var string
*/
const METADATA_ATTRIBUTE_SIZE = 'filesize';
/**
* {@inheritdoc}
*/
public function getMetadataAttributes() {
return [];
return [
static::METADATA_ATTRIBUTE_MIME => $this->t('MIME type'),
static::METADATA_ATTRIBUTE_SIZE => $this->t('File size'),
];
}
/**
@@ -40,6 +58,12 @@ class File extends MediaSourceBase {
return parent::getMetadata($media, $attribute_name);
}
switch ($attribute_name) {
case 'mimetype':
return $file->getMimeType();
case 'filesize':
return $file->getSize();
case 'default_name':
return $file->getFilename();
@@ -0,0 +1,39 @@
<?php
namespace Drupal\media\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Media source wrapping around a video file.
*
* @see \Drupal\file\FileInterface
*
* @MediaSource(
* id = "video_file",
* label = @Translation("Video file"),
* description = @Translation("Use video files for reusable media."),
* allowed_field_types = {"file"},
* default_thumbnail_filename = "video.png"
* )
*/
class VideoFile extends File {
/**
* {@inheritdoc}
*/
public function createSourceField(MediaTypeInterface $type) {
return parent::createSourceField($type)->set('settings', ['file_extensions' => 'mp4']);
}
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
$display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
'type' => 'file_video',
]);
}
}
@@ -22,22 +22,6 @@ class Media extends WizardPluginBase {
*/
protected $createdColumn = 'media_field_data-created';
/**
* Set default values for the filters.
*
* @var array
*/
protected $filters = [
'status' => [
'value' => '1',
'table' => 'media_field_data',
'field' => 'status',
'plugin_id' => 'boolean',
'entity_type' => 'media',
'entity_field' => 'status',
],
];
/**
* {@inheritdoc}
*/
@@ -22,22 +22,6 @@ class MediaRevision extends WizardPluginBase {
*/
protected $createdColumn = 'media_field_revision-created';
/**
* Set default values for the filters.
*
* @var array
*/
protected $filters = [
'status' => [
'value' => '1',
'table' => 'media_field_revision',
'field' => 'status',
'plugin_id' => 'boolean',
'entity_type' => 'media',
'entity_field' => 'status',
],
];
/**
* {@inheritdoc}
*/
@@ -0,0 +1,66 @@
{#
/**
* @file
* Theme override for media reference fields.
*
* @see template_preprocess_field_multiple_value_form()
* @see core/themes/classy/templates/form/fieldset.html.twig
*/
#}
{%
set classes = [
'js-form-item',
'form-item',
'js-form-wrapper',
'form-wrapper',
]
%}
<fieldset{{ attributes.addClass(classes) }}>
{%
set legend_span_classes = [
'fieldset-legend',
required ? 'js-form-required',
required ? 'form-required',
]
%}
{# Always wrap fieldset legends in a <span> for CSS positioning. #}
<legend{{ legend_attributes }}>
<span{{ legend_span_attributes.addClass(legend_span_classes) }}>{{ original_label }}</span>
</legend>
<div class="js-form-item form-item">
{% if media_add_help %}
<h4{{ header_attributes.addClass('label') }}>
{% trans %}
Create new media
{% endtrans %}
</h4><br />
<div class="description">
{{ media_add_help }}
</div>
{% endif %}
{% if multiple %}
{{ table }}
{% else %}
{% for element in elements %}
{{ element }}
{% endfor %}
{% endif %}
<div{{ description.attributes.addClass('description') }}>
{% if multiple and description.content %}
<ul>
<li>{{ media_list_help }} {{ media_list_link }} {{ allowed_types_help }}</li>
<li>{{ description.content }}</li>
</ul>
{% else %}
{{ media_list_help }} {{ media_list_link }} {{ allowed_types_help }}
{% endif %}
{% if multiple and button %}
<div class="clearfix">{{ button }}</div>
{% endif %}
</div>
</div>
</fieldset>
+1 -12
View File
@@ -32,17 +32,6 @@
*/
#}
<div{{ attributes }}>
{#
In the 'full' view mode the entity label is assumed to be displayed as the
page title, so we do not display it here.
#}
{{ title_prefix }}
{% if label and view_mode != 'full' %}
<h2{{ title_attributes }}>
{{ label }}
</h2>
{% endif %}
{{ title_suffix }}
{{ title_suffix.contextual_links }}
{{ content }}
</div>
File diff suppressed because one or more lines are too long
@@ -13,3 +13,11 @@ media.source.test_translation:
media.source.test_constraints:
type: media.source.test
label: 'Test media source with constraints configuration'
media.source.test_hidden_source_field:
type: media.source.test
label: 'Test media source with hidden source field'
media.source.test_different_displays:
type: media.source.test
label: 'Test media source with different source field displays'
@@ -5,8 +5,8 @@ description: 'Provides test media source to test configuration forms.'
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457825
@@ -0,0 +1,50 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test_different_displays",
* label = @Translation("Test source with different displays"),
* description = @Translation("Test source with different displays."),
* allowed_field_types = {"entity_reference"},
* )
*/
class TestDifferentDisplays extends Test {
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
parent::prepareViewDisplay($type, $display);
$source_name = $this->getSourceFieldDefinition($type)->getName();
$source_component = $display->getComponent($source_name) ?: [];
$source_component['type'] = 'entity_reference_entity_id';
$display->setComponent($source_name, $source_component);
}
/**
* {@inheritdoc}
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
parent::prepareFormDisplay($type, $display);
$source_name = $this->getSourceFieldDefinition($type)->getName();
$source_component = $display->getComponent($source_name) ?: [];
$source_component['type'] = 'entity_reference_autocomplete_tags';
$display->setComponent($source_name, $source_component);
}
/**
* {@inheritdoc}
*/
protected function getSourceFieldName() {
return 'field_media_different_display';
}
}
@@ -0,0 +1,42 @@
<?php
namespace Drupal\media_test_source\Plugin\media\Source;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\media\MediaTypeInterface;
/**
* Provides test media source.
*
* @MediaSource(
* id = "test_hidden_source_field",
* label = @Translation("Test source with hidden source field"),
* description = @Translation("Test media source with hidden source field."),
* allowed_field_types = {"string"},
* )
*/
class TestWithHiddenSourceField extends Test {
/**
* {@inheritdoc}
*/
public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
$display->removeComponent($this->getSourceFieldDefinition($type)->getName());
}
/**
* {@inheritdoc}
*/
public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
$display->removeComponent($this->getSourceFieldDefinition($type)->getName());
}
/**
* {@inheritdoc}
*/
protected function getSourceFieldName() {
return 'field_media_hidden';
}
}
@@ -5,10 +5,11 @@ description: 'Provides test type for a media item.'
package: Testing
# version: VERSION
dependencies:
- media
- media_test_source
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457825
@@ -8,8 +8,8 @@ dependencies:
- media
- views
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457825
@@ -12,10 +12,27 @@ use Drupal\user\RoleInterface;
*
* @group media
*/
class MediaAccessTest extends MediaUiFunctionalTest {
class MediaAccessTest extends MediaFunctionalTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'media_test_source',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// This is needed to provide the user cache context for a below assertion.
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test some access control functionality.
*/
@@ -64,43 +81,66 @@ class MediaAccessTest extends MediaUiFunctionalTest {
$this->assertSame("The 'view media' permission is required and the media item must be published.", $access_result->getReason());
$this->grantPermissions($role, ['view media']);
$this->drupalGet('media/' . $media->id());
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
// Test 'create BUNDLE media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = ['create ' . $media_type->id() . ' media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'create media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['create media']);
$permissions = ['create media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'update media' and 'delete media' permissions.
// Test 'edit own BUNDLE media' and 'delete own BUNDLE media' permissions.
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['update media']);
$this->grantPermissions($role, ['delete media']);
$permissions = [
'edit own ' . $user_media->bundle() . ' media',
'delete own ' . $user_media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'update any media' and 'delete any media' permissions.
// Test 'edit any BUNDLE media' and 'delete any BUNDLE media' permissions.
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $media->id() . '/delete');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['update any media']);
$this->grantPermissions($role, ['delete any media']);
$permissions = [
'edit any ' . $media->bundle() . ' media',
'delete any ' . $media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
@@ -7,7 +7,7 @@ use Drupal\media\Entity\Media;
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
/**
* Tests the media entity's cache tags.
* Tests the media items cache tags.
*
* @group media
*/
@@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
/**
* Tests views contextual links on media items.
*
* @group media
*/
class MediaContextualLinksTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'contextual',
];
/**
* Tests contextual links.
*/
public function testMediaContextualLinks() {
// Create a media type.
$mediaType = $this->createMediaType();
// Create a media item.
$media = Media::create([
'bundle' => $mediaType->id(),
'name' => 'Unnamed',
]);
$media->save();
$user = $this->drupalCreateUser([
'administer media',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('media/' . $media->id());
$this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'media:media=' . $media->id() . ':');
}
}
@@ -45,6 +45,7 @@ trait MediaFunctionalTestCreateMediaTypeTrait {
$this->assertIdentical(SAVED_NEW, $status, 'Media type was created successfully.');
// Ensure that the source field exists.
/** @var \Drupal\media\MediaSourceInterface $source */
$source = $media_type->getSource();
$source_field = $source->getSourceFieldDefinition($media_type);
if (!$source_field) {
@@ -61,6 +62,11 @@ trait MediaFunctionalTestCreateMediaTypeTrait {
->save();
}
// Move source field in form display.
entity_get_form_display('media', $id, 'default')
->setComponent($source_field->getName())
->save();
return $media_type;
}
@@ -13,18 +13,14 @@ trait MediaFunctionalTestTrait {
* @var array
*/
protected static $adminUserPermissions = [
// Media entity permissions.
// Media module permissions.
'access media overview',
'administer media',
'administer media fields',
'administer media form display',
'administer media display',
'administer media types',
'view media',
'create media',
'update media',
'update any media',
'delete media',
'delete any media',
// Other permissions.
'administer views',
'access content overview',
@@ -32,6 +28,7 @@ trait MediaFunctionalTestTrait {
'administer content types',
'administer node fields',
'administer node form display',
'administer node display',
'bypass node access',
];
@@ -0,0 +1,147 @@
<?php
namespace Drupal\Tests\media\Functional;
use Drupal\media\Entity\Media;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the Media overview page.
*
* @group media
*/
class MediaOverviewPageTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->nonAdminUser);
}
/**
* Test that the Media overview page (/admin/content/media).
*/
public function testMediaOverviewPage() {
$assert_session = $this->assertSession();
// Check the view exists, is access-restricted, and some defaults are there.
$this->drupalGet('/admin/content/media');
$assert_session->statusCodeEquals(403);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
$this->grantPermissions($role, ['access media overview']);
$this->drupalGet('/admin/content/media');
$assert_session->statusCodeEquals(200);
$assert_session->titleEquals('Media | Drupal');
$assert_session->fieldExists('Media name');
$assert_session->selectExists('type');
$assert_session->selectExists('status');
$assert_session->selectExists('langcode');
$assert_session->buttonExists('Filter');
$header = $assert_session->elementExists('css', 'th#view-thumbnail-target-id-table-column');
$this->assertEquals('Thumbnail', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-name-table-column');
$this->assertEquals('Media name', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-bundle-table-column');
$this->assertEquals('Type', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-uid-table-column');
$this->assertEquals('Author', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-status-table-column');
$this->assertEquals('Status', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-changed-table-column');
$this->assertEquals('Updated Sort ascending', $header->getText());
$header = $assert_session->elementExists('css', 'th#view-operations-table-column');
$this->assertEquals('Operations', $header->getText());
$assert_session->pageTextContains('No content available.');
// Create some content for the view.
$media_type1 = $this->createMediaType();
$media_type2 = $this->createMediaType();
$media1 = Media::create([
'bundle' => $media_type1->id(),
'name' => 'Media 1',
'uid' => $this->adminUser->id(),
]);
$media1->save();
$media2 = Media::create([
'bundle' => $media_type2->id(),
'name' => 'Media 2',
'uid' => $this->adminUser->id(),
'status' => FALSE,
]);
$media2->save();
$media3 = Media::create([
'bundle' => $media_type1->id(),
'name' => 'Media 3',
'uid' => $this->nonAdminUser->id(),
]);
$media3->save();
// Verify the view is now correctly populated.
$this->grantPermissions($role, [
'view media',
'update any media',
'delete any media',
]);
$this->drupalGet('/admin/content/media');
$row1 = $assert_session->elementExists('css', 'table tbody tr:nth-child(1)');
$row2 = $assert_session->elementExists('css', 'table tbody tr:nth-child(2)');
$row3 = $assert_session->elementExists('css', 'table tbody tr:nth-child(3)');
// Media thumbnails.
$assert_session->elementExists('css', 'td.views-field-thumbnail__target-id img', $row1);
$assert_session->elementExists('css', 'td.views-field-thumbnail__target-id img', $row2);
$assert_session->elementExists('css', 'td.views-field-thumbnail__target-id img', $row3);
// Media names.
$name1 = $assert_session->elementExists('css', 'td.views-field-name a', $row1);
$this->assertEquals($media1->label(), $name1->getText());
$name2 = $assert_session->elementExists('css', 'td.views-field-name a', $row2);
$this->assertEquals($media2->label(), $name2->getText());
$name3 = $assert_session->elementExists('css', 'td.views-field-name a', $row3);
$this->assertEquals($media3->label(), $name3->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id());
$assert_session->linkByHrefExists('/media/' . $media2->id());
$assert_session->linkByHrefExists('/media/' . $media3->id());
// Media types.
$type_element1 = $assert_session->elementExists('css', 'td.views-field-bundle', $row1);
$this->assertEquals($media_type1->label(), $type_element1->getText());
$type_element2 = $assert_session->elementExists('css', 'td.views-field-bundle', $row2);
$this->assertEquals($media_type2->label(), $type_element2->getText());
$type_element3 = $assert_session->elementExists('css', 'td.views-field-bundle', $row3);
$this->assertEquals($media_type1->label(), $type_element3->getText());
// Media authors.
$author_element1 = $assert_session->elementExists('css', 'td.views-field-uid', $row1);
$this->assertEquals($this->adminUser->getDisplayName(), $author_element1->getText());
$author_element2 = $assert_session->elementExists('css', 'td.views-field-uid', $row2);
$this->assertEquals($this->adminUser->getDisplayName(), $author_element2->getText());
$author_element3 = $assert_session->elementExists('css', 'td.views-field-uid', $row3);
$this->assertEquals($this->nonAdminUser->getDisplayName(), $author_element3->getText());
// Media publishing status.
$status_element1 = $assert_session->elementExists('css', 'td.views-field-status', $row1);
$this->assertEquals('Published', $status_element1->getText());
$status_element2 = $assert_session->elementExists('css', 'td.views-field-status', $row2);
$this->assertEquals('Unpublished', $status_element2->getText());
$status_element3 = $assert_session->elementExists('css', 'td.views-field-status', $row3);
$this->assertEquals('Published', $status_element3->getText());
// Timestamp.
$expected = \Drupal::service('date.formatter')->format($media1->getChangedTime(), 'short');
$changed_element1 = $assert_session->elementExists('css', 'td.views-field-changed', $row1);
$this->assertEquals($expected, $changed_element1->getText());
// Operations.
$edit_link1 = $assert_session->elementExists('css', 'td.views-field-operations li.edit a', $row1);
$this->assertEquals('Edit', $edit_link1->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id() . '/edit');
$delete_link1 = $assert_session->elementExists('css', 'td.views-field-operations li.delete a', $row1);
$this->assertEquals('Delete', $delete_link1->getText());
$assert_session->linkByHrefExists('/media/' . $media1->id() . '/delete');
}
}
@@ -80,6 +80,8 @@ class MediaRevisionTest extends MediaFunctionalTestBase {
$uri = 'temporary://foo.txt';
file_put_contents($uri, $this->randomString(128));
$this->createMediaType(['bundle' => 'file', 'new_revision' => TRUE], 'file');
// Create a media item.
$this->drupalGet('/media/add/file');
$page = $this->getSession()->getPage();
@@ -117,6 +119,8 @@ class MediaRevisionTest extends MediaFunctionalTestBase {
public function testImageMediaRevision() {
$assert = $this->assertSession();
$this->createMediaType(['bundle' => 'image', 'new_revision' => TRUE], 'image');
/** @var \Drupal\field\FieldConfigInterface $field */
// Disable the alt text field, because this is not a JavaScript test and
// the alt text field will therefore not appear without a full page refresh.
@@ -3,7 +3,6 @@
namespace Drupal\Tests\media\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\media\Entity\MediaType;
/**
* Tests the file media source.
@@ -12,17 +11,6 @@ use Drupal\media\Entity\MediaType;
*/
class MediaSourceFileTest extends MediaFunctionalTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// We need to test without any default configuration in place.
// @TODO: Remove this as part of https://www.drupal.org/node/2883813.
MediaType::load('file')->delete();
}
/**
* Test that it's possible to change the allowed file extensions.
*/
@@ -2,8 +2,12 @@
namespace Drupal\Tests\media\Functional;
use Behat\Mink\Element\NodeElement;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Ensures that media UI works correctly.
@@ -29,11 +33,6 @@ class MediaUiFunctionalTest extends MediaFunctionalTestBase {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
// We need to test without any default configuration in place.
// @TODO: Remove this when https://www.drupal.org/node/2883813 lands.
MediaType::load('file')->delete();
MediaType::load('image')->delete();
}
/**
@@ -59,6 +58,8 @@ class MediaUiFunctionalTest extends MediaFunctionalTestBase {
$page->fillField('name[0][value]', $media_name);
$revision_log_message = $this->randomString();
$page->fillField('revision_log_message[0][value]', $revision_log_message);
$source_field = $this->randomString();
$page->fillField('field_media_test[0][value]', $source_field);
$page->pressButton('Save');
$media_id = $this->container->get('entity.query')->get('media')->execute();
$media_id = reset($media_id);
@@ -179,4 +180,352 @@ class MediaUiFunctionalTest extends MediaFunctionalTestBase {
$assert_session->pageTextContains($second_media_item->getName());
}
/**
* Test that media in ER fields use the Rendered Entity formatter by default.
*/
public function testRenderedEntityReferencedMedia() {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
$this->drupalGet('/admin/structure/types/manage/page/fields/add-field');
$page->selectFieldOption('new_storage_type', 'field_ui:entity_reference:media');
$page->fillField('label', 'Foo field');
$page->fillField('field_name', 'foo_field');
$page->pressButton('Save and continue');
$this->drupalGet('/admin/structure/types/manage/page/display');
$assert_session->fieldValueEquals('fields[field_foo_field][type]', 'entity_reference_entity_view');
}
/**
* Data provider for testMediaReferenceWidget().
*
* @return array[]
* Test data. See testMediaReferenceWidget() for the child array structure.
*/
public function providerTestMediaReferenceWidget() {
return [
// Single-value fields with a single media type and the default widget:
// - The user can create and list the media.
'single_value:single_type:create_list' => [1, [TRUE], TRUE],
// - The user can list but not create the media.
'single_value:single_type:list' => [1, [FALSE], TRUE],
// - The user can create but not list the media.
'single_value:single_type:create' => [1, [TRUE], FALSE],
// - The user can neither create nor list the media.
'single_value:single_type' => [1, [FALSE], FALSE],
// Single-value fields with the tags-style widget:
// - The user can create and list the media.
'single_value:single_type:create_list:tags' => [1, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
// - The user can list but not create the media.
'single_value:single_type:list:tags' => [1, [FALSE], TRUE, 'entity_reference_autocomplete_tags'],
// - The user can create but not list the media.
'single_value:single_type:create:tags' => [1, [TRUE], FALSE, 'entity_reference_autocomplete_tags'],
// - The user can neither create nor list the media.
'single_value:single_type:tags' => [1, [FALSE], FALSE, 'entity_reference_autocomplete_tags'],
// Single-value fields with two media types:
// - The user can create both types.
'single_value:two_type:create2_list' => [1, [TRUE, TRUE], TRUE],
// - The user can create only one type.
'single_value:two_type:create1_list' => [1, [TRUE, FALSE], TRUE],
// - The user cannot create either type.
'single_value:two_type:list' => [1, [FALSE, FALSE], TRUE],
// Multiple-value field with a cardinality of 3, with media the user can
// create and list.
'multi_value:single_type:create_list' => [3, [TRUE], TRUE],
// The same, with the tags field.
'multi-value:single_type:create_list:tags' => [3, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
// Unlimited value field.
'unlimited_value:single_type:create_list' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE],
// Unlimited value field with the tags widget.
'unlimited_value:single_type:create_list' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
];
}
/**
* Tests the default autocomplete widgets for media reference fields.
*
* @param int $cardinality
* The field cardinality.
* @param bool[] $media_type_create_access
* An array of booleans indicating whether to grant the test user create
* access for each media type. A media type is created automatically for
* each; for example, an array [TRUE, FALSE] would create two media types,
* one that allows the user to create media and a second that does not.
* @param bool $list_access
* Whether to grant the test user access to list media.
*
* @see media_field_widget_entity_reference_autocomplete_form_alter()
* @see media_field_widget_multiple_entity_reference_autocomplete_form_alter()
*
* @dataProvider providerTestMediaReferenceWidget
*/
public function testMediaReferenceWidget($cardinality, array $media_type_create_access, $list_access, $widget_id = 'entity_reference_autocomplete') {
$assert_session = $this->assertSession();
// Create two content types.
$non_media_content_type = $this->createContentType();
$content_type = $this->createContentType();
// Create some media types.
$media_types = [];
$permissions = [];
$create_media_types = [];
foreach ($media_type_create_access as $id => $access) {
if ($access) {
$create_media_types[] = "media_type_$id";
$permissions[] = "create media_type_$id media";
}
$this->createMediaType(['bundle' => "media_type_$id"]);
$media_types["media_type_$id"] = "media_type_$id";
}
// Create a user that can create content of the type, with other
// permissions as given by the data provider.
$permissions[] = "create {$content_type->id()} content";
if ($list_access) {
$permissions[] = "access media overview";
}
$test_user = $this->drupalCreateUser($permissions);
// Create a non-media entity reference.
$non_media_storage = FieldStorageConfig::create([
'field_name' => 'field_not_a_media_field',
'entity_type' => 'node',
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'node',
],
]);
$non_media_storage->save();
$non_media_field = FieldConfig::create([
'label' => 'No media here!',
'field_storage' => $non_media_storage,
'entity_type' => 'node',
'bundle' => $non_media_content_type->id(),
'settings' => [
'handler' => 'default',
'handler_settings' => [
'target_bundles' => [
$non_media_content_type->id() => $non_media_content_type->id(),
],
],
],
]);
$non_media_field->save();
\Drupal::entityTypeManager()
->getStorage('entity_form_display')
->load('node.' . $non_media_content_type->id() . '.default')
->setComponent('field_not_a_media_field', [
'type' => $widget_id,
])
->save();
// Create a media field through the user interface to ensure that the
// help text handling does not break the default value entry on the field
// settings form.
// Using drupalPostForm() to avoid dealing with JavaScript on the previous
// page in the field creation.
$edit = [
'new_storage_type' => 'field_ui:entity_reference:media',
'label' => "Media (cardinality $cardinality)",
'field_name' => 'media_reference',
];
$this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/add-field", $edit, 'Save and continue');
$edit = [];
foreach ($media_types as $type) {
$edit["settings[handler_settings][target_bundles][$type]"] = TRUE;
}
$this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/node.{$content_type->id()}.field_media_reference", $edit, "Save settings");
\Drupal::entityTypeManager()
->getStorage('entity_form_display')
->load('node.' . $content_type->id() . '.default')
->setComponent('field_media_reference', [
'type' => $widget_id,
])
->save();
// Some of the expected texts.
$create_help = 'Create your media on the media add page (opens a new window), then add it by name to the field below.';
$list_text = 'See the media list (opens a new window) to help locate media.';
$use_help = 'Type part of the media name.';
$create_header = "Create new media";
$use_header = "Use existing media";
// First check that none of the help texts are on the non-media content.
$this->drupalGet("/node/add/{$non_media_content_type->id()}");
$this->assertNoHelpTexts([
$create_header,
$create_help,
$use_header,
$use_help,
$list_text,
'Allowed media types:',
]);
// Now, check that the widget displays the expected help text under the
// given conditions for the test user.
$this->drupalLogin($test_user);
$this->drupalGet("/node/add/{$content_type->id()}");
// Specific expected help texts for the media field.
$create_header = "Create new media";
$use_header = "Use existing media";
$type_list = 'Allowed media types: ' . implode(", ", array_keys($media_types));
$fieldset_selector = '#edit-field-media-reference-wrapper fieldset';
$fieldset = $assert_session->elementExists('css', $fieldset_selector);
$this->assertSame("Media (cardinality $cardinality)", $assert_session->elementExists('css', 'legend', $fieldset)->getText());
// Assert text that should be displayed regardless of other access.
$this->assertHelpTexts([$use_header, $use_help, $type_list], $fieldset_selector);
// The entire section for creating new media should only be displayed if
// the user can create at least one media of the type.
if ($create_media_types) {
if (count($create_media_types) === 1) {
$url = Url::fromRoute('entity.media.add_form')->setRouteParameter('media_type', $create_media_types[0]);
}
else {
$url = Url::fromRoute('entity.media.add_page');
}
$this->assertHelpTexts([$create_header, $create_help], $fieldset_selector);
$this->assertHelpLink(
$fieldset,
'media add page',
[
'target' => '_blank',
'href' => $url->toString(),
]
);
}
else {
$this->assertNoHelpTexts([$create_header, $create_help]);
$this->assertNoHelpLink($fieldset, 'media add page');
}
if ($list_access) {
$this->assertHelpTexts([$list_text], $fieldset_selector);
$this->assertHelpLink(
$fieldset,
'media list',
[
'target' => '_blank',
'href' => Url::fromRoute('entity.media.collection')->toString(),
]
);
}
else {
$this->assertNoHelpTexts([$list_text]);
$this->assertNoHelpLink($fieldset, 'media list');
}
}
/**
* Asserts that the given texts are present exactly once.
*
* @param string[] $texts
* A list of the help texts to check.
* @param string $selector
* (optional) The selector to search.
*/
public function assertHelpTexts(array $texts, $selector = '') {
$assert_session = $this->assertSession();
foreach ($texts as $text) {
// We only want to escape single quotes, so use str_replace() rather than
// addslashes().
$text = str_replace("'", "\'", $text);
if ($selector) {
$assert_session->elementsCount('css', $selector . ":contains('$text')", 1);
}
else {
$assert_session->pageTextContains($text);
}
}
}
/**
* Asserts that none of the given texts are present.
*
* @param string[] $texts
* A list of the help texts to check.
*/
public function assertNoHelpTexts(array $texts) {
$assert_session = $this->assertSession();
foreach ($texts as $text) {
$assert_session->pageTextNotContains($text);
}
}
/**
* Asserts whether a given link is present.
*
* @param \Behat\Mink\Element\NodeElement $element
* The element to search.
* @param string $text
* The link text.
* @param string[] $attributes
* An associative array of any expected attributes, keyed by the
* attribute name.
*/
protected function assertHelpLink(NodeElement $element, $text, array $attributes = []) {
// Find all the links inside the element.
$link = $element->findLink($text);
$this->assertNotEmpty($link);
foreach ($attributes as $attribute => $value) {
$this->assertEquals($link->getAttribute($attribute), $value);
}
}
/**
* Asserts that a given link is not present.
*
* @param \Behat\Mink\Element\NodeElement $element
* The element to search.
* @param string $text
* The link text.
*/
protected function assertNoHelpLink(NodeElement $element, $text) {
$assert_session = $this->assertSession();
// Assert that the link and its text are not present anywhere on the page.
$assert_session->elementNotExists('named', ['link', $text], $element);
$assert_session->pageTextNotContains($text);
}
/**
* Test the media collection route.
*/
public function testMediaCollectionRoute() {
/** @var \Drupal\Core\Entity\EntityStorageInterface $media_storage */
$media_storage = $this->container->get('entity_type.manager')->getStorage('media');
$this->container->get('module_installer')->uninstall(['views']);
// Create a media type and media item.
$media_type = $this->createMediaType();
$media = $media_storage->create([
'bundle' => $media_type->id(),
'name' => 'Unnamed',
]);
$media->save();
$this->drupalGet($media->toUrl('collection'));
$assert_session = $this->assertSession();
// Media list table exists.
$assert_session->elementExists('css', 'th:contains("Media Name")');
$assert_session->elementExists('css', 'th:contains("Type")');
$assert_session->elementExists('css', 'th:contains("Operations")');
// Media item is present.
$assert_session->elementExists('css', 'td:contains("Unnamed")');
}
}
@@ -0,0 +1,56 @@
<?php
namespace Drupal\Tests\media\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\user\Entity\Role;
/**
* Tests that media settings are properly updated during database updates.
*
* @group media
*/
class MediaUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.media-enabled.php',
];
}
/**
* Tests that media permissions are correctly migrated.
*
* @see media_update_8500()
*/
public function testBundlePermission() {
$role = Role::load(Role::AUTHENTICATED_ID);
$this->grantPermissions($role, [
'update media',
'update any media',
'delete media',
'delete any media',
'create media',
]);
$this->runUpdates();
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(Role::AUTHENTICATED_ID);
$media_types = \Drupal::entityQuery('media_type')->execute();
foreach ($media_types as $media_type) {
$this->assertTrue($role->hasPermission("create $media_type media"));
$this->assertTrue($role->hasPermission("edit own $media_type media"));
$this->assertTrue($role->hasPermission("edit any $media_type media"));
$this->assertTrue($role->hasPermission("delete own $media_type media"));
$this->assertTrue($role->hasPermission("delete any $media_type media"));
}
}
}
@@ -0,0 +1,173 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\media\Entity\Media;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Basic display tests for Media.
*
* @group media
*/
class MediaDisplayTest extends MediaJavascriptTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Install the optional configs from the standard profile.
$extension_path = drupal_get_path('profile', 'standard');
$optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
$storage = new FileStorage($optional_install_path);
$this->container->get('config.installer')->installOptionalConfig($storage, '');
// Reset all the static caches and list caches.
$this->container->get('config.factory')->reset();
}
/**
* Test basic media display.
*/
public function testMediaDisplay() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$media_type = $this->createMediaType();
// Create a media item.
$media = Media::create([
'bundle' => $media_type->id(),
'name' => 'Fantastic!',
]);
$media->save();
$this->drupalGet('media/' . $media->id());
// Verify the "name" field is really not present.
$assert_session->elementNotExists('css', '.field--name-name');
// Enable the field on the display and verify it becomes visible on the UI.
$this->drupalGet("/admin/structure/media/manage/{$media_type->id()}/display");
$page->selectFieldOption('fields[name][region]', 'content');
$assert_session->waitForElementVisible('css', '#edit-fields-name-settings-edit');
$page->pressButton('Save');
$this->drupalGet('media/' . $media->id());
// Verify the name is present, and its text matches what is expected.
$assert_session->elementExists('css', '.field--name-name');
$name_field = $page->find('css', '.field--name-name .field__item');
$this->assertEquals($media->label(), $name_field->getText());
// In the standard profile, there are some pre-cooked types. Make sure the
// elements configured on their displays are the expected ones.
$this->drupalGet('media/add/image');
$image_media_name = 'Fantastic image asset!';
$page->fillField('name[0][value]', $image_media_name);
$page->attachFileToField('files[field_media_image_0]', \Drupal::root() . '/core/modules/media/tests/fixtures/example_1.jpeg');
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->fillField('field_media_image[0][alt]', 'Image Alt Text 1');
$page->pressButton('Save');
$image_media_id = $this->container->get('entity.query')->get('media')
->sort('mid', 'DESC')
->execute();
$image_media_id = reset($image_media_id);
// Here we expect to see only the image, nothing else.
// Assert only one element in the content region.
$this->assertEquals(1, count($page->findAll('css', '.media--type-image > div')));
// Assert the image is present inside the media element.
$media_item = $assert_session->elementExists('css', '.media--type-image > div');
$assert_session->elementExists('css', 'img', $media_item);
// Assert that the image src is the original image and not an image style.
$media_image = $assert_session->elementExists('css', '.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/example_1.jpeg')));
$this->assertEquals($expected_image_src, $media_image->getAttribute('src'));
$test_filename = $this->randomMachineName() . '.txt';
$test_filepath = 'public://' . $test_filename;
file_put_contents($test_filepath, $this->randomMachineName());
$this->drupalGet("media/add/file");
$page->fillField('name[0][value]', 'File media 1');
$page->attachFileToField("files[field_media_file_0]", \Drupal::service('file_system')->realpath($test_filepath));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
// Here we expect to see only the linked filename.
// Assert only one element in the content region.
$this->assertEquals(1, count($page->findAll('css', 'article.media--type-file > div')));
// Assert the file link is present, and its text matches the filename.
$assert_session->elementExists('css', 'article.media--type-file .field--name-field-media-file a');
$link = $page->find('css', 'article.media--type-file .field--name-field-media-file a');
$this->assertEquals($test_filename, $link->getText());
// Create a node type "page" to use as host entity.
$node_type = NodeType::create([
'type' => 'page',
'name' => 'Page',
]);
$node_type->save();
// Reference the created media using an entity_refernce field and make sure
// the output is what we expect.
$storage = FieldStorageConfig::create([
'entity_type' => 'node',
'field_name' => 'field_related_media',
'type' => 'entity_reference',
'settings' => [
'target_type' => 'media',
],
]);
$storage->save();
FieldConfig::create([
'field_storage' => $storage,
'entity_type' => 'node',
'bundle' => $node_type->id(),
'label' => 'Related media',
'settings' => [
'handler_settings' => [
'target_bundles' => [
'image' => 'image',
],
],
],
])->save();
entity_get_display('node', $node_type->id(), 'default')
->setComponent('field_related_media', [
'type' => 'entity_reference_entity_view',
'label' => 'hidden',
'settings' => [
'view_mode' => 'full',
],
])->save();
$node = Node::create([
'title' => 'Host node',
'type' => $node_type->id(),
'field_related_media' => [
'target_id' => $image_media_id,
],
]);
$node->save();
$this->drupalGet('/node/' . $node->id());
// Media field is there.
$assert_session->elementExists('css', '.field--name-field-related-media');
// Media name element is not there.
$assert_session->elementNotExists('css', '.field--name-name');
$assert_session->pageTextNotContains($image_media_name);
// Only one element is present inside the media container.
$this->assertEquals(1, count($page->findAll('css', '.field--name-field-related-media article.media--type-image > div')));
// Assert the image is present.
$assert_session->elementExists('css', '.field--name-field-related-media article.media--type-image img');
}
}
@@ -0,0 +1,56 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Component\Utility\Html;
/**
* Tests related to media reference fields.
*
* @group media
*/
class MediaReferenceFieldHelpTest extends MediaJavascriptTestBase {
/**
* Test our custom help texts when creating a field.
*
* @see media_form_field_ui_field_storage_add_form_alter()
*/
public function testFieldCreationHelpText() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$type = $this->drupalCreateContentType([
'type' => 'foo',
]);
$this->drupalGet("/admin/structure/types/manage/{$type->id()}/fields/add-field");
$field_types = [
'file',
'image',
'field_ui:entity_reference:media',
];
$description_ids = array_map(function ($item) {
return '#edit-description-' . Html::cleanCssIdentifier($item);
}, $field_types);
// Choose a boolean field, none of the description containers should be
// visible.
$assert_session->optionExists('edit-new-storage-type', 'boolean');
$page->selectFieldOption('edit-new-storage-type', 'boolean');
foreach ($description_ids as $description_id) {
$this->assertFalse($assert_session->elementExists('css', $description_id)->isVisible());
}
// Select each of the file, image, and media fields and verify their
// descriptions are now visible and match the expected text.
$help_text = 'Use Media reference fields for most files, images, audio, videos, and remote media. Use File or Image reference fields when creating your own media types, or for legacy files and images created before enabling the Media module.';
foreach ($field_types as $field_name) {
$assert_session->optionExists('edit-new-storage-type', $field_name);
$page->selectFieldOption('edit-new-storage-type', $field_name);
$field_description_element = $assert_session->elementExists('css', '#edit-description-' . Html::cleanCssIdentifier($field_name));
$this->assertTrue($field_description_element->isVisible());
$this->assertEquals($help_text, $field_description_element->getText());
}
}
}
@@ -0,0 +1,105 @@
<?php
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\file\Entity\File;
/**
* Tests the Audio and Video media sources.
*
* @group media
*/
class MediaSourceAudioVideoTest extends MediaSourceTestBase {
/**
* Check the Audio source functionality.
*/
public function testAudioTypeCreation() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_id = 'audio_file';
$type_name = 'audio_type';
$field_name = 'field_media_' . $source_id;
$this->doTestCreateMediaType($type_name, $source_id);
// Check that the source field was created with the correct settings.
$storage = FieldStorageConfig::load("media.$field_name");
$this->assertInstanceOf(FieldStorageConfig::class, $storage);
$field = FieldConfig::load("media.$type_name.$field_name");
$this->assertInstanceOf(FieldConfig::class, $field);
$this->assertSame('mp3 wav aac', FieldConfig::load("media.$type_name.$field_name")->get('settings')['file_extensions']);
// Check that the display holds the correct formatter configuration.
$display = EntityViewDisplay::load("media.$type_name.default");
$this->assertInstanceOf(EntityViewDisplay::class, $display);
$formatter = $display->getComponent($field_name)['type'];
$this->assertSame('file_audio', $formatter);
// Create a media asset and verify that the <audio> tag is present.
file_put_contents('public://file.mp3', str_repeat('t', 10));
$file = File::create([
'uri' => 'public://file.mp3',
'filename' => 'file.mp3',
]);
$file->save();
$this->drupalGet("media/add/$type_name");
$page->fillField('Name', 'Audio media asset');
$page->attachFileToField("files[{$field_name}_0]", \Drupal::service('file_system')->realpath('public://file.mp3'));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$assert_session->pageTextContains("$type_name Audio media asset has been created.");
$assert_session->elementExists('css', "audio > source[type='audio/mpeg']");
}
/**
* Check the Video source functionality.
*/
public function testVideoTypeCreation() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$source_id = 'video_file';
$type_name = 'video_type';
$field_name = 'field_media_' . $source_id;
$this->doTestCreateMediaType($type_name, $source_id);
// Check that the source field was created with the correct settings.
$storage = FieldStorageConfig::load("media.$field_name");
$this->assertInstanceOf(FieldStorageConfig::class, $storage);
$field = FieldConfig::load("media.$type_name.$field_name");
$this->assertInstanceOf(FieldConfig::class, $field);
$this->assertSame('mp4', FieldConfig::load("media.$type_name.$field_name")->getSetting('file_extensions'));
// Check that the display holds the correct formatter configuration.
$display = EntityViewDisplay::load("media.$type_name.default");
$this->assertInstanceOf(EntityViewDisplay::class, $display);
$formatter = $display->getComponent($field_name)['type'];
$this->assertSame('file_video', $formatter);
// Create a media asset and verify that the <video> tag is present.
file_put_contents('public://file.mp4', str_repeat('t', 10));
$file = File::create([
'uri' => 'public://file.mp4',
'filename' => 'file.mp4',
]);
$file->save();
$this->drupalGet("media/add/$type_name");
$page->fillField('Name', 'Video media asset');
$page->attachFileToField("files[{$field_name}_0]", \Drupal::service('file_system')->realpath('public://file.mp4'));
$result = $assert_session->waitForButton('Remove');
$this->assertNotEmpty($result);
$page->pressButton('Save');
$assert_session->pageTextContains("$type_name Video media asset has been created.");
$assert_session->elementExists('css', "video > source[type='video/mp4']");
}
}
@@ -3,7 +3,7 @@
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media\Plugin\media\Source\File;
/**
* Tests the file media source.
@@ -12,33 +12,38 @@ use Drupal\media\Entity\MediaType;
*/
class MediaSourceFileTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// We need to test without any default configuration in place.
// @TODO: Remove this as part of https://www.drupal.org/node/2883813.
MediaType::load('file')->delete();
}
/**
* Tests the file media source.
*/
public function testMediaFileSource() {
$media_type_id = 'test_media_file_type';
$source_field_id = 'field_media_file';
$provided_fields = [
File::METADATA_ATTRIBUTE_SIZE,
File::METADATA_ATTRIBUTE_MIME,
];
$session = $this->getSession();
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->doTestCreateMediaType($media_type_id, 'file');
$this->doTestCreateMediaType($media_type_id, 'file', $provided_fields);
// Create custom fields for the media type to store metadata attributes.
$fields = [
'field_string_file_size' => 'string',
'field_string_mime_type' => 'string',
];
$this->createMediaTypeFields($fields, $media_type_id);
// Hide the name field widget to test default name generation.
$this->hideMediaTypeFieldWidget('name', $media_type_id);
$this->drupalGet("admin/structure/media/manage/{$media_type_id}");
$page->selectFieldOption("field_map[" . File::METADATA_ATTRIBUTE_SIZE . "]", 'field_string_file_size');
$page->selectFieldOption("field_map[" . File::METADATA_ATTRIBUTE_MIME . "]", 'field_string_mime_type');
$page->pressButton('Save');
$test_filename = $this->randomMachineName() . '.txt';
$test_filepath = 'public://' . $test_filename;
file_put_contents($test_filepath, $this->randomMachineName());
@@ -62,9 +67,11 @@ class MediaSourceFileTest extends MediaSourceTestBase {
$page->checkField('revision');
$assert_session->elementAttributeNotContains('css', '.field--name-revision-log-message', 'style', 'display');
// Load the media and check if the label was properly populated.
// Load the media and check that all the fields are properly populated.
$media = Media::load(1);
$this->assertEquals($test_filename, $media->getName());
$this->assertEquals('8', $media->get('field_string_file_size')->value);
$this->assertEquals('text/plain', $media->get('field_string_mime_type')->value);
// Test the MIME type icon.
$icon_base = \Drupal::config('media.settings')->get('icon_base_uri');
@@ -3,7 +3,6 @@
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media\Plugin\media\Source\Image;
/**
@@ -13,18 +12,6 @@ use Drupal\media\Plugin\media\Source\Image;
*/
class MediaSourceImageTest extends MediaSourceTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// We need to test without any default configuration in place.
// @TODO: Remove this as part of https://www.drupal.org/node/2883813.
MediaType::load('file')->delete();
MediaType::load('image')->delete();
}
/**
* Tests the image media source.
*/
@@ -2,6 +2,8 @@
namespace Drupal\Tests\media\FunctionalJavascript;
use Drupal\Component\Utility\Html;
/**
* Tests the media type creation.
*
@@ -49,6 +51,40 @@ class MediaTypeCreationTest extends MediaJavascriptTestBase {
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}");
$assert_session->pageTextContains('Test source field is used to store the essential information about the media item.');
// Check that the plugin cannot be changed after it is set on type creation.
$assert_session->fieldDisabled('Media source');
$assert_session->pageTextContains('The media source cannot be changed after the media type is created.');
// Open up the media add form and verify that the source field is right
// after the name, and before the vertical tabs.
$this->drupalGet("/media/add/$mediaTypeMachineName");
// Get the form element, and its HTML representation.
$form_selector = '#media-' . Html::cleanCssIdentifier($mediaTypeMachineName) . '-add-form';
$form = $assert_session->elementExists('css', $form_selector);
$form_html = $form->getOuterHtml();
// The name field should come before the source field, which should itself
// come before the vertical tabs.
$name_field = $assert_session->fieldExists('Name', $form)->getOuterHtml();
$test_source_field = $assert_session->fieldExists('Test source', $form)->getOuterHtml();
$vertical_tabs = $assert_session->elementExists('css', '.vertical-tabs', $form)->getOuterHtml();
$date_field = $assert_session->fieldExists('Date', $form)->getOuterHtml();
$published_checkbox = $assert_session->fieldExists('Published', $form)->getOuterHtml();
$this->assertTrue(strpos($form_html, $test_source_field) > strpos($form_html, $name_field));
$this->assertTrue(strpos($form_html, $vertical_tabs) > strpos($form_html, $test_source_field));
// The "Published" checkbox should be the last element.
$this->assertTrue(strpos($form_html, $published_checkbox) > strpos($form_html, $date_field));
// Check that a new type with the same machine name cannot be created.
$this->drupalGet('admin/structure/media/add');
$page->fillField('label', $label);
$session->wait(5000, "jQuery('.machine-name-value').text() === '{$mediaTypeMachineName}'");
$page->selectFieldOption('Media source', 'test');
$assert_session->assertWaitOnAjaxRequest();
$page->pressButton('Save');
$assert_session->pageTextContains('The machine-readable name is already in use. It must be unique.');
}
/**
@@ -38,11 +38,6 @@ class MediaUiJavascriptTest extends MediaJavascriptTestBase {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
// We need to test without any default configuration in place.
// @TODO: Remove this as part of https://www.drupal.org/node/2883813.
MediaType::load('file')->delete();
MediaType::load('image')->delete();
}
/**
@@ -68,6 +63,10 @@ class MediaUiJavascriptTest extends MediaJavascriptTestBase {
$this->assertJsCondition("jQuery('.form-item-source-configuration-test-config-value').length > 0;");
$page->fillField('description', $description);
$page->pressButton('Save');
// The wait prevents intermittent test failures.
$result = $assert_session->waitForLink('Add media type');
$this->assertNotEmpty($result);
$assert_session->addressEquals('admin/structure/media');
$assert_session->pageTextContains('The media type ' . $name . ' has been added.');
$this->drupalGet('admin/structure/media');
$assert_session->pageTextContains($name);
@@ -139,6 +138,10 @@ class MediaUiJavascriptTest extends MediaJavascriptTestBase {
$page->uncheckField('options[status]');
$page->checkField('options[queue_thumbnail_downloads]');
$page->pressButton('Save');
// The wait prevents intermittent test failures.
$result = $assert_session->waitForLink('Add media type');
$this->assertNotEmpty($result);
$assert_session->addressEquals('admin/structure/media');
$assert_session->pageTextContains("The media type $new_name has been updated.");
// Test if edit worked and if new field values have been saved as expected.
@@ -22,6 +22,8 @@ class MediaViewsWizardTest extends MediaJavascriptTestBase {
$page = $session->getPage();
$assert_session = $this->assertSession();
$this->createMediaType();
$view_id = strtolower($this->randomMachineName(16));
$this->drupalGet('admin/structure/views/add');
$page->fillField('label', $view_id);
@@ -6,6 +6,8 @@ use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
/**
* Tests creation of media types and media items.
@@ -33,6 +35,27 @@ class MediaCreationTest extends MediaKernelTestBase {
// be created automatically when a config is being imported.
$this->assertEquals(['source_field' => '', 'test_config_value' => 'Kakec'], $test_media_type->get('source_configuration'), 'Could not assure the correct media source configuration.');
$this->assertEquals(['metadata_attribute' => 'field_attribute_config_test'], $test_media_type->get('field_map'), 'Could not assure the correct field map.');
// Check the Media Type access handler behavior.
// We grant access to the 'view label' operation to all users having
// permission to 'view media'.
$user1 = User::create([
'name' => 'username1',
'status' => 1,
]);
$user1->save();
$user2 = User::create([
'name' => 'username2',
'status' => 1,
]);
$user2->save();
$role = Role::create([
'id' => 'role1',
'label' => 'role1',
]);
$role->grantPermission('view media')->trustData()->save();
$user2->addRole($role->id());
$this->assertFalse($test_media_type->access('view label', $user1));
$this->assertTrue($test_media_type->access('view label', $user2));
}
/**
@@ -109,7 +109,7 @@ abstract class MediaKernelTestBase extends KernelTestBase {
}
/**
* Helper to generate media entity.
* Helper to generate a media item.
*
* @param string $filename
* String filename with extension.
@@ -117,7 +117,7 @@ abstract class MediaKernelTestBase extends KernelTestBase {
* The the media type.
*
* @return \Drupal\media\Entity\Media
* A media entity.
* A media item.
*/
protected function generateMedia($filename, MediaTypeInterface $media_type) {
vfsStream::setup('drupal_root');
@@ -5,7 +5,7 @@ namespace Drupal\Tests\media\Kernel;
use Drupal\media\Entity\Media;
/**
* Tests link relationships for Media entity.
* Tests link relationships for media items.
*
* @group media
*/
@@ -20,7 +20,7 @@ class MediaLinkRelationsTest extends MediaKernelTestBase {
$media = Media::create(['bundle' => $this->testMediaType->id()]);
$media->save();
foreach ($media->uriRelationships() as $relation_name) {
$this->assertTrue($link_relation_type_manager->hasDefinition($relation_name), "Link relationship '{$relation_name}' for Media entity");
$this->assertTrue($link_relation_type_manager->hasDefinition($relation_name), "Link relationship '{$relation_name}' for a media item");
}
}
@@ -2,8 +2,6 @@
namespace Drupal\Tests\media\Kernel;
use Drupal\media\Entity\MediaType;
/**
* Tests the file media source.
*
@@ -11,18 +9,6 @@ use Drupal\media\Entity\MediaType;
*/
class MediaSourceFileTest extends MediaKernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// We need to test without any default configuration in place.
// @TODO: Remove this as part of https://www.drupal.org/node/2883813.
MediaType::load('file')->delete();
MediaType::load('image')->delete();
}
/**
* Tests the file extension constraint.
*/
@@ -140,6 +140,20 @@ class MediaSourceTest extends MediaKernelTestBase {
$this->assertEquals('Snowball', $media->get($field_name)->value, 'Metadata attribute was not mapped to the field.');
}
/**
* Tests the getSourceFieldValue() method.
*/
public function testGetSourceFieldValue() {
/** @var \Drupal\media\MediaInterface $media */
$media = Media::create([
'bundle' => $this->testMediaType->id(),
'field_media_test' => 'some_value',
]);
$media->save();
$media_source = $media->getSource();
$this->assertSame('some_value', $media_source->getSourceFieldValue($media));
}
/**
* Tests the thumbnail functionality.
*/
@@ -147,7 +161,7 @@ class MediaSourceTest extends MediaKernelTestBase {
file_put_contents('public://thumbnail1.jpg', '');
file_put_contents('public://thumbnail2.jpg', '');
// Save a media entity and make sure thumbnail was added.
// Save a media item and make sure thumbnail was added.
\Drupal::state()->set('media_source_test_attributes', [
'thumbnail_uri' => ['title' => 'Thumbnail', 'value' => 'public://thumbnail1.jpg'],
]);
@@ -160,7 +174,7 @@ class MediaSourceTest extends MediaKernelTestBase {
$media_source = $media->getSource();
$this->assertEquals('public://thumbnail1.jpg', $media_source->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is not correct.');
$media->save();
$this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'Thumbnail was not added to the media entity.');
$this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'Thumbnail was not added to the media item.');
$this->assertEquals('Mr. Jones', $media->thumbnail->title, 'Title text was not set on the thumbnail.');
$this->assertEquals('Thumbnail', $media->thumbnail->alt, 'Alt text was not set on the thumbnail.');
@@ -179,7 +193,7 @@ class MediaSourceTest extends MediaKernelTestBase {
$media->thumbnail->target_id = NULL;
$this->assertEquals('public://thumbnail2.jpg', $media_source->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is not correct.');
$media->save();
$this->assertEquals('public://thumbnail2.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was not added to the media entity.');
$this->assertEquals('public://thumbnail2.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was not added to the media item.');
$this->assertEquals('Mr. Jones', $media->thumbnail->title, 'Title text was not set on the thumbnail.');
$this->assertEquals('Thumbnail', $media->thumbnail->alt, 'Alt text was not set on the thumbnail.');
@@ -191,7 +205,7 @@ class MediaSourceTest extends MediaKernelTestBase {
$media->field_media_test->value = 'some_new_value';
$this->assertEquals('public://thumbnail1.jpg', $media_source->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is not correct.');
$media->save();
$this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was not added to the media entity.');
$this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was not added to the media item.');
$this->assertEquals('Mr. Jones', $media->thumbnail->title, 'Title text was not set on the thumbnail.');
$this->assertEquals('Thumbnail', $media->thumbnail->alt, 'Alt text was not set on the thumbnail.');
@@ -268,13 +282,13 @@ class MediaSourceTest extends MediaKernelTestBase {
'field_media_test' => 'some_value',
]);
$media->save();
$this->assertEquals('Boxer', $media->getName(), 'Correct name was not set on the media entity.');
$this->assertEquals('Boxer', $media->getName(), 'Correct name was not set on the media item.');
$this->assertEquals('This will be title.', $media->thumbnail->title, 'Title text was not set on the thumbnail.');
$this->assertEquals('This will be alt.', $media->thumbnail->alt, 'Alt text was not set on the thumbnail.');
}
/**
* Tests the media entity constraints functionality.
* Tests the media item constraints functionality.
*/
public function testConstraints() {
// Test entity constraints.
@@ -442,4 +456,79 @@ class MediaSourceTest extends MediaKernelTestBase {
$this->assertEquals($expected, $source->getConfiguration(), 'Submitted values were saved correctly.');
}
/**
* Tests different display options for the source field.
*/
public function testDifferentSourceFieldDisplays() {
$id = 'test_different_displays';
$field_name = 'field_media_different_display';
$this->createMediaTypeViaForm($id, $field_name);
// Source field not in displays.
$display = entity_get_display('media', $id, 'default');
$components = $display->getComponents();
$this->assertArrayHasKey($field_name, $components);
$this->assertSame('entity_reference_entity_id', $components[$field_name]['type']);
$display = entity_get_form_display('media', $id, 'default');
$components = $display->getComponents();
$this->assertArrayHasKey($field_name, $components);
$this->assertSame('entity_reference_autocomplete_tags', $components[$field_name]['type']);
}
/**
* Tests hidden source field in media type.
*/
public function testHiddenSourceField() {
$id = 'test_hidden_source_field';
$field_name = 'field_media_hidden';
$this->createMediaTypeViaForm($id, $field_name);
// Source field not in displays.
$display = entity_get_display('media', $id, 'default');
$this->assertArrayNotHasKey($field_name, $display->getComponents());
$display = entity_get_form_display('media', $id, 'default');
$this->assertArrayNotHasKey($field_name, $display->getComponents());
}
/**
* Creates a media type via form submit.
*
* @param string $source_plugin_id
* Source plugin ID.
* @param string $field_name
* Source field name.
*/
protected function createMediaTypeViaForm($source_plugin_id, $field_name) {
/** @var \Drupal\media\MediaTypeInterface $type */
$type = MediaType::create(['source' => $source_plugin_id]);
$form = $this->container->get('entity_type.manager')
->getFormObject('media_type', 'add')
->setEntity($type);
$form_state = new FormState();
$form_state->setValues([
'label' => 'Test type',
'id' => $source_plugin_id,
'op' => t('Save'),
]);
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
$field_manager = \Drupal::service('entity_field.manager');
// Source field not created yet.
$fields = $field_manager->getFieldDefinitions('media', $source_plugin_id);
$this->assertArrayNotHasKey($field_name, $fields);
\Drupal::formBuilder()->submitForm($form, $form_state);
// Source field exists now.
$fields = $field_manager->getFieldDefinitions('media', $source_plugin_id);
$this->assertArrayHasKey($field_name, $fields);
}
}
@@ -12,7 +12,7 @@ use Drupal\media\Entity\Media;
class MediaTest extends MediaKernelTestBase {
/**
* Tests various aspects of a Media entity.
* Tests various aspects of a media item.
*/
public function testEntity() {
$media = Media::create(['bundle' => $this->testMediaType->id()]);
@@ -21,14 +21,17 @@ class MediaTest extends MediaKernelTestBase {
}
/**
* Ensure media name is configurable on manage display.
* Tests the Media "name" base field behavior.
*/
public function testNameIsConfigurable() {
public function testNameBaseField() {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $field_definitions */
$field_definitions = $this->container->get('entity_field.manager')
->getBaseFieldDefinitions('media');
// Ensure media name is configurable on manage display.
$this->assertTrue($field_definitions['name']->isDisplayConfigurable('view'));
// Ensure it is not visible by default.
$this->assertEquals($field_definitions['name']->getDisplayOptions('view'), ['region' => 'hidden']);
}
}