Problem/Motivation
Revisionable entities with custom layout override only track layout changes incidentally when an entity is saved via the entity's edit form. In order to provide full layout revisioning, we should check that the entity type's bundle is revisionable and whether or not a new revision should be created before calling entity save in the Section Storage object.
Proposed resolution
Section storage for the field should determine if the entity's bundle is RevisionableEntityBundleInterface and check shouldCreateNewRevision(). If this is "true" then a new revision should be set on the entity before saving.
Remaining tasks
Write a patch
Write tests
Convince people
User interface changes
No direct changes to the UI. Layout saves for entity override would prompt the creation of new revision which would display the entity's revision tab. That "ui change" is incidental and expected.
API changes
None
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #56 | 2937199-56.patch | 1.11 KB | johnwebdev |
| #50 | Screen Shot 2019-02-16 at 11.28.24.png | 42.43 KB | johnwebdev |
| #45 | 2937199-45.patch | 11.16 KB | tedbow |
| #45 | interdiff-43-45.txt | 856 bytes | tedbow |
| #43 | 2937199-43.patch | 11.1 KB | tedbow |
Comments
Comment #2
eclipsegc commentedSomething like this, but better probably.
Eclipse
Comment #3
kristen polThis wasn't marked for review but I tested it anyway in order to write out some instructions for testing. I did the following:
Comment #4
eclipsegc commentedSounds like a pretty good test to me. The patch needs to be "more correct" but how'd you feel about the functionality? Thoughts?
Eclipse
Comment #5
kristen pol@EclipseGc It worked exactly like I thought it would so it makes sense to me from a UX perspective. The patch code is readable and I understand you want to make it "more correct" but I don't know what would make it "more correct" ;)
Comment #6
tim.plunketthttps://www.drupal.org/node/2936357 recently changed the code for revisions
Comment #7
tim.plunkettComment #8
johnwebdev commentedComment #9
johnwebdev commentedHere's a rerolled patch from #2 taking the new API into consideration and changes done since. Still needs tests, and everything else required to complete this issue.
Comment #10
johnwebdev commentedComment #11
johnwebdev commentedLooking at this again, especially when working with inline blocks (https://www.drupal.org/project/drupal/issues/2957425 or serialized one). This one makes much sense.
If enforcing a new revision is bad, can we make some UX changes to allow for saving with a new revision?
Comment #12
andypostbundle could be not loadable entity
This code will fail for user entity
Comment #13
tedbow@var should use full space
Comment #14
tim.plunkettThere is no way this is reasonable for modules to need to do themselves. I think we need an issue like #2942907: Entity system does not provide an API for retrieving an entity variant that is safe for editing and an @todo here pointing to it.
Comment #16
tedbowAdded @todo pointing to https://www.drupal.org/project/drupal/issues/2942907
Comment #18
johnwebdev commentedSetting this back to NR because the fails in patch in earlier comment was due to #2973992: Permission issue in Nightwatch step marks all full testruns as unstable
Also retest the patch.
Comment #20
twfahey commentedPatch in #16 looks good to me. Test coverage makes sense and I think is comprehensive enough. All coding standards check out. Manual testing is working as expected - changes to the layout are triggering revisions, and reverting back to a previous revision is restoring the previous layout. Looks like everything is well commented and readable.
Comment #22
tedbowI am guessing this was random unrelated test failure: "Drupal\Tests\quickedit\FunctionalJavascript\FieldTest"
retesting
Comment #23
tedbowYep unrelated random test failure. Patch is passing again.
Comment #25
johnwebdev commentedNot sure why this is Needs work because 2937199-16.patch looks like it did pass.
Comment #26
tim.plunkettJust got a chance to review this, had a couple suggestions and questions.
This code is in an API method, why is it being testing with a BrowserTestBase?
$this->getEntity() is in here twice. Not a big deal, but it keeps jumping out at me :)
@see is invalid inline, and I think the comment could be rewritten:
These are bundles (node types), not nodes
Extra blank line
Nitpick, move this variable declaration down to where it's used
The reuse of
$revision_idfor both$revision_nodeand$no_revisions_nodeis confusing. Also, this could use some comments to explain why these two are differentWhat are we actually asserting here? The saveLayoutOverride helper doesn't do that much on it's own.
Comment #27
tedbow1. added
\Drupal\Tests\layout_builder\Unit\OverridesSectionStorageTest::testSave2. fixed
3. fixed
4. fixed
5. fixed
6. fixed.
7. used more descriptive variable names and added comments.
8. I wanted to make sure that the logic didn't assume the entity is revisionable.
i.e. call
saveLayoutOverride()checks the confirmation message which would not be the case if there was error. But maybe we don't need this now\Drupal\Tests\layout_builder\Unit\OverridesSectionStorageTest::testSaveis added.Comment #29
tim.plunkettThanks for all the changes! One more thought
This is really precise and complex bundle/revision logic that LB should have no knowledge of (hence the @todo).
Can this patch provide methods getNewRevisionDefault() and getBundleEntity(), and @see the implementations on \Drupal\Core\Entity\ContentEntityForm? I think it would be much clearer that way, and easier to clean up in the long run.
Also, nit: every line after the first in an @todo should be indented an additionally 2 spaces
Comment #30
tedbow@tim.plunkett good suggestion. Done
Comment #31
johnwebdev commentedThis could be a one-liner now.
Do we really need to mention these are copied from there?
Comment #32
tedbowWe have to use the entity 2 other times in the method so creating a local variable makes sense I think
Comment #33
johnwebdev commented#32.1 Yeah you're absolutely right, I totally over-looked that. Sorry :P
Comment #35
tim.plunkettI agree with @tedbow on both counts.
Can we keep an @todo here too?
Test failure is in InlineBlockPrivateFilesTest::testPrivateFiles, uh oh.
Comment #36
tim.plunkettComment #37
xjmComment #38
tim.plunkettComment #39
tedbowre #35 added back the todo.
uh oh indeed
for anyone interested see security problem documented here #2957425-239: Allow the inline creation of non-reusable Custom Blocks in the layout builder
Comment #40
tim.plunkettNow that the above issue was recommitted with the fix, this looks to be ready!
Comment #42
larowlannit: language is off here, think the word 'be' might be missing
we could return here and avoid the local $new_revision_default variable, with a return FALSE later
the two uses of `does` here make this confusing to read, can we improve it?
Suggest:
Ensure that saving a layout for a node where the bundle defaults to saving new revisions causes a new revision to be saved.
or something.
this comment needs work
do we need these?
am I the only one that things this test is highly contrived because of the amount of mocking required?
I've also asked some folks with better knowledge of revision api than I have to give it a review
Comment #43
tedbow@larowlan thanks for the review
1. fixed
2. Fixed
3. Suggestion looks good. Fixed
4. fixed
5. removed.
6. I don't it shows that ::save() respects the entity type for whether there is bundle available and the bundle for whether a new revision is the default.
Comment #45
tedbowThis is where #43 failed. The difference that makes it fail is not actually anything in this issue but rather #2983887: Add static cache to \Drupal\Core\Entity\ContentEntityStorageBase::getLatest*RevisionId()
Basically before we could call
$node_storage->getLatestRevisionId($revision_node->id())before and we would always get the latest revision.But now have to call
$node_storage->resetCache([$revision_node->id()]);to make sure we get that latest revision.I am actually wondering if #2983887: Add static cache to \Drupal\Core\Entity\ContentEntityStorageBase::getLatest*RevisionId() is actually a BC break because if a class gets the storage handler for an entity type\Drupal\Core\Entity\EntityTypeManager::getHandler()is going to give me an existing instance if another class has asked for the storage handler.UPDATE: Actually @berdir pointed out that
\Drupal\Core\Entity\EntityStorageBase::doPostSave()resets the cache and this is only a problem in tests.Comment #46
jibranIt is not a getter so the method should be named better.
Comment #47
tedbow@jibran
This function was added because of @tim.plunkett's comment in #29 and are copies of
\Drupal\Core\Entity\ContentEntityForm. They will be removed in #2942907: Entity system does not provide an API for retrieving an entity variant that is safe for editingComment #48
sam152 commentedFor what it's worth, the proposal in #3004536: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features subverts the need for this and also provides the same options content admins already have elsewhere: the option to create a new revision and the ability to write a revision log message.
Comment #49
tim.plunkettLet's postpone for now on that entityform approach.
Comment #50
johnwebdev commentedNow that #3004536: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features has been committed. We should be able to mark this issue as Closed.
Manual test:
1. Installed Drupal 8.7.x.
2. Installed Layout Builder and Enabled it and overrides on Basic page.
3. Created a new node.
4. Updated the Layout override and Saved. A new revision was made.
5. It's possible to revert back.
However, unticking "Create a new revision" in the Layout Builder UI still creates a new revision, which perhaps deserves its own issue?
The timestamp on the new revision is also incorrect.I created: #3033516: Revision log message field missing from layout overrides entity form.
Comment #51
phenaproximaI tried the steps in #50 with the very tip of 8.7.x HEAD. Unchecking the box and saving the layout did not create additional revisions. So I'm not sure this issue, or #3033516: Revision log message field missing from layout overrides entity form, is needed anymore...
Comment #52
phenaproximaI checked out 8.7.x to #2926508: Add DateTimeNormalizer+TimestampNormalizer, deprecate TimestampItemNormalizer: @DataType-level normalizers are reusable by JSON:API and gave this another quick manual test. I still couldn't reproduce a problem; if unchecked the "Create new revision" checkbox, no new revision was created. If I left the checkbox checked, then a new revision was created. That seems like expected behavior...
Comment #53
johnwebdev commentedMe and phenapromixa manually tested the concerns in #50 which seems to work as expected, which means we can now close this issue.
Comment #54
johnwebdev commentedComment #55
tim.plunkettThis issue has an @todo in code pointing to it, with what looks like a temporary workaround that was added in #2957425: Allow the inline creation of non-reusable Custom Blocks in the layout builder.
Reopening for more discussion, or to remove the @todo pointing here.
Comment #56
johnwebdev commentedCorrect Tim!
We always make a new revision on the inline blocks, regardless if the parent entity is saved with a new revision or not. Let's see what happens now when we update the code to do what was proposed in the todo.
Comment #57
sam152 commentedPatch looks great to me. I assume this is NW for tests? I imagine we'll need to test both the scenarios where new inline block revisions are created as a result of the parent creating a new revision and the opposite where a new revision is not created.
Comment #58
sam152 commentedHm, I've figured out a bunch of reasons why #56 would lead to rewriting the revision history of an item of content in #3053881: Reverting entity revisions that contain custom blocks erroneously triggers EntityChangedConstraint.
So I think this actually needs to go the opposite way, new revisions need to be enforced on all custom blocks that are modified as part of an entity save, regardless of the parent entity and tests should be introduced to enforce that.
Anything short of that will compromise the integrity of content revisions and essentially cause data loss.
Comment #59
sam152 commentedSince this issue has been repurposes a couple of times already, I've spun off #3053887: Add test coverage and document why inline blocks require a new revision to be created when modified, regardless of whether a new revision of the parent has been created to add appropriate test coverage which would have marked #56 as red.
Comment #60
sam152 commentedI think based on the consensus in #3053887: Add test coverage and document why inline blocks require a new revision to be created when modified, regardless of whether a new revision of the parent has been created, this is a won't-fix?
Comment #61
tim.plunkettThere's a link to this issue in the codebase, we need to remove that
Comment #62
bkosborneRe-opening per #61
Comment #63
bkosborneNevermind, the patch in #3053887: Add test coverage and document why inline blocks require a new revision to be created when modified, regardless of whether a new revision of the parent has been created will remove it.