eca_workflow: From state in transition seems to be always the default revision

Created on 2 March 2023, over 1 year ago
Updated 27 March 2023, over 1 year ago

Problem/Motivation

The event is not triggered when there is a published version and revision with example moderation state review and you want that the event triggers when transition review -> published.

Steps to reproduce

Create state draft/review/published with transition draft/published -> review, draft/review -> publish.

Proposed resolution

Use the latest revision for from_state to compare with to_state in \Drupal\eca_workflow\HookHandler::transition.

📌 Task
Status

Closed: works as designed

Version

1.1

Component

Code

Created by

🇩🇪Germany tobiasb Berlin

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Issue created by @tobiasb
  • 🇩🇪Germany jurgenhaas Gottmadingen

    Haven't tried to reproduce this yet, but \Drupal\eca_workflow\HookHandler::transition gets triggered by the hook implementation eca_workflow_entity_update which receives the entity from Drupal core. That entity must be the latest revision already, isn't it? Because that's the one that got updated and will be transitioned from some state to published. If I misunderstood, could you please explain in a bit more detail, please?

  • 🇩🇪Germany tobiasb Berlin

    The entity is fine. I changed the title I hope the makes it more clear.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    Should that be "... the default state" instead of revision?

  • 🇩🇪Germany tobiasb Berlin

    original is set via \Drupal\Core\Entity\EntityStorageBase::doPreSave, but the load in \Drupal\Core\Entity\EntityStorageBase::loadUnchanged loads the default entity revision.

    Which means the from_state is not the state from the revision before you change it.

    For Nodes this works:

          $from_state = NULL;
          /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
          $storage = \Drupal::entityTypeManager()
            ->getStorage($entity->getEntityTypeId());
          $revision_ids = $storage->revisionIds($entity);
          if ($revision_ids) {
            // We need to remove the revision_id which is same as from the entity.
            array_pop($revision_ids);
            $last_revision_id = end($revision_ids);
            if ($entity->getRevisionId() != $last_revision_id) {
              $last_revision = $storage->loadRevision($last_revision_id);
              $from_state = $last_revision->get('moderation_state')
                ->getString();
            }
          }
    
  • 🇩🇪Germany jurgenhaas Gottmadingen

    OK, here is how this can be reproduced:

    • New Drupal site with content moderation enabled and configured
    • Create new node in draft
    • Transition that node from draft to published: from state is draft and to state is published, as expected
    • Edit node, creates new revision, save in draft state
    • Transition that node from draft to published again: from state is published and to state is published, here is the error

    I will next try to reproduce this without ECA being installed and see if this is still the same. If so, I'd presume this to be a core issue.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    Confirmed, this is the same when just working with plain Drupal 10, fresh installation.

    So, the update hook from core provides the content entity including its original as well. Our expectation is that this original contains the entity revision from right before updating the given entity. Instead, origin contains the latest published revision, i.e. the default revision.

    If I go that right, this seems to be a core bug, and we should report that upstream rather than trying to build a workaround.

  • 🇩🇪Germany tobiasb Berlin

    I do not believe this is a core issue. The fact is just that original is set via load not loadRevision as you can read in entity.api.php.

    This would be more a feature-request that drupal provides the revision before it creates/saves the new revision.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    I tend to disagree on that judgement. The hook_entity_update states this: "Get the original entity object from $entity->original."

    What else should a developer expect than finding the entity's version prior to being updated?

    Getting the "wrong" original has far bigger impact, e.g. when we want to find out if a field value has changed, we also compare the field value from the entity to the field value from the original. That will also provide wrong results in such scenarios. I'm pretty sure that other use cases of this hook will have fallen into the same trap.

    Maybe this hasn't been recognized before, because the revisions are not the problem - this only ever happens when content moderation is being used. I'll ask that question in Slack to see what other think about this, so we get a better view whether my expectation is the issue or if it's really a core issue.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    Here is the thread in Slack: https://drupal.slack.com/archives/C1BMUQ9U6/p1677844022810869

    Only last week, @catch committed a similar looking fix, which was just about 6 years old, to Drupal 10.1 in 🐛 Impossible to update an entity revision if the field value you are updating matches the default revision. Fixed and published this change record .

    Unfortunately, this was the right thing to do, but it doesn't cover the use case in \Drupal\Core\Entity\EntityStorageBase::doPreSave which then affects the usage of original in hook_entity_update. To also resolve that, the following change is required in that method:

        // Load the original entity, if any.
        if ($id_exists && !isset($entity->original)) {
    -      $entity->original = $this->loadUnchanged($id);
    +     $entity->original = $this->loadRevision($entity->getLoadedRevisionId());
        }
    

    That resolves the issue and has been tested. We don't know yet if and when this also goes into Drupal core, but we should hold off with our decision what to do until we know how this will be handled.

  • Status changed to Closed: works as designed over 1 year ago
  • 🇩🇪Germany jurgenhaas Gottmadingen

    Closing this as "Works as designed" as we've identified this as a core bug and hope to push this over the finish line in the referenced issue.

  • First commit to issue fork.
Production build 0.71.5 2024