cms_content_sync-3.0.x-dev/src/Controller/Embed.php

src/Controller/Embed.php
<?php

namespace Drupal\cms_content_sync\Controller;

use Drupal\block_content\BlockContentInterface;
use Drupal\cms_content_sync\Entity\EntityStatus;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Form\JsonForm;
use Drupal\cms_content_sync\Plugin\Type\EntityHandlerPluginManager;
use Drupal\cms_content_sync\SyncCoreInterface\DrupalApplication;
use Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\media\MediaInterface;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\node\NodeInterface;
use EdgeBox\SyncCore\Exception\UnauthorizedException;
use EdgeBox\SyncCore\Interfaces\Embed\IEmbedService;
use EdgeBox\SyncCore\Interfaces\ISyncCore;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteSiteConfigRequestMode;
use Firebase\JWT\JWT;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Class Embed provides helpers to embed Sync Core functionality into the site.
 */
class Embed extends ControllerBase {

  /**
   * SyncCoreFactory.
   *
   * @var \Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory
   */
  protected $core;

  /**
   * SyncCoreFactory.
   *
   * @var ContentSyncSettings
   */
  protected $settings;

  /**
   * Entity Type parameters.
   *
   * @var array
   */
  protected $params;

  /**
   * The Sync Core embed service.
   */
  protected $embedService;

  /**
   * Check if the site has been migrated.
   */
  public static function didMigrate() {
    return !empty(\Drupal::service('config.factory')
      ->get('cms_content_sync.migration')
      ->get('cms_content_sync_v2_pool_statuses'));
  }

  /**
   * Embed the syndication dashboard.
   */
  public function syndicationDashboard() {
    $this->init();
    if (!$this->settings->getSiteUuid()) {
      \Drupal::messenger()->addWarning($this->t('Please register your site first.'));

      return new RedirectResponse(Url::fromRoute('cms_content_sync.site', ['absolute' => TRUE])->toString());
    }
    $embed = $this->embedService->syndicationDashboard([
      'selectedFlowMachineName' => $this->params['flow'] ?? NULL,
      'startNew' => isset($this->params['startNew']) ? 'true' === $this->params['startNew'] : FALSE,
      'forbidStartNew' => FALSE,
      'query' => $this->params,
    ]);

    return $this->run($embed, []);
  }

  /**
   * Embed the site tab.
   */
  public function site() {
    $this->init(TRUE);

    // Already registered if this exists.
    $uuid = $this->settings->getSiteUuid();

    $force = empty($this->params['force']) ? NULL : $this->params['force'];

    $this->params['migrated'] = self::didMigrate();

    $this->params['domains'] = _cms_content_sync_get_domains();

    $is_registered = $uuid && (empty($this->params['force']) || IEmbedService::REGISTER_SITE !== $this->params['force']);

    if ($is_registered) {
      $this->params['createFlowUrl'] = Url::fromRoute('entity.cms_content_sync_flow.add_form', [], ['absolute' => TRUE])->toString();
      // TODO: Provide a route to export all Flows (like drush cse)
      // $this->params['exportAllFlowsUrl'] = '';.
      $this->params['existingFlows'] = [];
      foreach (Flow::getAll() as $flow) {
        $this->params['existingFlows'][] = [
          'machineName' => $flow->id(),
          'name' => $flow->label(),
          'exportUrl' => Url::fromRoute('entity.cms_content_sync_flow.export', ['cms_content_sync_flow' => $flow->id()], ['absolute' => TRUE])->toString(),
          'requiresExport' => $flow->getController()->needsEntityTypeUpdate(),
        ];
      }
    }

    $embed = $is_registered
        ? $this->embedService->siteRegistered($this->params)
        : $this->embedService->registerSite($this->params);

    try {
      $result = $this->run($embed);

      // Site was just registered.
      if (!empty($this->params['uuid'])) {
        // Clear feature flag cache.
        SyncCoreFactory::clearCache();

        // Automatically enable the private environment submodule and set the request polling feature flag
        // if this is a localhost environment that requires it anyway.
        if ($this->params['enableRequestPolling'] === 'true') {
          $module_installed = \Drupal::moduleHandler()->moduleExists('cms_content_sync_private_environment');
          if (!$module_installed) {
            \Drupal::service('module_installer')->install(['cms_content_sync_private_environment']);
          }

          $client = SyncCoreFactory::getSyncCoreV2();
          $enabled = $client->featureEnabled(ISyncCore::FEATURE_REQUEST_POLLING);
          if (!$enabled) {
            $client->enableFeature(ISyncCore::FEATURE_REQUEST_POLLING);
          }
        }

        // Export configuration immediately if it's available as an asynchronous operation.
        SyncCoreFactory::export(RemoteSiteConfigRequestMode::ALL, FALSE);
      }

      return $result;
    }
    // The site registration JWT expires after only 5 minutes, then the user must get a new one.
    catch (UnauthorizedException $e) {
      \Drupal::messenger()->addMessage('Your registration token expired. Please try again.', 'warning');

      $url = \Drupal::request()->getRequestUri();
      // Remove invalid query params so the user can try again.
      $url = explode('?', $url)[0];

      return new RedirectResponse($url);
    }
  }

