eca-1.0.x-dev/modules/access/src/Plugin/ECA/Event/AccessEvent.php
modules/access/src/Plugin/ECA/Event/AccessEvent.php
<?php
namespace Drupal\eca_access\Plugin\ECA\Event;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\eca\Attribute\EcaEvent;
use Drupal\eca\Attribute\Token;
use Drupal\eca\Entity\Objects\EcaEvent as EcaEventObject;
use Drupal\eca\Event\AccessEventInterface;
use Drupal\eca\Event\Tag;
use Drupal\eca\Plugin\ECA\Event\EventBase;
use Drupal\eca_access\AccessEvents;
use Drupal\eca_access\Event\CreateAccess;
use Drupal\eca_access\Event\EntityAccess;
use Drupal\eca_access\Event\FieldAccess;
use Symfony\Contracts\EventDispatcher\Event;
/**
* Plugin implementation of ECA access events.
*/
#[EcaEvent(
id: 'access',
deriver: 'Drupal\eca_access\Plugin\ECA\Event\AccessEventDeriver',
version_introduced: '1.0.0',
)]
class AccessEvent extends EventBase {
/**
* {@inheritdoc}
*/
public static function definitions(): array {
$definitions = [];
$definitions['entity'] = [
'label' => 'Determining entity access',
'event_name' => AccessEvents::ENTITY,
'event_class' => EntityAccess::class,
'tags' => Tag::RUNTIME | Tag::EPHEMERAL,
];
$definitions['field'] = [
'label' => 'Determining entity field access',
'event_name' => AccessEvents::FIELD,
'event_class' => FieldAccess::class,
'tags' => Tag::RUNTIME | Tag::EPHEMERAL,
];
$definitions['create'] = [
'label' => 'Determining entity create access',
'event_name' => AccessEvents::CREATE,
'event_class' => CreateAccess::class,
'tags' => Tag::RUNTIME | Tag::EPHEMERAL,
'version_introduced' => '1.1.0',
];
return $definitions;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
$is_field_event = $this->eventClass() === FieldAccess::class;
$values = ($is_field_event ? ['field_name' => ''] : []) +
[
'entity_type_id' => '',
'bundle' => '',
'operation' => '',
] + parent::defaultConfiguration();
if ($this->eventClass() === CreateAccess::class) {
unset($values['operation']);
$values['langcode'] = '';
}
return $values;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
$is_field_event = $this->eventClass() === FieldAccess::class;
$is_create_event = $this->eventClass() === CreateAccess::class;
$form['account_token_info'] = [
'#type' => 'container',
'#markup' => $this->t('For any successor of this event, the account that asks for access is available under the <strong>[account]</strong> token. Example: <strong>[account:uid]</strong> provides the user ID of the account.'),
'#weight' => 0,
];
if ($is_field_event) {
$form['event_token_info'] = [
'#type' => 'container',
'#markup' => $this->t('Furthermore, following data of the event is available:<ul><li><strong>[event:operation]</strong> holds the requested operation, such as "view".</li><li><strong>[event:field]</strong> holds the machine name of the field.</li></ul>'),
'#weight' => 1,
];
}
elseif (!$is_field_event) {
$form['event_token_info'] = [
'#type' => 'container',
'#markup' => $this->t('Furthermore, following data of the event is available:<ul><li><strong>[event:operation]</strong> holds the requested operation, such as "view".</li></ul>'),
'#weight' => 1,
];
}
$form['entity_type_id'] = [
'#type' => 'textfield',
'#title' => $this->t('Restrict by entity type ID'),
'#default_value' => $this->configuration['entity_type_id'],
'#description' => $this->t('Example: <em>node, taxonomy_term, user</em>'),
'#weight' => 10,
];
$form['bundle'] = [
'#type' => 'textfield',
'#title' => $this->t('Restrict by entity bundle'),
'#default_value' => $this->configuration['bundle'],
'#description' => $this->t('Example: <em>article, tags</em>'),
'#weight' => 20,
];
if (!$is_create_event) {
$form['operation'] = [
'#type' => 'textfield',
'#title' => $this->t('Restrict by operation'),
'#default_value' => $this->configuration['operation'],
'#description' => $is_field_event ? $this->t('Example: <em>view, edit</em>') : $this->t('Example: <em>view, update, delete</em>'),
'#weight' => 30,
];
}
if ($is_field_event) {
$form['field_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Restrict by field name'),
'#default_value' => $this->configuration['field_name'],
'#description' => $this->t('Example: <em>title, body, field_myfield</em>'),
'#weight' => 40,
];
}
if ($is_create_event) {
$form['langcode'] = [
'#type' => 'textfield',
'#title' => $this->t('Restrict by language code'),
'#default_value' => $this->configuration['langcode'],
'#description' => $this->t('Example: <em>en</em>'),
'#weight' => 40,
];
}
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
parent::submitConfigurationForm($form, $form_state);
$is_field_event = $this->eventClass() === FieldAccess::class;
$is_create_event = $this->eventClass() === CreateAccess::class;
$this->configuration['entity_type_id'] = $form_state->getValue('entity_type_id');
$this->configuration['bundle'] = $form_state->getValue('bundle');
if (!$is_create_event) {
$this->configuration['operation'] = $form_state->getValue('operation');
}
if ($is_field_event) {
$this->configuration['field_name'] = $form_state->getValue('field_name');
}
if ($is_create_event) {
$this->configuration['langcode'] = $form_state->getValue('langcode');
}
}
/**
* {@inheritdoc}
*/
public function generateWildcard(string $eca_config_id, EcaEventObject $ecaEvent): string {
$configuration = $ecaEvent->getConfiguration();
$is_create_event = $this->eventClass() === CreateAccess::class;
$wildcard = '';
$entity_type_ids = [];
if (!empty($configuration['entity_type_id'])) {
foreach (explode(',', $configuration['entity_type_id']) as $entity_type_id) {
$entity_type_id = strtolower(trim($entity_type_id));
if ($entity_type_id !== '') {
$entity_type_ids[] = $entity_type_id;
}
}
}
if ($entity_type_ids) {
$wildcard .= implode(',', $entity_type_ids);
}
else {
$wildcard .= '*';
}
$wildcard .= ':';
$bundles = [];
if (!empty($configuration['bundle'])) {
foreach (explode(',', $configuration['bundle']) as $bundle) {
$bundle = strtolower(trim($bundle));
if ($bundle !== '') {
$bundles[] = $bundle;
}
}
}
if ($bundles) {
$wildcard .= implode(',', $bundles);
}
else {
$wildcard .= '*';
}
if (!$is_create_event) {
$wildcard .= ':';
$operations = [];
if (!empty($configuration['operation'])) {
foreach (explode(',', $configuration['operation']) as $operation) {
$operation = trim($operation);
if ($operation !== '') {
$operations[] = $operation;
}
}
}
if ($operations) {
$wildcard .= implode(',', $operations);
}
else {
$wildcard .= '*';
}
}
$is_field_event = $this->eventClass() === FieldAccess::class;
if ($is_field_event) {
$wildcard .= ':';
$field_names = [];
if (!empty($configuration['field_name'])) {
foreach (explode(',', $configuration['field_name']) as $field_name) {
$field_name = trim($field_name);
if ($field_name !== '') {
$field_names[] = $field_name;
}
}
}
if ($field_names) {
$wildcard .= implode(',', $field_names);
}
else {
$wildcard .= '*';
}
}
if ($is_create_event) {
$wildcard .= ':';
$langcodes = [];
if (!empty($configuration['langcode'])) {
foreach (explode(',', $configuration['langcode']) as $langcode) {
$langcode = trim($langcode);
if ($langcode !== '') {
$langcodes[] = $langcode;
}
}
}
if ($langcodes) {
$wildcard .= implode(',', $langcodes);
}
else {
$wildcard .= '*';
}
}
return $wildcard;
}
/**
* {@inheritdoc}
*/
public static function appliesForWildcard(Event $event, string $event_name, string $wildcard): bool {
$parts = explode(':', $wildcard);
switch ($event_name) {
case AccessEvents::ENTITY:
case AccessEvents::FIELD:
[$w_entity_type_ids, $w_bundles, $w_operations] = $parts;
/** @var \Drupal\eca_access\Event\EntityAccess $event */
if (($w_entity_type_ids !== '*') && !in_array($event->getEntity()->getEntityTypeId(), explode(',', $w_entity_type_ids), TRUE)) {
return FALSE;
}
if (($w_bundles !== '*') && !in_array($event->getEntity()->bundle(), explode(',', $w_bundles), TRUE)) {
return FALSE;
}
if (($w_operations !== '*') && !in_array($event->getOperation(), explode(',', $w_operations), TRUE)) {
return FALSE;
}
if ($event_name === AccessEvents::FIELD) {
$w_field_names = end($parts);
/** @var \Drupal\eca_access\Event\FieldAccess $event */
if (($w_field_names !== '*') && !in_array($event->getFieldName(), explode(',', $w_field_names), TRUE)) {
return FALSE;
}
}
break;
case AccessEvents::CREATE:
[$w_entity_type_ids, $w_bundles, $w_langcodes] = $parts;
/** @var \Drupal\eca_access\Event\CreateAccess $event */
$event_context = $event->getContext();
if (($w_entity_type_ids !== '*') && !in_array($event_context['entity_type_id'], explode(',', $w_entity_type_ids), TRUE)) {
return FALSE;
}
if (($w_bundles !== '*') && !in_array($event->getEntityBundle(), explode(',', $w_bundles), TRUE)) {
return FALSE;
}
if (($w_langcodes !== '*') && !in_array($event_context['langcode'], explode(',', $w_langcodes), TRUE)) {
return FALSE;
}
break;
default:
throw new \InvalidArgumentException(sprintf("Given event %s is not supported.", $event_name));
}
// Initialize with a neutral result.
$event->setAccessResult(AccessResult::neutral());
return TRUE;
}
/**
* {@inheritdoc}
*/
#[Token(
name: 'event',
description: 'The event.',
properties: [
new Token(name: 'context', description: 'Contains a list of properties depending on the context of the event.', classes: [
CreateAccess::class,
]),
new Token(name: 'entity_bundle', description: 'The bundle of the entity.', classes: [
CreateAccess::class,
EntityAccess::class,
]),
new Token(name: 'entity_id', description: 'The entity ID, only available if the entity is not new.', classes: [
EntityAccess::class,
]),
new Token(name: 'entity_type', description: 'The entity type.', classes: [
EntityAccess::class,
]),
new Token(name: 'field', description: 'The name of the field.', classes: [
FieldAccess::class,
]),
new Token(name: 'operation', description: 'The operation with which the entity should be accessed, e.g. "view", "update", etc.', classes: [
EntityAccess::class,
]),
new Token(name: 'uid', description: 'The ID of the user account of the event.', classes: [
AccessEventInterface::class,
]),
],
)]
protected function buildEventData(): array {
$event = $this->event;
$data = [];
if ($event instanceof AccessEventInterface) {
$data += [
'uid' => $event->getAccount()->id(),
];
}
if ($event instanceof EntityAccess) {
$entity = $event->getEntity();
$data += [
'operation' => $event->getOperation(),
'entity_type' => $entity->getEntityTypeId(),
'entity_bundle' => $entity->bundle(),
];
if (!$entity->isNew()) {
$data['entity_id'] = $entity->id();
}
}
if ($event instanceof FieldAccess) {
$data += [
'field' => $event->getFieldName(),
];
}
if ($event instanceof CreateAccess) {
$data += [
'context' => [],
'entity_bundle' => $event->getEntityBundle(),
];
$context = $event->getContext();
foreach ($context as $k => $v) {
if (is_scalar($v)) {
$data['context'][$k] = $v;
}
}
if (isset($context['entity_type_id'])) {
$data['entity_type'] = $context['entity_type_id'];
}
}
$data += parent::buildEventData();
return $data;
}
}
