You are here

class TransitionManager in Lightning Workflow 8.2

Same name and namespace in other branches
  1. 8.3 modules/lightning_scheduler/src/TransitionManager.php \Drupal\lightning_scheduler\TransitionManager

Hierarchy

Expanded class hierarchy of TransitionManager

2 files declare their use of TransitionManager
ModerationStateWidget.php in modules/lightning_scheduler/src/Plugin/Field/FieldWidget/ModerationStateWidget.php
TransitionManagerTest.php in modules/lightning_scheduler/tests/src/Kernel/TransitionManagerTest.php
1 string reference to 'TransitionManager'
lightning_scheduler.services.yml in modules/lightning_scheduler/lightning_scheduler.services.yml
modules/lightning_scheduler/lightning_scheduler.services.yml
1 service uses TransitionManager
lightning_scheduler.transition_manager in modules/lightning_scheduler/lightning_scheduler.services.yml
\Drupal\lightning_scheduler\TransitionManager

File

modules/lightning_scheduler/src/TransitionManager.php, line 17

Namespace

Drupal\lightning_scheduler
View source
class TransitionManager {
  use StringTranslationTrait;

  /**
   * The moderation information service.
   *
   * @var \Drupal\content_moderation\ModerationInformationInterface
   */
  protected $moderationInformation;

  /**
   * The currently logged-in user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The logger channel.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected $logger;

  /**
   * TransitionManager constructor.
   *
   * @param ModerationInformationInterface $moderation_information
   *   The moderation information service.
   * @param AccountInterface $current_user
   *   The currently logged-in user.
   * @param EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param LoggerChannelInterface $logger
   *   The logger channel.
   * @param TranslationInterface $translation
   *   (optional) The string translation service.
   */
  public function __construct(ModerationInformationInterface $moderation_information, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, LoggerChannelInterface $logger, TranslationInterface $translation = NULL) {
    $this->moderationInformation = $moderation_information;
    $this->currentUser = $current_user;
    $this->entityTypeManager = $entity_type_manager;
    $this->logger = $logger;
    if ($translation) {
      $this
        ->setStringTranslation($translation);
    }
  }

  /**
   * Returns an array of available workflow states for an entity.
   *
   * A workflow state is considered "available" if the current user has
   * permission to use or schedule it.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity which has the workflow.
   *
   * @return array
   *   An associative array where the keys are the workflow state IDs, and the
   *   values are the states' human-readable labels.
   */
  public function getStates(ContentEntityInterface $entity) {
    $states = [];
    $workflow = $this->moderationInformation
      ->getWorkflowForEntity($entity);
    foreach ($workflow
      ->getTypePlugin()
      ->getTransitions() as $transition) {
      $base_permission = $workflow
        ->id() . ' transition ' . $transition
        ->id();
      if ($this->currentUser
        ->hasPermission("schedule {$base_permission}") || $this->currentUser
        ->hasPermission("use {$base_permission}")) {
        $to_state = $transition
          ->to();
        $states[$to_state
          ->id()] = $to_state
          ->label();
      }
    }
    return $states;
  }

  /**
   * Validates incoming transition data.
   *
   * @param array $element
   *   The form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @see lightning_scheduler_form_alter()
   */
  public static function validate(array $element, FormStateInterface $form_state) {
    $data = Json::decode($element['#value']);
    if (json_last_error() !== JSON_ERROR_NONE) {
      $variables = [
        '%error' => json_last_error_msg(),
      ];
      $form_state
        ->setError($element, t('Invalid transition data: %error', $variables));
      return;
    }
    if (!is_array($data)) {
      $form_state
        ->setError($element, t('Expected scheduled transitions to be an array.'));
      return;
    }
    $minimum_date = NULL;
    $format_options = [
      'timezone' => drupal_get_user_timezone(),
    ];
    foreach ($data as $transition) {
      if (empty($transition['when'])) {
        $form_state
          ->setError($element, t('Scheduled transitions must have a date and time.'));
        return;
      }
      $date_time = new DrupalDateTime($transition['when'], 'UTC');
      if ($date_time
        ->hasErrors()) {
        $variables = [
          '%when' => $transition['when'],
        ];
        $form_state
          ->setError($element, t('"%when" is not a valid date and time.', $variables));
        return;
      }

      // The transition must take place after $minimum_date.
      if ($minimum_date instanceof DrupalDateTime && $date_time
        ->getTimestamp() < $minimum_date
        ->getTimestamp()) {
        $variables = [
          '@date' => $minimum_date
            ->format('F j, Y', $format_options),
          '@time' => $minimum_date
            ->format('g:i A', $format_options),
        ];
        $form_state
          ->setError($element, t('You cannot schedule a transition to take place before @time on @date.', $variables));
        return;
      }
      $minimum_date = $date_time;
    }
  }

