Problem/Motivation

Since 4.2.0 (commit 3f86fe82, released as the fix for #3346580) ParagraphBlocksEntityManager::getRefererEntity() reloads the host entity from the database through loadFreshEntity(), so that paragraphs added on the node form become available in the Layout Builder block listing.

loadFreshEntity() does not load the latest revision though. It asks for the latest translation affected revision, while the comment directly above it states a different intent:

// Load the latest revision directly from the database.
if ($entity->getEntityType()->isRevisionable()) {
  $latest_revision_id = $storage->getLatestTranslationAffectedRevisionId(
    $entity->id(),
    $entity->language()->getId()
  );

Those are not the same revision. revision_translation_affected is only set when core detects a change for that translation, and appending an item to an entity_reference_revisions field usually does not qualify: EntityReferenceRevisionsFieldItemList::hasAffectingChanges() returns during its first loop iteration whenever the referenced entity has a translation in the given langcode, so items after delta 0 are never inspected. If delta 0 is unchanged, the whole field reports "no changes" and ContentEntityStorageBase::populateAffectedRevisionTranslations() leaves the flag empty on that revision.

As a result loadFreshEntity() returns a revision that predates the paragraph the editor just added, and the "Choose a block" listing is built from that stale revision. To editors the behaviour looks arbitrary, because a save that also changes a translatable field, or a "Save layout" in Layout Builder (which writes layout_builder__layout and is compared normally), does set the flag and makes the listing catch up. That also explains why the workaround reported in #3346580 (save the layout first) still appears to help.

Revision data from a site where an editor added one paragraph per save:

rev   affected  paragraphs  changed fields
1693  yes       1           (node created)
1694  yes       1           field_paragraphs, layout_builder__layout
1695  no        2           field_paragraphs
1696  no        2           field_paragraphs
1697  no        3           field_paragraphs
1698  no        3           field_paragraphs

getLatestTranslationAffectedRevisionId() returns revision 1694 with one paragraph, while the current revision 1698 holds three. Only that one paragraph is offered in the block listing, no matter how many are added afterwards.

Reproduced on Drupal 11.3.16, paragraph_blocks 4.2.2, paragraphs 1.21.0, entity_reference_revisions 1.14.0, layout_paragraphs 2.1.3. Content moderation and Workspaces are not enabled.

Steps to reproduce

1. Enable Layout Builder overrides on a node type that has a multi-value paragraph field, and enable that field for paragraph_blocks including "Allow each paragraph".
2. Create a node with one paragraph, give it an admin title, save.
3. Open the Layout tab, place the paragraph block, save the layout.
4. Edit the node, append a second paragraph with an admin title, and save. Do not change the title, body or any other translatable field.
5. Open the Layout tab and click "Add block". The second paragraph is missing from the listing.

The revision lookup can be compared directly:

drush php:eval '$s = \Drupal::entityTypeManager()->getStorage("node");
print $s->getLatestTranslationAffectedRevisionId(NID, "en") . " vs " . $s->getLatestRevisionId(NID) . "\n";'

Proposed resolution

Use getLatestRevisionId(), which is what the comment already describes. See merge request.

Nothing in this code path is language specific. ParagraphBlocksLabeller never calls getTranslation(), and loadRevision() returns the revision in its source language, so the langcode only ever selected a revision, it never influenced which field values were read.

The two other callers of loadFreshEntity(), in ParagraphBlock::build() and ParagraphBlock::blockSubmit(), only use it as a fallback when the paragraph is not found on the context entity. There the newest revision can only contain more paragraphs than an older one, never fewer. The one behavioural difference is that a paragraph removed in a later revision is no longer resurrected from an older revision, which seems correct.

Remaining tasks

- Review
- Automated test coverage for appending a paragraph and asserting it appears in the block listing

User interface changes

None, other than the block listing now offering every paragraph in the field.

API changes

None. The signature and return type of loadFreshEntity() are unchanged.

Data model changes

None.

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:

Comments

matsalkemade created an issue. See original summary.

matsalkemade’s picture

Status: Active » Needs review
matsalkemade’s picture

MR !34 opened.

Verified on Drupal 11.3.16 with paragraph_blocks 4.2.2, paragraphs 1.21.0 and entity_reference_revisions 1.14.0. Content moderation and Workspaces are not enabled, so the latest revision is always the default revision on this site.

The test node had five paragraphs, each added in a separate save through the node form. Its current revision was 1702, while getLatestTranslationAffectedRevisionId() returned 1700. Requesting layout_builder.choose_block for that node as the same non-admin editor account, with only this change differing:

- before: four of the five paragraphs are listed, the one added in the most recent save is missing
- after: all five are listed

The same request also confirmed the listing is identical for an administrator and for a role without any administrative permissions, so this is not a permission issue even though it presents like one to editors: whether the newly added paragraph shows up depends on whether that particular save happened to set revision_translation_affected, not on who saved it.

basvredeling’s picture

Assigned: Unassigned » basvredeling
basvredeling’s picture

Hi @matsalkemade
thanks for you contribution. I've added another commit to the MR to address code quality issues. Could you double check / test please?

tim_dj’s picture

Status: Needs review » Reviewed & tested by the community

Code looks good and works fine in our environment.

basvredeling’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

basvredeling’s picture

Status: Fixed » Closed (fixed)

This is in release 4.2.3