Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / content_moderation / content_moderation.module
index 77d822be28ae6b49b284aa9f2387259f249a553c..5a7af0e2c9e2c2640dae1590bb8e58a23dc3fdd0 100644 (file)
@@ -13,12 +13,14 @@ use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublishNode;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\workflows\WorkflowInterface;
-use Drupal\node\NodeInterface;
 use Drupal\node\Plugin\Action\PublishNode;
 use Drupal\node\Plugin\Action\UnpublishNode;
 use Drupal\workflows\Entity\Workflow;
@@ -62,15 +64,6 @@ function content_moderation_entity_type_alter(array &$entity_types) {
     ->entityTypeAlter($entity_types);
 }
 
-/**
- * Implements hook_entity_operation().
- */
-function content_moderation_entity_operation(EntityInterface $entity) {
-  return \Drupal::service('class_resolver')
-    ->getInstanceFromDefinition(EntityTypeInfo::class)
-    ->entityOperation($entity);
-}
-
 /**
  * Implements hook_entity_presave().
  */
@@ -98,6 +91,33 @@ function content_moderation_entity_update(EntityInterface $entity) {
     ->entityUpdate($entity);
 }
 
+/**
+ * Implements hook_entity_delete().
+ */
+function content_moderation_entity_delete(EntityInterface $entity) {
+  return \Drupal::service('class_resolver')
+    ->getInstanceFromDefinition(EntityOperations::class)
+    ->entityDelete($entity);
+}
+
+/**
+ * Implements hook_entity_revision_delete().
+ */
+function content_moderation_entity_revision_delete(EntityInterface $entity) {
+  return \Drupal::service('class_resolver')
+    ->getInstanceFromDefinition(EntityOperations::class)
+    ->entityRevisionDelete($entity);
+}
+
+/**
+ * Implements hook_entity_translation_delete().
+ */
+function content_moderation_entity_translation_delete(EntityInterface $translation) {
+  return \Drupal::service('class_resolver')
+    ->getInstanceFromDefinition(EntityOperations::class)
+    ->entityTranslationDelete($translation);
+}
+
 /**
  * Implements hook_form_alter().
  */
@@ -109,9 +129,6 @@ function content_moderation_form_alter(&$form, FormStateInterface $form_state, $
 
 /**
  * Implements hook_preprocess_HOOK().
- *
- * Many default node templates rely on $page to determine whether to output the
- * node title as part of the node content.
  */
 function content_moderation_preprocess_node(&$variables) {
   \Drupal::service('class_resolver')
@@ -138,34 +155,34 @@ function content_moderation_entity_view(array &$build, EntityInterface $entity,
 }
 
 /**
- * Implements hook_node_access().
+ * Implements hook_entity_access().
  *
- * Nodes in particular should be viewable if unpublished and the user has
- * the appropriate permission. This permission is therefore effectively
- * mandatory for any user that wants to moderate things.
+ * Entities should be viewable if unpublished and the user has the appropriate
+ * permission. This permission is therefore effectively mandatory for any user
+ * that wants to moderate things.
  */
-function content_moderation_node_access(NodeInterface $node, $operation, AccountInterface $account) {
+function content_moderation_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
   /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
   $moderation_info = Drupal::service('content_moderation.moderation_information');
 
   $access_result = NULL;
   if ($operation === 'view') {
-    $access_result = (!$node->isPublished())
+    $access_result = (($entity instanceof EntityPublishedInterface) && !$entity->isPublished())
       ? AccessResult::allowedIfHasPermission($account, 'view any unpublished content')
       : AccessResult::neutral();
 
-    $access_result->addCacheableDependency($node);
+    $access_result->addCacheableDependency($entity);
   }
-  elseif ($operation === 'update' && $moderation_info->isModeratedEntity($node) && $node->moderation_state) {
+  elseif ($operation === 'update' && $moderation_info->isModeratedEntity($entity) && $entity->moderation_state) {
     /** @var \Drupal\content_moderation\StateTransitionValidation $transition_validation */
     $transition_validation = \Drupal::service('content_moderation.state_transition_validation');
 
-    $valid_transition_targets = $transition_validation->getValidTransitions($node, $account);
+    $valid_transition_targets = $transition_validation->getValidTransitions($entity, $account);
     $access_result = $valid_transition_targets ? AccessResult::neutral() : AccessResult::forbidden();
 
-    $access_result->addCacheableDependency($node);
+    $access_result->addCacheableDependency($entity);
     $access_result->addCacheableDependency($account);
-    $workflow = \Drupal::service('content_moderation.moderation_information')->getWorkflowForEntity($node);
+    $workflow = $moderation_info->getWorkflowForEntity($entity);
     $access_result->addCacheableDependency($workflow);
     foreach ($valid_transition_targets as $valid_transition_target) {
       $access_result->addCacheableDependency($valid_transition_target);
@@ -175,6 +192,27 @@ function content_moderation_node_access(NodeInterface $node, $operation, Account
   return $access_result;
 }
 
+/**
+ * Implements hook_entity_field_access().
+ */
+function content_moderation_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+  if ($items && $operation === 'edit') {
+    /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
+    $moderation_info = Drupal::service('content_moderation.moderation_information');
+
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($field_definition->getTargetEntityTypeId());
+
+    $entity = $items->getEntity();
+
+    // Deny edit access to the published field if the entity is being moderated.
+    if ($entity_type->hasKey('published') && $moderation_info->isModeratedEntity($entity) && $entity->moderation_state && $field_definition->getName() == $entity_type->getKey('published')) {
+      return AccessResult::forbidden();
+    }
+  }
+
+  return AccessResult::neutral();
+}
+
 /**
  * Implements hook_theme().
  */
@@ -217,6 +255,20 @@ function content_moderation_entity_bundle_info_alter(&$bundles) {
   }
 }
 
+/**
+ * Implements hook_entity_bundle_delete().
+ */
+function content_moderation_entity_bundle_delete($entity_type_id, $bundle_id) {
+  // Remove non-configuration based bundles from content moderation based
+  // workflows when they are removed.
+  foreach (Workflow::loadMultipleByType('content_moderation') as $workflow) {
+    if ($workflow->getTypePlugin()->appliesToEntityTypeAndBundle($entity_type_id, $bundle_id)) {
+      $workflow->getTypePlugin()->removeEntityTypeAndBundle($entity_type_id, $bundle_id);
+      $workflow->save();
+    }
+  }
+}
+
 /**
  * Implements hook_ENTITY_TYPE_insert().
  */
@@ -232,5 +284,19 @@ function content_moderation_workflow_insert(WorkflowInterface $entity) {
  * Implements hook_ENTITY_TYPE_update().
  */
 function content_moderation_workflow_update(WorkflowInterface $entity) {
-  content_moderation_workflow_insert($entity);
+  // Clear bundle cache so workflow gets added or removed from the bundle
+  // information.
+  \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
+  // Clear field cache so extra field is added or removed.
+  \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
+}
+
+/**
+ * Implements hook_rest_resource_alter().
+ */
+function content_moderation_rest_resource_alter(&$definitions) {
+  // ContentModerationState is an internal entity type. Therefore it should not
+  // be exposed via REST.
+  // @see \Drupal\content_moderation\ContentModerationStateAccessControlHandler
+  unset($definitions['entity:content_moderation_state']);
 }