Problem/Motivation

If two users attempt to edit the same layout, they will both see the changes the other is making.

Proposed resolution

Mimic the Views UI (which also uses tempstore.shared) and lock the Layout Builder UI when another user is editing the layout.

Remaining tasks

N/A

User interface changes

The LB UI will now show a message when there is a lock of another user. Breaking the lock will bring you back to the LB UI to make your own changes.

API changes

N/A

Data model changes

N/A

Release notes snippet

N/A

CommentFileSizeAuthor
#106 3025231-106.patch34.99 KBNitin shrivastava
#105 3025231-105.patch34.99 KBNivethaSubramaniyan
#91 3025231-91.patch35.52 KBsanjayk
#88 3025231-72-rerolled-88.patch46.31 KBarshadkhan35
#84 3025231-84.patch45.42 KBrpayanm
#77 3025231-77.patch45.62 KBmrinalini9
#72 3025231-72.patch48.03 KBrpayanm
#68 3025231-68.patch47.14 KBbnjmnm
#68 interdiff_65-68.txt4.92 KBbnjmnm
#67 3025231-67.patch90.5 KBbnjmnm
#67 interdiff_65-67.txt4.92 KBbnjmnm
#65 3025231-65.patch42.22 KBbnjmnm
#65 interdiff_63-65.txt8.74 KBbnjmnm
#63 3025231-63.patch38.95 KBbnjmnm
#63 interdiff_61-63.txt7.48 KBbnjmnm
#61 3025231-61.patch35.63 KBbnjmnm
#61 3025231-RerollOf59.patch26.81 KBbnjmnm
#61 interdiff-RerollOf59__61.txt19.52 KBbnjmnm
#59 3025231-concurrent-59-interdiff.txt13.9 KBtim.plunkett
#59 3025231-concurrent-59.patch26.85 KBtim.plunkett
#57 Screen Shot 2019-04-02 at 17.19.03.png241.3 KBjohnwebdev
#56 3025231-concurrent-56-interdiff.txt3.93 KBtim.plunkett
#56 3025231-concurrent-56.patch24.84 KBtim.plunkett
#53 3025231-concurrent-53-interdiff.txt7.16 KBtim.plunkett
#53 3025231-concurrent-53-PASS.patch24.3 KBtim.plunkett
#53 3025231-concurrent-53-FAIL.patch18.64 KBtim.plunkett
#52 3025231-concurrent-52-interdiff.txt2.9 KBtim.plunkett
#52 3025231-concurrent-52.patch18.24 KBtim.plunkett
#50 3025231-concurrent-50.patch17.09 KBtim.plunkett
#48 3025231-concurrent-48-interdiff.txt2.52 KBtim.plunkett
#48 3025231-concurrent-48.patch17.09 KBtim.plunkett
#46 3025231-concurrent-46.patch17.09 KBtim.plunkett
#45 3025231-concurrent-45-interdiff.txt1.43 KBtim.plunkett
#45 3025231-concurrent-45.patch16.85 KBtim.plunkett
#43 3025231-concurrent-43-interdiff.txt1.72 KBtim.plunkett
#43 3025231-concurrent-43.patch16.57 KBtim.plunkett
#41 3025231-concurrent-41-interdiff.txt5.2 KBtim.plunkett
#41 3025231-concurrent-41.patch14.86 KBtim.plunkett
#36 lock_with_save_link.png50.48 KBtedbow
#35 Are_you_sure_you_want_to_break_the_lock_on_the_layout_changes____Site-Install.png81.05 KBxjm
#35 Edit_layout_for_Article_content_items___Site-Install-2.png34.1 KBxjm
#29 3025231-tempstore-29-interdiff.txt528 bytestim.plunkett
#29 3025231-tempstore-29.patch12.46 KBtim.plunkett
#27 3025231-tempstore-27-interdiff.txt2.88 KBtim.plunkett
#27 3025231-tempstore-27-PASS.patch12.46 KBtim.plunkett
#27 3025231-tempstore-27-FAIL.patch11.53 KBtim.plunkett
#23 3025231-tempstore-23.patch11.63 KBtim.plunkett
#18 3025231-tempstore-18.patch11.49 KBtim.plunkett
#17 3025231-tempstore-17-do-not-test.patch11.49 KBtim.plunkett
#17 3025231-tempstore-17.patch38.84 KBtim.plunkett
#16 3025231-tempstore-16-do-not-test.patch11.72 KBtim.plunkett
#10 Screen Shot 2019-01-13 at 8.40.43 PM.png264.99 KBKristen Pol
#10 Screen Shot 2019-01-13 at 8.39.02 PM.png223.18 KBKristen Pol
#10 Screen Shot 2019-01-13 at 8.38.06 PM.png175.71 KBKristen Pol
#10 Screen Shot 2019-01-13 at 8.37.34 PM.png279.4 KBKristen Pol
#4 3025231-tempstore-4-interdiff.txt7.95 KBtim.plunkett
#4 3025231-tempstore-4.patch22.18 KBtim.plunkett
#2 3025231-tempstore-2-PASS.patch15.9 KBtim.plunkett
#2 3025231-tempstore-2-FAIL.patch2.46 KBtim.plunkett

Issue fork drupal-3025231

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett created an issue. See original summary.

tim.plunkett’s picture

Status: Needs review » Needs work

The last submitted patch, 2: 3025231-tempstore-2-PASS.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
22.18 KB
7.95 KB

Fail patch was good. Here are some fixes for other tests.

Status: Needs review » Needs work

The last submitted patch, 4: 3025231-tempstore-4.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review

D.o git infra snafu

Status: Needs review » Needs work

The last submitted patch, 4: 3025231-tempstore-4.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
Kristen Pol’s picture

Thanks for the patch. I reviewed the code for clarity and code formatting and didn't see anything obviously wrong. I compared the code against some of the Views lock code and when looking at this:

  /**
   * If this view is locked for editing.
   *
   * If this view is locked it will contain the result of
   * \Drupal\Core\TempStore\SharedTempStore::getMetadata(). Which can be a stdClass or
   * NULL.
   *
   * @var object
   */
  public $lock;

I'm wondering why the Lock class was created rather than using the same approach as Views. And, if the Lock class is better, then should Views be refactored to use that?

I'll do some testing.

Kristen Pol’s picture

I tested this by:

  1. Created "anotheradmin" user (so there would be two admins)
  2. Enabled the layout modules
  3. Configured the basic page content type to use layout per node
  4. Created node/1
  5. Went to layout page for node/1 as admin and made some changes but didn't save
  6. Went to layout page for node/1 as anotheradmin and saw the locked warning
  7. Clicked "break this lock" and continued with form to break the lock which was successful
  8. Then I reversed the process and started as anotheradmin and then tried as admin and saw the locked warning for the admin account
  9. As admin, I broke the lock and made changes and they were saved successfully

One oddity I found was after I broke the lock with the form and got back to the layout page, if I reloaded the page, it would show a message that there were unsaved changes even when there weren't any changes. This might not be due to this patch but it seems like it might be. Given that and my previous comment in #9 I'll mark this back to needs work for feedback.

tim.plunkett’s picture

Status: Needs work » Needs review

#9
I wrote up a small Lock class because using stdClass is kinda gross.
Ideally it would be made a part of TempStore more directly, but the TempStore subsystem needs some love anyway. In the meantime, putting a final and @internal implementation in this experimental module should be fine.

#10
The unsaved changes bit is the current expected behavior, even if that makes no sense.
It's tied to #2936501: Reverting an override redirects the user to an edit form for a new override and therefore still sets the new override as "unsaved changes", which is confusing and #2917777: Improvements to the styling, grouping, etc. of the Layout Builder UI actions form and even #2988970: Layout Builder should make it easier to modify the default layout for an entity type when viewing an entity

Kristen Pol’s picture

Status: Needs review » Reviewed & tested by the community

Given comments from #11, marking RTBC.

xjm’s picture

+++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
@@ -344,21 +386,4 @@ public function saveLayout(SectionStorageInterface $section_storage) {
-  /**
-   * Cancels the layout.
-   *
-   * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
-   *   The section storage.
-   *
-   * @return \Symfony\Component\HttpFoundation\RedirectResponse
-   *   A redirect response.
-   */
-  public function cancelLayout(SectionStorageInterface $section_storage) {
-    $this->layoutTempstoreRepository->delete($section_storage);
-
-    $this->messenger->addMessage($this->t('The changes to the layout have been discarded.'));
-
-    return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString());
-  }