  /**
   * Embed the site settings / advanced settings tab.
   */
  public function siteSettings() {
    $this->init();

    if (!$this->settings->getSiteUuid()) {
      \Drupal::messenger()->addWarning($this->t('Please register your site first.'));

      return new RedirectResponse(Url::fromRoute('cms_content_sync.site', ['absolute' => TRUE])->toString());
    }

    $force = empty($this->params['force']) ? NULL : $this->params['force'];

    $embed = $this->embedService->siteSettings($this->params);

    $result = $this->run($embed);

    return $result;
  }

  /**
   * Embed the flow edit form.
   */
  public function flowEditForm(Flow $cms_content_sync_flow) {
    if (Flow::VARIANT_PER_BUNDLE === $cms_content_sync_flow->variant) {
      $request = \Drupal::request();
      // Drupal will ignore whatever you pass to the redirect response and
      // instead use the destination query parameter if given. So we have to remove it.
      if ($request->query->get('destination')) {
        $destination = $request->query->get('destination');
        $request->query->remove('destination');
      }

      return new RedirectResponse(Url::fromRoute('entity.cms_content_sync_flow.edit_form_advanced', [
        'cms_content_sync_flow' => $cms_content_sync_flow->id
      ], [
        'absolute' => TRUE,
        'query' => empty($destination) ? [] : ['destination' => $destination]
      ])->toString());
    }

    return $this->flowForm($cms_content_sync_flow);
  }

  /**
   * Embed the flow creation form.
   */
  public function flowForm(?Flow $flow) {
    $this->init();

    if (!$this->settings->getSiteUuid()) {
      \Drupal::messenger()->addWarning($this->t('Please register your site first.'));

      return new RedirectResponse(Url::fromRoute('cms_content_sync.site', ['absolute' => TRUE])->toString());
    }

    $controller = $flow ? $flow->getController() : NULL;

    $embed = $this->embedService->flowForm($controller && $controller instanceof FlowControllerSimple ? $controller->getFormValues() : FlowControllerSimple::getFormValuesForNewFlow($flow));

    return $this->run($embed, [
      'form' => \Drupal::formBuilder()->getForm(JsonForm::class),
    ]);
  }

  /**
   * Embed the pull dashboard.
   */
  public function pullDashboard() {
    $this->init();

    $user = \Drupal::currentUser();

    $embed = $this->embedService->pullDashboard([
      'configurationAccess' => $user->hasPermission('administer cms content sync'),
      'query' => $this->params,
    ]);

    return $this->run($embed);
  }

  /**
   * Emebd the node status. Can't use general entityStatus because too many
   * modules simply expect routes at /node/:id to use a parameter named node,
   * so Drupal dies from exceptions if you name the parameter $entity.
   */
  public function nodeStatus(NodeInterface $node) {
    return $this->entityStatus($node);
  }

  /**
   * Emebd the media status.
   */
  public function mediaStatus(MediaInterface $media) {
    return $this->entityStatus($media);
  }

  /**
   * Emebd the block status.
   */
  public function blockContentStatus(BlockContentInterface $block_content) {
    return $this->entityStatus($block_content);
  }

  /**
   * Emebd the term status.
   */
  public function taxonomyTermStatus(EntityInterface $taxonomy_term) {
    return $this->entityStatus($taxonomy_term);
  }

  /**
   * Emebd the menu item status.
   */
  public function menuLinkContentStatus(MenuLinkContentInterface $menu_link_content) {
    return $this->entityStatus($menu_link_content);
  }

  /**
   * Emebd the paragraphs library item status.
   */
  public function paragraphsLibraryItemStatus(EntityInterface $paragraphs_library_item) {
    return $this->entityStatus($paragraphs_library_item);
  }

  /**
   * Emebd the commerce product status.
   */
  public function commerceProductStatus(EntityInterface $commerce_product) {
    return $this->entityStatus($commerce_product);
  }

  /**
   * Emebd the commerce product status.
   */
  public function commerceProductVariationStatus(EntityInterface $commerce_product_variation) {
    return $this->entityStatus($commerce_product_variation);
  }

  /**
   *
   */
  protected function serializeReference(EntityInterface $entity) {
    $params = [
      'namespaceMachineName' => $entity->getEntityTypeId(),
      'machineName' => $entity->bundle(),
    ];

    if (EntityHandlerPluginManager::mapById($entity->getEntityType())) {
      $params['remoteUniqueId'] = $entity->id();
    }
    else {
      $params['remoteUuid'] = $entity->uuid();
    }

    return $params;
  }

