summaryrefslogtreecommitdiffstats
path: root/core/modules/content_moderation/src/Entity
diff options
context:
space:
mode:
authorNathaniel Catchpole2016-08-08 12:26:31 (GMT)
committerNathaniel Catchpole2016-08-08 12:26:31 (GMT)
commitbc00f081e6b8e35e3b7ee57eb963b7e5b92593a2 (patch)
tree7b8ceb1d56763d8bccff57583b4318334d7dabcf /core/modules/content_moderation/src/Entity
parente1ef487b8d010367dc2325595a0c65d31cd71c7a (diff)
Issue #2725533 by timmillwood, alexpott, amateescu, webchick, dixon_, larowlan, dawehner, catch, Crell, Bojhan, jibran, Wim Leers, agentrickard, Berdir: Add experimental content_moderation module
Diffstat (limited to 'core/modules/content_moderation/src/Entity')
-rw-r--r--core/modules/content_moderation/src/Entity/ContentModerationState.php181
-rw-r--r--core/modules/content_moderation/src/Entity/Handler/BlockContentModerationHandler.php30
-rw-r--r--core/modules/content_moderation/src/Entity/Handler/ModerationHandler.php75
-rw-r--r--core/modules/content_moderation/src/Entity/Handler/ModerationHandlerInterface.php73
-rw-r--r--core/modules/content_moderation/src/Entity/Handler/NodeModerationHandler.php66
-rw-r--r--core/modules/content_moderation/src/Entity/ModerationState.php97
-rw-r--r--core/modules/content_moderation/src/Entity/ModerationStateTransition.php110
7 files changed, 632 insertions, 0 deletions
diff --git a/core/modules/content_moderation/src/Entity/ContentModerationState.php b/core/modules/content_moderation/src/Entity/ContentModerationState.php
new file mode 100644
index 0000000..1ff7f2a
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/ContentModerationState.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Drupal\content_moderation\Entity;
+
+use Drupal\content_moderation\ContentModerationStateInterface;
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\user\UserInterface;
+
+/**
+ * Defines the Content moderation state entity.
+ *
+ * @ContentEntityType(
+ * id = "content_moderation_state",
+ * label = @Translation("Content moderation state"),
+ * label_singular = @Translation("content moderation state"),
+ * label_plural = @Translation("content moderation states"),
+ * label_count = @PluralTranslation(
+ * singular = "@count content moderation state",
+ * plural = "@count content moderation states"
+ * ),
+ * handlers = {
+ * "storage_schema" = "Drupal\content_moderation\ContentModerationStateStorageSchema",
+ * "views_data" = "\Drupal\views\EntityViewsData",
+ * },
+ * base_table = "content_moderation_state",
+ * revision_table = "content_moderation_state_revision",
+ * data_table = "content_moderation_state_field_data",
+ * revision_data_table = "content_moderation_state_field_revision",
+ * translatable = TRUE,
+ * entity_keys = {
+ * "id" = "id",
+ * "revision" = "revision_id",
+ * "uuid" = "uuid",
+ * "uid" = "uid",
+ * "langcode" = "langcode",
+ * }
+ * )
+ */
+class ContentModerationState extends ContentEntityBase implements ContentModerationStateInterface {
+
+ use EntityChangedTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+ $fields = parent::baseFieldDefinitions($entity_type);
+
+ $fields['uid'] = BaseFieldDefinition::create('entity_reference')
+ ->setLabel(t('User'))
+ ->setDescription(t('The username of the entity creator.'))
+ ->setSetting('target_type', 'user')
+ ->setDefaultValueCallback('Drupal\content_moderation\Entity\ContentModerationState::getCurrentUserId')
+ ->setTranslatable(TRUE)
+ ->setRevisionable(TRUE);
+
+ $fields['moderation_state'] = BaseFieldDefinition::create('entity_reference')
+ ->setLabel(t('Moderation state'))
+ ->setDescription(t('The moderation state of the referenced content.'))
+ ->setSetting('target_type', 'moderation_state')
+ ->setRequired(TRUE)
+ ->setTranslatable(TRUE)
+ ->setRevisionable(TRUE)
+ ->addConstraint('ModerationState', []);
+
+ $fields['content_entity_type_id'] = BaseFieldDefinition::create('string')
+ ->setLabel(t('Content entity type ID'))
+ ->setDescription(t('The ID of the content entity type this moderation state is for.'))
+ ->setRequired(TRUE)
+ ->setRevisionable(TRUE);
+
+ $fields['content_entity_id'] = BaseFieldDefinition::create('integer')
+ ->setLabel(t('Content entity ID'))
+ ->setDescription(t('The ID of the content entity this moderation state is for.'))
+ ->setRequired(TRUE)
+ ->setRevisionable(TRUE);
+
+ // @todo https://www.drupal.org/node/2779931 Add constraint that enforces
+ // unique content_entity_type_id, content_entity_id and
+ // content_entity_revision_id.
+
+ $fields['content_entity_revision_id'] = BaseFieldDefinition::create('integer')
+ ->setLabel(t('Content entity revision ID'))
+ ->setDescription(t('The revision ID of the content entity this moderation state is for.'))
+ ->setRequired(TRUE)
+ ->setRevisionable(TRUE);
+
+ return $fields;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getOwner() {
+ return $this->get('uid')->entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getOwnerId() {
+ return $this->getEntityKey('uid');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setOwnerId($uid) {
+ $this->set('uid', $uid);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setOwner(UserInterface $account) {
+ $this->set('uid', $account->id());
+ return $this;
+ }
+
+ /**
+ * Creates or updates an entity's moderation state whilst saving that entity.
+ *
+ * @param \Drupal\content_moderation\Entity\ContentModerationState $content_moderation_state
+ * The content moderation entity content entity to create or save.
+ *
+ * @internal
+ * This method should only be called as a result of saving the related
+ * content entity.
+ */
+ public static function updateOrCreateFromEntity(ContentModerationState $content_moderation_state) {
+ $content_moderation_state->realSave();
+ }
+
+ /**
+ * Default value callback for the 'uid' base field definition.
+ *
+ * @see \Drupal\content_moderation\Entity\ContentModerationState::baseFieldDefinitions()
+ *
+ * @return array
+ * An array of default values.
+ */
+ public static function getCurrentUserId() {
+ return array(\Drupal::currentUser()->id());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save() {
+ $related_entity = \Drupal::entityTypeManager()
+ ->getStorage($this->content_entity_type_id->value)
+ ->loadRevision($this->content_entity_revision_id->value);
+ if ($related_entity instanceof TranslatableInterface) {
+ $related_entity = $related_entity->getTranslation($this->activeLangcode);
+ }
+ $related_entity->moderation_state->target_id = $this->moderation_state->target_id;
+ return $related_entity->save();
+ }
+
+ /**
+ * Saves an entity permanently.
+ *
+ * When saving existing entities, the entity is assumed to be complete,
+ * partial updates of entities are not supported.
+ *
+ * @return int
+ * Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ * In case of failures an exception is thrown.
+ */
+ protected function realSave() {
+ return parent::save();
+ }
+
+}
diff --git a/core/modules/content_moderation/src/Entity/Handler/BlockContentModerationHandler.php b/core/modules/content_moderation/src/Entity/Handler/BlockContentModerationHandler.php
new file mode 100644
index 0000000..b88b415
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/Handler/BlockContentModerationHandler.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\content_moderation\Entity\Handler;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Customizations for block content entities.
+ */
+class BlockContentModerationHandler extends ModerationHandler {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsEntityFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ $form['revision_information']['revision']['#default_value'] = TRUE;
+ $form['revision_information']['revision']['#disabled'] = TRUE;
+ $form['revision_information']['revision']['#description'] = $this->t('Revisions must be required when moderation is enabled.');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsBundleFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ $form['revision']['#default_value'] = 1;
+ $form['revision']['#disabled'] = TRUE;
+ $form['revision']['#description'] = $this->t('Revisions must be required when moderation is enabled.');
+ }
+
+}
diff --git a/core/modules/content_moderation/src/Entity/Handler/ModerationHandler.php b/core/modules/content_moderation/src/Entity/Handler/ModerationHandler.php
new file mode 100644
index 0000000..9d89253
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/Handler/ModerationHandler.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\content_moderation\Entity\Handler;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Common customizations for most/all entities.
+ *
+ * This class is intended primarily as a base class.
+ */
+class ModerationHandler implements ModerationHandlerInterface, EntityHandlerInterface {
+
+ use StringTranslationTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+ return new static();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onPresave(ContentEntityInterface $entity, $default_revision, $published_state) {
+ // This is probably not necessary if configuration is setup correctly.
+ $entity->setNewRevision(TRUE);
+ $entity->isDefaultRevision($default_revision);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onBundleModerationConfigurationFormSubmit(ConfigEntityInterface $bundle) {
+ // The Revisions portion of Entity API is not uniformly applied or
+ // consistent. Until that's fixed, we'll make a best-attempt to apply it to
+ // the common entity patterns so as to avoid every entity type needing to
+ // implement this method, although some will still need to do so for now.
+ // This is the API that should be universal, but isn't yet.
+ // @see \Drupal\node\Entity\NodeType
+ if (method_exists($bundle, 'setNewRevision')) {
+ $bundle->setNewRevision(TRUE);
+ }
+ // This is the raw property used by NodeType, and likely others.
+ elseif ($bundle->get('new_revision') !== NULL) {
+ $bundle->set('new_revision', TRUE);
+ }
+ // This is the raw property used by BlockContentType, and maybe others.
+ elseif ($bundle->get('revision') !== NULL) {
+ $bundle->set('revision', TRUE);
+ }
+
+ $bundle->save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsEntityFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsBundleFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ }
+
+}
diff --git a/core/modules/content_moderation/src/Entity/Handler/ModerationHandlerInterface.php b/core/modules/content_moderation/src/Entity/Handler/ModerationHandlerInterface.php
new file mode 100644
index 0000000..e897cf4
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/Handler/ModerationHandlerInterface.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\content_moderation\Entity\Handler;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Defines operations that need to vary by entity type.
+ *
+ * Much of the logic contained in this handler is an indication of flaws
+ * in the Entity API that are insufficiently standardized between entity types.
+ * Hopefully over time functionality can be removed from this interface.
+ */
+interface ModerationHandlerInterface {
+
+ /**
+ * Operates on moderatable content entities preSave().
+ *
+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+ * The entity to modify.
+ * @param bool $default_revision
+ * Whether the new revision should be made the default revision.
+ * @param bool $published_state
+ * Whether the state being transitioned to is a published state or not.
+ */
+ public function onPresave(ContentEntityInterface $entity, $default_revision, $published_state);
+
+ /**
+ * Operates on the bundle definition that has been marked as moderatable.
+ *
+ * Note: The values on the EntityModerationForm itself are already saved
+ * so do not need to be saved here. If any changes are made to the bundle
+ * object here it is this method's responsibility to call save() on it.
+ *
+ * The most common use case is to force revisions on for this bundle if
+ * moderation is enabled. That, sadly, does not have a common API in core.
+ *
+ * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle
+ * The bundle definition that is being saved.
+ */
+ public function onBundleModerationConfigurationFormSubmit(ConfigEntityInterface $bundle);
+
+ /**
+ * Alters entity forms to enforce revision handling.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ * @param string $form_id
+ * The form id.
+ *
+ * @see hook_form_alter()
+ */
+ public function enforceRevisionsEntityFormAlter(array &$form, FormStateInterface $form_state, $form_id);
+
+ /**
+ * Alters bundle forms to enforce revision handling.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ * @param string $form_id
+ * The form id.
+ *
+ * @see hook_form_alter()
+ */
+ public function enforceRevisionsBundleFormAlter(array &$form, FormStateInterface $form_state, $form_id);
+
+}
diff --git a/core/modules/content_moderation/src/Entity/Handler/NodeModerationHandler.php b/core/modules/content_moderation/src/Entity/Handler/NodeModerationHandler.php
new file mode 100644
index 0000000..83de187
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/Handler/NodeModerationHandler.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\content_moderation\Entity\Handler;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Customizations for node entities.
+ */
+class NodeModerationHandler extends ModerationHandler {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onPresave(ContentEntityInterface $entity, $default_revision, $published_state) {
+ if ($this->shouldModerate($entity, $published_state)) {
+ parent::onPresave($entity, $default_revision, $published_state);
+ // Only nodes have a concept of published.
+ /** @var \Drupal\node\NodeInterface $entity */
+ $entity->setPublished($published_state);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsEntityFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ $form['revision']['#disabled'] = TRUE;
+ $form['revision']['#default_value'] = TRUE;
+ $form['revision']['#description'] = $this->t('Revisions are required.');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enforceRevisionsBundleFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+ /* @var \Drupal\node\Entity\NodeType $entity */
+ $entity = $form_state->getFormObject()->getEntity();
+
+ if ($entity->getThirdPartySetting('content_moderation', 'enabled', FALSE)) {
+ // Force the revision checkbox on.
+ $form['workflow']['options']['#default_value']['revision'] = 'revision';
+ $form['workflow']['options']['revision']['#disabled'] = TRUE;
+ }
+ }
+
+ /**
+ * Check if an entity's default revision and/or state needs adjusting.
+ *
+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+ * The entity to check.
+ * @param bool $published_state
+ * Whether the state being transitioned to is a published state or not.
+ *
+ * @return bool
+ * TRUE when either the default revision or the state needs to be updated.
+ */
+ protected function shouldModerate(ContentEntityInterface $entity, $published_state) {
+ // @todo clarify the first condition.
+ // First condition is needed so you can add a translation.
+ // Second condition checks to see if the published status has changed.
+ return $entity->isDefaultTranslation() || $entity->isPublished() !== $published_state;
+ }
+
+}
diff --git a/core/modules/content_moderation/src/Entity/ModerationState.php b/core/modules/content_moderation/src/Entity/ModerationState.php
new file mode 100644
index 0000000..0522e7d
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/ModerationState.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\content_moderation\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\content_moderation\ModerationStateInterface;
+
+/**
+ * Defines the Moderation state entity.
+ *
+ * @ConfigEntityType(
+ * id = "moderation_state",
+ * label = @Translation("Moderation state"),
+ * handlers = {
+ * "access" = "Drupal\content_moderation\ModerationStateAccessControlHandler",
+ * "list_builder" = "Drupal\content_moderation\ModerationStateListBuilder",
+ * "form" = {
+ * "add" = "Drupal\content_moderation\Form\ModerationStateForm",
+ * "edit" = "Drupal\content_moderation\Form\ModerationStateForm",
+ * "delete" = "Drupal\content_moderation\Form\ModerationStateDeleteForm"
+ * },
+ * },
+ * config_prefix = "state",
+ * entity_keys = {
+ * "id" = "id",
+ * "label" = "label",
+ * "uuid" = "uuid",
+ * "weight" = "weight",
+ * },
+ * links = {
+ * "edit-form" = "/admin/config/workflow/moderation/states/{moderation_state}/edit",
+ * "delete-form" = "/admin/config/workflow/moderation/states/{moderation_state}/delete",
+ * "collection" = "/admin/config/workflow/moderation/states"
+ * },
+ * config_export = {
+ * "id",
+ * "label",
+ * "published",
+ * "default_revision",
+ * "weight",
+ * },
+ * )
+ */
+class ModerationState extends ConfigEntityBase implements ModerationStateInterface {
+
+ /**
+ * The Moderation state ID.
+ *
+ * @var string
+ */
+ protected $id;
+
+ /**
+ * The Moderation state label.
+ *
+ * @var string
+ */
+ protected $label;
+
+ /**
+ * Whether this state represents a published node.
+ *
+ * @var bool
+ */
+ protected $published;
+
+ /**
+ * Relative weight of this state.
+ *
+ * @var int
+ */
+ protected $weight;
+
+ /**
+ * Whether this state represents a default revision of the node.
+ *
+ * If this is a published state, then this property is ignored.
+ *
+ * @var bool
+ */
+ protected $default_revision;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isPublishedState() {
+ return $this->published;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isDefaultRevisionState() {
+ return $this->published || $this->default_revision;
+ }
+
+}
diff --git a/core/modules/content_moderation/src/Entity/ModerationStateTransition.php b/core/modules/content_moderation/src/Entity/ModerationStateTransition.php
new file mode 100644
index 0000000..99dbf93
--- /dev/null
+++ b/core/modules/content_moderation/src/Entity/ModerationStateTransition.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\content_moderation\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\content_moderation\ModerationStateTransitionInterface;
+
+/**
+ * Defines the Moderation state transition entity.
+ *
+ * @ConfigEntityType(
+ * id = "moderation_state_transition",
+ * label = @Translation("Moderation state transition"),
+ * handlers = {
+ * "list_builder" = "Drupal\content_moderation\ModerationStateTransitionListBuilder",
+ * "form" = {
+ * "add" = "Drupal\content_moderation\Form\ModerationStateTransitionForm",
+ * "edit" = "Drupal\content_moderation\Form\ModerationStateTransitionForm",
+ * "delete" = "Drupal\content_moderation\Form\ModerationStateTransitionDeleteForm"
+ * },
+ * },
+ * config_prefix = "state_transition",
+ * admin_permission = "administer moderation state transitions",
+ * entity_keys = {
+ * "id" = "id",
+ * "label" = "label",
+ * "uuid" = "uuid",
+ * "weight" = "weight"
+ * },
+ * links = {
+ * "edit-form" = "/admin/config/workflow/moderation/transitions/{moderation_state_transition}/edit",
+ * "delete-form" = "/admin/config/workflow/moderation/transitions/{moderation_state_transition}/delete",
+ * "collection" = "/admin/config/workflow/moderation/transitions"
+ * }
+ * )
+ */
+class ModerationStateTransition extends ConfigEntityBase implements ModerationStateTransitionInterface {
+
+ /**
+ * The Moderation state transition ID.
+ *
+ * @var string
+ */
+ protected $id;
+
+ /**
+ * The Moderation state transition label.
+ *
+ * @var string
+ */
+ protected $label;
+
+ /**
+ * ID of from state.
+ *
+ * @var string
+ */
+ protected $stateFrom;
+
+ /**
+ * ID of to state.
+ *
+ * @var string
+ */
+ protected $stateTo;
+
+ /**
+ * Relative weight of this transition.
+ *
+ * @var int
+ */
+ protected $weight;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function calculateDependencies() {
+ parent::calculateDependencies();
+
+ if ($this->stateFrom) {
+ $this->addDependency('config', ModerationState::load($this->stateFrom)->getConfigDependencyName());
+ }
+ if ($this->stateTo) {
+ $this->addDependency('config', ModerationState::load($this->stateTo)->getConfigDependencyName());
+ }
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFromState() {
+ return $this->stateFrom;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getToState() {
+ return $this->stateTo;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getWeight() {
+ return $this->weight;
+ }
+
+}