Skip to content
Snippets Groups Projects
Commit 4e88aed4 authored by Angie Byron's avatar Angie Byron
Browse files

Issue #2938116 by samuel.mortenson, sjerdo, jefuri, chr.fritsch, yogeshmpawar,...

Issue #2938116 by samuel.mortenson, sjerdo, jefuri, chr.fritsch, yogeshmpawar, starshaped, phenaproxima, jibran, webchick, seanB, Berdir, lauriii, xjm, tstoeckler, dawehner, benjifisher, marcoscano, bdimaggio, martin107, mtodor, slashrsm, Gábor Hojtsy, ckrina, yoroy: Allow media to be uploaded with the Media Library field widget

(cherry picked from commit 53d521a3)
parent 33511d35
Branches
Tags
No related merge requests found
Showing
with 1249 additions and 36 deletions
langcode: en
status: true
dependencies:
module:
- media
id: media.media_library
label: 'Media library'
targetEntityType: media
cache: true
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.audio.field_media_audio_file
- media.type.audio
id: media.audio.media_library
targetEntityType: media
bundle: audio
mode: media_library
content:
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
field_media_audio_file: true
path: true
status: true
uid: true
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.file.field_media_file
- media.type.file
id: media.file.media_library
targetEntityType: media
bundle: file
mode: media_library
content:
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
field_media_file: true
path: true
status: true
uid: true
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.image.field_media_image
- image.style.thumbnail
- media.type.image
module:
- image
id: media.image.media_library
targetEntityType: media
bundle: image
mode: media_library
content:
field_media_image:
type: image_image
weight: 1
region: content
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
path: true
status: true
uid: true
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.video.field_media_video_file
- media.type.video
id: media.video.media_library
targetEntityType: media
bundle: video
mode: media_library
content:
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
field_media_video_file: true
path: true
status: true
uid: true
...@@ -198,6 +198,44 @@ ...@@ -198,6 +198,44 @@
border-color: #40b6ff; border-color: #40b6ff;
} }
/* Style the wrappers around new media and files */
.media-library-upload__media,
.media-library-upload__file {
display: flex;
flex-wrap: wrap;
padding: 20px 0 20px 0;
}
.media-library-upload__file {
align-items: center;
}
.media-library-upload__file-label {
margin-right: 10px;
}
/* @todo Remove in https://www.drupal.org/project/drupal/issues/2987921 */
.media-library-upload__source-field .file,
.media-library-upload__source-field .button,
.media-library-upload__source-field .image-preview,
.media-library-upload__source-field .form-type-managed-file > label,
.media-library-upload__source-field .file-size {
display: none;
}
.media-library-upload__media-preview {
margin-right: 20px;
width: 220px;
background: #ebebeb;
display: flex;
align-items: center;
justify-content: center;
}
.media-library-upload__media-preview img {
display: block;
}
/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */ /* @todo Remove or re-work in https://www.drupal.org/node/2985168 */
.media-library-widget .media-library-item__name a, .media-library-widget .media-library-item__name a,
.media-library-view.view-display-id-widget .media-library-item__name a { .media-library-view.view-display-id-widget .media-library-item__name a {
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
*/ */
use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Template\Attribute; use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\views\Form\ViewsForm; use Drupal\views\Form\ViewsForm;
use Drupal\views\Plugin\views\cache\CachePluginBase; use Drupal\views\Plugin\views\cache\CachePluginBase;
use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\Plugin\views\query\QueryPluginBase;
...@@ -42,6 +44,36 @@ function media_library_theme() { ...@@ -42,6 +44,36 @@ function media_library_theme() {
]; ];
} }
/**
* Implements hook_preprocess_view().
*
* Adds a link to add media above the view.
*/
function media_library_preprocess_views_view(&$variables) {
$view = $variables['view'];
if ($view->id() === 'media_library' && $view->current_display === 'widget') {
$url = Url::fromRoute('media_library.upload');
if ($url->access()) {
$url->setOption('query', \Drupal::request()->query->all());
$variables['header']['add_media'] = [
'#type' => 'link',
'#title' => t('Add media'),
'#url' => $url,
'#attributes' => [
'class' => ['button', 'button-action', 'button--primary', 'use-ajax'],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'dialogClass' => 'media-library-widget-modal',
'height' => '75%',
'width' => '75%',
'title' => t('Add media'),
]),
],
];
}
}
}
/** /**
* Implements hook_views_post_render(). * Implements hook_views_post_render().
*/ */
......
media_library.upload:
path: '/admin/content/media-widget-upload'
defaults:
_form: '\Drupal\media_library\Form\MediaLibraryUploadForm'
requirements:
_custom_access: '\Drupal\media_library\Form\MediaLibraryUploadForm::access'
<?php
namespace Drupal\media_library\Form;
use Drupal\Core\Access\AccessResultAllowed;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\ElementInfoManagerInterface;
use Drupal\file\FileInterface;
use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
use Drupal\file\Plugin\Field\FieldType\FileItem;
use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* Creates a form to create media entities from uploaded files.
*
* @internal
*/
class MediaLibraryUploadForm extends FormBase {
/**
* The element info manager.
*
* @var \Drupal\Core\Render\ElementInfoManagerInterface
*/
protected $elementInfo;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Media types the current user has access to.
*
* @var \Drupal\media\MediaTypeInterface[]
*/
protected $types;
/**
* The media being processed.
*
* @var \Drupal\media\MediaInterface[]
*/
protected $media = [];
/**
* The files waiting for type selection.
*
* @var \Drupal\file\FileInterface[]
*/
protected $files = [];
/**
* Indicates whether the 'medium' image style exists.
*
* @var bool
*/
protected $mediumStyleExists = FALSE;
/**
* Constructs a new MediaLibraryUploadForm.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
* The element info manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ElementInfoManagerInterface $element_info) {
$this->entityTypeManager = $entity_type_manager;
$this->elementInfo = $element_info;
$this->mediumStyleExists = !empty($entity_type_manager->getStorage('image_style')->load('medium'));
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('element_info')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'media_library_upload_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['#prefix'] = '<div id="media-library-upload-wrapper">';
$form['#suffix'] = '</div>';
$form['#attached']['library'][] = 'media_library/style';
$form['#attributes']['class'][] = 'media-library-upload';
if (empty($this->media) && empty($this->files)) {
$process = (array) $this->elementInfo->getInfoProperty('managed_file', '#process', []);
$upload_validators = $this->mergeUploadValidators($this->getTypes());
$form['upload'] = [
'#type' => 'managed_file',
'#title' => $this->t('Upload'),
// @todo Move validation in https://www.drupal.org/node/2988215
'#process' => array_merge(['::validateUploadElement'], $process, ['::processUploadElement']),
'#upload_validators' => $upload_validators,
];
$form['upload_help'] = [
'#theme' => 'file_upload_help',
'#description' => $this->t('Upload files here to add new media.'),
'#upload_validators' => $upload_validators,
];
$remaining = $this->getRequest()->query->get('media_library_remaining');
if ($remaining) {
$form['upload']['#multiple'] = $remaining > 1;
$form['upload']['#cardinality'] = $form['upload_help']['#cardinality'] = (int) $remaining;
}
}
else {
$form['media'] = [
'#type' => 'container',
];
foreach ($this->media as $i => $media) {
$source_field = $media->getSource()
->getSourceFieldDefinition($media->bundle->entity)
->getName();
$element = [
'#type' => 'container',
'#attributes' => [
'class' => [
'media-library-upload__media',
],
],
'preview' => [
'#type' => 'container',
'#attributes' => [
'class' => [
'media-library-upload__media-preview',
],
],
],
'fields' => [
'#type' => 'container',
'#attributes' => [
'class' => [
'media-library-upload__media-fields',
],
],
// Parents is set here as it is used in the form display.
'#parents' => ['media', $i, 'fields'],
],
];
// @todo Make this configurable in https://www.drupal.org/node/2988223
if ($this->mediumStyleExists && $thumbnail_uri = $media->getSource()->getMetadata($media, 'thumbnail_uri')) {
$element['preview']['thumbnail'] = [
'#theme' => 'image_style',
'#style_name' => 'medium',
'#uri' => $thumbnail_uri,
];
}
EntityFormDisplay::collectRenderDisplay($media, 'media_library')
->buildForm($media, $element['fields'], $form_state);
// We hide certain elements in the image widget with CSS.
if (isset($element['fields'][$source_field])) {
$element['fields'][$source_field]['#attributes']['class'][] = 'media-library-upload__source-field';
}
if (isset($element['fields']['revision_log_message'])) {
$element['fields']['revision_log_message']['#access'] = FALSE;
}
$form['media'][$i] = $element;
}
$form['files'] = [
'#type' => 'container',
];
foreach ($this->files as $i => $file) {
$types = $this->filterTypesThatAcceptFile($file, $this->getTypes());
$form['files'][$i] = [
'#type' => 'container',
'#attributes' => [
'class' => [
'media-library-upload__file',
],
],
'help' => [
'#markup' => '<strong class="media-library-upload__file-label">' . $this->t('Select a media type for %filename:', [
'%filename' => $file->getFilename(),
]) . '</strong>',
],
];
foreach ($types as $type) {
$form['files'][$i][$type->id()] = [
'#type' => 'submit',
'#media_library_index' => $i,
'#media_library_type' => $type->id(),
'#value' => $type->label(),
'#submit' => ['::selectType'],
'#ajax' => [
'callback' => '::updateFormCallback',
'wrapper' => 'media-library-upload-wrapper',
],
'#limit_validation_errors' => [['files', $i, $type->id()]],
];
}
}
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
'#ajax' => [
'callback' => '::updateWidget',
'wrapper' => 'media-library-upload-wrapper',
],
];
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (count($this->files)) {
$form_state->setError($form['files'], $this->t('Please select a media type for all files.'));
}
foreach ($this->media as $i => $media) {
$form_display = EntityFormDisplay::collectRenderDisplay($media, 'media_library');
$form_display->extractFormValues($media, $form['media'][$i]['fields'], $form_state);
$form_display->validateFormValues($media, $form['media'][$i]['fields'], $form_state);
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($this->media as $i => $media) {
EntityFormDisplay::collectRenderDisplay($media, 'media_library')
->extractFormValues($media, $form['media'][$i]['fields'], $form_state);
$source_field = $media->getSource()->getSourceFieldDefinition($media->bundle->entity)->getName();
/** @var \Drupal\file\FileInterface $file */
$file = $media->get($source_field)->entity;
$file->setPermanent();
$file->save();
$media->save();
}
}
/**
* AJAX callback to select a media type for a file.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* If the triggering element is missing required properties.
*/
public function selectType(array &$form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
if (!isset($element['#media_library_index']) || !isset($element['#media_library_type'])) {
throw new BadRequestHttpException('The "#media_library_index" and "#media_library_type" properties on the triggering element are required for type selection.');
}
$i = $element['#media_library_index'];
$type = $element['#media_library_type'];
$this->media[] = $this->createMediaEntity($this->files[$i], $this->getTypes()[$type]);
unset($this->files[$i]);
$form_state->setRebuild();
}
/**
* AJAX callback to update the field widget.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* A command to send the selection to the current field widget.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* If the "media_library_widget_id" query parameter is not present.
*/
public function updateWidget(array &$form, FormStateInterface $form_state) {
if ($form_state->getErrors()) {
return $form;
}
$widget_id = $this->getRequest()->query->get('media_library_widget_id');
if (!$widget_id || !is_string($widget_id)) {
throw new BadRequestHttpException('The "media_library_widget_id" query parameter is required and must be a string.');
}
$mids = array_map(function (MediaInterface $media) {
return $media->id();
}, $this->media);
// Pass the selection to the field widget based on the current widget ID.
return (new AjaxResponse())
->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$widget_id\"]", 'val', [implode(',', $mids)]))
->addCommand(new InvokeCommand("[data-media-library-widget-update=\"$widget_id\"]", 'trigger', ['mousedown']))
->addCommand(new CloseDialogCommand());
}
/**
* Processes an upload (managed_file) element.
*
* @param array $element
* The upload element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The processed upload element.
*/
public function processUploadElement(array $element, FormStateInterface $form_state) {
$element['upload_button']['#submit'] = ['::uploadButtonSubmit'];
$element['upload_button']['#ajax'] = [
'callback' => '::updateFormCallback',
'wrapper' => 'media-library-upload-wrapper',
];
return $element;
}
/**
* Validates the upload element.
*
* @param array $element
* The upload element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The processed upload element.
*/
public function validateUploadElement(array $element, FormStateInterface $form_state) {
if ($form_state->getErrors()) {
$element['#value'] = [];
}
$values = $form_state->getValue('upload', []);
if (count($values['fids']) > $element['#cardinality']) {
$form_state->setError($element, $this->t('A maximum of @count files can be uploaded.', [
'@count' => $element['#cardinality'],
]));
$form_state->setValue('upload', []);
$element['#value'] = [];
}
return $element;
}
/**
* Submit handler for the upload button, inside the managed_file element.
*
* @param array $form
* The form render array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function uploadButtonSubmit(array $form, FormStateInterface $form_state) {
$fids = $form_state->getValue('upload', []);
$files = $this->entityTypeManager->getStorage('file')->loadMultiple($fids);
/** @var \Drupal\file\FileInterface $file */
foreach ($files as $file) {
$types = $this->filterTypesThatAcceptFile($file, $this->getTypes());
if (!empty($types)) {
if (count($types) === 1) {
$this->media[] = $this->createMediaEntity($file, reset($types));
}
else {
$this->files[] = $file;
}
}
}
$form_state->setRebuild();
}
/**
* Creates a new, unsaved media entity.
*
* @param \Drupal\file\FileInterface $file
* A file for the media source field.
* @param \Drupal\media\MediaTypeInterface $type
* A media type.
*
* @return \Drupal\media\MediaInterface
* An unsaved media entity.
*
* @throws \Exception
* If a file operation failed when moving the upload.
*/
protected function createMediaEntity(FileInterface $file, MediaTypeInterface $type) {
$media = $this->entityTypeManager->getStorage('media')->create([
'bundle' => $type->id(),
'name' => $file->getFilename(),
]);
$source_field = $type->getSource()->getSourceFieldDefinition($type)->getName();
$location = $this->getUploadLocationForType($media->bundle->entity);
if (!file_prepare_directory($location, FILE_CREATE_DIRECTORY)) {
throw new \Exception("The destination directory '$location' is not writable");
}
$file = file_move($file, $location);
if (!$file) {
throw new \Exception("Unable to move file to '$location'");
}
$media->set($source_field, $file->id());
return $media;
}
/**
* AJAX callback for refreshing the entire form.
*
* @param array $form
* The form render array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The form render array.
*/
public function updateFormCallback(array &$form, FormStateInterface $form_state) {
return $form;
}
/**
* Access callback to check that the user can create file based media.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access() {
return AccessResultAllowed::allowedIf(count($this->getTypes()))->mergeCacheMaxAge(0);
}
/**
* Returns media types which use files that the current user can create.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @return \Drupal\media\MediaTypeInterface[]
* A list of media types that are valid for this form.
*/
protected function getTypes() {
// Cache results if possible.
if (!isset($this->types)) {
$media_type_storage = $this->entityTypeManager->getStorage('media_type');
$allowed_types = _media_library_get_allowed_types() ?: NULL;
$types = $media_type_storage->loadMultiple($allowed_types);
$types = $this->filterTypesWithFileSource($types);
$types = $this->filterTypesWithCreateAccess($types);
$this->types = $types;
}
return $this->types;
}
/**
* Filters media types that accept a given file.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\file\FileInterface $file
* A file entity.
* @param \Drupal\media\MediaTypeInterface[] $types
* An array of available media types.
*
* @return \Drupal\media\MediaTypeInterface[]
* An array of media types that accept the file.
*/
protected function filterTypesThatAcceptFile(FileInterface $file, array $types) {
$types = $this->filterTypesWithFileSource($types);
return array_filter($types, function (MediaTypeInterface $type) use ($file) {
$validators = $this->getUploadValidatorsForType($type);
$errors = file_validate($file, $validators);
return empty($errors);
});
}
/**
* Filters an array of media types that accept file sources.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface[] $types
* An array of media types.
*
* @return \Drupal\media\MediaTypeInterface[]
* An array of media types that accept file sources.
*/
protected function filterTypesWithFileSource(array $types) {
return array_filter($types, function (MediaTypeInterface $type) {
return is_a($type->getSource()->getSourceFieldDefinition($type)->getClass(), FileFieldItemList::class, TRUE);
});
}
/**
* Merges file upload validators for an array of media types.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface[] $types
* An array of media types.
*
* @return array
* An array suitable for passing to file_save_upload() or the file field
* element's '#upload_validators' property.
*/
protected function mergeUploadValidators(array $types) {
$max_size = 0;
$extensions = [];
$types = $this->filterTypesWithFileSource($types);
foreach ($types as $type) {
$validators = $this->getUploadValidatorsForType($type);
if (isset($validators['file_validate_size'])) {
$max_size = max($max_size, $validators['file_validate_size'][0]);
}
if (isset($validators['file_validate_extensions'])) {
$extensions = array_unique(array_merge($extensions, explode(' ', $validators['file_validate_extensions'][0])));
}
}
// If no field defines a max size, default to the system wide setting.
if ($max_size === 0) {
$max_size = file_upload_max_size();
}
return [
'file_validate_extensions' => [implode(' ', $extensions)],
'file_validate_size' => [$max_size],
];
}
/**
* Gets upload validators for a given media type.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface $type
* A media type.
*
* @return array
* An array suitable for passing to file_save_upload() or the file field
* element's '#upload_validators' property.
*/
protected function getUploadValidatorsForType(MediaTypeInterface $type) {
return $this->getFileItemForType($type)->getUploadValidators();
}
/**
* Gets upload destination for a given media type.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface $type
* A media type.
*
* @return string
* An unsanitized file directory URI with tokens replaced.
*/
protected function getUploadLocationForType(MediaTypeInterface $type) {
return $this->getFileItemForType($type)->getUploadLocation();
}
/**
* Creates a file item for a given media type.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface $type
* A media type.
*
* @return \Drupal\file\Plugin\Field\FieldType\FileItem
* The file item.
*/
protected function getFileItemForType(MediaTypeInterface $type) {
$source = $type->getSource();
$source_data_definition = FieldItemDataDefinition::create($source->getSourceFieldDefinition($type));
return new FileItem($source_data_definition);
}
/**
* Filters an array of media types that can be created by the current user.
*
* @todo Move in https://www.drupal.org/node/2987924
*
* @param \Drupal\media\MediaTypeInterface[] $types
* An array of media types.
*
* @return \Drupal\media\MediaTypeInterface[]
* An array of media types that accept file sources.
*/
protected function filterTypesWithCreateAccess(array $types) {
$access_handler = $this->entityTypeManager->getAccessControlHandler('media');
return array_filter($types, function (MediaTypeInterface $type) use ($access_handler) {
return $access_handler->createAccess($type->id());
});
}
}
...@@ -124,31 +124,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ...@@ -124,31 +124,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
], ],
]; ];
// @todo Remove in https://www.drupal.org/project/drupal/issues/2938116
$allowed_bundles = !empty($element['#target_bundles']) ? $element['#target_bundles'] : [];
$add_url = _media_get_add_url($allowed_bundles);
if ($add_url) {
$element['create_help'] = [
'#type' => 'container',
];
$element['create_help']['label'] = [
'#type' => 'html_tag',
'#tag' => 'h4',
'#attributes' => [
'class' => ['label'],
],
'#value' => $this->t('Create new media'),
];
$element['create_help']['description'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
'class' => ['description'],
],
'#value' => $this->t('Create your media on the <a href=":add_page" target="_blank">media add page</a> (opens a new window), then select it in the library.', [':add_page' => $add_url]),
];
}
$element['selection'] = [ $element['selection'] = [
'#type' => 'container', '#type' => 'container',
'#attributes' => [ '#attributes' => [
...@@ -242,6 +217,18 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ...@@ -242,6 +217,18 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
$element['#description'] .= '<br />' . $cardinality_message; $element['#description'] .= '<br />' . $cardinality_message;
} }
$query = [
'media_library_widget_id' => $field_name . $id_suffix,
'media_library_allowed_types' => $element['#target_bundles'],
'media_library_remaining' => $cardinality_unlimited ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : $remaining,
];
$dialog_options = Json::encode([
'dialogClass' => 'media-library-widget-modal',
'height' => '75%',
'width' => '75%',
'title' => $this->t('Media library'),
]);
// Add a button that will load the Media library in a modal using AJAX. // Add a button that will load the Media library in a modal using AJAX.
$element['media_library_open_button'] = [ $element['media_library_open_button'] = [
'#type' => 'link', '#type' => 'link',
...@@ -249,27 +236,36 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ...@@ -249,27 +236,36 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'#name' => $field_name . '-media-library-open-button' . $id_suffix, '#name' => $field_name . '-media-library-open-button' . $id_suffix,
// @todo Make the view configurable in https://www.drupal.org/project/drupal/issues/2971209 // @todo Make the view configurable in https://www.drupal.org/project/drupal/issues/2971209
'#url' => Url::fromRoute('view.media_library.widget', [], [ '#url' => Url::fromRoute('view.media_library.widget', [], [
'query' => [ 'query' => $query,
'media_library_widget_id' => $field_name . $id_suffix,
'media_library_allowed_types' => $element['#target_bundles'],
'media_library_remaining' => $cardinality_unlimited ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : $remaining,
],
]), ]),
'#attributes' => [ '#attributes' => [
'class' => ['button', 'use-ajax', 'media-library-open-button'], 'class' => ['button', 'use-ajax', 'media-library-open-button'],
'data-dialog-type' => 'modal', 'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([ 'data-dialog-options' => $dialog_options,
'dialogClass' => 'media-library-widget-modal',
'height' => '75%',
'width' => '75%',
'title' => $this->t('Media library'),
]),
], ],
// Prevent errors in other widgets from preventing addition. // Prevent errors in other widgets from preventing addition.
'#limit_validation_errors' => $limit_validation_errors, '#limit_validation_errors' => $limit_validation_errors,
'#access' => $cardinality_unlimited || $remaining > 0, '#access' => $cardinality_unlimited || $remaining > 0,
]; ];
$add_url = Url::fromRoute('media_library.upload', [], [
'query' => $query,
]);
$element['media_library_add_button'] = [
'#type' => 'link',
'#title' => $this->t('Add media'),
'#name' => $field_name . '-media-library-add-button' . $id_suffix,
'#url' => $add_url,
'#attributes' => [
'class' => ['button', 'use-ajax', 'media-library-add-button'],
'data-dialog-type' => 'modal',
'data-dialog-options' => $dialog_options,
],
// Prevent errors in other widgets from preventing addition.
'#limit_validation_errors' => $limit_validation_errors,
'#access' => $add_url->access() && ($cardinality_unlimited || $remaining > 0),
];
// This hidden field and button are used to add new items to the widget. // This hidden field and button are used to add new items to the widget.
$element['media_library_selection'] = [ $element['media_library_selection'] = [
'#type' => 'hidden', '#type' => 'hidden',
......
langcode: en
status: true
dependencies:
config:
- field.field.media.type_four.field_media_test_image
- field.field.media.type_four.field_media_extra_image
- image.style.medium
- media.type.type_four
module:
- image
- path
id: media.type_four.default
targetEntityType: media
bundle: type_four
mode: default
content:
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_media_test_image:
weight: 0
settings:
progress_indicator: throbber
preview_image_style: medium
third_party_settings: { }
type: image_image
region: content
field_media_extra_image:
weight: 1
settings:
progress_indicator: throbber
preview_image_style: medium
third_party_settings: { }
type: image_image
region: content
name:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
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: { }
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.type_four.field_media_test_image
- image.style.thumbnail
- media.type.type_four
module:
- image
id: media.type_four.media_library
targetEntityType: media
bundle: type_four
mode: media_library
content:
field_media_test_image:
weight: 2
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
type: image_image
region: content
field_media_extra_image:
weight: 1
settings:
progress_indicator: throbber
preview_image_style: medium
third_party_settings: { }
type: image_image
region: content
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
path: true
status: true
uid: true
langcode: en
status: true
dependencies:
config:
- field.field.media.type_three.field_media_test_image
- image.style.medium
- media.type.type_three
module:
- image
- path
id: media.type_three.default
targetEntityType: media
bundle: type_three
mode: default
content:
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_media_test_image:
weight: 0
settings:
progress_indicator: throbber
preview_image_style: medium
third_party_settings: { }
type: image_image
region: content
name:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
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: { }
langcode: en
status: true
dependencies:
config:
- core.entity_form_mode.media.media_library
- field.field.media.type_three.field_media_test_image
- image.style.thumbnail
- media.type.type_three
module:
- image
id: media.type_three.media_library
targetEntityType: media
bundle: type_three
mode: media_library
content:
field_media_test_image:
weight: 1
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
type: image_image
region: content
name:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
path: true
status: true
uid: true
langcode: en
status: true
dependencies:
config:
- field.field.media.type_three.field_media_test_image
- image.style.thumbnail
- media.type.type_three
module:
- image
- user
id: media.type_three.default
targetEntityType: media
bundle: type_three
mode: default
content:
created:
label: hidden
type: timestamp
weight: 0
region: content
settings:
date_format: medium
custom_date_format: ''
timezone: ''
third_party_settings: { }
field_media_test_image:
weight: 6
label: above
settings:
image_style: ''
image_link: ''
third_party_settings: { }
type: image
region: content
thumbnail:
type: image
weight: 5
label: hidden
settings:
image_style: thumbnail
image_link: ''
region: content
third_party_settings: { }
uid:
label: hidden
type: author
weight: 0
region: content
settings: { }
third_party_settings: { }
hidden:
name: true
langcode: en
status: true
dependencies:
config:
- field.storage.media.field_media_extra_image
- media.type.type_four
module:
- image
id: media.type_three.field_media_extra_image
field_name: field_media_extra_image
entity_type: media
bundle: type_four
label: Extra Image
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
file_extensions: 'jpg'
alt_field: false
alt_field_required: false
title_field: false
title_field_required: false
max_resolution: ''
min_resolution: ''
default_image:
uuid: null
alt: ''
title: ''
width: null
height: null
file_directory: 'type-four-extra-dir'
max_filesize: ''
handler: 'default:file'
handler_settings: { }
field_type: image
langcode: en
status: true
dependencies:
config:
- field.storage.media.field_media_test_image
- media.type.type_four
module:
- image
id: media.type_three.field_media_test_image
field_name: field_media_test_image
entity_type: media
bundle: type_four
label: Image
description: ''
required: true
translatable: true
default_value: { }
default_value_callback: ''
settings:
file_extensions: 'jpg'
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: 'type-four-dir'
max_filesize: ''
handler: 'default:file'
handler_settings: { }
field_type: image
langcode: en
status: true
dependencies:
config:
- field.storage.media.field_media_test_image
- media.type.type_three
module:
- image
id: media.type_three.field_media_test_image
field_name: field_media_test_image
entity_type: media
bundle: type_three
label: Image
description: ''
required: true
translatable: true
default_value: { }
default_value_callback: ''
settings:
file_extensions: 'png gif jpg jpeg'
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: 'type-three-dir'
max_filesize: ''
handler: 'default:file'
handler_settings: { }
field_type: image
...@@ -22,6 +22,8 @@ settings: ...@@ -22,6 +22,8 @@ settings:
target_bundles: target_bundles:
type_one: type_one type_one: type_one
type_two: type_two type_two: type_two
type_three: type_three
type_four: type_four
sort: sort:
field: _none field: _none
auto_create: false auto_create: false
......
langcode: en
status: true
dependencies:
module:
- file
- image
- media
id: media.field_media_extra_image
field_name: field_media_extra_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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment