Commit dd4890bc authored by alexpott's avatar alexpott

Issue #2049573 by pcambra, damiankloip, tim.plunkett, dawehner: Move most or...

Issue #2049573 by pcambra, damiankloip, tim.plunkett, dawehner: Move most or all of the ActionBulkForm class to BulkFormBase.
parent 8910339d
<?php
/**
* @file
* Provides views data for action.module.
*/
/**
* 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['action']['table']['group'] = t('Action');
$data['action']['table']['join'] = array(
'#global' => array(),
);
$data['action']['action_bulk_form'] = array(
'title' => t('Bulk update'),
'help' => t('Allows users to apply an action to one or more items.'),
'field' => array(
'id' => 'action_bulk_form',
),
);
return $data;
}
......@@ -58,8 +58,8 @@ public function testBulkForm() {
// Make sure a checkbox appears on all rows.
$edit = array();
for ($i = 0; $i < 10; $i++) {
$this->assertFieldById('edit-action-bulk-form-' . $i, NULL, format_string('The checkbox on row @row appears.', array('@row' => $i)));
$edit["action_bulk_form[$i]"] = TRUE;
$this->assertFieldById('edit-node-bulk-form-' . $i, NULL, format_string('The checkbox on row @row appears.', array('@row' => $i)));
$edit["node_bulk_form[$i]"] = TRUE;
}
// Set all nodes to sticky and check that.
......@@ -77,7 +77,7 @@ public function testBulkForm() {
$node = node_load($nodes[0]->id());
$this->assertTrue($node->isPublished(), 'The node is published.');
$edit = array('action_bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action');
$edit = array('node_bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action');
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->assertText('Unpublish content was applied to 1 item.');
......@@ -93,9 +93,9 @@ public function testBulkForm() {
// Set up to include just the sticky actions.
$view = views_get_view('test_bulk_form');
$display = &$view->storage->getDisplay('default');
$display['display_options']['fields']['action_bulk_form']['include_exclude'] = 'include';
$display['display_options']['fields']['action_bulk_form']['selected_actions']['node_make_sticky_action'] = 'node_make_sticky_action';
$display['display_options']['fields']['action_bulk_form']['selected_actions']['node_make_unsticky_action'] = 'node_make_unsticky_action';
$display['display_options']['fields']['node_bulk_form']['include_exclude'] = 'include';
$display['display_options']['fields']['node_bulk_form']['selected_actions']['node_make_sticky_action'] = 'node_make_sticky_action';
$display['display_options']['fields']['node_bulk_form']['selected_actions']['node_make_unsticky_action'] = 'node_make_unsticky_action';
$view->save();
$this->drupalGet('test_bulk_form');
......@@ -107,7 +107,7 @@ public function testBulkForm() {
// Set up to exclude the sticky actions.
$view = views_get_view('test_bulk_form');
$display = &$view->storage->getDisplay('default');
$display['display_options']['fields']['action_bulk_form']['include_exclude'] = 'exclude';
$display['display_options']['fields']['node_bulk_form']['include_exclude'] = 'exclude';
$view->save();
$this->drupalGet('test_bulk_form');
......@@ -122,7 +122,7 @@ public function testBulkForm() {
// Setup up a different bulk form title.
$view = views_get_view('test_bulk_form');
$display = &$view->storage->getDisplay('default');
$display['display_options']['fields']['action_bulk_form']['action_title'] = 'Test title';
$display['display_options']['fields']['node_bulk_form']['action_title'] = 'Test title';
$view->save();
$this->drupalGet('test_bulk_form');
......
......@@ -35,7 +35,7 @@ display:
summary: ''
columns:
title: title
action_bulk_form: action_bulk_form
node_bulk_form: node_bulk_form
info:
title:
sortable: false
......@@ -73,10 +73,10 @@ display:
link_to_node: true
plugin_id: node
provider: node
action_bulk_form:
id: action_bulk_form
table: action
field: action_bulk_form
node_bulk_form:
id: node_bulk_form
table: node
field: node_bulk_form
relationship: none
group_type: group
admin_label: ''
......@@ -121,8 +121,8 @@ display:
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: action_bulk_form
provider: action
plugin_id: node_bulk_form
provider: node
filters:
status:
value: true
......
......@@ -8,7 +8,7 @@
namespace Drupal\node\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\views\Plugin\views\field\ActionBulkForm;
use Drupal\system\Plugin\views\field\BulkForm;
use Drupal\Core\Cache\Cache;
/**
......@@ -16,38 +16,24 @@
*
* @PluginID("node_bulk_form")
*/
class NodeBulkForm extends ActionBulkForm {
/**
* Constructs a new NodeBulkForm object.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $manager);
// Filter the actions to only include those for the 'node' entity type.
$this->actions = array_filter($this->actions, function ($action) {
return $action->getType() == 'node';
});
}
class NodeBulkForm extends BulkForm {
/**
* {@inheritdoc}
*/
public function viewsFormValidate(&$form, &$form_state) {
$selected = array_filter($form_state['values'][$this->options['id']]);
if (empty($selected)) {
form_set_error('', $form_state, t('No items selected.'));
public function viewsFormSubmit(&$form, &$form_state) {
parent::viewsFormSubmit($form, $form_state);
if ($form_state['step'] == 'views_form_views_form') {
Cache::invalidateTags(array('content' => TRUE));
}
}
/**
* {@inheritdoc}
*/
public function viewsFormSubmit(&$form, &$form_state) {
parent::viewsFormSubmit($form, $form_state);
if ($form_state['step'] == 'views_form_views_form') {
Cache::invalidateTags(array('content' => TRUE));
}
protected function emptySelectedMessage() {
return t('No content selected.');
}
}
......@@ -206,14 +206,6 @@ function node_views_data() {
);
}
$data['node']['node_bulk_form'] = array(
'title' => t('Node operations bulk form'),
'help' => t('Add a form element that lets you run operations on multiple nodes.'),
'field' => array(
'id' => 'node_bulk_form',
),
);
$data['node']['view_node'] = array(
'field' => array(
'title' => t('Link to content'),
......@@ -630,6 +622,19 @@ function node_views_data() {
return $data;
}
/**
* Implements hook_views_data_alter().
*/
function node_views_data_alter(&$data) {
$data['node']['node_bulk_form'] = array(
'title' => t('Node operations bulk form'),
'help' => t('Add a form element that lets you run operations on multiple nodes.'),
'field' => array(
'id' => 'node_bulk_form',
),
);
}
/**
* Implements hook_preprocess_node().
*/
......
......@@ -7,6 +7,7 @@
namespace Drupal\node\Tests\Plugin\views\field;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\node\Plugin\views\field\NodeBulkForm;
use Drupal\Tests\UnitTestCase;
......@@ -25,6 +26,15 @@ public static function getInfo() {
);
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
parent::tearDown();
$container = new ContainerBuilder();
\Drupal::setContainer($container);
}
/**
* Tests the constructor assignment of actions.
*/
......@@ -32,34 +42,55 @@ public function testConstructor() {
$actions = array();
for ($i = 1; $i <= 2; $i++) {
$action = $this->getMockBuilder('Drupal\system\Entity\Action')
->disableOriginalConstructor()
->getMock();
$action = $this->getMock('\Drupal\system\ActionConfigEntityInterface');
$action->expects($this->any())
->method('getType')
->will($this->returnValue('node'));
$actions[$i] = $action;
}
$action = $this->getMockBuilder('Drupal\system\Entity\Action')
->disableOriginalConstructor()
->getMock();
$action = $this->getMock('\Drupal\system\ActionConfigEntityInterface');
$action->expects($this->any())
->method('getType')
->will($this->returnValue('user'));
$actions[] = $action;
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$storage_controller = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface');
$storage_controller->expects($this->any())
->method('loadMultiple')
->will($this->returnValue($actions));
$entity_manager->expects($this->any())
->method('getStorageController')
->with('action')
->will($this->returnValue($storage_controller));
$node_bulk_form = new NodeBulkForm(array(), 'node_bulk_form', array(), $entity_manager);
$views_data = $this->getMockBuilder('Drupal\views\ViewsData')
->disableOriginalConstructor()
->getMock();
$views_data->expects($this->any())
->method('get')
->with('node')
->will($this->returnValue(array('table' => array('entity type' => 'node'))));
$container = new ContainerBuilder();
$container->set('views.views_data', $views_data);
\Drupal::setContainer($container);
$storage = $this->getMock('Drupal\views\ViewStorageInterface');
$storage->expects($this->any())
->method('get')
->with('base_table')
->will($this->returnValue('node'));
$executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
->disableOriginalConstructor()
->getMock();
$executable->storage = $storage;
$display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
->disableOriginalConstructor()
->getMock();
$definition['title'] = '';
$options = array();
$node_bulk_form = new NodeBulkForm(array(), 'node_bulk_form', $definition, $storage_controller);
$node_bulk_form->init($executable, $display, $options);
$this->assertAttributeEquals(array_slice($actions, 0, -1, TRUE), 'actions', $node_bulk_form);
}
......
......@@ -2,21 +2,32 @@
/**
* @file
* Contains \Drupal\system\Plugin\views\field\BulkFormBase.
* Contains \Drupal\system\Plugin\views\field\BulkForm.
*/
namespace Drupal\system\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\Plugin\views\style\Table;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a generic bulk operation form element.
* Defines a actions-based bulk operation form element.
*
* @PluginID("bulk_form")
*/
abstract class BulkFormBase extends FieldPluginBase {
class BulkForm extends FieldPluginBase {
/**
* The action storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $actionStorage;
/**
* An array of actions that can be executed.
......@@ -25,12 +36,56 @@ abstract class BulkFormBase extends FieldPluginBase {
*/
protected $actions = array();
/**
* Constructs a new BulkForm object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
* The action storage controller.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityStorageControllerInterface $storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->actionStorage = $storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager')->getStorageController('action'));
}
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$entity_type = $this->getEntityType();
// Filter the actions to only include those for this entity type.
$this->actions = array_filter($this->actionStorage->loadMultiple(), function ($action) use ($entity_type) {
return $action->getType() == $entity_type;
});
}
/**
* {@inheritdoc }
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['action_title'] = array('default' => 'With selection', 'translatable' => TRUE);
$options['include_exclude'] = array(
'default' => 'exclude',
);
$options['selected_actions'] = array(
'default' => array(),
);
return $options;
}
......@@ -45,33 +100,32 @@ public function buildOptionsForm(&$form, &$form_state) {
'#description' => t('The title shown above the actions dropdown.'),
);
parent::buildOptionsForm($form, $form_state);
}
/**
* Constructs a new BulkForm object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
* The entity manager.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$form['include_exclude'] = array(
'#type' => 'radios',
'#title' => t('Available actions'),
'#options' => array(
'exclude' => t('All actions, except selected'),
'include' => t('Only selected actions'),
),
'#default_value' => $this->options['include_exclude'],
);
$form['selected_actions'] = array(
'#type' => 'checkboxes',
'#title' => t('Selected actions'),
'#options' => $this->getBulkOptions(FALSE),
'#default_value' => $this->options['selected_actions'],
);
$this->actions = $manager->getStorageController('action')->loadMultiple();
parent::buildOptionsForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'));
public function validateOptionsForm(&$form, &$form_state) {
parent::validateOptionsForm($form, $form_state);
$form_state['values']['options']['selected_actions'] = array_filter($form_state['values']['options']['selected_actions']);
}
/**
......@@ -97,6 +151,7 @@ public function preRender(&$values) {
}
}
/**
* Form constructor for the bulk form.
*
......@@ -156,13 +211,33 @@ public function viewsForm(&$form, &$form_state) {
/**
* Returns the available operations for this form.
*
* @param bool $filtered
* (optional) Whether to filter actions to selected actions.
* @return array
* An associative array of operations, suitable for a select element.
*/
protected function getBulkOptions() {
return array_map(function ($action) {
return $action->label();
}, $this->actions);
protected function getBulkOptions($filtered = TRUE) {
$options = array();
// Filter the action list.
foreach ($this->actions as $id => $action) {
if ($filtered) {
$in_selected = in_array($id, $this->options['selected_actions']);
// If the field is configured to include only the selected actions,
// skip actions that were not selected.
if (($this->options['include_exclude'] == 'include') && !$in_selected) {
continue;
}
// Otherwise, if the field is configured to exclude the selected
// actions, skip actions that were selected.
elseif (($this->options['include_exclude'] == 'exclude') && $in_selected) {
continue;
}
}
$options[$id] = $action->label();
}
return $options;
}
/**
......@@ -190,6 +265,35 @@ public function viewsFormSubmit(&$form, &$form_state) {
if (!empty($operation_definition['confirm_form_path'])) {
$form_state['redirect'] = $operation_definition['confirm_form_path'];
}
$count = count(array_filter($form_state['values'][$this->options['id']]));
$action = $this->actions[$form_state['values']['action']];
if ($count) {
drupal_set_message($this->translationManager()->formatPlural($count, '%action was applied to @count item.', '%action was applied to @count items.', array(
'%action' => $action->label(),
)));
}
}
}
/**
* Returns the message to be displayed when there are no selected items.
*
* @return string
* Message displayed when no items are selected.
*/
protected function emptySelectedMessage() {
return t('No items selected.');
}
/**
* {@inheritdoc}
*/
public function viewsFormValidate(&$form, &$form_state) {
$selected = array_filter($form_state['values'][$this->options['id']]);
if (empty($selected)) {
form_set_error('', $form_state, $this->emptySelectedMessage());
}
}
......
......@@ -8,29 +8,15 @@
namespace Drupal\user\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\views\Plugin\views\field\ActionBulkForm;
use Drupal\system\Plugin\views\field\BulkForm;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a user operations bulk form element.
*
* @PluginID("user_bulk_form")
*/
class UserBulkForm extends ActionBulkForm {
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $manager);
// Filter the actions to only include those for the 'user' entity type.
$this->actions = array_filter($this->actions, function ($action) {
return $action->getType() == 'user';
});
}
class UserBulkForm extends BulkForm {
/**
* {@inheritdoc}
......@@ -53,11 +39,8 @@ public function viewsForm(&$form, &$form_state) {
/**
* {@inheritdoc}
*/
public function viewsFormValidate(&$form, &$form_state) {
$selected = array_filter($form_state['values'][$this->options['id']]);
if (empty($selected)) {
form_set_error('', $form_state, t('No users selected.'));
}
protected function emptySelectedMessage() {
return t('No users selected.');
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\user\Tests\Plugin\views\field;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
use Drupal\user\Plugin\views\field\UserBulkForm;
......@@ -25,6 +26,15 @@ public static function getInfo() {
);
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
parent::tearDown();
$container = new ContainerBuilder();
\Drupal::setContainer($container);
}