Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
ContentEntityBase::hasTranslationChanges is not skipping the changed fields (changed and content_translation_changed), which is leading a wrong outcome if comparing e.g. a form submitted entity without any real changes, because without changes if we submit then the revision translation affected flag will be set because of the changed field but when comparing the revision there will be no visual diff and therefore the revision should not be listed as changed at all.
Proposed resolution
Exclude changed fields from the comparison in hasTranslationChanges.
Remaining tasks
Review.
User interface changes
none
API changes
none
Data model changes
none
Comment | File | Size | Author |
---|---|---|---|
#52 | interdiff-48-52.txt | 5.81 KB | jofitz |
#52 | interdiff-46-52.txt | 6.04 KB | jofitz |
#52 | 2615016-52.patch | 6.91 KB | jofitz |
#48 | interdiff-46-48.txt | 1.9 KB | jofitz |
#48 | 2615016-48.patch | 4.88 KB | jofitz |
Comments
Comment #2
hchonovProviding a failing test proving the needed change.
Comment #3
hchonovAnd now the test together with the fix from the issue summary.
Comment #5
hchonovComment #6
mkalkbrennerGood catch. The fix is correct and the patch adds test coverage.
Comment #8
hchonovActually there is more than this in this issue... The revision metadata fields should be skipped from checking as well, otherwise a new revision with updated revision timestamp or diffrent revision_uid will lead to returning a false result...
Comment #9
hchonovComment #10
hchonovComment #11
plachCan we name this
$skip_compare
I think this logic should be applied only between different revisions, can we have changes in revision metadata when updating the current revision instead of creating a new one? If so we should add test coverage for this change.
Surplus blank line
The first letter should be capitalized and the comment should have a trailing dot.
Comment #12
hchonov@plach:
you are right of course!
I've made the changes, added the test coverage and updated the issue summary as well.
Comment #13
plachLooks good now, only a typo to fix!
have been
Comment #14
hchonovthanks..
Comment #15
mkalkbrennerI wonder if we should also skip
changed
andcontent_translation_changed
, because these fields are usually set to a new value as a consequence of the return value ofhasTranslationChanges()
. InChangedItem::presave()
the call ofhasTranslationChanges()
is skipped if the value of this field (named changed or content_translation_changed) has been changed already.But if you think of any re-usage of
hasTranslationChanges()
somewhere else as part of the API, it makes sense to skip this field like the revision specific fields.Comment #16
mkalkbrennerJust a little bit of history:
hasTranslationChanges() was a part of ChangedItem unless @catch proposed to make it available as part of the API to be re-usable. I feel like we missed to explicit skip the "changed" field within in the isolated hasTranslationChanges() at this point. But I'm still thinking about this ...
Comment #17
mkalkbrennerThe second usage of this method in core is in
ContentEntityStorageBase::populateAffectedRevisionTranslations()
. In this case it would be wrong to set the revision_translation_affected flag is set if only the value of the changed field itself changed. Otherwise for nodes we will list to revisions of a translation with no changes except their changed date. And this date is not revertable.So I'm convinced that it's correct to skip "changed" and "content_translation_changed" as well.
Comment #18
hchonovComment #20
hchonovComment #22
xjmComment #24
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedCan we also remove the @todo now?
Comment #25
hchonovActually if we decide us for this approach then we'll have to put the three other field names in, which were introduced in #2666808: Add a revisionable entity base class and log interface. Probably we should wait for #2248983: Define the revision metadata base fields in the entity annotation in order for the storage to create them only in the revision table and then use the approach from there for determining the revision metadata fields and then we remove the @todo?
Comment #26
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedYep, waiting for the outcome of #2248983: Define the revision metadata base fields in the entity annotation in order for the storage to create them only in the revision table sounds wise :)
Comment #28
plachThis was discussed with @alexpott, @amateescu, @berdir, @cilefen, @fago and @xjm. We decided to defer triage because we were not sure about the steps to reproduce a significant problem. What issues does this cause? Can they be reproduced via the UI or only via some particular programmatic circumstances? It would be great if the IS provided more info about this.
(Sorry, it's been a long time since the last time I had a look to this code :)
Quick code review:
The core Entity API should know nothing about the Content Translation module: we are introducing a circular dependency here. We probably need a way to hook into this process and provide a list of fields.
Comment #29
tstoecklerMarking this as postponed on #2248983: Define the revision metadata base fields in the entity annotation in order for the storage to create them only in the revision table in the title, I still think we should continue iterating here in the meantime, so not setting the status to Postponed.
There is actually a separate bug that I have yet to open an issue for, that currently masks any real-world consequences as far as I can tell. This is still major to me, because it breaks a fundamental expectation when working with translations and revisions, but more on that later...
Comment #30
tstoecklerSo as far as I can tell #2808335: Changed time not updated when only non-translatable fields are changed on a translated entity masks this bug because revision metadata fields are not marked translatable, so that they are actually ignored for changed time tracking currently because of this. @hchonov please correct me if I am incorrect on this (or anything else).
Assuming that that other bug were fixed, what this bug would lead to is that as soon as someone enters a revision log message, the changed timestamp will be updated even though nothing of the actual entity was changed. This is annoying for any code that actually relies on proper changed time tracking, especially across revisions.
Having written that, however, I agree that this is probably not major.
I would like @hchonov to validate my assessment, though, before downgrading it.
Comment #31
tstoecklerComment #32
hchonovThis currently blocks #2808335: Changed time not updated when only non-translatable fields are changed on a translated entity.
Comment #33
hchonovtstoeckler, yes, as the revision metadata fields are not translatable the bug is currently being masked... But as other issues which are major are waiting on this issue I would not downgrade it.
Comment #34
hchonovThis issue has to be fixed in order for the autosave_form module to work properly and be able to check for changes between the intermediate and the initial entity.
Comment #35
Rajab Natshah CreditAttribution: Rajab Natshah at Vardot commented+1 Testing
Comment #37
Berdir#2808335: Changed time not updated when only non-translatable fields are changed on a translated entity is in, but the comment above said this blocks that, I guess it was meant the other way round?
Comment #38
mkalkbrennerComment #39
hchonov@berdir well both issues got somehow merged. So that the other one is now in and #2248983: Define the revision metadata base fields in the entity annotation in order for the storage to create them only in the revision table will make use of the new method for retrieving the revision metadata fields here we have only one more thing to do - exclude the changed fields from comparison in hasTranslationChanges.
Additionally we have here a test that is checking hasTranslationChanges skipping the revision metadata fields and the changed timestamps so we should get it in as well.
I've changed the patch a lot so an interdiff does not make sense anymore.
Comment #40
BerdirI find the way $field is used here a bit hard to understand. We conditionally assign it in the first if but there's nothing conditional about it, and get() would actually throw an exception in that case.
So, can you move the $field = $this->get($field_name) either outside the loop or not use a temporary variable for it? $this->get($field_name) instanceof .. would IMHO be perfectly fine here as well.
remove this.
Comment #41
hchonovI hope it looks better now :).
Comment #42
tstoecklerI think the patch itself looks fine, but I think we should add a code-comment to the added condition. Something like this, maybe?:
I have some minor notes as well, but marking needs work mostly for the adding of a comment. Doesn't have to be my comment, but something that explains why we are doing this would be rather important to add, IMO.
Missing "l" in hasTranslationChanges. Also maybe "Tests ContentEntityBase::hasTranslationChanges()." would be clearer?
Why are you using "node" here and not some test entity type?
h -> H (for the first h ;-))
Comment #43
jofitz CreditAttribution: jofitz at ComputerMinds commentedDocumentation changes from #42.
Comment #44
jofitz CreditAttribution: jofitz at ComputerMinds commentedReturning to "Needs Work" for someone to address:
I tried using
EntityTest::create()
in place ofNodeType::create()
, but couldn't get it to work.Comment #45
tstoecklerRight, sorry that was a bit brief. Here is a more in detail description of what is needed.
Replace
node
withentity_test
here.Replace
node
withentity_test_mulrev_changed
here.Remove this entirely.
Rename this variable and change the typehint to
\Drupal\entity_test\Entity\EntityTestMulRevChanged
Remove this.
Change
title
toname
here.Comment #46
Pavan B S CreditAttribution: Pavan B S at Valuebound commentedMade changes as suggested in comment #45. Applying the patch, please review.
Comment #48
jofitz CreditAttribution: jofitz at ComputerMinds commented@tstoeckler I now see why we are using node rather than a test entity type: The tests rely on 3 functions that only exist on nodes - setRevisionAuthorId(), setRevisionCreationTime() and setRevisionLogMessage(). Theoretically we could remove the calls to these functions, but I think this would weaken the test (I have uploaded this patch as a proof of concept). I recommend returning to my patch in #43 as the solution.
Comment #49
hchonovYou should not remove the calls, but instead use a test entity implementing the RevisionLogInterface e.g. entity_test_revlog, the methods will be provided then but some have different names.
Comment #50
tstoecklerWe already have
EntityTestWithRevisionLog
however that is not translatable and does not have a changed field, so I guess we need to create aEntityTestMulRevChangedWithRevisionLog
or something?Comment #51
hchonovEither we create a new entity type or we simply use the node entity type :).
A new entity type will make the patch unnecessary bigger?
Comment #52
jofitz CreditAttribution: jofitz at ComputerMinds commentedIMO the new entity type is quite straightforward.
Comment #53
tstoecklerWow, that looks pretty sweet @Jo Fitzgerald!!
Thanks also for the two interdiffs, that was very helpful!
Missing an "a" in "chnged". I wanted to mark this "needs work" for that, but I checked and I guess you intentionally left that out, because otherwise the ID would be longer than
EntityTypeInterface::ID_MAX_LENGTH
. That's pretty funny, to be honest. But I think you chose the best resolution for this, as the ID is still pretty readable and still fits with out existing naming pattern.....so, RTBC! Thanks again!
Comment #54
jofitz CreditAttribution: jofitz at ComputerMinds commentedAww, shucks, that comment has made my day. Thanks @tsoeckler!
With hindsight I should've mentioned the missing 'a' in my comment, but you read my mind anyway!
Comment #55
tstoecklerHehe, well you gave yourself away a bit, because it was a bit too suspicious that you made that same mistake multiple times in different places ;-)
Comment #56
hchonovWell you could remove the underscore between entity and test -> entitytest_mulrev_changed_revlog or simply entity_test_mul_changed_revlog.
Comment #57
alexpottCommitted and pushed 4dde9c8 to 8.4.x and eb5cb0f to 8.3.x. Thanks!
entity_test_mulrev_chnged_revlog
is a bit weird but shouldn't hold up this patch.