access_policy-1.0.x-dev/src/Plugin/access_policy/SelectionRule/EntityFieldBase.php
src/Plugin/access_policy/SelectionRule/EntityFieldBase.php
<?php namespace Drupal\access_policy\Plugin\access_policy\SelectionRule; use Drupal\access_policy\Plugin\access_policy\OperatorValidationTrait; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\user\PermissionHandlerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * The entity field base for selection rules. */ class EntityFieldBase extends SelectionRuleBase { use OperatorValidationTrait; /** * The module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ protected $moduleHandler; /** * The entity field manager. * * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ protected $entityFieldManager; /** * The field type plugin manager. * * @var \Drupal\Core\Field\FieldTypePluginManagerInterface */ protected $fieldTypeManager; /** * The entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * The permission handler. * * @var \Drupal\user\PermissionHandlerInterface */ protected $permissionHandler; /** * Constructs a EntityFieldBase 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. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager * The entity field manager. * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager * The field type plugin manager. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. * @param \Drupal\user\PermissionHandlerInterface $permission_handler * The permission handler. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, EntityTypeManagerInterface $entity_type_manager, PermissionHandlerInterface $permission_handler) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->moduleHandler = $module_handler; $this->entityFieldManager = $entity_field_manager; $this->fieldTypeManager = $field_type_manager; $this->entityTypeManager = $entity_type_manager; $this->permissionHandler = $permission_handler; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('module_handler'), $container->get('entity_field.manager'), $container->get('plugin.manager.field.field_type'), $container->get('entity_type.manager'), $container->get('user.permissions'), ); } /** * {@inheritdoc} */ public function defaultSettings() { return [ 'value' => '', 'field_access' => [ 'type' => 'none', ], ] + parent::defaultSettings(); } /** * {@inheritdoc} */ public function isApplicable(EntityInterface $entity) { $field_name = $this->definition->getFieldName(); if ($entity->hasField($field_name)) { return TRUE; } return FALSE; } /** * {@inheritdoc} */ public function validate(EntityInterface $entity, AccountInterface $account) { return $this->validateCallback($entity, $account); } /** * Validate method callback. * * For multi-value fields, validation callbacks operate on each value. This * is to align with how queries match content with conditions. * * @param \Drupal\Core\Entity\EntityInterface $entity * The current entity. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * * @return bool * TRUE if valid; FALSE otherwise. */ public function validateCallback(EntityInterface $entity, AccountInterface $account) { $operator = $this->getOperator(); $info = $this->operators(); // If the values should not be passed then pass in the original entity and // account. if (isset($info[$operator]['values']) && $info[$operator]['values'] === FALSE) { $valid = $this->{$info[$operator]['method']}($entity, $account); if ($valid) { return TRUE; } return FALSE; } $field_name = $this->definition->getFieldName(); $field_values = $this->getEntityFieldValues($entity, $field_name); $options = $this->getValue(); if (!empty($info[$operator]['method'])) { // If field values are empty we should still validate against it. if (empty($field_values)) { foreach ($options as $option) { $valid = $this->{$info[$operator]['method']}($option, $field_values); if ($valid) { return TRUE; } } } else { // If any of the field values match then return true. foreach ($field_values as $value) { foreach ($options as $option) { $valid = $this->{$info[$operator]['method']}($option, $value); if ($valid) { return TRUE; } } } } } return FALSE; } /** * Validate is applicable. * * In some cases a simple field exists validation is enough. This is * particularly true for manual selection workflow. * * @param \Drupal\Core\Entity\EntityInterface $entity * The current entity. * @param \Drupal\Core\Session\AccountInterface $account * The current user. * * @return bool * TRUE if the entity is applicable; FALSE otherwise. */ public function validateIsApplicable(EntityInterface $entity, AccountInterface $account) { return $this->isApplicable($entity); } /** * {@inheritdoc} */ protected function valueForm(array &$form, FormStateInterface $form_state) { $form['value'] = [ '#type' => 'textfield', '#title' => $this->t('Value'), '#default_value' => $this->settings['value'], ]; } /** * {@inheritdoc} */ public function buildSettingsForm(array $form, FormStateInterface $form_state) { $this->operatorForm($form, $form_state); $this->valueForm($form, $form_state); $this->hideValueFormWithStates($form, $form_state); $form['field_access'] = [ '#type' => 'details', '#title' => $this->t('Field access'), '#tree' => TRUE, ]; $form['field_access']['help'] = [ '#type' => 'markup', '#markup' => $this->t('<strong>Warning:</strong> This is a global setting. All instances of this field will be affected regardless of policy assigned.'), ]; $form['field_access']['type'] = [ '#type' => 'radios', '#title' => $this->t('Access'), '#options' => [ 'none' => $this->t('Unrestricted'), 'permission' => $this->t('Permission'), ], '#default_value' => $this->settings['field_access']['type'], ]; $form['field_access']['permission'] = [ '#type' => 'select', '#title' => $this->t('Permission'), '#options' => $this->getPermissionsOptions(), '#description' => $this->t('Only users with the selected permission will be able to access this field.'), '#states' => [ 'visible' => [':input[name="field_access[type]"]' => ['value' => 'permission']], ], '#default_value' => $this->settings['field_access']['permission'] ?? $this->getDefaultPermission(), ]; $form['admin_label'] = [ '#type' => 'details', '#title' => $this->t('Administrative title'), '#open' => FALSE, ]; $form['admin_label']['admin_label'] = [ '#type' => 'textfield', '#title' => $this->t('Administrative title'), '#default_value' => $this->settings['admin_label'], '#description' => $this->t('This will be displayed on the rules list. This is useful when you have the same access rule used twice.'), ]; return $form; } /** * Optionally hide the value form with states as defined by the operators. * * @param array $form * The current form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. */ protected function hideValueFormWithStates(array &$form, FormStateInterface $form_state) { $hide_value = FALSE; foreach ($this->operators() as $operator) { if (!empty($operator['hide_value'])) { $hide_value = TRUE; break; } } if ($hide_value) { $fields = []; foreach ($this->operators() as $key => $operator) { if (!empty($operator['hide_value'])) { $fields[] = ['value' => $key]; } } $form['value']['#states'] = [ 'invisible' => [ 'select[name="operator"]' => $fields, ], ]; } } /** * Determine whether the value is hidden. * * @return bool * TRUE if the value is hidden; FALSE otherwise. */ protected function isValueHidden() { $operators = $this->operators(); $operator = $this->getOperator(); if (!empty($operators[$operator]['hide_value'])) { return TRUE; } return FALSE; } /** * Get all permission options grouped by module. * * @return array * Array of permissions. */ protected function getPermissionsOptions() { $permissions = $this->permissionHandler->getPermissions(); $perms = []; foreach ($permissions as $perm => $perm_item) { $provider = $perm_item['provider']; $display_name = $this->moduleHandler->getName($provider); $perms[$display_name][$perm] = strip_tags($perm_item['title']); } return $perms; } /** * Get the default permission. * * @return string * The default permission. */ protected function getDefaultPermission() { $policy = $this->definition->getAccessPolicy(); return 'assign ' . $policy . ' access policy'; } /** * Get the values from a field. * * @param \Drupal\Core\Entity\EntityInterface $entity * The current entity. * @param string $field_name * The field name. * * @return array * Index array of field values. */ public function getEntityFieldValues(EntityInterface $entity, $field_name) { if (!$entity->hasField($field_name)) { return []; } $field = $entity->get($field_name); $field_definition = $field->getFieldDefinition(); // Get the main property name. $storage = $field_definition->getFieldStorageDefinition(); $column_name = $storage->getMainPropertyName(); $values = $entity->get($field_name)->getValue(); return array_map(function ($value) use ($column_name) { if (isset($value[$column_name])) { return $value[$column_name]; } }, $values); } /** * Get the values to compare against. * * @return array * The indexed array of values to compare against. */ public function getValue() { $value = parent::getValue(); // If the value is an array check to see if it's a field value. if (is_array($value)) { $column_name = $this->getMainPropertyName($this->definition->getFieldType()); return array_map(function ($value) use ($column_name) { // If the column name exists then return that. Otherwise just return // the value. if (isset($value[$column_name])) { return $value[$column_name]; } return $value; }, $value); } return $value; } /** * Get the main property name of a field. * * @param string $field_type * The field type. * * @return string * The main property name. */ public function getMainPropertyName($field_type) { $definition = $this->fieldTypeManager->getDefinition($field_type); $class = $definition['class']; return $class::mainPropertyName(); } /** * {@inheritdoc} */ public function adminSummary() { if ($this->isValueHidden()) { return $this->getOperator(); } return $this->getOperator() . ' ' . $this->settings['value']; } }