358314a4cadfd14652a4639429768072a3eb88a3
[yaffs-website] / ContentModeration.php
1 <?php
2
3 namespace Drupal\content_moderation\Plugin\WorkflowType;
4
5 use Drupal\content_moderation\ModerationInformationInterface;
6 use Drupal\Core\Entity\ContentEntityInterface;
7 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Entity\EntityPublishedInterface;
10 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
11 use Drupal\Core\StringTranslation\StringTranslationTrait;
12 use Drupal\content_moderation\ContentModerationState;
13 use Drupal\workflows\Plugin\WorkflowTypeBase;
14 use Drupal\workflows\StateInterface;
15 use Drupal\workflows\WorkflowInterface;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
17
18 /**
19  * Attaches workflows to content entity types and their bundles.
20  *
21  * @WorkflowType(
22  *   id = "content_moderation",
23  *   label = @Translation("Content moderation"),
24  *   required_states = {
25  *     "draft",
26  *     "published",
27  *   },
28  *   forms = {
29  *     "configure" = "\Drupal\content_moderation\Form\ContentModerationConfigureForm",
30  *     "state" = "\Drupal\content_moderation\Form\ContentModerationStateForm"
31  *   },
32  * )
33  */
34 class ContentModeration extends WorkflowTypeBase implements ContentModerationInterface, ContainerFactoryPluginInterface {
35
36   use StringTranslationTrait;
37
38   /**
39    * The entity type manager.
40    *
41    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
42    */
43   protected $entityTypeManager;
44
45   /**
46    * The entity type bundle info service.
47    *
48    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
49    */
50   protected $entityTypeBundleInfo;
51
52   /**
53    * The moderation information service.
54    *
55    * @var \Drupal\content_moderation\ModerationInformationInterface
56    */
57   protected $moderationInfo;
58
59   /**
60    * Constructs a ContentModeration object.
61    *
62    * @param array $configuration
63    *   A configuration array containing information about the plugin instance.
64    * @param string $plugin_id
65    *   The plugin_id for the plugin instance.
66    * @param mixed $plugin_definition
67    *   The plugin implementation definition.
68    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
69    *   The entity type manager.
70    * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
71    *   Moderation information service.
72    */
73   public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, ModerationInformationInterface $moderation_info) {
74     parent::__construct($configuration, $plugin_id, $plugin_definition);
75     $this->entityTypeManager = $entity_type_manager;
76     $this->entityTypeBundleInfo = $entity_type_bundle_info;
77     $this->moderationInfo = $moderation_info;
78   }
79
80   /**
81    * {@inheritdoc}
82    */
83   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
84     return new static(
85       $configuration,
86       $plugin_id,
87       $plugin_definition,
88       $container->get('entity_type.manager'),
89       $container->get('entity_type.bundle.info'),
90       $container->get('content_moderation.moderation_information')
91     );
92   }
93
94   /**
95    * {@inheritdoc}
96    */
97   public function getState($state_id) {
98     $state = parent::getState($state_id);
99     if (isset($this->configuration['states'][$state->id()]['published']) && isset($this->configuration['states'][$state->id()]['default_revision'])) {
100       $state = new ContentModerationState($state, $this->configuration['states'][$state->id()]['published'], $this->configuration['states'][$state->id()]['default_revision']);
101     }
102     else {
103       $state = new ContentModerationState($state);
104     }
105     return $state;
106   }
107
108   /**
109    * {@inheritdoc}
110    */
111   public function workflowHasData(WorkflowInterface $workflow) {
112     return (bool) $this->entityTypeManager
113       ->getStorage('content_moderation_state')
114       ->getQuery()
115       ->condition('workflow', $workflow->id())
116       ->count()
117       ->accessCheck(FALSE)
118       ->range(0, 1)
119       ->execute();
120   }
121
122   /**
123    * {@inheritdoc}
124    */
125   public function workflowStateHasData(WorkflowInterface $workflow, StateInterface $state) {
126     return (bool) $this->entityTypeManager
127       ->getStorage('content_moderation_state')
128       ->getQuery()
129       ->condition('workflow', $workflow->id())
130       ->condition('moderation_state', $state->id())
131       ->count()
132       ->accessCheck(FALSE)
133       ->range(0, 1)
134       ->execute();
135   }
136
137   /**
138    * {@inheritdoc}
139    */
140   public function getEntityTypes() {
141     return array_keys($this->configuration['entity_types']);
142   }
143
144   /**
145    * {@inheritdoc}
146    */
147   public function getBundlesForEntityType($entity_type_id) {
148     return isset($this->configuration['entity_types'][$entity_type_id]) ? $this->configuration['entity_types'][$entity_type_id] : [];
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function appliesToEntityTypeAndBundle($entity_type_id, $bundle_id) {
155     return in_array($bundle_id, $this->getBundlesForEntityType($entity_type_id), TRUE);
156   }
157
158   /**
159    * {@inheritdoc}
160    */
161   public function removeEntityTypeAndBundle($entity_type_id, $bundle_id) {
162     if (!isset($this->configuration['entity_types'][$entity_type_id])) {
163       return;
164     }
165     $key = array_search($bundle_id, $this->configuration['entity_types'][$entity_type_id], TRUE);
166     if ($key !== FALSE) {
167       unset($this->configuration['entity_types'][$entity_type_id][$key]);
168       if (empty($this->configuration['entity_types'][$entity_type_id])) {
169         unset($this->configuration['entity_types'][$entity_type_id]);
170       }
171       else {
172         $this->configuration['entity_types'][$entity_type_id] = array_values($this->configuration['entity_types'][$entity_type_id]);
173       }
174     }
175   }
176
177   /**
178    * {@inheritdoc}
179    */
180   public function addEntityTypeAndBundle($entity_type_id, $bundle_id) {
181     if (!$this->appliesToEntityTypeAndBundle($entity_type_id, $bundle_id)) {
182       $this->configuration['entity_types'][$entity_type_id][] = $bundle_id;
183       sort($this->configuration['entity_types'][$entity_type_id]);
184       ksort($this->configuration['entity_types']);
185     }
186   }
187
188   /**
189    * {@inheritdoc}
190    */
191   public function defaultConfiguration() {
192     return [
193       'states' => [
194         'draft' => [
195           'label' => 'Draft',
196           'published' => FALSE,
197           'default_revision' => FALSE,
198           'weight' => 0,
199         ],
200         'published' => [
201           'label' => 'Published',
202           'published' => TRUE,
203           'default_revision' => TRUE,
204           'weight' => 1,
205         ],
206       ],
207       'transitions' => [
208         'create_new_draft' => [
209           'label' => 'Create New Draft',
210           'to' => 'draft',
211           'weight' => 0,
212           'from' => [
213             'draft',
214             'published',
215           ],
216         ],
217         'publish' => [
218           'label' => 'Publish',
219           'to' => 'published',
220           'weight' => 1,
221           'from' => [
222             'draft',
223             'published',
224           ],
225         ],
226       ],
227       'entity_types' => [],
228     ];
229   }
230
231   /**
232    * {@inheritdoc}
233    */
234   public function calculateDependencies() {
235     $dependencies = parent::calculateDependencies();
236     foreach ($this->getEntityTypes() as $entity_type_id) {
237       $entity_definition = $this->entityTypeManager->getDefinition($entity_type_id);
238       foreach ($this->getBundlesForEntityType($entity_type_id) as $bundle) {
239         $dependency = $entity_definition->getBundleConfigDependency($bundle);
240         $dependencies[$dependency['type']][] = $dependency['name'];
241       }
242     }
243     return $dependencies;
244   }
245
246   /**
247    * {@inheritdoc}
248    */
249   public function onDependencyRemoval(array $dependencies) {
250     $changed = parent::onDependencyRemoval($dependencies);
251
252     // When bundle config entities are removed, ensure they are cleaned up from
253     // the workflow.
254     foreach ($dependencies['config'] as $removed_config) {
255       if ($entity_type_id = $removed_config->getEntityType()->getBundleOf()) {
256         $bundle_id = $removed_config->id();
257         $this->removeEntityTypeAndBundle($entity_type_id, $bundle_id);
258         $changed = TRUE;
259       }
260     }
261
262     // When modules that provide entity types are removed, ensure they are also
263     // removed from the workflow.
264     if (!empty($dependencies['module'])) {
265       // Gather all entity definitions provided by the dependent modules which
266       // are being removed.
267       $module_entity_definitions = [];
268       foreach ($this->entityTypeManager->getDefinitions() as $entity_definition) {
269         if (in_array($entity_definition->getProvider(), $dependencies['module'])) {
270           $module_entity_definitions[] = $entity_definition;
271         }
272       }
273
274       // For all entity types provided by the uninstalled modules, remove any
275       // configuration for those types.
276       foreach ($module_entity_definitions as $module_entity_definition) {
277         foreach ($this->getBundlesForEntityType($module_entity_definition->id()) as $bundle) {
278           $this->removeEntityTypeAndBundle($module_entity_definition->id(), $bundle);
279           $changed = TRUE;
280         }
281       }
282     }
283
284     return $changed;
285   }
286
287   /**
288    * {@inheritdoc}
289    */
290   public function getConfiguration() {
291     $configuration = parent::getConfiguration();
292     // Ensure that states and entity types are ordered consistently.
293     ksort($configuration['states']);
294     ksort($configuration['entity_types']);
295     return $configuration;
296   }
297
298   /**
299    * {@inheritdoc}
300    */
301   public function getInitialState($entity = NULL) {
302     // Workflows are not tied to entities, but Content Moderation adds the
303     // relationship between Workflows and entities. Content Moderation needs the
304     // entity object to be able to determine the initial state based on
305     // publishing status.
306     if (!($entity instanceof ContentEntityInterface)) {
307       throw new \InvalidArgumentException('A content entity object must be supplied.');
308     }
309     if ($entity instanceof EntityPublishedInterface) {
310       return $this->getState($entity->isPublished() && !$entity->isNew() ? 'published' : 'draft');
311     }
312     // Workflows determines the initial state for non-publishable entities.
313     return parent::getInitialState();
314   }
315
316 }