This is a follow-up in response to #1188388-104: Entity translation UI in core
Problem
The core image field widget allows to enter alt and title along while uploading an image file. In many cases a desirable behavior, when translating content with image fields, is keeping the images and just translate their textual metadata. Currently when creating a translation the original image is preserved, but subsequent changes, such as uploading a new image for a certain language or change the image order in case of a gallery, are not reflected in the translations.
Proposed resolution
Introduce a field column synchronization capability which, once enabled, lets the field define which columns should be "shared" across translations, and which ones are allowed to change per-language. The synchronization takes care of propagating any change in the field items order and in the values themselves to all the available translations. This functionality is provided through a generic API which in core is exploited by the file and image fields.
Remaining tasks
Port the functionality.add steps to testin #68(novice) add screenshot of before the patchin #69(novice) add screenshot of after the patchin #70(novice) review patch. Review Task doc: http://drupal.org/node/1488992done by @fago and @yched(novice) manually test the patch. Manually Test Task doc: http://drupal.org/node/1489010done in #72(novice) Improve patch coding standards and documentation http://drupal.org/node/1487976done in #39decide on what follow-ups to do- final? review to rtbc
User interface changes
A checkbox to enable this functionality is added in the field instance settings. See #110 and #120 (#69 and #70).
API changes
A small Field API addition has been performed to allow declaring which field columns must be synchronized across translations.
Comment | File | Size | Author |
---|---|---|---|
#120 | sync-s02-confirm-label_and_invisible-2013-02-16_2157.png | 122.45 KB | YesCT |
#120 | sync-s03-2013-02-16_2237.png | 119.84 KB | YesCT |
#117 | et-sync-1807692-117.interdiff.do_not_test.patch | 7.17 KB | plach |
#117 | et-sync-1807692-117.patch | 50.83 KB | plach |
#110 | et-sync-1807692-110.interdiff.do_not_test.patch | 40.16 KB | plach |
Comments
Comment #0.0
YesCT CreditAttribution: YesCT commentedUpdated issue summary. link did not go to comment 104, added direct link to it.
Comment #1
plachThis probably can be fully taken from D7, see http://drupalcode.org/project/entity_translation.git/blob/refs/heads/7.x... - http://drupalcode.org/project/entity_translation.git/blob/refs/heads/7.x...
Comment #2
klonosYes please! Alt and title are used in (views) slideshows as overlaying text and it makes *a lot* of sense to have these translated in order to display the overlayed text in the proper language. So, providing an easy way to translate these texts is key and should be in core.
Of course there is this case where the images included in the slideshow might already include text. These cases call for a separate image per language, but that is OT here and IMO an edge case that has an easy solution.
Comment #3
YesCT CreditAttribution: YesCT commentedThis one is tagged with feature freeze but has no patch yet. @plach, did you have something started, or should anyone jump in?
Comment #4
plachI'm planning to post a patch for this during the upcoming weekend. @bforchhammer will help us with code reviews and coding, if needed.
Comment #5
plachHere is a first draft. Tests missing.
Comment #6
plachtypo
Comment #7
bforchhammer CreditAttribution: bforchhammer commentedThis doesn't seem to work when there's existing data, because then the checkbox is replaced with textual information, e.g. "This field has data in existing content. [Enable translation]".
There should also be a validation function in case JavaScript is not available.
Missing @param.
- missing space: "haveno"
- add a "then" after the comma?
The "?:" looks rather strange ;-)
Comment #8
plachPerformed some code clean-up. Could not test it as head is broken atm.
I suppressed the state on the checbox for now. We can restore it once the migration code has been dumped.
Comment #9
bforchhammer CreditAttribution: bforchhammer commentedNote: the "Synchronize translations" checkbox would also be good to have in the configuration/operation column on the new settings page that is being added in #1810386: Create workflow to setup multilingual for entity types, bundles and fields.
Comment #10
plachYes, once we have the modal :)
Comment #11
plachOk, here is test coverage. This should be more or less ready to go, hopefully we will be able to remove those ugly request attributes once we figure out how to deal with entity translations properly in #1810370: Entity Translation API improvements.
Comment #12
plachAny feedback here?
Comment #13
plachrerolled
Comment #15
plach#13: et-sync-1807692-13.patch queued for re-testing.
Comment #16
YesCT CreditAttribution: YesCT commentedNeeds tests (or a if tests are in place, a test only patch to show the tests work with the bot).
Needs manual testing.
Needs screenshots.
Updating tags and issue summary.
Comment #16.0
YesCT CreditAttribution: YesCT commentedUpdated issue summary. corrected [#99999-99] filter use and added blockquotes on quotes
Comment #17
YesCT CreditAttribution: YesCT commentedUpdated issue summary.
Comment #18
plachI don't think this needs an accessibility review or manual testing: it's only exposing a checkbox at UI level. And we have test coverage for the implented functionality.
Comment #18.0
plachUpdated issue summary remaining tasks.
Comment #19
plachI updated the issue summary: I don't see a great value in copying/pasting generic portions of handbook pages in every issue summary. I'd say tt's better to keep it slim and focused.
Comment #19.0
plachUpdated issue summary.
Comment #20
YesCT CreditAttribution: YesCT commentedI agree that links to the contributor tasks do not need to go into the remaining tasks of every issue. Issues that have people participating, who are experienced already, know how to make a patch or do a review.
But for issues where we want to make it easy to bring people in, and those people may not know what to do, it will let new people come in. Since this issue seemed to need more feedback (it's about 9 days since #12), it's a way of helping people know what feedback is needed and how to give it.
Comment #21
plachSure, but some of the stuff that was in the issue summary previously was not relevant to this issue and IMO could mislead new contributors :)
Comment #22
YesCT CreditAttribution: YesCT commentedYeah, I see (looking at the revisions). :) This is cleaner.
Tests are in the patch. So that is done.
And because this is adding a new functionality, we don't need to provide a tests only patch. Putting test only patches with the fix-and-tests patch in the issue queue for the test bot, is just for when there is a bug the patch is fixing. (right?)
Comment #23
plachCorrect. A failing test-only patch indicates some missing code in core, but new features are expected to be missing :)
Comment #24
YesCT CreditAttribution: YesCT commentedThanks!
Oh, and I *think* that the accessibility tag is just that it is related to accessibility. In this case translating alt and title tags is pretty great for accessibility.
+needs accessibility review
is what is used to get a accessibility review.
I agree we don't need an accessibility review. :)
Comment #25
plachOk, cool.
Comment #26
Gábor HojtsyBringing on D8MI sprint board.
Comment #27
plach#13: et-sync-1807692-13.patch queued for re-testing.
Comment #28
yched CreditAttribution: yched commentedGood inline code comments in the function itself, but I'm having a hard time figuring out the big picture of what the function actually does overall. Could we expand on "Performs field column synchronization" in the func's phpdoc, that's a bit cryptic.
Also - I might vey well be wrong, but it seems the behavior will only fire on form submission ? Not on programmatic saves ?
28 days to next Drupal core point release.
Comment #29
mgiffordI wanted to just jump in here to question why this issue is seeking to synchronize alt & title text in images.
Generally with a screen reader it is best to just stick with the alt text. Screen readers may actually read both the alt text & title to the user and if they are identical it really doesn't help.
As stated here:
http://webaim.org/articles/gonewild/
Comment #30
plachI think there's a misunderstanding here: this issue is about synchronizing the image accross translations, while leaving the possibility to enter different alt and title per language. They still are totally independent from one another.
Comment #30.0
plachUpdated issue summary.
Comment #30.1
plachUpdated issue summary
Comment #31
plachHere is a reroll to match the BC decorator stuff and improving PHP docs as per @yched request:
I also updated the issue summary.
Comment #32
plachThe BC fix has now its own issue: #1880926: Fatal error when retrieving $entity->original on a BC entity. Go and RTBC it!
@yched:
Any other concern?
Comment #33
attiks CreditAttribution: attiks commentedThis looks great, only remark are 2 small typos
typo
typo, 'we'
Comment #34
plachThanks, typos fixed.
@yched:
Sorry, I forgot:
Nope, the synchronization happens on
field_attach_presave()
. If you are referring to the request attributes stuff, it's a temporary solution. I am planning to find a way to pass along the "active" translation, however for now this works also on programmatic saves if you properly fill-in the request attributes.Comment #35
plachRTBC anyone?
Comment #36
plachRerolled
Comment #37
YesCT CreditAttribution: YesCT commentedI'm testing this. but first had to reroll.
git apply --index et-sync-1807692-73.patch
error: patch failed: core/lib/Drupal/Core/Entity/EntityBCDecorator.php:75
error: core/lib/Drupal/Core/Entity/EntityBCDecorator.php: patch does not apply
there were no conflicts.
Comment #38
plachThe reroll looks good, it just removed the hunk that was necessary to make tests pass and that had been moved to #1880926: Fatal error when retrieving $entity->original on a BC entity, which has been committed.
Comment #39
YesCT CreditAttribution: YesCT commentedContains
http://drupal.org/coding-standards/docs#files
\Drupal
http://drupal.org/coding-standards/docs#namespaces
http://drupal.org/node/325974 does need some updating but I think the bit about no php doc on getInfo() and setUp() is right.
no problem here. I'm just wondering what the unshift is for.
no problem here, just a question. it's not reordering the fields in the entity, is it? is this changing the order the translations are done?
It gets the fid from the index (shifted delta) but stores it in the [$langcode][delta]
Or perhaps this is the key to the sync'ing. Without the reordering the syncing would not be needed. And this test is making sure, things are sync'd by the fid?
That sounds strange. The default language was the source language. Source means something different here?
was one of the files removed from the original language? ... Or is this checking the translation to see if the original value for the translation that was not done is dropped? (only 2 of the three were translated)
This is just translating the remaining file? (only 2 were translated before)
the langcode on the third image, that was added in the language (not the default language), has a langcode of the langcode? why didn't the other's that were translated get that langcode overridden too?
http://drupal.org/coding-standards/docs#hookimpl
Implements hook_form_FORM_ID_alter() for field_ui_field_edit_form().
does this php check each field setting to see if it should by sync'd? I think I'm confusing that a field can be checked to be sync'd and then there is also a setting to say what part of the field (columns?) gets sync'd. Oh, if it's set, it's set to the columns that should be sync'd! "This functionality is provided by defining a 'translation_sync'
+ * key in the field instance settings, holding an array of column names to be
+ * synchronized."
Maybe re-write the comment to match the order in the condition.
$entity->isNew()
is clearly if it's a "new entity"."no information about the translation being saved" is that... ?
empty($sync_langcode)
and also!$original_langcode
why is count translations being tested for < 2? I would think that "if we have no translations" would be
count($translations) < 1
Oh yeah, it counts the language of the entity too. http://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TypedData%...
but why the && ?
I guess I would suggest:
But how do you know if there is more than one translation, if that is the translation being changed and needs the original_langcode?
Maybe the test for having enough information about the translation needs to come later, comparing something like the next block: the original/unchanged values with the current values?
Really? seems like there might be.
I admit, I dont know what NG stands for and google was less than helpful. I thought maybe if I searched for EntityNG... http://api.drupal.org/api/drupal/core!lib!Drupal!Core!Entity!EntityNG.php/8
comment seemed to imply that empty fields were not translatable... which was just a little odd wording.
I started with
Sync when possible. There is nothing to synchronize when the field is empty, if synchronization is not turned on for the field, or it's not translatable.
but that's double negatives. So I suggest:Use the source of the translation as the sync source?
You are checking if a translation is being created by checking if the $original_langcode arg was set? I think that means that the first condition in the function checking if we can even do sync'ing cannot know if we "have enough information about a translation".
What are we looping through here? overall, we are doing each field (that it makes sense to sync). are the items each language's value of the field? or is it taking into account the cardinality of the field, so it's the sync source language field's values?
Sorry, that's as far as I got... I didn't actually test it yet.
There is an interdiff with just the code style changes if that is all that is really needed.
Oh, I think in the tests and the code, maybe with some better variable names, things might be easier to understand. source is tricky as this is kind of dealing with translations, which have sources (or original values) and also with the sync, which has a source. Also, values and items were confusing. What to do if anything probably depends on the target audience of the comments.
Comment #41
YesCT CreditAttribution: YesCT commentedI know the condition is wrong still logically... just want to see.
Comment #42
YesCT CreditAttribution: YesCT commentedFound this while trying to manually test:
#1884148: field widget disappears when increase cardinality for image to allow multiple values
Reproduced in clean 8.x install
Comment #43
yched CreditAttribution: yched commentedOK, I understand the goal better now, thanks ! Wow, that's a nasty/complex feature to implement :-/
So, yeah, using drupal_container()->get('request')->attributes to set info and get it back in other parts of the code looks like an unholy hack - globals without globals :-). What are the possible ways of getting rid of this ?
There is some hairy logic in translation_entity_sync() to "recognize" and track items between old an new values and figure out which item got reordered where - the part from "// Make sure we can detect any change in the source items." to the end of the function.
It could really use an "algorithmic overview" comment to explain the logic IMO - even though the "step by step" comments that are present are good, I still have a hard time understanding the overall logic and figuring out if it's valid.
- For example, the case where there are several synced columns sounds like it might be complex ? And that case is not covered by the tests. And ouch, testing that case is not going to be trivial. Maybe splitting that logic to a helper func that works with raw arrays and not $field, $instance and $entities, would help make it more unit testable.
- Also, what if several items have the same value for a 'synced' column (e.g same image) ? how do we tell which is which ? I mean, the image might be the same but the alt text and their translations might differ, isn't that a problem ?
Looks like the comment doesn't match the logic : "has changed" vs
if ($created))
Are we 10%% sure that $value is safe to use as an array key ? I don't remember offhand what can and cannot be a valid array key...
Minor : s/The module/The modulo operator/ :)
Comment #44
plachGood suggestions, I'll try to implement them as soon as I can.
Comment #45
fagohm, that's a pretty complex feature and problem space. I must say I'm not in favour of the syncing solution, but it seems to be the most feasible one.
Thinking about how it could be done with EntityNG, we could support adding 'translatable' flags on the field value level there while by default they are all translatable, and then implement the synching logic inside of the entity field objects - i.e. if translatable is set and FALSE for a property being changed, do something like
$field, $delta and $name could be determined via $this->getPropertyPath(). Of course we'd have to apply it to all translation languages.
That way, we'd have full support for 'translatable' in the API while the storage layer would only have to take care of field translatability.
Maybe, it would make sense to do it inside EntityNG?
Update: I'm not sure it really makes sense in EntityNG as the field is not really translated anymore. It's kind of being shared while some values are translated - how would this feature be exposed in the UI?
Comment #46
plach@fago:
Honestly I thought converting this to NG would be pretty straightforward, once all the entities are converted.
I just spent other 3 hours to improve the documentation and write a brand new unit test. Would you consider letting this feature in as-is and talking about the NG conversion later? This could happen also after feature completion, while I'm pretty sure I won't have time to rewrite the code here from scratch since I have more urgent stuff to work on.
Comment #47
plachThis should address #43. Hopefully this can go in now, otherwise I'm afraid we won't have this functionality in D8 core.
Comment #49
plachTests were passing here because I had ET enabled. The attached patch uses
DrupalUnitTestBase
instead ofUnitTestBase
.Comment #50
fagoSure, it's not my intent to block this in any way - I was just trying to figure out the best way to implement this quite complex feature.
So am I right, that this can be enabled in addition to the general translation option, i.e. you have the choice to do both?
Then, while thinking more about it, I don't think we should have the sync-ing feature in the storage layer as we need to be able to switch between full translation and sync-ing anyway - so it can't just be magically the translation of the field. So the approach taken in the patch makes totally sense for me :-)
Comment #51
plachYes, you can choose to have you fields vary independently by language or have part of their values synchronized across languages. There's a new checkbox in the field settings.
Glad to hear that :)
Comment #52
plachSo, I guess we are just missing @yched's ok here, right?
Comment #53
plachAny other concern/suggestion?
Comment #54
YesCT CreditAttribution: YesCT commented#49: et-sync-1807692-49.patch queued for re-testing.
Comment #55
yched CreditAttribution: yched commentedSorry, very little drupal time lately :-/
Will try to make some time for this, but if it's not tomorrow it won't be before another week.
Comment #56
plach#49: et-sync-1807692-49.patch queued for re-testing.
Comment #57
plachRerolled
Comment #58
plachSorry, wrong one.
Comment #60
plach#58: et-sync-1807692-59.patch queued for re-testing.
Comment #61
plach#58: et-sync-1807692-59.patch queued for re-testing.
Comment #62
plach@yched:
Even a "yes" or "no" would be welcome :)
Comment #63
plachRerolled after #1807776: Support both simple and editorial workflows for translating entities went in.
Comment #65
plach#63: et-sync-1807692-63.patch queued for re-testing.
Comment #66
yched CreditAttribution: yched commentedSorry for the delay, the last fortnight has been pretty dense in my "non-drupal" mode :-/. Slowly getting back at the core queue.
Minor / nitpicky stuff first :
Double empty line (& after getInfo() too)
Can we avoid random values, use, like, 4 instead ?
A bit cryptic.
"Set up an intitial set of values in the correct state, i.e. with "synchrnozed" values being equal." ?
s/ti/to
Likewise, pick one language instead of random ?
Yay @ the new unit testable helper func, & extensive comments. I'd just suggest prefixing the func name with an underscore so that it's not mistaken for an API entry point.
-3 days to next Drupal core point release.
Comment #67
yched CreditAttribution: yched commentedMore substantial stuff :
"The column values must be integer or strings" - because they're used as array keys, IIRC ?
What about NULL ?
What about decimal / floats ?
What about LONGTEXT ?
Also, where / how do we document that the 'synchronize' feature only works on integer / text values ?
"The synchronized column values are assumed to be unique among the various items".
OK, but in lots of actual cases they won't be, what happens then ?
Yeah, so that's the "how does this work when there are several synced columns ?" part.
Not sure what are the practical implications of this though ?
This means that any change in the value of any of the synced columns is seen as "some delta disappeared, that gets removed in all other languages, and a new one appeared, that gets copied over to all languages" ?
Meaning, if I change the image (value for fid column is changed) but not the alt / title texts, then the alt / title texts in other languages (that were possibly translated) will be overwritten with the ones of the language I made the change in ?
Is that really what we want ?
All in all, major kudos for all the work and dedication that were put in this.
I have to admit I'm still scared at the complexity of the feature, and not fully convinced that a) it can be achieved 100% bullet-proof rather than "best effort", and b) that this "best effort" will be "good enough" rather than "unpredictable in practice".
It's not directly in my queue and I won't be in charge of handling followups or bugs ;-), so I won't be blocking this if everyone wants it, though.
Comment #68
YesCT CreditAttribution: YesCT commentedsteps to test (a simple test)
Comment #68.0
YesCT CreditAttribution: YesCT commentedUpdated issue summary.
Comment #69
YesCT CreditAttribution: YesCT commentedscreen shots before the patch
A.
B.
Comment #70
YesCT CreditAttribution: YesCT commentedwith patch
1. create article, with translation enabled, but not sync'd
1a. source with image and alt text
1b. translation (using different image and alt text)
2. sync setting in article manage fields edit image field tab
3. create another new article, with sync setting checked on article image
4. create translation, with sync setting on image
(very similar to what form looks like without the patch) if change the image here, though, it changes also for the source. [edit again: corrected.]
[edit: added the missing files here]
Comment #71
YesCT CreditAttribution: YesCT commentedopps. missed some files.
updated previous comment to include them.
Comment #71.0
YesCT CreditAttribution: YesCT commentedupdated remaining tasks to reflect steps to test were added.
Comment #71.1
YesCT CreditAttribution: YesCT commentedupdated remaining tasks to reflect screenshots done
Comment #72
YesCT CreditAttribution: YesCT commentedmanually tested. works!
I have some suggestions.
@plach let me know if you want to do them in follow-up (I can open the issues), here, or you think not really a problem or whatever.
from:
to:
Would be too specific.. but more clear to say:
(novice tasks done for now, will update issue summary, removing tag)
Comment #72.0
YesCT CreditAttribution: YesCT commentedupdated remaining tasks to reflect reviews have been done.
Comment #73
plach@yched:
Thanks for the review(s)! I'll try to address them ASAP. A couple of answers meanwhile:
Yes, this is a limitation we need to document (and probably better enforce). Our main use case are fids so this perfectly works for now.
One possible solution is that we dected this situation and we bail out without syncing anything. I'd like to explore the possibility to support multiple values, but I'm not sure it's possible without complicating the algorithm too much.
Well, we could refactor the algorithm to combine the column values so that we have a unique key (a primary key). This would allow us to support any combination of the synced columns, provided that at least one value differs from the others.
Yes, I am afraid we cannot get around this: a change in a synced column cannot be intepreted in any other way but "a new value is here". We might not propagate it to all the translations or leave the unsynced columns empty, but in the case of alt/titles a language fallback looks like the most reasonable behavior.
I agree we probably cannot achieve a 100% bullet-proof behavior. What I think we can get is something that works in the 80% of use cases and does not break things in the remaining 20%. The code here has been almost straightforwardly ported from contrib ET (D7), which has around 6000 installations, and no bug about it has been reported so far. Hence I'm pretty confident we can have something that can actually be useful.
Comment #74
YesCT CreditAttribution: YesCT commentedI'm not sure, @plach might be in the middle of making a new patch to address some stuff (or maybe just writing explanation).
This help text change could wait and be a follow-up, but here it is, in case it's helpful.
I looked and didn't see any tests that would need be changed as a result of the title change of the checkbox (guess testbot will tell me for sure).
Comment #75
plachHere is a new patch, I think it's considerably more solid than the previous one: it should now support multiple elements having the same values for the synchronized columns and the ability to detect changes when just one synced column changes.
This should also take care of #66.
As a bonus all the synchronization logic has been moved to an autoloadable class, to reduce memory footprint. Not sure we need to register it into the DIC: would it make sense for the sync logic to be swappable?
Anyway, the attached interdiff shows only the changes in the sync logic and the related tests. The actual one wouldn't be very useful.
@YesCT:
The attached patch incorporates the proposed textual changes, but it collapses them into the checkbox description: the label felt too long in the original proposal.
About #72:
@1: Yes, this should be solved by making translatability an instance setting.
@2: Also @bforchammer propoed something similar above: I think it would be nice if we could have all these settings in a modal dialog, as originally proposed by Bojhan.
@3: I think a warning message after enabling the setting should be enough. Do you wish to provide a text or a patch?
@4: Looks a bit verbose to me: maybe just "Synchronized"? I'd prefer to address this in a follow-up to be sure to have a UX feedback and not hold this patch on it.
Comment #76
YesCT CreditAttribution: YesCT commentedsounds good. I think the @2-4 can be follow-ups for sure... and that will make it easier for people (uh hem, me) to try and address some of them.
Comment #77
YesCT CreditAttribution: YesCT commentedfollow up
for 2 #1909202: Use modals in operations column of language settings config page
for 3 #1909212: Warn user when sync is enabled on field with previous values, they are not sync'd until one is changed
for 4 #1909218: add (all languages) hint to synchronized fields on translation add/edit form
Comment #78
plachIt would really important to get this RTBC before feature completion.
Comment #78.0
plachupdate remaining tasks, added need to decide on follow-ups
Comment #79
plachHere is a reroll.
Comment #81
plach#79: et-sync-1807692-79.patch queued for re-testing.
Comment #82
yched CreditAttribution: yched commentedMy remarks on the latest code. Mostly nitpicks / code organisation remarks.
Nitpick : both singular or both plural ?
Minor: missing word or extra 'of'.
Missing type hints for the langcode args.
Also, "$original_langcode is either a string or FALSE" looks sloppy, that's precisely why NULL exists.
But more generally, the method is always called with a value for $original_langcode, so why is it optional ?
[Edit : oh ok, FALSE is because the value comes from EntityTranslation::getSourceLangcode(), which currently returns "string or FALSE".
I'd be inclined to change that to NULL upstream, but then I guess that's not for this patch.]
Looks a bit hackish, relying on the fact that "real" deltas are positive, and then interpret "< 0" for "not found".
Initializing $old_delta & $new_delta to NULL and checking isset($old_delta) / isset($new_delta) should work.
Could it be named itemHash() instead ?
What this does is compute a hash for (the values of the synced cols of) an item, not an id. What makes the patch complex is precisely that field values don't have ids that we could use to track them, but this does not add ids, just works around the absence thereof :-).
Minor : implode($empty_array) == '', so the last line could be just
return implode('.', $values);
?+ I'd tend to think validateValue() as a separate method adds an unnecessary mental indirection, could be inlined within itemIdentifier() now.
+ I guess we could use a real hash, like md5(serialize(array_intersect($item, $synced_cols)) and therefore support more than number & strings, but maybe that's not worth the runtime overhead ?
is_numeric(NULL) == FALSE, is_string(NULL) == FALSE
That's annoying because a numeric db column could have a value of NULL. (for image fields that would mean the field is empty and is not stored, but for multi-column fields that could happen). It seems allowing NULL would be doable ?
implode('.', array(1, NULL, 2)) == '1..2', works.
This is still an ugly hack :-(. Not sure what are the plans to get rid of that ?
@todo + followup issue ?
Interesting.
In the previous patch, this was translation_entity_sync() function calling entity_load_uncached().
So now that the code moved to a class, we avoid calling functional stuff like entity_load_uncached() within a method and go for the OO equivalent ?
Does that mean that entity_load() too is considered bad practice in OO code, and that any class willing to load an entity needs to get an injected EntityManager and do
$this->entityManager->getStorageController($entity_type)->load(array($id));
instead ? Ew :-/Definitely not a debate for this thread, but IMO that would deserve a spin off discussion to establish the practice.
At the very worst, this loadUnchanged() method should be provided by EntityStorageController, not recoded in FieldTranslationSynchronizer and in each other class that might have a use for entity_load_uncached().
For this patch, I'd be inclined to keep FieldTranslationSynchronizer::synchronizeEntity() calling entity_load_uncached() for now...
Regarding the behavior itself :
As I mentioned earlier, I still don't get why we overwrite the whole item in the target languages, instead of only the values of the synced columns, which would be easily doable ?
The current code does : if I edit the node in german and change an image, the english and french alt & title texts that I previously entered will be overwritten with the german values, and I need to edit the other languages to switch them back.
I would think we want : if there are corresponding values in english and french, only synchronize the columns that are supposed to be synchronized and keep the unsynchronized ones untouched ?
In other words, the patch introduces the notion of "synchronized columns", but those are in fact only triggers for the sync behavior, and all columns are in fact synchronized.
But @plach says the current behavior comes straight from D7 ET contrib, where it received no major complaints, so I'm fine with committing the current and possibly discuss / revisit in a followup.
Comment #83
YesCT CreditAttribution: YesCT commentedyched, thanks for the thorough review.
Once we hear back from plach, and follow-ups are opened, then this sound ok to proceed with.
Comment #84
plachThanks for the review, I think I agree with almost everything. I'll be a bit busy this weekend. Hope to be able to squeeze some time out and get this done...
Comment #85
plachThis should address more or less everything in #83. Could not launch tests so I hope this is still good.
Some answers:
I originally thought about real hashing too, but I dismissed this solution because of performance concerns. After rethinking about it I went for a hybrid approach: a hash is computed only for non-string and non-int values.
In #1810370: Entity Translation API improvements we agreed to try and introduce an
EntityLanguageDecorator
that should allow us to remove this hack. Since this would be the only example in core right now, I think it would be fine to fix it there. Added a @todo about this.I think we already implicitly dediced to go this way when we introduced the DIC: no point in using procedural functions which are in the global scope and make everything harder to unit-test. That said nothing would prevent us from somehow improving the entity manager DX. Agreed on having this dicussion elsewhere. Reverted the change for now, since
loadUnchanged()
totally belongs to the storage controller.Well, the point is that if you change a synchronized value, supposedly you are invalidating also the other ones: think of a node with an image of an apple (english alt = "An apple", french alt = "Une pomme"), then you replace it with the photo of a kitten (I cannot image why you would do this, but let's suppose for a moment :) and enter an english alt the says "A cute kitten". For me it makes far more sense to have a french translation where you have an untranslated english alt rather than having the photo of kitten with a alt saying "Une pomme" :)
Comment #86
plachNo idea of wtf just happened :(
Comment #87
yched CreditAttribution: yched commentedYup, great idea !
Although, my bad, md5() is actually forbidden in core now even for non security-related uses, hash('sha256') is the standard. See http://drupal.org/node/845876 and the recent #1884826: Regression - replace md5 in Block module calls with sha2 hashes.
Shouldn't that be changed to
isset($old_delta) && isset($new_delta)
too ?Comment #88
plachIndeed, fixed. Thanks!
Comment #89
yched CreditAttribution: yched commentedThanks !
RTBC if green.
Comment #91
plachThis should be more like it :)
Comment #92
yched CreditAttribution: yched commented:-)
Comment #93
catchOK I don't have time for a full review but I don't see any discussion here about converting these to fields on the file entity instead of serialized data in the field storage. The problem here is really with the image field for me, and introducing this to work around that doesn't feel good.
Comment #94
plach@catch:
This has a far broader scope than just image fields: it will work with any field having item values needing to be synchronized across entity translations. Image fields are just a prominent example. This API would make sense even if we had no use case in core. Please, reconsider the issue status.
I don't think we can realistically make file entities fieldable in core one week before feature freeze, since this would require a dedicated UI, unless there already has been significant work on this that I totally missed. Which could be very well possible, btw :)
Comment #95
plachRetitling to be more clear about the goal of this issue.
Comment #96
plachComment #97
plachTagging for D8MI.
@catch:
This is the only issue I was able to find where the discussion you are missing might have happened: #376287-16: Make files fieldable.
I have no idea how we could get this done in the scope of the multilingual initiative. My approach with this was: if the Media initiative succeeds and we have fieldable files in core, wonderful, otherwise we need an alternative solution. Hence I came up with the current proposal which meanwhile matured in something that can totally live without image fields exploiting it. AAMOF if you review the patch you'll discover that only 2 lines of it, leaving alone a dedicated test, is dealing specifically with images/files.
To sum up I think this is a cool feature to exploit in contrib and leaving out from core will mostly kill it since I probably won't maintain a D8 version of contrib ET just for it, although people upgrading from D7 might expect to find it still there.
Comment #98
catchTalked to plach about this in irc. I asked for use-case other than image fields (which should really just be a reference). Plach mentioned a map field with a label - pointing out you probably wouldn't want that to be a reference to an entity with a text field.
Moving back to CNR - I still haven't reviewed the code here, only worried about the use cases at this point.
Comment #99
plachActually we have at least another use case in core: the link field, which has an optional Title textual column.
Comment #100
fagoAlso, the displayed label for a referenced image field might vary by usage? Having it in the file entity would not account for that.
Comment #101
Gábor HojtsyLooks like plenty of other use cases to back this up, back to RTBC then.
Comment #102
Dries CreditAttribution: Dries commentedThe UI elements introduced in this patch don't cut it for me. 'Synchronize translations' is just too cryptic of a setting for the end user. It's misleading actually as its suggests that the strings will be synchronized. In the case of the Image field, as far of the site builder is concerned, it would be better to write something like
[ ] Use different image for all translations
. If we do this right, we may be able to get rid of the help text too. Right now, it seems like the current patch exposes implementation details in the UI.Comment #103
plach@Dries:
I must say I completely agree :)
I spent some time thinking about an alternative when writing the first patches but nothing really user-friendly came to my mind and then forgot about it. Now I spent some more time thinking about this: would something like the screenshot below work better?
Comment #103.0
plachyched's comments were addressed. also follow-ups opened.
Comment #104
effulgentsia CreditAttribution: effulgentsia commentedI think that's an improvement, but how about this idea?
Instead of making the field type module decide which columns are 'sync' worthy, and implicitly assuming that everything else is "textual", and then needing to add an extra form element to the field settings form, we instead:
- Let field modules specify "column groups". For example, image_field_info() could add the following:
- Have translation_entity.module enhance the admin/config/regional/content-language form (where the administrator decides which fields to make translatable), such that when a field that has 'column_groups' is set to translatable, that a subrow appears for each column group, initially starting off checked, but the admin could uncheck as desired. Basically, the same pattern as how a bundle is expanded into its fields, just applied again to a field expanding to its column groups.
@plach: what do you think?
Comment #105
effulgentsia CreditAttribution: effulgentsia commentedYeah, this would have been great, but it didn't happen. The challenge with making file entities fieldable is that it's only useful if you define bundles. You don't necessarily want an "alt" field on all files, just on the image bundle. But implementing administrator-customizable bundles that properly relate to mime types turns out to be non trivial. #1260050: Provide administrative UI for adding/removing file types is an issue that's been open over a year in the File entity queue. Looks like it's gonna need more time in contrib, but would be awesome to get into D9.
Also a good point, and there's been discussion in various places (sorry, don't have links handy) about needing a "File usage" entity in addition to file entities to support that cleanly.
That's just background for folks interested in helping out with fieldable files. I also agree with #99 that the Link field provides another use case and several contrib field types do as well.
Comment #106
Bojhan CreditAttribution: Bojhan commentedHad a short call with plach. This looks like a good idea, I agree with Dries that the word sync is misleading. I really like effulgentsia his proposal, I think thats exactly what people will expect!
The ability to toggle translatability on properties, this should come with some sensible defaults and should be displayed on both the i18n setup page as one the field settings of an individual field. Otherwise we might end up to require way too much setup work for fields with properties.
Comment #107
plachOk, I will work on this ASAP. I'd just want to point out that the items order when we have a multiple field is not covered by the current proposal. Bojhan suggested to make it explicit by having an "Order" checkbox, but since this would force us to totally revisit the sync algorithm and that it would be more stuff for the user to configure, I'd suggest that that we just make it implicit and sync also the order when there is at least one synchronized column.
Comment #108
effulgentsia CreditAttribution: effulgentsia commentedThat makes complete sense to me. I can't think of a use case where it makes sense to vary item order by language, but require syncing of some part of each item. (says a person who's never operated a multilingual site)
Comment #109
Bojhan CreditAttribution: Bojhan commentedFYI, just to avoid confusion I just figured that would be a way to handle such a use case - if the use case does not exist we should definitely not support it.
@effulgentsia your last remark, is quite funny. I challenge you to just try it sometime :)
Comment #110
plachHere is a patch implementing #104 + #107.
As suggested by @Bojhan in the content language settings we have a slightly different behavior wrt fields: instead of making all columns translatable, when making a field translatable smart defaults are applied. In the screenshot only the textual columns are made translatable by default (thus triggering column synchronization). When making a field untranslatable column settings are reverted to their defaults. The same settings with their defaults are available on the instance settings page when the field is translatable.
Since the behavior implemented now might not satisfy every possible use case, above all because of #107, I decided to make the field sync logic swappable and registered the implementing class into the DIC.
Moreover while fixing tests I noticed that the image field sync test was not extending the ET base test, hence I updated it accordingly.
The end result is that the interdiff is pretty useless :)
Comment #111
plachI didn't foresee to work on this so much, I'd really like to see this going in now :)
Comment #112
YesCT CreditAttribution: YesCT commentedthis looks great! very clear!
Comment #113
YesCT CreditAttribution: YesCT commentedpostponed #1909218: add (all languages) hint to synchronized fields on translation add/edit form on this. it will needs to be looked at again after this lands.
Comment #114
plachComment #115
YesCT CreditAttribution: YesCT commentedin general this code is very well documented, and is even beautiful in some places.
some of the following are nits, and I'm happy to do them now, or after commit. documenting them here so I dont forget them.
I dont think any of these are blocking.
I did not (yet) test it manually. When I do, I'll check the hidden labels on the fields, but the code looked to add the necessary accessibility things. (or maybe someone will beat me to it)
-------------
should this be max, or source + unchanged?
spurious? as in bogus?
why && with created or removed, they are hard coded to be TRUE.
should there be an else in here? can something be both created (not old) and removed (not new)?
Oh... it can be neither...
http://drupal.org/node/1354#types says to use "int"
add int to function declaration for $delta.
setUp should be public
http://drupal.org/node/325974
(in the past this was not generally done though)
shifted one position ahead (remove of).
http://drupal.org/node/325974 says setUp should be public.
its -> it's (or: it is)
what does this test, that the other tests do not?
what does proxied indicate?
add a similar for ... field_ui_field_edit_form ?
Comment #116
effulgentsia CreditAttribution: effulgentsia commentedI haven't reviewed the code in-depth, or manually tested the syncing functionality, but from the little I've seen, I think this is really good. I hope @YesCT or someone else more familiar with this issue than I am can RTBC it in the next 24 hours or so. Otherwise, I'll try to detail review it then.
Comment #117
plachGreat review, thanks!
Max is enough, since we just need to ensure we reach the end of both source and unchanged items arrays. Anyway this is not needed when populating the change map, so moved it down and cleaned-up the related code.
Yep :)
There was a lot of cruft here left out from previous iterations. Cleaned-up.
Nope, type-hinting works only with classes.
It tests the sync behavior when multiple items have the same values to ensure they are retained and moved around if needed.
That it is the actual hook implementation. The proxy one is in the .module file. This helps reducing the memory footprint.
That one is not touched by this patch, so it would be out of scope.
Comment #118
plachOpened #1919322: entity_load_unchanged() should be part of the storage controller to address #82-#85.
Comment #119
YesCT CreditAttribution: YesCT commentedwow! quick turn around. glad some of that was helpful. I'm going to do manual testing now.
@plach
I thought that doc block was changed lines in the patch...
Comment #120
YesCT CreditAttribution: YesCT commented1. labels make sense, match the checkbox id, and have invisible text to help identify it's content to compensate for the equivalent visual indent.
2. unchecking all parts of image, on save the image is unchecked too. that makes sense.
3. I was confused because even though I tried unchecking and check translatable for title on image, it did not show in edit or add translation form. Then I remembered I might have to configure image itself to make title field show. yep.
suggestion, i) expect people will figure this out. ii) dont show fields are translatable if they are not enabled.
is this image specific? if so, go with i)
4. things make sense when file, alt, title are all translatable.
they make sense when file is not but alt and title are translatable.
stuff gets weird when the file is translatable but the alt and title are not. In practice this does not make sense anyway. During testing, the result is that changing an image means deleting it, and the alt and title are deleted when that happens also, so their values are not sync'd. since that behavior is image specific. it's a works as designed or wont fix too. since the code is generic, I think its behavior is ok.
that's as far as I got with manual testing and it works. rtbc from me.
Comment #121
plachYes, this is image-specific: we could have a novice follow-up to add a tiny bit of state magic in the image.module and hide the Title and Alt groups when the related form items are disabled.
Yep, this is why I originally went for a less flexible approach: less stuff to figure out for the user. Also here we might have a follow-up and add some JS to make the dependent groups always checked (and readonly) when the master group is checked. E.g. when I check "File", "Alt" and "Title" are automatically checked and cannot be unchecked.
Comment #122
YesCT CreditAttribution: YesCT commentedI agree those follow ups make sense. I can help open follow up issues.
Should we open follow ups for some of the @todos in the code?
Comment #123
plachYep, probably. Already created #1919322: entity_load_unchanged() should be part of the storage controller for:
Comment #123.0
plachUpdated issue summary.
Comment #124
YesCT CreditAttribution: YesCT commentedopened
as mentioned in #121
I'll add to issue summary
Comment #124.0
YesCT CreditAttribution: YesCT commentedadded recent screenshots.
Comment #125
webchickThe old issue summary linked to #89/90 and I was very scared because that UI needed a ton of work. But very stoked to see the screenshots at #110! That's exactly what I expected.
I did not test this patch myself, but it's clear that YesCT did a bang-up job of this already, and both she and yched have given it a thorough code review. I also think that being able to translate fields, but not being able to translate attributes of fields, is a WTF so am going to re-classify this as a major task.
Committed and pushed to 8.x. Thanks!
Comment #126
plachWonderful, thanks!
Comment #127
plachRemoving from sprint, thanks everyone for the help!
Comment #129
plach@yched:
Opened #1935762: Remove entity_load_unchanged() call from FieldTranslationSynchronizer::synchronizeFields() so we can dicuss the DX implications of injecting the entity manager.
Comment #129.0
plachUpdated issue summary, added follow-up section and some follow-ups
Comment #130
yched CreditAttribution: yched commentedIn #1992138-52: Helper issue for "field types as TypedData Plugins" (the "unify Field API with Entity/FieldNG" issue), we could use some help with failures in EntityTranslationSyncImageTest :-)
Comment #131
YesCT CreditAttribution: YesCT commentedI did a manual test today as part of looking at #2013837: Rewrite field sync on top of NG entities
without that patch, I've found that image files are always sync'd, even when the image file field is checked to be translatable.
I'm not sure when that broke, and someone should double check they have found the same thing as I have existing in 8.x right now.
Comment #131.0
YesCT CreditAttribution: YesCT commentedadded some followups that were already opened earlier to the issue summary
Comment #132
YesCT CreditAttribution: YesCT commentedAdding related, see #17 #2130811-17: Use config schema in saving and validating configuration form to ensure data is consistent and correct about translation sync schema config stuff
Comment #133
klonos...removed the "Related issues" section from the issue summary and moved all issues listed there as child issues of this one. That was a pain because we currently do not have this: #2130889: Allow adding child/follow-up issues directly from the parent issue and converting related issues to children.
Comment #134
klonos...
Comment #135
yched CreditAttribution: yched commented@YesCT, @klonos (and @plach) : opened #2136197: Move field/instance/widget/formatter settings out of annotation / plugin definition for the issue about config schema for 'translation_sync'.