You are here

final class TransitionManager in Lightning Scheduler 8

@internal This is an internal part of Lightning Scheduler and may be changed or removed at any time without warning. It should not be used by external code in any way.

Hierarchy

Expanded class hierarchy of TransitionManager

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

File

src/TransitionManager.php, line 23

Namespace

Drupal\lightning_scheduler
View source
final class TransitionManager {
  use StringTranslationTrait;

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

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

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

  /**
   * The logger channel.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  private $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);
    }
  }

  /**
   * 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;
    foreach ($data as $transition) {
      if (empty($transition['when'])) {
        $form_state
          ->setError($element, t('Scheduled transitions must have a date and time.'));
        return;
      }
      if (!preg_match('/^[0-9]+$/', $transition['when'])) {
        $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 (isset($minimum_date) && $transition['when'] < $minimum_date) {
        $variables = [
          '@date' => date('F j, Y', $minimum_date),
          '@time' => date('g:i A', $minimum_date),
        ];
        $form_state
          ->setError($element, t('You cannot schedule a transition to take place before @time on @date.', $variables));
        return;
      }
      $minimum_date = $transition['when'];
    }
  }

  /**
   * 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.
   */
  private 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 private property The currently logged-in user.
TransitionManager::$entityTypeManager private property The entity type manager service.
TransitionManager::$logger private property The logger channel.
TransitionManager::$moderationInformation private property The moderation information service.
TransitionManager::getTransitionable private 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.