cms_content_sync-3.0.x-dev/src/Plugin/rest/resource/SyncCoreSiteConfigResource.php

src/Plugin/rest/resource/SyncCoreSiteConfigResource.php
<?php

namespace Drupal\cms_content_sync\Plugin\rest\resource;

use Drupal\cms_content_sync\Controller\LoggerProxy;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Entity\Pool;
use Drupal\cms_content_sync\Event\BeforeEntityTypeExport;
use Drupal\cms_content_sync\Plugin\Type\EntityHandlerPluginManager;
use Drupal\cms_content_sync\PullIntent;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory;
use Drupal\Core\Language\Language;
use Drupal\Core\Serialization\Yaml;
use Drupal\rest\Plugin\ResourceBase;
use EdgeBox\SyncCore\Interfaces\ISyncCore;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteEntityTypePropertyFormat;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteRequestQueryParamsSiteConfig;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteSiteConfigRequestMode;
use EdgeBox\SyncCore\V2\Raw\Model\SiteConfigResponse;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Allow the Sync Core to query for this site's confiuration like it's entity
 * types, Pools and Flows.
 *
 * @RestResource(
 *   id = "cms_content_sync_sync_core_site_config",
 *   label = @Translation("Content Sync: Sync Core: Site Config"),
 *   uri_paths = {
 *     "canonical" = "/rest/cms-content-sync/v2/config"
 *   }
 * )
 */
class SyncCoreSiteConfigResource extends ResourceBase implements ContentSyncRestInterface {
  use ContentSyncRestTrait;

  /**
   * @var \EdgeBox\SyncCore\Interfaces\ISyncCore
   */
  protected $client;

  /**
   * Constructs an object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param array $serializer_formats
   *   The available serialization formats.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   * @param \Drupal\Core\Extension\ExtensionList $extension_list_module
   *   An extension list instance.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    array $serializer_formats,
    LoggerInterface $logger,
    ?ISyncCore $client,
  ) {
    parent::__construct(
          $configuration,
          $plugin_id,
          $plugin_definition,
          $serializer_formats,
          $logger
      );

    $this->client = $client;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ) {
    // When the config for this route is created, it will instantiate this
    // class but the site won't be registered yet so there's no Sync Core URL
    // set, resulting in an error.
    // But as this interface is used by the Sync Core (at which point a
    // Sync Core URL must have been set), we can safely ignore this.
    try {
      $client = SyncCoreFactory::getSyncCoreV2();
    }
    catch (\Exception $e) {
      $client = NULL;
    }

    return new static(
    $configuration,
    $plugin_id,
    $plugin_definition,
    $container->getParameter('serializer.formats'),
    LoggerProxy::get(),
    $client
    );
  }

  /**
   *
   */
  protected function serializeEntityTypeFields($entity_type_name, $bundle_name, $entity_type, $handler) {
    $field_plugin_manager = \Drupal::service('plugin.manager.cms_content_sync_field_handler');

    $entityFieldManager = \Drupal::service('entity_field.manager');
    /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */
    $fields = $entityFieldManager->getFieldDefinitions($entity_type_name, $bundle_name);

    $forbidden = $handler->getForbiddenFields();

    $added = [];
    foreach ($fields as $key => $field) {
      $field_handlers = $field_plugin_manager->getHandlerOptions($entity_type_name, $bundle_name, $key, $field, TRUE);
      $ignore = in_array($key, $forbidden) || empty($field_handlers);
      if ($ignore) {
        continue;
      }

      $handler_id = $ignore ? 'ignore' : key($field_handlers);

      $field_settings = [
        'handler' => $handler_id,
        'export' => NULL,
        'import' => NULL,
        'preview' => NULL,
        'entity_type' => $entity_type_name,
        'entity_bundle' => $bundle_name,
        'handler_settings' => [],
      ];

      /**
       * @var \Drupal\cms_content_sync\Plugin\FieldHandlerInterface $handler
       */
      $field_handler = $field_plugin_manager->createInstance($handler_id, [
        'entity_type_name' => $entity_type_name,
        'bundle_name' => $bundle_name,
        'field_name' => $key,
        'field_definition' => $field,
        'settings' => $field_settings,
        'sync' => NULL,
      ]);

      $field_handler->definePropertyAtType($entity_type);

      $added[] = $key;
    }

    if (!in_array('created', $added)) {
      $entity_type
        ->addObjectProperty('created', 'Created', FALSE, FALSE, 'created')
        ->addIntegerProperty('value', 'Value', FALSE, TRUE, 'integer')
        ->setFormat(RemoteEntityTypePropertyFormat::UNIX_TIMESTAMP);
    }
    if (!in_array('changed', $added)) {
      $entity_type
        ->addObjectProperty('changed', 'Changed', FALSE, FALSE, 'changed')
        ->addIntegerProperty('value', 'Value', FALSE, TRUE, 'integer')
        ->setFormat(RemoteEntityTypePropertyFormat::UNIX_TIMESTAMP);
    }
  }

