digital_signage_framework-2.3.x-dev/src/ScheduleManager.php

src/ScheduleManager.php
<?php

namespace Drupal\digital_signage_framework;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\digital_signage_framework\Entity\Schedule;

/**
 * Provides schedule services.
 */
class ScheduleManager {

  /**
   * The schedule generator plugin manager.
   *
   * @var \Drupal\digital_signage_framework\ScheduleGeneratorPluginManager
   */
  protected ScheduleGeneratorPluginManager $generatorPluginManager;

  /**
   * The platform plugin manager.
   *
   * @var \Drupal\digital_signage_framework\PlatformPluginManager
   */
  protected PlatformPluginManager $platformPluginManager;

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

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected ModuleHandlerInterface $moduleHandler;

  /**
   * Constructs the schedule manager.
   *
   * @param \Drupal\digital_signage_framework\ScheduleGeneratorPluginManager $generator_plugin_manager
   *   The schedule generator plugin manager.
   * @param \Drupal\digital_signage_framework\PlatformPluginManager $platform_plugin_manager
   *   The platform plugin manager.
   * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   */
  public function __construct(ScheduleGeneratorPluginManager $generator_plugin_manager, PlatformPluginManager $platform_plugin_manager, EntityTypeManager $entity_type_manager, ModuleHandlerInterface $module_handler) {
    $this->generatorPluginManager = $generator_plugin_manager;
    $this->platformPluginManager = $platform_plugin_manager;
    $this->entityTypeManager = $entity_type_manager;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Gets devices for schedule updates.
   *
   * @param int|null $deviceId
   *   The device ID or NULL, to find the relevant devices.
   * @param bool $all
   *   TRUE, to get all devices or FALSE to get only those that need an update.
   *
   * @return \Drupal\digital_signage_framework\DeviceInterface[]
   *   The list of devices.
   */
  protected function getDevices(?int $deviceId = NULL, bool $all = FALSE): array {
    try {
      $deviceManager = $this->entityTypeManager->getStorage('digital_signage_device');
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException) {
      // Can be ignored.
      return [];
    }
    // @phpstan-ignore-next-line
    $query = $deviceManager->getQuery()->accessCheck(FALSE);
    if ($deviceId !== NULL) {
      $query->condition('id', $deviceId);
    }
    if (!$all) {
      $query->condition('needs_update', 1);
    }
    $ids = $query
      ->execute();
    $devices = [];
    /** @var int[] $ids */
    array_walk($ids, static function (&$item) {
      $item = (int) $item;
    });
    foreach ($deviceManager->loadMultiple($ids) as $device) {
      if ($device instanceof DeviceInterface) {
        $devices[] = $device;
      }
    }
    return $devices;
  }

  /**
   * Create schedule for a device.
   *
   * @param \Drupal\digital_signage_framework\DeviceInterface $device
   *   The devices.
   * @param \Drupal\digital_signage_framework\ScheduleGeneratorInterface $plugin
   *   The generator plugin.
   * @param bool $store
   *   Indicates, whether the generated schedule should be stored in database.
   * @param bool $force
   *   Indicates, whether a schedule should be created, even if one already
   *   exists.
   * @param string|null $entityType
   *   The entity type ID, if only a single slide should be in the schedule,
   *   NULL otherwise.
   * @param string|null $entityId
   *   The entity ID, if only a single slide should be in the schedule, NULL
   *   otherwise.
   *
   * @return \Drupal\digital_signage_framework\ScheduleInterface
   *   The schedule.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \JsonException
   */
  protected function createSchedule(DeviceInterface $device, ScheduleGeneratorInterface $plugin, bool $store, bool $force, ?string $entityType = NULL, ?string $entityId = NULL): ScheduleInterface {
    // Collect the content entities for this schedule.
    // @phpstan-ignore-next-line
    $query = $this->entityTypeManager
      ->getStorage('digital_signage_content_setting')
      ->getQuery()
      ->accessCheck(FALSE)
      ->condition('status', 1)
      ->condition('emergencymode', 0);
    if ($entityType === NULL) {
      $orQuery = $query->orConditionGroup()
        ->condition($query->andConditionGroup()
          ->notExists('devices.target_id')
          ->notExists('segments.target_id')
        )
        ->condition('devices.target_id', $device->id());
      if ($segmentIds = $device->getSegmentIds()) {
        $orQuery->condition('segments.target_id', $segmentIds, 'IN');
      }
      $query->condition($orQuery);
      // Ignore entities that have a predecessor.
      $query->notExists('predecessor');

      // Allow other modules to alter the query for entities.
      $this->moduleHandler->alter('digital_signage_schedule_generator_query', $query, $device);
    }
    else {
      $query
        ->condition('parent_entity__target_type', $entityType)
        ->condition('parent_entity__target_id', $entityId);
    }

    $contentSettings = [];
    $hashMap = [];
    /** @var \Drupal\digital_signage_framework\ContentSettingInterface $item */
    foreach ($this->entityTypeManager->getStorage('digital_signage_content_setting')->loadMultiple($query->execute()) as $item) {
      /** @var \Drupal\Core\Entity\ContentEntityInterface|null $entity */
      $entity = $this->entityTypeManager->getStorage($item->getReverseEntityType())->load($item->getReverseEntityId());
      if ($entity instanceof FieldableEntityInterface && (!$entity->hasField('status') || $entity->get('status')->value)) {
        $contentSettings[] = $item;
        $hash_item = [
          'type' => $entity->getEntityTypeId(),
          'id' => $entity->id(),
        ];
        if ($entity->hasField('changed')) {
          $hash_item['changed'] = $entity->get('changed')->value;
        }
        elseif ($entity->hasField('created')) {
          $hash_item['created'] = $entity->get('created')->value;
        }
        $hashMap[] = $hash_item;
      }
    }

    // See if we already have a schedule with that.
    $scheduleHash = md5(json_encode($hashMap, JSON_THROW_ON_ERROR));
    try {
      /** @var \Drupal\digital_signage_framework\ScheduleInterface[] $schedules */
      $schedules = $this->entityTypeManager
        ->getStorage('digital_signage_schedule')
        ->loadByProperties([
          'hash' => $scheduleHash,
        ]);
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException) {
    }
    if ($force || empty($schedules)) {
      // We don't have one yet, let's create a new one.
      $items = [];
      foreach ($plugin->generate($device, $contentSettings) as $sequenceItem) {
        $this->addItemAndSuccessors($items, $sequenceItem, $device);
      }
      /** @var \Drupal\digital_signage_framework\ScheduleInterface $schedule */
      $schedule = Schedule::create([
        'hash' => $scheduleHash,
        'items' => [$items],
      ]);
      if ($store) {
        $schedule->save();
      }
    }
    else {
      $schedule = reset($schedules);
    }

    // Store the schedule with the device if necessary.
    if ($store) {
      if ($force || !$device->getSchedule() || $device->getSchedule()->id() !== $schedule->id()) {
        $device->setSchedule($schedule);
        $schedule->needsPush(TRUE);
      }
      $device
        ->scheduleUpdateCompleted()
        ->save();
    }

    return $schedule;
  }

  /**
   * Helper function to add the next item.
   *
   * @param array $items
   *   The current item list.
   * @param \Drupal\digital_signage_framework\SequenceItem $sequenceItem
   *   The next item.
   * @param DeviceInterface $device
   *   The device.
   * @param int $level
   *   The nesting level to avoid recursion.
   */
  private function addItemAndSuccessors(array &$items, SequenceItem $sequenceItem, DeviceInterface $device, int $level = 0): void {
    $items[] = $sequenceItem->toArray();
    if ($level > 3) {
      // Avoid infinite loop.
      return;
    }
    $level++;
    foreach ($sequenceItem->getSuccessors($device) as $successor) {
      $this->addItemAndSuccessors($items, $successor, $device, $level);
    }
  }

  /**
   * Push a schedule to a device.
   *
   * @param int|null $deviceId
   *   The device ID or NULL to push to all devices.
   * @param bool $force
   *   Indicates whether to push to a device even if nothing has changed.
   * @param bool $debug
   *   Indicates whether the device should be set to debug mode.
   * @param bool $reload_assets
   *   Indicates whether the device should reload assets.
   * @param bool $reload_content
   *   Indicates whether the device should reload content.
   * @param string|null $entityType
   *   The entity type ID, if only a single slide should be in the schedule,
   *   NULL otherwise.
   * @param string|null $entityId
   *   The entity ID, if only a single slide should be in the schedule, NULL
   *   otherwise.
   */
  public function pushSchedules(?int $deviceId = NULL, bool $force = FALSE, bool $debug = FALSE, bool $reload_assets = FALSE, bool $reload_content = FALSE, ?string $entityType = NULL, ?string $entityId = NULL): void {
    try {
      /** @var \Drupal\digital_signage_framework\ScheduleGeneratorInterface $plugin */
      $plugin = $this->generatorPluginManager->createInstance('default');
      foreach ($this->getDevices($deviceId, $force) as $device) {
        $schedule = $this->createSchedule($device, $plugin, TRUE, $force, $entityType, $entityId);
        if ($schedule->needsPush()) {
          $this->platformPluginManager->pushSchedule($device, $debug, $reload_assets, $reload_content);
          $schedule->needsPush(FALSE);
        }
      }
    }
    catch (PluginException | EntityStorageException | \JsonException) {
      // @todo Log this exception.
    }
  }

  /**
   * Push a configuration to a device.
   *
   * @param int|null $deviceId
   *   The device ID or NULL to push to all devices.
   * @param bool $debug
   *   Indicates whether the device should be set to debug mode.
   * @param bool $reload_schedule
   *   Indicates whether the device should reload the schedule.
   * @param bool $reload_assets
   *   Indicates whether the device should reload assets.
   * @param bool $reload_content
   *   Indicates whether the device should reload content.
   */
  public function pushConfiguration(?int $deviceId, bool $debug, bool $reload_schedule, bool $reload_assets, bool $reload_content): void {
    foreach ($this->getDevices($deviceId, TRUE) as $device) {
      try {
        $this->platformPluginManager->pushConfiguration($device, $debug, $reload_schedule, $reload_assets, $reload_content);
      }
      catch (PluginException) {
        // @todo Log this exception.
      }
    }
  }

  /**
   * Gets the latest device schedule.
   *
   * @param \Drupal\digital_signage_framework\DeviceInterface $device
   *   The device.
   *
   * @return \Drupal\digital_signage_framework\ScheduleInterface|null
   *   The schedule.
   */
  public function getSchedule(DeviceInterface $device): ?ScheduleInterface {
    try {
      /** @var \Drupal\digital_signage_framework\ScheduleGeneratorInterface $plugin */
      $plugin = $this->generatorPluginManager->createInstance('default');
      return $this->createSchedule($device, $plugin, FALSE, TRUE);
    }
    catch (PluginException | EntityStorageException | \JsonException) {
      // @todo Log this exception.
    }
    return NULL;
  }

}

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

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