scheduled_publish-8.x-3.9/src/Service/ScheduledPublishCron.php

src/Service/ScheduledPublishCron.php
<?php

namespace Drupal\scheduled_publish\Service;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\scheduled_publish\Event\ScheduledStateChangeEvent;
use Drupal\scheduled_publish\Plugin\Field\FieldType\ScheduledPublish;
use Drupal\scheduled_publish\ScheduledPublishEvents;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
 * Class ScheduledPublishCron.
 *
 * Allow scheduled publish to respond to Cron events.
 *
 * @package Drupal\scheduled_publish\Service
 */
class ScheduledPublishCron {

  /**
   * List of entity types supported by scheduled publish.
   *
   * @var array
   */
  public static $supportedTypes = [
    'node',
    'media',
  ];

  /**
   * The entity bundle information service.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfo
   */
  private $entityBundleInfoService;

  /**
   * The entity field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManager
   */
  private $entityFieldManager;

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

  /**
   * An event dispatcher.
   *
   * @var \Psr\EventDispatcher\EventDispatcherInterface
   */
  private $eventDispatcher;

  /**
   * The date and time to publish.
   *
   * @var \Drupal\Component\Datetime\Time
   */
  private $dateTime;

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

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

  /**
   * ScheduledPublishCron constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entityBundleInfo
   *   The entity type bundle information.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
   *   The entity field manager.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Component\Datetime\TimeInterface $dateTime
   *   The date and time.
   * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
   *   The moderation information service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
   *   The logger channel factory.
   * @param \Psr\EventDispatcher\EventDispatcherInterface|null $eventDispatcher
   *   An event dispatcher.
   */
  public function __construct(EntityTypeBundleInfoInterface $entityBundleInfo, EntityFieldManagerInterface $entityFieldManager, EntityTypeManagerInterface $entityTypeManager, TimeInterface $dateTime, ModerationInformationInterface $moderation_info, LoggerChannelFactoryInterface $logger, ?EventDispatcherInterface $eventDispatcher = NULL) {
    $this->entityBundleInfoService = $entityBundleInfo;
    $this->entityFieldManager = $entityFieldManager;
    $this->entityTypeManager = $entityTypeManager;
    $this->dateTime = $dateTime;
    $this->moderationInfo = $moderation_info;
    $this->logger = $logger;

    // To avoid breaking backwards compatibility, eventDispatcher needs to be
    // optional; so we use a global \Drupal call to get the service if it is not
    // defined.
    // @phpstan-ignore-next-line globalDrupalDependencyInjection.useDependencyInjection
    $this->eventDispatcher = $eventDispatcher ?? \Drupal::service('event_dispatcher');
  }

  /**
   * Run field updates.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function doUpdate(): void {
    foreach (self::$supportedTypes as $supportedType) {
      $this->doUpdateFor($supportedType);
    }
  }

  /**
   * Run field update for specific entity type.
   *
   * @param string $entityType
   *   The entity type.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Exception
   */
  private function doUpdateFor(string $entityType): void {
    $bundles = $this->entityBundleInfoService->getBundleInfo($entityType);

    foreach ($bundles as $bundleName => $value) {

      $scheduledFields = $this->getScheduledFields($entityType, $bundleName);
      if (\count($scheduledFields) > 0) {
        foreach ($scheduledFields as $scheduledField) {
          $query = $this->entityTypeManager->getStorage($entityType)
            ->getQuery('AND');
          $query->condition($entityType === 'media' ? 'bundle' : 'type', $bundleName);
          $query->condition($scheduledField, NULL, 'IS NOT NULL');
          $query->accessCheck(FALSE);
          $query->latestRevision();
          $entities = $query->execute();
          foreach ($entities as $entityRevision => $entityId) {
            $storage = $this->entityTypeManager->getStorage($entityType);
            /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
            $revision = $storage->loadRevision($entityRevision);
            $this->updateEntityField($revision, $scheduledField);
          }
        }
      }
    }
  }

