class Taxonomy in Workbench Access 8
Defines a hierarchy based on a Vocabulary.
Plugin annotation
@AccessControlHierarchy(
id = "taxonomy",
module = "taxonomy",
entity = "taxonomy_term",
label = @Translation("Taxonomy"),
description = @Translation("Uses a taxonomy vocabulary as an access control hierarchy.")
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\workbench_access\AccessControlHierarchyBase implements ContainerFactoryPluginInterface, AccessControlHierarchyInterface uses PluginWithFormsTrait, StringTranslationTrait
- class \Drupal\workbench_access\Plugin\AccessControlHierarchy\Taxonomy
- class \Drupal\workbench_access\AccessControlHierarchyBase implements ContainerFactoryPluginInterface, AccessControlHierarchyInterface uses PluginWithFormsTrait, StringTranslationTrait
Expanded class hierarchy of Taxonomy
File
- src/
Plugin/ AccessControlHierarchy/ Taxonomy.php, line 34
Namespace
Drupal\workbench_access\Plugin\AccessControlHierarchyView source
class Taxonomy extends AccessControlHierarchyBase {
/**
* Field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* Bundle info.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* Constructs a new AccessControlHierarchyBase object.
*
* @param array $configuration
* Configuration.
* @param string $plugin_id
* Plugin ID.
* @param mixed $plugin_definition
* Plugin definition.
* @param \Drupal\workbench_access\UserSectionStorageInterface $user_section_storage
* User section storage.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* Config factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* Entity type manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
* Entity field manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundleInfo
* Entity type bundle info.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, UserSectionStorageInterface $user_section_storage, ConfigFactoryInterface $configFactory, EntityTypeManagerInterface $entityTypeManager, EntityFieldManagerInterface $entityFieldManager, EntityTypeBundleInfoInterface $bundleInfo) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $user_section_storage, $configFactory, $entityTypeManager);
$this->entityFieldManager = $entityFieldManager;
$this->bundleInfo = $bundleInfo;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('workbench_access.user_section_storage'), $container
->get('config.factory'), $container
->get('entity_type.manager'), $container
->get('entity_field.manager'), $container
->get('entity_type.bundle.info'));
}
/**
* {@inheritdoc}
*/
public function getTree() {
if (!isset($this->tree)) {
$this->tree = [];
/** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
$term_storage = $this->entityTypeManager
->getStorage('taxonomy_term');
$tree = [];
foreach ($this->configuration['vocabularies'] as $vocabulary_id) {
if ($vocabulary = Vocabulary::load($vocabulary_id)) {
$tree[$vocabulary_id][$vocabulary_id] = [
'label' => $vocabulary
->label(),
'depth' => 0,
'parents' => [],
'weight' => 0,
'description' => $vocabulary
->label(),
'path' => $vocabulary
->toUrl('overview-form')
->toString(),
];
// @TODO: It is possible that this will return a filtered set, if
// term_access is applied to the query.
$data = $term_storage
->loadTree($vocabulary_id);
$this->tree = $this
->buildTree($vocabulary_id, $data, $tree);
}
}
}
return $this->tree;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$defaults = [
'fields' => [],
'vocabularies' => [],
];
return $defaults + parent::defaultConfiguration();
}
/**
* Traverses the taxonomy tree and builds parentage arrays.
*
* Note: this method is necessary to load all parents to the array.
*
* @param string $id
* The root id of the section tree.
* @param array $data
* An array of menu tree or subtree data.
* @param array &$tree
* The computed tree array to return.
*
* @return array
* The compiled tree data.
*/
protected function buildTree($id, array $data, array &$tree) {
foreach ($data as $term) {
$tree[$id][$term->tid] = [
'id' => $term->tid,
'label' => $term->name,
'depth' => $term->depth + 1,
'parents' => $this
->convertParents($term, $id),
'weight' => $term->weight,
'description' => $term->description__value,
'path' => Url::fromUri('entity:taxonomy_term/' . $term->tid)
->toString(),
];
foreach ($tree[$id][$term->tid]['parents'] as $key) {
if (!empty($tree[$id][$key]['parents'])) {
$tree[$id][$term->tid]['parents'] = array_unique(array_merge($tree[$id][$key]['parents'], $tree[$id][$term->tid]['parents']));
}
}
}
return $tree;
}
/**
* Coverts the 0 parent id to a string.
*
* @param object $term
* The term to modify.
* @param string $id
* The root parent id string.
*/
private function convertParents($term, $id) {
foreach ($term->parents as $pos => $parent) {
if ($parent === 0 || $parent === '0') {
$term->parents[$pos] = $id;
}
}
return $term->parents;
}
/**
* {@inheritdoc}
*/
public function alterForm(AccessSchemeInterface $scheme, array &$form, FormStateInterface &$form_state, ContentEntityInterface $entity) {
foreach (array_column($this
->getApplicableFields($entity
->getEntityTypeId(), $entity
->bundle()), 'field') as $field) {
if (!isset($form[$field])) {
continue;
}
$element =& $form[$field];
if (isset($element['widget']['#options'])) {
foreach ($element['widget']['#options'] as $id => $data) {
// When using a select list, options may be a nested array.
if (is_array($data)) {
foreach ($data as $index => $item) {
$sections = [
$index,
];
if (empty(WorkbenchAccessManager::checkTree($scheme, $sections, $this->userSectionStorage
->getUserSections($scheme)))) {
unset($element['widget']['#options'][$id][$index]);
}
}
// If the parent is empty, remove it.
if (empty($element['widget']['#options'][$id])) {
unset($element['widget']['#options'][$id]);
}
}
else {
$sections = [
$id,
];
if ($id !== '_none' && empty(WorkbenchAccessManager::checkTree($scheme, $sections, $this->userSectionStorage
->getUserSections($scheme)))) {
unset($element['widget']['#options'][$id]);
}
}
}
}
else {
foreach ($element['widget'] as $key => $item) {
if (is_array($item) && isset($item['target_id']['#type']) && $item['target_id']['#type'] == 'entity_autocomplete') {
$element['widget'][$key]['target_id']['#selection_handler'] = 'workbench_access:taxonomy_term:' . $scheme
->id();
$element['widget'][$key]['target_id']['#validate_reference'] = TRUE;
// Hide elements that cannot be edited.
if (!empty($element['widget'][$key]['target_id']['#default_value'])) {
$sections = [
$element['widget'][$key]['target_id']['#default_value']
->id(),
];
if (empty(WorkbenchAccessManager::checkTree($scheme, $sections, $this->userSectionStorage
->getUserSections($scheme)))) {
unset($element['widget'][$key]);
$id = current($sections);
$disallowed[$id] = $id;
}
}
}
}
}
if (!empty($disallowed)) {
$form['workbench_access_disallowed']['#tree'] = TRUE;
$form['workbench_access_disallowed'][$field] = [
$scheme
->id() => [
'#type' => 'value',
'#value' => $disallowed,
],
];
}
}
}
/**
* {@inheritdoc}
*/
public function applies($entity_type_id, $bundle) {
return (bool) $this
->getApplicableFields($entity_type_id, $bundle);
}
/**
* {@inheritdoc}
*/
public function getEntityValues(EntityInterface $entity) {
if (!$entity instanceof ContentEntityInterface) {
return [];
}
$values = [];
foreach (array_column($this
->getApplicableFields($entity
->getEntityTypeId(), $entity
->bundle()), 'field') as $field) {
foreach ($entity
->get($field)
->getValue() as $item) {
if (isset($item['target_id'])) {
$values[] = $item['target_id'];
}
}
}
return $values;
}
/**
* @inheritdoc
*/
public function getApplicableFields($entity_type, $bundle) {
return array_filter($this->configuration['fields'], function ($field) use ($entity_type, $bundle) {
$field += [
'entity_type' => NULL,
'bundle' => NULL,
'field' => '',
];
return $field['entity_type'] === $entity_type && $field['bundle'] === $bundle;
});
}
/**
* {@inheritdoc}
*/
public function viewsData(array &$data, AccessSchemeInterface $scheme) {
foreach (array_column($this->configuration['fields'], 'entity_type') as $entity_type_id) {
$entity_type = $this->entityTypeManager
->getDefinition($entity_type_id);
if (($base_table = $entity_type
->getBaseTable()) && ($id = $entity_type
->getKey('id'))) {
$data[$base_table]['workbench_access_section__' . $scheme
->id()] = [
'title' => t('Workbench access @name', [
'@name' => $scheme
->label(),
]),
'help' => t('The sections to which this content belongs in the @name scheme.', [
'@name' => $scheme
->label(),
]),
'field' => [
'scheme' => $scheme
->id(),
'id' => 'workbench_access_section',
],
'filter' => [
'field' => $id,
'scheme' => $scheme
->id(),
'id' => 'workbench_access_section',
],
];
}
}
}
/**
* {@inheritdoc}
*/
public function massageFormValues(ContentEntityInterface $entity, FormStateInterface $form_state, array $hidden_values) {
foreach (array_column($this
->getApplicableFields($entity
->getEntityTypeId(), $entity
->bundle()), 'field') as $field_name) {
$values = $form_state
->getValue($field_name);
// The $hidden_values are deeply nested.
foreach ($hidden_values as $key => $value) {
if ($key === $field_name) {
foreach ($value as $element) {
foreach ($element as $item) {
// Ensure that we do not save duplicate values. Note that this
// cannot be a strict in_array() check thanks to form handling.
if (empty($values[0]) || !in_array($item, array_values($values[0]))) {
$values[]['target_id'] = $item;
}
}
}
}
}
$form_state
->setValue($field_name, $values);
}
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['vocabularies'] = [
'#type' => 'checkboxes',
'#title' => $this
->t('Vocabularies'),
'#description' => $this
->t('Select the vocabularies to use for access control'),
'#default_value' => $this->configuration['vocabularies'],
'#options' => array_map(function (VocabularyInterface $vocabulary) {
return $vocabulary
->label();
}, $this->entityTypeManager
->getStorage('taxonomy_vocabulary')
->loadMultiple()),
];
$entity_reference_fields = $this->entityFieldManager
->getFieldMapByFieldType('entity_reference');
$taxonomy_fields = [];
foreach ($entity_reference_fields as $entity_type_id => $fields) {
foreach ($fields as $field_name => $details) {
// Parent fields on taxonomy terms would create infinite loops. Deny.
if ($entity_type_id == 'taxonomy_term' && $field_name == 'parent') {
continue;
}
foreach ($details['bundles'] as $bundle) {
$field_definitions = $this->entityFieldManager
->getFieldDefinitions($entity_type_id, $bundle);
if (isset($field_definitions[$field_name]) && $field_definitions[$field_name]
->getFieldStorageDefinition()
->getSetting('target_type') === 'taxonomy_term') {
$handler_settings = $field_definitions[$field_name]
->getSetting('handler_settings');
// Must refer to a proper target. Target bundles referring to
// themselves would create an infinite loop. Deny.
if ($entity_type_id == 'taxonomy_term' && in_array($bundle, $this->configuration['vocabularies'], TRUE)) {
continue;
}
// Must have a proper target.
if (!isset($handler_settings['target_bundles'])) {
continue;
}
// At least one target must be configured for access control.
$allowed = array_intersect($handler_settings['target_bundles'], $this->configuration['vocabularies']);
if (empty($allowed)) {
continue;
}
// Create a unique key for each option.
$key = sprintf('%s:%s:%s', $entity_type_id, $bundle, $field_name);
$taxonomy_fields[$key] = [
'entity_type' => $this->entityTypeManager
->getDefinition($entity_type_id)
->getLabel(),
'bundle' => $this->bundleInfo
->getBundleInfo($entity_type_id)[$bundle]['label'],
'field' => $field_definitions[$field_name]
->getLabel(),
];
$validate[$key] = $handler_settings['target_bundles'];
}
}
}
}
if (!$taxonomy_fields) {
$form['fields'] = [
'#markup' => $this
->t('There are no configured taxonomy fields, please create a new term reference field on a content type to continue'),
];
return $form;
}
$default_value = array_map(function (array $field) {
$field += [
'entity_type' => NULL,
'bundle' => NULL,
'field' => '',
];
return sprintf('%s:%s:%s', $field['entity_type'], $field['bundle'], $field['field']);
}, $this->configuration['fields']);
$form['fields'] = [
'#type' => 'tableselect',
'#header' => [
'entity_type' => $this
->t('Entity type'),
'bundle' => $this
->t('Bundle'),
'field' => $this
->t('Field name'),
],
'#options' => $taxonomy_fields,
'#default_value' => array_combine($default_value, $default_value),
];
$form['validate'] = [
'#type' => 'value',
'#value' => $validate,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
$settings = $form_state
->getValues();
$settings['vocabularies'] = array_values(array_filter($settings['vocabularies']));
if (!empty($settings['fields'])) {
$settings['fields'] = array_filter($settings['fields']);
foreach ($settings['fields'] as $field) {
if (isset($settings['validate'][$field])) {
$error = TRUE;
foreach ($settings['vocabularies'] as $vocabulary) {
if (in_array($vocabulary, $settings['validate'][$field], TRUE)) {
$error = FALSE;
}
}
if ($error) {
$form_field = $form['fields']['#options'][$field];
list($entity_type, $bundle, $field_name) = explode(':', $field);
$form_state
->setErrorByName('scheme_settings][fields][' . $field, $this
->t('The field %field on %type entities of type %bundle is not in the selected vocabularies.', [
'%field' => $form_field['field'],
'%type' => $entity_type,
'%bundle' => $form_field['bundle'],
]));
}
}
}
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$settings = $form_state
->getValues();
if (empty($settings['fields'])) {
$settings['fields'] = [];
}
// Saving 'validate' can cause schema errors.
unset($settings['validate']);
$settings['vocabularies'] = array_values(array_filter($settings['vocabularies']));
$settings['fields'] = array_values(array_map(function ($item) {
list($entity_type, $bundle, $field_name) = explode(':', $item);
return [
'entity_type' => $entity_type,
'bundle' => $bundle,
'field' => $field_name,
];
}, array_filter($settings['fields'])));
$this->configuration = $settings;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependent_entities = $this->entityTypeManager
->getStorage('taxonomy_vocabulary')
->loadMultiple($this->configuration['vocabularies']);
$dependent_entities = array_merge($dependent_entities, $this->entityTypeManager
->getStorage('field_config')
->loadMultiple(array_map(function (array $field) {
$field += [
'entity_type' => NULL,
'bundle' => NULL,
'field' => '',
];
return sprintf('%s.%s.%s', $field['entity_type'], $field['bundle'], $field['field']);
}, $this->configuration['fields'])));
return array_reduce($dependent_entities, function (array $carry, ConfigEntityInterface $entity) {
$carry[$entity
->getConfigDependencyKey()][] = $entity
->getConfigDependencyName();
return $carry;
}, []);
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
$fields = array_udiff($this->configuration['fields'], array_reduce($dependencies['config'], function (array $carry, $item) {
if (!$item instanceof FieldConfigInterface) {
return $carry;
}
$carry[] = [
'field' => $item
->getName(),
'entity_type' => $item
->getTargetEntityTypeId(),
'bundle' => $item
->getTargetBundle(),
];
return $carry;
}, []), function ($array1, $array2) {
$key1 = sprintf('%s.%s.%s', $array1['field'], $array1['entity_type'], $array1['bundle']);
$key2 = sprintf('%s.%s.%s', $array2['field'], $array2['entity_type'], $array2['bundle']);
if ($key1 < $key2) {
return -1;
}
elseif ($key1 > $key2) {
return 1;
}
else {
return 0;
}
});
$vocabularies = array_diff($this->configuration['vocabularies'], array_reduce($dependencies['config'], function (array $carry, $item) {
if (!$item instanceof VocabularyInterface) {
return $carry;
}
$carry[] = $item
->id();
return $carry;
}, []));
$changed = $fields != $this->configuration['fields'] || $vocabularies != $this->configuration['vocabularies'];
$this->configuration['fields'] = $fields;
$this->configuration['vocabularies'] = $vocabularies;
return $changed;
}
/**
* {@inheritdoc}
*
* @TODO: Refactor
*/
public function getViewsJoin($entity_type, $key, $alias = NULL) {
if ($entity_type == 'user') {
$configuration['taxonomy'] = [
'table' => 'section_association__user_id',
'field' => 'user_id_target_id',
'left_table' => 'users',
'left_field' => $key,
'operator' => '=',
'table_alias' => 'section_association__user_id',
'real_field' => 'entity_id',
];
return $configuration;
}
$fields = array_column(array_filter($this->configuration['fields'], function ($field) use ($entity_type) {
return isset($field['entity_type']) && $field['entity_type'] === $entity_type;
}), 'field');
$table_prefix = $entity_type;
$field_suffix = '_target_id';
$configuration = [];
foreach ($fields as $field) {
$configuration[$field] = [
'table' => $table_prefix . '__' . $field,
'field' => 'entity_id',
'left_table' => $entity_type,
'left_field' => $key,
'operator' => '=',
'table_alias' => $field,
'real_field' => $field . $field_suffix,
];
}
return $configuration;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AccessControlHierarchyBase:: |
protected | property | Config for module. | |
AccessControlHierarchyBase:: |
protected | property | A configuration factory object to store configuration. | |
AccessControlHierarchyBase:: |
protected | property | Entity type manager. | |
AccessControlHierarchyBase:: |
protected | property | The access tree array. | |
AccessControlHierarchyBase:: |
protected | property | User section storage. | |
AccessControlHierarchyBase:: |
public | function |
Adds a where clause to a view when using a section filter. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Responds to request for node access. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Gets any options that are set but cannot be changed by the editor. Overrides AccessControlHierarchyInterface:: |
1 |
AccessControlHierarchyBase:: |
public | function | Gets the entity type id controlled by the scheme. | |
AccessControlHierarchyBase:: |
public | function |
Gets this plugin's configuration. Overrides ConfigurableInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Returns the id for a hierarchy. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Returns the label for a hierarchy. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Loads a hierarchy definition for a single item in the tree. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Resets the internal cache of the tree. Overrides AccessControlHierarchyInterface:: |
|
AccessControlHierarchyBase:: |
public | function |
Sets the configuration for this plugin instance. Overrides ConfigurableInterface:: |
|
AccessControlHierarchyBase:: |
public static | function |
Responds to the submission of an entity form. Overrides AccessControlHierarchyInterface:: |
|
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginWithFormsTrait:: |
public | function | ||
PluginWithFormsTrait:: |
public | function | ||
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
Taxonomy:: |
protected | property | Bundle info. | |
Taxonomy:: |
protected | property | Field manager. | |
Taxonomy:: |
public | function |
Alters the selection options provided for an access control field. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Check if this access scheme applies to the given entity. Overrides AccessControlHierarchyInterface:: |
|
Taxonomy:: |
public | function |
Form constructor. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
protected | function | Traverses the taxonomy tree and builds parentage arrays. | |
Taxonomy:: |
public | function |
Calculates dependencies for the configured plugin. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
private | function | Coverts the 0 parent id to a string. | |
Taxonomy:: |
public static | function |
Creates an instance of the plugin. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Gets default configuration for this plugin. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
@inheritdoc Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Retrieves the access control values from an entity. Overrides AccessControlHierarchyInterface:: |
|
Taxonomy:: |
public | function |
Gets the entire hierarchy tree. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
@TODO: Refactor Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Massage form values as appropriate during entity submit. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Informs the plugin that a dependency of the scheme will be deleted. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Form submission handler. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Form validation handler. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Adds views data for the plugin. Overrides AccessControlHierarchyBase:: |
|
Taxonomy:: |
public | function |
Constructs a new AccessControlHierarchyBase object. Overrides AccessControlHierarchyBase:: |