At a first read, this seems out of scope?

xjm’s picture

Priority: Normal » Major
Status: Reviewed & tested by the community » Needs review

So given the impact on user input (where the user gets seemingly broken and irrelevant data on editing a layout the same time as someone else), this is a major bug.

I discussed this issue with @tim.plunkett a bit. There are two parts to the fix:

  1. Converting the "cancel layout" link to use a confirmation form.
  2. Adding the same locking pattern that Views uses to fix the concurrent editing behavior.

I suggest that we split the first off into its own blocking issue, since it's a good change on its own. For the second, reviewing it here is a little difficult because we have a slightly forked, slightly improved version of the API that the Views tempstore uses, which I don't think we should do. @tim.plunkett indicated that his main concern was using a stdClass object to pass around the data the way that Views currently does. We agreed to work on an upstream patch to fix that. At a minimum, knowing what the upstream patch will look like will make this issue easier to review, and ideally, we can just make that upstream fix without adding technical debt for this issue on top of what already exists in views.

Thanks!

tim.plunkett’s picture

tim.plunkett’s picture

Here's a patch for this issue that is built on those other two.

tim.plunkett’s picture

Status: Postponed » Needs review
FileSize
38.84 KB
11.49 KB

Still postponed, but rerolled.

tim.plunkett’s picture

Blocker is in!
This is the same as the 17-do-not-test

phenaproxima’s picture