  /**
   *
   */
  protected function getRootEntities(EntityInterface $entity, array &$result) {
    $status_entities = EntityStatus::getInfosForEntity($entity->getEntityTypeId(), $entity->uuid());

    foreach ($status_entities as $status_entity) {
      if (!$status_entity->wasPushedEmbedded() && !$status_entity->wasPulledEmbedded()) {
        foreach ($result as $candidate) {
          if ($candidate->getEntityTypeId() === $entity->getEntityTypeId() && $candidate->uuid() === $entity->uuid()) {
            continue 2;
          }
        }
        $result[] = $entity;
        continue;
      }

      $parent = $status_entity->getParentEntity();
      if (!$parent) {
        continue;
      }

      $this->getRootEntities($parent, $result);
    }
  }

  /**
   *
   */
  protected function getSerializedRootEntities(EntityInterface $entity) {
    $root_entities = [];
    $this->getRootEntities($entity, $root_entities);
    $root_entities_serialized = [];
    foreach ($root_entities as $root_entity) {
      $root_entities_serialized[] = $this->serializeReference($root_entity);
    }
    return $root_entities_serialized;
  }

  /**
   * Emebd the entity status.
   */
  public function entityStatus(EntityInterface $entity) {
    $this->init();

    $user = \Drupal::currentUser();

    $params = $this->serializeReference($entity);
    $params['configurationAccess'] = $user->hasPermission('administer cms content sync');

    $params['rootEntities'] = $this->getSerializedRootEntities($entity);

    $embed = $this->embedService->entityStatus($params);

    return $this->run($embed);
  }

  /**
   * Embed the udpate status box.
   *
   * @param \Drupal\Core\Entity\EntityInterface|string $entityOrId
   * @param bool from_recent_activity
   */
  public function updateStatusBox(mixed $entityOrId, ?bool $from_recent_activity = NULL) {
    $this->init();

    if (is_string($entityOrId)) {
      $params = [
        'id' => $entityOrId,
      ];
    }
    else {
      /**
       * @var \Drupal\Core\Entity\EntityInterface $entityOrId
       */
      $params = [
        'namespaceMachineName' => $entityOrId->getEntityTypeId(),
        'machineName' => $entityOrId->bundle(),
      ];

      if (EntityHandlerPluginManager::mapById($entityOrId->getEntityType())) {
        $params['remoteUniqueId'] = $entityOrId->id();
      }
      else {
        $params['remoteUuid'] = $entityOrId->uuid();
      }

      $params['rootEntities'] = $this->getSerializedRootEntities($entityOrId);
    }

    if ($from_recent_activity) {
      $params['fromRecentActivity'] = TRUE;
      $params['after'] = \Drupal::time()->getRequestTime() * 1_000;
    }

    $embed = $this->embedService->updateStatusBox($params);

    return $this->run($embed, [], 'line');
  }

  /**
   * Initialise the service.
   */
  protected function init($expectJwtWithSyncCoreUrl = FALSE) {
    $this->params = \Drupal::request()->query->all();

    if ($expectJwtWithSyncCoreUrl) {
      if (isset($this->params['uuid'], $this->params['jwt'])) {
        $tks = explode('.', $this->params['jwt']);
        [, $bodyb64] = $tks;
        $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64));
        if (!empty($payload->syncCoreBaseUrl)) {
          DrupalApplication::get()->setSyncCoreUrl($payload->syncCoreBaseUrl);
        }
      }
    }

    $this->core = SyncCoreFactory::getDummySyncCoreV2();

    $this->settings = ContentSyncSettings::getInstance();

    $this->embedService = $this->core->getEmbedService();
  }

  /**
   * Run the service.
   */
  protected function run($embed, $extras = [], $size = 'page') {
    $result = $embed->run();

    $redirect = $result->getRedirectUrl();

    if ($redirect) {
      return new RedirectResponse($redirect);
    }

    $html = $result->getRenderedHtml();

    // As the are issuing JWTs that are included in the render array, we don't
    // want to cache anything.
    \Drupal::service('page_cache_kill_switch')->trigger();

    $style = 'line' === $size ? 'margin-top: 3px;' : 'margin-top: 20px;';

    // @todo Put this in a theme file.
    return [
      '#type' => 'inline_template',
      '#template' => '<div className="content-sync-embed-wrapper" style="' . $style . '">{{ html|raw }}</div>',
      '#context' => [
        'html' => $html,
      ],
      '#cache' => [
        'max-age' => 0,
      ],
      '#attached' => [
        'library' => [
          'core/jquery',
        ],
      ],
    ] + $extras;
  }

}

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

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