1

I am working on a project with a non-standard editorial workflow using Drupal 8 and Content Moderation.

The workflow currently has the following states:

  • draft
  • pre-published
  • awaiting review
  • published

A content creator can pre-publish a content piece (node), which can then be shared and embedded in other sites, but will not appear on the project's site. For a content to appear on the site and be visible by other users, a review must be requested (put in awaiting review state) and then validated by an administrator (put in published state). The pre-published state does not set the node's status to 1, only the published state does so.

This workflow was fairly straightforward to setup, but causes issues with drafts that are created between the awaiting review and the published states. It is, in fact, important that the author has the possibility to continue working on his content while an old revision is awaiting review. Once approved, it is that old revision that gets published, and a new request for review needs to be submitted if the author wants to publish a more recent revision.

To allow drafts to continue being created, I have forced moderation state changes from awaiting review to published to not create a new revision using setSyncing.

This ensures that new drafts appear in the right order (after the reviewed revision), and can be further edited.

This works fine, but if a draft is created after the revision that is being reviewed, it sometimes becomes the default/current revision. This seems to happen because the vid field in the node table is not updated when publishing an existing revision. I have tried tracing the code that is responsible for updating the vid field in the node table using Xdebug, but couldn't.

I know I could eventually update the node table directly, but I would hate to have to do that, as I don't know the implications this may have.

Is there an API responsible for letting Drupal if it should update this field or not?

Any help would be highly appreciated!

Below is the custom code responsible for moving a revision from the awaiting review to published state, which happens in a custom form that inherits from NodeForm:

public function save(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\node\NodeInterface $node */
    $node = $this->getEntity();

    $is_default_published = $this->moderationInfo->isDefaultRevisionPublished($node);

    // Prevent the creation of a new revision.
    $node->setSyncing(TRUE);

    // Update moderation state.
    $node->set('moderation_state', 'published');

    if ($form_state->has('original_changed')) {
      // Reset changed time to original value.
      $node->setChangedTime($form_state->get('original_changed'));
    }

    $node->save();

    // Set draft forward revisions as non-default.
    if (!$is_default_published) {
      $vid = $node->getRevisionId();

      /** @var \Drupal\node\NodeStorage $node_storage */
      $node_storage = $this->entityTypeManager->getStorage($node->getEntityTypeId());
      foreach ($node_storage->revisionIds($node) as $revision_vid) {
        if ($revision_vid > $vid) {
          /** @var \Drupal\node\entity\Node $revision */
          $revision = $node_storage->loadRevision($revision_vid);

          if (!$revision->isPublished()) {
            $revision->setSyncing(TRUE);
            $revision->isDefaultRevision(FALSE);

            /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
            $entity_type = $revision->getEntityType();

            // Force updating the "revision_default" flag,
            // it is otherwise only updated for new revisions
            // see \Drupal\Core\Entity\ContentEntityStorageBase::doSave.
            $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default');
            $revision->set($revision_default_key, $revision->isDefaultRevision());

            // NOTE: the vid in the node table is not updated !!

            $revision->save();
          }
        }
      }
    }

    $form_state->setRedirectUrl($this->getCancelUrl());
  }
0

To answer my own question, the code responsible for updating the vid field in the node table is in Drupal\Core\Entity\Sql\SqlContentEntityStorage::doSaveFieldItems.

In my particular case, it's line 937.

To fix my issue, I actually simply needed to add the following line before saving the moderated revision (after $node->set('moderation_state', 'published')):

$node->isDefaultRevision(TRUE);

Although the field revision_default was already set to 1 in the node_revision table, the isDefaultRevision flag needs to be set to TRUE in order for the code in Drupal\Core\Entity\Sql\SqlContentEntityStorage::doSaveFieldItems to update the node and node_field_data tables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.