3 namespace Drupal\content_moderation\Plugin\views\filter;
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\Database\Query\Condition;
7 use Drupal\Core\Entity\EntityStorageInterface;
8 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
9 use Drupal\Core\Entity\EntityTypeManagerInterface;
10 use Drupal\views\Plugin\DependentWithRemovalPluginInterface;
11 use Drupal\views\Plugin\views\filter\InOperator;
12 use Drupal\views\Views;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
16 * Provides a filter for the moderation state of an entity.
18 * @ingroup views_filter_handlers
20 * @ViewsFilter("moderation_state_filter")
22 class ModerationStateFilter extends InOperator implements DependentWithRemovalPluginInterface {
27 protected $valueFormType = 'select';
30 * The entity type manager.
32 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
34 protected $entityTypeManager;
37 * The bundle information service.
39 * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
41 protected $bundleInfo;
44 * The storage handler of the workflow entity type.
46 * @var \Drupal\Core\Entity\EntityStorageInterface
48 protected $workflowStorage;
51 * Creates an instance of ModerationStateFilter.
53 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_info, EntityStorageInterface $workflow_storage) {
54 parent::__construct($configuration, $plugin_id, $plugin_definition);
55 $this->entityTypeManager = $entity_type_manager;
56 $this->bundleInfo = $bundle_info;
57 $this->workflowStorage = $workflow_storage;
63 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
68 $container->get('entity_type.manager'),
69 $container->get('entity_type.bundle.info'),
70 $container->get('entity_type.manager')->getStorage('workflow')
77 public function getCacheTags() {
78 return Cache::mergeTags(parent::getCacheTags(), $this->entityTypeManager->getDefinition('workflow')->getListCacheTags());
84 public function getCacheContexts() {
85 return Cache::mergeContexts(parent::getCacheContexts(), $this->entityTypeManager->getDefinition('workflow')->getListCacheContexts());
91 public function getValueOptions() {
92 if (isset($this->valueOptions)) {
93 return $this->valueOptions;
95 $this->valueOptions = [];
97 // Find all workflows which are moderating entity types of the same type the
98 // view is displaying.
99 foreach ($this->workflowStorage->loadByProperties(['type' => 'content_moderation']) as $workflow) {
100 /** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModerationInterface $workflow_type */
101 $workflow_type = $workflow->getTypePlugin();
102 if (in_array($this->getEntityType(), $workflow_type->getEntityTypes(), TRUE)) {
103 foreach ($workflow_type->getStates() as $state_id => $state) {
104 $this->valueOptions[$workflow->label()][implode('-', [$workflow->id(), $state_id])] = $state->label();
109 return $this->valueOptions;
115 public function ensureMyTable() {
116 if (!isset($this->tableAlias)) {
117 $table_alias = $this->query->ensureTable($this->table, $this->relationship);
119 // Filter the moderation states of the content via the
120 // ContentModerationState field revision table, joining either the entity
121 // field data or revision table. This allows filtering states against
122 // either the default or latest revision, depending on the relationship of
124 $left_entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
125 $entity_type = $this->entityTypeManager->getDefinition('content_moderation_state');
127 'table' => $entity_type->getRevisionDataTable(),
128 'field' => 'content_entity_revision_id',
129 'left_table' => $table_alias,
130 'left_field' => $left_entity_type->getKey('revision'),
133 'field' => 'content_entity_type_id',
134 'value' => $left_entity_type->id(),
138 if ($left_entity_type->isTranslatable()) {
139 $configuration['extra'][] = [
140 'field' => $entity_type->getKey('langcode'),
141 'left_field' => $left_entity_type->getKey('langcode'),
144 $join = Views::pluginManager('join')->createInstance('standard', $configuration);
145 $this->tableAlias = $this->query->addRelationship('content_moderation_state', $join, 'content_moderation_state_field_revision');
148 return $this->tableAlias;
154 protected function opSimple() {
155 if (empty($this->value)) {
159 $this->ensureMyTable();
161 $entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
162 if ($entity_type->hasKey('bundle')) {
163 // Get a list of bundles that are being moderated by the workflows
164 // configured in this filter.
165 $workflow_ids = $this->getWorkflowIds();
166 $moderated_bundles = [];
167 foreach ($this->bundleInfo->getBundleInfo($this->getEntityType()) as $bundle_id => $bundle) {
168 if (isset($bundle['workflow']) && in_array($bundle['workflow'], $workflow_ids, TRUE)) {
169 $moderated_bundles[] = $bundle_id;
173 // If we have a list of moderated bundles, restrict the query to show only
174 // entities in those bundles.
175 if ($moderated_bundles) {
176 $entity_base_table_alias = $this->table;
178 // The bundle field of an entity type is not revisionable so we need to
179 // join the base table.
180 $entity_base_table = $entity_type->getBaseTable();
181 $entity_revision_base_table = $entity_type->isTranslatable() ? $entity_type->getRevisionDataTable() : $entity_type->getRevisionTable();
182 if ($this->table === $entity_revision_base_table) {
184 'table' => $entity_base_table,
185 'field' => $entity_type->getKey('id'),
186 'left_table' => $entity_revision_base_table,
187 'left_field' => $entity_type->getKey('id'),
191 $join = Views::pluginManager('join')->createInstance('standard', $configuration);
192 $entity_base_table_alias = $this->query->addRelationship($entity_base_table, $join, $entity_revision_base_table);
195 $this->query->addWhere($this->options['group'], "$entity_base_table_alias.{$entity_type->getKey('bundle')}", $moderated_bundles, 'IN');
197 // Otherwise, force the query to return an empty result.
199 $this->query->addWhereExpression($this->options['group'], '1 = 0');
204 if ($this->operator === 'in') {
211 // The values are strings composed from the workflow ID and the state ID, so
212 // we need to create a complex WHERE condition.
213 $field = new Condition('OR');
214 foreach ((array) $this->value as $value) {
215 list($workflow_id, $state_id) = explode('-', $value, 2);
217 $and = new Condition('AND');
219 ->condition("$this->tableAlias.workflow", $workflow_id, '=')
220 ->condition("$this->tableAlias.$this->realField", $state_id, $operator);
222 $field->condition($and);
225 $this->query->addWhere($this->options['group'], $field);
231 public function calculateDependencies() {
232 $dependencies = parent::calculateDependencies();
234 if ($workflow_ids = $this->getWorkflowIds()) {
235 /** @var \Drupal\workflows\WorkflowInterface $workflow */
236 foreach ($this->workflowStorage->loadMultiple($workflow_ids) as $workflow) {
237 $dependencies[$workflow->getConfigDependencyKey()][] = $workflow->getConfigDependencyName();
241 return $dependencies;
247 public function onDependencyRemoval(array $dependencies) {
248 // See if this handler is responsible for any of the dependencies being
249 // removed. If this is the case, indicate that this handler needs to be
250 // removed from the View.
252 // Get all the current dependencies for this handler.
253 $current_dependencies = $this->calculateDependencies();
254 foreach ($current_dependencies as $group => $dependency_list) {
255 // Check if any of the handler dependencies match the dependencies being
257 foreach ($dependency_list as $config_key) {
258 if (isset($dependencies[$group]) && array_key_exists($config_key, $dependencies[$group])) {
259 // This handlers dependency matches a dependency being removed,
260 // indicate that this handler needs to be removed.
270 * Gets the list of Workflow IDs configured for this filter.
273 * And array of workflow IDs.
275 protected function getWorkflowIds() {
277 foreach ((array) $this->value as $value) {
278 list($workflow_id) = explode('-', $value, 2);
279 $workflow_ids[] = $workflow_id;
282 return array_unique($workflow_ids);