  /**
   *
   */
  protected function serializeEntityTypes($full = TRUE, $page = NULL, $items_per_page = NULL) {
    $entity_types = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo();

    $result = [];

    $skip = $page === NULL || $items_per_page === NULL ? NULL : $page * $items_per_page;
    $skipped = 0;

    ksort($entity_types);
    foreach ($entity_types as $entity_type_name => $bundles) {
      ksort($bundles);
      foreach ($bundles as $bundle_name => $entity_bundle) {
        if (!EntityHandlerPluginManager::isSupported($entity_type_name, $bundle_name)) {
          continue;
        }

        $entityPluginManager = \Drupal::service('plugin.manager.cms_content_sync_entity_handler');

        $entity_handlers = $entityPluginManager->getHandlerOptions($entity_type_name, $bundle_name, TRUE);
        if (!count($entity_handlers)) {
          continue;
        }

        if ($skip !== NULL) {
          if ($skipped < $skip) {
            $skipped++;
            continue;
          }
        }

        $entity_handler_names = array_keys($entity_handlers);
        $handler_id = reset($entity_handler_names);

        $handler = $entityPluginManager->createInstance(
          $handler_id,
          [
            'entity_type_name' => $entity_type_name,
            'bundle_name' => $bundle_name,
            'settings' => [],
            'sync' => NULL,
          ]
        );

        $version = Flow::getEntityTypeVersion($entity_type_name, $bundle_name);

        $entity_type_label = $entity_types[$entity_type_name][$bundle_name]['label'];
        $entity_type = $this->client
          ->getConfigurationService()
          ->defineEntityType(NULL, $entity_type_name, $bundle_name, $version, $entity_type_label)
          ->isTranslatable(TRUE);
        $entity_type
          ->addReferenceProperty('menu_items', 'Menu items', TRUE, FALSE, 'menu')
          ->addAllowedType('menu_link_content');

        if ($full && EntityHandlerPluginManager::isEntityTypeFieldable($entity_type_name)) {
          $this->serializeEntityTypeFields($entity_type_name, $bundle_name, $entity_type, $handler);
        }

        // Remote sites must use the same entity type handler otherwise sync
        // will fail- at least for these properties or it will not work at all.
        $handler->updateEntityTypeDefinition($entity_type);

        // Dispatch EntityTypeExport event to give other modules the possibility
        // to adjust the entity type definition and add custom fields.
        \Drupal::service('event_dispatcher')->dispatch(
          new BeforeEntityTypeExport($entity_type_name, $bundle_name, $entity_type),
          BeforeEntityTypeExport::EVENT_NAME
        );

        $result[$entity_type_name . '.' . $bundle_name] = $entity_type;

        if ($items_per_page !== NULL && count($result) >= $items_per_page) {
          break 2;
        }
      }
    }

    return $result;
  }

  /**
   *
   */
  protected function serializeLanguage(string $code, Language $language, $native_language) {
    $language_definition = $this->client->getConfigurationService()->defineLanguage($code, $language->getName());

    if ($language->getDirection()) {
      $language_definition->isRightToLeft($language->getDirection() === Language::DIRECTION_RTL);
    }

    if ($native_language) {
      $language_definition->setNativeName($native_language->getName());
    }

    return $language_definition;
  }