+++ b/core/modules/layout_builder/src/Form/DiscardLayoutChangesForm.php
@@ -84,6 +103,8 @@ public function getCancelUrl() {
+    $lock = $this->layoutTempstoreRepository->getLock($section_storage);
+    $this->isOwnerCurrentUser = $lock && $lock->getOwnerId() === $this->currentUser()->id();

It's not a big deal, but I'm not clear why we need to store this in a boolean property just for getQuestion(). Perhaps a new protected method would be better?

Other than that, I think I have no complaints. Patch seems very straightforward and consistent with the Views UI locking mechanism.

tim.plunkett’s picture

I don't see any reason to either store the lock statefully or cause multiple tempstore retrievals. Putting this in a property allows the other method to utilize it, treats buildForm like the form equivalent of __construct that it is, and allows future customization of the form based on this Boolean

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Thought about it a bit, and since the class is relatively sparse, I think this is probably fine. I'd be less comfortable with it if this were a more complex form, but for what it is, I don't think there are too many risks here. RTBC.

Kristen Pol’s picture

Does this need to be manually tested again?

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned
FileSize
11.63 KB

Here's another reroll. Idk if it needs another manual test, but it shouldn't because we have automated tests. Can't hurt though.

xjm’s picture

FWIW I would end up manually testing this myself before commit if the testing were not repeated, so if #10 were repeated with the final patch that would save committer time. :)

Kristen Pol’s picture

I tried to test the patch on simplytest.me a few times but it kept bombing out. I'll try to remember to try again tomorrow.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

One oddity I found was after I broke the lock with the form and got back to the layout page, if I reloaded the page, it would show a message that there were unsaved changes even when there weren't any changes. This might not be due to this patch but it seems like it might be. Given that and my previous comment in #9 I'll mark this back to needs work for feedback.

I tested with defaults (instead of overrides) using #10's steps and did not get this message with the patch applied. However, I did get redirected back to the "Manage display" page when I broke the lock, instead of the form I was on, which is confusing. Marking NW for that.

tim.plunkett’s picture

Aha, yes that is odd. And turns out, the test coverage I wrote was odd because of it!
After breaking the lock the test was drupalGet()ing back to where it should have just redirected in the first place.

The last submitted patch, 27: 3025231-tempstore-27-FAIL.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Ugh, extra newline.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

I briefly tested this, using defaults (not overrides), and was redirected back to the layout editing page when I broke the lock. Based on the last few comments, that seems to be the desired behavior. It worked for me, then, so I guess this is good to go.

xjm’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/layout_builder/src/Form/DiscardLayoutChangesForm.php
@@ -69,14 +88,14 @@ public function getFormId() {
-    return $this->t('Are you sure you want to discard your layout changes?');
+    return $this->isOwnerCurrentUser ? $this->t('Are you sure you want to discard your layout changes?') : $this->t('Are you sure you want to break the lock on the layout changes?');

I think the message or description here should indicate that "breaking the lock" means throwing away the other user's changes. (Edit: The "break this lock" part of the message on the UI itself should probably indicate that as well.)

tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community

Both this string and the string in the message are mimicking Views UI. And the message string is literally in Drupal\Core code now.

For the reference, other string is 'This @label is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.'

A follow-up to consider improving this in both Layout Builder and Views UI (and the generic shared code) would be fine, but I think the message is sufficient.

xjm’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs usability review

I know the original string message about the existence of the lock is from the Views UI, but the confirm form saying "Are you sure you want to break this lock?" does not give the user enough information. It just says "Are you sure you want to break this lock? This action cannot be undone." Which sounds fairly harmless. But it's deleting another person's work and data from the database.

Tagging for UX review.

xjm’s picture

I am talking about core/modules/layout_builder/src/Form/DiscardLayoutChangesForm.php, a change made to it in this patch. See #31. It's not in Drupal\Core.

xjm’s picture

Also, let's please not use Views UI as our standard of acceptable UX for LB (or anything). :P Views UI is very very admin-facing; LB is OTOH available to content authors.

Screenshots for the UX review. The user clicks the "break this lock" link in this message:

That link takes them to this form:

The result of the action is that another user's unsaved changes to the layout are lost forever, including their unsaved changes if they are on the form making edits right now. I don't think "break the lock on layout changes" sufficiently conveys to the user that the in-progress changes from @otheruser are going to be immediately and permanently lost.

tedbow’s picture

Status: Needs review » Needs work
FileSize
50.48 KB

Just testing with defaults but presumably this would happen with overrides.

  1. If the layout is locked by another user the the user who sees the lock message still sees the "Save" and "Discard" links
    Lock message still showing save and discard links
    These links also work so the other locked out user can actually discard or save the active users work with these links.

    We don't want to add an access check for locks in \Drupal\layout_builder\Access\LayoutBuilderAccessCheck::access() because this would stop the user from even seeing the Manage layout page with the lock message.

    But we could make another another access checker service and tag routes like we are tagging with '_layout_builder_access'. Maybe '_layout_builder_locked_access'. Then check if the section storage is locked by another user so we don't allow access Save and Discard links.

  2. This is more of an edge case but if 2 users open up the Manage Layout page before either one actually saves a change they will both be able to make changes. This is because the layout builder is loaded via ajax and the lock message will never show.

    I think if we add the new access tag '_layout_builder_locked_access' to all the "add section" "add block", etc routes then this would stop the both users from making changes at the same time. It would be good if we could detect the 403 on the dilag and reroute the page back to manage layout where they would then see the lock message.

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett
Status: Needs work » Postponed

Okay I'm postponing this on #3004536: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features when we can actually manipulate those actions (save, discard, etc) more dynamically.

xjm’s picture

tim.plunkett’s picture

Status: Postponed » Needs work
xjm’s picture

This will need to be updated with:

diff --git a/core/modules/layout_builder/src/LayoutTempstoreRepository.php b/core/modules/layout_builder/src/LayoutTempstoreRepository.php
index a2fad4d5f0..ed61a975c4 100644
--- a/core/modules/layout_builder/src/LayoutTempstoreRepository.php
+++ b/core/modules/layout_builder/src/LayoutTempstoreRepository.php
@@ -49,7 +49,7 @@ public function get(SectionStorageInterface $section_storage) {
    * {@inheritdoc}
    */
   public function getLock(SectionStorageInterface $section_storage) {
-    $id = $section_storage->getStorageId();
+    $id = $this->getKey($section_storage);
     return $this->getTempstore($section_storage)->getMetadata($id);
   }
 

following #3026698: Allow section storage to provide a more granular ID for tempstore.

Edit: Forgot to mention the above is from @tim.plunkett telling me what to type. :D

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
14.86 KB
5.2 KB

Rerolled! Also addressed #36 by hiding the actions if there is a lock.

Before anyone points it out: Yes, there is some duplicated code between OverridesEntityForm and DefaultsEntityForm.
There will likely be more added in future issues.
But refactoring now could jeopardize the ability to refactor it correctly later. I'd rather duplicate it for now than get stuck with something worse.

Status: Needs review » Needs work

The last submitted patch, 41: 3025231-concurrent-41.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
16.57 KB
1.72 KB

We can't 100% know in advance which UID we'll get in the test, so adding an assertion for both. Using == because even though it is documented that the ID will always be an int, sometimes it's a string :(

Status: Needs review » Needs work

The last submitted patch, 43: 3025231-concurrent-43.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
16.85 KB
1.43 KB
tim.plunkett’s picture

Status: Needs review » Needs work

The last submitted patch, 46: 3025231-concurrent-46.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
17.09 KB
2.52 KB

Fixing selectors for the BEM changes

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.

tim.plunkett’s picture

Status: Needs review » Needs work

The last submitted patch, 50: 3025231-concurrent-50.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
18.24 KB
2.9 KB

Fixing the tests

tim.plunkett’s picture

#36.1 is obsolete since those are form buttons now, not routes

This patch addresses #36.2

johnwebdev’s picture

  1. +++ b/core/modules/layout_builder/src/Access/LayoutBuilderLockAccessCheck.php
    @@ -0,0 +1,54 @@
    +  public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
    ...
    +  protected $layoutTempstoreRepository;
    

    The properties should probably be in top of the class.

  2. +++ b/core/modules/layout_builder/src/Access/LayoutBuilderLockAccessCheck.php
    @@ -0,0 +1,54 @@
    +   * Checks for a lock from another user on the layout.
    

    Maybe change to: Checks for a lock on the layout

+++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php
@@ -99,6 +112,27 @@ public function preRender($element) {
+    return \Drupal::service('renderer')->render($message_array);

Should be injected as well(?)

The last submitted patch, 53: 3025231-concurrent-53-FAIL.patch, failed testing. View results

tim.plunkett’s picture

#54
1) Not sure how it ended up in the wrong place! Thanks

2) Sure

3) I really didn't want to inject this because it's stupid that the messenger service doesn't accept render arrays, but I don't think that's going to change any time soon.

johnwebdev’s picture

#56 Those changes looks great!

Did some manual testing. The base functionality works as expected. Awesome!

I think it would be more nice if the message was in the content area, and the other UI elements (i.e. revisions) was hidden and maybe also the "You are editing message", because you aren't really editing it, right? IMO, this can be in a follow-up.

Did some edge case testing as well:

Assume two user's has the same layout opened, but they have not yet done any changes. If one of the user adds the block, they are now the owner. If the second user now tries to add a block they will get a 403 as expected, yay! Though there are no message explaining why, so it just looks like the UI is broken. Should we adress that? Maybe in follow-up?

+++ b/core/modules/layout_builder/tests/src/Functional/Update/TempstoreKeyUpdatePathTest.php
@@ -34,18 +35,28 @@ public function testRunUpdates() {
+    if ($account->id() == 1) {
...
+    else {

Why do we have a if-else here?

  /**
   * Tests the upgrade path for Layout Builder extra fields.
   */

Not showing in patch, but the comment for the test is wrong.

phenaproxima’s picture

  1. +++ b/core/modules/layout_builder/src/Access/LayoutBuilderLockAccessCheck.php
    @@ -0,0 +1,54 @@
    +/**
    + * Checks access based on whether the layout is locked.
    

    The term "locked" could use a bit of explanation here. How about "...is locked (i.e., changed by another user)"?

  2. +++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php
    @@ -57,11 +74,21 @@ class LayoutBuilder extends RenderElement implements ContainerFactoryPluginInter
    +   * @param \Drupal\Core\Session\AccountInterface $current_user
    +   *   The current user.
    +   * @param \Drupal\Core\Render\RendererInterface|null $renderer
    +   *   The renderer.
    

    Even though these are technically optional, not passing them triggers deprecation errors, so they're not truly optional. Therefore, +1 on not starting the descriptions with "(optional)".

  3. +++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php
    @@ -57,11 +74,21 @@ class LayoutBuilder extends RenderElement implements ContainerFactoryPluginInter
    +    $this->currentUser = $current_user;
    

    Why doesn't this parameter also trigger a deprecation notice?

  4. +++ b/core/modules/layout_builder/src/Form/DiscardLayoutChangesForm.php
    @@ -87,6 +106,10 @@ public function getCancelUrl() {
    +    $this->isOwnerCurrentUser = !$lock ? TRUE : $lock->getOwnerId() === $this->currentUser()->id();
    

    I find this logic jarring; there's a weird double-negative introduced by switching on !$lock. I guess this is a nitpick, but ternary expressions really read better (to me, anyway; I'm not sure if I'm in the majority here) if the thing being tested isn't inverted. If it's not too much of an eye-roller, can this be changed? :)

  5. +++ b/core/modules/layout_builder/src/Form/DiscardLayoutChangesForm.php
    @@ -98,7 +121,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    +    // If the user is discarding their own changes, redirect as usual. If they
    +    // are breaking the lock of another user's changes, redirect them back to
    +    // the Layout Builder UI to make their own changes.
    +    $form_state->setRedirectUrl($this->isOwnerCurrentUser ? $this->sectionStorage->getRedirectUrl() : $this->sectionStorage->getLayoutBuilderUrl());
    

    Nice, clear comment. Thanks!

  6. +++ b/core/modules/layout_builder/src/LayoutTempstoreRepositoryInterface.php
    @@ -22,6 +22,17 @@ interface LayoutTempstoreRepositoryInterface {
    +  /**
    +   * Returns the lock object.
    +   *
    +   * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
    +   *   The section storage.
    +   *
    +   * @return \Drupal\Core\TempStore\Lock|null
    +   *   The lock object, or NULL if none exists.
    +   */
    +  public function getLock(SectionStorageInterface $section_storage);
    

    This is a good API addition, but I think it also condemns this patch to 8.8.x and beyond only. So 8.7.x users will need to deal with confusing concurrency when editing layouts. :(

@tim.plunkett also asked me for my opinion on the discussion that took place between #31 and #35. I completely agree with @xjm -- we should not use the terminology of Views UI. In my ideal world, we wouldn't even use the word "lock" in this warning; that's a technical term we'd be exposing to content editors. My preferred wording would be something like this: "This layout is being edited by $other_user. If you edit this layout now, their changes will be lost. [link]Continue editing the layout anyway[/link]."

I like that the break_lock_link stuff is abstracted into a render element, but I don't really like the rigidity of its phrasing. So what if we added a new property to it with a default value that keeps the current wording? Something like:

'#question' => ''This @label is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.'

Using predefined placeholders (like formatPlural() does), calling code could easily customize the phrasing of the link:

$link = [
  '#type' => 'break_lock_link',
  '#question' => 'This layout is being edited by @user. If you edit this layout now, their changes will be lost. <a href=":url">Continue editing the layout anyway</a>.',
];
tim.plunkett’s picture

#57
1) Fixed that here, it drastically changes where we do this.

2) Hmm I think we should decide on that now. It could be really messy in a follow-up.
@TODO

3) That was a debugging thing I think, it can be simplified now.

#58
1) Punting on this for now until we settle on the phrase we're going to use throughout.
@TODO

2/3) This is all changed in response to #57

4) I rewrote it. I think it looks better as you suggest, but more closely matches to the intuitive logic the way I had it. /shrug

6) Nothing we can do about that :(

"7") Okay, I will look into this proposed approach
@TODO

Posting a patch for some intermediate changes, then going back for
#57.3
#58.1
#58.7

Status: Needs review » Needs work

The last submitted patch, 59: 3025231-concurrent-59.patch, failed testing. View results

bnjmnm’s picture

Assigned: tim.plunkett » Unassigned
Status: Needs work » Needs review
FileSize
19.52 KB
26.81 KB
35.63 KB

In #59 there were test failures in LayoutBuilderUiTest and LayoutBuilderTest... fixed half of them

Fixed LayoutBuilderUiTest by changing LayoutRebuildTrait to refresh the entire entity form, not just the layout builder render element. This required a decent amount of refactoring, which is what the majority of changes in this patch came from. It also surfaced a problem reported in #3045171: Form blocks rendered inside layout builder break save, where adding the search block to a layout would prevent the layout from being saveable. The solution in this patch fixes the issue without removing the form from the Layout Builder UI.

I was not able to fix LayoutBuilderTest::testConcurrentEditing(), but I was able to narrow down it down enough that someone may be able to identify the problem. The test fails when a user unlocks the layout to make it editable, but when they return to the layout it is still locked by the other user. This does not happen with manual testing, and I added assertions to the test that confirm the layout's temp storage has been cleared. To rule out a timing issue, I tried refreshing the page and adding a pause before going to the Layout Builder UI, but the test still thinks the layout is locked.

There are also some changes where I moved code shared by DefaultSectionStorage and OverridesSectionStorage into a shared trait.

tim.plunkett’s picture

  1. +++ b/core/modules/layout_builder/src/Controller/LayoutRebuildTrait.php
    @@ -40,11 +40,19 @@ protected function rebuildAndClose(SectionStorageInterface $section_storage) {
    +    $context_value = $section_storage->getPluginId() === 'overrides' ? 'entity' : 'display';
    +    $entity = $section_storage->getContextValue($context_value);
    

    Soooo we definitely can't do that. But I'm not sure what to do instead....

  2. +++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
    @@ -102,6 +102,14 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
    +      if (isset($content['#type']) && $content['#type'] === 'form' && $event->inPreview()) {
    +        unset($content['#theme_wrappers']);
    +      }
    

    Nice!

  3. +++ b/core/modules/layout_builder/src/Form/DefaultsEntityForm.php
    @@ -95,39 +90,13 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
    +    $form['#attributes']['data-drupal-layout-builder-entityform'] = "";
    

    Weird double quotes. Could use a comment

  4. +++ b/core/modules/layout_builder/src/Form/DefaultsEntityForm.php
    @@ -95,39 +90,13 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
    +    $form['#action'] = $section_storage->getLayoutBuilderUrl()->toString();
    
    +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
    @@ -113,38 +108,13 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
    +    $form['#action'] = $this->entity->toUrl('canonical')->toString() . '/layout';
    

    Can't this use getLayoutBuilderUrl in both?

bnjmnm’s picture

This addresses all the items in #62

#62.1 created a FormEditableSectionStorageInterface interface with a method for getting the entity directly from section storage. For section storage instances that don't implement the interface, the rebuild process after an ajax update will rebuild layout builder UI as opposed to the entire entity form

#62.3 the weird double quotes were just the byproduct of fatigue -- changed to single.

#62

Can't this use getLayoutBuilderUrl in both?

yeppp... (see above)

Still not sure how to address the failure in \Drupal\Tests\layout_builder\Functional\LayoutBuilderTest::testConcurrentEditing, another set of eyes on that would be appreciated. Manual tests work, and the test confirms the tempstore is emptied, but the UI within the test does not reflect that.

Status: Needs review » Needs work

The last submitted patch, 63: 3025231-63.patch, failed testing. View results

bnjmnm’s picture

Status: Needs work » Needs review
FileSize
8.74 KB
42.22 KB

Tests were failing because Layout Builder element was set to be uncacheable, but not the entity forms. Dynamic page cache was retaining the markup generated while locked.

I also expanded the concurrent editing test to cover overrides in addition to defaults and removed a method I added in a previous patch that wasn't supposed to be there to begin with (artifact of some earlier attempts at changing the AJAX refresh)

Status: Needs review » Needs work

The last submitted patch, 65: 3025231-65.patch, failed testing. View results

bnjmnm’s picture

Status: Needs work » Needs review
FileSize
4.92 KB
90.5 KB

Corrected unit tests to reflect the changed cache config for layout entity form routes.

bnjmnm’s picture

#67 is a mistake as the branch was not rebased. Here is the corrected patch, interdiffed from #65

effulgentsia’s picture

I haven't read or tested this patch yet to know if it applies to the Layout tab of an entity that's using overrides. Under the assumption that it does, I added #3063373: Add content locking (anti-concurrent editing) to core? to the Ideas queue.

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.

tim.plunkett’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Needs a reroll

rpayanm’s picture

Status: Needs work » Needs review
FileSize
48.03 KB
rpayanm’s picture

Issue tags: -Needs reroll

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.

tim.plunkett’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

And another one needed. I will try to review more promptly this time.

mrinalini9’s picture

Assigned: Unassigned » mrinalini9
mrinalini9’s picture

Assigned: mrinalini9 » Unassigned
Status: Needs work » Needs review
FileSize
45.62 KB

Rerolled patch for 9.1.x, please review.

Status: Needs review » Needs work

The last submitted patch, 77: 3025231-77.patch, failed testing. View results

sanjayk’s picture

Assigned: Unassigned » sanjayk
sanjayk’s picture

Assigned: sanjayk » Unassigned

Little bit busy in next couple of days. Later will plan to work on it.

deepakaryan1988’s picture

Assigned: Unassigned » deepakaryan1988
Status: Needs work » Active
deepakaryan1988’s picture

tim.plunkett’s picture

Status: Active » Needs work

Active means there is no patch yet.

rpayanm’s picture

Status: Needs work » Needs review
FileSize
45.42 KB
tim.plunkett’s picture

Status: Needs review » Needs work

@rpayanm Please include an interdiff when changing a patch and include a comment of some sort explaining your changes.

If it's a reroll and you didn't make other changes, please say that, and remove the `needs reroll` tag

rpayanm’s picture

Issue tags: -Needs reroll

@tim.plunkett It was a reroll.

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.

arshadkhan35’s picture

Re-rolling #72, as patch was failing against 8.9.x.

tim.plunkett’s picture

Assigned: deepakaryan1988 » Unassigned
Status: Needs work » Needs review

The patch needs to be rolled against 9.2.x (the current dev branch) for this issue to make any progress.

arshadkhan35’s picture

Patch #84 works with 8.9.x as well, please ignore #88 re-roll.

I have applied #84 and it seems to be working, however if we use this patch along with layout_builder_modal module, it introduces an issue where modal popup on layout while adding or updating block does not close.

Steps to reproduce:

  • On fresh drupal install apply the patch #84
  • Install and enable content_lock and layout_builder_modal module
  • You can enable content lock for layout by visiting admin/config/content/content_lock.
  • Go to layout builder page of a node and add a block ,a modal popup will open
  • Try to save the modal, the data in the background saved but modal popup does not close.
sanjayk’s picture

Re-roll patch for 9.2

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett
Status: Needs review » Needs work

A bit of code was lost in the last reroll. I'm going to tackle this myself, as it's much more confusing now after #3143635: Change Layout Preparation into an Event to allow proper alterations

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned
Status: Needs work » Needs review
Issue tags: -Trigyn-WeeklyCodeSprint +Needs tests

#3045171: Form blocks rendered inside layout builder break save made this MUCH simpler by moving the Layout UI out of the form itself. I was able to remove all of the entity form parts of this (I hope that was correct, but it's in the git history now so not impossible to revert)

This could do with a FunctionalJavascript test, so restoring the tag for that.

tim.plunkett’s picture

Hiding old patches now that this is using a Merge Request

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett
Status: Needs review » Needs work

Hmm, I was wrong. One of my changes broke this, so I will work to fix it.

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.

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.

Amber Himes Matz’s picture

Looks like the MR needs a reroll (rebase).

larowlan’s picture

Does this still need tests? Can that tag be removed.

For the re-roller, please try to debug the test fails too rather than a straight re-roll.

larowlan’s picture

We also need type-hints and return type-hints here on the new code.

Didn't comment on each line that needed it in the MR, because it applies to all new methods.

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.

quietone made their first commit to this issue’s fork.

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.

NivethaSubramaniyan’s picture

Creating patch for 10.1

Nitin shrivastava’s picture

try to fix #105

Bhanu951 made their first commit to this issue’s fork.

Bhanu951’s picture

@timplunkett
Can you pls change the target branch to 10.1.x for this MR #322 rerolled it to 10.1.x branch.

As it has review comments.

andypost made their first commit to this issue’s fork.

andypost’s picture

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.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned