Problem/Motivation
Images uploaded to an image field are lost when the node language is changed (because file usage incorrectly drops down to zero, causing the permanent file to be marked temporary, and then eventually being deleted — after 6 hours by default).
Same for files uploaded to a file field.
Steps to reproduce
- create a content type with an
imagefield - enable >=2 languages in site
- set the content translatable (tested setting the file translatable or not)
- create a node with an image and save it
- verify that the uploaded file has its
statuscolumn in thefile_managedtable set to1,
and it has 1 usage in thefile_usagetable - edit the node and change the language
-
- expected result: the uploaded file still has its
statuscolumn in thefile_managedtable set to1, and it has 1 usage in thefile_usagetable - actual result: the uploaded file now has its
statuscolumn in thefile_managedtable set to0, and it has 0 usage in thefile_usagetable
- expected result: the uploaded file still has its
- run cron 6 hours later (this is the default value of the
temporary_maximum_agesetting insystem.file) -
- expected result: the uploaded file is still present (because usage > 0)
- actual result: the uploaded file is absent (because usage = 0)
Note: see #40, where it is explained how this same problem is even reproducible on sites without content_translation are affected by this, merely having the language (Interface translation
) module installed and having >1 interface language enabled is sufficient to reproduce this problem for image/file fields on User entities!
Proposed resolution
Root cause analysis:
\Drupal\file\Plugin\Field\FieldType\FileFieldItemList::delete()removing file usages:
public function delete() { parent::delete(); $entity = $this->getEntity(); // If a translation is deleted only decrement the file usage by one. If the // default translation is deleted remove all file usages within this entity. $count = $entity->isDefaultTranslation() ? 0 : 1; foreach ($this->referencedEntities() as $file) { \Drupal::service('file.usage')->delete($file, 'file', $entity->getEntityTypeId(), $entity->id(), $count); } }- to delete file usages, it relies on
$entity->isDefaultTranslation()(i.e.\Drupal\Core\TypedData\TranslatableInterface::isDefaultTranslation()) to determine whether a translation is being deleted or not $entity->isDefaultTranslation()behaves incorrectly when the default translation is being changed
Therefore the solution is: fix \Drupal\Core\Entity\ContentEntityBase::isDefaultTranslation().
Remaining tasks
TBD
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #131 | 2810355-131.patch | 6.5 KB | benjifisher |
| #131 | interdiff-2810355-121-131.txt | 4.63 KB | benjifisher |
| #121 | interdiff_115_to_121-2810355-121.txt | 1.02 KB | joseph.olstad |
| #121 | 2810355-121.patch | 6.41 KB | joseph.olstad |
| #115 | 2810355-115-interdiff.txt | 976 bytes | berdir |
Comments
Comment #2
quironComment #3
quironComment #4
quironComment #5
catchThis looks like a duplicate of #2708411: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations. If it's not, please re-open but otherwise see you over there.
Comment #6
quironHi @catch,
its true that looks like a duplicated, but IMHO it isn't. I have been working on the patch accepted for this issue and is not solving nothing of this problem. This is why the issue you points to is about the editor code, not the field image types.
If you think its better I can post it to this issue but I think is so different and the code affected is not the same...
thanks.
Comment #7
quiron...so I change the status again
Comment #8
catchMoving to entity system and bumping to critical.
Comment #9
wluisi commentedHaving this same problem on a Drupal 8 site [ 8.1.10 ] Having this problem with image fields and file fields.
Here are my steps to reproduce:
Comment #10
wim leersIsn't this essentially the same problem as #2708411: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations? That'd not be surprising, because
editormodule essentially copied this logic fromfilemodule.Comment #11
alexpottRecently @catch, @webchick, @xjm, @cottser, @cilefen and I discussed #2801777: Give users the option to prevent drupal from automatically marking unused files as temporary , #2810355: $entity->isDefaultTranslation() behaves incorrectly when changing default translation, causing file/image field usage to be set to zero, causing files to be deleted and #2708411: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations. We all agreed that the current file usage behaviour that is resulting in files being deleted when they are being used by a site is unacceptable and a critical issue. We've created #2821423: Dealing with unexpected file deletion due to incorrect file usage to plan for how to fix and mitigate the related issues.
Comment #12
leksat commentedI'm investigating this issue currently. Going to check if existing tests will fail if we don't remove all usages, but only 1.
Comment #13
leksat commentedDid not know that unchecked "Display" checkbox makes the patch skip testing.
Comment #15
leksat commentedThe test fail seems to be correct.
FileFieldItemList need to delete all file usages within the entity if the entity is going to be completely deleted.
This happens here: https://github.com/drupal/drupal/blob/740ccca37f6f3255e8afcf3246ce8cd7de...
The only wrong thing here: it tried to guess if entity is going to be deleted completely by checking $entity->isDefaultTranslation(). But there is no other way.
In case of changing the original language, this check is false positive because ContentEntityStorageBase loads a translation from the original entity. This translation is marked as default one.
It happens here: https://github.com/drupal/drupal/blob/740ccca37f6f3255e8afcf3246ce8cd7de...
I still don't know how to fix this because both pieces of code try to make the right things.
One possibility to solve this would be to unmark default translation on the deleting translation, but there is no way to do that.
Comment #16
leksat commentedBTW, according to my tests, the bug happens only if file field is translatable (no matter which its properties are translatable).
Comment #17
leksat commentedLet's try that.
Comment #19
alexpott@Leksat have you seen #2821423: Dealing with unexpected file deletion due to incorrect file usage - i think we need to discuss the whole problem space - the counting of file usages seems very very brittle.
Comment #20
leksat commented@alexpott I agree that it would be nice to have a general solution for all the file usages problems, but
1) we have a production D8 project where we lose files and we need at least a temporary fix for that (disabling "Delete orphaned files" option completely does not seem the right choice)
2) my work can be useful in case if it would be decided to leave file usages and fix all bugs
So, sorry for disturbing people following this issue, but I need to test at least one more patch :)
Comment #21
leksat commentedOkay, #20 can be used as a temporary fix for this issue.
Comment #22
alexpott@Leksat are you able to write a test to prove the temporary fix works? Also I didn't mean to discourage you from working on this at all - just pointing to the overarching plan. If you can make progress here that is absolutely fantastic. These file deletions bugs are the most serious we have.
Comment #23
leksat commented@alexpott, okay :)
Wrote a small test for this issue.
But I still think that this is only temporary fix because the solution - a public $isDefaultTranslation property marked as @internal - is way too hackish. Yet I can't find anything better for now.
Comment #25
steinmb commentedAlso noticed this old issue from D7 #1716884: Changing the language of a piece of content deletes attached media that might be related?
Comment #26
jummonk commentedAlso occurs if you add an image (for instance with text on it in one language) in a node and an image with the same file name (with for instance the same text in another language on it) in the translation of that node. Once you have saved the node translation, the usage of the first file is set to 0, resulting in deletion by system_cron after a while.
Comment #27
leksat commentedComment #28
dawehnerIs there a reason we could not have an internal
setIsDefaultTranslation method?Comment #30
xjmI closed #2837023: Image fields that follow core gets broken when set to translatable as a duplicate. Report from that issue:
Comment #31
wim leersComment #32
mile23Patch in #23 applies to 8.3.x.
Re-running the patches from #23.
Comment #33
leksat commentedAdded setIsDefaultTranslation() method.
Comment #36
leksat commentedUpdated ContentEntityCloneTest to exclude isDefaultTranslation property.
Comment #37
alexpottI think there is a question if we want to have an API that can do both sides of this. I.e make a translation not the default translation and make a translation the default translation. Maybe introducing a
enforceNonDefaultTranslation()would be better. Because then we can document when it should be used which is never. Or maybe we should add a parameter to\Drupal\Core\Entity\ContentEntityBase::getTranslation()which indicates whether or not we should enforce non default translation-ness. I'm not sure. I'll ping the entity maintainers.Comment #38
wim leersI went through the entire issue.
The following comments were especially important:
Having looked at this again, I now disagree with my own analysis in #10. It's very similar to #2708411: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations, but it's not the same. The difference:
\Drupal\file\Plugin\Field\FieldType\FileFieldItemList::delete()removing file usages, and it relying on$entity->isDefaultTranslation()(i.e.\Drupal\Core\TypedData\TranslatableInterface::isDefaultTranslation()) to determine whether a translation is being deleted or not, and apparently$entity->isDefaultTranslation()behaves incorrectly in case the default translation is being changededitor.module's use ofhook_entity_insert()+hook_entity_update()+hook_entity_delete()+editor_entity_revision_delete()that were introduced in #1932652: Add image uploading to WYSIWYGs through editor.module. It looks like it should also usehook_entity_translation_insert()+hook_entity_translation_delete(), but those were added after #1932652 had already been started, in #1810370: Entity Translation API improvements.I think it's important we retitle this issue & update its summary for clarity. Did both of those. A change record seems necessary too, because there's likely other code in contrib/custom code that is relying on
isDefaultTranslation()— added the issue tag.Finally, a patch review.
This looks to me like it's a hacky temporary work-around rather than a complete solution. Because rather than tracking the new default translation, this is just an override that will only work if
ContentEntityStorageBase::invokeFieldMethod()happens to be called on behalf ofpostSave(). Which meansisDefaultTranslation()will still return the incorrect value when the entity is being updated in the future (when no translation is being removed, but the active lang code also won't beLanguageInterface::LANGCODE_DEFAULT?Rather than stating that this translation is no longer the default, this should be indicating which translation now is the default.
If that were the case, then we also wouldn't have to perform this if-test for every iteration of this loop.
@internalsetIsDefaultTranslation()method. @Leksat did this in #33. @alexpott is now saying in #37 that it should be possible to do both sides. But I'm not sure Alex' request makes sense entirely. It makes sense on the surface. But … there must be a default translation. And so making a translation not the default translation (which he suggests should becomeenforceNonDefaultTranslation()) implies that at the same time, another one must be made the default translation. Which effectively implies that you cannot ever callenforceNonDefaultTranslation()without also callingsetIsDefaultTranslation().This needs to be reviewed by an Entity/Field Translation API maintainer/expert. Tagging for that.
Comment #39
wim leersComment #40
wim leersThanks to @Juanswe at #2666700-28: User profile images unexpectedly deleted, I was able to close #2666700 as a duplicate of this with 100% certainty. Quoting my comment at #2666700-30: User profile images unexpectedly deleted:
This means that even sites without
content_translationare affected by this, merely having thelanguage() module installed and having >1 interface language enabled is sufficient to reproduce this problem for image/file fields onUserentities!That significantly increases the chances that somebody is running into this.
Comment #41
wim leersThis is now a blocker of #2708411 — see #2708411-54: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations.
Comment #43
amateescu commentedGiven #2708411-60: [PP-1] editor.module's editor_file_reference filter not tracking file usage correctly for translations, I'm downgrading this one to major as well.
Comment #44
suranga.gamage commentedSo looking at this further.
I think fixing the "isDefaultTranslation()" as in the patch is not the way to go.
Since in essence the entity the field is connected to is indeed the default translation even if its langcode changed.
Also note that at no point the actual "entity" is really changed in any other way. But the fields connected to the old language value have been stripped off and cloned to values in the new value (calling update and deleted respectively).
However it's possible to check the "status" of the new translation.
See \Drupal\Core\TypedData\TranslationStatusInterface which (in case of the updating of an existing translation would lack the needed flag and can be used in this context to differentiate between the changing of the language and the deleting of the parent entity).
Flow as I can see it.
- Entity is constructed. At this point the $activeLangcode property is set to LanguageInterface::LANGCODE_DEFAULT.
- The field update / delete callbacks are called.
- First the postSave will add an extra file usage instance to the table (which is correct, but overwritten in the next step).
- Then the delete callback will delete the entire file usage for this entity based on the isDefaultTranslation(). (as Mentioned)
The isDefaultTranslation() is a very simple function:
return $this->activeLangcode === LanguageInterface::LANGCODE_DEFAULT;It would seem this will indeed return true for every entity that hasn't explicitly loaded another language. Since the only way that I could find that changes this property from it's original value seems to be:
\Drupal\Core\Entity\ContentEntityBase::getTranslation()
Comment #45
suranga.gamage commentedNew version without a silly oversight in how the isDefaultTranslation is resolved.
However,
This still seems to trigger an error in the test that doesn't seem to be related to the patch directly.
Added a link to a more general revision issue.
Comment #48
jelle_sRerolled patch. test was removed it seems, so I didn't reroll that part.
Comment #49
loudpixels commentedHi there,
I'm getting a similar disappearing avatar issue/behavior in a drupal 7 install ... is there a patch or recommended solution for D7? Thank you!
Comment #50
joseph.olstadI suspected that a version of this patch might help our use case.
I'm helping develop/improve the Drupal 8 version of ETUF (entity_translation_unified_form (a VERY cool module, the D7 version is prestine, D8/9 version requires some polishing as I am about to describe:
ETUF module puts both English and French (our two active languages) field elements onto the same node edit form.
Image fields however are uploaded via ajax, the upload actually works fine with the ajax call, however, when saving the node, or somewhere along the line, the OTHER language image field doesn't get saved properly, the image (file managed) record remains a 'temporary' file and does not get stored as 'permanent'.
I'm looking to fix the root cause however I'm pretty close to the symptoms and have isolated this enough that I can probably create a workaround however I did try a couple of the above patches with no change in behavior, perhaps I need to take a closer look at the proposed api change here.
Comment #51
joseph.olstadTurned out not to make a difference in our case, sorry for the noise. I found a workaround for my use case, although it looks like some sort of a core bug, I fixed the symptom.
Please disregard my previous comment above.
Comment #53
joseph.olstadConfirmed file_usage is out of wack for sites with more than one language. I tried relying on file_usage data to do a cleanup, ended up deleting files that were still in use.
patch #48 still applies cleanly to 8.7.x, still applies to 8.8.x and 9.0.x
Something is wrong with the tests. The test needs updating or else there's some regression with the patch.
we have two languages on our site, and our file_usage table is incorrect, values that were flagged as unused are in fact still in use by the other language.
We would really appreciate this issue being fixed.
Comment #54
joseph.olstadComment #55
joseph.olstadthis is a data integrity issue
Comment #56
joseph.olstadnew patch comming shortly.
Comment #57
joseph.olstadRerolled 36, a test moved so I adjusted the patch, and there was a conflict that I resolved with another test, so I generate a new patch.
Comment #59
joseph.olstadreplaced deprecated calls with D9 api calls
Comment #61
joseph.olstadok one more try.
see interdiff and new patch.
Comment #62
joseph.olstadIt would be great if someone wants to take the glory and push this home, have to first see Wim Leers comments in #2810355-38: $entity->isDefaultTranslation() behaves incorrectly when changing default translation, causing file/image field usage to be set to zero, causing files to be deleted
Patch 61 (reroll of 36) has tests to prove worthiness.
Thanks!
Comment #63
catchComment #64
joseph.olstadAdding same patch as 61 with the tests only patch.
Comment #66
matsbla commentedComment #67
joseph.olstadThis core isDefaultTranslation() function is incorrectly labelled and incorrectly utilised in core, this patch essentially forces it to return false but would have to look closer just as to 'when' and "how often" it will return false.
imho, the isDefaultTranslation would be more appropriate as a method of the entity interface. It should only do processing if the core content_translation module is enabled.
With that said, isDefaultTranslation is used elsewhere in core, those calls would be done to a renamed function that is more accurate as to the meaning/context/use of it (to save us from the confusion).
A translation is relative, relative to what? relative to the configuration in use.
each entity can have a different language for a 'source' translation, however isDefaultTranslation assumes that the whole site works in the way defined in the language default without considering that source language can be any language on a site and entities can be differently configured (source language) from one entity to the next regardless of the global site language configuration. Ex, install Drupal with a language of your choice, add a second language, (take your pick) , an added language could be the source language for an entity as can the default language (so what is the default translation in this case? what does that mean?, the usecase does not seem to match.
inversely, a site can be installed with any language and any other language can be the 'added' language.
What is confusing here is the label of this function and its usage. The latest patch has a test that illustrates the problem but the solution is concerns that Wim Leers mentions brings up debate on how to approach this.
I think we need @plach to have a look at this.
Comment #68
hchonov@joseph.olstad, entity translations are part of the
content entity APIand do not requirecontent_translationto be enabled. TheisDefaultTranslation()method returnsTRUEfor the first translation of the entity - this is the translation language in which the entity was created initially and represent the default translation of the entity. Through the entity API it is possible to translate the entity in different languages using\Drupal\Core\TypedData\TranslatableInterface::addTranslation(). For this new translationsisDefaultTranslation()will returnFALSE. The method is not placed on theEntityInterface, but onTranslatableInterface, because we do not want to force someone directly implementing theEntityInterfaceto implement any methods related to translations. For example config entities do not implement this interface, but only content entities and theContentEntityIntefaceextends from theTranslatableInterface. I do not think that there are any reasons to change this architecture.I hope that this clarifies it for you? If not let us please discuss this in a dedicated issue as this even though related falls out of the scope of the current issue. Thank you.
Comment #69
joseph.olstad@hchonov ,
I was probably a bit sleepy when I wrote the previous post , some precisions:
a more appropriate name for this functionality (outside of the entity translation / (content translation)) referring to interface language or interface translation would be 'isDefaultInterfaceTranslation' or 'isDefaultInterfaceLanguage' . I recall programming in the 1980s on computers like the VIC 20 when there was only a few kilobytes of memory available, back then it was appropriate to write cryptic code with very short function names and variable names as every "byte" was preciously conserved however we no longer have such limitations and it is far more appropriate to write explicitly named function names and method names than the opposite.
of course we now have namespaces, but when debugging code revolving around concepts as complex and multi-facetted as translation it is easy to get confused so in this case I think a more explicit label for the function would be helpful for those writing the code and debugging it.
Although more importantly we need to permanently fix the bug that the above test illustrates.
I looked at the patch and while it does work and fixes the problem it is confusing looking at this code.
Translation is on several levels, interface strings, entities, current interface language, default language has more than one meaning (you could talk about site default interface language or get confused with the source language of an entity translation, a translation has more than one meaning, to debug the code you have to be aware of all of this and when the labels are not explicit it's easier to get confused).
Can you please take a close look at patch 64 and explain why it works? It is not that obvious.
Comment #70
joseph.olstadAs Wim Leer mentioned, this bug also affects installations not using content translation. Installations can be installed in languages other than English and only have one language but Drupal in this case still has to deal with string /interface label translations (not entity).
With that said, the fix has to work with installations that either do not use content translation and installations that use content translation.
Comment #71
hchonovThis is why it is always important to consider the context. When calling the function on an entity object it is pretty obvious that one is asking the entity object itself and not some other service. We cannot go and provide unique names for every possible method, nevertheless methods are also being reused. Please note that
\Drupal\Core\TypedData\TranslatableInterface::isTranslatable()is in the TypedData and not in the Entity namespace, which means that the same interface might be reused for translatable data in Drupal which does not represent entities. Having one interface and a one method make the code reusable.If you still think there is further discussion about this needed, then please open a new dedicated issue as this is clearly out-of-scope here.
Because of this change in the storage, which will flag the translation of the original entity object as non-default. The delete method is then called on every field of the original translation inclusive the File field delete method, which checks whether only a translation was removed or the whole entity was deleted. In
\Drupal\file\Plugin\Field\FieldType\FileFieldItemList::delete()it is assumed that if the entity is in the default translation, then the whole entity itself is being deleted and otherwise only a single translation.The test also works because when
\Drupal\file\Plugin\Field\FieldType\FileFieldItemList::postSave()is called it would have increased the count to 2. Then the delete method will be called on the original translation after flagging it as a non-default translation and then this will decrease the file usage by 1. Before flagging the removed translation as non-default this used to remove all the usages.-----
Looking at the patch and considering that in order to execute the delete method on the fields of a deleted translation we need to load the original entity and from there retrieve the deleted translation object on which fields to execute the delete method, I think that a better approach would be to make it possible for the storage to load a deleted translation from the current object instead. To cover the case where there is only one translation and its langcode is being changed, then when this happen we would have to create dynamically a new translation and flag the previous/original one as deleted. Then the field's
postSaveanddeletefield methods will behave correctly and we will invoke thepostSavemethod on the new default translation, which then will increase the usage by 1 for this new translation, but afterwards call thedeletemethod on the removed and already non-default translation and decrease the usage by 1. Basically this will result into the same what is already happening with the current patch, but in a more proper way.I do not like the approach of introducing another setter method to temporarily disable
isDefaultTranslation()from returningTRUE, instead it should be possible for::getTranslation($langcode)to load a removed translation from the entity object.getTranslationLanguages()should further not return the langcode for the removed translation(s).Comment #72
xjmYowza, this not good. I thought we'd fixed all these file deletion bugs already.
Since we should fix this bug in D8 too, I'm filing it against 8.8.x (which is the current bugfix support branch). Patches can be tested against other branches as needed when they're uploaded.
The issue will be automatically updated to 8.9.x after the last 8.8.x bugfix release. Thanks!
Comment #73
kkalaskar commentedRemoved Wrong comment, posted by mistake over here : -
Still this patch is under review. I tried to unset this error constraint.Comment #74
joseph.olstadComment #75
xjm@joseph.olstad, see #72. Thanks!
Comment #76
jungle> The issue will be automatically updated to 8.9.x after the last 8.8.x bugfix release. Thanks!
8.9.x? Tagging "Bug smash initiative" as well.
Comment #77
idebr commentedPosting the relevant error message here so it can be picked up by search engines.
This validation message is triggered when retroactively setting a language to existing media, and then adding a translation:
Comment #78
joseph.olstadPatch 64 still applies cleanly to 9.2.x, 9.1.x and 9.0.x, 8.9.x, 8.8.x, has tests.
If no one can find anything wrong with the patch then we should please move forward with it as running 'with' the fix is far better than running 'without' the fix.
Most people won't realize there's a problem until it's too late!!! Lets save everyone the trouble and get this in.
Comment #79
alexpottI think I agree with #71. I'm not convinced about the solution. I agree that adding the setter doesn't look great and it feels like there should be another way around this.
Here's a review of the actual patch.
I think this can be protected.
setters should return $this for a fluent interface.
If we going to add this to the interface then we should add scalar typehints. Ie.
public function setIsDefaultTranslation(bool $is_default_translation = NULL);Comment #80
joseph.olstad@alexpott thanks for the feedback, I don't fully understand why this patch works however I made your suggested changes (very good) and have provided an interdiff also.
Thanks!
Comment #81
joseph.olstadchecking patch
Comment #82
joseph.olstadNew patch , new interdiff
Comment #83
joseph.olstadComment #84
joseph.olstadAll green again with recommended changes from #79.
Has tests.
Let me know if there's more to be done here.
It's maybe not pretty but it gets the job done and has tests.
Comment #85
berdirI'm not so sure we're allowed to add a method to this interface under the BC rules. This is not a 1:1 interface, although core only uses it on content entities through TranslatableInterface in the entity namespace and then TranslatableRevisionableInterface.
It is definitely going to break a bunch of modules like simple_oauth, which has its own custom implementation of UserInterface extending ContentEntityInterface. I don't think that was a good idea, but hard to change now ;)
Do we have a pattern for doing this in a BC-way?
Putting it on a separate interface doesn't help too much in practice, as ContentEntityInterface would still have it, but it would technically fall under the 1:1 rule then.
Comment #86
joseph.olstadI'm not sure how BC would be broken here, we're defaulting is_default_translation to NULL and is only needed in the functions in the patch above (for now) but probably this will be helpful in the future as well.
IMHO, we have to be pragmatic about this.
While there's only 62 people subscribed to this issue there will inevitably be many more not realizing the problem and by then too late, it was actually pure hasard that I discovered this problem and sheer determination that I followed up here (during import/export scripting to/from Drupal8/9 to Drupal 8/9. ) by the time someone discovers this issue, they've probably lost their files (which was my case) aka were deleted inadvertantly as my script was relying on file usage but file usage came up 0, and then my script logic relied on this thinking it was safe to delete so I lost a bunch of files and didn't realize until later.
Comment #88
joseph.olstadI retriggered the test for 9.1.x, probably just a hiccup in the runner.
Comment #89
daffie commentedWe should use a "use" statement instead of
\Drupal\file\Entity\FileNitpick: change to
bool|nullCould we add some more documentation about this class variable. How and why is it different from the method with the same name. The how and why of this variable is not completely clear to me.
Comment #90
ravi.shankar commentedAddressed comment #89.1 and #89.2
NW for remaining points of #89.
Comment #91
ravi.shankar commentedFixed failed test of patch #90.
Comment #92
adityasingh commentedWorked on test case.
Still remaining #89.3, #89.4 and #89.5.
Comment #95
stephencamilo commentedComment #96
joseph.olstadThere was no reason given for closing this and it's still something worthwhile fixing.
Still remaining #89.3, #89.4 and #89.5.
Comment #97
joel_osc commentedFYI #3276540: Block (temporarily?) stephencamilo user for modifying many issues to be "won't fix"
Comment #99
xjmComment #100
ravi.shankar commentedFixed failed the test of patch #92. Still needs work for #96.
Comment #102
joseph.olstadGoing through important patches that we have been using for several years. It would be nice to get this one in.
All the nits from #89 are resolved by 100 and then 102
See interdiff.
Comment #103
joseph.olstadComment #104
joseph.olstadJustification for RTBC:
Comment #105
jungle> I agree with @berdir that adding this @internal method to the interface is a BC break. Please remove the method from the interface.
-- from #89.4
+1 to remove this.
Otherwise, RTBC +1
Comment #106
jungleAddressing #105
Comment #108
jungleTagging "Needs Review Queue Initiative" looks for review.
Comment #109
catchI don't think #106 addresses the bc break brought up by @berdir.
If we don't check an interface (or ContentEntityBase), we can't rely on it here - instead of an immediate fatal error because you haven't implemented the method, you'd get one when this code runs, which would be a slower but nastier surprise.
I think this probably needs a new interface, which ContentEntityBase then implements, and then we can check instanceof for that interface.
EntityTranslatableInterface maybe?
Then we could open a follow-up merge that back into TranslatableInterface for 11.0.0
Comment #110
berdirIf it's a temporary name then I'd go with ha specific name for the interface, like SettableDefaultTranslationInterface.
But then we also need to do a deprecation messsage if an entity doesn't implement that and the bug won't be fixed there for now.
And then in D11, we need to deprecate the interface and remove it in D12 :-/.
I do wonder if we can somehow find a different solution for this.
I think it would kind of make sense if invokeFieldMethod() would be on the entity, then it would be an internal property that we could handle without a new public method for one very specific use case. But moving that has exactly the same problem.
The other idea is that we're not actually deleting a translation, we're changing the langcode of an existing one. Feels weird to call delete at all in this scenario, but we're doing it to counter the equally "wrong" is-new-translation check in \Drupal\file\Plugin\Field\FieldType\FileFieldItemList::postSave(), so we add a usage and then remove one again, resulting in no change (with the patch).
I don't have a completely formed idea, but I'll try to think about it a bit more.
Comment #112
berdirWhat about just doing a method_exists() check on $entity for BC?
The problem that I see is that we can't detect this at runtime in a useful way. We could easily add an else to this logic and then trigger the deprecation message, but the chances of an actually working full implementation of ContentEnttityInterface that actually get saved are near-zero. The cases we're working with are edge cases that aren't really entities. Searching a bit, I couldn't find any examples even for that, I suspect simple_oauth has been refactored to no longer do that.
That means that if there really are such classes, they will never get in there, they are most likely also not actual entity types, so we have no easy mechanism to find them and trigger a deprecation message. We could maybe do something with phpstan or so, I checked if symfony/deubgclassloader has anything for that, but I think not.
So, method_exists(), deprecation message, and worst case is someone will get an easy to fix fatal error on D11 when they update.
Comment #113
berdirHere's a patch for that, also created a change record. I added a @trigger_error, while I don't expect this will ever be triggered, it also serves as a reminder to make that change in D11.
Comment #115
berdirFixing that test fail.
Comment #116
joseph.olstadThanks for that @Berdir .
This is a pretty important fix, I hope that the core maintainers prioritize this. The current behavior was a nasty surprise when I relied on file usage and ended up losing a lot of media due to the bug.
Comment #117
smustgrave commentedRemoving CR tag as I see that was added.
Can the needs subsystem maintainer tag be removed since @berdir, @catch, and @hchonov are all on this ticket?
Leaving in NR but can deprecation message be updated in #115 also please.
Comment #118
joseph.olstad@smustgrave, what part of the deprecation message needs changing?
In #115 it is as follows:
Comment #119
berdirThe version, it needs to say 10.2 instead of 9.4. We can't retroactively deprecate it, this will not be backported as it introduces a new method.
Comment #120
smustgrave commentedBrought up in #needs-review-queue-initative and can remove the needs subsystem maintainer tag.
Moving to NW for the trigger_error update so that doesn't get lost.
Comment #121
joseph.olstadOk thanks for clarifying, here's a new patch and interdiff.
This patch applies on 9.5.x, 10.0.x, 10.1.x, and 11.x
Comment #122
smustgrave commentedThanks!
Going to mark this and see if we can get into 10.2 soon!
Comment #126
xjmAdding credits from the triage discussion mentioned in #11 (nb: Cottser has a different username now).
Comment #127
anybodyThank you all very very much. We just ran into this in a Drupal 10.1.2 project and I was searching for hours until I found the solution here. Indeed critical and super nice to see this close to be fixed, thank you so much! Confirming that #121 fixed the issue for us!
I first thought it was #1452100: Private file download returns access denied, when file attached to revision other than current.
Would be really important to have this fixed in the next possible release.
Comment #129
lauriiiRandom fail in
Drupal\Tests\Component\Utility\RandomTest.Comment #130
alexpottI think this should be
protected bool|null $enforceDefaultTranslation = NULL;As this is consistent with $enforceRevisionTranslationAffected.
If we look at the methods above this I think this method naming is awkward...
The code above this is:
I also think we should not provide the default for the argument.
Should have a return type of :static and the @return needs documenting.
So I this should be
public function setDefaultTranslation(bool|null $is_default_translation): static {Comment #131
benjifisher@alexpott:
?boolinstead ofbool|null. I like the idea of naming the new propertyenforce....Enforced. Again, I think we should use?bool.I am also updating some of the comments.
Personally, I would use
but I will restrain myself since this is someone else's code.
Comment #132
smustgrave commentedSeems points in #130 have been addressed.
Comment #133
alexpottCommitted and pushed aa3ecb2c2d2 to 11.x and 4976f3a460f to 10.2.x. Thanks!
Comment #136
alexpottI created #3394676: Add setDefaultTranslationEnforced to \Drupal\Core\TypedData\TranslatableInterface so we remember to add the method to the interface.