  /**
   *
   */
  protected function serializeFlow(Flow $flow, $entity_types) {
    $all_pools = Pool::getAll();

    $flow_definition = $this->client
      ->getConfigurationService()
      ->defineFlow(
        $flow->id,
        $flow->label(),
        Yaml::encode(\Drupal::service('config.storage')->read('cms_content_sync.flow.' . $flow->id()))
      );

    $allowed_languages = $flow->getController()->getAllowedLanguages();
    if (!empty($allowed_languages)) {
      $flow_definition->allowedLanguages($allowed_languages);
    }

    // Ignore disabled flows at export.
    if (!$flow->get('status')) {
      $flow_definition->isActive(FALSE);
      return $flow_definition;
    }

    foreach ($flow->getController()->getEntityTypeConfig(NULL, NULL, FALSE, TRUE) as $entity_type_name => $bundles) {
      foreach ($bundles as $bundle_name => $type) {
        $version = $type['version'];

        if (Flow::HANDLER_IGNORE == $type['handler']) {
          continue;
        }

        $current = Flow::getEntityTypeVersion($entity_type_name, $bundle_name);
        if ($current !== $version) {
          throw new \Exception("Entity type {$entity_type_name}.{$bundle_name} was changed without updating Flow {$flow->id}. Please re-save that Flow first to apply the latest entity type changes.");
        }

        if (empty($entity_types[$entity_type_name . '.' . $bundle_name])) {
          throw new \Exception("Entity type {$entity_type_name}.{$bundle_name} is missing in the cache.");
          continue;
        }

        $entity_type = $entity_types[$entity_type_name . '.' . $bundle_name];

        $entity_type_pools = [];
        if (isset($type['import_pools'])) {
          foreach ($type['import_pools'] as $pool_id => $state) {
            if (!isset($entity_type_pools[$pool_id])) {
              $entity_type_pools[$pool_id] = [];
            }

            if (PullIntent::PULL_DISABLED == $type['import']) {
              $entity_type_pools[$pool_id]['import'] = Pool::POOL_USAGE_FORBID;

              continue;
            }

            $entity_type_pools[$pool_id]['import'] = $state;
          }
        }

        if (isset($type['export_pools'])) {
          foreach ($type['export_pools'] as $pool_id => $state) {
            if (!isset($entity_type_pools[$pool_id])) {
              $entity_type_pools[$pool_id] = [];
            }

            if (PushIntent::PUSH_DISABLED == $type['export']) {
              $entity_type_pools[$pool_id]['export'] = Pool::POOL_USAGE_FORBID;

              continue;
            }

            $entity_type_pools[$pool_id]['export'] = $state;
          }
        }

        foreach ($entity_type_pools as $pool_id => $definition) {
          if (empty($all_pools[$pool_id])) {
            continue;
          }

          $export = $definition['export'] ?? NULL;
          $import = $definition['import'] ?? NULL;

          if ((!$export || Pool::POOL_USAGE_FORBID == $export) && (!$import || Pool::POOL_USAGE_FORBID == $import)) {
            continue;
          }

          $pull_condition = [];

          if (EntityHandlerPluginManager::isEntityTypeFieldable($entity_type_name)) {
            $entityFieldManager = \Drupal::service('entity_field.manager');
            /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */
            $fields = $entityFieldManager->getFieldDefinitions($entity_type_name, $bundle_name);

            $entityPluginManager = \Drupal::service('plugin.manager.cms_content_sync_entity_handler');

            $entity_handlers = $entityPluginManager->getHandlerOptions($entity_type_name, $bundle_name, TRUE);
            if (!count($entity_handlers)) {
              continue;
            }

            $entity_handler_names = array_keys($entity_handlers);
            $handler_id = reset($entity_handler_names);

            $handler = $entityPluginManager->createInstance(
              $handler_id,
              [
                'entity_type_name' => $entity_type_name,
                'bundle_name' => $bundle_name,
                'settings' => [],
                'sync' => NULL,
              ]
            );

            $forbidden = $handler->getForbiddenFields();

            foreach ($fields as $key => $field) {
              $field_config = $flow->getController()->getPropertyConfig($entity_type_name, $bundle_name, $key);
              if (!$field_config) {
                continue;
              }
              // Exception for taxonomy terms so they can be filtered by parent
              // to only pull a sub-tree of terms.
              if (in_array($key, $forbidden) && $key !== "parent") {
                continue;
              }

              if (!empty($field_config['handler_settings']['subscribe_only_to'])) {
                $allowed = [];

                foreach ($field_config['handler_settings']['subscribe_only_to'] as $ref) {
                  $allowed[] = $ref['uuid'];
                }

                $pull_condition[$key] = $allowed;
              }
            }
          }

          /**
           * @var \EdgeBox\SyncCore\Interfaces\Configuration\IDefinePoolForFlow $pool_definition
           */
          $pool_definition = $flow_definition->usePool($pool_id);

          if (Pool::POOL_USAGE_FORBID != $export && PushIntent::PUSH_DISABLED != $type['export']) {
            $push_config = $pool_definition
              ->enablePush($entity_type);
            if ($push_config) {
              $push_config
                ->manually(PushIntent::PUSH_MANUALLY == $type['export'])
                ->asDependency(PushIntent::PUSH_AS_DEPENDENCY == $type['export'])
                ->pushDeletions(boolval($type['export_deletion_settings']['export_deletion']));
            }
          }

          if (Pool::POOL_USAGE_FORBID != $import && PullIntent::PULL_DISABLED != $type['import']) {
            $pull_configuration = $pool_definition
              ->enablePull($entity_type)
              ->manually(PullIntent::PULL_MANUALLY == $type['import'])
              ->asDependency(PullIntent::PULL_AS_DEPENDENCY == $type['import'])
              ->pullDeletions(boolval($type['import_deletion_settings']['import_deletion']));

            foreach ($pull_condition as $property => $allowed_entity_ids) {
              $pull_configuration
                ->ifTaggedWith($property, $allowed_entity_ids);
            }
          }
        }
      }
    }

    return $flow_definition;
  }

