From 259b4e24684fdf77ed5ee3d9b48b75e72273a29e Mon Sep 17 00:00:00 2001 From: webchick <webchick@24967.no-reply.drupal.org> Date: Wed, 21 Nov 2012 09:48:44 -0800 Subject: [PATCH] Issue #1828410 by dawehner, damiankloip, tim.plunkett, stevector: Added Provide a bulk_form() element for actions. --- core/modules/action/action.views.inc | 44 ++++++ .../action/Plugin/views/field/BulkForm.php | 127 +++++++++++++++ .../lib/Drupal/action/Tests/BulkFormTest.php | 83 ++++++++++ .../action_bulk_test/action_bulk_test.info | 8 + .../action_bulk_test/action_bulk_test.module | 1 + .../config/views.view.test_bulk_form.yml | 149 ++++++++++++++++++ .../Drupal/views/Plugin/views/HandlerBase.php | 27 ++++ core/modules/views/views.module | 23 +-- 8 files changed, 453 insertions(+), 9 deletions(-) create mode 100644 core/modules/action/action.views.inc create mode 100644 core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php create mode 100644 core/modules/action/lib/Drupal/action/Tests/BulkFormTest.php create mode 100644 core/modules/action/tests/action_bulk_test/action_bulk_test.info create mode 100644 core/modules/action/tests/action_bulk_test/action_bulk_test.module create mode 100644 core/modules/action/tests/action_bulk_test/config/views.view.test_bulk_form.yml diff --git a/core/modules/action/action.views.inc b/core/modules/action/action.views.inc new file mode 100644 index 000000000000..847096bbbb93 --- /dev/null +++ b/core/modules/action/action.views.inc @@ -0,0 +1,44 @@ +<?php + +/** + * @file + * Provides views data and handlers for action.module. + * + * @ingroup views_module_handlers + */ + +/** + * Implements hook_views_data(). + * + * @todo hook_views_data() is used instead of hook_views_data_alter(), because + * the alter hook doesn't load the *.views.inc automatically. + */ +function action_views_data() { + $data['views']['action_bulk_form'] = array( + 'title' => t('Actions bulk form'), + 'help' => t('Add a form element that lets you apply actions to multiple items.'), + 'field' => array( + 'id' => 'action_bulk_form', + ), + ); + + return $data; +} + +/** + * Implements hook_views_form_substitutions(). + */ +function action_views_form_substitutions() { + // Views check_plains the column label, so by doing it matches for the + // replacement. + $select_all_placeholder = check_plain('<!--action-bulk-form-select-all-->'); + $select_all = array( + '#type' => 'checkbox', + '#default_value' => FALSE, + '#attributes' => array('class' => array('action-table-select-all')), + ); + + return array( + $select_all_placeholder => drupal_render($select_all), + ); +} diff --git a/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php b/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php new file mode 100644 index 000000000000..08d6c856e56a --- /dev/null +++ b/core/modules/action/lib/Drupal/action/Plugin/views/field/BulkForm.php @@ -0,0 +1,127 @@ +<?php + +/** + * @file + * Contains \Drupal\action\Plugin\views\field\BulkForm. + */ + +namespace Drupal\action\Plugin\views\field; + +use Drupal\Core\Annotation\Plugin; +use Drupal\views\Plugin\views\field\FieldPluginBase; + +/** + * Defines a simple bulk operation form element. + * + * @Plugin( + * id = "action_bulk_form", + * module = "action" + * ) + */ +class BulkForm extends FieldPluginBase { + + /** + * Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::render(). + */ + public function render($values) { + return '<!--form-item-' . $this->options['id'] . '--' . $this->view->row_index . '-->'; + } + + /** + * Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::pre_render(). + */ + public function pre_render(&$values) { + parent::pre_render($values); + + // If the view is using a table style, provide a placeholder for a + // "select all" checkbox. + if (!empty($this->view->style_plugin) && $this->view->style_plugin instanceof \Drupal\views\Plugin\views\style\Table) { + // Add the tableselect css classes. + $this->options['element_label_class'] .= 'select-all'; + // Hide the actual label of the field on the table header. + $this->options['label'] = ''; + } + } + + /** + * Implements \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::views_form(). + */ + public function views_form(&$form, &$form_state) { + // Add the tableselect javascript. + $form['#attached']['library'][] = array('system', 'drupal.tableselect'); + + // Render checkboxes for all rows. + foreach ($this->view->result as $row_index => $row) { + $entity_id = $this->get_value($row); + + $form[$this->options['id']][$row_index] = array( + '#type' => 'checkbox', + '#default_value' => FALSE, + ); + } + + $form[$this->options['id']]['#tree'] = TRUE; + + // Get all available actions. + $actions = action_get_all_actions(); + $entity_type = $this->getEntityType(); + // Filter actions by the entity type and build options for the form. + $actions = array_filter($actions, function($action) use ($entity_type) { + return $action['type'] == $entity_type && empty($action['configurable']); + }); + $options = array_map(function($action) { + return $action['label']; + }, $actions); + + $form['action'] = array( + '#type' => 'select', + '#title' => t('Action'), + '#options' => $options, + '#description' => t('Select the action you want to execute on the content entitites.'), + ); + + // Move the submit button beside the selection. + $form['actions']['#weight'] = 1; + + // Replace the text with Update. + $form['actions']['submit']['#value'] = t('Update'); + + // Put the submit button both at the top and bottom. + $form['actions_bottom'] = $form['actions']; + $form['actions_bottom']['#weight'] = 100; + } + + /** + * Implements \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::views_form_submit(). + */ + public function views_form_submit(&$form, &$form_state) { + if ($form_state['step'] == 'views_form_views_form') { + $action = $form_state['values']['action']; + $action = action_load($action); + $count = 0; + + // Filter only selected checkboxes. + $selected = array_filter($form_state['values'][$this->options['id']]); + + if (!empty($selected)) { + foreach (array_keys($selected) as $row_index) { + $entity = $this->get_entity($this->view->result[$row_index]); + actions_do($action->aid, $entity); + $entity->save(); + $count++; + } + } + + if ($count) { + drupal_set_message(t('%action action performed on %count item(s).', array('%action' => $action->label, '%count' => $count))); + } + } + } + + /** + * Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::query(). + */ + public function query() { + } + +} diff --git a/core/modules/action/lib/Drupal/action/Tests/BulkFormTest.php b/core/modules/action/lib/Drupal/action/Tests/BulkFormTest.php new file mode 100644 index 000000000000..8891ed42dd6b --- /dev/null +++ b/core/modules/action/lib/Drupal/action/Tests/BulkFormTest.php @@ -0,0 +1,83 @@ +<?php + +/** + * @file + * Contains \Drupal\action\Tests\BulkFormTest. + */ + +namespace Drupal\action\Tests; + +use Drupal\simpletest\WebTestBase; + +/** + * Tests the views bulk form test. + * + * @see \Drupal\action\Plugin\views\field\BulkForm + */ +class BulkFormTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('action_bulk_test'); + + public static function getInfo() { + return array( + 'name' => 'Bulk form', + 'description' => 'Tests the views bulk form test.', + 'group' => 'Action', + ); + } + + /** + * Tests the bulk form. + */ + public function testBulkForm() { + $nodes = array(); + for ($i = 0; $i < 10; $i++) { + $nodes[] = $this->drupalCreateNode(array('sticky' => FALSE)); + } + + $this->drupalGet('test_bulk_form'); + + $this->assertFieldById('edit-action', NULL, 'The action select field appears.'); + + // Make sure a checkbox appears on all rows. + $edit = array(); + for ($i = 0; $i < 10; $i++) { + $this->assertFieldById('edit-bulk-form-' . $i, NULL, format_string('The checkbox on row @row appears.', array('@row' => $i))); + $edit["bulk_form[$i]"] = TRUE; + } + + // Set all nodes to sticky and check that. + $edit += array('action' => 'node_make_sticky_action'); + $this->drupalPost(NULL, $edit, t('Update')); + + foreach ($nodes as $node) { + $changed_node = node_load($node->id()); + $this->assertTrue($changed_node->sticky, format_string('Node @nid got marked as sticky.', array('@nid' => $node->id()))); + } + + $this->assertText('Make content sticky action performed on 10 item(s).'); + + // Unpublish just one node. + $node = node_load($nodes[0]->id()); + $this->assertTrue($node->status, 'The node is published.'); + + $edit = array('bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action'); + $this->drupalPost(NULL, $edit, t('Update')); + + $this->assertText('Unpublish content action performed on 1 item(s).'); + + // Load the node again. + $node = node_load($node->id(), TRUE); + $this->assertFalse($node->status, 'A single node has been unpublished.'); + + // The second node should still be published. + $node = node_load($nodes[1]->id(), TRUE); + $this->assertTrue($node->status, 'An unchecked node is still published.'); + } + +} diff --git a/core/modules/action/tests/action_bulk_test/action_bulk_test.info b/core/modules/action/tests/action_bulk_test/action_bulk_test.info new file mode 100644 index 000000000000..36983703e305 --- /dev/null +++ b/core/modules/action/tests/action_bulk_test/action_bulk_test.info @@ -0,0 +1,8 @@ +name = Action bulk form test +description = Support module for action bulk form testing. +package = Testing +version = VERSION +core = 8.x +hidden = TRUE +dependencies[] = action +dependencies[] = views diff --git a/core/modules/action/tests/action_bulk_test/action_bulk_test.module b/core/modules/action/tests/action_bulk_test/action_bulk_test.module new file mode 100644 index 000000000000..b3d9bbc7f371 --- /dev/null +++ b/core/modules/action/tests/action_bulk_test/action_bulk_test.module @@ -0,0 +1 @@ +<?php diff --git a/core/modules/action/tests/action_bulk_test/config/views.view.test_bulk_form.yml b/core/modules/action/tests/action_bulk_test/config/views.view.test_bulk_form.yml new file mode 100644 index 000000000000..ea8f70126639 --- /dev/null +++ b/core/modules/action/tests/action_bulk_test/config/views.view.test_bulk_form.yml @@ -0,0 +1,149 @@ +base_table: node +name: test_bulk_form +description: '' +tag: '' +human_name: form +core: 8.x +api_version: '3.0' +display: + default: + display_plugin: default + id: default + display_title: Master + position: '' + display_options: + access: + type: perm + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + options: + items_per_page: '10' + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: '1' + row_class_special: '1' + override: '1' + sticky: '0' + summary: '' + columns: + title: title + action_bulk_form: action_bulk_form + info: + title: + sortable: '0' + default_sort_order: asc + align: '' + separator: '' + empty_column: '0' + responsive: '' + bulk_form: + align: '' + separator: '' + empty_column: '0' + responsive: '' + default: '-1' + empty_table: '0' + row: + type: fields + fields: + title: + id: title + table: node + field: title + label: '' + alter: + alter_text: '0' + make_link: '0' + absolute: '0' + trim: '0' + word_boundary: '0' + ellipsis: '0' + strip_tags: '0' + html: '0' + hide_empty: '0' + empty_zero: '0' + link_to_node: '1' + bulk_form: + id: action_bulk_form + table: views + field: action_bulk_form + relationship: none + group_type: group + admin_label: '' + label: 'Bulk form' + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + filters: + status: + value: '1' + table: node + field: status + id: status + expose: + operator: '0' + group: '1' + sorts: + created: + id: created + table: node + field: created + order: DESC + title: form + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: '' + display_options: + path: test_bulk_form +base_field: nid +disabled: '0' +module: views +langcode: und diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php index 0b897ea3f9b4..deaf56d761cf 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php @@ -813,6 +813,33 @@ public static function getTableJoin($table, $base_table) { } } + /** + * Determines the entity type used by this handler. + * + * If this handler uses a relationship, the base class of the relationship is + * taken into account. + * + * @return string + * The machine name of the entity type. + */ + public function getEntityType() { + // If the user has configured a relationship on the handler take that into + // account. + if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') { + $views_data = views_fetch_data($this->view->relationship->table); + } + else { + $views_data = views_fetch_data($this->view->storage->get('base_table')); + } + + if (isset($views_data['table']['entity type'])) { + return $views_data['table']['entity type']; + } + else { + throw new \Exception(format_string('No entity type for field @field on view @view', array('@field' => $this->options['id'], '@view' => $this->view->storage->get('name')))); + } + } + /** * Breaks x,y,z and x+y+z into an array. Numeric only. * diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 72068d134a33..6347df67a942 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -1110,9 +1110,15 @@ function views_hook_info() { $hooks['views_data'] = array( 'group' => 'views', ); + $hooks['views_data_alter'] = array( + 'group' => 'views', + ); $hooks['views_query_substitutions'] = array( 'group' => 'views', ); + $hooks['views_form_substitutions'] = array( + 'group' => 'views', + ); return $hooks; } @@ -1641,6 +1647,14 @@ function views_form_views_form($form, &$form_state, ViewExecutable $view, $outpu '#weight' => 50, ); + $form['actions'] = array( + '#type' => 'actions', + ); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + $substitutions = array(); foreach ($view->field as $field_name => $field) { $form_element_name = $field_name; @@ -1696,15 +1710,6 @@ function views_form_views_form($form, &$form_state, ViewExecutable $view, $outpu '#type' => 'value', '#value' => $substitutions, ); - $form['actions'] = array( - '#type' => 'container', - '#attributes' => array('class' => array('form-actions')), - '#weight' => 100, - ); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); return $form; } -- GitLab