diff --git a/core/composer.json b/core/composer.json index 7ae0ccf..e75d742 100644 --- a/core/composer.json +++ b/core/composer.json @@ -108,6 +108,7 @@ "drupal/link": "self.version", "drupal/locale": "self.version", "drupal/minimal": "self.version", + "drupal/media_entity": "self.version", "drupal/menu_link_content": "self.version", "drupal/menu_ui": "self.version", "drupal/migrate": "self.version", diff --git a/core/modules/media_entity/config/install/media_entity.settings.yml b/core/modules/media_entity/config/install/media_entity.settings.yml new file mode 100644 index 0000000..85f1c81 --- /dev/null +++ b/core/modules/media_entity/config/install/media_entity.settings.yml @@ -0,0 +1 @@ +icon_base: 'public://media-icons/generic' diff --git a/core/modules/media_entity/config/install/system.action.media_delete_action.yml b/core/modules/media_entity/config/install/system.action.media_delete_action.yml new file mode 100644 index 0000000..c68b0a5 --- /dev/null +++ b/core/modules/media_entity/config/install/system.action.media_delete_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media_entity +id: media_delete_action +label: 'Delete media' +type: media +plugin: media_delete_action +configuration: { } diff --git a/core/modules/media_entity/config/install/system.action.media_publish_action.yml b/core/modules/media_entity/config/install/system.action.media_publish_action.yml new file mode 100644 index 0000000..a5b4959 --- /dev/null +++ b/core/modules/media_entity/config/install/system.action.media_publish_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media_entity +id: media_publish_action +label: 'Publish media' +type: media +plugin: media_publish_action +configuration: { } diff --git a/core/modules/media_entity/config/install/system.action.media_save_action.yml b/core/modules/media_entity/config/install/system.action.media_save_action.yml new file mode 100644 index 0000000..fecb8a2 --- /dev/null +++ b/core/modules/media_entity/config/install/system.action.media_save_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media_entity +id: media_save_action +label: 'Save media' +type: media +plugin: media_save_action +configuration: { } diff --git a/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml b/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml new file mode 100644 index 0000000..1e87b11 --- /dev/null +++ b/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media_entity +id: media_unpublish_action +label: 'Unpublish media' +type: media +plugin: media_unpublish_action +configuration: { } diff --git a/core/modules/media_entity/config/install/views.view.media.yml b/core/modules/media_entity/config/install/views.view.media.yml new file mode 100644 index 0000000..45c8ddf --- /dev/null +++ b/core/modules/media_entity/config/install/views.view.media.yml @@ -0,0 +1,854 @@ +langcode: en +status: true +dependencies: + module: + - image + - media_entity + - user +id: media +label: Media +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 + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 50 + 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: '‹ Previous' + next: 'Next ›' + first: '« First' + last: 'Last »' + quantity: 9 + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + name: name + bundle: bundle + changed: changed + uid: uid + status: status + thumbnail__target_id: thumbnail__target_id + info: + name: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + bundle: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + uid: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + status: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + thumbnail__target_id: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: changed + empty_table: false + row: + type: fields + 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: '' + 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: media_bulk_form + thumbnail__target_id: + id: thumbnail__target_id + table: media_field_data + field: thumbnail__target_id + relationship: none + group_type: group + admin_label: '' + label: Thumbnail + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: image + settings: + image_style: thumbnail + image_link: '' + group_column: '' + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: thumbnail + plugin_id: field + name: + id: name + table: media_field_data + field: name + entity_type: media + entity_field: media + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + settings: + link_to_entity: true + plugin_id: field + relationship: none + group_type: group + admin_label: '' + label: 'Media name' + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + type: string + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + label: Provider + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: false + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: bundle + plugin_id: field + uid: + id: uid + table: media_field_data + field: uid + relationship: none + group_type: group + admin_label: '' + label: Author + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: true + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: uid + plugin_id: field + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + label: Status + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: boolean + settings: + format: custom + format_custom_true: Published + format_custom_false: Unpublished + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: status + plugin_id: field + changed: + id: changed + table: media_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + label: Updated + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: changed + plugin_id: field + operations: + id: operations + table: media + field: operations + relationship: none + group_type: group + admin_label: '' + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + entity_type: media + plugin_id: entity_operations + 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: 'True' + 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: 'Publishing status' + 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 + 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: Provider + description: '' + use_operator: false + operator: bundle_op + identifier: provider + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + 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: bundle + plugin_id: bundle + 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: 'Media 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 + langcode: + id: langcode + table: media_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: langcode_op + label: Language + description: '' + use_operator: false + operator: langcode_op + identifier: langcode + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + 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: langcode + plugin_id: language + sorts: + created: + id: created + table: media_field_data + field: created + order: DESC + entity_type: media + entity_field: created + plugin_id: date + relationship: none + group_type: group + admin_label: '' + exposed: false + expose: + label: '' + granularity: second + 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 content available.' + plugin_id: text_custom + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } + media_page_list: + display_plugin: page + id: media_page_list + display_title: Media + position: 1 + display_options: + display_extenders: { } + path: admin/content/media + menu: + type: tab + title: Media + description: '' + expanded: false + parent: '' + weight: 0 + context: '0' + menu_name: main + display_description: '' + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/core/modules/media_entity/config/schema/media_entity.schema.yml b/core/modules/media_entity/config/schema/media_entity.schema.yml new file mode 100644 index 0000000..aafb50e --- /dev/null +++ b/core/modules/media_entity/config/schema/media_entity.schema.yml @@ -0,0 +1,64 @@ +media_entity.settings: + type: config_object + label: 'Media entity settings' + mapping: + icon_base: + type: string + label: 'Base folder for icons installation' + +media_entity.bundle.*: + type: config_entity + label: 'Media bundle' + mapping: + id: + type: string + label: 'Machine name' + label: + type: label + label: 'Label' + description: + type: text + label: 'Description' + type: + type: string + label: 'Type plugin ID' + queue_thumbnail_downloads: + type: boolean + label: 'Queue thumbnail downloads' + new_revision: + type: boolean + label: 'Whether a new revision should be created by default' + type_configuration: + type: media_entity.bundle.type.[%parent.type] + field_map: + type: sequence + label: 'Field map' + sequence: + type: string + +action.configuration.media_delete_action: + type: action_configuration_default + label: 'Delete media configuration' + +action.configuration.media_save_action: + type: action_configuration_default + label: 'Save media configuration' + +action.configuration.media_publish_action: + type: action_configuration_default + label: 'Publish media configuration' + +action.configuration.media_unpublish_action: + type: action_configuration_default + label: 'Unpublish media configuration' + +field.formatter.settings.media_thumbnail: + type: mapping + label: 'Media thumbnail field display format settings' + mapping: + image_link: + type: string + label: 'Link image to' + image_style: + type: string + label: 'Image style' diff --git a/core/modules/media_entity/config/schema/media_entity.views.schema.yml b/core/modules/media_entity/config/schema/media_entity.views.schema.yml new file mode 100644 index 0000000..6106249 --- /dev/null +++ b/core/modules/media_entity/config/schema/media_entity.views.schema.yml @@ -0,0 +1,5 @@ +# Schema for the views plugins of the Media Entity module. + +views.field.media_bulk_form: + type: views_field_bulk_form + label: 'Media Entity bulk form' diff --git a/core/modules/media_entity/images/icons/generic.png b/core/modules/media_entity/images/icons/generic.png new file mode 100644 index 0000000..46125e7 --- /dev/null +++ b/core/modules/media_entity/images/icons/generic.png @@ -0,0 +1,8 @@ +PNG + + IHDRetEXtSoftwareAdobe ImageReadyqe<IDATxK*[Q#ʄ2(8T(.*?>񦏋"$>H)iY(DxvGg-"d7gt_a%@@@@@@@@@@@@@@@@@@@@@3Ѥ;%,rJrS 7==-ubB!LܔEAZ???i;gqJ%Bv}nn΂f|dttTlQp:V5Mz-ԗrIqǣB)ioo u#!̂f1111<<ޕx  E9ONN~uuu"XX]qᇆǍBL(d2L+uc% qŸe}[[5wds)bwoYww(^v;r4e?nP0%S3sM*GP<55Յ MMMKKK9ԏ +pp*ahDĴbfqݲ +VѺB +{ 9dy׸׮ԽiTI4 G!/uGrXKX*J^C:QX*XHqn(et rR9jntUΕRF"Uk"reJm---Rş8yiHˋQS+~xd!n,lpe3   H뉨#l6i&5 %CJ<y?>>nnn!kr4$HDѮ.3 95H6tH)~ E9jMR5吣H>rrrrrșO@9Ltu8^X7rK\A#%L+_ V j|dz9~&NgYHR\NC9ޞDoiJ&ڒ+5쫫tl|!lu=a$Y>xwvv<|M0>H@@@@@@@@@@@@@@@@@@@@@5O +a?IENDB` \ No newline at end of file diff --git a/core/modules/media_entity/js/media_bundle_form.js b/core/modules/media_entity/js/media_bundle_form.js new file mode 100644 index 0000000..145fee6 --- /dev/null +++ b/core/modules/media_entity/js/media_bundle_form.js @@ -0,0 +1,45 @@ +/** + * @file + * Javascript for the media bundle form. + */ + +(function ($, Drupal) { + 'use strict'; + + /** + * Behaviors for setting summaries on media bundle form. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches summary behaviors on media bundle edit forms. + */ + Drupal.behaviors.mediaBundles = { + attach: function (context) { + var $context = $(context); + // Provide the vertical tab summaries. + $context.find('#edit-workflow').drupalSetSummary(function (context) { + var vals = []; + $(context).find('input[name^="options"]:checked').parent().each(function () { + vals.push(Drupal.checkPlain($(this).find('label').text())); + }); + if (!$(context).find('#edit-options-status').is(':checked')) { + vals.unshift(Drupal.t('Not published')); + } + return vals.join(', '); + }); + $('#edit-language', context).drupalSetSummary(function (context) { + var vals = []; + + vals.push($('.js-form-item-language-configuration-langcode select option:selected', context).text()); + + $('input:checked', context).next('label').each(function () { + vals.push(Drupal.checkPlain($(this).text())); + }); + + return vals.join(', '); + }); + } + }; + +})(jQuery, Drupal); diff --git a/core/modules/media_entity/media_entity.api.php b/core/modules/media_entity/media_entity.api.php new file mode 100644 index 0000000..c8eb7b8 --- /dev/null +++ b/core/modules/media_entity/media_entity.api.php @@ -0,0 +1,25 @@ +get('icon_base'); + media_entity_copy_icons($source, $destination); +} + +/** + * Implements hook_update_last_removed(). + */ +function media_entity_update_last_removed() { + return 8003; +} diff --git a/core/modules/media_entity/media_entity.libraries.yml b/core/modules/media_entity/media_entity.libraries.yml new file mode 100644 index 0000000..ed0507a --- /dev/null +++ b/core/modules/media_entity/media_entity.libraries.yml @@ -0,0 +1,17 @@ +media_form: + version: VERSION + js: + 'js/media_form.js': {} + dependencies: + - core/jquery + - core/drupal + - core/drupal.form + +media_bundle_form: + version: VERSION + js: + 'js/media_bundle_form.js': {} + dependencies: + - core/jquery + - core/drupal + - core/drupal.form diff --git a/core/modules/media_entity/media_entity.links.action.yml b/core/modules/media_entity/media_entity.links.action.yml new file mode 100644 index 0000000..87e2fa3 --- /dev/null +++ b/core/modules/media_entity/media_entity.links.action.yml @@ -0,0 +1,12 @@ +media.bundle_add: + route_name: entity.media_bundle.add_form + title: 'Add media bundle' + appears_on: + - entity.media_bundle.collection + +media.add: + route_name: entity.media.add_page + title: 'Add media' + weight: 10 + appears_on: + - view.media.media_page_list diff --git a/core/modules/media_entity/media_entity.links.contextual.yml b/core/modules/media_entity/media_entity.links.contextual.yml new file mode 100644 index 0000000..cce5f83 --- /dev/null +++ b/core/modules/media_entity/media_entity.links.contextual.yml @@ -0,0 +1,9 @@ +entity.media.edit_form: + route_name: entity.media.edit_form + group: media + title: Edit +entity.media.delete_form: + route_name: entity.media.delete_form + group: media + title: Delete + weight: 10 diff --git a/core/modules/media_entity/media_entity.links.menu.yml b/core/modules/media_entity/media_entity.links.menu.yml new file mode 100644 index 0000000..55b2bf3 --- /dev/null +++ b/core/modules/media_entity/media_entity.links.menu.yml @@ -0,0 +1,17 @@ +entity.media_bundle.collection: + title: 'Media bundles' + parent: system.admin_structure + description: 'Manage media bundles.' + route_name: entity.media_bundle.collection + +entity.media.collection: + title: 'Media' + parent: system.admin_content + description: 'Find and manage media.' + route_name: view.media.media_page_list + +entity.media.add: + title: 'Add a new media' + parent: entity.media.collection + description: 'Add a new media entity.' + route_name: entity.media.add_page diff --git a/core/modules/media_entity/media_entity.links.task.yml b/core/modules/media_entity/media_entity.links.task.yml new file mode 100644 index 0000000..c044847 --- /dev/null +++ b/core/modules/media_entity/media_entity.links.task.yml @@ -0,0 +1,21 @@ +entity.media.canonical: + route_name: entity.media.canonical + base_route: entity.media.canonical + title: 'View' +entity.media.edit_form: + route_name: entity.media.edit_form + base_route: entity.media.canonical + title: Edit +entity.media.delete_form: + route_name: entity.media.delete_form + base_route: entity.media.canonical + title: Delete + weight: 10 +entity.media_bundle.edit_form: + title: 'Edit' + route_name: entity.media_bundle.edit_form + base_route: entity.media_bundle.edit_form +entity.media_bundle.collection: + title: List + route_name: entity.media_bundle.collection + base_route: entity.media_bundle.collection diff --git a/core/modules/media_entity/media_entity.module b/core/modules/media_entity/media_entity.module new file mode 100644 index 0000000..5f75514 --- /dev/null +++ b/core/modules/media_entity/media_entity.module @@ -0,0 +1,85 @@ +' . t('About') . ''; + $output .= '

' . t('The Media Entity module provides a "base" entity for media. This is a very basic entity which can reference to all kinds of media-objects (local files, YouTube videos, Tweets, Instagram photos, ...). Media entity provides a relation between your website and the media resource. You can reference to/use this entity within any other entity on your site. For more information, see the online documentation for the Media Entity module.', + [ + ':media_entity_url' => 'https://www.drupal.org/project/media_entity', + ':media_entity_handbook' => 'https://drupal-media.gitbooks.io/drupal8-guide/content/modules/media_entity/intro.html', + ]) . '

'; + $output .= '

' . t('Uses') . '

'; + $output .= '

' . t('For detailed information about the usage of this module please refer to the official documentation.', + [ + ':media_entity_handbook' => 'https://drupal-media.gitbooks.io/drupal8-guide/content/modules/media_entity/intro.html', + ]) . '

'; + + return $output; + } +} + +/** + * Implements hook_theme(). + */ +function media_entity_theme() { + return [ + 'media' => [ + 'render element' => 'elements', + 'file' => 'media_entity.theme.inc', + 'template' => 'media', + ], + ]; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function media_entity_theme_suggestions_media(array $variables) { + $suggestions = []; + $media = $variables['elements']['#media']; + $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); + + $suggestions[] = 'media__' . $sanitized_view_mode; + $suggestions[] = 'media__' . $media->bundle(); + $suggestions[] = 'media__' . $media->bundle() . '__' . $sanitized_view_mode; + $suggestions[] = 'media__' . $media->id(); + $suggestions[] = 'media__' . $media->id() . '__' . $sanitized_view_mode; + + return $suggestions; +} + +/** + * Copy the media file icons to files directory for use with image styles. + * + * @param string $source + * Source folder. + * @param string $destination + * Destination folder. + * + * @throws Exception + * Thrown when media icons can't be copied to their destination. + */ +function media_entity_copy_icons($source, $destination) { + if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + throw new Exception("Unable to create directory $destination."); + } + + $files = file_scan_directory($source, '/.*\.(png|jpg)$/'); + foreach ($files as $file) { + $result = file_unmanaged_copy($file->uri, $destination, FILE_EXISTS_REPLACE); + if (!$result) { + throw new Exception("Unable to copy {$file->uri} to $destination."); + } + } +} diff --git a/core/modules/media_entity/media_entity.permissions.yml b/core/modules/media_entity/media_entity.permissions.yml new file mode 100644 index 0000000..1a748d9 --- /dev/null +++ b/core/modules/media_entity/media_entity.permissions.yml @@ -0,0 +1,21 @@ +administer media: + title: 'Administer media' + restrict access: TRUE +administer media bundles: + title: 'Administer media bundles' + restrict access: TRUE +access media overview: + title: 'Access the Media overview page' + description: 'Get an overview of all media items.' +view media: + title: 'View media' +update media: + title: 'Update own media' +update any media: + title: 'Update any media' +delete media: + title: 'Delete own media' +delete any media: + title: 'Delete any media' +create media: + title: 'Create media' diff --git a/core/modules/media_entity/media_entity.routing.yml b/core/modules/media_entity/media_entity.routing.yml new file mode 100644 index 0000000..b4c75ba --- /dev/null +++ b/core/modules/media_entity/media_entity.routing.yml @@ -0,0 +1,14 @@ +entity.media.multiple_delete_confirm: + path: '/admin/content/media/delete' + defaults: + _form: '\Drupal\media_entity\Form\DeleteMultiple' + requirements: + _permission: 'delete any media' + +entity.media_bundle.collection: + path: '/admin/structure/media' + defaults: + _entity_list: 'media_bundle' + _title: 'Media bundles' + requirements: + _permission: 'administer media bundles' diff --git a/core/modules/media_entity/media_entity.services.yml b/core/modules/media_entity/media_entity.services.yml new file mode 100644 index 0000000..a227b73 --- /dev/null +++ b/core/modules/media_entity/media_entity.services.yml @@ -0,0 +1,4 @@ +services: + plugin.manager.media_entity.type: + class: Drupal\media_entity\MediaTypeManager + parent: default_plugin_manager diff --git a/core/modules/media_entity/media_entity.theme.inc b/core/modules/media_entity/media_entity.theme.inc new file mode 100644 index 0000000..9ccc6f5 --- /dev/null +++ b/core/modules/media_entity/media_entity.theme.inc @@ -0,0 +1,40 @@ +label(); + + // Helpful $content variable for templates. + foreach (Element::children($variables['elements']) as $key) { + $variables['content'][$key] = $variables['elements'][$key]; + } + + $variables['attributes']['class'][] = 'media'; + $variables['attributes']['class'][] = Html::getClass('media-' . $media->bundle()); + if (!$media->isPublished()) { + $variables['attributes']['class'][] = 'unpublished'; + } + if ($variables['elements']['#view_mode']) { + $variables['attributes']['class'][] = Html::getClass('view-mode-' . $variables['elements']['#view_mode']); + } +} diff --git a/core/modules/media_entity/media_entity.tokens.inc b/core/modules/media_entity/media_entity.tokens.inc new file mode 100644 index 0000000..31f72be --- /dev/null +++ b/core/modules/media_entity/media_entity.tokens.inc @@ -0,0 +1,180 @@ + t('Media'), + 'description' => t('Tokens related to individual media items.'), + 'needs-data' => 'media', + ]; + + // Core tokens for media. + $media['mid'] = [ + 'name' => t("Media ID"), + 'description' => t('The unique ID of the media item.'), + ]; + $media['uuid'] = [ + 'name' => t("Media UUID"), + 'description' => t('The unique UUID of the media item.'), + ]; + $media['vid'] = [ + 'name' => t("Revision ID"), + 'description' => t("The unique ID of the media's latest revision."), + ]; + $media['bundle'] = [ + 'name' => t("Media bundle"), + ]; + $media['bundle-name'] = [ + 'name' => t("Media bundle name"), + 'description' => t("The human-readable name of the media bundle."), + ]; + $media['langcode'] = [ + 'name' => t('Language code'), + 'description' => t('The language code of the language the media is written in.'), + ]; + $media['name'] = [ + 'name' => t('Name'), + 'description' => t('The name of this media.'), + ]; + $node['author'] = [ + 'name' => t("Author"), + 'type' => 'user', + ]; + $media['url'] = [ + 'name' => t("URL"), + 'description' => t("The URL of the media."), + ]; + $media['edit-url'] = [ + 'name' => t("Edit URL"), + 'description' => t("The URL of the media's edit page."), + ]; + + // Chained tokens for media. + $media['created'] = [ + 'name' => t("Date created"), + 'type' => 'date', + ]; + $media['changed'] = [ + 'name' => t("Date changed"), + 'description' => t("The date the media was most recently updated."), + 'type' => 'date', + ]; + + return [ + 'types' => ['media' => $type], + 'tokens' => ['media' => $media], + ]; +} + +/** + * Implements hook_tokens(). + */ +function media_entity_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { + $token_service = \Drupal::token(); + + $url_options = ['absolute' => TRUE]; + if (isset($options['langcode'])) { + $url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']); + $langcode = $options['langcode']; + } + else { + $langcode = LanguageInterface::LANGCODE_DEFAULT; + } + + $replacements = []; + if ($type == 'media' && !empty($data['media'])) { + /** @var \Drupal\media_entity\MediaInterface $media */ + $media = \Drupal::service('entity.repository')->getTranslationFromContext($data['media'], $langcode, ['operation' => 'media_entity_tokens']); + + foreach ($tokens as $name => $original) { + switch ($name) { + // Simple key values on the media_entity. + case 'mid': + $replacements[$original] = $media->id(); + break; + + case 'uuid': + $replacements[$original] = $media->uuid(); + break; + + case 'vid': + $replacements[$original] = $media->getRevisionId(); + break; + + case 'bundle': + $replacements[$original] = $media->bundle(); + break; + + case 'bundle-name': + $replacements[$original] = $media->bundle->entity->label(); + break; + + case 'langcode': + $replacements[$original] = $media->language()->getId(); + break; + + case 'name': + $replacements[$original] = $media->name->value; + break; + + case 'url': + $replacements[$original] = $media->toUrl('canonical', $url_options); + break; + + case 'edit-url': + $replacements[$original] = $media->toUrl('edit-form', $url_options); + break; + + // Default values for the chained tokens handled below. + case 'author': + /** @var \Drupal\user\UserInterface $account */ + $account = $media->getPublisher(); + $bubbleable_metadata->addCacheableDependency($account); + $replacements[$original] = $account->label(); + break; + + case 'created': + $date_format = DateFormat::load('medium'); + $bubbleable_metadata->addCacheableDependency($date_format); + $replacements[$original] = \Drupal::service('date.formatter') + ->format($media->getCreatedTime(), 'medium', '', NULL, $langcode); + break; + + case 'changed': + $date_format = DateFormat::load('medium'); + $bubbleable_metadata->addCacheableDependency($date_format); + $replacements[$original] = \Drupal::service('date.formatter') + ->format($media->getChangedTime(), 'medium', '', NULL, $langcode); + break; + } + } + + if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) { + $account = $media->get('uid')->entity; + $replacements += $token_service->generate('user', $author_tokens, ['user' => $account], $options, $bubbleable_metadata); + } + + if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) { + $replacements += $token_service->generate('date', $created_tokens, ['date' => $media->getCreatedTime()], $options, $bubbleable_metadata); + } + + if ($changed_tokens = $token_service->findWithPrefix($tokens, 'changed')) { + $replacements += $token_service->generate('date', $changed_tokens, ['date' => $media->getChangedTime()], $options, $bubbleable_metadata); + } + } + + return $replacements; +} diff --git a/core/modules/media_entity/src/Annotation/MediaType.php b/core/modules/media_entity/src/Annotation/MediaType.php new file mode 100644 index 0000000..65a3d70 --- /dev/null +++ b/core/modules/media_entity/src/Annotation/MediaType.php @@ -0,0 +1,43 @@ +$property; + } + } + } + +} diff --git a/core/modules/media_entity/src/Entity/Media.php b/core/modules/media_entity/src/Entity/Media.php new file mode 100644 index 0000000..2543318 --- /dev/null +++ b/core/modules/media_entity/src/Entity/Media.php @@ -0,0 +1,459 @@ +get('created')->value; + } + + /** + * {@inheritdoc} + */ + public function setCreatedTime($timestamp) { + $this->set('created', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isPublished() { + return (bool) $this->get('status')->value; + } + + /** + * {@inheritdoc} + */ + public function setPublished($published) { + $this->set('status', $published ? Media::PUBLISHED : Media::NOT_PUBLISHED); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwner() { + $this->get('uid')->entity; + } + + /** + * {@inheritdoc} + */ + public function setOwner(UserInterface $account) { + $this->set('uid', $account->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwnerId() { + $this->get('uid')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setOwnerId($uid) { + $this->set('uid', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getPublisher() { + return $this->get('uid')->entity; + } + + /** + * {@inheritdoc} + */ + public function getPublisherId() { + return $this->get('uid')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setPublisherId($uid) { + $this->set('uid', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->bundle->entity->getType(); + } + + /** + * {@inheritdoc} + */ + public function preSave(EntityStorageInterface $storage) { + parent::preSave($storage); + + // If no revision author has been set explicitly, make the media owner the + // revision author. + if (!$this->get('revision_uid')->entity) { + $this->setRevisionUserId($this->getOwnerId()); + } + + // Set thumbnail. + if (!$this->get('thumbnail')->entity) { + $this->automaticallySetThumbnail(); + } + + // Try to set fields provided by type plugin and mapped in bundle + // configuration. + foreach ($this->bundle->entity->getFieldMap() as $source_field => $destination_field) { + // Only save value in entity field if empty. Do not overwrite existing + // data. + if ($this->hasField($destination_field) && $this->{$destination_field}->isEmpty() && ($value = $this->getType()->getField($this, $source_field))) { + $this->set($destination_field, $value); + } + } + + // Try to set a default name for this media, if there is no label provided. + if (empty($this->label())) { + $this->set('name', $this->getType()->getDefaultName($this)); + } + + } + + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + if (!$update && $this->bundle->entity->getQueueThumbnailDownloads()) { + $queue = \Drupal::queue('media_entity_thumbnail'); + $queue->createItem(['id' => $this->id()]); + } + } + + /** + * {@inheritdoc} + */ + public function automaticallySetThumbnail() { + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + if ($this->bundle->entity->getQueueThumbnailDownloads() && $this->isNew()) { + $thumbnail_uri = $this->getType()->getDefaultThumbnail(); + } + else { + $thumbnail_uri = $this->getType()->thumbnail($this); + } + $existing = \Drupal::entityQuery('file') + ->condition('uri', $thumbnail_uri) + ->execute(); + + if ($existing) { + $this->thumbnail->target_id = reset($existing); + } + else { + /** @var \Drupal\file\FileInterface $file */ + $file = $this->entityTypeManager()->getStorage('file')->create(['uri' => $thumbnail_uri]); + if ($publisher = $this->getOwner()) { + $file->setOwner($publisher); + } + $file->setPermanent(); + $file->save(); + $this->thumbnail->target_id = $file->id(); + } + + // TODO - We should probably use something smarter (tokens, ...). + $this->thumbnail->alt = t('Thumbnail'); + $this->thumbnail->title = $this->label(); + } + + /** + * {@inheritdoc} + */ + public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) { + parent::preSaveRevision($storage, $record); + + if (!$this->isNewRevision() && isset($this->original) && (!isset($record->revision_log) || $record->revision_log === '')) { + // If we are updating an existing node without adding a new revision, we + // need to make sure $entity->revision_log is reset whenever it is empty. + // Therefore, this code allows us to avoid clobbering an existing log + // entry with an empty one. + $record->revision_log = $this->original->revision_log->value; + } + } + + /** + * {@inheritdoc} + */ + public function validate() { + $this->getType()->attachConstraints($this); + return parent::validate(); + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['name'] = BaseFieldDefinition::create('string') + ->setLabel(t('Media name')) + ->setDescription(t('The name of this media.')) + ->setRequired(TRUE) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDefaultValue('') + ->setSetting('max_length', 255) + ->setDisplayOptions('form', [ + 'type' => 'string_textfield', + 'weight' => -5, + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + ]) + ->setDisplayConfigurable('view', TRUE); + + $fields['thumbnail'] = BaseFieldDefinition::create('image') + ->setLabel(t('Thumbnail')) + ->setDescription(t('The thumbnail of the media.')) + ->setRevisionable(TRUE) + ->setDisplayOptions('view', [ + 'type' => 'image', + 'weight' => 1, + 'label' => 'hidden', + 'settings' => [ + 'image_style' => 'thumbnail', + ], + ]) + ->setDisplayConfigurable('view', TRUE) + ->setReadOnly(TRUE); + + $fields['uid'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Publisher ID')) + ->setDescription(t('The user ID of the media publisher.')) + ->setRevisionable(TRUE) + ->setDefaultValueCallback('Drupal\media_entity\Entity\Media::getCurrentUserId') + ->setSetting('target_type', 'user') + ->setTranslatable(TRUE) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'author', + 'weight' => 0, + ]) + ->setDisplayConfigurable('view', TRUE) + ->setDisplayOptions('form', [ + 'type' => 'entity_reference_autocomplete', + 'weight' => 5, + 'settings' => [ + 'match_operator' => 'CONTAINS', + 'size' => '60', + 'autocomplete_type' => 'tags', + 'placeholder' => '', + ], + ]) + ->setDisplayConfigurable('form', TRUE); + + $fields['status'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Publishing status')) + ->setDescription(t('A boolean indicating whether the media is published.')) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDefaultValue(TRUE); + + $fields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Created')) + ->setDescription(t('The time that the media was created.')) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'timestamp', + 'weight' => 0, + ]) + ->setDisplayConfigurable('view', TRUE) + ->setDisplayOptions('form', [ + 'type' => 'datetime_timestamp', + 'weight' => 10, + ]) + ->setDisplayConfigurable('form', TRUE); + + $fields['changed'] = BaseFieldDefinition::create('changed') + ->setLabel(t('Changed')) + ->setDescription(t('The time that the media was last edited.')) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE); + + $fields['revision_timestamp'] = BaseFieldDefinition::create('created') + ->setLabel(t('Revision timestamp')) + ->setDescription(t('The time that the current revision was created.')) + ->setQueryable(FALSE) + ->setRevisionable(TRUE); + + $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Revision publisher ID')) + ->setDescription(t('The user ID of the publisher of the current revision.')) + ->setSetting('target_type', 'user') + ->setQueryable(FALSE) + ->setRevisionable(TRUE); + + $fields['revision_log'] = BaseFieldDefinition::create('string_long') + ->setLabel(t('Revision Log')) + ->setDescription(t('The log entry explaining the changes in this revision.')) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE); + + return $fields; + } + + /** + * Default value callback for 'uid' base field definition. + * + * @see ::baseFieldDefinitions() + * + * @return array + * An array of default values. + */ + public static function getCurrentUserId() { + return [\Drupal::currentUser()->id()]; + } + + /** + * {@inheritdoc} + */ + public function getRevisionCreationTime() { + return $this->revision_timestamp->value; + } + + /** + * {@inheritdoc} + */ + public function setRevisionCreationTime($timestamp) { + $this->revision_timestamp->value = $timestamp; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRevisionUser() { + return $this->revision_uid->entity; + } + + /** + * {@inheritdoc} + */ + public function setRevisionUser(UserInterface $account) { + $this->revision_uid->entity = $account; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRevisionUserId() { + return $this->revision_uid->target_id; + } + + /** + * {@inheritdoc} + */ + public function setRevisionUserId($user_id) { + $this->revision_uid->target_id = $user_id; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRevisionLogMessage() { + return $this->revision_log->value; + } + + /** + * {@inheritdoc} + */ + public function setRevisionLogMessage($revision_log_message) { + $this->revision_log-> value = $revision_log_message; + return $this; + } + +} diff --git a/core/modules/media_entity/src/Entity/MediaBundle.php b/core/modules/media_entity/src/Entity/MediaBundle.php new file mode 100644 index 0000000..c98a098 --- /dev/null +++ b/core/modules/media_entity/src/Entity/MediaBundle.php @@ -0,0 +1,244 @@ + $this->typePluginCollection(), + ]; + } + + /** + * {@inheritdoc} + */ + public static function getLabel(MediaInterface $media) { + $bundle = static::load($media->bundle()); + return $bundle ? $bundle->label() : FALSE; + } + + /** + * {@inheritdoc} + */ + public static function exists($id) { + return (bool) static::load($id); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->description; + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->description = $description; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getTypeConfiguration() { + return $this->type_configuration; + } + + /** + * {@inheritdoc} + */ + public function setTypeConfiguration($configuration) { + $this->type_configuration = $configuration; + $this->typePluginCollection = NULL; + } + + /** + * {@inheritdoc} + */ + public function getQueueThumbnailDownloads() { + return $this->queue_thumbnail_downloads; + } + + /** + * {@inheritdoc} + */ + public function setQueueThumbnailDownloads($queue_thumbnail_downloads) { + $this->queue_thumbnail_downloads = $queue_thumbnail_downloads; + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->typePluginCollection()->get($this->type); + } + + /** + * Returns type lazy plugin collection. + * + * @return \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection + * The tag plugin collection. + */ + protected function typePluginCollection() { + if (!$this->typePluginCollection) { + $this->typePluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.media_entity.type'), $this->type, $this->type_configuration); + } + return $this->typePluginCollection; + } + + /** + * {@inheritdoc} + */ + public function getStatus() { + return $this->status; + } + + /** + * {@inheritdoc} + */ + public function shouldCreateNewRevision() { + return $this->new_revision; + } + + /** + * {@inheritdoc} + */ + public function setNewRevision($new_revision) { + $this->new_revision = $new_revision; + } + + /** + * {@inheritdoc} + */ + public function getFieldMap() { + return $this->field_map; + } + +} diff --git a/core/modules/media_entity/src/Form/DeleteMultiple.php b/core/modules/media_entity/src/Form/DeleteMultiple.php new file mode 100644 index 0000000..2a9cc40 --- /dev/null +++ b/core/modules/media_entity/src/Form/DeleteMultiple.php @@ -0,0 +1,199 @@ +tempStoreFactory = $temp_store_factory; + $this->storage = $manager->getStorage('media'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.private_tempstore'), + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'media_multiple_delete_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->formatPlural(count($this->entityInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('system.admin_content'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $this->entityInfo = $this->tempStoreFactory->get('media_multiple_delete_confirm')->get(\Drupal::currentUser()->id()); + if (empty($this->entityInfo)) { + return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString()); + } + /** @var \Drupal\media_entity\MediaInterface[] $entities */ + $entities = $this->storage->loadMultiple(array_keys($this->entityInfo)); + + $items = []; + foreach ($this->entityInfo as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $entity = $entities[$id]->getTranslation($langcode); + $key = $id . ':' . $langcode; + $default_key = $id . ':' . $entity->getUntranslated()->language()->getId(); + + // If we have a translated entity we build a nested list of translations + // that will be deleted. + $languages = $entity->getTranslationLanguages(); + if (count($languages) > 1 && $entity->isDefaultTranslation()) { + $names = []; + foreach ($languages as $translation_langcode => $language) { + $names[] = $language->getName(); + unset($items[$id . ':' . $translation_langcode]); + } + $items[$default_key] = [ + 'label' => [ + '#markup' => $this->t('@label (Original translation) - The following media translations will be deleted:', ['@label' => $entity->label()]), + ], + 'deleted_translations' => [ + '#theme' => 'item_list', + '#items' => $names, + ], + ]; + } + elseif (!isset($items[$default_key])) { + $items[$key] = $entity->label(); + } + } + } + + $form['entities'] = [ + '#theme' => 'item_list', + '#items' => $items, + ]; + $form = parent::buildForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + if ($form_state->getValue('confirm') && !empty($this->entityInfo)) { + $total_count = 0; + $delete_entities = []; + /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */ + $delete_translations = []; + /** @var \Drupal\media_entity\MediaInterface[] $entities */ + $entities = $this->storage->loadMultiple(array_keys($this->entityInfo)); + + foreach ($this->entityInfo as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $entity = $entities[$id]->getTranslation($langcode); + if ($entity->isDefaultTranslation()) { + $delete_entities[$id] = $entity; + unset($delete_translations[$id]); + $total_count += count($entity->getTranslationLanguages()); + } + elseif (!isset($delete_entities[$id])) { + $delete_translations[$id][] = $entity; + } + } + } + + if ($delete_entities) { + $this->storage->delete($delete_entities); + $this->logger('media_entity')->notice('Deleted @count media entities.', ['@count' => count($delete_entities)]); + } + + if ($delete_translations) { + $count = 0; + foreach ($delete_translations as $id => $translations) { + $entity = $entities[$id]->getUntranslated(); + foreach ($translations as $translation) { + $entity->removeTranslation($translation->language()->getId()); + } + $entity->save(); + $count += count($translations); + } + if ($count) { + $total_count += $count; + $this->logger('media_entity')->notice('Deleted @count media translations.', ['@count' => $count]); + } + } + + if ($total_count) { + drupal_set_message($this->formatPlural($total_count, 'Deleted 1 media entity.', 'Deleted @count media entities.')); + } + + $this->tempStoreFactory->get('media_multiple_delete_confirm')->delete(\Drupal::currentUser()->id()); + } + + $form_state->setRedirect('system.admin_content'); + } + +} diff --git a/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php b/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php new file mode 100644 index 0000000..f860eb4 --- /dev/null +++ b/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php @@ -0,0 +1,59 @@ +queryFactory = $query_factory; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.query') + ); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $num_entities = $this->queryFactory->get('media') + ->condition('bundle', $this->entity->id()) + ->count() + ->execute(); + if ($num_entities) { + $caption = '

' . $this->formatPlural($num_entities, '%type is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the %type content.', '%type is used by @count pieces of content on your site. You may not remove %type until you have removed all of the %type content.', ['%type' => $this->entity->label()]) . '

'; + $form['#title'] = $this->getQuestion(); + $form['description'] = ['#markup' => $caption]; + return $form; + } + + return parent::buildForm($form, $form_state); + } + +} diff --git a/core/modules/media_entity/src/Form/MediaDeleteForm.php b/core/modules/media_entity/src/Form/MediaDeleteForm.php new file mode 100644 index 0000000..d84e34a --- /dev/null +++ b/core/modules/media_entity/src/Form/MediaDeleteForm.php @@ -0,0 +1,20 @@ +hasPermission('administer media')) { + return AccessResult::allowed()->cachePerPermissions(); + } + + $is_owner = ($account->id() && $account->id() == $entity->getPublisherId()) ? TRUE : FALSE; + switch ($operation) { + case 'view': + return AccessResult::allowedIf($account->hasPermission('view media') && $entity->status->value); + + case 'update': + return AccessResult::allowedIf(($account->hasPermission('update media') && $is_owner) || $account->hasPermission('update any media'))->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity); + + case 'delete': + return AccessResult::allowedIf(($account->hasPermission('delete media') && $is_owner) || $account->hasPermission('delete any media'))->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity); + } + + // No opinion. + return AccessResult::neutral()->cachePerPermissions(); + } + + /** + * {@inheritdoc} + */ + protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { + return AccessResult::allowedIfHasPermission($account, 'create media'); + } + +} diff --git a/core/modules/media_entity/src/MediaBundleForm.php b/core/modules/media_entity/src/MediaBundleForm.php new file mode 100644 index 0000000..66f0cca --- /dev/null +++ b/core/modules/media_entity/src/MediaBundleForm.php @@ -0,0 +1,386 @@ +mediaTypeManager = $media_type_manager; + $this->entityFieldManager = $entity_field_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.media_entity.type'), + $container->get('entity_field.manager') + ); + } + + /** + * Ajax callback triggered by the type provider select element. + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Current form state. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * The ajax response. + */ + public function ajaxTypeProviderData(array $form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + $plugin = $this->entity->getType()->getPluginId(); + + $response->addCommand(new ReplaceCommand('#edit-type-configuration-plugin-wrapper', $form['type_configuration'][$plugin])); + $response->addCommand(new ReplaceCommand('#field-mapping-wrapper', $form['field_mapping'])); + + return $response; + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + $form['#entity'] = $bundle = $this->entity; + $form_state->set('bundle', $bundle->id()); + + if ($this->operation == 'add') { + $form['#title'] = $this->t('Add media bundle'); + } + elseif ($this->operation == 'edit') { + $form['#title'] = $this->t('Edit %label media bundle', ['%label' => $bundle->label()]); + } + + $form['label'] = [ + '#title' => $this->t('Label'), + '#type' => 'textfield', + '#default_value' => $bundle->label(), + '#description' => $this->t('The human-readable name of this media bundle.'), + '#required' => TRUE, + '#size' => 30, + '#weight' => -100, + ]; + + // @todo: '#disabled' not always FALSE. + $form['id'] = [ + '#type' => 'machine_name', + '#default_value' => $bundle->id(), + '#maxlength' => 32, + '#disabled' => !$bundle->isNew(), + '#machine_name' => [ + 'exists' => ['\Drupal\media_entity\Entity\MediaBundle', 'exists'], + 'source' => ['label'], + ], + '#description' => $this->t('A unique machine-readable name for this media bundle.'), + '#weight' => -90, + ]; + + $form['description'] = [ + '#title' => $this->t('Description'), + '#type' => 'textarea', + '#default_value' => $bundle->getDescription(), + '#description' => $this->t('Describe this media bundle. The text will be displayed on the Add new media page.'), + '#weight' => -80, + ]; + + $plugins = $this->mediaTypeManager->getDefinitions(); + $options = []; + foreach ($plugins as $plugin => $definition) { + $options[$plugin] = $definition['label']; + } + + $form['type'] = [ + '#type' => 'select', + '#title' => $this->t('Type provider'), + '#default_value' => $bundle->getType()->getPluginId(), + '#options' => $options, + '#description' => $this->t('Media type provider plugin that is responsible for additional logic related to this media.'), + '#weight' => -70, + '#ajax' => [ + 'callback' => '::ajaxTypeProviderData', + 'progress' => [ + 'type' => 'throbber', + 'message' => $this->t('Updating type provider configuration form.'), + ], + ], + ]; + + // Media type plugin configuration. + $form['type_configuration'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Type provider configuration'), + '#tree' => TRUE, + '#weight' => -60, + ]; + + /** @var \Drupal\media_entity\MediaTypeInterface $plugin */ + if ($plugin = $bundle->getType()) { + $plugin_configuration = (empty($this->configurableInstances[$plugin->getPluginId()]['plugin_config'])) ? $bundle->type_configuration : $this->configurableInstances[$plugin->getPluginId()]['plugin_config']; + /** @var \Drupal\media_entity\MediaTypeBase $instance */ + $instance = $this->mediaTypeManager->createInstance($plugin->getPluginId(), $plugin_configuration); + // Store the configuration for validate and submit handlers. + $this->configurableInstances[$plugin->getPluginId()]['plugin_config'] = $plugin_configuration; + + $form['type_configuration'][$plugin->getPluginId()] = [ + '#type' => 'container', + '#attributes' => [ + 'id' => 'edit-type-configuration-plugin-wrapper', + ], + ]; + $form['type_configuration'][$plugin->getPluginId()] += $instance->buildConfigurationForm([], $form_state); + } + + // Field mapping configuration. + $form['field_mapping'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Field mapping'), + '#tree' => TRUE, + '#attributes' => ['id' => 'field-mapping-wrapper'], + 'description' => [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $this->t('Media type plugins can provide metadata fields such as title, caption, size information, credits, ... Media entity can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.'), + ], + '#weight' => -50, + ]; + + if (empty($plugin) || empty($plugin->providedFields())) { + $form['field_mapping']['empty_message'] = [ + '#prefix' => '', + '#suffix' => '', + '#markup' => $this->t('No metadata fields available.'), + ]; + } + else { + $skipped_fields = [ + 'mid', + 'uuid', + 'vid', + 'bundle', + 'langcode', + 'default_langcode', + 'uid', + 'revision_timestamp', + 'revision_log', + 'revision_uid', + ]; + $options = ['_none' => $this->t('- Skip field -')]; + foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) { + if (!in_array($field_name, $skipped_fields)) { + $options[$field_name] = $field->getLabel(); + } + } + + $field_map = $bundle->getFieldMap(); + foreach ($plugin->providedFields() as $field_name => $field_label) { + $form['field_mapping'][$field_name] = [ + '#type' => 'select', + '#title' => $field_label, + '#options' => $options, + '#default_value' => isset($field_map[$field_name]) ? $field_map[$field_name] : '_none', + ]; + } + } + + $form['additional_settings'] = [ + '#type' => 'vertical_tabs', + '#attached' => [ + 'library' => ['media_entity/media_bundle_form'], + ], + '#weight' => 100, + ]; + + $form['workflow'] = [ + '#type' => 'details', + '#title' => $this->t('Publishing options'), + '#group' => 'additional_settings', + ]; + + $workflow_options = [ + 'status' => $bundle->getStatus(), + 'new_revision' => $bundle->shouldCreateNewRevision(), + 'queue_thumbnail_downloads' => $bundle->getQueueThumbnailDownloads(), + ]; + // Prepare workflow options to be used for 'checkboxes' form element. + $keys = array_keys(array_filter($workflow_options)); + $workflow_options = array_combine($keys, $keys); + $form['workflow']['options'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Default options'), + '#default_value' => $workflow_options, + '#options' => [ + 'status' => $this->t('Published'), + 'new_revision' => $this->t('Create new revision'), + 'queue_thumbnail_downloads' => $this->t('Queue thumbnail downloads'), + ], + ]; + + $form['workflow']['options']['status']['#description'] = $this->t('Entities will be automatically published when they are created.'); + $form['workflow']['options']['new_revision']['#description'] = $this->t('Automatically create a new revision of media entities. Users with the Administer media permission will be able to override this option.'); + $form['workflow']['options']['queue_thumbnail_downloads']['#description'] = $this->t('Download thumbnails via a queue.'); + + if ($this->moduleHandler->moduleExists('language')) { + $form['language'] = [ + '#type' => 'details', + '#title' => $this->t('Language settings'), + '#group' => 'additional_settings', + ]; + + $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('media', $bundle->id()); + + $form['language']['language_configuration'] = [ + '#type' => 'language_configuration', + '#entity_information' => [ + 'entity_type' => 'media', + 'bundle' => $bundle->id(), + ], + '#default_value' => $language_configuration, + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + parent::validateForm($form, $form_state); + + // Let the selected plugin validate its settings. + $plugin = $this->entity->getType()->getPluginId(); + $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : []; + $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration); + $instance->validateConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + + $workflow_options = ['status', 'queue_thumbnail_downloads']; + foreach ($workflow_options as $option) { + $this->entity->$option = (bool) $form_state->getValue(['options', $option]); + } + + $this->entity->setNewRevision((bool) $form_state->getValue(['options', 'new_revision'])); + + // Let the selected plugin save its settings. + $plugin = $this->entity->getType()->getPluginId(); + $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : []; + $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration); + $instance->submitConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $actions = parent::actions($form, $form_state); + $actions['submit']['#value'] = $this->t('Save media bundle'); + $actions['delete']['#value'] = $this->t('Delete media bundle'); + $actions['delete']['#access'] = $this->entity->access('delete'); + return $actions; + } + + /** + * {@inheritdoc} + */ + protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { + $configuration = $form_state->getValue('type_configuration'); + + // Store previous plugin config. + $plugin = $entity->getType()->getPluginId(); + $this->configurableInstances[$plugin]['plugin_config'] = empty($configuration[$plugin]) ? [] : $configuration[$plugin]; + + /** @var \Drupal\media_entity\MediaBundleInterface $entity */ + parent::copyFormValuesToEntity($entity, $form, $form_state); + + // Use type configuration for the plugin that was chosen. + $plugin = $entity->getType()->getPluginId(); + $plugin_configuration = empty($configuration[$plugin]) ? [] : $configuration[$plugin]; + $entity->set('type_configuration', $plugin_configuration); + + // Save field mapping. + $entity->field_map = array_filter( + $form_state->getValue('field_mapping', []), + function ($item) { return $item != '_none'; } + ); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + $bundle = $this->entity; + $status = $bundle->save(); + + $t_args = ['%name' => $bundle->label()]; + if ($status == SAVED_UPDATED) { + drupal_set_message($this->t('The media bundle %name has been updated.', $t_args)); + } + elseif ($status == SAVED_NEW) { + drupal_set_message($this->t('The media bundle %name has been added.', $t_args)); + $this->logger('media')->notice('Added bundle %name.', $t_args); + } + + // Override the "status" base field default value, for this bundle. + $fields = $this->entityFieldManager->getFieldDefinitions('media', $bundle->id()); + $media = $this->entityTypeManager->getStorage('media')->create(array('bundle' => $bundle->id())); + $value = (bool) $form_state->getValue(['options', 'status']); + if ($media->status->value != $value) { + $fields['status']->getConfig($bundle->id())->setDefaultValue($value)->save(); + } + + $form_state->setRedirectUrl($bundle->toUrl('collection')); + } + +} diff --git a/core/modules/media_entity/src/MediaBundleInterface.php b/core/modules/media_entity/src/MediaBundleInterface.php new file mode 100644 index 0000000..6a2332c --- /dev/null +++ b/core/modules/media_entity/src/MediaBundleInterface.php @@ -0,0 +1,115 @@ +t('Name'); + $header['description'] = [ + 'data' => $this->t('Description'), + 'class' => [RESPONSIVE_PRIORITY_MEDIUM], + ]; + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + $row['title'] = [ + 'data' => $entity->label(), + 'class' => ['menu-label'], + ]; + $row['description'] = Xss::filterAdmin($entity->getDescription()); + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function render() { + $build = parent::render(); + $build['#empty'] = $this->t('No media bundle available. Add media bundle.', [ + '@link' => Url::fromRoute('entity.media_bundle.add_form')->toString(), + ]); + return $build; + } + +} diff --git a/core/modules/media_entity/src/MediaForm.php b/core/modules/media_entity/src/MediaForm.php new file mode 100644 index 0000000..b728fad --- /dev/null +++ b/core/modules/media_entity/src/MediaForm.php @@ -0,0 +1,238 @@ +entity->isNew()) { + $this->entity->setRevisionLogMessage(NULL); + $this->entity->setPublisherId($this->currentUser()->id()); + $this->entity->setCreatedTime(\Drupal::time()->getRequestTime()); + } + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + $entity_type = $this->entity->getEntityType(); + /** @var \Drupal\media_entity\Entity\MediaBundle $bundle_entity */ + $bundle_entity = $this->entity->bundle->entity; + $account = $this->currentUser(); + $new_revision = $this->entity->bundle->entity->shouldCreateNewRevision(); + + if ($this->operation == 'edit') { + $form['#title'] = $this->t('Edit %bundle_label @label', [ + '%bundle_label' => $bundle_entity->label(), + '@label' => $this->entity->label(), + ]); + } + + $form['advanced'] = [ + '#type' => 'vertical_tabs', + '#weight' => 99, + ]; + + // Add a log field if the "Create new revision" option is checked, or if the + // current user has the ability to check that option. + $form['revision_information'] = [ + '#type' => 'details', + '#title' => $this->t('Revision information'), + // Open by default when "Create new revision" is checked. + '#open' => $new_revision, + '#group' => 'advanced', + '#weight' => 20, + '#access' => $new_revision || $account->hasPermission($entity_type->get('admin_permission')), + '#attributes' => [ + 'class' => ['media-form-revision-information'], + ], + ]; + + $form['revision_information']['revision'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create new revision'), + '#default_value' => $new_revision, + '#access' => $account->hasPermission($entity_type->get('admin_permission')), + ]; + + // Check the revision log checkbox when the log textarea is filled in. + // This must not happen if "Create new revision" is enabled by default, + // since the state would auto-disable the checkbox otherwise. + if (!$new_revision) { + $form['revision_information']['revision']['#states'] = [ + 'checked' => [ + 'textarea[name="revision_log"]' => ['empty' => FALSE], + ], + ]; + } + + $form['revision_information']['revision_log'] = [ + '#type' => 'textarea', + '#title' => $this->t('Revision log message'), + '#rows' => 4, + '#default_value' => $this->entity->getRevisionLogMessage(), + '#description' => $this->t('Briefly describe the changes you have made.'), + ]; + + // Media author information for administrators. + if (isset($form['uid']) || isset($form['created'])) { + $form['author'] = [ + '#type' => 'details', + '#title' => $this->t('Authoring information'), + '#group' => 'advanced', + '#attributes' => [ + 'class' => ['media-form-author'], + ], + '#weight' => 90, + '#optional' => TRUE, + ]; + } + + if (isset($form['uid'])) { + $form['uid']['#group'] = 'author'; + } + + if (isset($form['created'])) { + $form['created']['#group'] = 'author'; + } + + $form['#attached']['library'][] = 'media_entity/media_form'; + + $form['#entity_builders']['update_status'] = [$this, 'updateStatus']; + + return $form; + } + + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $element = parent::actions($form, $form_state); + $media = $this->entity; + + // Add a "Publish" button. + $element['publish'] = $element['submit']; + // If the "Publish" button is clicked, we want to update the status to + // "published". + $element['publish']['#published_status'] = TRUE; + $element['publish']['#dropbutton'] = 'save'; + if ($media->isNew()) { + $element['publish']['#value'] = $this->t('Save and publish'); + } + else { + $element['publish']['#value'] = $media->isPublished() ? $this->t('Save and keep published') : $this->t('Save and publish'); + } + $element['publish']['#weight'] = 0; + + // Add a "Unpublish" button. + $element['unpublish'] = $element['submit']; + // If the "Unpublish" button is clicked, we want to update the status to + // "unpublished". + $element['unpublish']['#published_status'] = FALSE; + $element['unpublish']['#dropbutton'] = 'save'; + if ($media->isNew()) { + $element['unpublish']['#value'] = $this->t('Save as unpublished'); + } + else { + $element['unpublish']['#value'] = !$media->isPublished() ? $this->t('Save and keep unpublished') : $this->t('Save and unpublish'); + } + $element['unpublish']['#weight'] = 10; + + // If already published, the 'publish' button is primary. + if ($media->isPublished()) { + unset($element['unpublish']['#button_type']); + } + // Otherwise, the 'unpublish' button is primary and should come first. + else { + unset($element['publish']['#button_type']); + $element['unpublish']['#weight'] = -10; + } + + // Remove the "Save" button. + $element['submit']['#access'] = FALSE; + + $element['delete']['#access'] = $media->access('delete'); + $element['delete']['#weight'] = 100; + + return $element; + } + + /** + * Entity builder updating the media status with the submitted value. + * + * @param string $entity_type_id + * The entity type identifier. + * @param \Drupal\media_entity\MediaInterface $media + * The media updated with the submitted values. + * @param array $form + * The complete form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @see \Drupal\media\MediaForm::form() + */ + public function updateStatus($entity_type_id, MediaInterface $media, array $form, FormStateInterface $form_state) { + $element = $form_state->getTriggeringElement(); + if (isset($element['#published_status'])) { + $media->setPublished($element['#published_status']); + } + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + // Save as a new revision if requested to do so. + if (!$form_state->isValueEmpty('revision')) { + $this->entity->setNewRevision(); + } + + $insert = $this->entity->isNew(); + $this->entity->save(); + $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()]; + $logger = $this->logger($this->entity->id()); + $t_args = ['@type' => $this->entity->bundle->entity->label(), '%info' => $this->entity->label()]; + + if ($insert) { + $logger->notice('@type: added %info.', $context); + drupal_set_message($this->t('@type %info has been created.', $t_args)); + } + else { + $logger->notice('@type: updated %info.', $context); + drupal_set_message($this->t('@type %info has been updated.', $t_args)); + } + + $form_state->setValue('id', $this->entity->id()); + $form_state->set('id', $this->entity->id()); + $form_state->setRedirectUrl($this->entity->toUrl('canonical')); + } + +} diff --git a/core/modules/media_entity/src/MediaInterface.php b/core/modules/media_entity/src/MediaInterface.php new file mode 100644 index 0000000..23513fc --- /dev/null +++ b/core/modules/media_entity/src/MediaInterface.php @@ -0,0 +1,107 @@ +entityTypeManager = $entity_type_manager; + $this->entityFieldManager = $entity_field_manager; + $this->configFactory = $config_factory; + $this->setConfiguration($configuration); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('entity_field.manager'), + $container->get('config.factory') + ); + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getDefaultThumbnail() { + return ''; + } + + /** + * {@inheritdoc} + */ + public function label() { + return $this->label; + } + + /** + * {@inheritdoc} + */ + public function attachConstraints(MediaInterface $media) {} + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + return []; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {} + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {} + + /** + * {@inheritdoc} + */ + public function getDefaultName(MediaInterface $media) { + return 'media:' . $media->bundle() . ':' . $media->uuid(); + } + +} diff --git a/core/modules/media_entity/src/MediaTypeException.php b/core/modules/media_entity/src/MediaTypeException.php new file mode 100644 index 0000000..9e3ac2c --- /dev/null +++ b/core/modules/media_entity/src/MediaTypeException.php @@ -0,0 +1,45 @@ +element = $element; + } + + /** + * Gets element. + * + * @return string + * Element name. + */ + public function getElement() { + return $this->element; + } + +} diff --git a/core/modules/media_entity/src/MediaTypeInterface.php b/core/modules/media_entity/src/MediaTypeInterface.php new file mode 100644 index 0000000..2a171c2 --- /dev/null +++ b/core/modules/media_entity/src/MediaTypeInterface.php @@ -0,0 +1,88 @@ +alterInfo('media_entity_type_info'); + $this->setCacheBackend($cache_backend, 'media_entity_type_plugins'); + } + +} diff --git a/core/modules/media_entity/src/MediaViewsData.php b/core/modules/media_entity/src/MediaViewsData.php new file mode 100644 index 0000000..f1a4e6f --- /dev/null +++ b/core/modules/media_entity/src/MediaViewsData.php @@ -0,0 +1,24 @@ +currentUser = $current_user; + $this->tempStore = $temp_store_factory->get('media_multiple_delete_confirm'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('user.private_tempstore'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function executeMultiple(array $entities) { + $info = []; + /** @var \Drupal\media_entity\MediaInterface $media */ + foreach ($entities as $media) { + $langcode = $media->language()->getId(); + $info[$media->id()][$langcode] = $langcode; + } + $this->tempStore->set($this->currentUser->id(), $info); + } + + /** + * {@inheritdoc} + */ + public function execute($object = NULL) { + $this->executeMultiple(array($object)); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media_entity\MediaInterface $object */ + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/core/modules/media_entity/src/Plugin/Action/PublishMedia.php b/core/modules/media_entity/src/Plugin/Action/PublishMedia.php new file mode 100644 index 0000000..d223bab --- /dev/null +++ b/core/modules/media_entity/src/Plugin/Action/PublishMedia.php @@ -0,0 +1,38 @@ +setPublished(TRUE)->save(); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media_entity\MediaInterface $object */ + $result = $object->access('update', $account, TRUE) + ->andIf($object->status->access('edit', $account, TRUE)); + + return $return_as_object ? $result : $result->isAllowed(); + } + +} diff --git a/core/modules/media_entity/src/Plugin/Action/SaveMedia.php b/core/modules/media_entity/src/Plugin/Action/SaveMedia.php new file mode 100644 index 0000000..a061cfc --- /dev/null +++ b/core/modules/media_entity/src/Plugin/Action/SaveMedia.php @@ -0,0 +1,37 @@ +changed = 0; + $entity->save(); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media_entity\MediaInterface $object */ + return $object->access('update', $account, $return_as_object); + } + +} diff --git a/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php b/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php new file mode 100644 index 0000000..59e4d2f --- /dev/null +++ b/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php @@ -0,0 +1,38 @@ +setPublished(FALSE)->save(); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media_entity\MediaInterface $object */ + $result = $object->access('update', $account, TRUE) + ->andIf($object->status->access('edit', $account, TRUE)); + + return $return_as_object ? $result : $result->isAllowed(); + } + +} diff --git a/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php b/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php new file mode 100644 index 0000000..a94d121 --- /dev/null +++ b/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php @@ -0,0 +1,188 @@ +renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['label'], + $configuration['view_mode'], + $configuration['third_party_settings'], + $container->get('current_user'), + $container->get('entity.manager')->getStorage('image_style'), + $container->get('renderer') + ); + } + + /** + * {@inheritdoc} + * + * This has to be overriden because FileFormatterBase expects $item to be + * of type \Drupal\file\Plugin\Field\FieldType\FileItem and calls + * isDisplayed() which is not in FieldItemInterface. + */ + protected function needsEntityLoad(EntityReferenceItem $item) { + return !$item->hasNewEntity(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $element = parent::settingsForm($form, $form_state); + + $link_types = [ + 'content' => $this->t('Content'), + 'media' => $this->t('Media entity'), + ]; + $element['image_link']['#options'] = $link_types; + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $link_types = [ + 'content' => $this->t('Linked to content'), + 'media' => $this->t('Linked to media entity'), + ]; + // Display this setting only if image is linked. + $image_link_setting = $this->getSetting('image_link'); + if (isset($link_types[$image_link_setting])) { + $summary[] = $link_types[$image_link_setting]; + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = []; + $media = $this->getEntitiesToView($items, $langcode); + + // Early opt-out if the field is empty. + if (empty($media)) { + return $elements; + } + + $url = NULL; + $image_link_setting = $this->getSetting('image_link'); + // Check if the formatter involves a link. + if ($image_link_setting == 'content') { + $entity = $items->getEntity(); + if (!$entity->isNew()) { + $url = $entity->toUrl(); + } + } + elseif ($image_link_setting == 'media') { + $link_media = TRUE; + } + + $image_style_setting = $this->getSetting('image_style'); + + /** @var \Drupal\media_entity\MediaInterface $media_item */ + foreach ($media as $delta => $media_item) { + if (isset($link_media)) { + $url = $media_item->toUrl(); + } + + $elements[$delta] = [ + '#theme' => 'image_formatter', + '#item' => $media_item->get('thumbnail'), + '#item_attributes' => [], + '#image_style' => $image_style_setting, + '#url' => $url, + ]; + + // Collect cache tags to be added for each item in the field. + $this->renderer->addCacheableDependency($elements[$delta], $media_item); + } + + // Collect cache tags related to the image style setting. + if ($image_link_setting && ($image_style = $this->imageStyleStorage->load($image_style_setting))) { + $this->renderer->addCacheableDependency($elements, $image_style); + } + + return $elements; + } + + /** + * {@inheritdoc} + */ + public static function isApplicable(FieldDefinitionInterface $field_definition) { + // This formatter is only available for entity types that reference + // media entities. + $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type'); + return $target_type == 'media'; + } + +} diff --git a/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php new file mode 100644 index 0000000..3a3b40c --- /dev/null +++ b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php @@ -0,0 +1,53 @@ +configFactory->get('media_entity.settings')->get('icon_base') . '/generic.png'; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['text'] = [ + '#type' => 'markup', + '#markup' => $this->t("This type provider doesn't need configuration."), + ]; + + return $form; + } + +} diff --git a/core/modules/media_entity/src/Plugin/QueueWorker/thumbnailDownloader.php b/core/modules/media_entity/src/Plugin/QueueWorker/thumbnailDownloader.php new file mode 100644 index 0000000..165f6ab --- /dev/null +++ b/core/modules/media_entity/src/Plugin/QueueWorker/thumbnailDownloader.php @@ -0,0 +1,30 @@ +automaticallySetThumbnail(); + $entity->save(); + } + } + +} diff --git a/core/modules/media_entity/src/Plugin/views/wizard/Media.php b/core/modules/media_entity/src/Plugin/views/wizard/Media.php new file mode 100644 index 0000000..3a2d9da --- /dev/null +++ b/core/modules/media_entity/src/Plugin/views/wizard/Media.php @@ -0,0 +1,84 @@ + [ + 'value' => TRUE, + 'table' => 'media_field_data', + 'field' => 'status', + 'plugin_id' => 'boolean', + 'entity_type' => 'media', + 'entity_field' => 'status', + ], + ]; + + /** + * {@inheritdoc} + */ + public function getAvailableSorts() { + return [ + 'media_field_data-name:DESC' => $this->t('Media name'), + ]; + } + + /** + * {@inheritdoc} + */ + protected function defaultDisplayOptions() { + $display_options = parent::defaultDisplayOptions(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + $display_options['access']['options']['perm'] = 'view media'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + // Add the name field, so that the display has content if the user switches + // to a row style that uses fields. + /* Field: Media: Name */ + $display_options['fields']['name']['id'] = 'name'; + $display_options['fields']['name']['table'] = 'media_field_data'; + $display_options['fields']['name']['field'] = 'name'; + $display_options['fields']['name']['entity_type'] = 'media'; + $display_options['fields']['name']['entity_field'] = 'media'; + $display_options['fields']['name']['label'] = ''; + $display_options['fields']['name']['alter']['alter_text'] = 0; + $display_options['fields']['name']['alter']['make_link'] = 0; + $display_options['fields']['name']['alter']['absolute'] = 0; + $display_options['fields']['name']['alter']['trim'] = 0; + $display_options['fields']['name']['alter']['word_boundary'] = 0; + $display_options['fields']['name']['alter']['ellipsis'] = 0; + $display_options['fields']['name']['alter']['strip_tags'] = 0; + $display_options['fields']['name']['alter']['html'] = 0; + $display_options['fields']['name']['hide_empty'] = 0; + $display_options['fields']['name']['empty_zero'] = 0; + $display_options['fields']['name']['settings']['link_to_entity'] = 1; + $display_options['fields']['name']['plugin_id'] = 'field'; + + return $display_options; + } + +} diff --git a/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php b/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php new file mode 100644 index 0000000..72bceb0 --- /dev/null +++ b/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php @@ -0,0 +1,95 @@ + [ + 'value' => TRUE, + 'table' => 'media_field_revision', + 'field' => 'status', + 'plugin_id' => 'boolean', + 'entity_type' => 'media', + 'entity_field' => 'status', + ], + ]; + + /** + * {@inheritdoc} + */ + protected function defaultDisplayOptions() { + $display_options = parent::defaultDisplayOptions(); + + // Add permission-based access control. + $display_options['access']['type'] = 'perm'; + $display_options['access']['options']['perm'] = 'view all revisions'; + + // Remove the default fields, since we are customizing them here. + unset($display_options['fields']); + + /* Field: Media revision: Created date */ + $display_options['fields']['changed']['id'] = 'changed'; + $display_options['fields']['changed']['table'] = 'media_field_revision'; + $display_options['fields']['changed']['field'] = 'changed'; + $display_options['fields']['changed']['entity_type'] = 'media'; + $display_options['fields']['changed']['entity_field'] = 'changed'; + $display_options['fields']['changed']['alter']['alter_text'] = FALSE; + $display_options['fields']['changed']['alter']['make_link'] = FALSE; + $display_options['fields']['changed']['alter']['absolute'] = FALSE; + $display_options['fields']['changed']['alter']['trim'] = FALSE; + $display_options['fields']['changed']['alter']['word_boundary'] = FALSE; + $display_options['fields']['changed']['alter']['ellipsis'] = FALSE; + $display_options['fields']['changed']['alter']['strip_tags'] = FALSE; + $display_options['fields']['changed']['alter']['html'] = FALSE; + $display_options['fields']['changed']['hide_empty'] = FALSE; + $display_options['fields']['changed']['empty_zero'] = FALSE; + $display_options['fields']['changed']['plugin_id'] = 'field'; + $display_options['fields']['changed']['type'] = 'timestamp'; + $display_options['fields']['changed']['settings']['date_format'] = 'medium'; + $display_options['fields']['changed']['settings']['custom_date_format'] = ''; + $display_options['fields']['changed']['settings']['timezone'] = ''; + + /* Field: Media revision: Name */ + $display_options['fields']['name']['id'] = 'name'; + $display_options['fields']['name']['table'] = 'media_field_revision'; + $display_options['fields']['name']['field'] = 'name'; + $display_options['fields']['name']['entity_type'] = 'media'; + $display_options['fields']['name']['entity_field'] = 'name'; + $display_options['fields']['name']['label'] = ''; + $display_options['fields']['name']['alter']['alter_text'] = 0; + $display_options['fields']['name']['alter']['make_link'] = 0; + $display_options['fields']['name']['alter']['absolute'] = 0; + $display_options['fields']['name']['alter']['trim'] = 0; + $display_options['fields']['name']['alter']['word_boundary'] = 0; + $display_options['fields']['name']['alter']['ellipsis'] = 0; + $display_options['fields']['name']['alter']['strip_tags'] = 0; + $display_options['fields']['name']['alter']['html'] = 0; + $display_options['fields']['name']['hide_empty'] = 0; + $display_options['fields']['name']['empty_zero'] = 0; + $display_options['fields']['name']['settings']['link_to_entity'] = 0; + $display_options['fields']['name']['plugin_id'] = 'field'; + + return $display_options; + } + +} diff --git a/core/modules/media_entity/src/Tests/BasicTest.php b/core/modules/media_entity/src/Tests/BasicTest.php new file mode 100644 index 0000000..2589c53 --- /dev/null +++ b/core/modules/media_entity/src/Tests/BasicTest.php @@ -0,0 +1,193 @@ +testBundle = $this->drupalCreateMediaBundle(); + } + + /** + * Tests creating a media bundle programmatically. + */ + public function testMediaBundleCreation() { + $bundle = $this->drupalCreateMediaBundle(); + /** @var \Drupal\media_entity\MediaBundleInterface $bundle_storage */ + $bundle_storage = $this->container->get('entity.manager')->getStorage('media_bundle'); + + $bundle_exists = (bool) $bundle_storage->load($bundle->id()); + $this->assertTrue($bundle_exists, 'The new media bundle has been created in the database.'); + + // Test default bundle created from default configuration. + $this->container->get('module_installer')->install(['media_entity_test_bundle']); + $test_bundle = $bundle_storage->load('test'); + $this->assertTrue((bool) $test_bundle, 'The media bundle from default configuration has been created in the database.'); + $this->assertEqual($test_bundle->get('label'), 'Test bundle', 'Correct label detected.'); + $this->assertEqual($test_bundle->get('description'), 'Test bundle.', 'Correct description detected.'); + $this->assertEqual($test_bundle->get('type'), 'generic', 'Correct plugin ID detected.'); + $this->assertEqual($test_bundle->get('type_configuration'), [], 'Correct plugin configuration detected.'); + $this->assertEqual($test_bundle->get('field_map'), [], 'Correct field map detected.'); + } + + /** + * Tests creating a media entity programmatically. + */ + public function testMediaEntityCreation() { + $media = Media::create([ + 'bundle' => $this->testBundle->id(), + 'name' => 'Unnamed', + ]); + $media->save(); + + $media_not_exist = (bool) Media::load(rand(1000, 9999)); + $this->assertFalse($media_not_exist, 'The media entity does not exist.'); + + $media_exists = (bool) Media::load($media->id()); + $this->assertTrue($media_exists, 'The new media entity has been created in the database.'); + $this->assertEqual($media->bundle(), $this->testBundle->id(), 'The media was created with correct bundle.'); + $this->assertEqual($media->label(), 'Unnamed', 'The media was corrected with correct name.'); + + // Test the creation of a media without user-defined label and check if a + // default name is provided. + $media = Media::create([ + 'bundle' => $this->testBundle->id(), + ]); + $media->save(); + $expected_name = 'media' . ':' . $this->testBundle->id() . ':' . $media->uuid(); + $this->assertEqual($media->bundle(), $this->testBundle->id(), 'The media was created with correct bundle.'); + $this->assertEqual($media->label(), $expected_name, 'The media was correctly created with a default name.'); + + } + + /** + * Runs basic tests for media_access function. + */ + public function testMediaAccess() { + // Create users and roles. + $admin = $this->drupalCreateUser(['administer media'], 'editor'); + $user = $this->drupalCreateUser([], 'user'); + + $permissions = [ + 'view media', + 'create media', + 'update media', + 'update any media', + 'delete media', + 'delete any media', + ]; + + $roles = []; + foreach ($permissions as $permission) { + $roles[$permission] = $this->createRole([$permission]); + } + + // Create media. + $media = Media::create([ + 'bundle' => $this->testBundle->id(), + 'name' => 'Unnamed', + ]); + $media->save(); + + $user_media = Media::create([ + 'bundle' => $this->testBundle->id(), + 'name' => 'Unnamed', + 'uid' => $user->id(), + ]); + $user_media->save(); + + // Test 'administer media' permission. + $this->drupalLogin($admin); + $this->drupalGet('media/' . $user_media->id()); + $this->assertResponse(200); + $this->drupalGet('media/' . $user_media->id() . '/edit'); + $this->assertResponse(200); + $this->drupalGet('media/' . $user_media->id() . '/delete'); + $this->assertResponse(200); + + // Test 'view media' permission. + $this->drupalLogin($user); + $this->drupalGet('media/' . $media->id()); + $this->assertResponse(403); + + $user->addRole($roles['view media']); + $user->save(); + + $this->drupalGet('media/' . $media->id()); + $this->assertResponse(200); + + // Test 'create media' permissions. + $this->drupalLogin($user); + $this->drupalGet('media/add/' . $this->testBundle->id()); + $this->assertResponse(403); + + $user->addRole($roles['create media']); + $user->save(); + + $this->drupalGet('media/add/' . $this->testBundle->id()); + $this->assertResponse(200); + + // Test 'update media' and 'delete media' permissions. + $this->drupalGet('media/' . $user_media->id() . '/edit'); + $this->assertResponse(403); + + $this->drupalGet('media/' . $user_media->id() . '/delete'); + $this->assertResponse(403); + + $user->addRole($roles['update media']); + $user->addRole($roles['delete media']); + $user->save(); + + $this->drupalGet('media/' . $user_media->id() . '/edit'); + $this->assertResponse(200); + + $this->drupalGet('media/' . $user_media->id() . '/delete'); + $this->assertResponse(200); + + // Test 'update any media' and 'delete any media' permissions. + $this->drupalGet('media/' . $media->id() . '/edit'); + $this->assertResponse(403); + + $this->drupalGet('media/' . $media->id() . '/delete'); + $this->assertResponse(403); + + $user->addRole($roles['update any media']); + $user->addRole($roles['delete any media']); + $user->save(); + + $this->drupalGet('media/' . $media->id() . '/edit'); + $this->assertResponse(200); + + $this->drupalGet('media/' . $media->id() . '/delete'); + $this->assertResponse(200); + } + +} diff --git a/core/modules/media_entity/src/Tests/MediaTestTrait.php b/core/modules/media_entity/src/Tests/MediaTestTrait.php new file mode 100644 index 0000000..f7646aa --- /dev/null +++ b/core/modules/media_entity/src/Tests/MediaTestTrait.php @@ -0,0 +1,48 @@ +randomMachineName()); + } + else { + $id = $values['bundle']; + } + $values += [ + 'id' => $id, + 'label' => $id, + 'type' => $type_name, + 'type_configuration' => [], + 'field_map' => [], + 'new_revision' => FALSE, + ]; + + $bundle = MediaBundle::create($values); + $status = $bundle->save(); + + $this->assertEqual($status, SAVED_NEW, t('Created media bundle %bundle.', ['%bundle' => $bundle->id()])); + + return $bundle; + } + +} diff --git a/core/modules/media_entity/src/Tests/MediaUITest.php b/core/modules/media_entity/src/Tests/MediaUITest.php new file mode 100644 index 0000000..c2a7c31 --- /dev/null +++ b/core/modules/media_entity/src/Tests/MediaUITest.php @@ -0,0 +1,512 @@ +drupalPlaceBlock('local_actions_block'); + $this->drupalPlaceBlock('local_tasks_block'); + $this->adminUser = $this->drupalCreateUser([ + 'administer media', + 'administer media fields', + 'administer media form display', + 'administer media display', + 'administer media bundles', + // Media entity permissions. + 'view media', + 'create media', + 'update media', + 'update any media', + 'delete media', + 'delete any media', + 'access media overview', + // Other permissions. + 'administer views', + 'access content overview', + 'view all revisions', + ]); + $this->drupalLogin($this->adminUser); + + $this->nonAdminUser = $this->drupalCreateUser([ + // Media entity permissions. + 'view media', + 'create media', + 'update media', + 'update any media', + 'delete media', + 'delete any media', + 'access media overview', + // Other permissions. + 'administer views', + 'access content overview', + ]); + } + + /** + * Tests a media bundle administration. + */ + public function testMediaBundles() { + $this->container->get('module_installer')->install(['media_entity_test_type']); + + // Test and create one media bundle. + $bundle = $this->createMediaBundle(); + $bundle_id = $bundle['id']; + unset($bundle['id']); + + // Check if all action links exist. + $this->assertLinkByHref('admin/structure/media/add'); + $this->assertLinkByHref('admin/structure/media/manage/' . $bundle_id . '/fields'); + $this->assertLinkByHref('admin/structure/media/manage/' . $bundle_id . '/form-display'); + $this->assertLinkByHref('admin/structure/media/manage/' . $bundle_id . '/display'); + + // Assert that fields have expected values before editing. + $this->drupalGet('admin/structure/media/manage/' . $bundle_id); + $this->assertFieldByName('label', $bundle['label'], 'Label field has correct value.'); + $this->assertFieldByName('description', $bundle['description'], 'Description field has a correct value.'); + $this->assertFieldByName('type', $bundle['type'], 'Generic plugin is selected.'); + $this->assertNoFieldChecked('edit-options-new-revision', 'Revision checkbox is not checked.'); + $this->assertFieldChecked('edit-options-status', 'Status checkbox is checked.'); + $this->assertNoFieldChecked('edit-options-queue-thumbnail-downloads', 'Queue thumbnail checkbox is not checked.'); + $this->assertText('Create new revision', 'Revision checkbox label found.'); + $this->assertText('Automatically create a new revision of media entities. Users with the Administer media permission will be able to override this option.', 'Revision help text found'); + $this->assertText('Download thumbnails via a queue.', 'Queue thumbnails help text found'); + $this->assertText('Entities will be automatically published when they are created.', 'Published help text found'); + $this->assertText("This type provider doesn't need configuration."); + $this->assertText('No metadata fields available.'); + $this->assertText('Media type plugins can provide metadata fields such as title, caption, size information, credits, ... Media entity can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.'); + + // Try to change media type and check if new configuration sub-form appears. + $commands = $this->drupalPostAjaxForm(NULL, ['type' => 'test_type'], 'type'); + // WebTestBase::drupalProcessAjaxResponse() won't correctly execute our ajax + // commands so we have to do it manually. Code below is based on the logic + // in that function. + $content = $this->content; + $dom = new \DOMDocument(); + @$dom->loadHTML($content); + $xpath = new \DOMXPath($dom); + foreach ($commands as $command) { + if ($command['command'] == 'insert' && $command['method'] == 'replaceWith') { + $wrapperNode = $xpath->query('//*[@id="' . ltrim($command['selector'], '#') . '"]')->item(0); + $newDom = new \DOMDocument(); + @$newDom->loadHTML('
' . $command['data'] . '
'); + $newNode = @$dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE); + $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode); + $content = $dom->saveHTML(); + $this->setRawContent($content); + } + } + $this->assertFieldByName('type_configuration[test_type][test_config_value]', 'This is default value.'); + $this->assertText('Field 1', 'First metadata field found.'); + $this->assertText('Field 2', 'Second metadata field found.'); + $this->assertFieldByName('field_mapping[field_1]', '_none', 'First metadata field is not mapped by default.'); + $this->assertFieldByName('field_mapping[field_2]', '_none', 'Second metadata field is not mapped by default.'); + + // Test if the edit machine name button is disabled. + $elements = $this->xpath('//*[@id="edit-label-machine-name-suffix"]/span[@class="admin-link"]'); + $this->assertTrue(empty($elements), 'Edit machine name not found.'); + + // Edit and save media bundle form fields with new values. + $bundle['label'] = $this->randomMachineName(); + $bundle['description'] = $this->randomMachineName(); + $bundle['type'] = 'test_type'; + $bundle['type_configuration[test_type][test_config_value]'] = 'This is new config value.'; + $bundle['field_mapping[field_1]'] = 'name'; + $bundle['options[new_revision]'] = TRUE; + $bundle['options[status]'] = FALSE; + $bundle['options[queue_thumbnail_downloads]'] = TRUE; + + $this->drupalPostForm(NULL, $bundle, t('Save media bundle')); + + // Test if edit worked and if new field values have been saved as expected. + $this->drupalGet('admin/structure/media/manage/' . $bundle_id); + $this->assertFieldByName('label', $bundle['label'], 'Label field has correct value.'); + $this->assertFieldByName('description', $bundle['description'], 'Description field has correct value.'); + $this->assertFieldByName('type', $bundle['type'], 'Test type is selected.'); + $this->assertFieldChecked('edit-options-new-revision', 'Revision checkbox is checked.'); + $this->assertFieldChecked('edit-options-queue-thumbnail-downloads', 'Queue thumbnail checkbox is checked.'); + $this->assertNoFieldChecked('edit-options-status', 'Status checkbox is not checked.'); + $this->assertFieldByName('type_configuration[test_type][test_config_value]', 'This is new config value.'); + $this->assertText('Field 1', 'First metadata field found.'); + $this->assertText('Field 2', 'Second metadata field found.'); + $this->assertFieldByName('field_mapping[field_1]', 'name', 'First metadata field is mapped to the name field.'); + $this->assertFieldByName('field_mapping[field_2]', '_none', 'Second metadata field is not mapped.'); + + /** @var \Drupal\media_entity\MediaBundleInterface $loaded_bundle */ + $loaded_bundle = $this->container->get('entity_type.manager') + ->getStorage('media_bundle') + ->load($bundle_id); + $this->assertEqual($loaded_bundle->id(), $bundle_id, 'Media bundle ID saved correctly.'); + $this->assertEqual($loaded_bundle->label(), $bundle['label'], 'Media bundle label saved correctly.'); + $this->assertEqual($loaded_bundle->getDescription(), $bundle['description'], 'Media bundle description saved correctly.'); + $this->assertEqual($loaded_bundle->getType()->getPluginId(), $bundle['type'], 'Media bundle type saved correctly.'); + $this->assertEqual($loaded_bundle->getType()->getConfiguration()['test_config_value'], $bundle['type_configuration[test_type][test_config_value]'], 'Media bundle type configuration saved correctly.'); + $this->assertTrue($loaded_bundle->shouldCreateNewRevision(), 'New revisions are configured to be created.'); + $this->assertTrue($loaded_bundle->getQueueThumbnailDownloads(), 'Thumbnails are created through queues.'); + $this->assertFalse($loaded_bundle->getStatus(), 'Default status is unpublished.'); + $this->assertEqual($loaded_bundle->field_map, ['field_1' => $bundle['field_mapping[field_1]']], 'Field mapping was saved correctly.'); + + // Test that a media being created with default status to "FALSE" will be + // created unpublished. + /** @var MediaInterface $unpublished_media */ + $unpublished_media = Media::create(['name' => 'unpublished test media', 'bundle' => $loaded_bundle->id()]); + $this->assertFalse($unpublished_media->isPublished(), 'Unpublished media correctly created.'); + + // Tests media bundle delete form. + $this->clickLink(t('Delete')); + $this->assertUrl('admin/structure/media/manage/' . $bundle_id . '/delete'); + $this->drupalPostForm(NULL, [], t('Delete')); + $this->assertUrl('admin/structure/media'); + $this->assertRaw(t('The media bundle %name has been deleted.', ['%name' => $bundle['label']])); + $this->assertNoRaw(Xss::filterAdmin($bundle['description'])); + // Test bundle delete prevention when there is existing media. + $bundle2 = $this->createMediaBundle(); + $media = Media::create(['name' => 'lorem ipsum', 'bundle' => $bundle2['id']]); + $media->save(); + $this->drupalGet('admin/structure/media/manage/' . $bundle2['id']); + $this->clickLink(t('Delete')); + $this->assertUrl('admin/structure/media/manage/' . $bundle2['id'] . '/delete'); + $this->assertNoFieldById('edit-submit'); + $this->assertRaw(t('%type is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the %type content.', ['%type' => $bundle2['label']])); + } + + /** + * Tests the media actions (add/edit/delete). + */ + public function testMediaWithOnlyOneBundle() { + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + $bundle = $this->drupalCreateMediaBundle(['new_revision' => TRUE]); + + // Assert that media item list is empty. + $this->drupalGet('admin/content/media'); + $this->assertResponse(200); + $this->assertText('No content available.'); + + $this->drupalGet('media/add'); + $this->assertResponse(200); + $this->assertUrl('media/add/' . $bundle->id()); + $this->assertFieldChecked('edit-revision', 'New revision should always be created when a new entity is being created.'); + + // Tests media item add form. + $edit = [ + 'name[0][value]' => $this->randomMachineName(), + 'revision_log' => $this->randomString(), + ]; + $this->drupalPostForm('media/add', $edit, t('Save and publish')); + $this->assertTitle($edit['name[0][value]'] . ' | Drupal'); + $media_id = $this->container->get('entity.query')->get('media')->execute(); + $media_id = reset($media_id); + /** @var \Drupal\media_entity\MediaInterface $media */ + $media = $this->container->get('entity_type.manager') + ->getStorage('media') + ->loadUnchanged($media_id); + $this->assertEqual($media->getRevisionLogMessage(), $edit['revision_log'], 'Revision log was saved.'); + + // Test if the media list contains exactly 1 media bundle. + $this->drupalGet('admin/content/media'); + $this->assertResponse(200); + $this->assertText($edit['name[0][value]']); + + // Tests media edit form. + $bundle->setNewRevision(FALSE); + $bundle->save(); + $this->drupalGet('media/' . $media_id . '/edit'); + $this->assertNoFieldChecked('edit-revision', 'New revisions are disabled by default.'); + $edit['name[0][value]'] = $this->randomMachineName(); + $this->drupalPostForm(NULL, $edit, t('Save and keep published')); + $this->assertTitle($edit['name[0][value]'] . ' | Drupal'); + + // Assert that the media list updates after an edit. + $this->drupalGet('admin/content/media'); + $this->assertResponse(200); + $this->assertText($edit['name[0][value]']); + + // Test that there is no empty vertical tabs element, if the container is + // empty (see #2750697). + // Make the "Publisher ID" and "Created" fields hidden. + $edit = [ + 'fields[created][parent]' => 'hidden', + 'fields[uid][parent]' => 'hidden', + ]; + $this->drupalPostForm('/admin/structure/media/manage/' . $bundle->id . '/form-display', $edit, t('Save')); + // Assure we are testing with a user without permission to manage revisions. + $this->drupalLogout(); + $this->drupalLogin($this->nonAdminUser); + // Check the container is not present. + $this->drupalGet('media/' . $media_id . '/edit'); + // An empty tab container would look like this. + $raw_html = '
' . "\n" . '
'; + $this->assertNoRaw($raw_html); + // Continue testing as admin. + $this->drupalLogout(); + $this->drupalLogin($this->adminUser); + + // Enable revisions by default. + $bundle->setNewRevision(TRUE); + $bundle->save(); + $this->drupalGet('media/' . $media_id . '/edit'); + $this->assertFieldChecked('edit-revision', 'New revisions are disabled by default.'); + $edit = [ + 'name[0][value]' => $this->randomMachineName(), + 'revision_log' => $this->randomString(), + ]; + $this->drupalPostForm(NULL, $edit, t('Save and keep published')); + $this->assertTitle($edit['name[0][value]'] . ' | Drupal'); + /** @var \Drupal\media_entity\MediaInterface $media */ + $media = $this->container->get('entity_type.manager') + ->getStorage('media') + ->loadUnchanged($media_id); + $this->assertEqual($media->getRevisionLogMessage(), $edit['revision_log'], 'Revision log was saved.'); + + // Tests media delete form. + $this->drupalPostForm('media/' . $media_id . '/delete', [], t('Delete')); + $media_id = \Drupal::entityQuery('media')->execute(); + $this->assertFalse($media_id); + + // Assert that the media list is empty after deleting the media item. + $this->drupalGet('admin/content/media'); + $this->assertResponse(200); + $this->assertNoText($edit['name[0][value]']); + $this->assertText('No content available.'); + } + + /** + * Tests the views wizards provided by the media module. + */ + public function testMediaViewsWizard() { + $bundle = $this->drupalCreateMediaBundle(); + $data = [ + 'name' => $this->randomMachineName(), + 'bundle' => $bundle->id(), + 'type' => 'Unknown', + 'uid' => $this->adminUser->id(), + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(), + 'status' => Media::PUBLISHED, + ]; + $media = Media::create($data); + $media->save(); + + // Test the Media wizard. + $this->drupalPostForm('admin/structure/views/add', [ + 'label' => 'media view', + 'id' => 'media_test', + 'show[wizard_key]' => 'media', + 'page[create]' => 1, + 'page[title]' => 'media_test', + 'page[path]' => 'media_test', + ], t('Save and edit')); + + $this->drupalGet('media_test'); + $this->assertText($data['name']); + + user_role_revoke_permissions('anonymous', ['access content']); + $this->drupalLogout(); + $this->drupalGet('media_test'); + $this->assertResponse(403); + + $this->drupalLogin($this->adminUser); + + // Test the MediaRevision wizard. + $this->drupalPostForm('admin/structure/views/add', [ + 'label' => 'media revision view', + 'id' => 'media_revision', + 'show[wizard_key]' => 'media_revision', + 'page[create]' => 1, + 'page[title]' => 'media_revision', + 'page[path]' => 'media_revision', + ], t('Save and edit')); + + $this->drupalGet('media_revision'); + // Check only for the label of the changed field as we want to only test + // if the field is present and not its value. + $this->assertText($data['name']); + + user_role_revoke_permissions('anonymous', ['view revisions']); + $this->drupalLogout(); + $this->drupalGet('media_revision'); + $this->assertResponse(403); + } + + /** + * Tests the "media/add" and "admin/content/media" pages. + * + * Tests if the "media/add" page gives you a selecting option if there are + * multiple media bundles available. + */ + public function testMediaWithMultipleBundles() { + // Test access to media overview page. + $this->drupalLogout(); + $this->drupalGet('admin/content/media'); + $this->assertResponse(403); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('admin/content'); + + // Test there is a media tab in the menu. + $this->clickLink('Media'); + $this->assertResponse(200); + $this->assertText('No content available.'); + + // Tests and creates the first media bundle. + $first_media_bundle = $this->createMediaBundle(); + + // Test and create a second media bundle. + $second_media_bundle = $this->createMediaBundle(); + + // Test if media/add displays two media bundle options. + $this->drupalGet('media/add'); + + // Checks for the first media bundle. + $this->assertRaw($first_media_bundle['label']); + $this->assertRaw(Xss::filterAdmin($first_media_bundle['description'])); + + // Checks for the second media bundle. + $this->assertRaw($second_media_bundle['label']); + $this->assertRaw(Xss::filterAdmin($second_media_bundle['description'])); + + // Continue testing media bundle filter. + $this->doTestMediaBundleFilter($first_media_bundle, $second_media_bundle); + } + + /** + * Creates and tests a new media bundle. + * + * @return array + * Returns the media bundle fields. + */ + public function createMediaBundle() { + // Generates and holds all media bundle fields. + $name = $this->randomMachineName(); + $edit = [ + 'id' => strtolower($name), + 'label' => $name, + 'type' => 'generic', + 'description' => $this->randomMachineName(), + ]; + + // Create new media bundle. + $this->drupalPostForm('admin/structure/media/add', $edit, t('Save media bundle')); + $this->assertText('The media bundle ' . $name . ' has been added.'); + + // Check if media bundle is successfully created. + $this->drupalGet('admin/structure/media'); + $this->assertResponse(200); + $this->assertRaw($edit['label']); + $this->assertRaw(Xss::filterAdmin($edit['description'])); + + return $edit; + } + + /** + * Creates a media item in the media bundle that is passed along. + * + * @param array $media_bundle + * The media bundle the media item should be assigned to. + * + * @return array + * Returns the + */ + public function createMediaItem($media_bundle) { + // Define the media item name. + $name = $this->randomMachineName(); + $edit = [ + 'name[0][value]' => $name, + ]; + // Save it and retrieve new media item ID, then return all information. + $this->drupalPostForm('media/add/' . $media_bundle['id'], $edit, t('Save and publish')); + $this->assertTitle($edit['name[0][value]'] . ' | Drupal'); + $media_id = \Drupal::entityQuery('media')->execute(); + $media_id = reset($media_id); + $edit['id'] = $media_id; + + return $edit; + } + + /** + * Tests the media list filter functionality. + */ + public function doTestMediaBundleFilter($first_media_bundle, $second_media_bundle) { + // Assert that the list is not empty and contains at least 2 media items + // with each a different media bundle. + (is_array($first_media_bundle) && is_array($second_media_bundle) ?: $this->assertTrue(FALSE)); + + $first_media_item = $this->createMediaItem($first_media_bundle); + $second_media_item = $this->createMediaItem($second_media_bundle); + + // Go to media item list. + $this->drupalGet('admin/content/media'); + $this->assertResponse(200); + $this->assertLink('Add media'); + + // Assert that all available media items are in the list. + $this->assertText($first_media_item['name[0][value]']); + $this->assertText($first_media_bundle['label']); + $this->assertText($second_media_item['name[0][value]']); + $this->assertText($second_media_bundle['label']); + + // Filter for each bundle and assert that the list has been updated. + $this->drupalGet('admin/content/media', ['query' => ['provider' => $first_media_bundle['id']]]); + $this->assertResponse(200); + $this->assertText($first_media_item['name[0][value]']); + $this->assertText($first_media_bundle['label']); + $this->assertNoText($second_media_item['name[0][value]']); + + $this->drupalGet('admin/content/media', ['query' => ['provider' => $second_media_bundle['id']]]); + $this->assertResponse(200); + $this->assertNoText($first_media_item['name[0][value]']); + $this->assertText($second_media_item['name[0][value]']); + $this->assertText($second_media_bundle['label']); + + // Filter all and check for all items again. + $this->drupalGet('admin/content/media', ['query' => ['provider' => 'All']]); + $this->assertResponse(200); + $this->assertText($first_media_item['name[0][value]']); + $this->assertText($first_media_bundle['label']); + $this->assertText($second_media_item['name[0][value]']); + $this->assertText($second_media_bundle['label']); + } + +} diff --git a/core/modules/media_entity/src/Tests/Views/BulkFormTest.php b/core/modules/media_entity/src/Tests/Views/BulkFormTest.php new file mode 100644 index 0000000..3349dfb --- /dev/null +++ b/core/modules/media_entity/src/Tests/Views/BulkFormTest.php @@ -0,0 +1,171 @@ +testBundle = $this->drupalCreateMediaBundle(); + + // Create some test media entities. + $this->mediaEntities = []; + for ($i = 1; $i <= 5; $i++) { + $media = Media::create([ + 'bundle' => $this->testBundle->id(), + 'name' => $this->randomMachineName(), + ]); + $media->save(); + + $this->mediaEntities[] = $media; + } + + // Check that all created entities are present in the test view. + $view = Views::getView('test_media_entity_bulk_form'); + $view->execute(); + $this->assertEqual(count($view->result), 5, 'All created media entities are present in the view.'); + + $this->adminUser = $this->drupalCreateUser([ + 'view media', + 'update any media', + 'delete any media', + ]); + $this->drupalLogin($this->adminUser); + + // Check the operations are accessible to the logged in user. + $this->drupalGet('test-media-entity-bulk-form'); + $elements = $this->xpath('//select[@id="edit-action"]//option'); + // Current available actions: Delete, Save, Publish, Unpublish. + $this->assertIdentical(count($elements), 4, 'All media operations are found.'); + } + + /** + * Tests the media bulk form. + */ + public function testBulkForm() { + + // Test unpublishing in bulk. + $edit = [ + 'media_bulk_form[0]' => TRUE, + 'media_bulk_form[1]' => TRUE, + 'media_bulk_form[2]' => TRUE, + 'action' => 'media_unpublish_action', + ]; + $this->drupalPostForm(NULL, $edit, t('Apply to selected items')); + $this->assertText("Unpublish media was applied to 3 items"); + $media1_status = $this->loadMedia(1)->isPublished(); + $this->assertEqual(FALSE, $media1_status, 'First media entity was unpublished correctly.'); + $media2_status = $this->loadMedia(2)->isPublished(); + $this->assertEqual(FALSE, $media2_status, 'Second media entity was unpublished correctly.'); + $media3_status = $this->loadMedia(3)->isPublished(); + $this->assertEqual(FALSE, $media3_status, 'Third media entity was unpublished correctly.'); + + // Test publishing in bulk. + $edit = [ + 'media_bulk_form[0]' => TRUE, + 'media_bulk_form[1]' => TRUE, + 'action' => 'media_publish_action', + ]; + $this->drupalPostForm(NULL, $edit, t('Apply to selected items')); + $this->assertText("Publish media was applied to 2 items"); + $media1_status = $this->loadMedia(1)->isPublished(); + $this->assertEqual(TRUE, $media1_status, 'First media entity was published back correctly.'); + $media2_status = $this->loadMedia(2)->isPublished(); + $this->assertEqual(TRUE, $media2_status, 'Second media entity was published back correctly.'); + + // Test deletion in bulk. + $edit = [ + 'media_bulk_form[0]' => TRUE, + 'media_bulk_form[1]' => TRUE, + 'action' => 'media_delete_action', + ]; + $this->drupalPostForm(NULL, $edit, t('Apply to selected items')); + + $this->assertText("Are you sure you want to delete these items?"); + $label1 = $this->loadMedia(1)->label(); + $this->assertRaw('
  • ' . $label1 . '
  • '); + $label2 = $this->loadMedia(2)->label(); + $this->assertRaw('
  • ' . $label2 . '
  • '); + + $this->drupalPostForm(NULL, [], t('Delete')); + + $media = $this->loadMedia(1); + $this->assertNull($media, 'Media 1 has been correctly deleted.'); + $media = $this->loadMedia(2); + $this->assertNull($media, 'Media 2 has been correctly deleted.'); + + $this->assertText('Deleted 2 media entities.'); + } + + /** + * Load the specified media from the storage. + * + * @param int $id + * The media identifier. + * + * @return \Drupal\media_entity\MediaInterface + * The loaded media entity. + */ + protected function loadMedia($id) { + /** @var \Drupal\media_entity\MediaStorage $storage */ + $storage = $this->container->get('entity.manager')->getStorage('media'); + return $storage->loadUnchanged($id); + } + +} diff --git a/core/modules/media_entity/src/Tests/Views/WizardTest.php b/core/modules/media_entity/src/Tests/Views/WizardTest.php new file mode 100644 index 0000000..ee91ccd --- /dev/null +++ b/core/modules/media_entity/src/Tests/Views/WizardTest.php @@ -0,0 +1,94 @@ +randomMachineName(16); + $view['id'] = strtolower($this->randomMachineName(16)); + $view['show[wizard_key]'] = 'media'; + $view['page[create]'] = TRUE; + $view['page[path]'] = $this->randomMachineName(16); + + // Just triggering the saving should automatically choose a proper row + // plugin. + $this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit')); + $this->assertUrl('admin/structure/views/view/' . $view['id'], [], 'Make sure the view saving was successful and the browser got redirected to the edit page.'); + + $user = $this->drupalCreateUser(['access content']); + $this->drupalLogin($user); + + $view = Views::getView($view['id']); + $view->initHandlers(); + $row = $view->display_handler->getOption('row'); + $this->assertEqual($row['type'], 'fields'); + + // Check for the default filters. + $this->assertEqual($view->filter['status']->table, 'media_field_data'); + $this->assertEqual($view->filter['status']->field, 'status'); + $this->assertTrue($view->filter['status']->value); + + // Check for the default fields. + $this->assertEqual($view->field['name']->table, 'media_field_data'); + $this->assertEqual($view->field['name']->field, 'name'); + } + + /** + * Tests adding a view of media revisions. + */ + public function testMediaRevisionWizard() { + $view = []; + $view['label'] = $this->randomMachineName(16); + $view['id'] = strtolower($this->randomMachineName(16)); + $view['show[wizard_key]'] = 'media_revision'; + $view['page[create]'] = TRUE; + $view['page[path]'] = $this->randomMachineName(16); + + // Just triggering the saving should automatically choose a proper row + // plugin. + $this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit')); + $this->assertUrl('admin/structure/views/view/' . $view['id'], [], 'Make sure the view saving was successful and the browser got redirected to the edit page.'); + + $user = $this->drupalCreateUser(['view all revisions']); + $this->drupalLogin($user); + + $view = Views::getView($view['id']); + $view->initHandlers(); + $row = $view->display_handler->getOption('row'); + $this->assertEqual($row['type'], 'fields'); + + // Check for the default filters. + $this->assertEqual($view->filter['status']->table, 'media_field_revision'); + $this->assertEqual($view->filter['status']->field, 'status'); + $this->assertTrue($view->filter['status']->value); + + // Check for the default fields. + $this->assertEqual($view->field['name']->table, 'media_field_revision'); + $this->assertEqual($view->field['name']->field, 'name'); + $this->assertEqual($view->field['changed']->table, 'media_field_revision'); + $this->assertEqual($view->field['changed']->field, 'changed'); + } + +} diff --git a/core/modules/media_entity/templates/media.html.twig b/core/modules/media_entity/templates/media.html.twig new file mode 100644 index 0000000..9d43771 --- /dev/null +++ b/core/modules/media_entity/templates/media.html.twig @@ -0,0 +1,19 @@ +{# +/** + * @file + * Default theme implementation to present a media entity. + * + * Available variables: + * - name: Name of the media. + * - content: Media content. + * + * @see template_preprocess_media() + * + * @ingroup themeable + */ +#} + + {% if content %} + {{ content }} + {% endif %} + diff --git a/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml b/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml new file mode 100644 index 0000000..c0a0f70 --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml @@ -0,0 +1,9 @@ +id: test +label: 'Test bundle' +description: 'Test bundle.' +type: generic +type_configuration: { } +status: true +langcode: en +dependencies: { } +field_map: { } diff --git a/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml new file mode 100644 index 0000000..9969dad --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml @@ -0,0 +1,6 @@ +name: 'Media entity test bundle' +type: module +description: 'Provides test bundle for media entity.' +core: 8.x +package: Testing +version: VERSION diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml b/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml new file mode 100644 index 0000000..6ff7e14 --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml @@ -0,0 +1,7 @@ +media_entity.bundle.type.test_type: + type: mapping + label: 'Test type configuration' + mapping: + test_config_value: + type: string + label: 'Test config value' diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml new file mode 100644 index 0000000..ed82c4c --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml @@ -0,0 +1,6 @@ +name: 'Test media type' +type: module +description: 'Provides test media type plugin to test configuration forms.' +core: 8.x +package: Testing +version: VERSION diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php b/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php new file mode 100644 index 0000000..60d68a0 --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php @@ -0,0 +1,51 @@ + $this->t('Field 1'), + 'field_2' => $this->t('Field 2'), + ]; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'test_config_value' => 'This is default value.', + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['test_config_value'] = [ + '#type' => 'textfield', + '#title' => $this->t('Test config value'), + '#default_value' => empty($this->configuration['test_config_value']) ? NULL : $this->configuration['test_config_value'], + ]; + + return $form; + } + +} diff --git a/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml new file mode 100644 index 0000000..3c93c11 --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml @@ -0,0 +1,9 @@ +name: 'Media Entity test views' +type: module +description: 'Provides default views for views media entity tests.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - media_entity + - views diff --git a/core/modules/media_entity/tests/modules/media_entity_test_views/test_views/views.view.test_media_entity_bulk_form.yml b/core/modules/media_entity/tests/modules/media_entity_test_views/test_views/views.view.test_media_entity_bulk_form.yml new file mode 100644 index 0000000..5c068b6 --- /dev/null +++ b/core/modules/media_entity/tests/modules/media_entity_test_views/test_views/views.view.test_media_entity_bulk_form.yml @@ -0,0 +1,154 @@ +langcode: en +status: true +dependencies: + module: + - media_entity + - user +id: test_media_entity_bulk_form +label: '' +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: + style: + type: table + row: + type: fields + fields: + media_bulk_form: + id: media_bulk_form + table: media + field: media_bulk_form + element_type: '' + element_class: '' + 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: 'With selection' + include_exclude: exclude + selected_actions: { } + entity_type: media + plugin_id: media_bulk_form + name: + id: name + table: media_field_data + field: name + entity_type: media + entity_field: media + hide_empty: false + empty_zero: false + settings: + link_to_entity: false + plugin_id: field + relationship: none + group_type: group + admin_label: '' + label: 'Media name' + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + type: string + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + label: Status + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: boolean + settings: + format: custom + format_custom_true: Published + format_custom_false: Unpublished + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: media + entity_field: status + plugin_id: field + sorts: + mid: + id: mid + table: media_field_data + field: mid + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: media + entity_field: mid + plugin_id: standard + title: 'Entity bulk form test view' + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + path: test-media-entity-bulk-form diff --git a/core/modules/media_entity/tests/src/Kernel/TokensTest.php b/core/modules/media_entity/tests/src/Kernel/TokensTest.php new file mode 100644 index 0000000..7fb843e --- /dev/null +++ b/core/modules/media_entity/tests/src/Kernel/TokensTest.php @@ -0,0 +1,81 @@ +installEntitySchema('file'); + $this->installSchema('file', 'file_usage'); + $this->installEntitySchema('media'); + $this->installConfig(['language', 'datetime', 'field', 'system']); + } + + /** + * Tests some of the tokens provided by media_entity. + */ + public function testMediaEntityTokens() { + // Create a generic media bundle. + $bundle_name = $this->randomMachineName(); + + MediaBundle::create([ + 'id' => $bundle_name, + 'label' => $bundle_name, + 'type' => 'generic', + 'type_configuration' => [], + 'field_map' => [], + 'status' => 1, + 'new_revision' => FALSE, + ])->save(); + + // Create a media entity. + $media = Media::create([ + 'name' => $this->randomMachineName(), + 'bundle' => $bundle_name, + 'uid' => '1', + 'langcode' => Language::LANGCODE_DEFAULT, + 'status' => Media::PUBLISHED, + ]); + $media->save(); + + $token_service = $this->container->get('token'); + + // @TODO Extend this to cover also the other tokens, if necessary. + $replaced_value = $token_service->replace('[media:name]', ['media' => $media]); + $this->assertEquals($media->label(), $replaced_value, 'Token replacement for the media label was sucessful.'); + + } + +}