Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -0,0 +1,12 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- media_library
|
||||
module:
|
||||
- media
|
||||
id: media.media_library
|
||||
label: 'Media library'
|
||||
targetEntityType: media
|
||||
cache: true
|
|
@ -0,0 +1,12 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- media_library
|
||||
module:
|
||||
- media
|
||||
id: media.media_library
|
||||
label: 'Media library'
|
||||
targetEntityType: media
|
||||
cache: true
|
|
@ -0,0 +1,545 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
enforced:
|
||||
module:
|
||||
- media_library
|
||||
module:
|
||||
- media
|
||||
- media_library
|
||||
- user
|
||||
id: media_library
|
||||
label: 'Media library'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: media_field_data
|
||||
base_field: mid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access media overview'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: 'Apply Filters'
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: false
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 25
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: 'media-library-item js-media-library-item js-click-to-select'
|
||||
default_row_class: true
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
default_field_elements: true
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
fields:
|
||||
media_bulk_form:
|
||||
id: media_bulk_form
|
||||
table: media
|
||||
field: media_bulk_form
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: js-click-to-select-checkbox
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
action_title: Action
|
||||
include_exclude: exclude
|
||||
selected_actions: { }
|
||||
entity_type: media
|
||||
plugin_id: bulk_form
|
||||
rendered_entity:
|
||||
id: rendered_entity
|
||||
table: media
|
||||
field: rendered_entity
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: media-library-item__content
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
view_mode: media_library
|
||||
entity_type: media
|
||||
plugin_id: rendered_entity
|
||||
filters:
|
||||
status:
|
||||
id: status
|
||||
table: media_field_data
|
||||
field: status
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: '1'
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: 'Publishing status'
|
||||
description: null
|
||||
use_operator: false
|
||||
operator: status_op
|
||||
identifier: status
|
||||
required: true
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
is_grouped: true
|
||||
group_info:
|
||||
label: Published
|
||||
description: ''
|
||||
identifier: status
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items:
|
||||
1:
|
||||
title: Published
|
||||
operator: '='
|
||||
value: '1'
|
||||
2:
|
||||
title: Unpublished
|
||||
operator: '='
|
||||
value: '0'
|
||||
plugin_id: boolean
|
||||
entity_type: media
|
||||
entity_field: status
|
||||
name:
|
||||
id: name
|
||||
table: media_field_data
|
||||
field: name
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: contains
|
||||
value: ''
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: name_op
|
||||
label: Name
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: name_op
|
||||
identifier: name
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
entity_type: media
|
||||
entity_field: name
|
||||
plugin_id: string
|
||||
bundle:
|
||||
id: bundle
|
||||
table: media_field_data
|
||||
field: bundle
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: bundle_op
|
||||
label: 'Media type'
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: bundle_op
|
||||
identifier: type
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: 'Media type'
|
||||
description: null
|
||||
identifier: bundle
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items:
|
||||
1: { }
|
||||
2: { }
|
||||
3: { }
|
||||
entity_type: media
|
||||
entity_field: bundle
|
||||
plugin_id: bundle
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: media_field_data
|
||||
field: created
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: DESC
|
||||
exposed: true
|
||||
expose:
|
||||
label: 'Newest first'
|
||||
granularity: second
|
||||
entity_type: media
|
||||
entity_field: created
|
||||
plugin_id: date
|
||||
name:
|
||||
id: name
|
||||
table: media_field_data
|
||||
field: name
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: true
|
||||
expose:
|
||||
label: 'Name (A-Z)'
|
||||
entity_type: media
|
||||
entity_field: name
|
||||
plugin_id: standard
|
||||
name_1:
|
||||
id: name_1
|
||||
table: media_field_data
|
||||
field: name
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: DESC
|
||||
exposed: true
|
||||
expose:
|
||||
label: 'Name (Z-A)'
|
||||
entity_type: media
|
||||
entity_field: name
|
||||
plugin_id: standard
|
||||
title: Media
|
||||
header: { }
|
||||
footer: { }
|
||||
empty:
|
||||
area_text_custom:
|
||||
id: area_text_custom
|
||||
table: views
|
||||
field: area_text_custom
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
empty: true
|
||||
tokenize: false
|
||||
content: 'No media available.'
|
||||
plugin_id: text_custom
|
||||
relationships: { }
|
||||
display_extenders: { }
|
||||
use_ajax: true
|
||||
css_class: 'media-library-view js-media-library-view'
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'url.query_args:sort_by'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page:
|
||||
display_plugin: page
|
||||
id: page
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: admin/content/media
|
||||
menu:
|
||||
type: tab
|
||||
title: Media
|
||||
description: 'Allows users to browse and administer media items'
|
||||
expanded: false
|
||||
parent: system.admin_content
|
||||
weight: 5
|
||||
context: '0'
|
||||
menu_name: admin
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'url.query_args:sort_by'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
# @todo Lock down access in https://www.drupal.org/node/2983179
|
||||
widget:
|
||||
display_plugin: page
|
||||
id: widget
|
||||
display_title: Widget
|
||||
position: 2
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: admin/content/media-widget
|
||||
fields:
|
||||
rendered_entity:
|
||||
id: rendered_entity
|
||||
table: media
|
||||
field: rendered_entity
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: media-library-item__content
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
view_mode: media_library
|
||||
entity_type: media
|
||||
plugin_id: rendered_entity
|
||||
media_library_select_form:
|
||||
id: media_library_select_form
|
||||
table: media
|
||||
field: media_library_select_form
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: js-click-to-select-checkbox
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
entity_type: media
|
||||
plugin_id: media_library_select_form
|
||||
defaults:
|
||||
fields: false
|
||||
access: false
|
||||
display_description: ''
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'view media'
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'url.query_args:sort_by'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,26 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.audio.field_media_audio_file
|
||||
- media.type.audio
|
||||
id: media.audio.media_library
|
||||
targetEntityType: media
|
||||
bundle: audio
|
||||
mode: media_library
|
||||
content:
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_audio_file: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,26 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.file.field_media_file
|
||||
- media.type.file
|
||||
id: media.file.media_library
|
||||
targetEntityType: media
|
||||
bundle: file
|
||||
mode: media_library
|
||||
content:
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_file: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,36 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.image.field_media_image
|
||||
- image.style.thumbnail
|
||||
- media.type.image
|
||||
module:
|
||||
- image
|
||||
id: media.image.media_library
|
||||
targetEntityType: media
|
||||
bundle: image
|
||||
mode: media_library
|
||||
content:
|
||||
field_media_image:
|
||||
type: image_image
|
||||
weight: 1
|
||||
region: content
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: thumbnail
|
||||
third_party_settings: { }
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,26 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.video.field_media_video_file
|
||||
- media.type.video
|
||||
id: media.video.media_library
|
||||
targetEntityType: media
|
||||
bundle: video
|
||||
mode: media_library
|
||||
content:
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_video_file: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
- field.field.media.audio.field_media_audio_file
|
||||
- image.style.thumbnail
|
||||
- media.type.audio
|
||||
module:
|
||||
- image
|
||||
id: media.audio.media_library
|
||||
targetEntityType: media
|
||||
bundle: audio
|
||||
mode: media_library
|
||||
content:
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 0
|
||||
region: content
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_audio_file: true
|
||||
name: true
|
||||
uid: true
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
- field.field.media.file.field_media_file
|
||||
- image.style.thumbnail
|
||||
- media.type.file
|
||||
module:
|
||||
- image
|
||||
id: media.file.media_library
|
||||
targetEntityType: media
|
||||
bundle: file
|
||||
mode: media_library
|
||||
content:
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 0
|
||||
region: content
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_file: true
|
||||
name: true
|
||||
uid: true
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
- field.field.media.image.field_media_image
|
||||
- image.style.medium
|
||||
- media.type.image
|
||||
module:
|
||||
- image
|
||||
id: media.image.media_library
|
||||
targetEntityType: media
|
||||
bundle: image
|
||||
mode: media_library
|
||||
content:
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 0
|
||||
region: content
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: medium
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_image: true
|
||||
name: true
|
||||
uid: true
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
- field.field.media.remote_video.field_media_oembed_video
|
||||
- image.style.medium
|
||||
- media.type.remote_video
|
||||
module:
|
||||
- image
|
||||
id: media.remote_video.media_library
|
||||
targetEntityType: media
|
||||
bundle: remote_video
|
||||
mode: media_library
|
||||
content:
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 0
|
||||
region: content
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: medium
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_oembed_video: true
|
||||
name: true
|
||||
uid: true
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.media.media_library
|
||||
- field.field.media.video.field_media_video_file
|
||||
- image.style.thumbnail
|
||||
- media.type.video
|
||||
module:
|
||||
- image
|
||||
id: media.video.media_library
|
||||
targetEntityType: media
|
||||
bundle: video
|
||||
mode: media_library
|
||||
content:
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 0
|
||||
region: content
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
field_media_video_file: true
|
||||
name: true
|
||||
uid: true
|
76
web/core/modules/media_library/css/media_library.module.css
Normal file
76
web/core/modules/media_library/css/media_library.module.css
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* @file media_library.module.css
|
||||
*/
|
||||
|
||||
.media-library-views-form > .form-actions {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.media-library-views-form,
|
||||
.media-library-selection,
|
||||
.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 .js-click-to-select-trigger {
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.media-library-view .form-actions {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.media-library-item .js-click-to-select-checkbox {
|
||||
position: absolute;
|
||||
display: block;
|
||||
z-index: 1;
|
||||
top: 5px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.media-library-item__status {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 2px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.media-library-select-all {
|
||||
flex-basis: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-library-view.view-display-id-widget .media-library-select-all {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-library-item--disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.media-library-selection .media-library-item__preview {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* @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 {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.media-library-view .form-actions {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
259
web/core/modules/media_library/css/media_library.theme.css
Normal file
259
web/core/modules/media_library/css/media_library.theme.css
Normal file
|
@ -0,0 +1,259 @@
|
|||
/**
|
||||
* @file media_library.theme.css
|
||||
*
|
||||
* @todo Move into the Seven theme when this module is marked as stable.
|
||||
* @see https://www.drupal.org/project/drupal/issues/2980769
|
||||
*/
|
||||
|
||||
.media-library-views-form__header .form-item {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#drupal-modal .view-header {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.media-library-item {
|
||||
justify-content: center;
|
||||
vertical-align: top;
|
||||
border: 1px solid #dbdbdb;
|
||||
margin: 16px 16px 2px 2px;
|
||||
width: 180px;
|
||||
background: #fff;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.media-library-view {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.media-library-view .form-actions {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
|
||||
.media-library-view .media-library-view--form-actions {
|
||||
clear: left;
|
||||
margin: 0.75em 0;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.media-library-item .field--name-thumbnail {
|
||||
background-color: #ebebeb;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.media-library-item .field--name-thumbnail img {
|
||||
height: 180px;
|
||||
object-fit: contain;
|
||||
object-position: center center;
|
||||
}
|
||||
|
||||
.media-library-item.is-hover,
|
||||
.media-library-item.checked,
|
||||
.media-library-item.is-focus {
|
||||
border-color: #40b6ff;
|
||||
border-width: 3px;
|
||||
border-radius: 3px;
|
||||
margin: 14px 14px 0 0;
|
||||
}
|
||||
|
||||
.media-library-item.checked {
|
||||
border-color: #0076c0;
|
||||
}
|
||||
|
||||
.media-library-item .js-click-to-select-checkbox input {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.media-library-item .js-click-to-select-checkbox .form-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-library-item__preview {
|
||||
padding-bottom: 34px;
|
||||
}
|
||||
|
||||
.media-library-item__status {
|
||||
color: #e4e4e4;
|
||||
font-style: italic;
|
||||
background: #666;
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.media-library-item .views-field-operations {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.media-library-item .views-field-operations .dropbutton-wrapper {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.media-library-item__attributes {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
max-width: calc(100% - 10px);
|
||||
max-height: calc(100% - 50px);
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.media-library-item__name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.media-library-item__name a {
|
||||
display: block;
|
||||
text-decoration: underline;
|
||||
margin: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.media-library-item__attributes:hover .media-library-item__name a,
|
||||
.media-library-item__name a:focus,
|
||||
.media-library-item.is-focus .media-library-item__name a,
|
||||
.media-library-item.checked .media-library-item__name a {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.media-library-item__name a:focus {
|
||||
border: 2px solid;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-library-item__type {
|
||||
font-size: 12px;
|
||||
color: #696969;
|
||||
}
|
||||
|
||||
.media-library-select-all {
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.media-library-select-all input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.media-library-item--disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.media-library-selection {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.media-library-widget {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-library-widget__toggle-weight {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.media-library-item .form-item {
|
||||
margin: 0.75em;
|
||||
}
|
||||
|
||||
.media-library-item__remove,
|
||||
.media-library-item__remove:hover,
|
||||
.media-library-item__remove:focus,
|
||||
.media-library-item__remove.button,
|
||||
.media-library-item__remove.button:disabled,
|
||||
.media-library-item__remove.button:disabled:active,
|
||||
.media-library-item__remove.button:hover,
|
||||
.media-library-item__remove.button:focus {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 5px;
|
||||
padding: 0;
|
||||
background: url('../../../misc/icons/787878/ex.svg') #fff center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 20px;
|
||||
color: transparent;
|
||||
text-shadow: none;
|
||||
transition: 0.2s border-color;
|
||||
}
|
||||
|
||||
.media-library-item__remove:hover,
|
||||
.media-library-item__remove:focus,
|
||||
.media-library-item__remove.button:hover,
|
||||
.media-library-item__remove.button:focus,
|
||||
.media-library-item__remove.button:disabled:active {
|
||||
border-color: #40b6ff;
|
||||
}
|
||||
|
||||
/* Style the wrappers around new media and files */
|
||||
.media-library-upload__media,
|
||||
.media-library-upload__file {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px 0 20px 0;
|
||||
}
|
||||
|
||||
.media-library-upload__file {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-library-upload__file-label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* @todo Remove in https://www.drupal.org/project/drupal/issues/2987921 */
|
||||
.media-library-upload__source-field .file,
|
||||
.media-library-upload__source-field .button,
|
||||
.media-library-upload__source-field .image-preview,
|
||||
.media-library-upload__source-field .form-type-managed-file > label,
|
||||
.media-library-upload__source-field .file-size {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-library-upload__media-preview {
|
||||
margin-right: 20px;
|
||||
width: 220px;
|
||||
background: #ebebeb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.media-library-upload__media-preview img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */
|
||||
.media-library-widget .media-library-item__name a,
|
||||
.media-library-view.view-display-id-widget .media-library-item__name a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.media-library-item {
|
||||
width: 150px;
|
||||
}
|
||||
.media-library-item .field--name-thumbnail img {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
.media-library-item .views-field-operations .dropbutton-wrapper {
|
||||
position: relative;
|
||||
right: 0;
|
||||
border: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file media_library.click_to_select.es6.js
|
||||
*/
|
||||
|
||||
(($, Drupal) => {
|
||||
/**
|
||||
* Allows users to select an element which checks a hidden checkbox.
|
||||
*/
|
||||
Drupal.behaviors.ClickToSelect = {
|
||||
attach(context) {
|
||||
$('.js-click-to-select-trigger', context)
|
||||
.once('media-library-click-to-select')
|
||||
.on('click', event => {
|
||||
// Links inside the trigger should not be click-able.
|
||||
event.preventDefault();
|
||||
// Click the hidden checkbox when the trigger is clicked.
|
||||
const $input = $(event.currentTarget)
|
||||
.closest('.js-click-to-select')
|
||||
.find('.js-click-to-select-checkbox input');
|
||||
$input.prop('checked', !$input.prop('checked')).trigger('change');
|
||||
});
|
||||
$('.js-click-to-select-checkbox input', context)
|
||||
.once('media-library-click-to-select')
|
||||
.on('change', ({ currentTarget }) => {
|
||||
$(currentTarget)
|
||||
.closest('.js-click-to-select')
|
||||
.toggleClass('checked', $(currentTarget).prop('checked'));
|
||||
});
|
||||
},
|
||||
};
|
||||
})(jQuery, Drupal);
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
Drupal.behaviors.ClickToSelect = {
|
||||
attach: function attach(context) {
|
||||
$('.js-click-to-select-trigger', context).once('media-library-click-to-select').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var $input = $(event.currentTarget).closest('.js-click-to-select').find('.js-click-to-select-checkbox input');
|
||||
$input.prop('checked', !$input.prop('checked')).trigger('change');
|
||||
});
|
||||
$('.js-click-to-select-checkbox input', context).once('media-library-click-to-select').on('change', function (_ref) {
|
||||
var currentTarget = _ref.currentTarget;
|
||||
|
||||
$(currentTarget).closest('.js-click-to-select').toggleClass('checked', $(currentTarget).prop('checked'));
|
||||
});
|
||||
}
|
||||
};
|
||||
})(jQuery, Drupal);
|
76
web/core/modules/media_library/js/media_library.view.es6.js
Normal file
76
web/core/modules/media_library/js/media_library.view.es6.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* @file media_library.view.es6.js
|
||||
*/
|
||||
(($, Drupal) => {
|
||||
/**
|
||||
* Adds hover effect to media items.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryHover = {
|
||||
attach(context) {
|
||||
$(
|
||||
'.media-library-item .js-click-to-select-trigger,.media-library-item .js-click-to-select-checkbox',
|
||||
context,
|
||||
)
|
||||
.once('media-library-item-hover')
|
||||
.on('mouseover mouseout', ({ currentTarget, type }) => {
|
||||
$(currentTarget)
|
||||
.closest('.media-library-item')
|
||||
.toggleClass('is-hover', type === 'mouseover');
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds focus effect to media items.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryFocus = {
|
||||
attach(context) {
|
||||
$('.media-library-item .js-click-to-select-checkbox input', context)
|
||||
.once('media-library-item-focus')
|
||||
.on('focus blur', ({ currentTarget, type }) => {
|
||||
$(currentTarget)
|
||||
.closest('.media-library-item')
|
||||
.toggleClass('is-focus', type === 'focus');
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds checkbox to select all items in the library.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibrarySelectAll = {
|
||||
attach(context) {
|
||||
const $view = $('.media-library-view', context).once(
|
||||
'media-library-select-all',
|
||||
);
|
||||
if ($view.length && $view.find('.media-library-item').length) {
|
||||
const $checkbox = $(
|
||||
'<input type="checkbox" class="form-checkbox" />',
|
||||
).on('click', ({ currentTarget }) => {
|
||||
// Toggle all checkboxes.
|
||||
const $checkboxes = $(currentTarget)
|
||||
.closest('.media-library-view')
|
||||
.find('.media-library-item input[type="checkbox"]');
|
||||
$checkboxes
|
||||
.prop('checked', $(currentTarget).prop('checked'))
|
||||
.trigger('change');
|
||||
// Announce the selection.
|
||||
const announcement = $(currentTarget).prop('checked')
|
||||
? Drupal.t('Zero items selected')
|
||||
: Drupal.t('All @count items selected', {
|
||||
'@count': $checkboxes.length,
|
||||
});
|
||||
Drupal.announce(announcement);
|
||||
});
|
||||
const $label = $(
|
||||
'<label class="media-library-select-all"></label>',
|
||||
).text(Drupal.t('Select all media'));
|
||||
$label.prepend($checkbox);
|
||||
$view
|
||||
.find('.media-library-item')
|
||||
.first()
|
||||
.before($label);
|
||||
}
|
||||
},
|
||||
};
|
||||
})(jQuery, Drupal);
|
52
web/core/modules/media_library/js/media_library.view.js
Normal file
52
web/core/modules/media_library/js/media_library.view.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
Drupal.behaviors.MediaLibraryHover = {
|
||||
attach: function attach(context) {
|
||||
$('.media-library-item .js-click-to-select-trigger,.media-library-item .js-click-to-select-checkbox', context).once('media-library-item-hover').on('mouseover mouseout', function (_ref) {
|
||||
var currentTarget = _ref.currentTarget,
|
||||
type = _ref.type;
|
||||
|
||||
$(currentTarget).closest('.media-library-item').toggleClass('is-hover', type === 'mouseover');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibraryFocus = {
|
||||
attach: function attach(context) {
|
||||
$('.media-library-item .js-click-to-select-checkbox input', context).once('media-library-item-focus').on('focus blur', function (_ref2) {
|
||||
var currentTarget = _ref2.currentTarget,
|
||||
type = _ref2.type;
|
||||
|
||||
$(currentTarget).closest('.media-library-item').toggleClass('is-focus', type === 'focus');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibrarySelectAll = {
|
||||
attach: function attach(context) {
|
||||
var $view = $('.media-library-view', context).once('media-library-select-all');
|
||||
if ($view.length && $view.find('.media-library-item').length) {
|
||||
var $checkbox = $('<input type="checkbox" class="form-checkbox" />').on('click', function (_ref3) {
|
||||
var currentTarget = _ref3.currentTarget;
|
||||
|
||||
var $checkboxes = $(currentTarget).closest('.media-library-view').find('.media-library-item input[type="checkbox"]');
|
||||
$checkboxes.prop('checked', $(currentTarget).prop('checked')).trigger('change');
|
||||
|
||||
var announcement = $(currentTarget).prop('checked') ? Drupal.t('Zero items selected') : Drupal.t('All @count items selected', {
|
||||
'@count': $checkboxes.length
|
||||
});
|
||||
Drupal.announce(announcement);
|
||||
});
|
||||
var $label = $('<label class="media-library-select-all"></label>').text(Drupal.t('Select all media'));
|
||||
$label.prepend($checkbox);
|
||||
$view.find('.media-library-item').first().before($label);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery, Drupal);
|
120
web/core/modules/media_library/js/media_library.widget.es6.js
Normal file
120
web/core/modules/media_library/js/media_library.widget.es6.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* @file media_library.widget.js
|
||||
*/
|
||||
(($, Drupal) => {
|
||||
/**
|
||||
* Allows users to re-order their selection with drag+drop.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryWidgetSortable = {
|
||||
attach(context) {
|
||||
// Allow media items to be re-sorted with drag+drop in the widget.
|
||||
$('.js-media-library-selection', context)
|
||||
.once('media-library-sortable')
|
||||
.sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
handle: '.js-media-library-item-preview',
|
||||
stop: ({ target }) => {
|
||||
// Update all the hidden "weight" fields.
|
||||
$(target)
|
||||
.children()
|
||||
.each((index, child) => {
|
||||
$(child)
|
||||
.find('.js-media-library-item-weight')
|
||||
.val(index);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows selection order to be set without drag+drop for accessibility.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryWidgetToggleWeight = {
|
||||
attach(context) {
|
||||
const strings = {
|
||||
show: Drupal.t('Show media item weights'),
|
||||
hide: Drupal.t('Hide media item weights'),
|
||||
};
|
||||
$('.js-media-library-widget-toggle-weight', context)
|
||||
.once('media-library-toggle')
|
||||
.on('click', e => {
|
||||
e.preventDefault();
|
||||
$(e.currentTarget)
|
||||
.toggleClass('active')
|
||||
.text(
|
||||
$(e.currentTarget).hasClass('active')
|
||||
? strings.hide
|
||||
: strings.show,
|
||||
)
|
||||
.parent()
|
||||
.find('.js-media-library-item-weight')
|
||||
.parent()
|
||||
.toggle();
|
||||
})
|
||||
.text(strings.show);
|
||||
$('.js-media-library-item-weight', context)
|
||||
.once('media-library-toggle')
|
||||
.parent()
|
||||
.hide();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Warn users when clicking outgoing links from the library or widget.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryWidgetWarn = {
|
||||
attach(context) {
|
||||
$('.js-media-library-item a[href]', context)
|
||||
.once('media-library-warn-link')
|
||||
.on('click', e => {
|
||||
const message = Drupal.t(
|
||||
'Unsaved changes to the form will be lost. Are you sure you want to leave?',
|
||||
);
|
||||
const confirmation = window.confirm(message);
|
||||
if (!confirmation) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent users from selecting more items than allowed in the view.
|
||||
*/
|
||||
Drupal.behaviors.MediaLibraryWidgetRemaining = {
|
||||
attach(context, settings) {
|
||||
const $view = $('.js-media-library-view', context).once(
|
||||
'media-library-remaining',
|
||||
);
|
||||
$view
|
||||
.find('.js-media-library-item input[type="checkbox"]')
|
||||
.on('change', () => {
|
||||
if (
|
||||
settings.media_library &&
|
||||
settings.media_library.selection_remaining
|
||||
) {
|
||||
const $checkboxes = $view.find(
|
||||
'.js-media-library-item input[type="checkbox"]',
|
||||
);
|
||||
if (
|
||||
$checkboxes.filter(':checked').length ===
|
||||
settings.media_library.selection_remaining
|
||||
) {
|
||||
$checkboxes
|
||||
.not(':checked')
|
||||
.prop('disabled', true)
|
||||
.closest('.js-media-library-item')
|
||||
.addClass('media-library-item--disabled');
|
||||
} else {
|
||||
$checkboxes
|
||||
.prop('disabled', false)
|
||||
.closest('.js-media-library-item')
|
||||
.removeClass('media-library-item--disabled');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
})(jQuery, Drupal);
|
67
web/core/modules/media_library/js/media_library.widget.js
Normal file
67
web/core/modules/media_library/js/media_library.widget.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
Drupal.behaviors.MediaLibraryWidgetSortable = {
|
||||
attach: function attach(context) {
|
||||
$('.js-media-library-selection', context).once('media-library-sortable').sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
handle: '.js-media-library-item-preview',
|
||||
stop: function stop(_ref) {
|
||||
var target = _ref.target;
|
||||
|
||||
$(target).children().each(function (index, child) {
|
||||
$(child).find('.js-media-library-item-weight').val(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibraryWidgetToggleWeight = {
|
||||
attach: function attach(context) {
|
||||
var strings = {
|
||||
show: Drupal.t('Show media item weights'),
|
||||
hide: Drupal.t('Hide media item weights')
|
||||
};
|
||||
$('.js-media-library-widget-toggle-weight', context).once('media-library-toggle').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$(e.currentTarget).toggleClass('active').text($(e.currentTarget).hasClass('active') ? strings.hide : strings.show).parent().find('.js-media-library-item-weight').parent().toggle();
|
||||
}).text(strings.show);
|
||||
$('.js-media-library-item-weight', context).once('media-library-toggle').parent().hide();
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibraryWidgetWarn = {
|
||||
attach: function attach(context) {
|
||||
$('.js-media-library-item a[href]', context).once('media-library-warn-link').on('click', function (e) {
|
||||
var message = Drupal.t('Unsaved changes to the form will be lost. Are you sure you want to leave?');
|
||||
var confirmation = window.confirm(message);
|
||||
if (!confirmation) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.MediaLibraryWidgetRemaining = {
|
||||
attach: function attach(context, settings) {
|
||||
var $view = $('.js-media-library-view', context).once('media-library-remaining');
|
||||
$view.find('.js-media-library-item input[type="checkbox"]').on('change', function () {
|
||||
if (settings.media_library && settings.media_library.selection_remaining) {
|
||||
var $checkboxes = $view.find('.js-media-library-item input[type="checkbox"]');
|
||||
if ($checkboxes.filter(':checked').length === settings.media_library.selection_remaining) {
|
||||
$checkboxes.not(':checked').prop('disabled', true).closest('.js-media-library-item').addClass('media-library-item--disabled');
|
||||
} else {
|
||||
$checkboxes.prop('disabled', false).closest('.js-media-library-item').removeClass('media-library-item--disabled');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})(jQuery, Drupal);
|
10
web/core/modules/media_library/media_library.info.yml
Normal file
10
web/core/modules/media_library/media_library.info.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: 'Media library'
|
||||
type: module
|
||||
description: 'Enhances the media list with additional features to easier find and use existing media items.'
|
||||
package: Core (Experimental)
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:media
|
||||
- drupal:views
|
||||
- drupal:user
|
49
web/core/modules/media_library/media_library.install
Normal file
49
web/core/modules/media_library/media_library.install
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the media_library module.
|
||||
*/
|
||||
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function media_library_install() {
|
||||
// Change the path to the original media view.
|
||||
/** @var \Drupal\views\Entity\View $view */
|
||||
if ($view = View::load('media')) {
|
||||
$display = &$view->getDisplay('media_page_list');
|
||||
if (!empty($display)) {
|
||||
$display['display_options']['path'] = 'admin/content/media-table';
|
||||
unset($display['display_options']['menu']);
|
||||
$view->trustData()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function media_library_uninstall() {
|
||||
// Restore the path to the original media view.
|
||||
/** @var \Drupal\views\Entity\View $view */
|
||||
if ($view = View::load('media')) {
|
||||
$display = &$view->getDisplay('media_page_list');
|
||||
if (!empty($display)) {
|
||||
$display['display_options']['path'] = 'admin/content/media';
|
||||
$display['display_options']['menu'] = [
|
||||
'type' => 'tab',
|
||||
'title' => 'Media',
|
||||
'description' => '',
|
||||
'expanded' => FALSE,
|
||||
'parent' => '',
|
||||
'weight' => 0,
|
||||
'context' => '0',
|
||||
'menu_name' => 'main',
|
||||
];
|
||||
$view->trustData()->save();
|
||||
}
|
||||
}
|
||||
}
|
34
web/core/modules/media_library/media_library.libraries.yml
Normal file
34
web/core/modules/media_library/media_library.libraries.yml
Normal file
|
@ -0,0 +1,34 @@
|
|||
style:
|
||||
version: VERSION
|
||||
css:
|
||||
component:
|
||||
css/media_library.module.css: {}
|
||||
theme:
|
||||
css/media_library.theme.css: {}
|
||||
|
||||
click_to_select:
|
||||
version: VERSION
|
||||
js:
|
||||
js/media_library.click_to_select.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
- core/jquery.once
|
||||
|
||||
view:
|
||||
version: VERSION
|
||||
js:
|
||||
js/media_library.view.js: {}
|
||||
dependencies:
|
||||
- media_library/style
|
||||
- media_library/click_to_select
|
||||
|
||||
widget:
|
||||
version: VERSION
|
||||
js:
|
||||
js/media_library.widget.js: {}
|
||||
dependencies:
|
||||
- core/drupal.ajax
|
||||
- core/jquery.ui.sortable
|
||||
- media_library/view
|
||||
- core/drupal.announce
|
||||
- core/jquery.once
|
|
@ -0,0 +1,5 @@
|
|||
media_library.add:
|
||||
route_name: entity.media.add_page
|
||||
title: 'Add media'
|
||||
appears_on:
|
||||
- view.media.media_page_list
|
10
web/core/modules/media_library/media_library.links.task.yml
Normal file
10
web/core/modules/media_library/media_library.links.task.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
media_library.grid:
|
||||
title: 'Grid'
|
||||
parent_id: entity.media.collection
|
||||
route_name: entity.media.collection
|
||||
weight: 10
|
||||
media_library.table:
|
||||
title: 'Table'
|
||||
parent_id: entity.media.collection
|
||||
route_name: view.media.media_page_list
|
||||
weight: 20
|
275
web/core/modules/media_library/media_library.module
Normal file
275
web/core/modules/media_library/media_library.module
Normal file
|
@ -0,0 +1,275 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains hook implementations for the media_library module.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\Form\ViewsForm;
|
||||
use Drupal\views\Plugin\views\cache\CachePluginBase;
|
||||
use Drupal\views\Plugin\views\query\QueryPluginBase;
|
||||
use Drupal\views\Plugin\views\query\Sql;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*
|
||||
* @todo Update in https://www.drupal.org/project/drupal/issues/2964789
|
||||
*/
|
||||
function media_library_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.media_library':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Media library module overrides the /admin/content/media view to provide a rich visual interface for performing administrative operations on media. For more information, see the <a href=":media">online documentation for the Media library module</a>.', [':media' => 'https://www.drupal.org/docs/8/core/modules/media']) . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function media_library_theme() {
|
||||
return [
|
||||
'media__media_library' => [
|
||||
'base hook' => 'media',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*/
|
||||
function media_library_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
|
||||
if ($view->id() === 'media_library') {
|
||||
$output['#attached']['library'][] = 'media_library/view';
|
||||
if ($view->current_display === 'widget') {
|
||||
$query = array_intersect_key(\Drupal::request()->query->all(), array_flip([
|
||||
'media_library_widget_id',
|
||||
'media_library_allowed_types',
|
||||
'media_library_remaining',
|
||||
]));
|
||||
// If the current query contains any parameters we use to contextually
|
||||
// filter the view, ensure they persist across AJAX rebuilds.
|
||||
// The ajax_path is shared for all AJAX views on the page, but our query
|
||||
// parameters are prefixed and should not interfere with any other views.
|
||||
// @todo Rework or remove this in https://www.drupal.org/node/2983451
|
||||
if (!empty($query)) {
|
||||
$ajax_path = &$output['#attached']['drupalSettings']['views']['ajax_path'];
|
||||
$parsed_url = UrlHelper::parse($ajax_path);
|
||||
$query = array_merge($query, $parsed_url['query']);
|
||||
$ajax_path = $parsed_url['path'] . '?' . UrlHelper::buildQuery($query);
|
||||
if (isset($query['media_library_remaining'])) {
|
||||
$output['#attached']['drupalSettings']['media_library']['selection_remaining'] = (int) $query['media_library_remaining'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_media().
|
||||
*/
|
||||
function media_library_preprocess_media(&$variables) {
|
||||
if ($variables['view_mode'] === 'media_library') {
|
||||
/** @var \Drupal\media\MediaInterface $media */
|
||||
$media = $variables['media'];
|
||||
$variables['#cache']['contexts'][] = 'user.permissions';
|
||||
$rel = $media->access('edit') ? 'edit-form' : 'canonical';
|
||||
$variables['url'] = $media->toUrl($rel, [
|
||||
'language' => $media->language(),
|
||||
]);
|
||||
$variables['preview_attributes'] = new Attribute();
|
||||
$variables['preview_attributes']->addClass('media-library-item__preview', 'js-media-library-item-preview', 'js-click-to-select-trigger');
|
||||
$variables['metadata_attributes'] = new Attribute();
|
||||
$variables['metadata_attributes']->addClass('media-library-item__attributes');
|
||||
$variables['status'] = $media->isPublished();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the bulk form to add a more accessible label.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @todo Remove in https://www.drupal.org/node/2983454
|
||||
*/
|
||||
function media_library_form_views_form_media_library_page_alter(array &$form, FormStateInterface $form_state) {
|
||||
if (isset($form['media_bulk_form']) && isset($form['output'])) {
|
||||
/** @var \Drupal\views\ViewExecutable $view */
|
||||
$view = $form['output'][0]['#view'];
|
||||
foreach (Element::getVisibleChildren($form['media_bulk_form']) as $key) {
|
||||
if (isset($view->result[$key])) {
|
||||
$media = $view->field['media_bulk_form']->getEntity($view->result[$key]);
|
||||
$form['media_bulk_form'][$key]['#title'] = t('Select @label', [
|
||||
'@label' => $media->label(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function media_library_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
|
||||
$form_object = $form_state->getFormObject();
|
||||
if ($form_object instanceof ViewsForm && strpos($form_object->getBaseFormId(), 'views_form_media_library') === 0) {
|
||||
$form['#attributes']['class'][] = 'media-library-views-form';
|
||||
if (isset($form['header'])) {
|
||||
$form['header']['#attributes']['class'][] = 'media-library-views-form__header';
|
||||
$form['header']['media_bulk_form']['#attributes']['class'][] = 'media-library-views-form__bulk_form';
|
||||
}
|
||||
}
|
||||
|
||||
// Add after build to fix media library views exposed filter's submit button.
|
||||
if ($form_id === 'views_exposed_form' && $form['#id'] === 'views-exposed-form-media-library-widget') {
|
||||
$form['#after_build'][] = '_media_library_views_form_media_library_after_build';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After build callback for views form media library.
|
||||
*/
|
||||
function _media_library_views_form_media_library_after_build(array $form, FormStateInterface $form_state) {
|
||||
// Remove .form-actions from media library views exposed filter actions
|
||||
// and replace with .media-library-view--form-actions.
|
||||
//
|
||||
// This prevents the views exposed filter's 'Apply filter' submit button from
|
||||
// being moved into the dialog's buttons.
|
||||
// @see \Drupal\Core\Render\Element\Actions::processActions
|
||||
// @see Drupal.behaviors.dialog.prepareDialogButtons
|
||||
if (($key = array_search('form-actions', $form['actions']['#attributes']['class'])) !== FALSE) {
|
||||
unset($form['actions']['#attributes']['class'][$key]);
|
||||
}
|
||||
$form['actions']['#attributes']['class'][] = 'media-library-view--form-actions';
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_query_alter().
|
||||
*
|
||||
* Alters the widget view's query to only show media that can be selected,
|
||||
* based on what types are allowed in the field settings.
|
||||
*
|
||||
* @todo Remove in https://www.drupal.org/node/2983454
|
||||
*/
|
||||
function media_library_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
|
||||
if ($query instanceof Sql && $view->id() === 'media_library' && $view->current_display === 'widget') {
|
||||
$types = _media_library_get_allowed_types();
|
||||
if ($types) {
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition('media');
|
||||
$group = $query->setWhereGroup();
|
||||
$query->addWhere($group, $entity_type->getDataTable() . '.' . $entity_type->getKey('bundle'), $types, 'in');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* Limits the types available in the exposed filter to avoid users trying to
|
||||
* filter by a type that is un-selectable.
|
||||
*
|
||||
* @see media_library_views_query_alter()
|
||||
*
|
||||
* @todo Remove in https://www.drupal.org/node/2983454
|
||||
*/
|
||||
function media_library_form_views_exposed_form_alter(array &$form, FormStateInterface $form_state) {
|
||||
if (isset($form['#id']) && $form['#id'] === 'views-exposed-form-media-library-widget') {
|
||||
$types = _media_library_get_allowed_types();
|
||||
if ($types && isset($form['type']['#options'])) {
|
||||
$keys = array_flip($types);
|
||||
// Ensure that the default value (by default "All") persists.
|
||||
if (isset($form['type']['#default_value'])) {
|
||||
$keys[$form['type']['#default_value']] = TRUE;
|
||||
}
|
||||
$form['type']['#options'] = array_intersect_key($form['type']['#options'], $keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_ui_preconfigured_options_alter().
|
||||
*/
|
||||
function media_library_field_ui_preconfigured_options_alter(array &$options, $field_type) {
|
||||
// If the field is not an "entity_reference"-based field, bail out.
|
||||
$class = \Drupal::service('plugin.manager.field.field_type')->getPluginClass($field_type);
|
||||
if (!is_a($class, EntityReferenceItem::class, TRUE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the default field widget for media to be the Media library.
|
||||
if (!empty($options['media'])) {
|
||||
$options['media']['entity_form_display']['type'] = 'media_library_widget';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_local_tasks_alter().
|
||||
*
|
||||
* Removes tasks for the Media library if the view display no longer exists.
|
||||
*/
|
||||
function media_library_local_tasks_alter(&$local_tasks) {
|
||||
/** @var \Symfony\Component\Routing\RouteCollection $route_collection */
|
||||
$route_collection = \Drupal::service('router')->getRouteCollection();
|
||||
foreach (['media_library.grid', 'media_library.table'] as $key) {
|
||||
if (isset($local_tasks[$key]) && !$route_collection->get($local_tasks[$key]['route_name'])) {
|
||||
unset($local_tasks[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what types are allowed based on the current request.
|
||||
*
|
||||
* @return array
|
||||
* An array of allowed types.
|
||||
*/
|
||||
function _media_library_get_allowed_types() {
|
||||
$types = \Drupal::request()->query->get('media_library_allowed_types');
|
||||
if ($types && is_array($types)) {
|
||||
return array_filter($types, 'is_string');
|
||||
}
|
||||
return [];
|
||||
}
|
6
web/core/modules/media_library/media_library.routing.yml
Normal file
6
web/core/modules/media_library/media_library.routing.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
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'
|
22
web/core/modules/media_library/media_library.views.inc
Normal file
22
web/core/modules/media_library/media_library.views.inc
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Views integration for the media_library module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function media_library_views_data() {
|
||||
$data = [];
|
||||
$data['media']['media_library_select_form'] = [
|
||||
'title' => t('Select media'),
|
||||
'help' => t('Provides a field for selecting media entities in our media library view'),
|
||||
'real field' => 'mid',
|
||||
'field' => [
|
||||
'id' => 'media_library_select_form',
|
||||
],
|
||||
];
|
||||
return $data;
|
||||
}
|
|
@ -0,0 +1,626 @@
|
|||
<?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 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,
|
||||
];
|
||||
}
|
||||
EntityFormDisplay::collectRenderDisplay($media, 'media_library')
|
||||
->buildForm($media, $element['fields'], $form_state);
|
||||
// We hide certain elements in the image widget with CSS.
|
||||
if (isset($element['fields'][$source_field])) {
|
||||
$element['fields'][$source_field]['#attributes']['class'][] = 'media-library-upload__source-field';
|
||||
}
|
||||
if (isset($element['fields']['revision_log_message'])) {
|
||||
$element['fields']['revision_log_message']['#access'] = FALSE;
|
||||
}
|
||||
$form['media'][$i] = $element;
|
||||
}
|
||||
|
||||
$form['files'] = [
|
||||
'#type' => 'container',
|
||||
];
|
||||
foreach ($this->files as $i => $file) {
|
||||
$types = $this->filterTypesThatAcceptFile($file, $this->getTypes());
|
||||
$form['files'][$i] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'media-library-upload__file',
|
||||
],
|
||||
],
|
||||
'help' => [
|
||||
'#markup' => '<strong class="media-library-upload__file-label">' . $this->t('Select a media type for %filename:', [
|
||||
'%filename' => $file->getFilename(),
|
||||
]) . '</strong>',
|
||||
],
|
||||
];
|
||||
foreach ($types as $type) {
|
||||
$form['files'][$i][$type->id()] = [
|
||||
'#type' => 'submit',
|
||||
'#media_library_index' => $i,
|
||||
'#media_library_type' => $type->id(),
|
||||
'#value' => $type->label(),
|
||||
'#submit' => ['::selectType'],
|
||||
'#ajax' => [
|
||||
'callback' => '::updateFormCallback',
|
||||
'wrapper' => 'media-library-upload-wrapper',
|
||||
],
|
||||
'#limit_validation_errors' => [['files', $i, $type->id()]],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$form['actions'] = [
|
||||
'#type' => 'actions',
|
||||
];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#ajax' => [
|
||||
'callback' => '::updateWidget',
|
||||
'wrapper' => 'media-library-upload-wrapper',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
if (count($this->files)) {
|
||||
$form_state->setError($form['files'], $this->t('Please select a media type for all files.'));
|
||||
}
|
||||
foreach ($this->media as $i => $media) {
|
||||
$form_display = EntityFormDisplay::collectRenderDisplay($media, 'media_library');
|
||||
$form_display->extractFormValues($media, $form['media'][$i]['fields'], $form_state);
|
||||
$form_display->validateFormValues($media, $form['media'][$i]['fields'], $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach ($this->media as $i => $media) {
|
||||
EntityFormDisplay::collectRenderDisplay($media, 'media_library')
|
||||
->extractFormValues($media, $form['media'][$i]['fields'], $form_state);
|
||||
$source_field = $media->getSource()->getSourceFieldDefinition($media->bundle->entity)->getName();
|
||||
/** @var \Drupal\file\FileInterface $file */
|
||||
$file = $media->get($source_field)->entity;
|
||||
$file->setPermanent();
|
||||
$file->save();
|
||||
$media->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to select a media type for a file.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
|
||||
* If the triggering element is missing required properties.
|
||||
*/
|
||||
public function selectType(array &$form, FormStateInterface $form_state) {
|
||||
$element = $form_state->getTriggeringElement();
|
||||
if (!isset($element['#media_library_index']) || !isset($element['#media_library_type'])) {
|
||||
throw new BadRequestHttpException('The "#media_library_index" and "#media_library_type" properties on the triggering element are required for type selection.');
|
||||
}
|
||||
$i = $element['#media_library_index'];
|
||||
$type = $element['#media_library_type'];
|
||||
$this->media[] = $this->createMediaEntity($this->files[$i], $this->getTypes()[$type]);
|
||||
unset($this->files[$i]);
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to update the field widget.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse
|
||||
* A command to send the selection to the current field widget.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
|
||||
* If the "media_library_widget_id" query parameter is not present.
|
||||
*/
|
||||
public function updateWidget(array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getErrors()) {
|
||||
return $form;
|
||||
}
|
||||
$widget_id = $this->getRequest()->query->get('media_library_widget_id');
|
||||
if (!$widget_id || !is_string($widget_id)) {
|
||||
throw new BadRequestHttpException('The "media_library_widget_id" query parameter is required and must be a string.');
|
||||
}
|
||||
$mids = array_map(function (MediaInterface $media) {
|
||||
return $media->id();
|
||||
}, $this->media);
|
||||
// Pass the selection to the field widget based on the current widget ID.
|
||||
return (new AjaxResponse())
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$widget_id\"]", 'val', [implode(',', $mids)]))
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-update=\"$widget_id\"]", 'trigger', ['mousedown']))
|
||||
->addCommand(new CloseDialogCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an upload (managed_file) element.
|
||||
*
|
||||
* @param array $element
|
||||
* The upload element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* The processed upload element.
|
||||
*/
|
||||
public function processUploadElement(array $element, FormStateInterface $form_state) {
|
||||
$element['upload_button']['#submit'] = ['::uploadButtonSubmit'];
|
||||
$element['upload_button']['#ajax'] = [
|
||||
'callback' => '::updateFormCallback',
|
||||
'wrapper' => 'media-library-upload-wrapper',
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the upload element.
|
||||
*
|
||||
* @param array $element
|
||||
* The upload element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* The processed upload element.
|
||||
*/
|
||||
public function validateUploadElement(array $element, FormStateInterface $form_state) {
|
||||
if ($form_state->getErrors()) {
|
||||
$element['#value'] = [];
|
||||
}
|
||||
$values = $form_state->getValue('upload', []);
|
||||
if (count($values['fids']) > $element['#cardinality'] && $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) {
|
||||
$allowed_types = _media_library_get_allowed_types() ?: NULL;
|
||||
}
|
||||
$types = $media_type_storage->loadMultiple($allowed_types);
|
||||
$types = $this->filterTypesWithFileSource($types);
|
||||
$types = $this->filterTypesWithCreateAccess($types);
|
||||
$this->types = $types;
|
||||
}
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters media types that accept a given file.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\file\FileInterface $file
|
||||
* A file entity.
|
||||
* @param \Drupal\media\MediaTypeInterface[] $types
|
||||
* An array of available media types.
|
||||
*
|
||||
* @return \Drupal\media\MediaTypeInterface[]
|
||||
* An array of media types that accept the file.
|
||||
*/
|
||||
protected function filterTypesThatAcceptFile(FileInterface $file, array $types) {
|
||||
$types = $this->filterTypesWithFileSource($types);
|
||||
return array_filter($types, function (MediaTypeInterface $type) use ($file) {
|
||||
$validators = $this->getUploadValidatorsForType($type);
|
||||
$errors = file_validate($file, $validators);
|
||||
return empty($errors);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an array of media types that accept file sources.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface[] $types
|
||||
* An array of media types.
|
||||
*
|
||||
* @return \Drupal\media\MediaTypeInterface[]
|
||||
* An array of media types that accept file sources.
|
||||
*/
|
||||
protected function filterTypesWithFileSource(array $types) {
|
||||
return array_filter($types, function (MediaTypeInterface $type) {
|
||||
return is_a($type->getSource()->getSourceFieldDefinition($type)->getClass(), FileFieldItemList::class, TRUE);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges file upload validators for an array of media types.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface[] $types
|
||||
* An array of media types.
|
||||
*
|
||||
* @return array
|
||||
* An array suitable for passing to file_save_upload() or the file field
|
||||
* element's '#upload_validators' property.
|
||||
*/
|
||||
protected function mergeUploadValidators(array $types) {
|
||||
$max_size = 0;
|
||||
$extensions = [];
|
||||
$types = $this->filterTypesWithFileSource($types);
|
||||
foreach ($types as $type) {
|
||||
$validators = $this->getUploadValidatorsForType($type);
|
||||
if (isset($validators['file_validate_size'])) {
|
||||
$max_size = max($max_size, $validators['file_validate_size'][0]);
|
||||
}
|
||||
if (isset($validators['file_validate_extensions'])) {
|
||||
$extensions = array_unique(array_merge($extensions, explode(' ', $validators['file_validate_extensions'][0])));
|
||||
}
|
||||
}
|
||||
// If no field defines a max size, default to the system wide setting.
|
||||
if ($max_size === 0) {
|
||||
$max_size = file_upload_max_size();
|
||||
}
|
||||
return [
|
||||
'file_validate_extensions' => [implode(' ', $extensions)],
|
||||
'file_validate_size' => [$max_size],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets upload validators for a given media type.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $type
|
||||
* A media type.
|
||||
*
|
||||
* @return array
|
||||
* An array suitable for passing to file_save_upload() or the file field
|
||||
* element's '#upload_validators' property.
|
||||
*/
|
||||
protected function getUploadValidatorsForType(MediaTypeInterface $type) {
|
||||
return $this->getFileItemForType($type)->getUploadValidators();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets upload destination for a given media type.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $type
|
||||
* A media type.
|
||||
*
|
||||
* @return string
|
||||
* An unsanitized file directory URI with tokens replaced.
|
||||
*/
|
||||
protected function getUploadLocationForType(MediaTypeInterface $type) {
|
||||
return $this->getFileItemForType($type)->getUploadLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file item for a given media type.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface $type
|
||||
* A media type.
|
||||
*
|
||||
* @return \Drupal\file\Plugin\Field\FieldType\FileItem
|
||||
* The file item.
|
||||
*/
|
||||
protected function getFileItemForType(MediaTypeInterface $type) {
|
||||
$source = $type->getSource();
|
||||
$source_data_definition = FieldItemDataDefinition::create($source->getSourceFieldDefinition($type));
|
||||
return new FileItem($source_data_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an array of media types that can be created by the current user.
|
||||
*
|
||||
* @todo Move in https://www.drupal.org/node/2987924
|
||||
*
|
||||
* @param \Drupal\media\MediaTypeInterface[] $types
|
||||
* An array of media types.
|
||||
*
|
||||
* @return \Drupal\media\MediaTypeInterface[]
|
||||
* An array of media types that accept file sources.
|
||||
*/
|
||||
protected function filterTypesWithCreateAccess(array $types) {
|
||||
$access_handler = $this->entityTypeManager->getAccessControlHandler('media');
|
||||
return array_filter($types, function (MediaTypeInterface $type) use ($access_handler) {
|
||||
return $access_handler->createAccess($type->id());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,539 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\media_library\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\SortArray;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\media\Entity\Media;
|
||||
use Drupal\media_library\Form\MediaLibraryUploadForm;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'media_library_widget' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "media_library_widget",
|
||||
* label = @Translation("Media library"),
|
||||
* description = @Translation("Allows you to select items from the media library."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* },
|
||||
* multiple_values = TRUE,
|
||||
* )
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MediaLibraryWidget extends WidgetBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Entity type manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the add button should be shown.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $addAccess = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a MediaLibraryWidget widget.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The definition of the field to which the widget is associated.
|
||||
* @param array $settings
|
||||
* The widget settings.
|
||||
* @param array $third_party_settings
|
||||
* Any third party settings.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* Entity type manager service.
|
||||
* @param bool $add_access
|
||||
* Indicates whether or not the add button should be shown.
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager, $add_access) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->addAccess = $add_access;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
$settings = $configuration['field_definition']->getSettings()['handler_settings'];
|
||||
$target_bundles = isset($settings['target_bundles']) ? $settings['target_bundles'] : NULL;
|
||||
return new static(
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$configuration['field_definition'],
|
||||
$configuration['settings'],
|
||||
$configuration['third_party_settings'],
|
||||
$container->get('entity_type.manager'),
|
||||
// @todo Use URL access in https://www.drupal.org/node/2956747
|
||||
MediaLibraryUploadForm::create($container)->access($target_bundles)->isAllowed()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isApplicable(FieldDefinitionInterface $field_definition) {
|
||||
return $field_definition->getSetting('target_type') === 'media';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
|
||||
// Load the items for form rebuilds from the field state.
|
||||
$field_state = static::getWidgetState($form['#parents'], $this->fieldDefinition->getName(), $form_state);
|
||||
if (isset($field_state['items'])) {
|
||||
usort($field_state['items'], [SortArray::class, 'sortByWeightElement']);
|
||||
$items->setValue($field_state['items']);
|
||||
}
|
||||
|
||||
return parent::form($items, $form, $form_state, $get_delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */
|
||||
$referenced_entities = $items->referencedEntities();
|
||||
$view_builder = $this->entityTypeManager->getViewBuilder('media');
|
||||
$field_name = $this->fieldDefinition->getName();
|
||||
$parents = $form['#parents'];
|
||||
$id_suffix = '-' . implode('-', $parents);
|
||||
$wrapper_id = $field_name . '-media-library-wrapper' . $id_suffix;
|
||||
$limit_validation_errors = [array_merge($parents, [$field_name])];
|
||||
|
||||
$settings = $this->getFieldSetting('handler_settings');
|
||||
$element += [
|
||||
'#type' => 'fieldset',
|
||||
'#cardinality' => $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(),
|
||||
'#target_bundles' => isset($settings['target_bundles']) ? $settings['target_bundles'] : FALSE,
|
||||
'#attributes' => [
|
||||
'id' => $wrapper_id,
|
||||
'class' => ['media-library-widget'],
|
||||
],
|
||||
'#attached' => [
|
||||
'library' => ['media_library/widget'],
|
||||
],
|
||||
];
|
||||
|
||||
$element['selection'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'js-media-library-selection',
|
||||
'media-library-selection',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (empty($referenced_entities)) {
|
||||
$element['empty_selection'] = [
|
||||
'#markup' => $this->t('<p>No media items are selected.</p>'),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$element['weight_toggle'] = [
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'button',
|
||||
'#value' => $this->t('Show media item weights'),
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'link',
|
||||
'media-library-widget__toggle-weight',
|
||||
'js-media-library-widget-toggle-weight',
|
||||
],
|
||||
'title' => $this->t('Re-order media by numerical weight instead of dragging'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($referenced_entities as $delta => $media_item) {
|
||||
$element['selection'][$delta] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'media-library-item',
|
||||
'js-media-library-item',
|
||||
],
|
||||
],
|
||||
'preview' => [
|
||||
'#type' => 'container',
|
||||
// @todo Make the view mode configurable in https://www.drupal.org/project/drupal/issues/2971209
|
||||
'rendered_entity' => $view_builder->view($media_item, 'media_library'),
|
||||
'remove_button' => [
|
||||
'#type' => 'submit',
|
||||
'#name' => $field_name . '-' . $delta . '-media-library-remove-button' . $id_suffix,
|
||||
'#value' => $this->t('Remove'),
|
||||
'#attributes' => [
|
||||
'class' => ['media-library-item__remove'],
|
||||
],
|
||||
'#ajax' => [
|
||||
'callback' => [static::class, 'updateWidget'],
|
||||
'wrapper' => $wrapper_id,
|
||||
],
|
||||
'#submit' => [[static::class, 'removeItem']],
|
||||
// Prevent errors in other widgets from preventing removal.
|
||||
'#limit_validation_errors' => $limit_validation_errors,
|
||||
],
|
||||
],
|
||||
'target_id' => [
|
||||
'#type' => 'hidden',
|
||||
'#value' => $media_item->id(),
|
||||
],
|
||||
// This hidden value can be toggled visible for accessibility.
|
||||
'weight' => [
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Weight'),
|
||||
'#default_value' => $delta,
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'js-media-library-item-weight',
|
||||
'media-library-item__weight',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$cardinality_unlimited = ($element['#cardinality'] === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$remaining = $element['#cardinality'] - count($referenced_entities);
|
||||
|
||||
// Inform the user of how many items are remaining.
|
||||
if (!$cardinality_unlimited) {
|
||||
if ($remaining) {
|
||||
$cardinality_message = $this->formatPlural($remaining, 'One media item remaining.', '@count media items remaining.');
|
||||
}
|
||||
else {
|
||||
$cardinality_message = $this->t('The maximum number of media items have been selected.');
|
||||
}
|
||||
$element['#description'] .= '<br />' . $cardinality_message;
|
||||
}
|
||||
|
||||
$query = [
|
||||
'media_library_widget_id' => $field_name . $id_suffix,
|
||||
'media_library_allowed_types' => $element['#target_bundles'],
|
||||
'media_library_remaining' => $cardinality_unlimited ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : $remaining,
|
||||
];
|
||||
$dialog_options = Json::encode([
|
||||
'dialogClass' => 'media-library-widget-modal',
|
||||
'height' => '75%',
|
||||
'width' => '75%',
|
||||
'title' => $this->t('Media library'),
|
||||
]);
|
||||
|
||||
// Add a button that will load the Media library in a modal using AJAX.
|
||||
$element['media_library_open_button'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Browse media'),
|
||||
'#name' => $field_name . '-media-library-open-button' . $id_suffix,
|
||||
// @todo Make the view configurable in https://www.drupal.org/project/drupal/issues/2971209
|
||||
'#url' => Url::fromRoute('view.media_library.widget', [], [
|
||||
'query' => $query,
|
||||
]),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'use-ajax', 'media-library-open-button'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => $dialog_options,
|
||||
],
|
||||
// Prevent errors in other widgets from preventing addition.
|
||||
'#limit_validation_errors' => $limit_validation_errors,
|
||||
'#access' => $cardinality_unlimited || $remaining > 0,
|
||||
];
|
||||
|
||||
$element['media_library_add_button'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Add media'),
|
||||
'#name' => $field_name . '-media-library-add-button' . $id_suffix,
|
||||
'#url' => Url::fromRoute('media_library.upload', [], [
|
||||
'query' => $query,
|
||||
]),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'use-ajax', 'media-library-add-button'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => $dialog_options,
|
||||
],
|
||||
// Prevent errors in other widgets from preventing addition.
|
||||
'#limit_validation_errors' => $limit_validation_errors,
|
||||
'#access' => $this->addAccess && ($cardinality_unlimited || $remaining > 0),
|
||||
];
|
||||
|
||||
// This hidden field and button are used to add new items to the widget.
|
||||
$element['media_library_selection'] = [
|
||||
'#type' => 'hidden',
|
||||
'#attributes' => [
|
||||
// This is used to pass the selection from the modal to the widget.
|
||||
'data-media-library-widget-value' => $field_name . $id_suffix,
|
||||
],
|
||||
];
|
||||
|
||||
// When a selection is made this hidden button is pressed to add new media
|
||||
// items based on the "media_library_selection" value.
|
||||
$element['media_library_update_widget'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Update widget'),
|
||||
'#name' => $field_name . '-media-library-update' . $id_suffix,
|
||||
'#ajax' => [
|
||||
'callback' => [static::class, 'updateWidget'],
|
||||
'wrapper' => $wrapper_id,
|
||||
],
|
||||
'#attributes' => [
|
||||
'data-media-library-widget-update' => $field_name . $id_suffix,
|
||||
'class' => ['js-hide'],
|
||||
],
|
||||
'#validate' => [[static::class, 'validateItems']],
|
||||
'#submit' => [[static::class, 'updateItems']],
|
||||
// Prevent errors in other widgets from preventing updates.
|
||||
'#limit_validation_errors' => $limit_validation_errors,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
|
||||
return isset($element['target_id']) ? $element['target_id'] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
|
||||
if (isset($values['selection'])) {
|
||||
usort($values['selection'], [SortArray::class, 'sortByWeightElement']);
|
||||
return $values['selection'];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to update the widget when the selection changes.
|
||||
*
|
||||
* @param array $form
|
||||
* The form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* An array representing the updated widget.
|
||||
*/
|
||||
public static function updateWidget(array $form, FormStateInterface $form_state) {
|
||||
$triggering_element = $form_state->getTriggeringElement();
|
||||
// This callback is either invoked from the remove button or the update
|
||||
// button, which have different nesting levels.
|
||||
$length = end($triggering_element['#parents']) === 'remove_button' ? -4 : -1;
|
||||
if (count($triggering_element['#array_parents']) < abs($length)) {
|
||||
throw new \LogicException('The element that triggered the widget update was at an unexpected depth. Triggering element parents were: ' . implode(',', $triggering_element['#array_parents']));
|
||||
}
|
||||
$parents = array_slice($triggering_element['#array_parents'], 0, $length);
|
||||
$element = NestedArray::getValue($form, $parents);
|
||||
// Always clear the textfield selection to prevent duplicate additions.
|
||||
$element['media_library_selection']['#value'] = '';
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for remove buttons.
|
||||
*
|
||||
* @param array $form
|
||||
* The form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*/
|
||||
public static function removeItem(array $form, FormStateInterface $form_state) {
|
||||
$triggering_element = $form_state->getTriggeringElement();
|
||||
|
||||
// Get the parents required to find the top-level widget element.
|
||||
if (count($triggering_element['#array_parents']) < 4) {
|
||||
throw new \LogicException('Expected the remove button to be more than four levels deep in the form. Triggering element parents were: ' . implode(',', $triggering_element['#array_parents']));
|
||||
}
|
||||
$parents = array_slice($triggering_element['#array_parents'], 0, -4);
|
||||
// Get the delta of the item being removed.
|
||||
$delta = array_slice($triggering_element['#array_parents'], -3, 1)[0];
|
||||
$element = NestedArray::getValue($form, $parents);
|
||||
|
||||
// Get the field state.
|
||||
$path = $element['#parents'];
|
||||
$values = NestedArray::getValue($form_state->getValues(), $path);
|
||||
$field_state = static::getFieldState($element, $form_state);
|
||||
|
||||
// Remove the item from the field state and update it.
|
||||
if (isset($values['selection'][$delta])) {
|
||||
array_splice($values['selection'], $delta, 1);
|
||||
$field_state['items'] = $values['selection'];
|
||||
static::setFieldState($element, $form_state, $field_state);
|
||||
}
|
||||
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that newly selected items can be added to the widget.
|
||||
*
|
||||
* Making an invalid selection from the view should not be possible, but we
|
||||
* still validate in case other selection methods (ex: upload) are valid.
|
||||
*
|
||||
* @param array $form
|
||||
* The form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*/
|
||||
public static function validateItems(array $form, FormStateInterface $form_state) {
|
||||
$button = $form_state->getTriggeringElement();
|
||||
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
|
||||
|
||||
$field_state = static::getFieldState($element, $form_state);
|
||||
$media = static::getNewMediaItems($element, $form_state);
|
||||
if (empty($media)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if more items were selected than we allow.
|
||||
$cardinality_unlimited = ($element['#cardinality'] === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$selection = count($field_state['items']) + count($media);
|
||||
if (!$cardinality_unlimited && ($selection > $element['#cardinality'])) {
|
||||
$form_state->setError($element, \Drupal::translation()->formatPlural($element['#cardinality'], 'Only one item can be selected.', 'Only @count items can be selected.'));
|
||||
}
|
||||
|
||||
// Validate that each selected media is of an allowed bundle.
|
||||
$all_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('media');
|
||||
$bundle_labels = array_map(function ($bundle) use ($all_bundles) {
|
||||
return $all_bundles[$bundle]['label'];
|
||||
}, $element['#target_bundles']);
|
||||
foreach ($media as $media_item) {
|
||||
if ($element['#target_bundles'] && !in_array($media_item->bundle(), $element['#target_bundles'], TRUE)) {
|
||||
$form_state->setError($element, t('The media item "@label" is not of an accepted type. Allowed types: @types', [
|
||||
'@label' => $media_item->label(),
|
||||
'@types' => implode(', ', $bundle_labels),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the field state and flags the form for rebuild.
|
||||
*
|
||||
* @param array $form
|
||||
* The form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*/
|
||||
public static function updateItems(array $form, FormStateInterface $form_state) {
|
||||
$button = $form_state->getTriggeringElement();
|
||||
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
|
||||
|
||||
$field_state = static::getFieldState($element, $form_state);
|
||||
|
||||
$media = static::getNewMediaItems($element, $form_state);
|
||||
if (!empty($media)) {
|
||||
$weight = count($field_state['items']);
|
||||
foreach ($media as $media_item) {
|
||||
// Any ID can be passed to the widget, so we have to check access.
|
||||
if ($media_item->access('view')) {
|
||||
$field_state['items'][] = [
|
||||
'target_id' => $media_item->id(),
|
||||
'weight' => $weight++,
|
||||
];
|
||||
}
|
||||
}
|
||||
static::setFieldState($element, $form_state, $field_state);
|
||||
}
|
||||
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets newly selected media items.
|
||||
*
|
||||
* @param array $element
|
||||
* The wrapping element for this widget.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return \Drupal\media\MediaInterface[]
|
||||
* An array of selected media items.
|
||||
*/
|
||||
protected static function getNewMediaItems(array $element, FormStateInterface $form_state) {
|
||||
// Get the new media IDs passed to our hidden button.
|
||||
$values = $form_state->getValues();
|
||||
$path = $element['#parents'];
|
||||
$value = NestedArray::getValue($values, $path);
|
||||
|
||||
if (!empty($value['media_library_selection'])) {
|
||||
$ids = explode(',', $value['media_library_selection']);
|
||||
$ids = array_filter($ids, 'is_numeric');
|
||||
if (!empty($ids)) {
|
||||
/** @var \Drupal\media\MediaInterface[] $media */
|
||||
return Media::loadMultiple($ids);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the field state for the widget.
|
||||
*
|
||||
* @param array $element
|
||||
* The wrapping element for this widget.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array[]
|
||||
* An array of arrays with the following key/value pairs:
|
||||
* - items: (array) An array of selections.
|
||||
* - target_id: (int) A media entity ID.
|
||||
* - weight: (int) A weight for the selection.
|
||||
*/
|
||||
protected static function getFieldState(array $element, FormStateInterface $form_state) {
|
||||
// Default to using the current selection if the form is new.
|
||||
$path = $element['#parents'];
|
||||
$values = NestedArray::getValue($form_state->getValues(), $path);
|
||||
$selection = isset($values['selection']) ? $values['selection'] : [];
|
||||
|
||||
$widget_state = static::getWidgetState($element['#field_parents'], $element['#field_name'], $form_state);
|
||||
$widget_state['items'] = isset($widget_state['items']) ? $widget_state['items'] : $selection;
|
||||
return $widget_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field state for the widget.
|
||||
*
|
||||
* @param array $element
|
||||
* The wrapping element for this widget.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array[] $field_state
|
||||
* An array of arrays with the following key/value pairs:
|
||||
* - items: (array) An array of selections.
|
||||
* - target_id: (int) A media entity ID.
|
||||
* - weight: (int) A weight for the selection.
|
||||
*/
|
||||
protected static function setFieldState(array $element, FormStateInterface $form_state, array $field_state) {
|
||||
static::setWidgetState($element['#field_parents'], $element['#field_name'], $form_state, $field_state);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\media_library\Plugin\views\field;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\CloseDialogCommand;
|
||||
use Drupal\Core\Ajax\InvokeCommand;
|
||||
use Drupal\Core\Form\FormBuilderInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\Plugin\views\field\FieldPluginBase;
|
||||
use Drupal\views\Render\ViewsRenderPipelineMarkup;
|
||||
use Drupal\views\ResultRow;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
/**
|
||||
* Defines a field that outputs a checkbox and form for selecting media.
|
||||
*
|
||||
* @ViewsField("media_library_select_form")
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MediaLibrarySelectForm extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue(ResultRow $row, $field = NULL) {
|
||||
return '<!--form-item-' . $this->options['id'] . '--' . $row->index . '-->';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(ResultRow $values) {
|
||||
return ViewsRenderPipelineMarkup::create($this->getValue($values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor for the media library select form.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function viewsForm(array &$form, FormStateInterface $form_state) {
|
||||
// Only add the bulk form options and buttons if there are results.
|
||||
if (empty($this->view->result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render checkboxes for all rows.
|
||||
$form[$this->options['id']]['#tree'] = TRUE;
|
||||
foreach ($this->view->result as $row_index => $row) {
|
||||
$entity = $this->getEntity($row);
|
||||
$form[$this->options['id']][$row_index] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Select @label', [
|
||||
'@label' => $entity->label(),
|
||||
]),
|
||||
'#title_display' => 'invisible',
|
||||
'#return_value' => $entity->id(),
|
||||
];
|
||||
}
|
||||
|
||||
// @todo Remove in https://www.drupal.org/project/drupal/issues/2504115
|
||||
// Currently the default URL for all AJAX form elements is the current URL,
|
||||
// not the form action. This causes bugs when this form is rendered from an
|
||||
// AJAX path like /views/ajax, which cannot process AJAX form submits.
|
||||
$url = parse_url($form['#action'], PHP_URL_PATH);
|
||||
$query = \Drupal::request()->query->all();
|
||||
$query[FormBuilderInterface::AJAX_FORM_REQUEST] = TRUE;
|
||||
$form['actions']['submit']['#ajax'] = [
|
||||
'url' => Url::fromUserInput($url),
|
||||
'options' => [
|
||||
'query' => $query,
|
||||
],
|
||||
'callback' => [static::class, 'updateWidget'],
|
||||
];
|
||||
|
||||
$form['actions']['submit']['#value'] = $this->t('Select media');
|
||||
$form['actions']['submit']['#field_id'] = $this->options['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the media library select form.
|
||||
*
|
||||
* @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 static function updateWidget(array &$form, FormStateInterface $form_state) {
|
||||
$widget_id = \Drupal::request()->query->get('media_library_widget_id');
|
||||
if (!$widget_id || !is_string($widget_id)) {
|
||||
throw new BadRequestHttpException('The "media_library_widget_id" query parameter is required and must be a string.');
|
||||
}
|
||||
$field_id = $form_state->getTriggeringElement()['#field_id'];
|
||||
$selected = array_values(array_filter($form_state->getValue($field_id, [])));
|
||||
// Pass the selection to the field widget based on the current widget ID.
|
||||
return (new AjaxResponse())
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$widget_id\"]", 'val', [implode(',', $selected)]))
|
||||
->addCommand(new InvokeCommand("[data-media-library-widget-update=\"$widget_id\"]", 'trigger', ['mousedown']))
|
||||
->addCommand(new CloseDialogCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewsFormValidate(array &$form, FormStateInterface $form_state) {
|
||||
$selected = array_filter($form_state->getValue($this->options['id']));
|
||||
if (empty($selected)) {
|
||||
$form_state->setErrorByName('', $this->t('No items selected.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clickSortable() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to present a media entity in the media library.
|
||||
*
|
||||
* Available variables:
|
||||
* - media: The entity with limited access to object properties and methods.
|
||||
* Only method names starting with "get", "has", or "is" and a few common
|
||||
* methods such as "id", "label", and "bundle" are available. For example:
|
||||
* - entity.getEntityTypeId() will return the entity type ID.
|
||||
* - entity.hasField('field_example') returns TRUE if the entity includes
|
||||
* field_example. (This does not indicate the presence of a value in this
|
||||
* field.)
|
||||
* Calling other methods, such as entity.delete(), will result in an exception.
|
||||
* See \Drupal\Core\Entity\EntityInterface for a full list of methods.
|
||||
* - name: Name of the media.
|
||||
* - content: Media content.
|
||||
* - title_prefix: Additional output populated by modules, intended to be
|
||||
* displayed in front of the main title tag that appears in the template.
|
||||
* - title_suffix: Additional output populated by modules, intended to be
|
||||
* displayed after the main title tag that appears in the template.
|
||||
* - view_mode: View mode; for example, "teaser" or "full".
|
||||
* - attributes: HTML attributes for the containing element.
|
||||
* - title_attributes: Same as attributes, except applied to the main title
|
||||
* tag that appears in the template.
|
||||
* - url: Direct URL of the media.
|
||||
* - preview_attributes: HTML attributes for the preview wrapper.
|
||||
* - metadata_attributes: HTML attributes for the expandable metadata area.
|
||||
* - status: Whether or not the Media is published.
|
||||
*
|
||||
* @see template_preprocess_media()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<article{{ attributes }}>
|
||||
{% if content %}
|
||||
<div{{ preview_attributes }}>
|
||||
{{ content|without('name') }}
|
||||
</div>
|
||||
{% if not status %}
|
||||
<div class="media-library-item__status">{{ "unpublished" | t }}</div>
|
||||
{% endif %}
|
||||
<div{{ metadata_attributes }}>
|
||||
<div class="media-library-item__name">
|
||||
<a href="{{ url }}" rel="bookmark">{{ name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
|
@ -0,0 +1,69 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_four.field_media_test_image
|
||||
- field.field.media.type_four.field_media_extra_image
|
||||
- image.style.medium
|
||||
- media.type.type_four
|
||||
module:
|
||||
- image
|
||||
- path
|
||||
id: media.type_four.default
|
||||
targetEntityType: media
|
||||
bundle: type_four
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_media_test_image:
|
||||
weight: 0
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: medium
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
field_media_extra_image:
|
||||
weight: 1
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: medium
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
path:
|
||||
type: path
|
||||
weight: 30
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 100
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,44 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.type_four.field_media_test_image
|
||||
- image.style.thumbnail
|
||||
- media.type.type_four
|
||||
module:
|
||||
- image
|
||||
id: media.type_four.media_library
|
||||
targetEntityType: media
|
||||
bundle: type_four
|
||||
mode: media_library
|
||||
content:
|
||||
field_media_test_image:
|
||||
weight: 2
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: thumbnail
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
field_media_extra_image:
|
||||
weight: 1
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: medium
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,43 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_one.field_media_test
|
||||
- media.type.type_one
|
||||
id: media.type_one.default
|
||||
targetEntityType: media
|
||||
bundle: type_one
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_media_test:
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: string_textfield
|
||||
weight: 11
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,60 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_three.field_media_test_image
|
||||
- image.style.medium
|
||||
- media.type.type_three
|
||||
module:
|
||||
- image
|
||||
- path
|
||||
id: media.type_three.default
|
||||
targetEntityType: media
|
||||
bundle: type_three
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_media_test_image:
|
||||
weight: 0
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: medium
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
path:
|
||||
type: path
|
||||
weight: 30
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 100
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,36 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_form_mode.media.media_library
|
||||
- field.field.media.type_three.field_media_test_image
|
||||
- image.style.thumbnail
|
||||
- media.type.type_three
|
||||
module:
|
||||
- image
|
||||
id: media.type_three.media_library
|
||||
targetEntityType: media
|
||||
bundle: type_three
|
||||
mode: media_library
|
||||
content:
|
||||
field_media_test_image:
|
||||
weight: 1
|
||||
settings:
|
||||
progress_indicator: throbber
|
||||
preview_image_style: thumbnail
|
||||
third_party_settings: { }
|
||||
type: image_image
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
created: true
|
||||
path: true
|
||||
status: true
|
||||
uid: true
|
|
@ -0,0 +1,43 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_two.field_media_test_1
|
||||
- media.type.type_two
|
||||
id: media.type_two.default
|
||||
targetEntityType: media
|
||||
bundle: type_two
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_media_test_1:
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: string_textfield
|
||||
weight: 11
|
||||
region: content
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,78 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.node.basic_page.field_twin_media
|
||||
- field.field.node.basic_page.field_unlimited_media
|
||||
- field.field.node.basic_page.field_noadd_media
|
||||
- node.type.basic_page
|
||||
module:
|
||||
- media_library
|
||||
id: node.basic_page.default
|
||||
targetEntityType: node
|
||||
bundle: basic_page
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_twin_media:
|
||||
type: media_library_widget
|
||||
weight: 122
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
field_unlimited_media:
|
||||
type: media_library_widget
|
||||
weight: 121
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
field_noadd_media:
|
||||
type: media_library_widget
|
||||
weight: 123
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
promote:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 15
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 120
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
sticky:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 16
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
title:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,50 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_one.field_media_test
|
||||
- image.style.thumbnail
|
||||
- media.type.type_one
|
||||
module:
|
||||
- image
|
||||
- user
|
||||
id: media.type_one.default
|
||||
targetEntityType: media
|
||||
bundle: type_one
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
label: hidden
|
||||
type: timestamp
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
date_format: medium
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
third_party_settings: { }
|
||||
field_media_test:
|
||||
label: above
|
||||
settings:
|
||||
link_to_entity: true
|
||||
third_party_settings: { }
|
||||
type: string
|
||||
weight: 6
|
||||
region: content
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 5
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
label: hidden
|
||||
type: author
|
||||
weight: 0
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,52 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_three.field_media_test_image
|
||||
- image.style.thumbnail
|
||||
- media.type.type_three
|
||||
module:
|
||||
- image
|
||||
- user
|
||||
id: media.type_three.default
|
||||
targetEntityType: media
|
||||
bundle: type_three
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
label: hidden
|
||||
type: timestamp
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
date_format: medium
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
third_party_settings: { }
|
||||
field_media_test_image:
|
||||
weight: 6
|
||||
label: above
|
||||
settings:
|
||||
image_style: ''
|
||||
image_link: ''
|
||||
third_party_settings: { }
|
||||
type: image
|
||||
region: content
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 5
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
label: hidden
|
||||
type: author
|
||||
weight: 0
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
name: true
|
|
@ -0,0 +1,50 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.media.type_two.field_media_test_1
|
||||
- image.style.thumbnail
|
||||
- media.type.type_two
|
||||
module:
|
||||
- image
|
||||
- user
|
||||
id: media.type_two.default
|
||||
targetEntityType: media
|
||||
bundle: type_two
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
label: hidden
|
||||
type: timestamp
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
date_format: medium
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
third_party_settings: { }
|
||||
field_media_test_1:
|
||||
label: above
|
||||
settings:
|
||||
link_to_entity: false
|
||||
third_party_settings: { }
|
||||
type: string
|
||||
weight: 6
|
||||
region: content
|
||||
thumbnail:
|
||||
type: image
|
||||
weight: 5
|
||||
label: hidden
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
label: hidden
|
||||
type: author
|
||||
weight: 0
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
|
@ -0,0 +1,48 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.node.basic_page.field_twin_media
|
||||
- field.field.node.basic_page.field_unlimited_media
|
||||
- field.field.node.basic_page.field_noadd_media
|
||||
- node.type.basic_page
|
||||
module:
|
||||
- user
|
||||
id: node.basic_page.default
|
||||
targetEntityType: node
|
||||
bundle: basic_page
|
||||
mode: default
|
||||
content:
|
||||
field_twin_media:
|
||||
type: entity_reference_entity_view
|
||||
weight: 102
|
||||
label: above
|
||||
settings:
|
||||
view_mode: default
|
||||
link: false
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
field_unlimited_media:
|
||||
type: entity_reference_entity_view
|
||||
weight: 101
|
||||
label: above
|
||||
settings:
|
||||
view_mode: default
|
||||
link: false
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
field_noadd_media:
|
||||
type: entity_reference_entity_view
|
||||
weight: 103
|
||||
label: above
|
||||
settings:
|
||||
view_mode: default
|
||||
link: false
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
links:
|
||||
weight: 100
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
hidden: { }
|
|
@ -0,0 +1,37 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.media.field_media_extra_image
|
||||
- media.type.type_four
|
||||
module:
|
||||
- image
|
||||
id: media.type_three.field_media_extra_image
|
||||
field_name: field_media_extra_image
|
||||
entity_type: media
|
||||
bundle: type_four
|
||||
label: Extra Image
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
file_extensions: 'jpg'
|
||||
alt_field: false
|
||||
alt_field_required: false
|
||||
title_field: false
|
||||
title_field_required: false
|
||||
max_resolution: ''
|
||||
min_resolution: ''
|
||||
default_image:
|
||||
uuid: null
|
||||
alt: ''
|
||||
title: ''
|
||||
width: null
|
||||
height: null
|
||||
file_directory: 'type-four-extra-dir'
|
||||
max_filesize: ''
|
||||
handler: 'default:file'
|
||||
handler_settings: { }
|
||||
field_type: image
|
|
@ -0,0 +1,37 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.media.field_media_test_image
|
||||
- media.type.type_four
|
||||
module:
|
||||
- image
|
||||
id: media.type_three.field_media_test_image
|
||||
field_name: field_media_test_image
|
||||
entity_type: media
|
||||
bundle: type_four
|
||||
label: Image
|
||||
description: ''
|
||||
required: true
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
file_extensions: 'jpg'
|
||||
alt_field: true
|
||||
alt_field_required: true
|
||||
title_field: false
|
||||
title_field_required: false
|
||||
max_resolution: ''
|
||||
min_resolution: ''
|
||||
default_image:
|
||||
uuid: null
|
||||
alt: ''
|
||||
title: ''
|
||||
width: null
|
||||
height: null
|
||||
file_directory: 'type-four-dir'
|
||||
max_filesize: ''
|
||||
handler: 'default:file'
|
||||
handler_settings: { }
|
||||
field_type: image
|
|
@ -0,0 +1,18 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.media.field_media_test
|
||||
- media.type.type_one
|
||||
id: media.type_one.field_media_test
|
||||
field_name: field_media_test
|
||||
entity_type: media
|
||||
bundle: type_one
|
||||
label: field_media_test
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
|
@ -0,0 +1,37 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.media.field_media_test_image
|
||||
- media.type.type_three
|
||||
module:
|
||||
- image
|
||||
id: media.type_three.field_media_test_image
|
||||
field_name: field_media_test_image
|
||||
entity_type: media
|
||||
bundle: type_three
|
||||
label: Image
|
||||
description: ''
|
||||
required: true
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
file_extensions: 'png gif jpg jpeg'
|
||||
alt_field: true
|
||||
alt_field_required: true
|
||||
title_field: false
|
||||
title_field_required: false
|
||||
max_resolution: ''
|
||||
min_resolution: ''
|
||||
default_image:
|
||||
uuid: null
|
||||
alt: ''
|
||||
title: ''
|
||||
width: null
|
||||
height: null
|
||||
file_directory: 'type-three-dir'
|
||||
max_filesize: ''
|
||||
handler: 'default:file'
|
||||
handler_settings: { }
|
||||
field_type: image
|
|
@ -0,0 +1,18 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.media.field_media_test_1
|
||||
- media.type.type_two
|
||||
id: media.type_two.field_media_test_1
|
||||
field_name: field_media_test_1
|
||||
entity_type: media
|
||||
bundle: type_two
|
||||
label: field_media_test_1
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_noadd_media
|
||||
- media.type.type_one
|
||||
- media.type.type_two
|
||||
- node.type.basic_page
|
||||
id: node.basic_page.field_noadd_media
|
||||
field_name: field_noadd_media
|
||||
entity_type: node
|
||||
bundle: basic_page
|
||||
label: 'No add media'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:media'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
type_one: type_one
|
||||
type_two: type_two
|
||||
sort:
|
||||
field: _none
|
||||
auto_create: false
|
||||
auto_create_bundle: file
|
||||
field_type: entity_reference
|
|
@ -0,0 +1,31 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_twin_media
|
||||
- media.type.type_one
|
||||
- media.type.type_two
|
||||
- node.type.basic_page
|
||||
id: node.basic_page.field_twin_media
|
||||
field_name: field_twin_media
|
||||
entity_type: node
|
||||
bundle: basic_page
|
||||
label: 'Twin media'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:media'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
type_one: type_one
|
||||
type_two: type_two
|
||||
type_three: type_three
|
||||
type_four: type_four
|
||||
sort:
|
||||
field: _none
|
||||
auto_create: false
|
||||
auto_create_bundle: file
|
||||
field_type: entity_reference
|
|
@ -0,0 +1,28 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_unlimited_media
|
||||
- media.type.type_one
|
||||
- node.type.basic_page
|
||||
id: node.basic_page.field_unlimited_media
|
||||
field_name: field_unlimited_media
|
||||
entity_type: node
|
||||
bundle: basic_page
|
||||
label: 'Unlimited media'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:media'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
type_one: type_one
|
||||
type_three: type_three
|
||||
sort:
|
||||
field: _none
|
||||
auto_create: false
|
||||
auto_create_bundle: audio
|
||||
field_type: entity_reference
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- file
|
||||
- image
|
||||
- media
|
||||
id: media.field_media_extra_image
|
||||
field_name: field_media_extra_image
|
||||
entity_type: media
|
||||
type: image
|
||||
settings:
|
||||
default_image:
|
||||
uuid: null
|
||||
alt: ''
|
||||
title: ''
|
||||
width: null
|
||||
height: null
|
||||
target_type: file
|
||||
display_field: false
|
||||
display_default: false
|
||||
uri_scheme: public
|
||||
module: image
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,20 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
id: media.field_media_test
|
||||
field_name: field_media_test
|
||||
entity_type: media
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,20 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
id: media.field_media_test_1
|
||||
field_name: field_media_test_1
|
||||
entity_type: media
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,29 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- file
|
||||
- image
|
||||
- media
|
||||
id: media.field_media_test_image
|
||||
field_name: field_media_test_image
|
||||
entity_type: media
|
||||
type: image
|
||||
settings:
|
||||
default_image:
|
||||
uuid: null
|
||||
alt: ''
|
||||
title: ''
|
||||
width: null
|
||||
height: null
|
||||
target_type: file
|
||||
display_field: false
|
||||
display_default: false
|
||||
uri_scheme: public
|
||||
module: image
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,19 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- node
|
||||
id: node.field_noadd_media
|
||||
field_name: field_noadd_media
|
||||
entity_type: node
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: media
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,19 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- node
|
||||
id: node.field_twin_media
|
||||
field_name: field_twin_media
|
||||
entity_type: node
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: media
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 2
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,19 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- node
|
||||
id: node.field_unlimited_media
|
||||
field_name: field_unlimited_media
|
||||
entity_type: node
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: media
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -0,0 +1,12 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: type_four
|
||||
label: 'Type Four'
|
||||
description: ''
|
||||
source: image
|
||||
queue_thumbnail_downloads: false
|
||||
new_revision: false
|
||||
source_configuration:
|
||||
source_field: field_media_test_image
|
||||
field_map: { }
|
|
@ -0,0 +1,16 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- media_test_source
|
||||
id: type_one
|
||||
label: 'Type One'
|
||||
description: ''
|
||||
source: test
|
||||
queue_thumbnail_downloads: false
|
||||
new_revision: false
|
||||
source_configuration:
|
||||
source_field: field_media_test
|
||||
test_config_value: 'This is default value.'
|
||||
field_map: { }
|
|
@ -0,0 +1,12 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: type_three
|
||||
label: 'Type Three'
|
||||
description: ''
|
||||
source: image
|
||||
queue_thumbnail_downloads: false
|
||||
new_revision: false
|
||||
source_configuration:
|
||||
source_field: field_media_test_image
|
||||
field_map: { }
|
|
@ -0,0 +1,16 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- media_test_source
|
||||
id: type_two
|
||||
label: 'Type Two'
|
||||
description: ''
|
||||
source: test
|
||||
queue_thumbnail_downloads: false
|
||||
new_revision: false
|
||||
source_configuration:
|
||||
source_field: field_media_test_1
|
||||
test_config_value: 'This is default value.'
|
||||
field_map: { }
|
|
@ -0,0 +1,9 @@
|
|||
langcode: en
|
||||
status: true
|
||||
name: 'Basic Page'
|
||||
type: basic_page
|
||||
description: ''
|
||||
help: ''
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
|
@ -0,0 +1,12 @@
|
|||
name: 'Media library test'
|
||||
type: module
|
||||
description: 'Test module for Media library.'
|
||||
package: Testing
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:image
|
||||
- drupal:media_library
|
||||
- drupal:media_test_source
|
||||
- drupal:menu_ui
|
||||
- drupal:node
|
||||
- drupal:path
|
|
@ -0,0 +1,445 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\media_library\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\media\Entity\Media;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Contains Media library integration tests.
|
||||
*
|
||||
* @group media_library
|
||||
*/
|
||||
class MediaLibraryTest extends WebDriverTestBase {
|
||||
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['block', 'media_library_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a few example media items for use in selection.
|
||||
$media = [
|
||||
'type_one' => [
|
||||
'Horse',
|
||||
'Bear',
|
||||
'Cat',
|
||||
'Dog',
|
||||
],
|
||||
'type_two' => [
|
||||
'Crocodile',
|
||||
'Lizard',
|
||||
'Snake',
|
||||
'Turtle',
|
||||
],
|
||||
];
|
||||
|
||||
$time = time();
|
||||
foreach ($media as $type => $names) {
|
||||
foreach ($names as $name) {
|
||||
$entity = Media::create(['name' => $name, 'bundle' => $type]);
|
||||
$source_field = $type === 'type_one' ? 'field_media_test' : 'field_media_test_1';
|
||||
$entity->setCreatedTime(++$time);
|
||||
$entity->set($source_field, $name);
|
||||
$entity->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a user who can use the Media library.
|
||||
$user = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access content',
|
||||
'access media overview',
|
||||
'edit own basic_page content',
|
||||
'create basic_page content',
|
||||
'create media',
|
||||
'delete any media',
|
||||
'view media',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Media library's administration page works as expected.
|
||||
*/
|
||||
public function testAdministrationPage() {
|
||||
$session = $this->getSession();
|
||||
$page = $session->getPage();
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
// Visit the administration page.
|
||||
$this->drupalGet('admin/content/media');
|
||||
|
||||
// Verify that the "Add media" link is present.
|
||||
$assert_session->linkExists('Add media');
|
||||
|
||||
// Verify that media from two separate types is present.
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
|
||||
// Test that users can filter by type.
|
||||
$page->selectFieldOption('Media type', 'Type One');
|
||||
$page->pressButton('Apply Filters');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextNotContains('Turtle');
|
||||
$page->selectFieldOption('Media type', 'Type Two');
|
||||
$page->pressButton('Apply Filters');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
|
||||
// Test that selecting elements as a part of bulk operations works.
|
||||
$page->selectFieldOption('Media type', '- Any -');
|
||||
$page->pressButton('Apply Filters');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// This tests that anchor tags clicked inside the preview are suppressed.
|
||||
$this->getSession()->executeScript('jQuery(".js-click-to-select-trigger a")[0].click()');
|
||||
$this->submitForm([], 'Apply to selected items');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextNotContains('Cat');
|
||||
$this->submitForm([], 'Delete');
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
$assert_session->pageTextContains('Cat');
|
||||
|
||||
// Test 'Select all media'.
|
||||
$this->getSession()->getPage()->checkField('Select all media');
|
||||
$this->getSession()->getPage()->selectFieldOption('Action', 'media_delete_action');
|
||||
$this->submitForm([], 'Apply to selected items');
|
||||
$this->getSession()->getPage()->pressButton('Delete');
|
||||
|
||||
$assert_session->pageTextNotContains('Cat');
|
||||
$assert_session->pageTextNotContains('Turtle');
|
||||
$assert_session->pageTextNotContains('Snake');
|
||||
|
||||
// Test empty text.
|
||||
$assert_session->pageTextContains('No media available.');
|
||||
|
||||
// Verify that the "Table" link is present, click it and check address.
|
||||
$assert_session->linkExists('Table');
|
||||
$page->clickLink('Table');
|
||||
$assert_session->addressEquals('admin/content/media-table');
|
||||
// Verify that the "Add media" link is present.
|
||||
$assert_session->linkExists('Add media');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Media library's widget works as expected.
|
||||
*/
|
||||
public function testWidget() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Visit a node create page.
|
||||
$this->drupalGet('node/add/basic_page');
|
||||
|
||||
// Verify that both media widget instances are present.
|
||||
$assert_session->pageTextContains('Unlimited media');
|
||||
$assert_session->pageTextContains('Twin media');
|
||||
|
||||
// Add to the unlimited cardinality field.
|
||||
$unlimited_button = $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]');
|
||||
$unlimited_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Assert that only type_one media items exist, since this field only
|
||||
// accepts items of that type.
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
$assert_session->pageTextNotContains('Turtle');
|
||||
// Ensure that the "Select all" checkbox is not visible.
|
||||
$this->assertFalse($assert_session->elementExists('css', '.media-library-select-all')->isVisible());
|
||||
// Use an exposed filter.
|
||||
$session = $this->getSession();
|
||||
$session->getPage()->fillField('Name', 'Dog');
|
||||
$session->getPage()->pressButton('Apply Filters');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextNotContains('Bear');
|
||||
// Clear the exposed filter.
|
||||
$session->getPage()->fillField('Name', '');
|
||||
$session->getPage()->pressButton('Apply Filters');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Select the first three media items (should be Dog/Cat/Bear).
|
||||
$checkbox_selector = '.media-library-view .js-click-to-select-checkbox input';
|
||||
$checkboxes = $page->findAll('css', $checkbox_selector);
|
||||
$checkboxes[0]->click();
|
||||
$checkboxes[1]->click();
|
||||
$checkboxes[2]->click();
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Ensure that the selection completed successfully.
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Cat');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
// Remove "Dog" (happens to be the first remove button on the page).
|
||||
$assert_session->elementExists('css', '.media-library-item__remove')->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
$assert_session->pageTextContains('Cat');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
|
||||
// Open another Media library on the same page.
|
||||
$twin_button = $assert_session->elementExists('css', '.media-library-open-button[href*="field_twin_media"]');
|
||||
$twin_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// This field allows both media types.
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
// Attempt to select three items - the cardinality of this field is two so
|
||||
// the third selection should be disabled.
|
||||
$checkbox_selector = '.media-library-view .js-click-to-select-checkbox input';
|
||||
$checkboxes = $page->findAll('css', $checkbox_selector);
|
||||
$this->assertFalse($checkboxes[5]->hasAttribute('disabled'));
|
||||
$checkboxes[0]->click();
|
||||
$checkboxes[7]->click();
|
||||
$this->assertTrue($checkboxes[5]->hasAttribute('disabled'));
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Ensure that the selection completed successfully, and we have only two
|
||||
// media items of two different types.
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains('Horse');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
$assert_session->pageTextNotContains('Snake');
|
||||
|
||||
// Finally, save the form.
|
||||
$assert_session->elementExists('css', '.js-media-library-widget-toggle-weight')->click();
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'My page',
|
||||
'field_unlimited_media[selection][0][weight]' => '2',
|
||||
], 'Save');
|
||||
$assert_session->pageTextContains('Basic Page My page has been created');
|
||||
// We removed this item earlier.
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
// This item should not have been selected due to cardinality constraints.
|
||||
$assert_session->pageTextNotContains('Snake');
|
||||
// "Cat" should come after "Bear", since we changed the weight.
|
||||
$assert_session->elementExists('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child:contains("Cat")');
|
||||
// Make sure everything that was selected shows up.
|
||||
$assert_session->pageTextContains('Cat');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
$assert_session->pageTextContains('Horse');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
|
||||
// Re-edit the content and make a new selection.
|
||||
$this->drupalGet('node/1/edit');
|
||||
$assert_session->pageTextNotContains('Dog');
|
||||
$assert_session->pageTextContains('Cat');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
$assert_session->pageTextContains('Horse');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
$unlimited_button = $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]');
|
||||
$unlimited_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
// Select the first media items (should be Dog, again).
|
||||
$checkbox_selector = '.media-library-view .js-click-to-select-checkbox input';
|
||||
$checkboxes = $page->findAll('css', $checkbox_selector);
|
||||
$checkboxes[0]->click();
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// "Dog" and the existing selection should still exist.
|
||||
$assert_session->pageTextContains('Dog');
|
||||
$assert_session->pageTextContains('Cat');
|
||||
$assert_session->pageTextContains('Bear');
|
||||
$assert_session->pageTextContains('Horse');
|
||||
$assert_session->pageTextContains('Turtle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the widget works as expected for anonymous users.
|
||||
*/
|
||||
public function testWidgetAnonymous() {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
$role = Role::load(RoleInterface::ANONYMOUS_ID);
|
||||
$role->revokePermission('view media');
|
||||
$role->save();
|
||||
|
||||
// Verify that unprivileged users can't access the widget view.
|
||||
$this->drupalGet('admin/content/media-widget');
|
||||
$assert_session->responseContains('Access denied');
|
||||
|
||||
// Allow the anonymous user to create pages and view media.
|
||||
$this->grantPermissions($role, [
|
||||
'access content',
|
||||
'create basic_page content',
|
||||
'view media',
|
||||
]);
|
||||
|
||||
// Ensure the widget works as an anonymous user.
|
||||
$this->drupalGet('node/add/basic_page');
|
||||
|
||||
// Add to the unlimited cardinality field.
|
||||
$unlimited_button = $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]');
|
||||
$unlimited_button->click();
|
||||
$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();
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Ensure that the selection completed successfully.
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
|
||||
// Save the form.
|
||||
$assert_session->elementExists('css', '.js-media-library-widget-toggle-weight')->click();
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'My page',
|
||||
'field_unlimited_media[selection][0][weight]' => '0',
|
||||
], 'Save');
|
||||
$assert_session->pageTextContains('Basic Page My page has been created');
|
||||
$assert_session->pageTextContains('Dog');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that uploads in the Media library's widget works as expected.
|
||||
*/
|
||||
public function testWidgetUpload() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
foreach ($this->getTestFiles('image') as $image) {
|
||||
$extension = pathinfo($image->filename, PATHINFO_EXTENSION);
|
||||
if ($extension === 'png') {
|
||||
$png_image = $image;
|
||||
}
|
||||
elseif ($extension === 'jpg') {
|
||||
$jpg_image = $image;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($png_image) || !isset($jpg_image)) {
|
||||
$this->fail('Expected test files not present.');
|
||||
}
|
||||
|
||||
// Visit a node create page.
|
||||
$this->drupalGet('node/add/basic_page');
|
||||
|
||||
$file_storage = $this->container->get('entity_type.manager')->getStorage('file');
|
||||
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
|
||||
$file_system = $this->container->get('file_system');
|
||||
|
||||
// Ensure that the add button is not present if no media can be created.
|
||||
$assert_session->elementNotExists('css', '.media-library-add-button[href*="field_noadd_media"]');
|
||||
|
||||
// Add to the twin media field using the add button directly on the widget.
|
||||
$twin_button = $assert_session->elementExists('css', '.media-library-add-button[href*="field_twin_media"]');
|
||||
$twin_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$page->attachFileToField('Upload', $this->container->get('file_system')->realpath($png_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Files are temporary until the form is saved.
|
||||
$files = $file_storage->loadMultiple();
|
||||
$file = array_pop($files);
|
||||
$this->assertSame('public://type-three-dir', $file_system->dirname($file->getFileUri()));
|
||||
$this->assertTrue($file->isTemporary());
|
||||
|
||||
$this->assertSame($assert_session->fieldExists('Name')->getValue(), $png_image->filename);
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Alternative text field is required');
|
||||
$page->fillField('Alternative text', $this->randomString());
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// The file should be permanent now.
|
||||
$files = $file_storage->loadMultiple();
|
||||
$file = array_pop($files);
|
||||
$this->assertFalse($file->isTemporary());
|
||||
|
||||
// Ensure the media item was added.
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains($png_image->filename);
|
||||
|
||||
// Also make sure that we can upload to the unlimited cardinality field.
|
||||
$unlimited_button = $assert_session->elementExists('css', '.media-library-add-button[href*="field_unlimited_media"]');
|
||||
$unlimited_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// 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'));
|
||||
|
||||
$page->attachFileToField('Upload', $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.
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains('Unlimited Cardinality Image');
|
||||
|
||||
// Open the browser again to test type resolution.
|
||||
$twin_button = $assert_session->elementExists('css', '.media-library-open-button[href*="field_twin_media"]');
|
||||
$twin_button->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContains('Media library');
|
||||
$assert_session->elementExists('css', '#drupal-modal')->clickLink('Add media');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$page->attachFileToField('Upload', $file_system->realpath($jpg_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$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_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.
|
||||
$assert_session->pageTextContains('Extra Image');
|
||||
$page->attachFileToField('Extra Image', $this->container->get('file_system')->realpath($jpg_image->uri));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Ensure that the extra image was uploaded to the correct directory.
|
||||
$files = $file_storage->loadMultiple();
|
||||
$file = array_pop($files);
|
||||
$this->assertSame('public://type-four-extra-dir', $file_system->dirname($file->getFileUri()));
|
||||
|
||||
$assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
$assert_session->pageTextNotContains('Media library');
|
||||
$assert_session->pageTextContains($jpg_image->filename);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue