diff --git a/config/install/system.action.message_delete_action.yml b/config/install/system.action.message_delete_action.yml new file mode 100644 index 0000000..e07db82 --- /dev/null +++ b/config/install/system.action.message_delete_action.yml @@ -0,0 +1,13 @@ +langcode: en +status: true +dependencies: + enforced: + module: + - contact_storage + module: + - contact_storage +id: message_delete_action +label: 'Delete message' +type: contact_message +plugin: message_delete_action +configuration: { } diff --git a/config/install/views.view.contact_messages.yml b/config/install/views.view.contact_messages.yml index 8250ad1..f2b7499 100644 --- a/config/install/views.view.contact_messages.yml +++ b/config/install/views.view.contact_messages.yml @@ -84,6 +84,59 @@ display: row: type: 'entity:contact_message' fields: + message_bulk_form: + id: message_bulk_form + table: contact_message + field: message_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: 'With selection' + include_exclude: exclude + selected_actions: { } + entity_type: contact_message + plugin_id: message_bulk_form subject: id: subject table: contact_message @@ -445,10 +498,14 @@ display: display_extenders: { } cache_metadata: contexts: - 0: user - 2: cache.context.url - 3: language + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions cacheable: false + max-age: 0 + tags: { } page_1: display_plugin: page id: page_1 @@ -467,7 +524,11 @@ display: display_extenders: { } cache_metadata: contexts: - 0: user - 2: cache.context.url - 3: language + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions cacheable: false + max-age: 0 + tags: { } diff --git a/config/schema/contact_storage.schema.yml b/config/schema/contact_storage.schema.yml index 476314b..5ad348d 100644 --- a/config/schema/contact_storage.schema.yml +++ b/config/schema/contact_storage.schema.yml @@ -52,3 +52,7 @@ contact_storage.settings: send_html: type: boolean label: 'Whether the mail should be sent as HTML' + +action.configuration.message_delete_action: + type: action_configuration_default + label: 'Delete message configuration' diff --git a/config/schema/contact_storage.views.schema.yml b/config/schema/contact_storage.views.schema.yml index b000275..1ea1cce 100644 --- a/config/schema/contact_storage.views.schema.yml +++ b/config/schema/contact_storage.views.schema.yml @@ -1,3 +1,7 @@ views.field.contact_form: type: views_field label: 'Contact form' + +views.field.message_bulk_form: + type: views_field_bulk_form + label: 'Contact Message bulk form' diff --git a/contact_storage.install b/contact_storage.install index ba3da64..6c4d1f0 100644 --- a/contact_storage.install +++ b/contact_storage.install @@ -5,6 +5,9 @@ * Contains install and update hooks. */ +use Drupal\Core\Config\InstallStorage; +use Drupal\Core\Config\FileStorage; + /** * Implements hook_install(). */ @@ -43,3 +46,19 @@ function _contact_storage_ensure_fields() { $entity_definition_update_manager->installFieldStorageDefinition($field_name, 'contact_message', 'contact_storage', $field_definition); } } + +/** + * Save the bulk delete action to config. + */ +function contact_storage_update_8002() { + $entity_type_manager = \Drupal::entityTypeManager(); + $module_handler = \Drupal::moduleHandler(); + + // Save the bulk delete action to config. + $config_install_path = $module_handler->getModule('contact_storage')->getPath() . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY; + $storage = new FileStorage($config_install_path); + $entity_type_manager + ->getStorage('action') + ->create($storage->read('system.action.message_delete_action')) + ->save(); +} diff --git a/contact_storage.routing.yml b/contact_storage.routing.yml index e21921e..b0c3c79 100644 --- a/contact_storage.routing.yml +++ b/contact_storage.routing.yml @@ -5,3 +5,9 @@ contact_storage.settings: _title: 'Contact settings' requirements: _permission: 'administer contact forms' +entity.contact.multiple_delete_confirm: + path: '/admin/structure/contact/messages/delete' + defaults: + _form: '\Drupal\contact_storage\Form\DeleteMultiple' + requirements: + _permission: 'administer contact forms' diff --git a/src/Form/DeleteMultiple.php b/src/Form/DeleteMultiple.php new file mode 100644 index 0000000..c1f999b --- /dev/null +++ b/src/Form/DeleteMultiple.php @@ -0,0 +1,140 @@ +privateTempStoreFactory = $temp_store_factory; + $this->storage = $entity_type_manager->getStorage('contact_message'); + $this->account = $account; + $this->setStringTranslation($string_translation); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.private_tempstore'), + $container->get('entity_type.manager'), + $container->get('current_user'), + $container->get('string_translation') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'message_multiple_delete_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->formatPlural(count($this->messages), 'Are you sure you want to delete this message?', 'Are you sure you want to delete these messages?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.contact_message.collection'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $this->messages = $this->privateTempStoreFactory->get('message_multiple_delete_confirm')->get($this->account->id()); + if (empty($this->messages)) { + return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString()); + } + + $form['messages'] = [ + '#theme' => 'item_list', + '#items' => array_map(function ($message) { + return $message->label(); + }, $this->messages), + ]; + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + + if ($form_state->getValue('confirm') && !empty($this->messages)) { + $this->storage->delete($this->messages); + $this->privateTempStoreFactory->get('message_multiple_delete_confirm')->delete($this->account->id()); + $count = count($this->messages); + $this->logger('contact')->notice('Deleted @count messages.', ['@count' => $count]); + drupal_set_message($this->stringTranslation->formatPlural($count, 'Deleted 1 message.', 'Deleted @count messages.')); + } + $form_state->setRedirect('entity.contact_message.collection'); + } + +} diff --git a/src/MessageViewsData.php b/src/MessageViewsData.php index 3af8031..6ff6503 100644 --- a/src/MessageViewsData.php +++ b/src/MessageViewsData.php @@ -28,6 +28,14 @@ class MessageViewsData extends EntityViewsData { ), ); + $data['contact_message']['message_bulk_form'] = array( + 'title' => $this->t('Message operations bulk form'), + 'help' => $this->t('Add a form element that lets you run operations on multiple messages.'), + 'field' => array( + 'id' => 'message_bulk_form', + ), + ); + return $data; } diff --git a/src/Plugin/Action/DeleteMessage.php b/src/Plugin/Action/DeleteMessage.php new file mode 100644 index 0000000..5498fb3 --- /dev/null +++ b/src/Plugin/Action/DeleteMessage.php @@ -0,0 +1,93 @@ +currentUser = $current_user; + $this->privateTempStore = $temp_store_factory->get('message_multiple_delete_confirm'); + + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@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) { + $this->privateTempStore->set($this->currentUser->id(), $entities); + } + + /** + * {@inheritdoc} + */ + public function execute($object = NULL) { + $this->executeMultiple(array($object)); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\contact\MessageInterface $object */ + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/src/Plugin/views/field/MessageBulkForm.php b/src/Plugin/views/field/MessageBulkForm.php new file mode 100644 index 0000000..4afee92 --- /dev/null +++ b/src/Plugin/views/field/MessageBulkForm.php @@ -0,0 +1,21 @@ +t('No message selected.'); + } + +} diff --git a/src/Tests/BulkFormTest.php b/src/Tests/BulkFormTest.php new file mode 100644 index 0000000..a78d10a --- /dev/null +++ b/src/Tests/BulkFormTest.php @@ -0,0 +1,78 @@ +drupalCreateUser(array( + 'administer contact forms', + 'administer modules', + )); + $this->drupalLogin($admin_user); + $this->drupalGet('admin/modules'); + // Create first valid contact form. + $mail = 'simpletest@example.com'; + $this->addContactForm('test_id', 'test_label', $mail, '', TRUE); + $this->assertText(t('Contact form test_label has been added.')); + $this->drupalLogout(); + + // Ensure that anonymous can submit site-wide contact form. + user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access site-wide contact form')); + $this->drupalGet('contact'); + $this->assertText(t('Your email address')); + // Submit contact form few times. + for ($i = 1; $i <= 5; $i++) { + $this->submitContact($this->randomMachineName(), $mail, $this->randomMachineName(), 'test_id', $this->randomMachineName()); + $this->assertText(t('Your message has been sent.')); + } + } + + /** + * Test multiple deletion. + */ + public function testBulkDeletion() { + $this->drupalGet('contact'); + ViewTestData::createTestViews(get_class($this), array('contact_test_views')); + // Check the operations are accessible to the administer permission user. + $this->drupalLogin($this->drupalCreateUser(array('administer contact forms'))); + $this->drupalGet('test-contact-message-bulk-form'); + $elements = $this->xpath('//select[@id="edit-action"]//option'); + $this->assertIdentical(count($elements), 1, 'All contact message operations are found.'); + $this->drupalPostForm('test-contact-message-bulk-form', [], t('Apply')); + $this->assertText(t('No message selected.')); + } + +} diff --git a/tests/modules/contact_test_views/contact_test_views.info.yml b/tests/modules/contact_test_views/contact_test_views.info.yml new file mode 100644 index 0000000..3157e2d --- /dev/null +++ b/tests/modules/contact_test_views/contact_test_views.info.yml @@ -0,0 +1,11 @@ +name: 'Contact test views' +type: module +description: 'Provides default views for views contact tests.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - contact + - contact_storage + - views + - language diff --git a/tests/modules/contact_test_views/test_views/views.view.test_contact_message_bulk_form.yml b/tests/modules/contact_test_views/test_views/views.view.test_contact_message_bulk_form.yml new file mode 100644 index 0000000..fd7f0f3 --- /dev/null +++ b/tests/modules/contact_test_views/test_views/views.view.test_contact_message_bulk_form.yml @@ -0,0 +1,188 @@ +langcode: en +status: true +dependencies: + module: + - contact + - contact_storage +id: test_contact_message_bulk_form +label: '' +module: views +description: '' +tag: '' +base_table: contact_message +base_field: id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: null + display_options: + style: + type: table + row: + type: fields + fields: + message_bulk_form: + id: message_bulk_form + table: contact_message + field: message_bulk_form + relationship: none + group_type: group + admin_label: '' + label: 'Message operations bulk form' + 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 + action_title: 'With selection' + include_exclude: exclude + selected_actions: { } + entity_type: contact_message + plugin_id: message_bulk_form + subject: + table: contact_message + field: subject + id: subject + entity_type: null + entity_field: subject + plugin_id: field + relationship: none + group_type: group + admin_label: '' + label: Subject + 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: string + settings: { } + 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 + filters: { } + sorts: + id: + id: id + table: contact_message + field: id + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: contact_message + entity_field: id + plugin_id: standard + title: '' + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: test-contact-message-bulk-form + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { }