Message "One or more entities were skipped as they are under moderation and may not be directly published or unpublished."

Using Drupal 8.1.1
and Workbench Moderation 8.X-1.0

Comments

gpandher created an issue. See original summary.

gpandher’s picture

Title: Schedular do not work with Workbench Moderation » Scheduler module does not work with Workbench Moderation module
jonathan1055’s picture

Project: Scheduler » Workbench Moderation
Version: 8.x-1.0-alpha1 » 8.x-1.0
Category: Bug report » Support request
Priority: Critical » Normal

Hi gpandher,

The message "One or more entities were skipped as they are under moderation and may not be directly published or unpublished" is produced by the workbench_moderation module. Scheduler cannot control that. It does not sound like a bug at all, but if you think that it is you need to tell us why, and explain a bit more. I have moved this to the Workbench Moderation issue queue as a normal (not critical) support request.

Jonathan
Scheduler maintainer

gpandher’s picture

My Use Case is to use moderation states as well as scheduler.
Is there any existing way, these two modules can be used simultaneously?

agentrickard’s picture

Version: 8.x-1.0 » 8.x-1.x-dev
Category: Support request » Feature request

I suspect this is a feature request.

in D7 we used Revision Scheduler to get around this issue.

From the WBM D7 Readme:

"Note that when an entity is under moderation by this module, explicitly setting its published state (on nodes) or its "make default revision" value (on all entities) will have no effect, because WBM will always overwrite those values based on your configured State rules. That is most notable with regards to core's Publish and Unpublish actions for Bulk Operations on nodes, as seen on /admin/content. Those actions will simply have no effect on a moderated node."

That's what is happening here as well, though I thought there was a D8 solution for it.

agentrickard’s picture

@jonathan1055 If you want to add this to Scheduler, I'm sure we can provide documentation for how to accommodate the feature.

agentrickard’s picture

Project: Workbench Moderation » Scheduler

@johnathon1055 Just popping this back to get your feedback.

It looks like we can hook in to the Events fired by Scheduler to change this behavior:

      // Trigger the PRE_PUBLISH event so that modules can react before the node
      // is published.
      $event = new SchedulerEvent($node);
      $dispatcher->dispatch(SchedulerEvents::PRE_PUBLISH, $event);
      $node = $event->getNode();

And then again for:

      // Trigger the PRE_UNPUBLISH event so that modules can react before the
      // node is unpublished.
      $event = new SchedulerEvent($node);
      $dispatcher->dispatch(SchedulerEvents::PRE_UNPUBLISH, $event);
      $node = $event->getNode();

That would allow us to add the necessary node data which would allow scheduler to complete this action.

But the $node property on SchedulerEvent is protected. We need to alter that node data to add moderation information.

Can SchedulerEvent::setNode() be fired to do that by an event listener? Or do we have to make $node public?

agentrickard’s picture

For the O.P. -- I think you can use https://www.drupal.org/project/scheduled_updates

jonathan1055’s picture

Hi agentrickard,
If I have understood you correctly, you should be able to update the node data directly within your implementation of the event listeners, by calling $event->setNode(). Here is a snippet from our simpletest API test module, in file /scheduler/tests/modules/src/EventSubscriber.php

class EventSubscriber implements EventSubscriberInterface {

  /**
   * Operations to perform before Scheduler publishes a node.
   *
   * @param \Drupal\scheduler\SchedulerEvent $event
   */
  public function prePublish(SchedulerEvent $event) {
    /** @var Node $node */
    $node = $event->getNode();
    // Before publishing a node make it sticky.
    if (!$node->isPublished() && strpos($node->title->value, 'API TEST') === 0) {
      $node->setSticky(TRUE);
      $event->setNode($node);
    }
  }

This works, the node sticky attribute gets set on. Other node data could be updated too, I presume.
Or maybe I have mis-understood your question.

Jonathan

agentrickard’s picture

Project: Scheduler » Workbench Moderation

Nope. That was my question. I wasn't certain that making $node protected in your class prevented me from updating the node properties and then using the setter as you are showing.

Thank you.

jonathan1055’s picture

#2841741: How to use Scheduler 8.x with revisions by Workbench Moderation is a new issue on the Scheduler queue. It would be very nice to get Scheduler and Workbench Moderation to work better together.

joshua.boltz’s picture

@agentrickard,
Scheduled Updates module does not seem to be playing nice with Workbench Moderation either.
Scheduler and Scheduled Updates are the 2 modules I've been considering for adding scheduled content capabilities.

The main issue I noticed with Scheduled Updates module is, when having Workbench Moderation enabled on a content type, that when the scheduled job runs via cron, it says it updated, the node shows an updated Updated timestamp, but the actual publish status does not update. This also is an issue when trying to schedule other things like unpublish, promote, unpromote, etc.

With WBM disabled on the node type, all of those things can be scheduled and complete successfully.

jonathan1055’s picture

Project: Workbench Moderation » Scheduler Workbench Integration
Version: 8.x-1.x-dev » 7.x-1.x-dev
Related issues: +#2820026: Drupal 8 version of Scheduler Workbench Integration, +#2841741: How to use Scheduler 8.x with revisions by Workbench Moderation

Moving this to the Scheduler Workbench Integration queue.

See also #2820026: Drupal 8 version of Scheduler Workbench Integration for the start of the port to 8.x

achap’s picture

I wrote a custom module for a client project recently. There is no 8.x-1.x branch yet so I can't create a patch but I can post some code snippets that I wrote that could maybe help you get started porting the module.

<?php

namespace Drupal\scheduler_workbench_integration\EventSubscriber;

use Drupal\scheduler\SchedulerEvent;
use Drupal\scheduler\SchedulerEvents;
use Drupal\workbench_moderation\Event\WorkbenchModerationEvents;
use Drupal\workbench_moderation\Event\WorkbenchModerationTransitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Class SchedulerWorkbenchIntegrationSubscriber.
 *
 * This class is an event subscriber that subscribes to events defined by the scheduler and workbench_moderation modules.
 * The reason this module exists is because when integrating with workbench_moderation,
 * scheduler is not able to publish and un publish entities. Instead we must set the state, according
 * to what is defined by workbench moderation.
 *
 */
class SchedulerWorkbenchIntegrationSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {

    // Here we are subscribing to the PRE_UNPUBLISH event and defining our own
    // callback function onPreUnpublish with a weight of 800. Weight affects when it
    // gets called, because other modules can also subscribe.
    $events[SchedulerEvents::PRE_UNPUBLISH] = array('onPreUnpublish', 800);

    // Same same but different for the PRE_PUBLISH event.
    $events[SchedulerEvents::PRE_PUBLISH] = array('onPrePublish', 800);

    // Same same but different for the STATE_TRANSITION event which is fired when workbench_moderation's
    // state changes.
    $events[WorkbenchModerationEvents::STATE_TRANSITION] = array('onStateTransition', 800);

    return $events;

  }

  /**
   * Callback when the PRE_UNPUBLISH event is fired by scheduler module.
   *
   * @param SchedulerEvent $event - The event defined by the scheduler module.
   *
   * When scheduler executes the code to unpublish the node, workbench_moderation will complain with this message:
   * "One or more entities were skipped as they are under moderation and may not be directly published or unpublished."
   * Ignore that message because our custom code below archives the node.
   */
  public function onPreUnpublish(SchedulerEvent $event) {

    // SchedulerEvent has a getNode method which gets the node it is acting on.
    $node = $event->getNode();

    // Set the node moderation state to archived.
    $node->set('moderation_state', 'archived');

    // Now use SchedulerEvent's setNode when we are done with our node object.
    $event->setNode($node);

  }

  /**
   * Callback when the PRE_PUBLISH event is fired by scheduler module.
   *
   * @param SchedulerEvent $event - The event defined by the scheduler module.
   */
  public function onPrePublish(SchedulerEvent $event) {

    // SchedulerEvent has a getNode method which gets the node it is acting on.
    $node = $event->getNode();

    // Set the node moderation state to archived.
    $node->set('moderation_state', 'published');
    $node->set('status', 1);

    // Now use SchedulerEvent's setNode when we are done with our node object.
    $event->setNode($node);

  }

  /**
   *
   * Callback when the STATE_TRANSITION event is fired by the workbench_moderation module.
   *
   * On a cron run, this event fires after PRE_PUBLISH, so will overwrite any changes you made to the node
   * in that callback.
   *
   * @param WorkbenchModerationTransitionEvent $event - The event defined by the workbench_moderation module.
   */
  public function onStateTransition(WorkbenchModerationTransitionEvent $event) {

    $node = $event->getEntity();

    // If the node is not null
    // and it has the publish_on field
    // and the publish_on field is not null
    // and the node's before state was not published (i.e. archived, draft)
    // and the after state is published
    if (isset($node) && $node->hasField('publish_on')) {
      $publish_on = $node->get('publish_on')->getValue();
      if (
        isset($publish_on[0]['value']) &&
        $publish_on[0]['value'] !== null &&
        $event->getStateBefore() !== 'published' &&
        $event->getStateAfter() === 'published'
      ) {
        // Set the moderation_state to draft and the published status to 0
        // because this node is scheduled to be published in the future and we
        // don't want to publish it yet.
        $node->set('moderation_state', 'draft');
        $node->set('status', 0);
      }
    }
  }

}

And then in services.yml

services:
  scheduler_workbench_integration.subscriber:
    class: Drupal\scheduler_workbench_integration\EventSubscriber\SchedulerWorkbenchIntegrationSubscriber
    arguments: []
    tags:
      - { name: event_subscriber }