View source
<?php
namespace Drupal\views_bulk_operations\Plugin\views\field;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\Plugin\views\field\UncacheableFieldHandlerTrait;
use Drupal\views\Plugin\views\style\Table;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\views_bulk_operations\Service\ViewsbulkOperationsViewDataInterface;
use Drupal\views_bulk_operations\Service\ViewsBulkOperationsActionManager;
use Drupal\views_bulk_operations\Service\ViewsBulkOperationsActionProcessorInterface;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
class ViewsBulkOperationsBulkForm extends FieldPluginBase implements CacheableDependencyInterface, ContainerFactoryPluginInterface {
use RedirectDestinationTrait;
use UncacheableFieldHandlerTrait;
protected $viewData;
protected $actionManager;
protected $actionProcessor;
protected $tempStoreFactory;
protected $currentUser;
protected $actions = [];
protected $bulkOptions;
protected $userTempStore;
protected $tempStoreData = [];
public function __construct(array $configuration, $plugin_id, $plugin_definition, ViewsbulkOperationsViewDataInterface $viewData, ViewsBulkOperationsActionManager $actionManager, ViewsBulkOperationsActionProcessorInterface $actionProcessor, PrivateTempStoreFactory $tempStoreFactory, AccountInterface $currentUser) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->viewData = $viewData;
$this->actionManager = $actionManager;
$this->actionProcessor = $actionProcessor;
$this->tempStoreFactory = $tempStoreFactory;
$this->currentUser = $currentUser;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('views_bulk_operations.data'), $container
->get('plugin.manager.views_bulk_operations_action'), $container
->get('views_bulk_operations.processor'), $container
->get('user.private_tempstore'), $container
->get('current_user'));
}
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->viewData
->init($view, $display, $this->options['relationship']);
$this->actions = [];
$entity_types = $this->viewData
->getEntityTypeIds();
if (!empty($entity_types)) {
foreach ($this->actionManager
->getDefinitions() as $id => $definition) {
if (empty($definition['type']) || in_array($definition['type'], $entity_types, TRUE)) {
$this->actions[$id] = $definition;
}
}
}
$tempstore_name = 'views_bulk_operations_' . $view
->id() . '_' . $view->current_display;
$this->userTempStore = $this->tempStoreFactory
->get($tempstore_name);
$this->options['form_step'] = TRUE;
}
public function getCacheMaxAge() {
return 0;
}
public function getCacheContexts() {
return [];
}
public function getCacheTags() {
return [];
}
public function getEntity(ResultRow $row) {
return $this->viewData
->getEntity($row);
}
public function query() {
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['batch'] = [
'default' => TRUE,
];
$options['batch_size'] = [
'default' => 10,
];
$options['form_step'] = [
'default' => TRUE,
];
$options['buttons'] = [
'default' => FALSE,
];
$options['action_title'] = [
'default' => $this
->t('Action'),
];
$options['selected_actions'] = [
'default' => [],
];
$options['preconfiguration'] = [
'default' => [],
];
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
if (empty($this->actions)) {
$form = [
'#type' => 'item',
'#title' => $this
->t('NOTE'),
'#markup' => $this
->t('Views Bulk Operations will work only with normal entity views and contrib module views that are integrated. See /Drupal\\views_bulk_operations\\EventSubscriber\\ViewsBulkOperationsEventSubscriber class for integration best practice.'),
'#prefix' => '<div class="scroll">',
'#suffix' => '</div>',
];
return;
}
$form['#attributes']['class'][] = 'views-bulk-operations-ui';
$form['#attached']['library'][] = 'views_bulk_operations/adminUi';
$form['batch'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Process in a batch operation'),
'#default_value' => $this->options['batch'],
];
$form['batch_size'] = [
'#title' => $this
->t('Batch size'),
'#type' => 'number',
'#min' => 1,
'#step' => 1,
'#description' => $this
->t('Only applicable if results are processed in a batch operation.'),
'#default_value' => $this->options['batch_size'],
];
$form['form_step'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Configuration form on new page (configurable actions)'),
'#default_value' => $this->options['form_step'],
'#access' => FALSE,
];
$form['buttons'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display selectable actions as buttons.'),
'#default_value' => $this->options['buttons'],
];
$form['action_title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Action title'),
'#default_value' => $this->options['action_title'],
'#description' => $this
->t('The title shown above the actions dropdown.'),
];
$form['selected_actions'] = [
'#tree' => TRUE,
'#type' => 'details',
'#open' => TRUE,
'#title' => $this
->t('Selected actions'),
'#attributes' => [
'class' => [
'vbo-actions-widget',
],
],
];
$form_values = $form_state
->getValue([
'options',
'selected_actions',
]);
if (is_null($form_values)) {
$selected_actions = $this->options['selected_actions'];
$preconfiguration = $this->options['preconfiguration'];
}
else {
$selected_actions = [];
$preconfiguration = [];
foreach ($form_values as $id => $value) {
$selected_actions[$id] = $value['state'] ? $id : 0;
$preconfiguration[$id] = isset($value['preconfiguration']) ? $value['preconfiguration'] : [];
}
}
foreach ($this->actions as $id => $action) {
$form['selected_actions'][$id]['state'] = [
'#type' => 'checkbox',
'#title' => $action['label'],
'#default_value' => empty($selected_actions[$id]) ? 0 : 1,
'#attributes' => [
'class' => [
'vbo-action-state',
],
],
];
$form['selected_actions'][$id]['preconfiguration'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Preconfiguration for "@action"', [
'@action' => $action['label'],
]),
'#attributes' => [
'data-for' => $id,
'style' => empty($selected_actions[$id]) ? 'display: none' : NULL,
],
];
$form['selected_actions'][$id]['preconfiguration']['label_override'] = [
'#type' => 'textfield',
'#title' => $this
->t('Override label'),
'#description' => $this
->t('Leave empty for the default label.'),
'#default_value' => isset($preconfiguration[$id]['label_override']) ? $preconfiguration[$id]['label_override'] : '',
];
if (method_exists($action['class'], 'buildPreConfigurationForm')) {
if (!isset($preconfiguration[$id])) {
$preconfiguration[$id] = [];
}
$actionObject = $this->actionManager
->createInstance($id);
$form['selected_actions'][$id]['preconfiguration'] = $actionObject
->buildPreConfigurationForm($form['selected_actions'][$id]['preconfiguration'], $preconfiguration[$id], $form_state);
}
}
parent::buildOptionsForm($form, $form_state);
}
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
$options =& $form_state
->getValue('options');
foreach ($options['selected_actions'] as $id => $action) {
if (!empty($action['state'])) {
if (isset($action['preconfiguration'])) {
$options['preconfiguration'][$id] = $action['preconfiguration'];
unset($options['selected_actions'][$id]['preconfiguration']);
}
$options['selected_actions'][$id] = $id;
}
else {
unset($options['preconfiguration'][$id]);
$options['selected_actions'][$id] = 0;
}
}
parent::submitOptionsForm($form, $form_state);
}
public function preRender(&$values) {
parent::preRender($values);
if (empty($this
->getBulkOptions())) {
$this->options['element_label_class'] .= 'empty';
$this->options['element_class'] .= 'empty';
$this->options['element_wrapper_class'] .= 'empty';
$this->options['label'] = '';
}
elseif (!empty($this->view->style_plugin) && $this->view->style_plugin instanceof Table) {
$this->options['element_label_class'] .= 'select-all';
$this->options['label'] = '';
}
}
public function getValue(ResultRow $row, $field = NULL) {
return '<!--form-item-' . $this->options['id'] . '--' . $row->index . '-->';
}
public function viewsForm(array &$form, FormStateInterface $form_state) {
$form['#cache']['max-age'] = 0;
$use_revision = array_key_exists('revision', $this->view
->getQuery()
->getEntityTableInfo());
if ($this->view->style_plugin instanceof Table) {
$form['#attached']['library'][] = 'core/drupal.tableselect';
$form['#attached']['library'][] = 'views_bulk_operations/selectAll';
}
$action_options = $this
->getBulkOptions();
if (!empty($this->view->result) && !empty($action_options)) {
$this->tempStoreData['entity_labels'] = [];
$form[$this->options['id']]['#tree'] = TRUE;
foreach ($this->view->result as $row_index => $row) {
$entity = $this
->getEntity($row);
$this->tempStoreData['entity_labels'][$row_index] = $entity
->label();
$form[$this->options['id']][$row_index] = [
'#type' => 'checkbox',
'#title' => $this
->t('Update this item'),
'#title_display' => 'invisible',
'#default_value' => !empty($form_state
->getValue($this->options['id'])[$row_index]) ? 1 : NULL,
'#return_value' => self::calculateEntityBulkFormKey($entity, $use_revision, $row_index),
];
}
$form['header'] = [
'#type' => 'container',
'#weight' => -100,
];
$form['header'][$this->options['id']] = [
'#type' => 'container',
'#attributes' => [
'id' => 'vbo-action-form-wrapper',
],
];
if ($this->options['buttons']) {
unset($form['actions']['submit']);
foreach ($action_options as $id => $label) {
$form['actions'][$id] = [
'#type' => 'submit',
'#value' => $label,
];
}
}
else {
$form['actions']['submit']['#value'] = $this
->t('Apply to selected items');
$form['header'][$this->options['id']]['action'] = [
'#type' => 'select',
'#title' => $this->options['action_title'],
'#options' => [
'' => $this
->t('-- Select action --'),
] + $action_options,
];
}
if (empty($this->options['form_step'])) {
$form['header'][$this->options['id']]['action']['#ajax'] = [
'callback' => [
__CLASS__,
'viewsFormAjax',
],
'wrapper' => 'vbo-action-configuration-wrapper',
];
$form['header'][$this->options['id']]['configuration'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'vbo-action-configuration-wrapper',
],
];
$action_id = $form_state
->getValue('action');
if (!empty($action_id)) {
$action = $this->actions[$action_id];
if ($this
->isConfigurable($action)) {
$actionObject = $this->actionManager
->createInstance($action_id);
$form['header'][$this->options['id']]['configuration'] += $actionObject
->buildConfigurationForm($form['header'][$this->options['id']]['configuration'], $form_state);
$form['header'][$this->options['id']]['configuration']['#config_included'] = TRUE;
}
}
}
$show_all_selector = FALSE;
if (!empty($this->view->pager) && method_exists($this->view->pager, 'hasMoreRecords')) {
$show_all_selector = $this->view->pager
->getCurrentPage() > 0 || $this->view->pager
->hasMoreRecords();
}
$this->tempStoreData['total_results'] = $this->viewData
->getTotalResults();
if ($show_all_selector) {
$form['header'][$this->options['id']]['select_all'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Select all@count results in this view', [
'@count' => $this->tempStoreData['total_results'] ? ' ' . $this->tempStoreData['total_results'] : '',
]),
'#attributes' => [
'class' => [
'vbo-select-all',
],
],
];
}
$form['header'][$this->options['id']]['actions'] = $form['actions'];
}
else {
unset($form['actions']);
}
}
public static function viewsFormAjax(array $form, FormStateInterface $form_state) {
$trigger = $form_state
->getTriggeringElement();
$plugin_id = $trigger['#array_parents'][1];
return $form['header'][$plugin_id]['configuration'];
}
protected function getBulkOptions() {
if (!isset($this->bulkOptions)) {
$this->bulkOptions = [];
foreach ($this->actions as $id => $definition) {
if (!in_array($id, $this->options['selected_actions'], TRUE)) {
continue;
}
if (!empty($definition['requirements']['_permission']) && !$this->currentUser
->hasPermission($definition['requirements']['_permission'])) {
continue;
}
if (!empty($this->options['preconfiguration'][$id]['label_override'])) {
$this->bulkOptions[$id] = $this->options['preconfiguration'][$id]['label_override'];
}
else {
$this->bulkOptions[$id] = $definition['label'];
}
}
}
return $this->bulkOptions;
}
public function viewsFormSubmit(array &$form, FormStateInterface $form_state) {
if ($form_state
->get('step') == 'views_form_views_form') {
$action_id = $form_state
->getValue('action');
$action = $this->actions[$action_id];
$this->tempStoreData += [
'action_id' => $action_id,
'action_label' => empty($this->options['preconfiguration'][$action_id]['label_override']) ? (string) $action['label'] : $this->options['preconfiguration'][$action_id]['label_override'],
'relationship_id' => $this->options['relationship'],
'preconfiguration' => isset($this->options['preconfiguration'][$action_id]) ? $this->options['preconfiguration'][$action_id] : [],
'list' => [],
'view_id' => $this->view
->id(),
'display_id' => $this->view->current_display,
'batch' => $this->options['batch'],
'arguments' => $this->view->args,
'exposed_input' => $this->view
->getExposedInput(),
];
if (!empty($action['pass_view'])) {
$this->tempStoreData['current_page'] = 0;
if (!empty($this->view->pager) && method_exists($this->view->pager, 'getCurrentPage')) {
$this->tempStoreData['current_page'] = $this->view->pager
->getCurrentPage();
}
}
if (!$form_state
->getValue('select_all')) {
$selected = array_filter($form_state
->getValue($this->options['id']));
$selected_indexes = [];
foreach ($selected as $bulk_form_key) {
$item = json_decode(base64_decode($bulk_form_key));
$this->tempStoreData['list'][] = $item;
$selected_indexes[] = $item[0];
}
$this->tempStoreData['entity_labels'] = array_filter($this->tempStoreData['entity_labels'], function ($key) use ($selected_indexes) {
return in_array($key, $selected_indexes, TRUE);
}, ARRAY_FILTER_USE_KEY);
}
else {
$this->tempStoreData['entity_labels'] = [];
}
$configurable = $this
->isConfigurable($action);
if ($configurable && empty($this->options['form_step'])) {
$actionObject = $this->actionManager
->createInstance($action_id);
if (method_exists($actionObject, 'submitConfigurationForm')) {
$actionObject
->submitConfigurationForm($form, $form_state);
$this->tempStoreData['configuration'] = $actionObject
->getConfiguration();
}
else {
$form_state
->cleanValues();
$this->tempStoreData['configuration'] = $form_state
->getValues();
}
}
if ($this->options['form_step'] && $configurable) {
$redirect_route = 'views_bulk_operations.execute_configurable';
}
elseif ($this->options['batch']) {
if (!empty($action['confirm_form_route_name'])) {
$redirect_route = $action['confirm_form_route_name'];
}
else {
$redirect_route = 'views_bulk_operations.execute_batch';
}
}
elseif (!empty($action['confirm_form_route_name'])) {
$redirect_route = $action['confirm_form_route_name'];
}
if (!empty($redirect_route)) {
$this->tempStoreData['batch_size'] = $this->options['batch_size'];
$this->tempStoreData['redirect_url'] = Url::createFromRequest(\Drupal::request());
$this->userTempStore
->set($this->currentUser
->id(), $this->tempStoreData);
$form_state
->setRedirect($redirect_route, [
'view_id' => $this->view
->id(),
'display_id' => $this->view->current_display,
]);
}
else {
$this->actionProcessor
->executeProcessing($this->tempStoreData, $this->view);
}
}
}
public function viewsFormValidate(&$form, FormStateInterface $form_state) {
if ($this->options['buttons']) {
$trigger = $form_state
->getTriggeringElement();
$action_id = end($trigger['#parents']);
$form_state
->setValue('action', $action_id);
}
if (empty($form_state
->getValue('action'))) {
$form_state
->setErrorByName('action', $this
->t('Please select an action to perform.'));
}
if (!isset($this->actions[$form_state
->getValue('action')])) {
$form_state
->setErrorByName('action', $this
->t('Form error occurred, please try again.'));
}
if (!$form_state
->getValue('select_all')) {
$selected = array_filter($form_state
->getValue($this->options['id']));
if (empty($selected)) {
$form_state
->setErrorByName('', $this
->t('No items selected.'));
}
}
if (empty($this->options['form_step']) && !empty($form['header'][$this->options['id']]['configuration']['#config_included'])) {
$action_id = $form_state
->getValue('action');
$action = $this->actions[$action_id];
if (method_exists($action['class'], 'validateConfigurationForm')) {
$actionObject = $this->actionManager
->createInstance($action_id);
$actionObject
->validateConfigurationForm($form['header'][$this->options['id']]['configuration'], $form_state);
}
}
}
public function clickSortable() {
return FALSE;
}
protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
drupal_set_message($message, $type, $repeat);
}
public static function calculateEntityBulkFormKey(EntityInterface $entity, $use_revision, $row_index) {
$key_parts = [
$row_index,
$entity
->language()
->getId(),
$entity
->getEntityTypeId(),
$entity
->id(),
];
if ($entity instanceof RevisionableInterface && $use_revision) {
$key_parts[] = $entity
->getRevisionId();
}
$key = json_encode($key_parts);
return base64_encode($key);
}
protected function isConfigurable($action) {
return in_array('Drupal\\Core\\Plugin\\PluginFormInterface', class_implements($action['class']), TRUE) || method_exists($action['class'], 'buildConfigurationForm');
}
}