Newer
Older
Angie Byron
committed
<?php
/**
* @file
* Contains \Drupal\system\Plugin\views\field\BulkForm.
Angie Byron
committed
*/
namespace Drupal\system\Plugin\views\field;
catch
committed
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
Angie Byron
committed
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\Plugin\views\style\Table;
Alex Pott
committed
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
Angie Byron
committed
/**
* Defines a actions-based bulk operation form element.
*
* @ViewsField("bulk_form")
Angie Byron
committed
*/
class BulkForm extends FieldPluginBase {
/**
catch
committed
* The action storage.
*
catch
committed
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $actionStorage;
Angie Byron
committed
/**
* An array of actions that can be executed.
*
* @var array
*/
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 mixed $plugin_definition
* The plugin implementation definition.
catch
committed
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The action storage.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->actionStorage = $storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
catch
committed
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager')->getStorage('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;
});
}
Angie Byron
committed
/**
* {@inheritdoc}
Angie Byron
committed
*/
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(),
);
Angie Byron
committed
return $options;
}
/**
* {@inheritdoc}
Angie Byron
committed
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['action_title'] = array(
'#type' => 'textfield',
'#title' => t('Action title'),
'#default_value' => $this->options['action_title'],
'#description' => t('The title shown above the actions dropdown.'),
);
$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'],
);
parent::buildOptionsForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
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']);
Angie Byron
committed
/**
Alex Pott
committed
* {@inheritdoc}
Angie Byron
committed
*/
Alex Pott
committed
public function render(ResultRow $values) {
Angie Byron
committed
return '<!--form-item-' . $this->options['id'] . '--' . $this->view->row_index . '-->';
}
/**
* {@inheritdoc}
Angie Byron
committed
*/
public function preRender(&$values) {
parent::preRender($values);
Angie Byron
committed
// 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 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'] = '';
}
}
Angie Byron
committed
/**
* Form constructor for the bulk form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function viewsForm(&$form, &$form_state) {
Angie Byron
committed
// Add the tableselect javascript.
$form['#attached']['library'][] = 'core/drupal.tableselect';
Angie Byron
committed
Alex Pott
committed
// Only add the bulk form options and buttons if there are results.
if (!empty($this->view->result)) {
// Render checkboxes for all rows.
$form[$this->options['id']]['#tree'] = TRUE;
foreach ($this->view->result as $row_index => $row) {
$form[$this->options['id']][$row_index] = array(
'#type' => 'checkbox',
// We are not able to determine a main "title" for each row, so we can
// only output a generic label.
'#title' => t('Update this item'),
'#title_display' => 'invisible',
'#default_value' => !empty($form_state['values'][$this->options['id']][$row_index]) ? 1 : NULL,
);
}
// Replace the form submit button label.
$form['actions']['submit']['#value'] = t('Apply');
// Ensure a consistent container for filters/operations in the view header.
$form['header'] = array(
'#type' => 'container',
'#weight' => -100,
Angie Byron
committed
);
Alex Pott
committed
// Build the bulk operations action widget for the header.
// Allow themes to apply .container-inline on this separate container.
$form['header'][$this->options['id']] = array(
'#type' => 'container',
);
$form['header'][$this->options['id']]['action'] = array(
'#type' => 'select',
Angie Byron
committed
'#title' => $this->options['action_title'],
Alex Pott
committed
'#options' => $this->getBulkOptions(),
);
// Duplicate the form actions into the action container in the header.
$form['header'][$this->options['id']]['actions'] = $form['actions'];
}
else {
// Remove the default actions build array.
unset($form['actions']);
}
Angie Byron
committed
}
/**
* Returns the available operations for this form.
*
* @param bool $filtered
* (optional) Whether to filter actions to selected actions.
Angie Byron
committed
* @return array
* An associative array of operations, suitable for a select element.
*/
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;
Alex Pott
committed
}
Angie Byron
committed
/**
* Submit handler for the bulk form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function viewsFormSubmit(&$form, &$form_state) {
if ($form_state['step'] == 'views_form_views_form') {
// Filter only selected checkboxes.
$selected = array_filter($form_state['values'][$this->options['id']]);
$entities = array();
foreach (array_intersect_key($this->view->result, $selected) as $row) {
Alex Pott
committed
$entity = $this->getEntity($row);
$entities[$entity->id()] = $entity;
}
$action = $this->actions[$form_state['values']['action']];
$action->execute($entities);
$operation_definition = $action->getPluginDefinition();
if (!empty($operation_definition['confirm_form_path'])) {
$form_state['redirect'] = $operation_definition['confirm_form_path'];
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
$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());
}
}
Angie Byron
committed
/**
* Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::query().
*/
public function query() {
}
Alex Pott
committed
/**
* {@inheritdoc}
*/
public function clickSortable() {
return FALSE;
}