diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php index e69de29..6338746 100644 --- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php @@ -0,0 +1,406 @@ +installSchema('node', 'node_access'); + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test_with_bundle'); + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mulrevpub'); + $this->installEntitySchema('block_content'); + $this->installEntitySchema('content_moderation_state'); + $this->installConfig('content_moderation'); + + $this->entityTypeManager = $this->container->get('entity_type.manager'); + } + + /** + * Tests basic monolingual content moderation through the API. + * + * @dataProvider basicModerationTestCases + */ + public function testBasicModeration($entity_type_id) { + // Make the 'entity_test_with_bundle' entity type revisionable. + if ($entity_type_id == 'entity_test_with_bundle') { + $this->setEntityTestWithBundleKeys(['revision' => 'revision_id']); + } + + $entity_storage = $this->entityTypeManager->getStorage($entity_type_id); + $bundle_id = $entity_type_id; + $bundle_entity_type_id = $this->entityTypeManager->getDefinition($entity_type_id)->getBundleEntityType(); + if ($bundle_entity_type_id) { + $bundle_entity_type_definition = $this->entityTypeManager->getDefinition($bundle_entity_type_id); + $entity_type_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id); + + $entity_type = $entity_type_storage->create([ + $bundle_entity_type_definition->getKey('id') => 'example', + ]); + $entity_type->save(); + $bundle_id = $entity_type->id(); + } + + $workflow = Workflow::load('editorial'); + $workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id); + $workflow->save(); + + $entity = $entity_storage->create([ + 'title' => 'Test title', + $this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id, + ]); + $entity->save(); + $entity = $this->reloadEntity($entity); + $this->assertEquals('draft', $entity->moderation_state->value); + + $entity->moderation_state->value = 'published'; + $entity->save(); + + $entity = $this->reloadEntity($entity); + $this->assertEquals('published', $entity->moderation_state->value); + + // 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(); + + $entity = $this->reloadEntity($entity, 3); + $this->assertEquals('draft', $entity->moderation_state->value); + if ($entity instanceof EntityPublishedInterface) { + $this->assertFalse($entity->isPublished()); + } + + // Get the default revision. + $entity = $this->reloadEntity($entity); + if ($entity instanceof EntityPublishedInterface) { + $this->assertTrue((bool) $entity->isPublished()); + } + $this->assertEquals(2, $entity->getRevisionId()); + + $entity->moderation_state->value = 'published'; + $entity->save(); + + $entity = $this->reloadEntity($entity, 4); + $this->assertEquals('published', $entity->moderation_state->value); + + // Get the default revision. + $entity = $this->reloadEntity($entity); + if ($entity instanceof EntityPublishedInterface) { + $this->assertTrue((bool) $entity->isPublished()); + } + $this->assertEquals(4, $entity->getRevisionId()); + + // Update the node to archived which will then be the default revision. + $entity->moderation_state->value = 'archived'; + $entity->save(); + + // Revert to the previous (published) revision. + $previous_revision = $entity_storage->loadRevision(4); + $previous_revision->isDefaultRevision(TRUE); + $previous_revision->setNewRevision(TRUE); + $previous_revision->save(); + + // Get the default revision. + $entity = $this->reloadEntity($entity); + $this->assertEquals('published', $entity->moderation_state->value); + if ($entity instanceof EntityPublishedInterface) { + $this->assertTrue($entity->isPublished()); + } + } + + /** + * Test cases for basic moderation test. + */ + public function basicModerationTestCases() { + return [ + 'Nodes' => [ + 'node', + ], + 'Block content' => [ + 'block_content', + ], + 'Test Entity with Bundle' => [ + 'entity_test_with_bundle', + ], + 'Test entity - revisions, data table, and published interface' => [ + 'entity_test_mulrevpub', + ], + 'Entity Test with revisions' => [ + 'entity_test_rev', + ] + ]; + } + + /** + * Tests basic multilingual content moderation through the API. + */ + public function testMultilingualModeration() { + // Enable French. + ConfigurableLanguage::createFromLangcode('fr')->save(); + $node_type = NodeType::create([ + 'type' => 'example', + ]); + $node_type->save(); + + $workflow = Workflow::load('editorial'); + $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example'); + $workflow->save(); + + $english_node = Node::create([ + 'type' => 'example', + 'title' => 'Test title', + ]); + // Revision 1 (en). + $english_node + ->setUnpublished() + ->save(); + $this->assertEquals('draft', $english_node->moderation_state->value); + $this->assertFalse($english_node->isPublished()); + + // Create a French translation. + $french_node = $english_node->addTranslation('fr', ['title' => 'French title']); + $french_node->setUnpublished(); + // Revision 1 (fr). + $french_node->save(); + $french_node = $this->reloadEntity($english_node)->getTranslation('fr'); + $this->assertEquals('draft', $french_node->moderation_state->value); + $this->assertFalse($french_node->isPublished()); + + // Move English node to create another draft. + $english_node = $this->reloadEntity($english_node); + $english_node->moderation_state->value = 'draft'; + // Revision 2 (en, fr). + $english_node->save(); + $english_node = $this->reloadEntity($english_node); + $this->assertEquals('draft', $english_node->moderation_state->value); + + // French node should still be in draft. + $french_node = $this->reloadEntity($english_node)->getTranslation('fr'); + $this->assertEquals('draft', $french_node->moderation_state->value); + + // Publish the French node. + $french_node->moderation_state->value = 'published'; + // Revision 3 (en, fr). + $french_node->save(); + $french_node = $this->reloadEntity($french_node)->getTranslation('fr'); + $this->assertTrue($french_node->isPublished()); + $this->assertEquals('published', $french_node->moderation_state->value); + $this->assertTrue($french_node->isPublished()); + $english_node = $french_node->getTranslation('en'); + $this->assertEquals('draft', $english_node->moderation_state->value); + + // Publish the English node. + $english_node->moderation_state->value = 'published'; + // Revision 4 (en, fr). + $english_node->save(); + $english_node = $this->reloadEntity($english_node); + $this->assertTrue($english_node->isPublished()); + + // Move the French node back to draft. + $french_node = $this->reloadEntity($english_node)->getTranslation('fr'); + $this->assertTrue($french_node->isPublished()); + $french_node->moderation_state->value = 'draft'; + // Revision 5 (en, fr). + $french_node->save(); + $french_node = $this->reloadEntity($english_node, 5)->getTranslation('fr'); + $this->assertFalse($french_node->isPublished()); + $this->assertTrue($french_node->getTranslation('en')->isPublished()); + + // Republish the French node. + $french_node->moderation_state->value = 'published'; + // Revision 6 (en, fr). + $french_node->save(); + $french_node = $this->reloadEntity($english_node)->getTranslation('fr'); + $this->assertTrue($french_node->isPublished()); + + // Change the EN state without saving the node. + $content_moderation_state = ContentModerationState::load(1); + $content_moderation_state->set('moderation_state', 'draft'); + $content_moderation_state->setNewRevision(TRUE); + // Revision 7 (en, fr). + $content_moderation_state->save(); + $english_node = $this->reloadEntity($french_node, $french_node->getRevisionId() + 1); + + $this->assertEquals('draft', $english_node->moderation_state->value); + $french_node = $this->reloadEntity($english_node)->getTranslation('fr'); + $this->assertEquals('published', $french_node->moderation_state->value); + + // This should unpublish the French node. + $content_moderation_state = ContentModerationState::load(1); + $content_moderation_state = $content_moderation_state->getTranslation('fr'); + $content_moderation_state->set('moderation_state', 'draft'); + $content_moderation_state->setNewRevision(TRUE); + // Revision 8 (en, fr). + $content_moderation_state->save(); + + $english_node = $this->reloadEntity($english_node, $english_node->getRevisionId()); + $this->assertEquals('draft', $english_node->moderation_state->value); + $french_node = $this->reloadEntity($english_node, '8')->getTranslation('fr'); + $this->assertEquals('draft', $french_node->moderation_state->value); + // Switching the moderation state to an unpublished state should update the + // entity. + $this->assertFalse($french_node->isPublished()); + + // Get the default english node. + $english_node = $this->reloadEntity($english_node); + $this->assertTrue($english_node->isPublished()); + $this->assertEquals(6, $english_node->getRevisionId()); + } + + /** + * Tests that a non-translatable entity type with a langcode can be moderated. + */ + public function testNonTranslatableEntityTypeModeration() { + // Make the 'entity_test_with_bundle' entity type revisionable. + $this->setEntityTestWithBundleKeys(['revision' => 'revision_id']); + + // Create a test bundle. + $entity_test_bundle = EntityTestBundle::create([ + 'id' => 'example', + ]); + $entity_test_bundle->save(); + + $workflow = Workflow::load('editorial'); + $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_with_bundle', 'example'); + $workflow->save(); + + // Check that the tested entity type is not translatable. + $entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle'); + $this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.'); + + // Create a test entity. + $entity_test_with_bundle = EntityTestWithBundle::create([ + 'type' => 'example' + ]); + $entity_test_with_bundle->save(); + $this->assertEquals('draft', $entity_test_with_bundle->moderation_state->value); + + $entity_test_with_bundle->moderation_state->value = 'published'; + $entity_test_with_bundle->save(); + + $this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->value); + } + + /** + * Tests that a non-translatable entity type without a langcode can be + * moderated. + */ + public function testNonLangcodeEntityTypeModeration() { + // Make the 'entity_test_with_bundle' entity type revisionable and unset + // the langcode entity key. + $this->setEntityTestWithBundleKeys(['revision' => 'revision_id'], ['langcode']); + + // Create a test bundle. + $entity_test_bundle = EntityTestBundle::create([ + 'id' => 'example', + ]); + $entity_test_bundle->save(); + + $workflow = Workflow::load('editorial'); + $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_with_bundle', 'example'); + $workflow->save(); + + // Check that the tested entity type is not translatable. + $entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle'); + $this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.'); + + // Create a test entity. + $entity_test_with_bundle = EntityTestWithBundle::create([ + 'type' => 'example' + ]); + $entity_test_with_bundle->save(); + $this->assertEquals('draft', $entity_test_with_bundle->moderation_state->value); + + $entity_test_with_bundle->moderation_state->value = 'published'; + $entity_test_with_bundle->save(); + + $this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->value); + } + + /** + * Set the keys on the test entity type. + * + * @param array $keys + * The entity keys to override + * @param array $remove_keys + * Keys to remove. + */ + protected function setEntityTestWithBundleKeys($keys, $remove_keys = []) { + $entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle'); + $original_keys = $entity_type->getKeys(); + foreach ($remove_keys as $remove_key) { + unset($original_keys[$remove_key]); + } + $entity_type->set('entity_keys', $keys + $original_keys); + \Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type); + \Drupal::entityDefinitionUpdateManager()->applyUpdates(); + } + + /** + * Reloads the entity after clearing the static cache. + * + * @param \Drupal\Core\Entity\EntityInterface + * The entity to reload. + * @param int|bool $revision_id + * The specific revision ID to load. Defaults FALSE and just loads the + * default revision. + * + * @return \Drupal\Core\Entity\EntityInterface + * The reloaded entity. + */ + protected function reloadEntity(EntityInterface $entity, $revision_id = FALSE) { + $storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId()); + $storage->resetCache([$entity->id()]); + if ($revision_id) { + return $storage->loadRevision($revision_id); + } + return $storage->load($entity->id()); + } + +} diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 368e59f..cf1a63c 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -216,7 +216,7 @@ function entity_test_entity_bundle_info() { $bundles = array(); $entity_types = \Drupal::entityManager()->getDefinitions(); foreach ($entity_types as $entity_type_id => $entity_type) { - if ($entity_type->getProvider() == 'entity_test' && $entity_type_id != 'entity_test_with_bundle' && $entity_type_id != 'entity_test_publish_with_bundle') { + if ($entity_type->getProvider() == 'entity_test' && $entity_type_id != 'entity_test_with_bundle') { $bundles[$entity_type_id] = \Drupal::state()->get($entity_type_id . '.bundles') ?: array($entity_type_id => array('label' => 'Entity Test Bundle')); } } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php index e414c17..4f8434e 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php @@ -2,56 +2,55 @@ namespace Drupal\entity_test\Entity; -use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\EntityPublishedTrait; use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Field\BaseFieldDefinition; /** - * Defines the Publishable test entity with bundle entity class. + * Defines the test entity class. * * @ContentEntityType( - * id = "entity_test_publish_with_bundle", - * label = @Translation("Publishable test entity with bundle"), + * id = "entity_test_mulrevpub", + * label = @Translation("Test entity - revisions, data table, and published interface"), * handlers = { - * "list_builder" = "Drupal\entity_test\EntityTestListBuilder", * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", * "form" = { - * "default" = "\Drupal\Core\Entity\ContentEntityForm", - * "delete" = "\Drupal\Core\Entity\EntityDeleteForm" + * "default" = "Drupal\entity_test\EntityTestForm", + * "delete" = "Drupal\entity_test\EntityTestDeleteForm" * }, + * "translation" = "Drupal\content_translation\ContentTranslationHandler", + * "views_data" = "Drupal\views\EntityViewsData", * "route_provider" = { * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", * }, * }, - * base_table = "entity_test_publish_with_bundle", - * data_table = "entity_test_publish_with_bundle_field_data", - * revision_table = "entity_test_publish_with_bundle_revision", - * revision_data_table = "entity_test_publish_with_bundle_field_revision", - * admin_permission = "administer entity_test_publish_with_bundle content", - * persistent_cache = FALSE, + * base_table = "entity_test_mulrevpub", + * data_table = "entity_test_mulrevpub_property_data", + * revision_table = "entity_test_mulrevpub_revision", + * revision_data_table = "entity_test_mulrevpub_property_revision", + * admin_permission = "administer entity_test content", + * translatable = TRUE, + * show_revision_ui = TRUE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", * "bundle" = "type", + * "revision" = "revision_id", * "label" = "name", * "langcode" = "langcode", * "published" = "status", - * "revision" = "revision_id", * }, - * bundle_entity_type = "entity_test_publish_bundle", * links = { - * "canonical" = "/entity_test_publish_with_bundle/{entity_test_publish_with_bundle}", - * "add-page" = "/entity_test_publish_with_bundle/add", - * "add-form" = "/entity_test_publish_with_bundle/add/{entity_test_bundle}", - * "edit-form" = "/entity_test_publish_with_bundle/{entity_test_publish_with_bundle}/edit", - * "delete-form" = "/entity_test_publish_with_bundle/{entity_test_publish_with_bundle}/delete", - * }, + * "add-form" = "/entity_test_mulrevpub/add", + * "canonical" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}", + * "delete-form" = "/entity_test/delete/entity_test_mulrevpub/{entity_test_mulrevpub}", + * "edit-form" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}/edit", + * "revision" = "/entity_test_mulrevpub/{entity_test_mulrevpub}/revision/{entity_test_mulrevpub_revision}/view", + * } * ) */ -class EntityTestPublishWithBundle extends ContentEntityBase implements EntityPublishedInterface { +class EntityTestMulRevPub extends EntityTestMulRev implements EntityPublishedInterface { use EntityPublishedTrait; @@ -59,23 +58,7 @@ class EntityTestPublishWithBundle extends ContentEntityBase implements EntityPub * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields = parent::baseFieldDefinitions($entity_type) + EntityPublishedTrait::publishedBaseFieldDefinitions($entity_type); - - $fields['name'] = BaseFieldDefinition::create('string') - ->setLabel(t('Name')) - ->setDescription(t('The name of the test entity.')) - ->setTranslatable(TRUE) - ->setSetting('max_length', 32) - ->setDisplayOptions('view', [ - 'label' => 'hidden', - 'type' => 'string', - 'weight' => -5, - ]) - ->setDisplayOptions('form', [ - 'type' => 'string_textfield', - 'weight' => -5, - ]); - return $fields; + return parent::baseFieldDefinitions($entity_type) + EntityPublishedTrait::publishedBaseFieldDefinitions($entity_type); } }