Commit 92390915 authored by xjm's avatar xjm

Issue #3082690 by phenaproxima, bnjmnm, oknate, JeroenT, effulgentsia, xjm,...

Issue #3082690 by phenaproxima, bnjmnm, oknate, JeroenT, effulgentsia, xjm, lauriii, webchick, seanB, andrewmacpherson, Wim Leers, DyanneNova, Gábor Hojtsy, cboyden, peterx, rainbreaw, jan.stoeckler, shaal, annagaz, FeyP, chr.fritsch, marcoscano, samuel.mortenson, Berdir, webflo: Mark Media Library as a stable core module

(cherry picked from commit 946835f4)
parent 51adbc6a
...@@ -283,6 +283,10 @@ Media ...@@ -283,6 +283,10 @@ Media
- Christian Fritsch 'chr.fritsch' https://www.drupal.org/u/chr.fritsch - Christian Fritsch 'chr.fritsch' https://www.drupal.org/u/chr.fritsch
- Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima - Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
Media Library
- Sean Blommaert 'seanB' https://www.drupal.org/u/seanb
- Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
Menu Menu
- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner - Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin - Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
......
...@@ -73,7 +73,7 @@ display: ...@@ -73,7 +73,7 @@ display:
type: default type: default
options: options:
grouping: { } grouping: { }
row_class: 'media-library-item media-library-item--grid js-media-library-item js-click-to-select' row_class: ''
default_row_class: true default_row_class: true
row: row:
type: fields type: fields
...@@ -120,7 +120,7 @@ display: ...@@ -120,7 +120,7 @@ display:
preserve_tags: '' preserve_tags: ''
html: false html: false
element_type: '' element_type: ''
element_class: js-click-to-select-checkbox media-library-item__click-to-select-checkbox element_class: ''
element_label_type: '' element_label_type: ''
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
...@@ -173,7 +173,7 @@ display: ...@@ -173,7 +173,7 @@ display:
preserve_tags: '' preserve_tags: ''
html: false html: false
element_type: '' element_type: ''
element_class: media-library-item__content element_class: ''
element_label_type: '' element_label_type: ''
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
...@@ -468,7 +468,7 @@ display: ...@@ -468,7 +468,7 @@ display:
relationships: { } relationships: { }
display_extenders: { } display_extenders: { }
use_ajax: true use_ajax: true
css_class: 'media-library-view js-media-library-view' css_class: ''
cache_metadata: cache_metadata:
max-age: 0 max-age: 0
contexts: contexts:
...@@ -525,7 +525,7 @@ display: ...@@ -525,7 +525,7 @@ display:
preserve_tags: '' preserve_tags: ''
html: false html: false
element_type: '' element_type: ''
element_class: js-click-to-select-checkbox media-library-item__click-to-select-checkbox element_class: ''
element_label_type: '' element_label_type: ''
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
...@@ -627,7 +627,7 @@ display: ...@@ -627,7 +627,7 @@ display:
trim_whitespace: false trim_whitespace: false
alt: 'Edit {{ name }}' alt: 'Edit {{ name }}'
rel: '' rel: ''
link_class: media-library-item__edit link_class: ''
prefix: '' prefix: ''
suffix: '' suffix: ''
target: '' target: ''
...@@ -680,7 +680,7 @@ display: ...@@ -680,7 +680,7 @@ display:
trim_whitespace: false trim_whitespace: false
alt: 'Delete {{ name }}' alt: 'Delete {{ name }}'
rel: '' rel: ''
link_class: media-library-item__remove link_class: ''
prefix: '' prefix: ''
suffix: '' suffix: ''
target: '' target: ''
...@@ -749,7 +749,7 @@ display: ...@@ -749,7 +749,7 @@ display:
preserve_tags: '' preserve_tags: ''
html: false html: false
element_type: '' element_type: ''
element_class: media-library-item__content element_class: ''
element_label_type: '' element_label_type: ''
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
...@@ -786,10 +786,10 @@ display: ...@@ -786,10 +786,10 @@ display:
display_extenders: { } display_extenders: { }
path: admin/content/media-widget path: admin/content/media-widget
fields: fields:
rendered_entity: media_library_select_form:
id: rendered_entity id: media_library_select_form
table: media table: media
field: rendered_entity field: media_library_select_form
relationship: none relationship: none
group_type: group group_type: group
admin_label: '' admin_label: ''
...@@ -823,7 +823,7 @@ display: ...@@ -823,7 +823,7 @@ display:
preserve_tags: '' preserve_tags: ''
html: false html: false
element_type: '' element_type: ''
element_class: media-library-item__content element_class: ''
element_label_type: '' element_label_type: ''
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
...@@ -834,13 +834,12 @@ display: ...@@ -834,13 +834,12 @@ display:
hide_empty: false hide_empty: false
empty_zero: false empty_zero: false
hide_alter_empty: true hide_alter_empty: true
view_mode: media_library
entity_type: media entity_type: media
plugin_id: rendered_entity plugin_id: media_library_select_form
media_library_select_form: rendered_entity:
id: media_library_select_form id: rendered_entity
table: media table: media
field: media_library_select_form field: rendered_entity
relationship: none relationship: none
group_type: group group_type: group
admin_label: '' admin_label: ''
...@@ -879,14 +878,15 @@ display: ...@@ -879,14 +878,15 @@ display:
element_label_class: '' element_label_class: ''
element_label_colon: false element_label_colon: false
element_wrapper_type: '' element_wrapper_type: ''
element_wrapper_class: js-click-to-select-checkbox media-library-item__click-to-select-checkbox element_wrapper_class: ''
element_default_classes: true element_default_classes: true
empty: '' empty: ''
hide_empty: false hide_empty: false
empty_zero: false empty_zero: false
hide_alter_empty: true hide_alter_empty: true
view_mode: media_library
entity_type: media entity_type: media
plugin_id: media_library_select_form plugin_id: rendered_entity
defaults: defaults:
fields: false fields: false
access: false access: false
...@@ -1086,7 +1086,7 @@ display: ...@@ -1086,7 +1086,7 @@ display:
label: 'Table' label: 'Table'
plugin_id: display_link plugin_id: display_link
empty: true empty: true
css_class: 'media-library-view js-media-library-view media-library-view--widget' css_class: ''
rendering_language: '***LANGUAGE_language_interface***' rendering_language: '***LANGUAGE_language_interface***'
cache_metadata: cache_metadata:
max-age: -1 max-age: -1
...@@ -1131,7 +1131,7 @@ display: ...@@ -1131,7 +1131,7 @@ display:
relationship: none relationship: none
entity_type: media entity_type: media
plugin_id: media_library_select_form plugin_id: media_library_select_form
element_wrapper_class: js-click-to-select-checkbox media-library-item__click-to-select-checkbox element_wrapper_class: ''
element_class: '' element_class: ''
thumbnail__target_id: thumbnail__target_id:
id: thumbnail__target_id id: thumbnail__target_id
...@@ -1374,7 +1374,7 @@ display: ...@@ -1374,7 +1374,7 @@ display:
label: 'Table' label: 'Table'
plugin_id: display_link plugin_id: display_link
empty: true empty: true
css_class: 'media-library-view js-media-library-view media-library-view--widget' css_class: ''
rendering_language: '***LANGUAGE_language_interface***' rendering_language: '***LANGUAGE_language_interface***'
cache_metadata: cache_metadata:
max-age: -1 max-age: -1
......
/**
* @file media_library.module.css
*/
/**
* By default, the dialog is too narrow to be usable.
* @see Drupal.ckeditor.openDialog()
*/
.ui-dialog--narrow.media-library-widget-modal {
max-width: 75%;
}
.media-library-wrapper {
display: flex;
}
.media-library-menu {
margin: 0;
padding: 0;
}
/* @todo Use a class instead of the li element.
https://www.drupal.org/project/drupal/issues/3029227 */
.media-library-menu li {
padding: 0;
list-style: none;
}
.media-library-views-form > .form-actions {
flex-basis: 100%;
}
.media-library-views-form,
.media-library-selection,
.media-library-add-form__selected-media .details-wrapper,
.media-library-views-form__bulk_form,
.media-library-view .form--inline {
display: flex;
flex-wrap: wrap;
}
.media-library-views-form__header {
flex-basis: 100%;
}
.media-library-item {
position: relative;
}
.media-library-item__click-to-select-trigger {
overflow: hidden;
height: 100%;
cursor: pointer;
}
.media-library-view .form-actions {
align-self: flex-end;
}
.media-library-item__click-to-select-checkbox {
position: absolute;
z-index: 1;
top: 16px;
left: 16px; /* LTR */
display: block;
}
[dir="rtl"] .media-library-item__click-to-select-checkbox {
right: 16px;
left: auto;
}
.media-library-item__status {
position: absolute;
top: 40px;
left: 5px; /* LTR */
pointer-events: none;
}
[dir="rtl"] .media-library-item__status {
right: 5px;
left: auto;
}
.media-library-select-all {
flex-basis: 100%;
width: 100%;
}
.media-library-view--widget .media-library-select-all {
display: none;
}
.media-library-item--disabled {
pointer-events: none;
}
.media-library-selection .media-library-item__preview {
cursor: move;
}
.media-library-widget-modal .ui-dialog-buttonpane {
display: flex;
align-items: center;
}
.media-library-widget-modal .ui-dialog-buttonpane .form-actions {
flex: 1;
}
@media screen and (max-width: 600px) {
.media-library-view .form-actions {
flex-basis: 100%;
}
}
/* @todo Remove in https://www.drupal.org/project/drupal/issues/3064914 */
.views-live-preview .media-library-view div.views-row + div.views-row {
margin-top: 0;
}
...@@ -12,9 +12,10 @@ ...@@ -12,9 +12,10 @@
*/ */
Drupal.behaviors.MediaLibrarySelectAll = { Drupal.behaviors.MediaLibrarySelectAll = {
attach(context) { attach(context) {
const $view = $('.js-media-library-view', context).once( const $view = $(
'media-library-select-all', '.js-media-library-view[data-view-display-id="page"]',
); context,
).once('media-library-select-all');
if ($view.length && $view.find('.js-media-library-item').length) { if ($view.length && $view.find('.js-media-library-item').length) {
const $checkbox = $(Drupal.theme('checkbox')).on( const $checkbox = $(Drupal.theme('checkbox')).on(
'click', 'click',
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
(function ($, Drupal) { (function ($, Drupal) {
Drupal.behaviors.MediaLibrarySelectAll = { Drupal.behaviors.MediaLibrarySelectAll = {
attach: function attach(context) { attach: function attach(context) {
var $view = $('.js-media-library-view', context).once('media-library-select-all'); var $view = $('.js-media-library-view[data-view-display-id="page"]', context).once('media-library-select-all');
if ($view.length && $view.find('.js-media-library-item').length) { if ($view.length && $view.find('.js-media-library-item').length) {
var $checkbox = $(Drupal.theme('checkbox')).on('click', function (_ref) { var $checkbox = $(Drupal.theme('checkbox')).on('click', function (_ref) {
var currentTarget = _ref.currentTarget; var currentTarget = _ref.currentTarget;
......
name: 'Media Library' name: 'Media Library'
type: module type: module
description: 'Enhances the media list with additional features to more easily find and use existing media items.' description: 'Enhances the media list with additional features to more easily find and use existing media items.'
package: Core (Experimental) package: Core
version: VERSION version: VERSION
core: 8.x core: 8.x
dependencies: dependencies:
......
style:
version: VERSION
css:
component:
css/media_library.module.css: {}
theme:
css/media_library.theme.css: {}
click_to_select: click_to_select:
version: VERSION version: VERSION
js: js:
...@@ -21,7 +13,6 @@ view: ...@@ -21,7 +13,6 @@ view:
dependencies: dependencies:
- core/drupal.announce - core/drupal.announce
- core/drupal.checkbox - core/drupal.checkbox
- media_library/style
- media_library/click_to_select - media_library/click_to_select
widget: widget:
...@@ -31,7 +22,6 @@ widget: ...@@ -31,7 +22,6 @@ widget:
dependencies: dependencies:
- core/drupal.dialog.ajax - core/drupal.dialog.ajax
- core/jquery.once - core/jquery.once
- media_library/style
- core/sortable - core/sortable
ui: ui:
......
...@@ -18,9 +18,8 @@ ...@@ -18,9 +18,8 @@
* @ingroup ajax * @ingroup ajax
* *
* @internal * @internal
* Media Library is an experimental module and its internal code may be * This is an internal part of Media Library and may be subject to change in
* subject to change in minor releases. External code should not instantiate * minor releases. External code should not instantiate or extend this class.
* or extend this class.
*/ */
class UpdateSelectionCommand implements CommandInterface { class UpdateSelectionCommand implements CommandInterface {
......
...@@ -9,8 +9,11 @@ ...@@ -9,8 +9,11 @@
use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\media\MediaInterface; use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface; use Drupal\media\MediaTypeInterface;
...@@ -21,13 +24,8 @@ ...@@ -21,13 +24,8 @@
/** /**
* Provides a base class for creating media items from within the media library. * 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 { abstract class AddFormBase extends FormBase implements BaseFormIdInterface, TrustedCallbackInterface {
/** /**
* The entity type manager. * The entity type manager.
...@@ -99,7 +97,7 @@ public static function create(ContainerInterface $container) { ...@@ -99,7 +97,7 @@ public static function create(ContainerInterface $container) {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFormId() { public function getBaseFormId() {
return 'media_library_add_form'; return 'media_library_add_form';
} }
...@@ -137,9 +135,8 @@ protected function getMediaType(FormStateInterface $form_state) { ...@@ -137,9 +135,8 @@ protected function getMediaType(FormStateInterface $form_state) {
public function buildForm(array $form, FormStateInterface $form_state) { public function buildForm(array $form, FormStateInterface $form_state) {
// @todo Remove the ID when we can use selectors to replace content via // @todo Remove the ID when we can use selectors to replace content via
// AJAX in https://www.drupal.org/project/drupal/issues/2821793. // AJAX in https://www.drupal.org/project/drupal/issues/2821793.
$form['#prefix'] = '<div id="media-library-add-form-wrapper" class="media-library-add-form-wrapper">'; $form['#prefix'] = '<div id="media-library-add-form-wrapper">';
$form['#suffix'] = '</div>'; $form['#suffix'] = '</div>';
$form['#attached']['library'][] = 'media_library/style';
// The media library is loaded via AJAX, which means that the form action // The media library is loaded via AJAX, which means that the form action
// URL defaults to the current URL. However, to add media, we always need to // URL defaults to the current URL. However, to add media, we always need to
...@@ -157,27 +154,37 @@ public function buildForm(array $form, FormStateInterface $form_state) { ...@@ -157,27 +154,37 @@ public function buildForm(array $form, FormStateInterface $form_state) {
]; ];
$form['#attributes']['class'] = [ $form['#attributes']['class'] = [
'media-library-add-form',
'js-media-library-add-form', 'js-media-library-add-form',
]; ];
$added_media = $this->getAddedMediaItems($form_state); $added_media = $this->getAddedMediaItems($form_state);
if (empty($added_media)) { if (empty($added_media)) {
$form['#attributes']['class'][] = 'media-library-add-form--without-input';
$form = $this->buildInputElement($form, $form_state); $form = $this->buildInputElement($form, $form_state);
} }
else { else {
$form['#attributes']['class'][] = 'media-library-add-form--with-input'; $form['#attributes']['data-input'] = 'true';
// This deserves to be themeable, but it doesn't need to be its own "real"
// template.
$form['description'] = [
'#type' => 'inline_template',
'#template' => '<p>{{ text }}</p>',
'#context' => [
'text' => $this->formatPlural(count($added_media), 'The media item has been created but has not yet been saved. Fill in any required fields and save to add it to the media library.', 'The media items have been created but have not yet been saved. Fill in any required fields and save to add them to the media library.'),
],
];
$form['media'] = [ $form['media'] = [
'#type' => 'container', '#pre_render' => [
[$this, 'preRenderAddedMedia'],
],
'#attributes' => [ '#attributes' => [
'class' => [ 'class' => [
// This needs to be focus-able by an AJAX response.
// @see ::updateFormCallback()
'js-media-library-add-form-added-media', 'js-media-library-add-form-added-media',
'media-library-add-form__added-media',
], ],
'aria-label' => $this->t('Added media items'), 'aria-label' => $this->t('Added media items'),
'role' => 'list',
// Add the tabindex '-1' to allow the focus to be shifted to the added // Add the tabindex '-1' to allow the focus to be shifted to the added
// media wrapper when items are added. We set focus to the container // media wrapper when items are added. We set focus to the container
// because a media item does not necessarily have required fields and // because a media item does not necessarily have required fields and
...@@ -186,18 +193,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { ...@@ -186,18 +193,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'tabindex' => '-1', 'tabindex' => '-1',
], ],
]; ];
$form['media']['description'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#value' => $this->formatPlural(count($added_media), 'The media item has been created but has not yet been saved. Fill in any required fields and save to add it to the media library.', 'The media items have been created but have not yet been saved. Fill in any required fields and save to add them to the media library.'),
'#attributes' => [
'class' => [
'media-library-add-form__description',
],
],
];
foreach ($added_media as $delta => $media) { foreach ($added_media as $delta => $media) {
$form['media'][$delta] = $this->buildEntityFormElement($media, $form, $form_state, $delta); $form['media'][$delta] = $this->buildEntityFormElement($media, $form, $form_state, $delta);
} }
...@@ -267,13 +262,8 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo ...@@ -267,13 +262,8 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
$id_suffix = $parents ? '-' . implode('-', $parents) : ''; $id_suffix = $parents ? '-' . implode('-', $parents) : '';
$element = [ $element = [
'#type' => 'container', '#wrapper_attributes' => [
'#attributes' => [
'class' => [
'media-library-add-form__media',
],
'aria-label' => $media->getName(), 'aria-label' => $media->getName(),
'role' => 'listitem',
// Add the tabindex '-1' to allow the focus to be shifted to the next // Add the tabindex '-1' to allow the focus to be shifted to the next
// media item when an item is removed. We set focus to the container // media item when an item is removed. We set focus to the container
// because a media item does not necessarily have required fields and we // because a media item does not necessarily have required fields and we
...@@ -288,20 +278,10 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo ...@@ -288,20 +278,10 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
'preview' => [ 'preview' => [
'#type' => 'container', '#type' => 'container',
'#weight' => 10, '#weight' => 10,
'#attributes' => [
'class' => [
'media-library-add-form__preview',
],
],
], ],
'fields' => [ 'fields' => [
'#type' => 'container', '#type' => 'container',
'#weight' => 20, '#weight' => 20,
'#attributes' => [
'class' => [
'media-library-add-form__fields',
],
],
// The '#parents' are set here because the entity form display needs it // The '#parents' are set here because the entity form display needs it
// to build the entity form fields. // to build the entity form fields.
'#parents' => ['media', $delta, 'fields'], '#parents' => ['media', $delta, 'fields'],
...@@ -312,7 +292,6 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo ...@@ -312,7 +292,6 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
'#name' => 'media-' . $delta . '-remove-button' . $id_suffix, '#name' => 'media-' . $delta . '-remove-button' . $id_suffix,
'#weight' => 30, '#weight' => 30,
'#attributes' => [ '#attributes' => [
'class' => ['media-library-add-form__remove-button'],
'aria-label' => $this->t('Remove @label', ['@label' => $media->getName()]), 'aria-label' => $this->t('Remove @label', ['@label' => $media->getName()]),
], ],
'#ajax' => [ '#ajax' => [
...@@ -349,13 +328,13 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo ...@@ -349,13 +328,13 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
} }
$form_display->buildForm($media, $element['fields'], $form_state); $form_display->buildForm($media, $element['fields'], $form_state);
// We hide the preview of the uploaded file in the image widget with CSS. // We hide the preview of the uploaded file in the image widget with CSS, so