You are here

class ChangesListController in Workspace 8

Hierarchy

Expanded class hierarchy of ChangesListController

File

src/Controller/ChangesListController.php, line 21

Namespace

Drupal\workspace\Controller
View source
class ChangesListController extends ControllerBase {

  /**
   * Pager limit - the number of elements pe page.
   *
   * @var int
   */
  protected $changesPerPage = 50;

  /**
   * @var \Drupal\multiversion\Workspace\WorkspaceManagerInterface
   */
  protected $workspaceManager;

  /**
   * @var ChangesFactoryInterface
   */
  protected $changesFactory;

  /**
   * @var RevisionDiffFactoryInterface
   */
  protected $revisionDiffFactory;

  /**
   * @var RevisionIndexInterface
   */
  protected $revIndex;

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The state service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * ChangesListController constructor.
   *
   * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager
   * @param \Drupal\replication\ChangesFactoryInterface $changes_factory
   * @param \Drupal\replication\RevisionDiffFactoryInterface $revisiondiff_factory
   * @param \Drupal\multiversion\Entity\Index\RevisionIndexInterface $rev_index
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   * @param \Drupal\Core\State\StateInterface $state
   */
  public function __construct(WorkspaceManagerInterface $workspace_manager, ChangesFactoryInterface $changes_factory, RevisionDiffFactoryInterface $revisiondiff_factory, RevisionIndexInterface $rev_index, EntityTypeManagerInterface $entity_type_manager, StateInterface $state) {
    $this->workspaceManager = $workspace_manager;
    $this->changesFactory = $changes_factory;
    $this->revisionDiffFactory = $revisiondiff_factory;
    $this->revIndex = $rev_index;
    $this->entityTypeManager = $entity_type_manager;
    $this->state = $state;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('workspace.manager'), $container
      ->get('replication.changes_factory'), $container
      ->get('replication.revisiondiff_factory'), $container
      ->get('multiversion.entity_index.rev'), $container
      ->get('entity_type.manager'), $container
      ->get('state'));
  }

  /**
   * View a list of changes between current workspace and the target workspace.
   *
   * @param int $workspace
   *
   * @return array The render array to display for the page.
   *  The render array to display for the page.
   * @throws \Doctrine\CouchDB\HTTP\HTTPException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Core\Entity\EntityMalformedException
   */
  public function viewChanges($workspace) {
    $source_workspace = $this->workspaceManager
      ->load($workspace);
    if (!$source_workspace || !$source_workspace
      ->isPublished() || $source_workspace
      ->getQueuedForDelete()) {
      throw new NotFoundHttpException();
    }
    $source_workspace_pointer = $this
      ->getPointerToWorkspace($source_workspace);
    $target_workspace_pointer = $source_workspace
      ->get('upstream')->entity;
    if (!$target_workspace_pointer) {
      return [
        '#markup' => '<p>' . $this
          ->t('The upstream is not set.') . '</p>',
      ];
    }
    $target_workspace = $target_workspace_pointer
      ->getWorkspace();
    $replicator_exists = class_exists('Relaxed\\Replicator\\Replicator');
    $couchdbclient_exists = class_exists('Doctrine\\CouchDB\\CouchDBClient');
    $entities = [];
    if ($target_workspace instanceof WorkspaceInterface) {
      $entities = $this
        ->getChangesBetweenLocalWorkspaces($source_workspace, $target_workspace);
    }
    elseif ($replicator_exists && $couchdbclient_exists) {
      $entities = $this
        ->getChangesBetweenRemoteWorkspaces($source_workspace_pointer, $target_workspace_pointer);
    }
    return $this
      ->adminOverview($entities);
  }

  /**
   * Return the array with changed entities for workspaces on the same site.
   *
   * @param $source_workspace
   * @param $target_workspace
   *
   * @return array
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   */
  protected function getChangesBetweenLocalWorkspaces($source_workspace, $target_workspace) {
    $since = $this->state
      ->get('last_sequence.workspace.' . $source_workspace
      ->id(), 0);

    // Get changes on the source workspace.
    $source_changes = $this->changesFactory
      ->get($source_workspace)
      ->setSince($since)
      ->getNormal();
    $data = [];
    foreach (array_reverse($source_changes) as $source_change) {
      $data[$source_change['id']] = [];
      foreach ($source_change['changes'] as $change) {
        $data[$source_change['id']][] = $change['rev'];
      }
    }

    // Get revisions the target workspace is missing.
    $revs_diff = $this->revisionDiffFactory
      ->get($target_workspace)
      ->setRevisionIds($data)
      ->getMissing();
    return $this
      ->getEntitiesFromRevsDiff($source_workspace, $revs_diff);
  }

  /**
   * Return the array with changed entities when target is a remote workspace.
   *
   * @param $source_workspace_pointer \Drupal\workspace\WorkspacePointerInterface
   * @param $target_workspace_pointer \Drupal\workspace\WorkspacePointerInterface
   *
   * @return array
   * @throws \Doctrine\CouchDB\HTTP\HTTPException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   */
  protected function getChangesBetweenRemoteWorkspaces($source_workspace_pointer, $target_workspace_pointer) {
    $since = $this->state
      ->get('last_sequence.workspace.' . $source_workspace_pointer
      ->getWorkspaceId(), 0);

    /** @var \Drupal\relaxed\CouchdbReplicator $couch_db_replicator */
    $couch_db_replicator = \Drupal::service('relaxed.couchdb_replicator');

    /** @var \Doctrine\CouchDB\CouchDBClient $source */
    $source = $couch_db_replicator
      ->setupEndpoint($source_workspace_pointer);

    /** @var \Doctrine\CouchDB\CouchDBClient $target */
    $target = $couch_db_replicator
      ->setupEndpoint($target_workspace_pointer);
    $revs_diff = [];
    $source_workspace = $source_workspace_pointer
      ->getWorkspace();
    $task = $this
      ->getTask($source_workspace, 'push_replication_settings');
    while (1) {
      $changes = $source
        ->getChanges(array(
        'feed' => 'normal',
        'style' => 'all_docs',
        'since' => $since,
        'filter' => $task
          ->getFilter(),
        'parameters' => $task
          ->getParameters(),
        'doc_ids' => NULL,
        'limit' => 50,
      ));
      if (empty($changes['results']) || empty($changes['last_seq'])) {
        break;
      }
      $data = [];
      foreach (array_reverse($changes['results']) as $source_change) {
        $data[$source_change['id']] = [];
        foreach ($source_change['changes'] as $change) {
          $data[$source_change['id']][] = $change['rev'];
        }
      }
      $revs_diff += !empty($data) ? $target
        ->getRevisionDifference($data) : [];
      if (!in_array($changes['last_seq'], array_column($changes['results'], 'seq'))) {
        break;
      }
      $since = $changes['last_seq'];
    }
    return $this
      ->getEntitiesFromRevsDiff($source_workspace, $revs_diff);
  }

  /**
   * @param $source_workspace \Drupal\multiversion\Entity\WorkspaceInterface
   * @param $revs_diff array
   *
   * @return array
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   */
  protected function getEntitiesFromRevsDiff($source_workspace, $revs_diff) {
    $entities = [];
    $source_workspace_id = $source_workspace
      ->id();
    foreach ($revs_diff as $uuid => $revs) {
      foreach ($revs['missing'] as $rev) {
        $item = $this->revIndex
          ->useWorkspace($source_workspace_id)
          ->get("{$uuid}:{$rev}");
        $entity_type_id = $item['entity_type_id'];
        $revision_id = $item['revision_id'];

        /** @var \Drupal\multiversion\Entity\Storage\ContentEntityStorageInterface $storage */
        $storage = $this
          ->entityTypeManager()
          ->getStorage($entity_type_id)
          ->useWorkspace($source_workspace_id);
        $entity = $storage
          ->loadRevision($revision_id);
        if ($entity instanceof ContentEntityInterface) {
          $entities[] = $entity;
        }
      }
    }
    return $entities;
  }

  /**
   * Returns an administrative overview of all changes.
   *
   * @param array $entities
   *
   * @return array A render array representing the administrative page content.
   *   A render array representing the administrative page content.
   * @throws \Drupal\Core\Entity\EntityMalformedException
   */
  protected function adminOverview(array $entities = []) {
    $total = count($entities);

    // Initialize the pager.
    $page = pager_default_initialize($total, $this->changesPerPage);

    // Split the items up into chunks:
    $chunks = array_chunk($entities, $this->changesPerPage);

    // Get changes for the current page:
    $current_page_changes = isset($chunks[$page]) ? $chunks[$page] : [];
    $headers = [
      $this
        ->t('Entities'),
      $this
        ->t('Entity type'),
      $this
        ->t('Status'),
      $this
        ->t('Author'),
      $this
        ->t('Changed time'),
      $this
        ->t('Operations'),
    ];
    $rows = [];

    /** @var \Drupal\Core\Entity\ContentEntityInterface[] $current_page_changes */
    foreach ($current_page_changes as $entity) {
      $row = [
        $entity
          ->label() ?: '* ' . $this
          ->t('No label') . ' *',
        $entity
          ->getEntityTypeId(),
      ];

      //Set status.
      if ($entity->_deleted->value) {
        $row[] = $this
          ->t('Deleted');
      }
      elseif (!empty($entity->_rev->value) && $entity->_rev->value[0] == 1) {
        $row[] = $this
          ->t('Added');
      }
      else {
        $row[] = $this
          ->t('Changed');
      }

      // Set the author.
      if (method_exists($entity, 'getOwner')) {
        $row[] = ($name = $entity
          ->getOwner()
          ->get('name')->value) ? $name : '* ' . $this
          ->t('No author') . ' *';
      }
      else {
        $row[] = '* ' . $this
          ->t('No author') . ' *';
      }

      // Set changed value.
      if (method_exists($entity, 'getChangedTime') && ($changed = $entity
        ->getChangedTime())) {
        $row[] = DateTimePlus::createFromTimestamp($changed)
          ->format('m/d/Y | H:i:s | e');
      }
      else {
        $row[] = '* ' . $this
          ->t('No changed time') . ' *';
      }

      // Set operations.
      $links = [];
      if ($entity
        ->hasLinkTemplate('canonical') && !$entity->_deleted->value) {
        $links['view'] = [
          'title' => t('View'),
          'url' => $entity
            ->toUrl('canonical', [
            'absolute' => TRUE,
          ]),
        ];
      }
      else {
        $links['view'] = [
          'title' => '* ' . $this
            ->t('No view link') . ' *',
        ];
      }
      $row[] = [
        'data' => [
          '#type' => 'operations',
          '#links' => $links,
        ],
      ];
      $rows[] = $row;
    }
    $build['prefix']['#markup'] = '<p>' . $this
      ->t('The array is sorted by last change first.') . '</p>';
    $build['changes-list'] = [
      '#type' => 'table',
      '#header' => $headers,
      '#rows' => $rows,
      '#empty' => t('There are no changes.'),
    ];
    $build['pager'] = [
      '#type' => 'pager',
    ];
    return $build;
  }

  /**
   * Get the page title for the list of changes page.
   *
   * @param int $workspace
   *
   * @return string The page title.
   * The page title.
   */
  public function getViewChangesTitle($workspace) {

    /** @var WorkspaceInterface $active_workspace */
    $active_workspace = $this->workspaceManager
      ->load($workspace);
    $active_workspace_label = $active_workspace
      ->label();
    $target_workspace_pointer = $active_workspace
      ->get('upstream')->entity;
    if (!$target_workspace_pointer) {
      $target_workspace_label = $this
        ->t('target');
    }
    else {
      $target_workspace_label = $target_workspace_pointer
        ->label();
    }
    return $this
      ->t('Changes between @source workspace and @target workspace', [
      '@source' => $active_workspace_label,
      '@target' => $target_workspace_label,
    ]);
  }

  /**
   * Returns a pointer to the specified workspace.
   *
   * In most cases this pointer will be unique, but that is not guaranteed
   * by the schema. If there are multiple pointers, which one is returned is
   * undefined.
   *
   * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace
   *   The workspace for which we want a pointer.
   *
   * @return \Drupal\workspace\WorkspacePointerInterface
   *   The pointer to the provided workspace.
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   */
  protected function getPointerToWorkspace(WorkspaceInterface $workspace) {
    $pointers = $this->entityTypeManager
      ->getStorage('workspace_pointer')
      ->loadByProperties([
      'workspace_pointer' => $workspace
        ->id(),
    ]);
    $pointer = reset($pointers);
    return $pointer;
  }

  /**
   * Create a task using workspace info.
   *
   * @param \Drupal\multiversion\Entity\WorkspaceInterface $entity
   * @param $field_name
   *
   * @return \Drupal\replication\ReplicationTask\ReplicationTask
   */
  protected function getTask(WorkspaceInterface $entity, $field_name) {
    $task = new ReplicationTask();
    $items = $entity
      ->get($field_name);
    if (!$items instanceof EntityReferenceFieldItemListInterface) {
      throw new LogicException('Replication settings field does not exist.');
    }
    $referenced_entities = $items
      ->referencedEntities();
    if (count($referenced_entities) > 0) {
      $task
        ->setFilter($referenced_entities[0]
        ->getFilterId());
      $task
        ->setParameters($referenced_entities[0]
        ->getParameters());
    }
    return $task;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ChangesListController::$changesFactory protected property
ChangesListController::$changesPerPage protected property Pager limit - the number of elements pe page.
ChangesListController::$entityTypeManager protected property Overrides ControllerBase::$entityTypeManager
ChangesListController::$revIndex protected property
ChangesListController::$revisionDiffFactory protected property
ChangesListController::$state protected property The state service.
ChangesListController::$workspaceManager protected property
ChangesListController::adminOverview protected function Returns an administrative overview of all changes.
ChangesListController::create public static function Instantiates a new instance of this class. Overrides ControllerBase::create
ChangesListController::getChangesBetweenLocalWorkspaces protected function Return the array with changed entities for workspaces on the same site.
ChangesListController::getChangesBetweenRemoteWorkspaces protected function Return the array with changed entities when target is a remote workspace.
ChangesListController::getEntitiesFromRevsDiff protected function
ChangesListController::getPointerToWorkspace protected function Returns a pointer to the specified workspace.
ChangesListController::getTask protected function Create a task using workspace info.
ChangesListController::getViewChangesTitle public function Get the page title for the list of changes page.
ChangesListController::viewChanges public function View a list of changes between current workspace and the target workspace.
ChangesListController::__construct public function ChangesListController constructor.
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityManager protected property The entity manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
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.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.