diff --git a/core/modules/content_moderation/content_moderation.services.yml b/core/modules/content_moderation/content_moderation.services.yml index 5035b673c5720fa63cf023a92c22dd303cc02042..3d6055a9a7913fc94225f15a2e9a6d0bf376a376 100644 --- a/core/modules/content_moderation/content_moderation.services.yml +++ b/core/modules/content_moderation/content_moderation.services.yml @@ -1,7 +1,7 @@ services: paramconverter.latest_revision: class: Drupal\content_moderation\ParamConverter\EntityRevisionConverter - arguments: ['@entity.manager', '@content_moderation.moderation_information'] + parent: paramconverter.entity tags: - { name: paramconverter, priority: 5 } content_moderation.state_transition_validation: diff --git a/core/modules/content_moderation/src/Entity/Routing/EntityModerationRouteProvider.php b/core/modules/content_moderation/src/Entity/Routing/EntityModerationRouteProvider.php index f72a47ede7f62ffdf8b16673ec70d9433bd229eb..ac6af35d3b4ace6f6deb7a3689667c376e40df2f 100644 --- a/core/modules/content_moderation/src/Entity/Routing/EntityModerationRouteProvider.php +++ b/core/modules/content_moderation/src/Entity/Routing/EntityModerationRouteProvider.php @@ -88,7 +88,7 @@ protected function getLatestVersionRoute(EntityTypeInterface $entity_type) { ->setOption('parameters', [ $entity_type_id => [ 'type' => 'entity:' . $entity_type_id, - 'load_pending_revision' => 1, + 'load_latest_revision' => TRUE, ], ]); diff --git a/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php b/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php index 1503f9363e87e574a4dce04ce2b6f7aa1938fbce..effd0fe23ab4421efb776cca09620347b8f458be 100644 --- a/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php +++ b/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php @@ -2,104 +2,29 @@ namespace Drupal\content_moderation\ParamConverter; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\ParamConverter\EntityConverter; -use Drupal\Core\TypedData\TranslatableInterface; -use Drupal\content_moderation\ModerationInformationInterface; -use Symfony\Component\Routing\Route; /** * Defines a class for making sure the edit-route loads the current draft. + * + * @internal + * This class only exists to provide backwards compatibility with the + * load_pending_revision flag, the predecessor to load_latest_revision. The + * core entity converter now natively loads the latest revision of an entity + * when the load_latest_revision flag is present. This flag is also added + * automatically to all entity forms. */ class EntityRevisionConverter extends EntityConverter { - /** - * Moderation information service. - * - * @var \Drupal\content_moderation\ModerationInformationInterface - */ - protected $moderationInformation; - - /** - * EntityRevisionConverter constructor. - * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager, needed by the parent class. - * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info - * The moderation info utility service. - */ - public function __construct(EntityManagerInterface $entity_manager, ModerationInformationInterface $moderation_info) { - parent::__construct($entity_manager, \Drupal::languageManager()); - $this->moderationInformation = $moderation_info; - } - - /** - * {@inheritdoc} - */ - public function applies($definition, $name, Route $route) { - return $this->hasPendingRevisionFlag($definition) || $this->isEditFormPage($route); - } - - /** - * Determines if the route definition includes a pending revision flag. - * - * This is a custom flag defined by the Content Moderation module to load - * pending revisions rather than the default revision on a given route. - * - * @param array $definition - * The parameter definition provided in the route options. - * - * @return bool - * TRUE if the pending revision flag is set, FALSE otherwise. - */ - protected function hasPendingRevisionFlag(array $definition) { - return (isset($definition['load_pending_revision']) && $definition['load_pending_revision']); - } - - /** - * Determines if a given route is the edit-form for an entity. - * - * @param \Symfony\Component\Routing\Route $route - * The route definition. - * - * @return bool - * Returns TRUE if the route is the edit form of an entity, FALSE otherwise. - */ - protected function isEditFormPage(Route $route) { - if ($default = $route->getDefault('_entity_form')) { - // If no operation is provided, use 'default'. - $default .= '.default'; - list($entity_type_id, $operation) = explode('.', $default); - if (!$this->entityManager->hasDefinition($entity_type_id)) { - return FALSE; - } - $entity_type = $this->entityManager->getDefinition($entity_type_id); - return in_array($operation, ['default', 'edit']) && $entity_type && $entity_type->isRevisionable(); - } - } - /** * {@inheritdoc} */ public function convert($value, $definition, $name, array $defaults) { - $entity = parent::convert($value, $definition, $name, $defaults); - - if ($entity && $this->moderationInformation->isModeratedEntity($entity) && !$this->moderationInformation->isLatestRevision($entity)) { - $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults); - $latest_revision = $this->moderationInformation->getLatestRevision($entity_type_id, $value); - - if ($latest_revision instanceof EntityInterface) { - // If the entity type is translatable, ensure we return the proper - // translation object for the current context. - if ($entity instanceof TranslatableInterface) { - $latest_revision = $this->entityManager->getTranslationFromContext($latest_revision, NULL, ['operation' => 'entity_upcast']); - } - $entity = $latest_revision; - } + if (!empty($definition['load_pending_revision'])) { + @trigger_error('The load_pending_revision flag has been deprecated. You should use load_latest_revision instead.', E_USER_DEPRECATED); + $definition['load_latest_revision'] = TRUE; } - - return $entity; + return parent::convert($value, $definition, $name, $defaults); } } diff --git a/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php b/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php index 3deaffa98e48fc4bcad8bea734da5848751aacf3..69eee60d1a30065911dea88935d49ae59ef1e89f 100644 --- a/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php @@ -2,142 +2,63 @@ namespace Drupal\Tests\content_moderation\Kernel; -use Drupal\entity_test\Entity\EntityTest; -use Drupal\entity_test\Entity\EntityTestRev; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; -use Drupal\workflows\Entity\Workflow; /** * @coversDefaultClass \Drupal\content_moderation\ParamConverter\EntityRevisionConverter * @group content_moderation + * @group legacy */ class EntityRevisionConverterTest extends KernelTestBase { + /** + * {@inheritdoc} + */ public static $modules = [ 'user', - 'entity_test', 'system', 'content_moderation', 'node', 'workflows', ]; - /** - * The entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * The router without access checks. - * - * @var \Symfony\Component\Routing\RouterInterface - */ - protected $router; - /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); - - $this->installEntitySchema('entity_test'); - $this->installEntitySchema('entity_test_rev'); $this->installEntitySchema('node'); $this->installEntitySchema('user'); - $this->installEntitySchema('content_moderation_state'); - $this->installSchema('system', 'router'); $this->installSchema('system', 'sequences'); $this->installSchema('node', 'node_access'); - $this->installConfig(['content_moderation']); - \Drupal::service('router.builder')->rebuild(); - - $this->entityTypeManager = $this->container->get('entity_type.manager'); - $this->router = $this->container->get('router.no_access_checks'); } /** * @covers ::convert + * @expectedDeprecationMessage The load_pending_revision flag has been deprecated. You should use load_latest_revision instead. */ - public function testConvertNonRevisionableEntityType() { - $entity_test = EntityTest::create([ - 'name' => 'test', - ]); - - $entity_test->save(); - - $result = $this->router->match('/entity_test/' . $entity_test->id()); - - $this->assertInstanceOf(EntityTest::class, $result['entity_test']); - $this->assertEquals($entity_test->getRevisionId(), $result['entity_test']->getRevisionId()); - } - - /** - * @covers ::applies - */ - public function testConvertNoEditFormHandler() { - $workflow = Workflow::load('editorial'); - $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev'); - $workflow->save(); - - $entity_test_rev = EntityTestRev::create([ - 'name' => 'Default Revision', - 'moderation_state' => 'published', - ]); - $entity_test_rev->save(); - - $entity_test_rev->name = 'Pending revision'; - $entity_test_rev->moderation_state = 'draft'; - $entity_test_rev->save(); - - // Ensure the entity type does not provide an explicit 'edit' form class. - $definition = $this->entityTypeManager->getDefinition($entity_test_rev->getEntityTypeId()); - $this->assertNull($definition->getFormClass('edit')); - - // Ensure the revision converter is invoked for the edit route. - $result = $this->router->match("/entity_test_rev/manage/{$entity_test_rev->id()}/edit"); - $this->assertEquals($entity_test_rev->getRevisionId(), $result['entity_test_rev']->getRevisionId()); - } - - /** - * @covers ::convert - */ - public function testConvertWithRevisionableEntityType() { - $node_type = NodeType::create([ + public function testDeprecatedLoadPendingRevisionFlag() { + NodeType::create([ 'type' => 'article', - ]); - $node_type->save(); - $workflow = Workflow::load('editorial'); - $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'article'); - $workflow->save(); + ])->save(); - $revision_ids = []; $node = Node::create([ 'title' => 'test', 'type' => 'article', ]); - $node->moderation_state->value = 'published'; - $node->save(); - - $revision_ids[] = $node->getRevisionId(); - - $node->setNewRevision(TRUE); $node->save(); - $revision_ids[] = $node->getRevisionId(); + $node->isDefaultRevision(FALSE); $node->setNewRevision(TRUE); - $node->moderation_state->value = 'draft'; $node->save(); - $revision_ids[] = $node->getRevisionId(); - - $result = $this->router->match('/node/' . $node->id() . '/edit'); - $this->assertInstanceOf(Node::class, $result['node']); - $this->assertEquals($revision_ids[2], $result['node']->getRevisionId()); - $this->assertFalse($result['node']->isDefaultRevision()); + $converted = $this->container->get('paramconverter.latest_revision')->convert($node->id(), [ + 'load_pending_revision' => TRUE, + 'type' => 'entity:node', + ], 'node', []); + $this->assertEquals($converted->getLoadedRevisionId(), $node->getLoadedRevisionId()); } } diff --git a/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php b/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php index 32379c8c5d8f7db022bcaacdfe6c20400960e186..40d38867f0c8c1e400ca8b75879af33342f3bd18 100644 --- a/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php +++ b/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php @@ -2,6 +2,7 @@ namespace Drupal\KernelTests\Core\ParamConverter; +use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTestMulRev; use Drupal\KernelTests\KernelTestBase; use Drupal\language\Entity\ConfigurableLanguage; @@ -41,6 +42,7 @@ protected function setUp() { $this->installEntitySchema('user'); $this->installEntitySchema('entity_test_mulrev'); + $this->installEntitySchema('entity_test'); $this->installConfig(['system', 'language']); $this->converter = $this->container->get('paramconverter.entity'); @@ -168,4 +170,19 @@ public function testOptimizedConvert() { $this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId()); } + /** + * Test the latest revision flag and non-revisionable entities. + */ + public function testConvertNonRevisionableEntityType() { + $entity = EntityTest::create(); + $entity->save(); + + $converted = $this->converter->convert(1, [ + 'load_latest_revision' => TRUE, + 'type' => 'entity:entity_test', + ], 'foo', []); + + $this->assertEquals($entity->id(), $converted->id()); + } + } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php index 404c1f5afb4a4217c221173ee5f7f09139b55ed1..fdcb9e661a3ff3ef7209136739e8f6866d47dbbd 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php @@ -520,6 +520,22 @@ public function setLatestRevisionFlagTestCases() { ], ], ], + 'Entity form with no operation' => [ + [ + '_entity_form' => 'entity_test_rev' + ], + [ + 'entity_test_rev' => [ + 'type' => 'entity:entity_test_rev', + ], + ], + [ + 'entity_test_rev' => [ + 'type' => 'entity:entity_test_rev', + 'load_latest_revision' => TRUE, + ], + ], + ], 'Multiple entity parameters on an entity form' => [ [ '_entity_form' => 'entity_test_rev.edit'