  /**
   * Returns scheduled publish fields.
   *
   * @param string $entityTypeName
   *   The entity type name.
   * @param string $bundleName
   *   The bundle name.
   *
   * @return array
   *   An array containing the scheduled fields.
   */
  private function getScheduledFields(string $entityTypeName, string $bundleName): array {
    $scheduledFields = [];
    $fields = $this->entityFieldManager
      ->getFieldDefinitions($entityTypeName, $bundleName);
    foreach ($fields as $fieldName => $field) {
      /** @var \Drupal\field\Entity\FieldConfig $field */
      if (strpos($fieldName, 'field_') !== FALSE) {
        if ($field->getType() === 'scheduled_publish') {
          $scheduledFields[] = $fieldName;
        }
      }
    }

    return $scheduledFields;
  }

  /**
   * Update scheduled publish fields.
   *
   * @param \Drupal\Core\Entity\ContentEntityBase $entity
   *   The entity to publish.
   * @param string $scheduledField
   *   The scheduled publish field.
   *
   * @throws \Exception
   */
  private function updateEntityField(ContentEntityBase $entity, string $scheduledField): void {
    /** @var \Drupal\Core\Field\FieldItemList $scheduledEntity */
    $scheduledEntity = $entity->get($scheduledField);
    $scheduledValue = $scheduledEntity->getValue();
    if (empty($scheduledValue)) {
      return;
    }

    foreach ($scheduledValue as $key => $value) {
      if ($this->getTimestampFromIso8601($value['value']) <= $this->dateTime->getCurrentTime()) {
        unset($scheduledValue[$key]);
        $this->updateEntity($entity, $value['moderation_state'], $scheduledField, $scheduledValue);
      }
    }
  }

  /**
   * Returns timestamp from ISO-8601 datetime.
   *
   * @param string $dateIso8601
   *   The date in ISO format.
   *
   * @return int
   *   The date/time as a timestamp.
   *
   * @throws \Exception
   */
  private function getTimestampFromIso8601(string $dateIso8601): int {
    $datetime = new \DateTime($dateIso8601, new \DateTimeZone(ScheduledPublish::STORAGE_TIMEZONE));
    $datetime->setTimezone(new \DateTimeZone(date_default_timezone_get()));

    return $datetime->getTimestamp();
  }

  /**
   * Updates entity.
   *
   * @param \Drupal\Core\Entity\ContentEntityBase $entity
   *   The entity to update.
   * @param string $moderationState
   *   The moderation state.
   * @param string $scheduledPublishField
   *   The value of the scheduled publish field.
   * @param mixed $scheduledValue
   *   The field value - type to be confirmed.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  private function updateEntity(ContentEntityBase $entity, string $moderationState, string $scheduledPublishField, $scheduledValue): void {
    $entity->set($scheduledPublishField, $scheduledValue);
    // Only make valid transitions.
    if ($this->isValidStateChange($entity, $moderationState)) {
      $currentModerationState = $entity->get('moderation_state')
        ->getValue()[0]['value'];
      $entity->set('moderation_state', $moderationState);

      // Dispatch the SCHEDULED_STATE_CHANGE event to allow other modules to act
      // on and/or modify the entity before the entity is saved. Note this event
      // is only dispatched if the state change is valid.
      $this->eventDispatcher->dispatch(new ScheduledStateChangeEvent($entity, $currentModerationState, $moderationState, $scheduledPublishField, $scheduledValue), ScheduledPublishEvents::SCHEDULED_STATE_CHANGE);
    }
    $entity->save();

    // Log valid transitions.
    if (isset($currentModerationState)) {
      $entity_info = $entity->label() . ' (' . $entity->id() . ')';
      $this->logger->get('scheduled_publish')
        ->info('The moderation state of @entity was changed from @orig_status to @current_status',
          [
            '@entity' => $entity_info,
            '@orig_status' => $currentModerationState,
            '@current_status' => $moderationState,
          ]
        );
    }
  }

  /**
   * Checks if the state change is valid.
   *
   * @param \Drupal\Core\Entity\ContentEntityBase $entity
   *   The entity to check.
   * @param string $moderationState
   *   The new state to check.
   *
   * @return bool
   *   True if state change is valid.
   */
  protected function isValidStateChange(ContentEntityBase $entity, string $moderationState): bool {
    $workflow = $this->moderationInfo->getWorkflowForEntity($entity);
    $current_state = $entity->moderation_state->value ? $workflow->getTypePlugin()->getState($entity->moderation_state->value) : $workflow->getTypePlugin()->getInitialState($entity);
    $transitions = $current_state->getTransitions();
    foreach ($transitions as $value) {
      if ($value->to()->id() === $moderationState) {
        return TRUE;
      }
    }
    return FALSE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc