diff --git a/core/modules/media/config/optional/system.action.media_delete_action.yml b/core/modules/media/config/optional/system.action.media_delete_action.yml new file mode 100644 index 0000000000..62af35ea5d --- /dev/null +++ b/core/modules/media/config/optional/system.action.media_delete_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media +id: media_delete_action +label: 'Delete media' +type: media +plugin: media_delete_action +configuration: { } diff --git a/core/modules/media/config/optional/system.action.media_publish_action.yml b/core/modules/media/config/optional/system.action.media_publish_action.yml new file mode 100644 index 0000000000..95e173d6f7 --- /dev/null +++ b/core/modules/media/config/optional/system.action.media_publish_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media +id: media_publish_action +label: 'Publish media' +type: media +plugin: media_publish_action +configuration: { } diff --git a/core/modules/media/config/optional/system.action.media_save_action.yml b/core/modules/media/config/optional/system.action.media_save_action.yml new file mode 100644 index 0000000000..c4d098f4bc --- /dev/null +++ b/core/modules/media/config/optional/system.action.media_save_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media +id: media_save_action +label: 'Save media' +type: media +plugin: media_save_action +configuration: { } diff --git a/core/modules/media/config/optional/system.action.media_unpublish_action.yml b/core/modules/media/config/optional/system.action.media_unpublish_action.yml new file mode 100644 index 0000000000..4189e4e743 --- /dev/null +++ b/core/modules/media/config/optional/system.action.media_unpublish_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - media +id: media_unpublish_action +label: 'Unpublish media' +type: media +plugin: media_unpublish_action +configuration: { } diff --git a/core/modules/media/config/optional/views.view.media.yml b/core/modules/media/config/optional/views.view.media.yml index 88e9fef139..7480f30a96 100644 --- a/core/modules/media/config/optional/views.view.media.yml +++ b/core/modules/media/config/optional/views.view.media.yml @@ -132,6 +132,59 @@ display: 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 diff --git a/core/modules/media/config/schema/media.schema.yml b/core/modules/media/config/schema/media.schema.yml index dad518985a..5f8d6edfa3 100644 --- a/core/modules/media/config/schema/media.schema.yml +++ b/core/modules/media/config/schema/media.schema.yml @@ -36,6 +36,22 @@ media.type.*: 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: field.formatter.settings.image label: 'Media thumbnail field display format settings' diff --git a/core/modules/media/config/schema/media.views.schema.yml b/core/modules/media/config/schema/media.views.schema.yml new file mode 100644 index 0000000000..0c7371d51e --- /dev/null +++ b/core/modules/media/config/schema/media.views.schema.yml @@ -0,0 +1,5 @@ +# Schema for the views plugins of the Media module. + +views.field.media_bulk_form: + type: views_field_bulk_form + label: 'Media bulk form' diff --git a/core/modules/media/src/Plugin/Action/DeleteMedia.php b/core/modules/media/src/Plugin/Action/DeleteMedia.php new file mode 100644 index 0000000000..dc06f21289 --- /dev/null +++ b/core/modules/media/src/Plugin/Action/DeleteMedia.php @@ -0,0 +1,98 @@ +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\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($object ? [$object] : []); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media\MediaInterface $object */ + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/core/modules/media/src/Plugin/Action/PublishMedia.php b/core/modules/media/src/Plugin/Action/PublishMedia.php new file mode 100644 index 0000000000..48e7e8f411 --- /dev/null +++ b/core/modules/media/src/Plugin/Action/PublishMedia.php @@ -0,0 +1,40 @@ +setPublished()->save(); + } + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media\MediaInterface $object */ + $access = $object->access('update', $account, TRUE) + ->andIf($object->status->access('update', $account, TRUE)); + + return $return_as_object ? $access : $access->isAllowed(); + } + +} diff --git a/core/modules/media/src/Plugin/Action/SaveMedia.php b/core/modules/media/src/Plugin/Action/SaveMedia.php new file mode 100644 index 0000000000..2a80271145 --- /dev/null +++ b/core/modules/media/src/Plugin/Action/SaveMedia.php @@ -0,0 +1,39 @@ +setChangedTime(0)->save(); + } + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media\MediaInterface $object */ + return $object->access('update', $account, $return_as_object); + } + +} diff --git a/core/modules/media/src/Plugin/Action/UnpublishMedia.php b/core/modules/media/src/Plugin/Action/UnpublishMedia.php new file mode 100644 index 0000000000..808ff314ad --- /dev/null +++ b/core/modules/media/src/Plugin/Action/UnpublishMedia.php @@ -0,0 +1,40 @@ +setUnpublished()->save(); + } + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\media\MediaInterface $object */ + $access = $object->access('update', $account, TRUE) + ->andIf($object->status->access('update', $account, TRUE)); + + return $return_as_object ? $access : $access->isAllowed(); + } + +} diff --git a/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml b/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml new file mode 100644 index 0000000000..dd432bbfbd --- /dev/null +++ b/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml @@ -0,0 +1,154 @@ +langcode: en +status: true +dependencies: + module: + - media + - user +id: test_media_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-bulk-form diff --git a/core/modules/media/tests/src/Functional/MediaBulkFormTest.php b/core/modules/media/tests/src/Functional/MediaBulkFormTest.php new file mode 100644 index 0000000000..cb9e340f99 --- /dev/null +++ b/core/modules/media/tests/src/Functional/MediaBulkFormTest.php @@ -0,0 +1,113 @@ +testMediaType = $this->createMediaType(); + + // Create some test media items. + $this->mediaItems = []; + for ($i = 1; $i <= 5; $i++) { + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + ]); + $media->save(); + $this->mediaItems[] = $media; + } + } + + /** + * Tests the media bulk form. + */ + public function testBulkForm() { + $session = $this->getSession(); + $page = $session->getPage(); + $assert_session = $this->assertSession(); + + // Check that all created items are present in the test view. + $view = Views::getView('test_media_bulk_form'); + $view->execute(); + $this->assertEquals($view->total_rows, 5); + + // Check the operations are accessible to the logged in user. + $this->drupalGet('test-media-bulk-form'); + // Current available actions: Delete, Save, Publish, Unpublish. + $available_actions = [ + 'media_delete_action', + 'media_publish_action', + 'media_save_action', + 'media_unpublish_action', + ]; + foreach ($available_actions as $action_name) { + $assert_session->optionExists('action', $action_name); + } + + // Test unpublishing in bulk. + $page->checkField('media_bulk_form[0]'); + $page->checkField('media_bulk_form[1]'); + $page->checkField('media_bulk_form[2]'); + $page->selectFieldOption('action', 'media_unpublish_action'); + $page->pressButton('Apply to selected items'); + $assert_session->pageTextContains('Unpublish media was applied to 3 items'); + $this->assertFalse($this->storage->loadUnchanged(1)->isPublished(), 'The unpublish action failed in some of the media items.'); + $this->assertFalse($this->storage->loadUnchanged(2)->isPublished(), 'The unpublish action failed in some of the media items.'); + $this->assertFalse($this->storage->loadUnchanged(3)->isPublished(), 'The unpublish action failed in some of the media items.'); + + // Test publishing in bulk. + $page->checkField('media_bulk_form[0]'); + $page->checkField('media_bulk_form[1]'); + $page->selectFieldOption('action', 'media_publish_action'); + $page->pressButton('Apply to selected items'); + $assert_session->pageTextContains('Publish media was applied to 2 items'); + $this->assertTrue($this->storage->loadUnchanged(1)->isPublished(), 'The publish action failed in some of the media items.'); + $this->assertTrue($this->storage->loadUnchanged(2)->isPublished(), 'The publish action failed in some of the media items.'); + + // Test deletion in bulk. + $page->checkField('media_bulk_form[0]'); + $page->checkField('media_bulk_form[1]'); + $page->selectFieldOption('action', 'media_delete_action'); + $page->pressButton('Apply to selected items'); + $assert_session->pageTextContains('Are you sure you want to delete these items?'); + $page->pressButton('Delete'); + $assert_session->pageTextContains('Deleted 2 media items.'); + $this->assertNull($this->storage->loadUnchanged(1), 'Could not delete some of the media items.'); + $this->assertNull($this->storage->loadUnchanged(2), 'Could not delete some of the media items.'); + } + +}