Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / content_moderation / content_moderation.module
1 <?php
2
3 /**
4  * @file
5  * Contains content_moderation.module.
6  */
7
8 use Drupal\content_moderation\EntityOperations;
9 use Drupal\content_moderation\EntityTypeInfo;
10 use Drupal\content_moderation\ContentPreprocess;
11 use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublishNode;
12 use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublishNode;
13 use Drupal\Core\Access\AccessResult;
14 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
15 use Drupal\Core\Entity\EntityInterface;
16 use Drupal\Core\Entity\EntityTypeInterface;
17 use Drupal\Core\Form\FormStateInterface;
18 use Drupal\Core\Routing\RouteMatchInterface;
19 use Drupal\Core\Session\AccountInterface;
20 use Drupal\workflows\WorkflowInterface;
21 use Drupal\node\NodeInterface;
22 use Drupal\node\Plugin\Action\PublishNode;
23 use Drupal\node\Plugin\Action\UnpublishNode;
24 use Drupal\workflows\Entity\Workflow;
25
26 /**
27  * Implements hook_help().
28  */
29 function content_moderation_help($route_name, RouteMatchInterface $route_match) {
30   switch ($route_name) {
31     // Main module help for the content_moderation module.
32     case 'help.page.content_moderation':
33       $output = '';
34       $output .= '<h3>' . t('About') . '</h3>';
35       $output .= '<p>' . t('The Content Moderation module provides moderation for content by applying workflows to content. For more information, see the <a href=":content_moderation">online documentation for the Content Moderation module</a>.', [':content_moderation' => 'https://www.drupal.org/documentation/modules/content_moderation']) . '</p>';
36       $output .= '<h3>' . t('Uses') . '</h3>';
37       $output .= '<dl>';
38       $output .= '<dt>' . t('Configuring workflows') . '</dt>';
39       $output .= '<dd>' . t('Enable the Workflow UI module to create, edit and delete content moderation workflows.') . '</p>';
40       $output .= '<dt>' . t('Configure Content Moderation permissions') . '</dt>';
41       $output .= '<dd>' . t('Each transition is exposed as a permission. If a user has the permission for a transition, then they can move that node from the start state to the end state') . '</p>';
42       $output .= '</dl>';
43       return $output;
44   }
45 }
46
47 /**
48  * Implements hook_entity_base_field_info().
49  */
50 function content_moderation_entity_base_field_info(EntityTypeInterface $entity_type) {
51   return \Drupal::service('class_resolver')
52     ->getInstanceFromDefinition(EntityTypeInfo::class)
53     ->entityBaseFieldInfo($entity_type);
54 }
55
56 /**
57  * Implements hook_entity_type_alter().
58  */
59 function content_moderation_entity_type_alter(array &$entity_types) {
60   \Drupal::service('class_resolver')
61     ->getInstanceFromDefinition(EntityTypeInfo::class)
62     ->entityTypeAlter($entity_types);
63 }
64
65 /**
66  * Implements hook_entity_operation().
67  */
68 function content_moderation_entity_operation(EntityInterface $entity) {
69   return \Drupal::service('class_resolver')
70     ->getInstanceFromDefinition(EntityTypeInfo::class)
71     ->entityOperation($entity);
72 }
73
74 /**
75  * Implements hook_entity_presave().
76  */
77 function content_moderation_entity_presave(EntityInterface $entity) {
78   return \Drupal::service('class_resolver')
79     ->getInstanceFromDefinition(EntityOperations::class)
80     ->entityPresave($entity);
81 }
82
83 /**
84  * Implements hook_entity_insert().
85  */
86 function content_moderation_entity_insert(EntityInterface $entity) {
87   return \Drupal::service('class_resolver')
88     ->getInstanceFromDefinition(EntityOperations::class)
89     ->entityInsert($entity);
90 }
91
92 /**
93  * Implements hook_entity_update().
94  */
95 function content_moderation_entity_update(EntityInterface $entity) {
96   return \Drupal::service('class_resolver')
97     ->getInstanceFromDefinition(EntityOperations::class)
98     ->entityUpdate($entity);
99 }
100
101 /**
102  * Implements hook_form_alter().
103  */
104 function content_moderation_form_alter(&$form, FormStateInterface $form_state, $form_id) {
105   \Drupal::service('class_resolver')
106     ->getInstanceFromDefinition(EntityTypeInfo::class)
107     ->formAlter($form, $form_state, $form_id);
108 }
109
110 /**
111  * Implements hook_preprocess_HOOK().
112  *
113  * Many default node templates rely on $page to determine whether to output the
114  * node title as part of the node content.
115  */
116 function content_moderation_preprocess_node(&$variables) {
117   \Drupal::service('class_resolver')
118     ->getInstanceFromDefinition(ContentPreprocess::class)
119     ->preprocessNode($variables);
120 }
121
122 /**
123  * Implements hook_entity_extra_field_info().
124  */
125 function content_moderation_entity_extra_field_info() {
126   return \Drupal::service('class_resolver')
127     ->getInstanceFromDefinition(EntityTypeInfo::class)
128     ->entityExtraFieldInfo();
129 }
130
131 /**
132  * Implements hook_entity_view().
133  */
134 function content_moderation_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
135   \Drupal::service('class_resolver')
136     ->getInstanceFromDefinition(EntityOperations::class)
137     ->entityView($build, $entity, $display, $view_mode);
138 }
139
140 /**
141  * Implements hook_node_access().
142  *
143  * Nodes in particular should be viewable if unpublished and the user has
144  * the appropriate permission. This permission is therefore effectively
145  * mandatory for any user that wants to moderate things.
146  */
147 function content_moderation_node_access(NodeInterface $node, $operation, AccountInterface $account) {
148   /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
149   $moderation_info = Drupal::service('content_moderation.moderation_information');
150
151   $access_result = NULL;
152   if ($operation === 'view') {
153     $access_result = (!$node->isPublished())
154       ? AccessResult::allowedIfHasPermission($account, 'view any unpublished content')
155       : AccessResult::neutral();
156
157     $access_result->addCacheableDependency($node);
158   }
159   elseif ($operation === 'update' && $moderation_info->isModeratedEntity($node) && $node->moderation_state) {
160     /** @var \Drupal\content_moderation\StateTransitionValidation $transition_validation */
161     $transition_validation = \Drupal::service('content_moderation.state_transition_validation');
162
163     $valid_transition_targets = $transition_validation->getValidTransitions($node, $account);
164     $access_result = $valid_transition_targets ? AccessResult::neutral() : AccessResult::forbidden();
165
166     $access_result->addCacheableDependency($node);
167     $access_result->addCacheableDependency($account);
168     $workflow = \Drupal::service('content_moderation.moderation_information')->getWorkflowForEntity($node);
169     $access_result->addCacheableDependency($workflow);
170     foreach ($valid_transition_targets as $valid_transition_target) {
171       $access_result->addCacheableDependency($valid_transition_target);
172     }
173   }
174
175   return $access_result;
176 }
177
178 /**
179  * Implements hook_theme().
180  */
181 function content_moderation_theme() {
182   return ['entity_moderation_form' => ['render element' => 'form']];
183 }
184
185 /**
186  * Implements hook_action_info_alter().
187  */
188 function content_moderation_action_info_alter(&$definitions) {
189
190   // The publish/unpublish actions are not valid on moderated entities. So swap
191   // their implementations out for alternates that will become a no-op on a
192   // moderated node. If another module has already swapped out those classes,
193   // though, we'll be polite and do nothing.
194   if (isset($definitions['node_publish_action']['class']) && $definitions['node_publish_action']['class'] == PublishNode::class) {
195     $definitions['node_publish_action']['class'] = ModerationOptOutPublishNode::class;
196   }
197   if (isset($definitions['node_unpublish_action']['class']) && $definitions['node_unpublish_action']['class'] == UnpublishNode::class) {
198     $definitions['node_unpublish_action']['class'] = ModerationOptOutUnpublishNode::class;
199   }
200 }
201
202 /**
203  * Implements hook_entity_bundle_info_alter().
204  */
205 function content_moderation_entity_bundle_info_alter(&$bundles) {
206   /** @var \Drupal\workflows\WorkflowInterface $workflow */
207   foreach (Workflow::loadMultipleByType('content_moderation') as $workflow) {
208     /** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $plugin */
209     $plugin = $workflow->getTypePlugin();
210     foreach ($plugin->getEntityTypes() as $entity_type_id) {
211       foreach ($plugin->getBundlesForEntityType($entity_type_id) as $bundle_id) {
212         if (isset($bundles[$entity_type_id][$bundle_id])) {
213           $bundles[$entity_type_id][$bundle_id]['workflow'] = $workflow->id();
214         }
215       }
216     }
217   }
218 }
219
220 /**
221  * Implements hook_ENTITY_TYPE_insert().
222  */
223 function content_moderation_workflow_insert(WorkflowInterface $entity) {
224   // Clear bundle cache so workflow gets added or removed from the bundle
225   // information.
226   \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
227   // Clear field cache so extra field is added or removed.
228   \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
229 }
230
231 /**
232  * Implements hook_ENTITY_TYPE_update().
233  */
234 function content_moderation_workflow_update(WorkflowInterface $entity) {
235   content_moderation_workflow_insert($entity);
236 }