c658ef8759e79eb1c092845ffc8d8a61b001d021
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / views / filter / ModerationStateFilter.php
1 <?php
2
3 namespace Drupal\content_moderation\Plugin\views\filter;
4
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;
14
15 /**
16  * Provides a filter for the moderation state of an entity.
17  *
18  * @ingroup views_filter_handlers
19  *
20  * @ViewsFilter("moderation_state_filter")
21  */
22 class ModerationStateFilter extends InOperator implements DependentWithRemovalPluginInterface {
23
24   /**
25    * {@inheritdoc}
26    */
27   protected $valueFormType = 'select';
28
29   /**
30    * The entity type manager.
31    *
32    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
33    */
34   protected $entityTypeManager;
35
36   /**
37    * The bundle information service.
38    *
39    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
40    */
41   protected $bundleInfo;
42
43   /**
44    * The storage handler of the workflow entity type.
45    *
46    * @var \Drupal\Core\Entity\EntityStorageInterface
47    */
48   protected $workflowStorage;
49
50   /**
51    * Creates an instance of ModerationStateFilter.
52    */
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;
58   }
59
60   /**
61    * {@inheritdoc}
62    */
63   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
64     return new static(
65       $configuration,
66       $plugin_id,
67       $plugin_definition,
68       $container->get('entity_type.manager'),
69       $container->get('entity_type.bundle.info'),
70       $container->get('entity_type.manager')->getStorage('workflow')
71     );
72   }
73
74   /**
75    * {@inheritdoc}
76    */
77   public function getCacheTags() {
78     return Cache::mergeTags(parent::getCacheTags(), $this->entityTypeManager->getDefinition('workflow')->getListCacheTags());
79   }
80
81   /**
82    * {@inheritdoc}
83    */
84   public function getCacheContexts() {
85     return Cache::mergeContexts(parent::getCacheContexts(), $this->entityTypeManager->getDefinition('workflow')->getListCacheContexts());
86   }
87
88   /**
89    * {@inheritdoc}
90    */
91   public function getValueOptions() {
92     if (isset($this->valueOptions)) {
93       return $this->valueOptions;
94     }
95     $this->valueOptions = [];
96
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();
105         }
106       }
107     }
108
109     return $this->valueOptions;
110   }
111
112   /**
113    * {@inheritdoc}
114    */
115   public function ensureMyTable() {
116     if (!isset($this->tableAlias)) {
117       $table_alias = $this->query->ensureTable($this->table, $this->relationship);
118
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
123       // the filter.
124       $left_entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
125       $entity_type = $this->entityTypeManager->getDefinition('content_moderation_state');
126       $configuration = [
127         'table' => $entity_type->getRevisionDataTable(),
128         'field' => 'content_entity_revision_id',
129         'left_table' => $table_alias,
130         'left_field' => $left_entity_type->getKey('revision'),
131         'extra' => [
132           [
133             'field' => 'content_entity_type_id',
134             'value' => $left_entity_type->id(),
135           ],
136         ],
137       ];
138       if ($left_entity_type->isTranslatable()) {
139         $configuration['extra'][] = [
140           'field' => $entity_type->getKey('langcode'),
141           'left_field' => $left_entity_type->getKey('langcode'),
142         ];
143       }
144       $join = Views::pluginManager('join')->createInstance('standard', $configuration);
145       $this->tableAlias = $this->query->addRelationship('content_moderation_state', $join, 'content_moderation_state_field_revision');
146     }
147
148     return $this->tableAlias;
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   protected function opSimple() {
155     if (empty($this->value)) {
156       return;
157     }
158
159     $this->ensureMyTable();
160
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;
170         }
171       }
172
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;
177
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) {
183           $configuration = [
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'),
188             'type' => 'INNER',
189           ];
190
191           $join = Views::pluginManager('join')->createInstance('standard', $configuration);
192           $entity_base_table_alias = $this->query->addRelationship($entity_base_table, $join, $entity_revision_base_table);
193         }
194
195         $this->query->addWhere($this->options['group'], "$entity_base_table_alias.{$entity_type->getKey('bundle')}", $moderated_bundles, 'IN');
196       }
197       // Otherwise, force the query to return an empty result.
198       else {
199         $this->query->addWhereExpression($this->options['group'], '1 = 0');
200         return;
201       }
202     }
203
204     if ($this->operator === 'in') {
205       $operator = "=";
206     }
207     else {
208       $operator = "<>";
209     }
210
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);
216
217       $and = new Condition('AND');
218       $and
219         ->condition("$this->tableAlias.workflow", $workflow_id, '=')
220         ->condition("$this->tableAlias.$this->realField", $state_id, $operator);
221
222       $field->condition($and);
223     }
224
225     $this->query->addWhere($this->options['group'], $field);
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function calculateDependencies() {
232     $dependencies = parent::calculateDependencies();
233
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();
238       }
239     }
240
241     return $dependencies;
242   }
243
244   /**
245    * {@inheritdoc}
246    */
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.
251     $remove = FALSE;
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
256       // removed.
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.
261           $remove = TRUE;
262           break 2;
263         }
264       }
265     }
266     return $remove;
267   }
268
269   /**
270    * Gets the list of Workflow IDs configured for this filter.
271    *
272    * @return array
273    *   And array of workflow IDs.
274    */
275   protected function getWorkflowIds() {
276     $workflow_ids = [];
277     foreach ((array) $this->value as $value) {
278       list($workflow_id) = explode('-', $value, 2);
279       $workflow_ids[] = $workflow_id;
280     }
281
282     return array_unique($workflow_ids);
283   }
284
285 }