From ad4b8214b32baa1f35cf0801633f3194fccf8e6f Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Wed, 2 Mar 2016 17:44:21 +0100 Subject: [PATCH 1/4] Add support for bulk operations, add one operation, redo the admin form. --- .../system.action.log_delete_action.yml | 10 + config/install/views.view.log_admin.yml | 187 +++++++++++++++- log.routing.yml | 7 + src/Form/DeleteMultiple.php | 204 ++++++++++++++++++ src/LogViewsData.php | 7 + src/Plugin/Action/DeleteLog.php | 104 +++++++++ src/Plugin/views/field/LogBulkForm.php | 18 ++ 7 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 config/install/system.action.log_delete_action.yml create mode 100644 src/Form/DeleteMultiple.php create mode 100644 src/Plugin/Action/DeleteLog.php create mode 100644 src/Plugin/views/field/LogBulkForm.php diff --git a/config/install/system.action.log_delete_action.yml b/config/install/system.action.log_delete_action.yml new file mode 100644 index 0000000..9c1f594 --- /dev/null +++ b/config/install/system.action.log_delete_action.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: + module: + - log +id: log_delete_action +label: 'Delete log' +type: log +plugin: log_delete_action +configuration: { } diff --git a/config/install/views.view.log_admin.yml b/config/install/views.view.log_admin.yml index 4719680..2578b03 100644 --- a/config/install/views.view.log_admin.yml +++ b/config/install/views.view.log_admin.yml @@ -123,6 +123,59 @@ display: row: type: fields fields: + log_bulk_form: + id: log_bulk_form + table: log + field: log_bulk_form + relationship: none + group_type: group + admin_label: '' + label: 'Log 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: log + plugin_id: log_bulk_form id: id: id table: log @@ -189,6 +242,71 @@ display: entity_type: log entity_field: id plugin_id: field + type: + id: type + table: log + field: type + relationship: none + group_type: group + admin_label: '' + label: Type + 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: log + entity_field: type + plugin_id: field name: id: name table: log @@ -240,7 +358,7 @@ display: click_sort_column: value type: string settings: - link_to_entity: false + link_to_entity: true group_column: value group_columns: { } group_rows: true @@ -386,6 +504,73 @@ display: entity_type: log entity_field: changed plugin_id: field + done: + id: done + table: log + field: done + relationship: none + group_type: group + admin_label: '' + label: Done + 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: default + format_custom_true: '' + format_custom_false: '' + 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: log + entity_field: done + plugin_id: field operations: table: log field: operations diff --git a/log.routing.yml b/log.routing.yml index 4d9f51c..e61cc68 100644 --- a/log.routing.yml +++ b/log.routing.yml @@ -41,3 +41,10 @@ entity.log_type.add_form: _permission: 'administer log types' options: _admin_route: TRUE + +log.multiple_delete_confirm: + path: '/admin/content/log/delete' + defaults: + _form: '\Drupal\log\Form\DeleteMultiple' + requirements: + _permission: 'administer log types' diff --git a/src/Form/DeleteMultiple.php b/src/Form/DeleteMultiple.php new file mode 100644 index 0000000..9e8fa8f --- /dev/null +++ b/src/Form/DeleteMultiple.php @@ -0,0 +1,204 @@ +tempStoreFactory = $temp_store_factory; + $this->storage = $manager->getStorage('log'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.private_tempstore'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'log_multiple_delete_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->formatPlural(count($this->logInfo), '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 t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $this->logInfo = $this->tempStoreFactory->get('log_multiple_delete_confirm')->get(\Drupal::currentUser()->id()); + if (empty($this->logInfo)) { + return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString()); + } + /** @var \Drupal\log\LogInterface[] $logs */ + $logs = $this->storage->loadMultiple(array_keys($this->logInfo)); + + $items = []; + foreach ($this->logInfo as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $log = $logs[$id]->getTranslation($langcode); + $key = $id . ':' . $langcode; + $default_key = $id . ':' . $log->getUntranslated()->language()->getId(); + + // If we have a translated entity we build a nested list of translations + // that will be deleted. + $languages = $log->getTranslationLanguages(); + if (count($languages) > 1 && $log->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 content translations will be deleted:', ['@label' => $log->label()]), + ], + 'deleted_translations' => [ + '#theme' => 'item_list', + '#items' => $names, + ], + ]; + } + elseif (!isset($items[$default_key])) { + $items[$key] = $log->label(); + } + } + } + + $form['logs'] = array( + '#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->logInfo)) { + $total_count = 0; + $delete_logs = []; + /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */ + $delete_translations = []; + /** @var \Drupal\log\LogInterface[] $logs */ + $logs = $this->storage->loadMultiple(array_keys($this->logInfo)); + + foreach ($this->logInfo as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $log = $logs[$id]->getTranslation($langcode); + if ($log->isDefaultTranslation()) { + $delete_logs[$id] = $log; + unset($delete_translations[$id]); + $total_count += count($log->getTranslationLanguages()); + } + elseif (!isset($delete_logs[$id])) { + $delete_translations[$id][] = $log; + } + } + } + + if ($delete_logs) { + $this->storage->delete($delete_logs); + $this->logger('log')->notice('Deleted @count posts.', array('@count' => count($delete_logs))); + } + + if ($delete_translations) { + $count = 0; + foreach ($delete_translations as $id => $translations) { + $log = $logs[$id]->getUntranslated(); + foreach ($translations as $translation) { + $log->removeTranslation($translation->language()->getId()); + } + $log->save(); + $count += count($translations); + } + if ($count) { + $total_count += $count; + $this->logger('log')->notice('Deleted @count content translations.', array('@count' => $count)); + } + } + + if ($total_count) { + drupal_set_message($this->formatPlural($total_count, 'Deleted 1 post.', 'Deleted @count posts.')); + } + + $this->tempStoreFactory->get('log_multiple_delete_confirm')->delete(\Drupal::currentUser()->id()); + } + + $form_state->setRedirect('system.admin_content'); + } + +} diff --git a/src/LogViewsData.php b/src/LogViewsData.php index 44e2811..a3d3b09 100644 --- a/src/LogViewsData.php +++ b/src/LogViewsData.php @@ -19,6 +19,13 @@ class LogViewsData extends EntityViewsData implements EntityViewsDataInterface { */ public function getViewsData() { $data = parent::getViewsData(); + $data['log']['log_bulk_form'] = array( + 'title' => t('Log operations bulk form'), + 'help' => t('Add a form element that lets you run operations on multiple log entities.'), + 'field' => array( + 'id' => 'log_bulk_form', + ), + ); return $data; } diff --git a/src/Plugin/Action/DeleteLog.php b/src/Plugin/Action/DeleteLog.php new file mode 100644 index 0000000..024f354 --- /dev/null +++ b/src/Plugin/Action/DeleteLog.php @@ -0,0 +1,104 @@ +currentUser = $current_user; + $this->tempStore = $temp_store_factory->get('log_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) { + $info = []; + /** @var \Drupal\log\LogInterface $log */ + foreach ($entities as $log) { + $langcode = $log->language()->getId(); + $info[$log->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\log\LogInterface $object */ + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/src/Plugin/views/field/LogBulkForm.php b/src/Plugin/views/field/LogBulkForm.php new file mode 100644 index 0000000..6100a1d --- /dev/null +++ b/src/Plugin/views/field/LogBulkForm.php @@ -0,0 +1,18 @@ + Date: Mon, 7 Mar 2016 20:03:09 +0100 Subject: [PATCH 2/4] Use log admin URL on delete form. --- src/Form/DeleteMultiple.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Form/DeleteMultiple.php b/src/Form/DeleteMultiple.php index 9e8fa8f..62d1aba 100644 --- a/src/Form/DeleteMultiple.php +++ b/src/Form/DeleteMultiple.php @@ -82,7 +82,7 @@ public function getQuestion() { * {@inheritdoc} */ public function getCancelUrl() { - return new Url('system.admin_content'); + return new Url('view.log_admin.collection'); } /** @@ -198,7 +198,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->tempStoreFactory->get('log_multiple_delete_confirm')->delete(\Drupal::currentUser()->id()); } - $form_state->setRedirect('system.admin_content'); + $form_state->setRedirectUrl($this->getCancelUrl()); } } From f703814330f96cbf7dc1c35854e7f5ef5110a39b Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Mon, 7 Mar 2016 20:03:26 +0100 Subject: [PATCH 3/4] Add config schema for bulk action. --- config/schema/log.schema.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/schema/log.schema.yml b/config/schema/log.schema.yml index 6e902da..68af3d6 100644 --- a/config/schema/log.schema.yml +++ b/config/schema/log.schema.yml @@ -40,3 +40,7 @@ condition.plugin.log_type: type: sequence sequence: type: string + +action.configuration.log_delete_action: + type: action_configuration_default + label: 'Delete log configuration' \ No newline at end of file From c2170f00923b68b1b03d3605ad86b5739c7dc833 Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Mon, 7 Mar 2016 20:03:40 +0100 Subject: [PATCH 4/4] Add config schema for views part of bulk operations. Remove a bunch of node module copy-paste. --- config/schema/log.views.schema.yml | 185 +---------------------------- 1 file changed, 3 insertions(+), 182 deletions(-) diff --git a/config/schema/log.views.schema.yml b/config/schema/log.views.schema.yml index 70c5c23..61bc251 100644 --- a/config/schema/log.views.schema.yml +++ b/config/schema/log.views.schema.yml @@ -1,183 +1,4 @@ -# Schema for the views plugins of the Node module. - -views.area.node_listing_empty: - type: views_area - label: 'Node link' - -views.argument.node_nid: - type: views_argument - label: 'Node ID' - mapping: - break_phrase: - type: boolean - label: 'Allow multiple values' - not: - type: boolean - label: 'Exclude' - -views.argument.node_type: - type: views_argument - label: 'Node type' - mapping: - glossary: - type: boolean - label: 'Glossary mode' - limit: - type: integer - label: 'Character limit' - case: - type: string - label: 'Case' - path_case: - type: string - label: 'Case in path' - transform_dash: - type: boolean - label: 'Transform spaces to dashes in URL' - break_phrase: - type: boolean - label: 'Allow multiple values' - add_table: - type: boolean - label: 'Allow multiple filter values to work together' - require_value: - type: boolean - label: 'Do not display items with no value in summary' - -views.argument.node_uid_revision: - type: views_argument - label: 'Node user ID' - mapping: - break_phrase: - type: boolean - label: 'Allow multiple values' - not: - type: boolean - label: 'Exclude' - -views.argument.node_vid: - type: views_argument - label: 'Node revision ID' - mapping: - break_phrase: - type: boolean - label: 'Allow multiple values' - not: - type: boolean - label: 'Exclude' - -views.argument_default.node: - type: sequence - label: 'Content ID from URL' - sequence: - type: string - label: 'Nid' - -views.argument_validator.node: - type: mapping - label: 'Content' - mapping: - types: - type: sequence - label: 'Content types' - sequence: - type: string - label: 'Type' - access: - type: boolean - label: 'Validate user has access to the content' - access_op: - type: boolean - label: 'Access operation to check' - nid_type: - type: string - label: 'Filter value format' - -views.field.node: - type: views_field - label: 'Node' - mapping: - link_to_node: - type: boolean - label: 'Link this field to the original piece of content' - -views.field.node_bulk_form: +# Schema for the views plugins of the Log module. +views.field.log_bulk_form: type: views_field_bulk_form - label: 'Node bulk form' - -views.field.node_path: - type: views_field - label: 'Node path' - mapping: - absolute: - type: boolean - label: 'Use absolute link (begins with "http://")' - -views.field.node_revision_link: - type: views_field - label: 'Link to a node revision' - mapping: - text: - type: label - label: 'Text to display' - -views.field.node_revision_link_delete: - type: views_field - label: 'Link to delete a node revision' - mapping: - text: - type: label - label: 'Text to display' - -views.field.node_revision_link_revert: - type: views_field - label: 'Link to revert a node to a revision' - mapping: - text: - type: label - label: 'Text to display' - -views.filter.node_access: - type: views_filter - label: 'Node access' - -views.filter.node_status: - type: views_filter - label: 'Node status' - -views.filter.node_uid_revision: - type: views_filter - label: 'Node revisions of an user' - mapping: - operator: - type: string - label: 'Operator' - value: - type: sequence - label: 'Values' - sequence: - type: string - label: 'Value' - expose: - type: mapping - label: 'Expose' - mapping: - reduce: - type: boolean - label: 'Reduce' - -views.filter_value.node_access: - type: string - label: 'Access' - -views.filter_value.node_status: - type: boolean - label: 'Status' - -views.row.node_rss: - type: views_row - label: 'Content options' - mapping: - view_mode: - type: string - label: 'Display type' + label: 'Log bulk form'