Issue #3023802 by seanB, phenaproxima, lauriii, Pancho, larowlan, shaal, dww: Show media add form directly on media type tab in media library
parent
ab15b22f43
commit
792a14f5bb
|
@ -57,6 +57,16 @@ class MediaSource extends Plugin {
|
|||
*/
|
||||
public $allowed_field_types = [];
|
||||
|
||||
/**
|
||||
* The classes used to define media source-specific forms.
|
||||
*
|
||||
* An array of form class names, keyed by ID. The ID represents the operation
|
||||
* the form is used for.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $forms = [];
|
||||
|
||||
/**
|
||||
* A filename for the default thumbnail.
|
||||
*
|
||||
|
|
|
@ -83,6 +83,19 @@
|
|||
border-left: 0;
|
||||
}
|
||||
|
||||
.media-library-add-form--without-input {
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.media-library-add-form--without-input .form-item {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
.media-library-add-form .file-upload-help {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
.media-library-views-form__header .form-item {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
@ -276,31 +289,34 @@
|
|||
border-color: #40b6ff;
|
||||
}
|
||||
|
||||
/* Style the wrappers around new media and files */
|
||||
.media-library-upload__media,
|
||||
.media-library-upload__file {
|
||||
/* Style the wrappers around new media and files. */
|
||||
.media-library-add-form__media {
|
||||
display: flex;
|
||||
padding: 20px 0 20px 0;
|
||||
border-bottom: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.media-library-upload__file {
|
||||
align-items: center;
|
||||
/* Do not show the top padding for the first item. */
|
||||
.media-library-add-form__media:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.media-library-upload__file-label {
|
||||
margin-right: 10px;
|
||||
/* Do not show the bottom border and padding for the last item. */
|
||||
.media-library-add-form__media:last-child {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* @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 {
|
||||
.media-library-add-form__source-field .file,
|
||||
.media-library-add-form__source-field .button,
|
||||
.media-library-add-form__source-field .image-preview,
|
||||
.media-library-add-form__source-field .form-type-managed-file > label,
|
||||
.media-library-add-form__source-field .file-size {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-library-upload__media-preview {
|
||||
.media-library-add-form__preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -308,15 +324,11 @@
|
|||
margin-right: 20px;
|
||||
background: #ebebeb;
|
||||
}
|
||||
[dir="rtl"] .media-library-upload__media-preview {
|
||||
[dir="rtl"] .media-library-add-form__preview {
|
||||
margin-right: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.media-library-upload__media-preview img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */
|
||||
.media-library-widget .media-library-item__name a,
|
||||
.media-library-view.view-display-id-widget .media-library-item__name a {
|
||||
|
|
|
@ -15,6 +15,26 @@
|
|||
currentSelection: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Command to update the current media library selection.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The Drupal Ajax object.
|
||||
* @param {object} response
|
||||
* Object holding the server response.
|
||||
* @param {number} [status]
|
||||
* The HTTP status code.
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.updateMediaLibrarySelection = function(
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
Object.values(response.mediaIds).forEach(value => {
|
||||
Drupal.MediaLibrary.currentSelection.push(value);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Warn users when clicking outgoing links from the library or widget.
|
||||
*
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
currentSelection: []
|
||||
};
|
||||
|
||||
Drupal.AjaxCommands.prototype.updateMediaLibrarySelection = function (ajax, response, status) {
|
||||
Object.values(response.mediaIds).forEach(function (value) {
|
||||
Drupal.MediaLibrary.currentSelection.push(value);
|
||||
});
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibraryWidgetWarn = {
|
||||
attach: function attach(context) {
|
||||
$('.js-media-library-item a[href]', context).once('media-library-warn-link').on('click', function (e) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* Contains hook implementations for the media_library module.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
@ -17,11 +16,11 @@ use Drupal\Core\Render\Element;
|
|||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\image\Plugin\Field\FieldType\ImageItem;
|
||||
use Drupal\media\MediaTypeForm;
|
||||
use Drupal\media\MediaTypeInterface;
|
||||
use Drupal\media_library\Form\FileUploadForm;
|
||||
use Drupal\media_library\MediaLibraryState;
|
||||
use Drupal\views\Form\ViewsForm;
|
||||
use Drupal\views\Plugin\views\cache\CachePluginBase;
|
||||
|
@ -41,6 +40,16 @@ function media_library_help($route_name, RouteMatchInterface $route_match) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_media_source_info_alter().
|
||||
*/
|
||||
function media_library_media_source_info_alter(array &$sources) {
|
||||
$sources['audio_file']['forms']['media_library_add'] = FileUploadForm::class;
|
||||
$sources['file']['forms']['media_library_add'] = FileUploadForm::class;
|
||||
$sources['image']['forms']['media_library_add'] = FileUploadForm::class;
|
||||
$sources['video_file']['forms']['media_library_add'] = FileUploadForm::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
|
@ -52,36 +61,6 @@ 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().
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
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'
|
||||
media_library.ui:
|
||||
path: '/media-library'
|
||||
defaults:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
services:
|
||||
media_library.ui_builder:
|
||||
class: Drupal\media_library\MediaLibraryUiBuilder
|
||||
arguments: ['@entity_type.manager', '@request_stack', '@views.executable']
|
||||
arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder']
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\media_library\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\CommandInterface;
|
||||
|
||||
/**
|
||||
* AJAX command for adding media items to the media library selection.
|
||||
*
|
||||
* This command instructs the client to add the given media item IDs to the
|
||||
* current selection of the media library stored in
|
||||
* Drupal.MediaLibrary.currentSelection.
|
||||
*
|
||||
* This command is implemented by
|
||||
* Drupal.AjaxCommands.prototype.updateMediaLibrarySelection() defined in
|
||||
* media_library.ui.js.
|
||||
*
|
||||
* @ingroup ajax
|
||||
*
|
||||
* @internal
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class UpdateSelectionCommand implements CommandInterface {
|
||||
|
||||
/**
|
||||
* An array of media IDs to add to the current selection.
|
||||
*
|
||||
* @var int[]
|
||||
*/
|
||||
protected $mediaIds;
|
||||
|
||||
/**
|
||||
* Constructs an UpdateSelectionCommand object.
|
||||
*
|
||||
* @param int[] $media_ids
|
||||
* An array of media IDs to add to the current selection.
|
||||
*/
|
||||
public function __construct(array $media_ids) {
|
||||
$this->mediaIds = $media_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
return [
|
||||
'command' => 'updateMediaLibrarySelection',
|
||||
'mediaIds' => $this->mediaIds,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,447 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\media_library\Form;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\media\MediaInterface;
|
||||
use Drupal\media\MediaTypeInterface;
|
||||
use Drupal\media_library\Ajax\UpdateSelectionCommand;
|
||||
use Drupal\media_library\MediaLibraryState;
|
||||
use Drupal\media_library\MediaLibraryUiBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a base class for creating media items from within the media library.
|
||||
*
|
||||
* @internal
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
abstract class AddFormBase extends FormBase {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The media library UI builder.
|
||||
*
|
||||
* @var \Drupal\media_library\MediaLibraryUiBuilder
|
||||
*/
|
||||
protected $libraryUiBuilder;
|
||||
|
||||
/**
|
||||
* The type of media items being created by this form.
|
||||
*
|
||||
* @var \Drupal\media\MediaTypeInterface
|
||||
*/
|
||||
protected $mediaType;
|
||||
|
||||
/**
|
||||
* Constructs a AddFormBase object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\media_library\MediaLibraryUiBuilder $library_ui_builder
|
||||
* The media library UI builder.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->libraryUiBuilder = $library_ui_builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('media_library.ui_builder')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'media_library_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media type from the form state.
|
||||
*
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*
|
||||
* @return \Drupal\media\MediaTypeInterface
|
||||
* The media type.
|
||||
*/
|
||||
protected function getMediaType(FormStateInterface $form_state) {
|
||||
if ($this->mediaType) {
|
||||
return $this->mediaType;
|
||||
}
|
||||
|
||||
$state = $form_state->get('media_library_state');
|
||||
|
||||
if (!$state) {
|
||||
throw new \InvalidArgumentException('The media library state is not present in the form state.');
|
||||
}
|
||||
|
||||
$selected_type_id = $form_state->get('media_library_state')->getSelectedTypeId();
|
||||
$this->mediaType = $this->entityTypeManager->getStorage('media_type')->load($selected_type_id);
|
||||
|
||||
if (!$this->mediaType) {
|
||||
throw new \InvalidArgumentException("The '$selected_type_id' media type does not exist.");
|
||||
}
|
||||
|
||||
return $this->mediaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['#prefix'] = '<div id="media-library-add-form-wrapper">';
|
||||
$form['#suffix'] = '</div>';
|
||||
$form['#attached']['library'][] = 'media_library/style';
|
||||
|
||||
// The form is posted via AJAX. When there are messages set during the
|
||||
// validation or submission of the form, the messages need to be shown to
|
||||
// the user.
|
||||
$form['status_messages'] = [
|
||||
'#type' => 'status_messages',
|
||||
];
|
||||
|
||||
$form['#attributes']['class'][] = 'media-library-add-form';
|
||||
$added_media = $form_state->get('media');
|
||||
if (empty($added_media)) {
|
||||
$form['#attributes']['class'][] = 'media-library-add-form--without-input';
|
||||
$form = $this->buildInputElement($form, $form_state);
|
||||
}
|
||||
else {
|
||||
$form['#attributes']['class'][] = 'media-library-add-form--with-input';
|
||||
|
||||
$form['media'] = [
|
||||
'#type' => 'container',
|
||||
];
|
||||
|
||||
foreach ($added_media as $delta => $media) {
|
||||
$form['media'][$delta] = $this->buildEntityFormElement($media, $form, $form_state, $delta);
|
||||
}
|
||||
|
||||
$form['actions'] = $this->buildActions($form, $form_state);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the element for submitting source field value(s).
|
||||
*
|
||||
* The input element needs to have a submit handler to create media items from
|
||||
* the user input and store them in the form state using
|
||||
* ::processInputValues().
|
||||
*
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*
|
||||
* @return array
|
||||
* The complete form, with the element added.
|
||||
*
|
||||
* @see ::processInputValues()
|
||||
*/
|
||||
abstract protected function buildInputElement(array $form, FormStateInterface $form_state);
|
||||
|
||||
/**
|
||||
* Builds the sub-form for setting required fields on a new media item.
|
||||
*
|
||||
* @param \Drupal\media\MediaInterface $media
|
||||
* A new, unsaved media item.
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
* @param int $delta
|
||||
* The delta of the media item.
|
||||
*
|
||||
* @return array
|
||||
* The element containing the required fields sub-form.
|
||||
*/
|
||||
protected function buildEntityFormElement(MediaInterface $media, array $form, FormStateInterface $form_state, $delta) {
|
||||
$element = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'media-library-add-form__media',
|
||||
],
|
||||
],
|
||||
'preview' => [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'media-library-add-form__preview',
|
||||
],
|
||||
],
|
||||
],
|
||||
'fields' => [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'media-library-add-form__fields',
|
||||
],
|
||||
],
|
||||
// The '#parents' are set here because the entity form display needs it
|
||||
// to build the entity form fields.
|
||||
'#parents' => ['media', $delta, 'fields'],
|
||||
],
|
||||
];
|
||||
// @todo Make the image style configurable in
|
||||
// https://www.drupal.org/node/2988223
|
||||
$source = $media->getSource();
|
||||
$plugin_definition = $source->getPluginDefinition();
|
||||
if ($thumbnail_uri = $source->getMetadata($media, $plugin_definition['thumbnail_uri_metadata_attribute'])) {
|
||||
$element['preview']['thumbnail'] = [
|
||||
'#theme' => 'image_style',
|
||||
'#style_name' => 'media_library',
|
||||
'#uri' => $thumbnail_uri,
|
||||
];
|
||||
}
|
||||
|
||||
$form_display = EntityFormDisplay::collectRenderDisplay($media, 'media_library');
|
||||
// When the name is not added to the form as an editable field, output
|
||||
// the name as a fixed element to confirm the right file was uploaded.
|
||||
if (!$form_display->getComponent('name')) {
|
||||
$element['fields']['name'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Name'),
|
||||
'#markup' => $media->getName(),
|
||||
];
|
||||
}
|
||||
$form_display->buildForm($media, $element['fields'], $form_state);
|
||||
|
||||
// We hide the preview of the uploaded file in the image widget with CSS.
|
||||
// @todo Improve hiding file widget elements in
|
||||
// https://www.drupal.org/project/drupal/issues/2987921
|
||||
$source_field_name = $this->getSourceFieldName($media->bundle->entity);
|
||||
if (isset($element['fields'][$source_field_name])) {
|
||||
$element['fields'][$source_field_name]['#attributes']['class'][] = 'media-library-add-form__source-field';
|
||||
}
|
||||
// The revision log field is currently not configurable from the form
|
||||
// display, so hide it by changing the access.
|
||||
// @todo Make the revision_log_message field configurable in
|
||||
// https://www.drupal.org/project/drupal/issues/2696555
|
||||
if (isset($element['fields']['revision_log_message'])) {
|
||||
$element['fields']['revision_log_message']['#access'] = FALSE;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of supported actions for the form.
|
||||
*
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*
|
||||
* @return array
|
||||
* An actions element containing the actions of the form.
|
||||
*/
|
||||
protected function buildActions(array $form, FormStateInterface $form_state) {
|
||||
return [
|
||||
'#type' => 'actions',
|
||||
'submit' => [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#ajax' => [
|
||||
'callback' => '::updateWidget',
|
||||
'wrapper' => 'media-library-add-form-wrapper',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates media items from source field input values.
|
||||
*
|
||||
* @param mixed[] $source_field_values
|
||||
* The values for source fields of the media items.
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*/
|
||||
protected function processInputValues(array $source_field_values, array $form, FormStateInterface $form_state) {
|
||||
$media_type = $this->getMediaType($form_state);
|
||||
$media_storage = $this->entityTypeManager->getStorage('media');
|
||||
$source_field_name = $this->getSourceFieldName($media_type);
|
||||
$media = array_map(function ($source_field_value) use ($media_type, $media_storage, $source_field_name) {
|
||||
return $this->createMediaFromValue($media_type, $media_storage, $source_field_name, $source_field_value);
|
||||
}, $source_field_values);
|
||||
$form_state->set('media', $media)->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, unsaved media item from a source field value.
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $media_type
|
||||
* The media type of the media item.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $media_storage
|
||||
* The media storage.
|
||||
* @param string $source_field_name
|
||||
* The name of the media type's source field.
|
||||
* @param mixed $source_field_value
|
||||
* The value for the source field of the media item.
|
||||
*
|
||||
* @return \Drupal\media\MediaInterface
|
||||
* An unsaved media entity.
|
||||
*/
|
||||
protected function createMediaFromValue(MediaTypeInterface $media_type, EntityStorageInterface $media_storage, $source_field_name, $source_field_value) {
|
||||
return $media_storage->create([
|
||||
'bundle' => $media_type->id(),
|
||||
$source_field_name => $source_field_value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a created media item to be permanently saved.
|
||||
*
|
||||
* @param \Drupal\media\MediaInterface $media
|
||||
* The unsaved media item.
|
||||
*/
|
||||
protected function prepareMediaEntityForSave(MediaInterface $media) {
|
||||
// Intentionally empty by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to update the entire form based on source field input.
|
||||
*
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse|array
|
||||
* The form render array or an AJAX response object.
|
||||
*/
|
||||
public function updateFormCallback(array &$form, FormStateInterface $form_state) {
|
||||
// When the source field input contains errors, replace the existing form to
|
||||
// let the user change the source field input. If the user input is valid,
|
||||
// the entire modal is replaced with the second step of the form to show the
|
||||
// form fields for each media item.
|
||||
if ($form_state::hasAnyErrors()) {
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $form));
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$added_media = $form_state->get('media') ?: [];
|
||||
foreach ($added_media as $delta => $media) {
|
||||
$this->validateMediaEntity($media, $form, $form_state, $delta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a created media item.
|
||||
*
|
||||
* @param \Drupal\media\MediaInterface $media
|
||||
* The media item to validate.
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
* @param int $delta
|
||||
* The delta of the media item.
|
||||
*/
|
||||
protected function validateMediaEntity(MediaInterface $media, array $form, FormStateInterface $form_state, $delta) {
|
||||
$form_display = EntityFormDisplay::collectRenderDisplay($media, 'media_library');
|
||||
$form_display->extractFormValues($media, $form['media'][$delta]['fields'], $form_state);
|
||||
$form_display->validateFormValues($media, $form['media'][$delta]['fields'], $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$added_media = $form_state->get('media') ?: [];
|
||||
foreach ($added_media as $delta => $media) {
|
||||
EntityFormDisplay::collectRenderDisplay($media, 'media_library')
|
||||
->extractFormValues($media, $form['media'][$delta]['fields'], $form_state);
|
||||
$this->prepareMediaEntityForSave($media);
|
||||
$media->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to send the new media item(s) to the calling code.
|
||||
*
|
||||
* @param array $form
|
||||
* The complete form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
*
|
||||
* @return array|\Drupal\Core\Ajax\AjaxResponse
|
||||
* The form array when there are form errors or a AJAX response to select
|
||||
* the created items in the media library.
|
||||
*/
|
||||
public function updateWidget(array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state::hasAnyErrors()) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
$added_media = $form_state->get('media') ?: [];
|
||||
$media_ids = array_map(function (MediaInterface $media) {
|
||||
return $media->id();
|
||||
}, $added_media);
|
||||
|
||||
// Get the render array for the media library. The media library state might
|
||||
// contain the 'media_library_content' when it has been opened from a
|
||||
// vertical tab. We need to remove that to make sure the render array
|
||||
// contains the vertical tabs. Besides that, we also need to force the media
|
||||
// library to create a new instance of the media add form.
|
||||
// @see \Drupal\media_library\MediaLibraryUiBuilder::buildMediaTypeAddForm()
|
||||
$state = MediaLibraryState::fromRequest($this->getRequest());
|
||||
$state->remove('media_library_content');
|
||||
$state->set('_media_library_form_rebuild', TRUE);
|
||||
$library_ui = $this->libraryUiBuilder->buildUi($state);
|
||||
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new UpdateSelectionCommand($media_ids));
|
||||
$response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $library_ui));
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the source field for a media type.
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $media_type
|
||||
* The media type to get the source field name for.
|
||||
*
|
||||
* @return string
|
||||
* The name of the media type's source field.
|
||||
*/
|
||||
protected function getSourceFieldName(MediaTypeInterface $media_type) {
|
||||
return $media_type->getSource()
|
||||
->getSourceFieldDefinition($media_type)
|
||||
->getName();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\media_library\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\ElementInfoManagerInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
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 Drupal\media_library\MediaLibraryUiBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Creates a form to create media entities from uploaded files.
|
||||
*
|
||||
* @internal
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class FileUploadForm extends AddFormBase {
|
||||
|
||||
/**
|
||||
* The element info manager.
|
||||
*
|
||||
* @var \Drupal\Core\Render\ElementInfoManagerInterface
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\ElementInfoManagerInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Constructs a new FileUploadForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\media_library\MediaLibraryUiBuilder $library_ui_builder
|
||||
* The media library UI builder.
|
||||
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
|
||||
* The element info manager.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer) {
|
||||
parent::__construct($entity_type_manager, $library_ui_builder);
|
||||
$this->elementInfo = $element_info;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('media_library.ui_builder'),
|
||||
$container->get('element_info'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getMediaType(FormStateInterface $form_state) {
|
||||
if ($this->mediaType) {
|
||||
return $this->mediaType;
|
||||
}
|
||||
|
||||
$media_type = parent::getMediaType($form_state);
|
||||
// The file upload form only supports media types which use a file field as
|
||||
// a source field.
|
||||
$field_definition = $media_type->getSource()->getSourceFieldDefinition($media_type);
|
||||
if (!is_a($field_definition->getClass(), FileFieldItemList::class, TRUE)) {
|
||||
throw new \InvalidArgumentException('Can only add media types which use a file field as a source field.');
|
||||
}
|
||||
return $media_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildInputElement(array $form, FormStateInterface $form_state) {
|
||||
$form['#attributes']['class'][] = 'media-library-add-form-upload';
|
||||
|
||||
// Create a file item to get the upload validators.
|
||||
$media_type = $this->getMediaType($form_state);
|
||||
$item = $this->createFileItem($media_type);
|
||||
|
||||
/** @var \Drupal\media_library\MediaLibraryState $state */
|
||||
$state = $form_state->get('media_library_state');
|
||||
if (!$state->hasSlotsAvailable()) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
$slots = $state->getAvailableSlots();
|
||||
|
||||
$process = (array) $this->elementInfo->getInfoProperty('managed_file', '#process', []);
|
||||
$form['upload'] = [
|
||||
'#type' => 'managed_file',
|
||||
'#title' => $this->formatPlural($slots, 'Add file', 'Add files'),
|
||||
// @todo Move validation in https://www.drupal.org/node/2988215
|
||||
'#process' => array_merge(['::validateUploadElement'], $process, ['::processUploadElement']),
|
||||
'#upload_validators' => $item->getUploadValidators(),
|
||||
'#multiple' => $slots > 1 || $slots === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
'#cardinality' => $slots,
|
||||
'#remaining_slots' => $slots,
|
||||
];
|
||||
|
||||
$file_upload_help = [
|
||||
'#theme' => 'file_upload_help',
|
||||
'#upload_validators' => $form['upload']['#upload_validators'],
|
||||
'#cardinality' => $slots,
|
||||
];
|
||||
|
||||
// The file upload help needs to be rendered since the description does not
|
||||
// accept render arrays. The FileWidget::formElement() method adds the file
|
||||
// upload help in the same way, so any theming improvements made to file
|
||||
// fields would also be applied to this upload field.
|
||||
// @see \Drupal\file\Plugin\Field\FieldWidget\FileWidget::formElement()
|
||||
$form['upload']['#description'] = $this->renderer->renderPlain($file_upload_help);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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::hasAnyErrors()) {
|
||||
// When an error occurs during uploading files, remove all files so the
|
||||
// user can re-upload the files.
|
||||
$element['#value'] = [];
|
||||
}
|
||||
$values = $form_state->getValue('upload', []);
|
||||
if (count($values['fids']) > $element['#cardinality'] && $element['#cardinality'] !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-wrapper',
|
||||
];
|
||||
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) {
|
||||
$files = $this->entityTypeManager
|
||||
->getStorage('file')
|
||||
->loadMultiple($form_state->getValue('upload', []));
|
||||
$this->processInputValues($files, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createMediaFromValue(MediaTypeInterface $media_type, EntityStorageInterface $media_storage, $source_field_name, $file) {
|
||||
if (!($file instanceof FileInterface)) {
|
||||
throw new \InvalidArgumentException('Cannot create a media item without a file entity.');
|
||||
}
|
||||
|
||||
// Create a file item to get the upload location.
|
||||
$item = $this->createFileItem($media_type);
|
||||
$upload_location = $item->getUploadLocation();
|
||||
if (!file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY)) {
|
||||
throw new \Exception("The destination directory '$upload_location' is not writable");
|
||||
}
|
||||
$file = file_move($file, $upload_location);
|
||||
if (!$file) {
|
||||
throw new \RuntimeException("Unable to move file to '$upload_location'");
|
||||
}
|
||||
|
||||
return parent::createMediaFromValue($media_type, $media_storage, $source_field_name, $file)->setName($file->getFilename());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file field item.
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $media_type
|
||||
* The media type of the media item.
|
||||
*
|
||||
* @return \Drupal\file\Plugin\Field\FieldType\FileItem
|
||||
* A created file item.
|
||||
*/
|
||||
protected function createFileItem(MediaTypeInterface $media_type) {
|
||||
$field_definition = $media_type->getSource()->getSourceFieldDefinition($media_type);
|
||||
$data_definition = FieldItemDataDefinition::create($field_definition);
|
||||
return new FileItem($data_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareMediaEntityForSave(MediaInterface $media) {
|
||||
/** @var \Drupal\file\FileInterface $file */
|
||||
$file = $media->get($this->getSourceFieldName($media->bundle->entity))->entity;
|
||||
$file->setPermanent();
|
||||
$file->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,639 +0,0 @@
|
|||
<?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\FieldStorageDefinitionInterface;
|
||||
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 Drupal\media_library\MediaLibraryState;
|
||||
use Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget;
|
||||
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 = (int) $this->getRequest()->query->get('media_library_remaining');
|
||||
if ($remaining || $remaining === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
|
||||
$form['upload']['#multiple'] = $remaining > 1 || $remaining === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED;
|
||||
$form['upload']['#cardinality'] = $form['upload_help']['#cardinality'] = $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,
|
||||
];
|
||||
}
|
||||
|
||||
$form_display = EntityFormDisplay::collectRenderDisplay($media, 'media_library');
|
||||
// When the name is not added to the form as a editable field, output
|
||||
// the name as a fixed element to confirm the right file was uploaded.
|
||||
if (!$form_display->getComponent('name')) {
|
||||
$element['fields']['name'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Name'),
|
||||
'#markup' => $media->getName(),
|
||||
];
|
||||
}
|
||||
$form_display->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.
|
||||
*/
|
||||
public function updateWidget(array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getErrors()) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
$mids = array_map(function (MediaInterface $media) {
|
||||
return $media->id();
|
||||
}, $this->media);
|
||||
|
||||
// Pass the selection to the field widget based on the current widget ID.
|
||||
$opener_id = MediaLibraryState::fromRequest($this->getRequest())->getOpenerId();
|
||||
if ($field_id = MediaLibraryWidget::getOpenerFieldId($opener_id)) {
|
||||
return (new AjaxResponse())
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$field_id\"]", 'val', [implode(',', $mids)]))
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-update=\"$field_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'] && $element['#cardinality'] !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
|
||||
$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.
|
||||
*
|
||||
* @param array $allowed_types
|
||||
* (optional) The contextually allowed types.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*
|
||||
* @todo Remove $allowed_types param in https://www.drupal.org/node/2956747
|
||||
*/
|
||||
public function access(array $allowed_types = NULL) {
|
||||
return AccessResultAllowed::allowedIf(count($this->getTypes($allowed_types)))->mergeCacheMaxAge(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns media types which use files that the current user can create.
|
||||
*
|
||||
* @param array $allowed_types
|
||||
* (optional) The contextually allowed types.
|
||||
*
|
||||
* @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(array $allowed_types = NULL) {
|
||||
// Cache results if possible.
|
||||
if (!isset($this->types)) {
|
||||
$media_type_storage = $this->entityTypeManager->getStorage('media_type');
|
||||
if (!$allowed_types) {
|
||||
$types = $media_type_storage->loadMultiple(MediaLibraryState::fromRequest($this->getRequest())->getAllowedTypeIds());
|
||||
}
|
||||
else {
|
||||
$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());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -29,8 +29,9 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
* items can be selected.
|
||||
*
|
||||
* @internal
|
||||
* This class is an internal part of the media library and should not be
|
||||
* instantiated or used by external code.
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class MediaLibraryState extends ParameterBag {
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\media_library;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Form\FormBuilderInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
@ -15,13 +17,21 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
* Service which builds the media library.
|
||||
*
|
||||
* @internal
|
||||
* This class is an internal part of the media library and should not be
|
||||
* instantiated or used by external code.
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class MediaLibraryUiBuilder {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The form builder.
|
||||
*
|
||||
* @var \Drupal\Core\Form\FormBuilderInterface
|
||||
*/
|
||||
protected $formBuilder;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
|
@ -52,11 +62,14 @@ class MediaLibraryUiBuilder {
|
|||
* The request stack.
|
||||
* @param \Drupal\views\ViewExecutableFactory $views_executable_factory
|
||||
* The views executable factory.
|
||||
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
|
||||
* The currently active request object.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory) {
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->request = $request_stack->getCurrentRequest();
|
||||
$this->viewsExecutableFactory = $views_executable_factory;
|
||||
$this->formBuilder = $form_builder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,11 +90,17 @@ class MediaLibraryUiBuilder {
|
|||
/**
|
||||
* Build the media library UI.
|
||||
*
|
||||
* @param \Drupal\media_library\MediaLibraryState $state
|
||||
* (optional) The current state of the media library, derived from the
|
||||
* current request.
|
||||
*
|
||||
* @return array
|
||||
* The render array for the media library.
|
||||
*/
|
||||
public function buildUi() {
|
||||
$state = MediaLibraryState::fromRequest($this->request);
|
||||
public function buildUi(MediaLibraryState $state = NULL) {
|
||||
if (!$state) {
|
||||
$state = MediaLibraryState::fromRequest($this->request);
|
||||
}
|
||||
// When navigating to a media type through the vertical tabs, we only want
|
||||
// to load the changed library content. This is not only more efficient, but
|
||||
// also provides a more accessible user experience for screen readers.
|
||||
|
@ -123,6 +142,7 @@ class MediaLibraryUiBuilder {
|
|||
'class' => ['media-library-content'],
|
||||
'tabindex' => -1,
|
||||
],
|
||||
'form' => $this->buildMediaTypeAddForm($state),
|
||||
'view' => $this->buildMediaLibraryView($state),
|
||||
];
|
||||
}
|
||||
|
@ -178,13 +198,15 @@ class MediaLibraryUiBuilder {
|
|||
],
|
||||
];
|
||||
|
||||
// Get the state parameters but remove the wrapper format. Also add the
|
||||
// 'media_library_content' argument to fetch only the updated content for
|
||||
// the tab.
|
||||
// @see self::buildUi()
|
||||
$state->remove(MainContentViewSubscriber::WRAPPER_FORMAT);
|
||||
$state->add(['media_library_content' => 1]);
|
||||
// Get the state parameters but remove the wrapper format, AJAX form and
|
||||
// form rebuild parameters. These are internal parameters that should never
|
||||
// be part of the vertical tab links.
|
||||
$query = $state->all();
|
||||
unset($query[MainContentViewSubscriber::WRAPPER_FORMAT], $query[FormBuilderInterface::AJAX_FORM_REQUEST], $query['_media_library_form_rebuild']);
|
||||
// Add the 'media_library_content' parameter so the response will contain
|
||||
// only the updated content for the tab.
|
||||
// @see self::buildUi()
|
||||
$query['media_library_content'] = 1;
|
||||
|
||||
$allowed_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple($allowed_type_ids);
|
||||
|
||||
|
@ -216,6 +238,42 @@ class MediaLibraryUiBuilder {
|
|||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the add form for the selected media type.
|
||||
*
|
||||
* @param \Drupal\media_library\MediaLibraryState $state
|
||||
* The current state of the media library, derived from the current request.
|
||||
*
|
||||
* @return array
|
||||
* The render array for the media type add form.
|
||||
*/
|
||||
protected function buildMediaTypeAddForm(MediaLibraryState $state) {
|
||||
$selected_type_id = $state->getSelectedTypeId();
|
||||
|
||||
if (!$this->entityTypeManager->getAccessControlHandler('media')->createAccess($selected_type_id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$selected_type = $this->entityTypeManager->getStorage('media_type')->load($selected_type_id);
|
||||
$plugin_definition = $selected_type->getSource()->getPluginDefinition();
|
||||
|
||||
if (empty($plugin_definition['forms']['media_library_add'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// After the form to add new media is submitted, we need to rebuild the
|
||||
// media library with a new instance of the media add form. The form API
|
||||
// allows us to do that by forcing empty user input.
|
||||
// @see \Drupal\Core\Form\FormBuilder::doBuildForm()
|
||||
$form_state = new FormState();
|
||||
if ($state->get('_media_library_form_rebuild')) {
|
||||
$form_state->setUserInput([]);
|
||||
$state->remove('_media_library_form_rebuild');
|
||||
}
|
||||
$form_state->set('media_library_state', $state);
|
||||
return $this->formBuilder->buildForm($plugin_definition['forms']['media_library_add'], $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media library view.
|
||||
*
|
||||
|
|
|
@ -33,6 +33,9 @@ use Symfony\Component\Validator\ConstraintViolationInterface;
|
|||
* )
|
||||
*
|
||||
* @internal
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class MediaLibraryWidget extends WidgetBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ use Drupal\views\ResultRow;
|
|||
* @ViewsField("media_library_select_form")
|
||||
*
|
||||
* @internal
|
||||
* Media Library is an experimental module and its internal code may be
|
||||
* subject to change in minor releases. External code should not instantiate
|
||||
* or extend this class.
|
||||
*/
|
||||
class MediaLibrarySelectForm extends FieldPluginBase {
|
||||
|
||||
|
|
|
@ -150,8 +150,8 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$role->save();
|
||||
|
||||
// Create a working state.
|
||||
$allowed_types = ['type_one', 'type_two'];
|
||||
$state = MediaLibraryState::create('test', $allowed_types, 'type_two', 2);
|
||||
$allowed_types = ['type_one', 'type_two', 'type_three', 'type_four'];
|
||||
$state = MediaLibraryState::create('test', $allowed_types, 'type_three', 2);
|
||||
$url_options = ['query' => $state->all()];
|
||||
|
||||
// Verify that unprivileged users can't access the widget view.
|
||||
|
@ -169,6 +169,18 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->elementExists('css', '.view-media-library');
|
||||
$this->drupalGet('media-library', $url_options);
|
||||
$assert_session->elementExists('css', '.view-media-library');
|
||||
// Assert the user does not have access to the media add form if the user
|
||||
// does not have the 'create media' permission.
|
||||
$assert_session->fieldNotExists('files[upload][]');
|
||||
|
||||
// Assert users with the 'create media' permission can access the media add
|
||||
// form.
|
||||
$this->grantPermissions($role, [
|
||||
'create media',
|
||||
]);
|
||||
$this->drupalGet('media-library', $url_options);
|
||||
$assert_session->elementExists('css', '.view-media-library');
|
||||
$assert_session->fieldExists('Add files');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,7 +270,7 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
$assert_session->pageTextNotContains('Turtle');
|
||||
$assert_session->elementExists('named', ['link', 'Type Three'])->click();
|
||||
$page->clickLink('Type Three');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->elementExists('named', ['link', 'Type Three (active tab)']);
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
|
@ -316,9 +328,9 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$this->assertFalse($checkboxes[3]->hasAttribute('disabled'));
|
||||
// The selection should be persisted when navigating to other media types in
|
||||
// the modal.
|
||||
$assert_session->elementExists('named', ['link', 'Type Three'])->click();
|
||||
$page->clickLink('Type Three');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->elementExists('named', ['link', 'Type One'])->click();
|
||||
$page->clickLink('Type One');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
|
||||
$selected_checkboxes = [];
|
||||
|
@ -331,7 +343,7 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->hiddenFieldValueEquals('media-library-modal-selection', implode(',', $selected_checkboxes));
|
||||
$assert_session->elementTextContains('css', '.media-library-selected-count', '1 of 2 items selected');
|
||||
// Add to selection from another type.
|
||||
$assert_session->elementExists('named', ['link', 'Type Two'])->click();
|
||||
$page->clickLink('Type Two');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
|
||||
$checkboxes[0]->click();
|
||||
|
@ -345,7 +357,7 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$this->assertTrue($checkboxes[2]->hasAttribute('disabled'));
|
||||
$this->assertTrue($checkboxes[3]->hasAttribute('disabled'));
|
||||
// Assert the checkboxes are also disabled on other pages.
|
||||
$assert_session->elementExists('named', ['link', 'Type One'])->click();
|
||||
$page->clickLink('Type One');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertTrue($checkboxes[0]->hasAttribute('disabled'));
|
||||
$this->assertFalse($checkboxes[1]->hasAttribute('disabled'));
|
||||
|
@ -473,6 +485,7 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
*/
|
||||
public function testWidgetAnonymous() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
|
@ -492,9 +505,7 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Select the first media item (should be Dog).
|
||||
$checkbox_selector = '.media-library-view .js-click-to-select-checkbox input';
|
||||
$checkboxes = $this->getSession()->getPage()->findAll('css', $checkbox_selector);
|
||||
$checkboxes[0]->click();
|
||||
$page->find('css', '.media-library-view .js-click-to-select-checkbox input')->click();
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
|
@ -533,6 +544,44 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$this->fail('Expected test files not present.');
|
||||
}
|
||||
|
||||
// Create a user that can only add media of type four.
|
||||
$user = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access content',
|
||||
'create basic_page content',
|
||||
'create type_four media',
|
||||
'view media',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit a node create page and open the media library.
|
||||
$this->drupalGet('node/add/basic_page');
|
||||
$assert_session->elementExists('css', '.media-library-open-button[href*="field_twin_media"]')->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
|
||||
// Assert the upload form is visible for type_four.
|
||||
$page->clickLink('Type Four');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldExists('Add files');
|
||||
$assert_session->pageTextContains('Maximum 2 files.');
|
||||
|
||||
// Assert the upload form is not visible for type_three.
|
||||
$page->clickLink('Type Three');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldNotExists('files[upload][]');
|
||||
$assert_session->pageTextNotContains('Maximum 2 files.');
|
||||
|
||||
// Create a user that can create media for all media types.
|
||||
$user = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access content',
|
||||
'create basic_page content',
|
||||
'create media',
|
||||
'view media',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit a node create page.
|
||||
$this->drupalGet('node/add/basic_page');
|
||||
|
||||
|
@ -544,11 +593,19 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->elementExists('css', '.media-library-open-button[href*="field_twin_media"]')->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->elementExists('css', '#drupal-modal')->clickLink('Add media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$page->attachFileToField('Upload', $this->container->get('file_system')->realpath($png_image->uri));
|
||||
// Assert the default tab for media type one does not have an upload form.
|
||||
$assert_session->fieldNotExists('files[upload][]');
|
||||
|
||||
// Assert we can upload a file to media type three.
|
||||
$page->clickLink('Type Three');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->elementExists('css', '.media-library-add-form--without-input');
|
||||
$assert_session->elementNotExists('css', '.media-library-add-form--with-input');
|
||||
$page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->elementExists('css', '.media-library-add-form--with-input');
|
||||
$assert_session->elementNotExists('css', '.media-library-add-form--without-input');
|
||||
|
||||
// Files are temporary until the form is saved.
|
||||
$files = $file_storage->loadMultiple();
|
||||
|
@ -556,6 +613,11 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$this->assertSame('public://type-three-dir', $file_system->dirname($file->getFileUri()));
|
||||
$this->assertTrue($file->isTemporary());
|
||||
|
||||
// Assert the revision_log_message field is not shown.
|
||||
$upload_form = $assert_session->elementExists('css', '.media-library-add-form');
|
||||
$assert_session->fieldNotExists('Revision log message', $upload_form);
|
||||
|
||||
// Assert the name field contains the filename and the alt text is required.
|
||||
$this->assertSame($assert_session->fieldExists('Name')->getValue(), $png_image->filename);
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
@ -569,7 +631,23 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$file = array_pop($files);
|
||||
$this->assertFalse($file->isTemporary());
|
||||
|
||||
// Ensure the media item was added.
|
||||
// Load the created media item.
|
||||
$media_storage = $this->container->get('entity_type.manager')->getStorage('media');
|
||||
$media_items = $media_storage->loadMultiple();
|
||||
$added_media = array_pop($media_items);
|
||||
|
||||
// Ensure the media item was saved to the library and automatically
|
||||
// selected. The added media items should be in the first position of the
|
||||
// add form.
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->pageTextContains($png_image->filename);
|
||||
$assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
|
||||
$assert_session->checkboxChecked('media_library_select_form[0]');
|
||||
$assert_session->pageTextContains('1 of 2 items selected');
|
||||
|
||||
// Ensure the created item is added in the widget.
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains($png_image->filename);
|
||||
|
||||
|
@ -577,52 +655,77 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]')->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->elementExists('css', '#drupal-modal')->clickLink('Add media');
|
||||
|
||||
// Navigate to the media type three tab first.
|
||||
$page->clickLink('Type Three');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Select a media item.
|
||||
$page->find('css', '.media-library-view .js-click-to-select-checkbox input')->click();
|
||||
$assert_session->pageTextContains('1 item selected');
|
||||
|
||||
// Multiple uploads should be allowed.
|
||||
// @todo Add test when https://github.com/minkphp/Mink/issues/358 is closed
|
||||
$this->assertTrue($assert_session->fieldExists('Upload')->hasAttribute('multiple'));
|
||||
$this->assertTrue($assert_session->fieldExists('Add files')->hasAttribute('multiple'));
|
||||
|
||||
$page->attachFileToField('Upload', $this->container->get('file_system')->realpath($png_image->uri));
|
||||
$page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->fillField('Name', 'Unlimited Cardinality Image');
|
||||
$page->fillField('Alternative text', $this->randomString());
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Ensure the media item was added.
|
||||
// Load the created media item.
|
||||
$media_storage = $this->container->get('entity_type.manager')->getStorage('media');
|
||||
$media_items = $media_storage->loadMultiple();
|
||||
$added_media = array_pop($media_items);
|
||||
|
||||
// Ensure the media item was saved to the library and automatically
|
||||
// selected. The added media items should be in the first position of the
|
||||
// add form.
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->pageTextContains('Unlimited Cardinality Image');
|
||||
$assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
|
||||
$assert_session->checkboxChecked('media_library_select_form[0]');
|
||||
|
||||
// Assert the item that was selected before uploading the file is still
|
||||
// selected.
|
||||
$assert_session->pageTextContains('2 items selected');
|
||||
$checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
|
||||
$selected_checkboxes = [];
|
||||
foreach ($checkboxes as $checkbox) {
|
||||
if ($checkbox->isChecked()) {
|
||||
$selected_checkboxes[] = $checkbox->getValue();
|
||||
}
|
||||
}
|
||||
$this->assertCount(2, $selected_checkboxes);
|
||||
|
||||
// Ensure the created item is added in the widget.
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains('Unlimited Cardinality Image');
|
||||
|
||||
// Open the browser again to test type resolution.
|
||||
// Verify we can only upload the files allowed by the media type.
|
||||
$assert_session->elementExists('css', '.media-library-open-button[href*="field_twin_media"]')->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->elementExists('css', '#drupal-modal')->clickLink('Add media');
|
||||
$page->clickLink('Type Four');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$page->attachFileToField('Upload', $file_system->realpath($jpg_image->uri));
|
||||
// Assert we can now only upload one more media item.
|
||||
$this->assertFalse($assert_session->fieldExists('Add file')->hasAttribute('multiple'));
|
||||
$assert_session->pageTextContains('One file only.');
|
||||
|
||||
// Assert media type four should only allow jpg files by trying a png file
|
||||
// first.
|
||||
$page->attachFileToField('Add file', $file_system->realpath($png_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Only files with the following extensions are allowed');
|
||||
|
||||
$assert_session->pageTextContains('Select a media type for ' . $jpg_image->filename);
|
||||
|
||||
// Before the type is determined, the file lives in the default upload
|
||||
// location (temporary://).
|
||||
$files = $file_storage->loadMultiple();
|
||||
$file = array_pop($files);
|
||||
$this->assertSame('temporary', $file_system->uriScheme($file->getFileUri()));
|
||||
|
||||
// Both the type_three and type_four media types accept jpg images.
|
||||
$assert_session->buttonExists('Type Three');
|
||||
$assert_session->buttonExists('Type Four')->click();
|
||||
// Assert that jpg files are accepted by type four.
|
||||
$page->attachFileToField('Add file', $file_system->realpath($jpg_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// The file should have been moved when the type was selected.
|
||||
$files = $file_storage->loadMultiple();
|
||||
$file = array_pop($files);
|
||||
$this->assertSame('public://type-four-dir', $file_system->dirname($file->getFileUri()));
|
||||
$this->assertSame($assert_session->fieldExists('Name')->getValue(), $jpg_image->filename);
|
||||
$page->fillField('Alternative text', $this->randomString());
|
||||
|
||||
// The type_four media type has another optional image field.
|
||||
|
@ -637,6 +740,14 @@ class MediaLibraryTest extends WebDriverTestBase {
|
|||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Ensure the media item was saved to the library and automatically
|
||||
// selected.
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->pageTextContains($jpg_image->filename);
|
||||
|
||||
// Ensure the created item is added in the widget.
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains($jpg_image->filename);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\media_library\Kernel;
|
||||
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\media_library\Form\FileUploadForm;
|
||||
use Drupal\media_library\MediaLibraryState;
|
||||
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
|
||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the media library add form.
|
||||
*
|
||||
* @group media_library
|
||||
*/
|
||||
class MediaLibraryAddFormTest extends KernelTestBase {
|
||||
|
||||
use MediaTypeCreationTrait;
|
||||
use UserCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'media',
|
||||
'media_library',
|
||||
'file',
|
||||
'field',
|
||||
'image',
|
||||
'system',
|
||||
'views',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', 'file_usage');
|
||||
$this->installSchema('system', ['sequences', 'key_value_expire']);
|
||||
$this->installEntitySchema('media');
|
||||
$this->installConfig([
|
||||
'field',
|
||||
'system',
|
||||
'file',
|
||||
'image',
|
||||
'media',
|
||||
'media_library',
|
||||
]);
|
||||
|
||||
// Create an account with special UID 1.
|
||||
$this->createUser([]);
|
||||
|
||||
$this->createMediaType('image', ['id' => 'image']);
|
||||
$this->createMediaType('oembed:video', ['id' => 'remote_video']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the media library add form.
|
||||
*/
|
||||
public function testMediaTypeAddForm() {
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
$image = $entity_type_manager->getStorage('media_type')->load('image');
|
||||
$remote_video = $entity_type_manager->getStorage('media_type')->load('remote_video');
|
||||
$image_source_definition = $image->getSource()->getPluginDefinition();
|
||||
$remote_video_source_definition = $remote_video->getSource()->getPluginDefinition();
|
||||
|
||||
// Assert the form class is added to the media source.
|
||||
$this->assertSame(FileUploadForm::class, $image_source_definition['forms']['media_library_add']);
|
||||
$this->assertArrayNotHasKey('media_library_add', $remote_video_source_definition['forms']);
|
||||
|
||||
// Assert the media library UI does not contains the add form when the user
|
||||
// does not have access.
|
||||
$state = MediaLibraryState::create('test', ['image', 'remote_video'], 'image', -1);
|
||||
$library_ui = \Drupal::service('media_library.ui_builder')->buildUi($state);
|
||||
$this->assertEmpty($library_ui['content']['form']);
|
||||
|
||||
// Create a user that has access to the media add form.
|
||||
$this->setCurrentUser($this->createUser([
|
||||
'create image media',
|
||||
]));
|
||||
$library_ui = \Drupal::service('media_library.ui_builder')->buildUi($state);
|
||||
$this->assertSame('managed_file', $library_ui['content']['form']['upload']['#type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of the library state in the media library add form.
|
||||
*/
|
||||
public function testFormStateValidation() {
|
||||
$form_state = new FormState();
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'The media library state is not present in the form state.');
|
||||
\Drupal::formBuilder()->buildForm(FileUploadForm::class, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of the selected type in the media library add form.
|
||||
*/
|
||||
public function testSelectedTypeValidation() {
|
||||
$state = MediaLibraryState::create('test', ['image', 'remote_video', 'header_image'], 'header_image', -1);
|
||||
$form_state = new FormState();
|
||||
$form_state->set('media_library_state', $state);
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The 'header_image' media type does not exist.");
|
||||
\Drupal::formBuilder()->buildForm(FileUploadForm::class, $form_state);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue