Verified Commit 8672c435 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3053881 by Luke.Leber, bkosborne, tyler-paavola, Sam152, Rishi...

Issue #3053881 by Luke.Leber, bkosborne, tyler-paavola, Sam152, Rishi Kulshreshtha, Odai Atieh, chrisolof, acbramley, ethomas08, Oscaner, kyberman, aarti zikre, joshua1234511, larowlan, catch: Reverting entity revisions that contain custom blocks erroneously triggers EntityChangedConstraint

(cherry picked from commit 7362bc50)
parent 24dc4ef8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -66,6 +66,13 @@ function block_content_entity_type_alter(array &$entity_types) {
    $translation['block_content'] = TRUE;
    $entity_types['block_content']->set('translation', $translation);
  }

  // Swap out the default EntityChanged constraint with a custom one with
  // different logic for inline blocks.
  $constraints = $entity_types['block_content']->getConstraints();
  unset($constraints['EntityChanged']);
  $constraints['BlockContentEntityChanged'] = NULL;
  $entity_types['block_content']->setConstraints($constraints);
}

/**
+7 −0
Original line number Diff line number Diff line
@@ -13,3 +13,10 @@ function block_content_removed_post_updates() {
    'block_content_post_update_add_views_reusable_filter' => '9.0.0',
  ];
}

/**
 * Clear the entity type cache.
 */
function block_content_post_update_entity_changed_constraint() {
  // Empty post_update hook.
}
+17 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\block_content\Plugin\Validation\Constraint;

use Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraint;

/**
 * Validation constraint for the block content entity changed timestamp.
 *
 * @Constraint(
 *   id = "BlockContentEntityChanged",
 *   label = @Translation("Block content entity changed", context = "Validation"),
 *   type = {"entity"}
 * )
 */
class BlockContentEntityChangedConstraint extends EntityChangedConstraint {
}
+30 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\block_content\Plugin\Validation\Constraint;

use Drupal\block_content\BlockContentInterface;
use Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraintValidator;
use Symfony\Component\Validator\Constraint;

/**
 * Validates the BlockContentEntityChanged constraint.
 */
class BlockContentEntityChangedConstraintValidator extends EntityChangedConstraintValidator {

  /**
   * {@inheritdoc}
   */
  public function validate($entity, Constraint $constraint) {
    // This prevents saving an update to the block via a host entity's form if
    // the host entity has had other changes made via the API instead of the
    // entity form, such as a revision revert. This is safe, for example, in the
    // Layout Builder the inline blocks are not saved until the whole layout is
    // saved, in which case Layout Builder forces a new revision for the block.
    // @see \Drupal\layout_builder\InlineBlockEntityOperations::handlePreSave.
    if ($entity instanceof BlockContentInterface && !$entity->isReusable()) {
      return;
    }
    parent::validate($entity, $constraint);
  }

}
+39 −0
Original line number Diff line number Diff line
@@ -669,4 +669,43 @@ public function testEditInlineBlocksPermission() {
    $assert($permissions, TRUE);
  }

  /**
   * Test editing inline blocks when the parent has been reverted.
   */
  public function testInlineBlockParentRevert() {
    $this->drupalLogin($this->drupalCreateUser([
      'access contextual links',
      'configure any layout',
      'administer node display',
      'administer node fields',
      'administer nodes',
      'bypass node access',
      'create and edit custom blocks',
    ]));
    $display = \Drupal::service('entity_display.repository')->getViewDisplay('node', 'bundle_with_section_field');
    $display->enableLayoutBuilder()->setOverridable()->save();
    $test_node = $this->createNode([
      'title' => 'test node',
      'type' => 'bundle_with_section_field',
    ]);

    $this->drupalGet("node/{$test_node->id()}/layout");
    $this->addInlineBlockToLayout('Example block', 'original content');
    $this->assertSaveLayout();
    $original_content_revision_id = Node::load($test_node->id())->getLoadedRevisionId();

    $this->drupalGet("node/{$test_node->id()}/layout");
    $this->configureInlineBlock('original content', 'updated content');
    $this->assertSaveLayout();

    $this->drupalGet("node/{$test_node->id()}/revisions/$original_content_revision_id/revert");
    $this->submitForm([], 'Revert');
    $this->drupalGet("node/{$test_node->id()}/layout");
    $this->configureInlineBlock('original content', 'second updated content');
    $this->assertSaveLayout();

    $this->drupalGet($test_node->toUrl());
    $this->assertSession()->pageTextContains('second updated content');
  }

}