Problem/Motivation

Currently the node UI prevents concurrent edits of the same node. This however does not take into account the case of multiple translators editing different translations of the same node.
Additionally this logic is applied only to nodes, but it would make more sense to generalize it to any entity type relying on the entity form handler.

Proposed resolution

  • Implement per-language locking, thus preventing only concurrent edits of the same translations.
  • Extend this check to any entity type.

Remaining tasks

  • Validate the proposed solution
  • Implement it
  • Review patches

User interface changes

Concurrent edit locking would be enabled on any entity edit form.

API changes

None foreseen

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

plach’s picture

Title: Implement per-language locking at entity form level » [PP-1] Implement per-language locking at entity form level
Status: Active » Postponed

This should be easier to implement if we do #2465913: Write to the storage only affected entity field values first.

plach’s picture

plach’s picture

Title: [PP-1] Implement per-language locking at entity form level » Implement per-language locking at entity form level
Status: Postponed » Active

The blocker has been committed.

plach’s picture

Issue tags: +rc target
xjm’s picture

See https://groups.drupal.org/node/484788 for more information on the rc target tag. I'd need some more detail on the impact vs. potential disruption of this change to assess whether it could be committed during RC or not, myself. :)

xjm’s picture

Issue tags: -rc target

Untagging for now.

mkalkbrenner’s picture

This feature makes sense!
But the implementation details might become sophisticated because an entity might consist of translatable and untranslatable fields.

Before we start implementing something, we need to define exactly how per-language locking should work in the context of untranslated fields.

While pessimistic locking should lock all translations if the entity contains untranslatable fields, optimistic locking has to check for changes on untranslatable fields on submit. If the untranslated fields did not change, we need to merge in between changes to these fields from other translations before we save.

plach’s picture

[...] optimistic locking has to check for changes on untranslatable fields on submit. If the untranslated fields did not change, we need to merge in between changes to these fields from other translations before we save.

I agree.

I think ideally we could even generalize this "merge approach" and allow any concurrent changes that did not affect the same field values. This would automatically allow for concurrent translation edit, since translatable field values would never generate conflicts when different translations are involved. However it would also support concurrent edits of the same translations as long as they do not touch the same fields, otherwise we would get the current behavior.

This would probably be a powerful feature for editors (and probably close to a VCS merge experience :) but I guess we should aim for the multilingual stuff for now and maybe "relax" these constraints in a later iteration.

Thoughts?

plach’s picture

Issue tags: +rc deadline

I guess this is either 8.0.0-RC1 or 8.1.0 material.

plach’s picture

Another tag

webchick’s picture

Version: 8.0.x-dev » 8.1.x-dev
Status: Active » Postponed
Issue tags: -rc deadline, -Needs product manager review

Hey there!

We're closing up shop on RC1 here very, very shortly, so I think the most realistic target for this is 8.1. Updating version/status to reflect that.

However!

+100 for the general idea of this feature. I think it would be awesome not only for translators, but also for any sites with multiple content authors. The proposed implementation sounds very similar to the https://www.drupal.org/project/nodechanges module, which is used by Drupal.org for issue comments that change statuses (this is actually changing node revisions behind the scenes). So that'd be one fewer module for D.o to port as well. :)

mkalkbrenner’s picture

Implementing optimistic locking by auto-merging all non-conflicting fields is a very technical approach. For Developers that are used to tools like git this sounds great, but if you think twice you'll notice, that this is definitely not a "one size fits all" solution!

Examples:

You have an untranslated image field in combination with a translatable text field for it's caption. It makes no sense to auto-merge a concurrent edit to the caption in one translation when the image has been changed in between.

Or think about commerce: One decides to "promote" an item by selecting the corresponding checkbox while another one marks it as "out of stock" in between.

These kind of merges are even more dangerous if you don't create a new revision!

I think we need to decide very careful here, like the decision for #2465907: Node revision UI reverts multiple languages when only one language should be reverted.

I would like to see a generic configuration option in core to switch between optimistic and pessimistic locking. In case of a merge we need to support the decision of what to merge.

I recommend that we let this sophisticated stuff incubate in contrib.

In Revision UI we're currently working on a form that lets you select single fields to be reverted. The corresponding list of fields is limited to fields that really changed between two revisions. Maybe this approach - or even better the code - could be re-used here for merging field changes.
BTW By using parts of HTML Diff it will be possible to really visualize the difference between two field values, like tracking changes in Word.

plach’s picture

@mkalkbrenner:

This is 8.1 (at least) now, so we have all the time to find a proper solution in contrib and evaluate core inclusion later. Thanks for starting looking into this!

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Status: Postponed » Active

I think this can be active now, since feature development is open for 8.2.x.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

matsbla’s picture

I tested now to edit different translations of a node concurrently, and it seems like working fine!

Have this issue been fixed?

Berdir’s picture

Now that we per-language changed tracking, this might look like it works, yes.

But I'm worried it will actually result in data loss. Can you check that? Is the data for the other translation list if you do something like this (using EN and DE, you can use any language combination):

1. Open first node form in language EN, doe an ajax request like add another item to make sure form state is cached
2. Open second node form in DE, make a change, save.
3. Save the first form with language EN, making another change.

Is your DE change still there?

mkalkbrenner’s picture

I tested now to edit different translations of a node concurrently, and it seems like working fine!
Have this issue been fixed?

I don't think that this issue is solved. (At least the use-cases in #12 are not addressed.)

But the current behavior you see might change again once there's progress on #2808335: Changed time not updated when only non-translatable fields are changed on a translated entity and #2615016: ContentEntityBase::hasTranslationChanges should exclude the "changed" fields from the comparison.

hchonov’s picture

Hm how would this be fixed? I can imagine that it only works if you do not have the EntityChangedConstraintValidator. But actually as soon as we have conflict management this issue would be fixed by it.

matsbla’s picture

Okay I tested with an image field that is untranslatable (but with translatable alt and title value).

I open form for 3 different translations. I add a picture to one of them with translations and saved it. After I saved the other translations, but the image is still only visible for the translation where I've added it, and I can now even add other images to the other translations (even though the image field itself it set to untranslatable).

hchonov’s picture

I am confused as in this case the EntityChangedConstraint should have failed and you shouldn't be allowed to save the other translations, but it looks like the constraint is not failing during your manual test. Could you please try to reproduce it in a test?

matsbla’s picture

I'm not able to make any test, but I can see if I manage to get help with it.

I tested more, it seems like what I experienced is only present with the default content type Article using the default image field that is shipped with core, I made one issue here #2837023: Image fields that follow core gets broken when set to translatable

However it seems like the concurrent editing of translatable image fields (with untranslatable files) is still not working. Currently EntityChangedConstraint seems to not fail, so old image is saved over new image when someone edit another translation while you try to add a new image, I made an issue for it here: #2837022: Concurrently editing two translations of a node may result in data loss for non-translatable fields

matsbla’s picture

One question; is EntityChangedConstraint only suppose to conatrain cuncurrent editing of same translation? Or also cuncurrent editing of untranslatable fields? It seems like untrabslatable fields are not constrained now so if someone edit them cuncurrently they will revert any changes made on these fields.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

vlad.dancer’s picture

vlad.dancer’s picture

Status: Active » Needs review
vlad.dancer’s picture

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

RoSk0’s picture

I believe we need to extend the scope of this issue to add not only per-language locking, but also per object-locking for translations. On one of the client projects we faced with the issue of loosing published translation when multiple editors publishing different translations of the same node at the same time.

Steps to reproduce:

  1. Install Drupal 8.9.x (I used 125a331dc300abb5d383ed2b3dfe1e6c08c14e9d commit)
  2. Enable "Content Translation" & "Content moderation" modules
  3. To mock complex site behaviour and help us to emulate simultaneous submission of multiple translation I created a test module, test_translation_data_loss, it has only two files required, test_translation_data_loss.info.yml and test_translation_data_loss.module and only one hook implementation hook_entity_presave() with one line of code sleep(1); . This small hack will help us in replicating long processes that run on big complex sites. You can find it attached. Install and enable it.
  4. Add 2+ languages
  5. Mark "Basic page" translatable
  6. Turn Editorial workflow for "Basic page"
  7. Create "Test page" in English published
  8. Open "Translate" tab for "Test page"
  9. Create draft translations for all configured languages
  10. Open edit form for all draft translations in separate tabs
  11. change moderation state in all draft translations to "Published", do not save
  12. Quickly click "Save (this translation)" button on all tabs

I also recorded screen capture of the process on clean Drupal installation following steps above. Link: https://youtu.be/Un8rc8BnJhk

RoSk0’s picture

FileSize
829 bytes

Forgotten module.

hchonov’s picture

@RoSk0, this a content_moderation issue and you should report it in the issue queue of the module. Default core behavior wouldn't have allowed you to save multiple tabs of the same entity without having the conflict 2.x module enabled. This means that content_moderation somehow interferes with the core protection mechanisms or overwrite them and introduces its own.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

matsbla’s picture

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

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

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

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

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

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

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

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