View source
<?php
namespace Drupal\entity_reference_actions;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsButtonsWidget;
use Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsSelectWidget;
use Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsWidgetBase;
use Drupal\Core\Form\EnforcedResponseException;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\system\ActionConfigEntityInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class EntityReferenceActionsHandler implements ContainerInjectionInterface {
use DependencySerializationTrait;
use StringTranslationTrait;
protected $entityTypeManager;
protected $currentUser;
protected $requestStack;
protected $uuidGenerator;
protected $httpKernel;
protected $actions = [];
protected $settings = [];
protected $entityTypeId;
public function __construct(EntityTypeManagerInterface $entityTypeManager, AccountProxyInterface $currentUser, RequestStack $requestStack, UuidInterface $uuidGenerator, HttpKernelInterface $httpKernel) {
$this->entityTypeManager = $entityTypeManager;
$this->currentUser = $currentUser;
$this->requestStack = $requestStack;
$this->uuidGenerator = $uuidGenerator;
$this->httpKernel = $httpKernel;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_type.manager'), $container
->get('current_user'), $container
->get('request_stack'), $container
->get('uuid'), $container
->get('http_kernel'));
}
public function init($entity_type_id, array $settings) {
$this->entityTypeId = $entity_type_id;
$actionStorage = $this->entityTypeManager
->getStorage('action');
$this->actions = array_filter($actionStorage
->loadMultiple(), function ($action) {
return $action
->getType() == $this->entityTypeId;
});
$this->settings = NestedArray::mergeDeepArray([
[
'enabled' => FALSE,
'options' => [
'action_title' => $this
->t('Action'),
'include_exclude' => 'exclude',
'selected_actions' => [],
],
],
$settings,
]);
}
public function formAlter(array &$element, FormStateInterface $form_state, array $context) {
if (!$this->settings['enabled']) {
return;
}
$items = $context['items'];
$field_definition = $items
->getFieldDefinition();
$uuid = 'entity_reference_actions-' . $this->uuidGenerator
->generate();
$form_state
->set($uuid, $context);
$element['entity_reference_actions_messages'] = [
'#type' => 'container',
'#attributes' => [
'data-entity-reference-actions-messages' => '',
],
];
$element['entity_reference_actions'] = [
'#type' => 'simple_actions',
'#uuid' => $uuid,
'#attached' => [
'library' => [
'core/drupal.dialog.ajax',
'core/drupal.states',
],
],
'#states' => [
'visible' => $this
->getVisibleStateConditions($element, $context),
],
];
$bulk_options = $this
->getBulkOptions();
foreach ($bulk_options as $id => $label) {
$element['entity_reference_actions'][$id] = [
'#type' => 'submit',
'#limit_validation_errors' => [
$element['widget']['#parents'],
],
'#submit' => [],
'#id' => $field_definition
->getName() . '_' . $id . '_button',
'#name' => $field_definition
->getName() . '_' . $id . '_button',
'#value' => $label,
'#ajax' => [
'callback' => [
$this,
'submitForm',
],
],
];
if (count($bulk_options) > 1) {
$element['entity_reference_actions'][$id]['#dropbutton'] = 'bulk_edit';
}
}
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$parents = array_slice($button['#array_parents'], 0, -2);
$field_name = end($parents);
$parents = array_slice($parents, 0, -1);
$values = NestedArray::getValue($form, $parents);
$context = $form_state
->get($values[$field_name]['entity_reference_actions']['#uuid']);
$items = $context['items'];
$context['widget']
->extractFormValues($items, $values, $form_state);
$action = $this->actions[end($button['#array_parents'])];
$ids = array_filter(!$items
->isEmpty() ? array_column($items
->getValue(), 'target_id') : []);
$entities = $this->entityTypeManager
->getStorage($items
->getSettings()['target_type'])
->loadMultiple($ids);
$commands = [];
$entities = array_filter($entities, function ($entity) use ($action, &$commands) {
if (!$action
->getPlugin()
->access($entity, $this->currentUser)) {
$commands[] = new MessageCommand($this
->t('No access to execute %action on the @entity_type_label %entity_label.', [
'%action' => $action
->label(),
'@entity_type_label' => $entity
->getEntityType()
->getLabel(),
'%entity_label' => $entity
->label(),
]), '[data-entity-reference-actions-messages]', [
'type' => 'warning',
], FALSE);
return FALSE;
}
return TRUE;
});
$response = new AjaxResponse();
if ($entities) {
$dialog_options = [
'minHeight' => '75%',
'maxHeight' => '75%',
'width' => '75%',
];
$operation_definition = $action
->getPluginDefinition();
if (!empty($operation_definition['confirm_form_route_name'])) {
$action
->getPlugin()
->executeMultiple($entities);
$request = $this->requestStack
->getCurrentRequest();
$dialog_url = Url::fromRoute($operation_definition['confirm_form_route_name'], [
MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_modal',
])
->toString(TRUE);
$parameter = [
'ajax_page_state' => $request->request
->get('ajax_page_state'),
'dialogOptions' => $dialog_options,
];
$sub_request = Request::create($dialog_url
->getGeneratedUrl(), 'POST', $parameter, [], [], $request->server
->all());
if ($request
->getSession()) {
$sub_request
->setSession($request
->getSession());
}
$response = $this->httpKernel
->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
$response
->setContent("{}");
}
else {
$batch_builder = (new BatchBuilder())
->setTitle($this
->getActionLabel($action))
->setFinishCallback([
__CLASS__,
'batchFinish',
]);
foreach ($entities as $entity) {
$batch_builder
->addOperation([
__CLASS__,
'batchCallback',
], [
$entity
->id(),
$entity
->getEntityTypeId(),
$action
->id(),
]);
}
batch_set($batch_builder
->toArray());
batch_process();
require_once DRUPAL_ROOT . '/core/includes/batch.inc';
$batch_page = _batch_progress_page();
$batch_page['#attached']['library'] = [
'entity_reference_actions/batch',
];
$response
->addCommand(new OpenModalDialogCommand($this
->getActionLabel($action), $batch_page, $dialog_options));
}
}
else {
$entity_type = $this->entityTypeManager
->getDefinition($this->entityTypeId);
$commands[] = new MessageCommand($this
->t('No @entity_type_label selected.', [
'@entity_type_label' => $entity_type
->getPluralLabel(),
]), '[data-entity-reference-actions-messages]', [
'type' => 'warning',
]);
}
foreach ($commands as $command) {
$response
->addCommand($command);
}
return $response;
}
public static function batchFinish() {
$batch =& batch_get();
\Drupal::service('batch.storage')
->delete($batch['id']);
foreach ($batch['sets'] as $batch_set) {
if ($queue = _batch_queue($batch_set)) {
$queue
->deleteQueue();
}
}
if (isset($_SESSION)) {
unset($_SESSION['batches'][$batch['id']]);
if (empty($_SESSION['batches'])) {
unset($_SESSION['batches']);
}
}
$response = new AjaxResponse();
$response
->addCommand(new CloseModalDialogCommand());
$response
->addCommand(new MessageCommand(t('%action was successfully applied.', [
'%action' => $batch['sets'][0]['title'],
]), '[data-entity-reference-actions-messages]'));
throw new EnforcedResponseException($response);
}
public static function batchCallback($entity_id, $entity_type_id, $action_id) {
$entity_type_manager = \Drupal::entityTypeManager();
$action = $entity_type_manager
->getStorage('action')
->load($action_id);
$entity = $entity_type_manager
->getStorage($entity_type_id)
->load($entity_id);
$action
->getPlugin()
->executeMultiple([
$entity,
]);
}
public function buildSettingsForm(array &$form, FormStateInterface $form_state, $field_name) {
$form['enabled'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Enable Entity Reference Actions'),
'#default_value' => $this->settings['enabled'],
];
$form['options'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Entity Reference Actions settings'),
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][entity_reference_actions][enabled]"]' => [
'checked' => TRUE,
],
],
],
];
$form['options']['action_title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Action title'),
'#default_value' => $this->settings['options']['action_title'],
'#description' => $this
->t('The title shown above the actions dropdown.'),
];
$form['options']['include_exclude'] = [
'#type' => 'radios',
'#title' => $this
->t('Available actions'),
'#options' => [
'exclude' => $this
->t('All actions, except selected'),
'include' => $this
->t('Only selected actions'),
],
'#default_value' => $this->settings['options']['include_exclude'],
];
$form['options']['selected_actions'] = [
'#type' => 'checkboxes',
'#title' => $this
->t('Selected actions'),
'#options' => $this
->getBulkOptions(FALSE),
'#default_value' => $this->settings['options']['selected_actions'],
];
}
protected function getBulkOptions($filtered = TRUE) {
$options = [];
foreach ($this->actions as $id => $action) {
if ($filtered) {
$in_selected = in_array($id, array_filter($this->settings['options']['selected_actions']));
if ($this->settings['options']['include_exclude'] == 'include' && !$in_selected) {
continue;
}
elseif ($this->settings['options']['include_exclude'] == 'exclude' && $in_selected) {
continue;
}
}
$options[$id] = $this
->getActionLabel($action);
}
return $options;
}
protected function getActionLabel(ActionConfigEntityInterface $action) {
$entity_type = $this->entityTypeManager
->getDefinition($this->entityTypeId);
$label = $action
->label();
if (isset($action
->getPlugin()
->getPluginDefinition()['action_label'])) {
$label = sprintf('%s all %s', $action
->getPlugin()
->getPluginDefinition()['action_label'], $entity_type
->getPluralLabel());
}
return $label;
}
public static function dialogAjaxSubmit(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
if ($form_state
->hasAnyErrors()) {
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -1000,
];
$form['#sorted'] = FALSE;
$response
->addCommand(new ReplaceCommand('[data-drupal-selector="' . $form['#attributes']['data-drupal-selector'] . '"]', $form));
}
else {
$response
->addCommand(new MessageCommand(t('Action was successfully applied.'), '[data-entity-reference-actions-messages]'));
$response
->addCommand(new CloseModalDialogCommand());
}
return $response;
}
protected function getVisibleStateConditions(array $element, array $context) {
$items = $context['items'];
$parents = $element['widget']['#parents'];
$first_parent = array_shift($parents) . '[';
$secondary_parents = '';
if ($parents) {
$secondary_parents = implode('][', $parents) . ']';
}
$field_selector = 'name^="' . $first_parent . $secondary_parents . '"';
$state_selector = [
":input[{$field_selector}]" => [
'filled' => TRUE,
],
];
$multiple = $items
->getFieldDefinition()
->getFieldStorageDefinition()
->isMultiple();
if ($context['widget'] instanceof OptionsWidgetBase) {
$state_selector = [
":input[{$field_selector}]" => [
'checked' => TRUE,
],
];
}
if ($context['widget'] instanceof OptionsButtonsWidget && !$multiple) {
$state_selector = [
":input[{$field_selector}]" => [
'!value' => isset($element['widget']['#empty_value']) ? $element['widget']['#empty_value'] : '_none',
],
];
}
if ($context['widget'] instanceof OptionsSelectWidget) {
$state_selector = [
"select[{$field_selector}]" => [
'!value' => isset($element['widget']['#empty_value']) ? $element['widget']['#empty_value'] : '_none',
],
];
if ($multiple) {
$state_selector = [];
}
}
return $state_selector;
}
}