  /**
   *
   */
  protected function getDtos(array $operations) {
    $result = [];
    foreach ($operations as $operation) {
      $result[] = $operation->getSerializedDto();
    }
    return $result;
  }

  /**
   *
   */
  public function get() {
    $response = new SiteConfigResponse();

    $query = \Drupal::request()->query->all();
    $queryObject = new RemoteRequestQueryParamsSiteConfig($query);

    $mode = $queryObject->getMode();

    $page = $queryObject->getPage();
    if (is_string($page)) {
      $page = (int) $page;
    }
    else {
      $page = NULL;
    }

    $items_per_page = $queryObject->getItemsPerPage();
    if (is_string($items_per_page)) {
      $items_per_page = (int) $items_per_page;
    }
    else {
      $items_per_page = NULL;
    }

    if ($mode === RemoteSiteConfigRequestMode::POOLS || $mode === RemoteSiteConfigRequestMode::CS || $mode === RemoteSiteConfigRequestMode::ALL) {
      $pools = [];

      foreach (Pool::getAll() as $pool) {
        $pools[] = $this->client->getConfigurationService()->usePool($pool->id(), $pool->label());
      }

      $response->setPoolCount(count($pools));
      if ($items_per_page !== 0) {
        $response->setPools($this->getDtos($pools));
      }
    }

    $export_entity_types = $mode === RemoteSiteConfigRequestMode::ENTITY_TYPES || $mode === RemoteSiteConfigRequestMode::ALL;
    $any_entity_types = $mode !== RemoteSiteConfigRequestMode::POOLS && $items_per_page !== 0;

    $entity_types = $any_entity_types ? $this->serializeEntityTypes() : [];

    if ($export_entity_types) {
      $entity_types_detailed = $this->serializeEntityTypes($export_entity_types, $page, $items_per_page);
      $response->setEntityTypeCount(count($entity_types));
      if ($items_per_page !== 0) {
        $response->setEntityTypes($this->getDtos(array_values($entity_types_detailed)));
      }
    }

    if ($mode === RemoteSiteConfigRequestMode::FLOWS || $mode === RemoteSiteConfigRequestMode::CS || $mode === RemoteSiteConfigRequestMode::ALL) {
      $flows = [];

      foreach (Flow::getAll() as $flow) {
        $flow->getController()->updateEntityTypeVersions();

        $flows[] = $this->serializeFlow($flow, $entity_types);
      }

      $response->setFlowCount(count($flows));
      if ($items_per_page !== 0) {
        $response->setFlows($this->getDtos($flows));
      }
    }

    if ($mode === RemoteSiteConfigRequestMode::LANGUAGES || $mode === RemoteSiteConfigRequestMode::ALL) {
      $languages = [];

      $site_languages = \Drupal::languageManager()->getLanguages();
      $native_site_languages = \Drupal::languageManager()->getNativeLanguages();
      foreach ($site_languages as $code => $language) {
        $languages[] = $this->serializeLanguage($code, $language, $native_site_languages[$code] ?? NULL);

        if ($language->isDefault()) {
          $response->setDefaultLanguageCode($code);
        }
      }

      $response->setLanguageCount(count($languages));
      if ($items_per_page !== 0) {
        $response->setLanguages($this->getDtos($languages));
      }
    }

    $body = $response->jsonSerialize();

    // Turn objects into arrays.
    return $this->respondWith(json_decode(json_encode($body), TRUE), self::CODE_OK, FALSE);
  }

}

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

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