  /**
   * Executes all scheduled transitions for a particular entity type.
   *
   * @param string $entity_type_id
   *   The entity type ID.
   * @param DrupalDateTime $now
   *   The time that processing began.
   */
  public function process($entity_type_id, DrupalDateTime $now) {

    /** @var ContentEntityInterface $entity */
    foreach ($this
      ->getTransitionable($entity_type_id, $now) as $entity) {
      $error_context = [
        'entity_type' => (string) $entity
          ->getEntityType()
          ->getSingularLabel(),
        'entity' => $entity
          ->label(),
      ];
      $workflow = $this->moderationInformation
        ->getWorkflowForEntity($entity);

      // If the entity hasn't got a workflow, what are we doing here?
      if (empty($workflow)) {
        $message = $this
          ->t('Could not execute scheduled transition(s) for {entity_type} "{entity}" because no workflow is assigned to it.');
        $this->logger
          ->error($message, $error_context);
        continue;
      }
      $transition_set = new TransitionSet($entity
        ->get('scheduled_transition_date'), $entity
        ->get('scheduled_transition_state'));
      $to_state = $transition_set
        ->getExpectedState($now);

      // If no workflow state is targeted, there's nothing to transition to.
      if (empty($to_state)) {
        continue;
      }
      $from_state = $entity->moderation_state->value;
      $plugin = $workflow
        ->getTypePlugin();
      if ($plugin
        ->hasTransitionFromStateToState($from_state, $to_state)) {
        $entity
          ->set('moderation_state', $to_state);
      }
      else {
        $error_context += [
          'from_state' => $plugin
            ->getState($from_state)
            ->label(),
          'to_state' => $plugin
            ->getState($to_state)
            ->label(),
          'workflow' => $workflow
            ->label(),
        ];
        $message = $this
          ->t('Could not transition {entity_type} "{entity}" from {from_state} to {to_state} because no such transition exists in the "{workflow}" workflow.');
        $this->logger
          ->warning($message, $error_context);
      }
      $transition_set
        ->trim($now);
      $entity
        ->save();
    }
  }

  /**
   * Returns all transitionable entities of a given type.
   *
   * The entity type is assumed to have the scheduled_transition_date field.
   *
   * @param string $entity_type_id
   *   The entity type ID.
   * @param DrupalDateTime $now
   *   The time that processing began.
   *
   * @return \Generator
   *   An iterable of the latest revisions of all transitionable entities of the
   *   given type.
   */
  protected function getTransitionable($entity_type_id, DrupalDateTime $now) {
    $storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $now = $now
      ->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT);

    // Entities are transitionable if its latest revision has any transitions
    // scheduled now or in the past.
    $IDs = $storage
      ->getQuery()
      ->latestRevision()
      ->accessCheck(FALSE)
      ->condition('scheduled_transition_date.value', $now, '<=')
      ->execute();
    foreach (array_keys($IDs) as $revision_id) {
      (yield $storage
        ->loadRevision($revision_id));
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TransitionManager::$currentUser protected property The currently logged-in user.
TransitionManager::$entityTypeManager protected property The entity type manager service.
TransitionManager::$logger protected property The logger channel.
TransitionManager::$moderationInformation protected property The moderation information service.
TransitionManager::getStates public function Returns an array of available workflow states for an entity.
TransitionManager::getTransitionable protected function Returns all transitionable entities of a given type.
TransitionManager::process public function Executes all scheduled transitions for a particular entity type.
TransitionManager::validate public static function Validates incoming transition data.
TransitionManager::__construct public function TransitionManager constructor.