/** * Gets the moderation state entity linked to a content entity revision. * * @return \Drupal\content_moderation\ModerationStateInterface|null * The moderation state configuration entity linked to a content entity * revision. */ protected function getModerationState() { $entity = $this->getEntity(); if (!\Drupal::service('content_moderation.moderation_information')->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle())) { return NULL; } if ($entity->id() && $entity->getRevisionId()) { $revisions = \Drupal::service('entity.query')->get('content_moderation_state')->condition('content_entity_type_id', $entity->getEntityTypeId())->condition('content_entity_id', $entity->id())->condition('content_entity_revision_id', $entity->getRevisionId())->allRevisions()->sort('revision_id', 'DESC')->execute(); if ($revision_to_load = key($revisions)) { /** @var \Drupal\content_moderation\ContentModerationStateInterface $content_moderation_state */ $content_moderation_state = \Drupal::entityTypeManager()->getStorage('content_moderation_state')->loadRevision($revision_to_load); // Return the correct translation. $langcode = $entity->language()->getId(); if (!$content_moderation_state->hasTranslation($langcode)) { $content_moderation_state->addTranslation($langcode); } if ($content_moderation_state->language()->getId() !== $langcode) { $content_moderation_state = $content_moderation_state->getTranslation($langcode); } return $content_moderation_state->get('moderation_state')->entity; } } // It is possible that the bundle does not exist at this point. For example, // the node type form creates a fake Node entity to get default values. // @see \Drupal\node\NodeTypeForm::form() $bundle_entity = \Drupal::entityTypeManager()->getStorage($entity->getEntityType()->getBundleEntityType())->load($entity->bundle()); if ($bundle_entity && ($default = $bundle_entity->getThirdPartySetting('content_moderation', 'default_moderation_state'))) { return ModerationState::load($default); } }
/** * Verify moderation state methods based on entity properties. * * @covers ::isPublishedState * @covers ::isDefaultRevisionState * * @dataProvider moderationStateProvider */ public function testModerationStateProperties($published, $default_revision, $is_published, $is_default) { $moderation_state_id = $this->randomMachineName(); $moderation_state = ModerationState::create(['id' => $moderation_state_id, 'label' => $this->randomString(), 'published' => $published, 'default_revision' => $default_revision]); $moderation_state->save(); $moderation_state = ModerationState::load($moderation_state_id); $this->assertEquals($is_published, $moderation_state->isPublishedState()); $this->assertEquals($is_default, $moderation_state->isDefaultRevisionState()); }
/** * {@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; }
/** * Returns an array of transition permissions. * * @return array * The transition permissions. */ public function transitionPermissions() { // @todo https://www.drupal.org/node/2779933 write a test for this. $perms = []; /* @var \Drupal\content_moderation\ModerationStateInterface[] $states */ $states = ModerationState::loadMultiple(); /* @var \Drupal\content_moderation\ModerationStateTransitionInterface $transition */ foreach (ModerationStateTransition::loadMultiple() as $id => $transition) { $perms['use ' . $id . ' transition'] = ['title' => $this->t('Use the %transition_name transition', ['%transition_name' => $transition->label()]), 'description' => $this->t('Move content from %from state to %to state.', ['%from' => $states[$transition->getFromState()]->label(), '%to' => $states[$transition->getToState()]->label()])]; } return $perms; }
/** * Tests content moderation third party schema for block content types. */ public function testContentModerationBlockContentTypeConfig() { $this->installEntitySchema('block_content'); $this->installEntitySchema('user'); $this->installConfig(['content_moderation']); $typed_config = \Drupal::service('config.typed'); $moderation_states = ModerationState::loadMultiple(); $block_content_type = BlockContentType::create(['id' => 'basic', 'label' => 'basic', 'revision' => TRUE]); $block_content_type->setThirdPartySetting('content_moderation', 'enabled', TRUE); $block_content_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', array_keys($moderation_states)); $block_content_type->setThirdPartySetting('content_moderation', 'default_moderation_state', ''); $block_content_type->save(); $this->assertConfigSchema($typed_config, $block_content_type->getEntityType()->getConfigPrefix() . '.' . $block_content_type->id(), $block_content_type->toArray()); }
/** * Tests basic monolingual content moderation through the API. */ public function testBasicModeration() { $node_type = NodeType::create(['type' => 'example']); $node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE); $node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']); $node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft'); $node_type->save(); $node = Node::create(['type' => 'example', 'title' => 'Test title']); $node->save(); $node = $this->reloadNode($node); $this->assertEquals('draft', $node->moderation_state->entity->id()); $published = ModerationState::load('published'); $node->moderation_state->entity = $published; $node->save(); $node = $this->reloadNode($node); $this->assertEquals('published', $node->moderation_state->entity->id()); // Change the state without saving the node. $content_moderation_state = ContentModerationState::load(1); $content_moderation_state->set('moderation_state', 'draft'); $content_moderation_state->setNewRevision(TRUE); $content_moderation_state->save(); $node = $this->reloadNode($node, 3); $this->assertEquals('draft', $node->moderation_state->entity->id()); $this->assertFalse($node->isPublished()); // Get the default revision. $node = $this->reloadNode($node); $this->assertTrue($node->isPublished()); $this->assertEquals(2, $node->getRevisionId()); $node->moderation_state->target_id = 'published'; $node->save(); $node = $this->reloadNode($node, 4); $this->assertEquals('published', $node->moderation_state->entity->id()); // Get the default revision. $node = $this->reloadNode($node); $this->assertTrue($node->isPublished()); $this->assertEquals(4, $node->getRevisionId()); }
/** * {@inheritdoc} */ public function validate($value, Constraint $constraint) { /** @var \Drupal\Core\Entity\EntityInterface $entity */ $entity = $value->getEntity(); // Ignore entities that are not subject to moderation anyway. if (!$this->moderationInformation->isModeratedEntity($entity)) { return; } // Ignore entities that are being created for the first time. if ($entity->isNew()) { return; } // Ignore entities that are being moderated for the first time, such as // when they existed before moderation was enabled for this entity type. if ($this->isFirstTimeModeration($entity)) { return; } $original_entity = $this->moderationInformation->getLatestRevision($entity->getEntityTypeId(), $entity->id()); if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) { $original_entity = $original_entity->getTranslation($entity->language()->getId()); } if ($entity->moderation_state->target_id) { $new_state_id = $entity->moderation_state->target_id; } else { $new_state_id = $default = $this->moderationInformation->loadBundleEntity($entity->getEntityType()->getBundleEntityType(), $entity->bundle())->getThirdPartySetting('content_moderation', 'default_moderation_state'); } if ($new_state_id) { $new_state = ModerationStateEntity::load($new_state_id); } // @todo - what if $new_state_id references something that does not exist or // is null. if (!$this->validation->isTransitionAllowed($original_entity->moderation_state->entity, $new_state)) { $this->context->addViolation($constraint->message, ['%from' => $original_entity->moderation_state->entity->label(), '%to' => $new_state->label()]); } }
/** * Verifies that an unpublished state may be made the default revision. */ public function testArchive() { $published_id = $this->randomMachineName(); $published_state = ModerationState::create(['id' => $published_id, 'label' => $this->randomString(), 'published' => TRUE, 'default_revision' => TRUE]); $published_state->save(); $archived_id = $this->randomMachineName(); $archived_state = ModerationState::create(['id' => $archived_id, 'label' => $this->randomString(), 'published' => FALSE, 'default_revision' => TRUE]); $archived_state->save(); $page = Node::create(['type' => 'page', 'title' => $this->randomString()]); $page->moderation_state->target_id = $published_id; $page->save(); $id = $page->id(); // The newly-created page should already be published. $page = Node::load($id); $this->assertTrue($page->isPublished()); // When the page is moderated to the archived state, then the latest // revision should be the default revision, and it should be unpublished. $page->moderation_state->target_id = $archived_id; $page->save(); $new_revision_id = $page->getRevisionId(); $storage = \Drupal::entityTypeManager()->getStorage('node'); $new_revision = $storage->loadRevision($new_revision_id); $this->assertFalse($new_revision->isPublished()); $this->assertTrue($new_revision->isDefaultRevision()); }
/** * Enable moderation for a specified content type, using the UI. * * @param string $content_type_id * Machine name. * @param string[] $allowed_states * Array of allowed state IDs. * @param string $default_state * Default state. */ protected function enableModerationThroughUi($content_type_id, array $allowed_states, $default_state) { $this->drupalGet('admin/structure/types'); $this->assertLinkByHref('admin/structure/types/manage/' . $content_type_id . '/moderation'); $this->drupalGet('admin/structure/types/manage/' . $content_type_id); $this->assertLinkByHref('admin/structure/types/manage/' . $content_type_id . '/moderation'); $this->drupalGet('admin/structure/types/manage/' . $content_type_id . '/moderation'); $this->assertFieldByName('enable_moderation_state'); $this->assertNoFieldChecked('edit-enable-moderation-state'); $edit['enable_moderation_state'] = 1; /** @var ModerationState $state */ foreach (ModerationState::loadMultiple() as $state) { $key = $state->isPublishedState() ? 'allowed_moderation_states_published[' . $state->id() . ']' : 'allowed_moderation_states_unpublished[' . $state->id() . ']'; $edit[$key] = in_array($state->id(), $allowed_states, TRUE) ? $state->id() : FALSE; } $edit['default_moderation_state'] = $default_state; $this->drupalPostForm(NULL, $edit, t('Save')); }