51605d5383d68aa9113afffcceabfe7ef49fe5f7
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / WorkflowType / ContentModeration.php
1 <?php
2
3 namespace Drupal\content_moderation\Plugin\WorkflowType;
4
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Entity\EntityPublishedInterface;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\Core\Session\AccountInterface;
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  * )
29  */
30 class ContentModeration extends WorkflowTypeBase implements ContainerFactoryPluginInterface {
31
32   use StringTranslationTrait;
33
34   /**
35    * The entity type manager.
36    *
37    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
38    */
39   protected $entityTypeManager;
40
41   /**
42    * Creates an instance of the ContentModeration WorkflowType plugin.
43    */
44   public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
45     parent::__construct($configuration, $plugin_id, $plugin_definition);
46     $this->entityTypeManager = $entity_type_manager;
47   }
48
49   /**
50    * {@inheritdoc}
51    */
52   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
53     return new static(
54       $configuration,
55       $plugin_id,
56       $plugin_definition,
57       $container->get('entity_type.manager')
58     );
59   }
60
61   /**
62    * {@inheritdoc}
63    */
64   public function initializeWorkflow(WorkflowInterface $workflow) {
65     $workflow
66       ->addState('draft', $this->t('Draft'))
67       ->setStateWeight('draft', -5)
68       ->addState('published', $this->t('Published'))
69       ->setStateWeight('published', 0)
70       ->addTransition('create_new_draft', $this->t('Create New Draft'), ['draft', 'published'], 'draft')
71       ->addTransition('publish', $this->t('Publish'), ['draft', 'published'], 'published');
72     return $workflow;
73   }
74
75   /**
76    * {@inheritdoc}
77    */
78   public function checkWorkflowAccess(WorkflowInterface $entity, $operation, AccountInterface $account) {
79     if ($operation === 'view') {
80       return AccessResult::allowedIfHasPermission($account, 'view content moderation');
81     }
82     return parent::checkWorkflowAccess($entity, $operation, $account);
83   }
84
85   /**
86    * {@inheritdoc}
87    */
88   public function decorateState(StateInterface $state) {
89     if (isset($this->configuration['states'][$state->id()])) {
90       $state = new ContentModerationState($state, $this->configuration['states'][$state->id()]['published'], $this->configuration['states'][$state->id()]['default_revision']);
91     }
92     else {
93       $state = new ContentModerationState($state);
94     }
95     return $state;
96   }
97
98   /**
99    * {@inheritdoc}
100    */
101   public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL) {
102     /** @var \Drupal\content_moderation\ContentModerationState $state */
103     $is_required_state = isset($state) ? in_array($state->id(), $this->getRequiredStates(), TRUE) : FALSE;
104
105     $form = [];
106     $form['published'] = [
107       '#type' => 'checkbox',
108       '#title' => $this->t('Published'),
109       '#description' => $this->t('When content reaches this state it should be published.'),
110       '#default_value' => isset($state) ? $state->isPublishedState() : FALSE,
111       '#disabled' => $is_required_state,
112     ];
113
114     $form['default_revision'] = [
115       '#type' => 'checkbox',
116       '#title' => $this->t('Default revision'),
117       '#description' => $this->t('When content reaches this state it should be made the default revision; this is implied for published states.'),
118       '#default_value' => isset($state) ? $state->isDefaultRevisionState() : FALSE,
119       '#disabled' => $is_required_state,
120       // @todo Add form #state to force "make default" on when "published" is
121       // on for a state.
122       // @see https://www.drupal.org/node/2645614
123     ];
124     return $form;
125   }
126
127   /**
128    * Gets the entity types the workflow is applied to.
129    *
130    * @return string[]
131    *   The entity types the workflow is applied to.
132    */
133   public function getEntityTypes() {
134     return array_keys($this->configuration['entity_types']);
135   }
136
137   /**
138    * Gets any bundles the workflow is applied to for the given entity type.
139    *
140    * @param string $entity_type_id
141    *   The entity type ID to get the bundles for.
142    *
143    * @return string[]
144    *   The bundles of the entity type the workflow is applied to or an empty
145    *   array if the entity type is not applied to the workflow.
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    * Checks if the workflow applies to the supplied entity type and bundle.
153    *
154    * @param string $entity_type_id
155    *   The entity type ID to check.
156    * @param string $bundle_id
157    *   The bundle ID to check.
158    *
159    * @return bool
160    *   TRUE if the workflow applies to the supplied entity type ID and bundle
161    *   ID. FALSE if not.
162    */
163   public function appliesToEntityTypeAndBundle($entity_type_id, $bundle_id) {
164     return in_array($bundle_id, $this->getBundlesForEntityType($entity_type_id), TRUE);
165   }
166
167   /**
168    * Removes an entity type ID / bundle ID from the workflow.
169    *
170    * @param string $entity_type_id
171    *   The entity type ID to remove.
172    * @param string $bundle_id
173    *   The bundle ID to remove.
174    */
175   public function removeEntityTypeAndBundle($entity_type_id, $bundle_id) {
176     $key = array_search($bundle_id, $this->configuration['entity_types'][$entity_type_id], TRUE);
177     if ($key !== FALSE) {
178       unset($this->configuration['entity_types'][$entity_type_id][$key]);
179       if (empty($this->configuration['entity_types'][$entity_type_id])) {
180         unset($this->configuration['entity_types'][$entity_type_id]);
181       }
182       else {
183         $this->configuration['entity_types'][$entity_type_id] = array_values($this->configuration['entity_types'][$entity_type_id]);
184       }
185     }
186   }
187
188   /**
189    * Add an entity type ID / bundle ID to the workflow.
190    *
191    * @param string $entity_type_id
192    *   The entity type ID to add. It is responsibility of the caller to provide
193    *   a valid entity type ID.
194    * @param string $bundle_id
195    *   The bundle ID to add. It is responsibility of the caller to provide a
196    *   valid bundle ID.
197    */
198   public function addEntityTypeAndBundle($entity_type_id, $bundle_id) {
199     if (!$this->appliesToEntityTypeAndBundle($entity_type_id, $bundle_id)) {
200       $this->configuration['entity_types'][$entity_type_id][] = $bundle_id;
201       sort($this->configuration['entity_types'][$entity_type_id]);
202       ksort($this->configuration['entity_types']);
203     }
204   }
205
206   /**
207    * {@inheritDoc}
208    */
209   public function defaultConfiguration() {
210     // This plugin does not store anything per transition.
211     return [
212       'states' => [
213         'draft' => [
214           'published' => FALSE,
215           'default_revision' => FALSE,
216         ],
217         'published' => [
218           'published' => TRUE,
219           'default_revision' => TRUE,
220         ],
221       ],
222       'entity_types' => [],
223     ];
224   }
225
226   /**
227    * {@inheritdoc}
228    */
229   public function calculateDependencies() {
230     $dependencies = parent::calculateDependencies();
231     foreach ($this->getEntityTypes() as $entity_type_id) {
232       $entity_definition = $this->entityTypeManager->getDefinition($entity_type_id);
233       foreach ($this->getBundlesForEntityType($entity_type_id) as $bundle) {
234         $dependency = $entity_definition->getBundleConfigDependency($bundle);
235         $dependencies[$dependency['type']][] = $dependency['name'];
236       }
237     }
238     return $dependencies;
239   }
240
241   /**
242    * {@inheritdoc}
243    */
244   public function onDependencyRemoval(array $dependencies) {
245     $changed = parent::onDependencyRemoval($dependencies);
246
247     // When bundle config entities are removed, ensure they are cleaned up from
248     // the workflow.
249     foreach ($dependencies['config'] as $removed_config) {
250       if ($entity_type_id = $removed_config->getEntityType()->getBundleOf()) {
251         $bundle_id = $removed_config->id();
252         $this->removeEntityTypeAndBundle($entity_type_id, $bundle_id);
253         $changed = TRUE;
254       }
255     }
256
257     // When modules that provide entity types are removed, ensure they are also
258     // removed from the workflow.
259     if (!empty($dependencies['module'])) {
260       // Gather all entity definitions provided by the dependent modules which
261       // are being removed.
262       $module_entity_definitions = [];
263       foreach ($this->entityTypeManager->getDefinitions() as $entity_definition) {
264         if (in_array($entity_definition->getProvider(), $dependencies['module'])) {
265           $module_entity_definitions[] = $entity_definition;
266         }
267       }
268
269       // For all entity types provided by the uninstalled modules, remove any
270       // configuration for those types.
271       foreach ($module_entity_definitions as $module_entity_definition) {
272         foreach ($this->getBundlesForEntityType($module_entity_definition->id()) as $bundle) {
273           $this->removeEntityTypeAndBundle($module_entity_definition->id(), $bundle);
274           $changed = TRUE;
275         }
276       }
277     }
278
279     return $changed;
280   }
281
282   /**
283    * {@inheritdoc}
284    */
285   public function getConfiguration() {
286     $configuration = parent::getConfiguration();
287     // Ensure that states and entity types are ordered consistently.
288     ksort($configuration['states']);
289     ksort($configuration['entity_types']);
290     return $configuration;
291   }
292
293   /**
294    * {@inheritdoc}
295    */
296   public function getInitialState(WorkflowInterface $workflow, $entity = NULL) {
297     if ($entity instanceof EntityPublishedInterface) {
298       return $workflow->getState($entity->isPublished() ? 'published' : 'draft');
299     }
300     return parent::getInitialState($workflow);
301   }
302
303 }