Problem/Motivation

Creating a duplicate of a content entity does not reset the default revision flag. That's not initially a problem as a new entity is enforced to be a default revision, but if you duplicate and then save an entity twice, extremely weird things happen because only the revision table is updated.

While this may sound like a weird thing to do, it is happening for us in context of paragraphs for example, where we had troubles when a paragraph was saved as non-default but not actually as a new revision.

The reason for that is that \Drupal\content_moderation\EntityTypeInfo::entityPrepareForm() switches the entity into a non-default revision (I don't really understand why, but that's a different topic), and then ERR ensures that all referenced paragraphs are in a non-default revision as well, then they are stored in form state. When you then duplicate a paragraph and change it at the same time, it does properly persist the changes. See #3198091: Changes of newly replicated Paragraphs are not saved. There is more going on there, but I think this change makes sense to do anyway.

Funny enough, we already "fixed" this in #2850022: Duplicating a non-default revision should produce a default revision for a newly created entity and we have a test for it: \Drupal\KernelTests\Core\Entity\EntityDuplicateTest::testDuplicateNonDefaultRevision. But we fixed it by checking isNew() in the isDefaultRevision() method, so the double save edge case is not covered yet.

Steps to reproduce

Proposed resolution

Remaining tasks

Extend the test by changing the saved entity again and resaving it.

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3220784

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Berdir created an issue. See original summary.

berdir’s picture

Status: Active » Needs review
Issue tags: -Needs tests
StatusFileSize
new1.86 KB
new2.65 KB

Oh, even weirder. The test already does save multiple times, but only tests with a base field. The bug that we see with paragraphs happens only with configurable fields, because dedicated table *reads* are different.

Drupal reads base fields also from the revision table too, and since that is updated, the test is happy. But if actually check the database, you can see that the base table has _not_ been updated in that test.

I made the test more explicit by checking the new/default revision flags explicitly and also doing an entity query to check the base table values.

berdir’s picture

I wonder if we can somehow detect if you attempt to save the default revision as not-the-default revision and throw an exception then or something because that is not a valid thing to do.

Status: Needs review » Needs work

The last submitted patch, 2: 3220784-default-revision-duplicate.patch, failed testing. View results

johnchque’s picture

Adding access check.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

triggering D10 tests

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

Tests pass D10

The tests do an excellent job showing the issue.

quietone’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -1138,7 +1138,11 @@ public function createDuplicate() {
+    // Explicitly mark the entity as new and default revision. A new entity is
+    // always the default revision, but that persists only until the entity
+    // is saved.

s/and/and the/

I am not following the second sentence. That a new entity, once saved is no longer the default revision? Then what is the default revision for a newly saved entity?

Let's get this testing on a supported version of Drupal.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

berdir’s picture

> I am not following the second sentence. That a new entity, once saved is no longer the default revision? Then what is the default revision for a newly saved entity?

That's the point, there is no default revision anymore the in the loaded entity object.

The override that it is the default revision is transient and goes away as soon as it's saved:

  public function isDefaultRevision($new_value = NULL) {
    $return = $this->isDefaultRevision;
    if (isset($new_value)) {
      $this->isDefaultRevision = (bool) $new_value;
    }
    // New entities should always ensure at least one default revision exists,
    // creating an entity without a default revision is an invalid state.
    return $this->isNew() || $return;
  }

So if it's unsaved, it's always default, but as soon as isNew() returns FALSE, it suddenly might no longer be, and if you save it again, you end up with an invalid state.

mathilde_dumond’s picture

Status: Needs work » Needs review
StatusFileSize
new2.76 KB
new952 bytes

I tried to rephrase the comment.

mathilde_dumond’s picture

oy sorry about the patch name, the .patch is not an interdiff

quietone’s picture

StatusFileSize
new2.67 KB

@Berdir asked me in #contribute to review the comment. I still found it hard to follow, and in some ways the changes made it harder. I chatted with Berdir and he kindly explained what was happening. I now 'get it' and think the original comment make sense.

I am re-uploading the patch in #5 and testing with 11.x.

Sorry for delaying this!

quietone’s picture

Status: Needs review » Reviewed & tested by the community

The patch I read and commented on in #12 was RTBC. That is the patch I re-uploaded. Since it is passing tests and there has been no changes to the patch, I am restoring the RTBC.

dww’s picture

Thanks for opening this, and fixing it!

However, #17's duplicate of #5 didn't fix the first (valid) point from #12. Leaving RTBC since it's a trivial comment nit. Interdiff also seems a bit confused, and is showing a bigger change than I actually made, which is just:

-    // Explicitly mark the entity as new and default revision. A new entity is
-    // always the default revision, but that persists only until the entity
+    // Explicitly mark the entity as new and the default revision. A new entity
+    // is always the default revision, but that persists only until the entity

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 19: 3220784-19.default-revision.patch, failed testing. View results

berdir’s picture

Status: Needs work » Needs review

> I wonder if we can somehow detect if you attempt to save the default revision as not-the-default revision and throw an exception then or something because that is not a valid thing to do.

good thinking, past-me! current-me discovered this again and opened an issue for it: #3499181: Disallow saving the current default revision as a non-default revision. This is blocking that now.

This was RTBC and somehow fell off the bandwagon, lets try to finalize it. Still applies, not sure why it was needs work, random fail maybe?

berdir’s picture

Status: Needs review » Reviewed & tested by the community

Merge request is green and identical to the patch that was RTBC.

Not sure if we want to keep the explicit test coverage with the direct query, as with the issue that this is blocking, saving would result in an exception instead, we're kind of testing that issue, not this.

  • catch committed e5a67b91 on 11.x
    Issue #3220784 by berdir, johnchque, mathilde_dumond, dww, quietone:...

  • catch committed 48f7f549 on 10.4.x
    Issue #3220784 by berdir, johnchque, mathilde_dumond, dww, quietone:...

  • catch committed 166ca820 on 10.5.x
    Issue #3220784 by berdir, johnchque, mathilde_dumond, dww, quietone:...

  • catch committed 886ee4e3 on 11.1.x
    Issue #3220784 by berdir, johnchque, mathilde_dumond, dww, quietone:...
catch’s picture

Version: 11.x-dev » 10.4.x-dev
Status: Reviewed & tested by the community » Fixed

Let's add the test coverage here and then consider modifying it in the other issue if we need to.

Committed/pushed to 11.x and cherry-picked to 11.1.x, 10.5.x, 10.4.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.