cms_content_sync-3.0.x-dev/src/Plugin/Type/EntityHandlerPluginManager.php

src/Plugin/Type/EntityHandlerPluginManager.php
<?php

namespace Drupal\cms_content_sync\Plugin\Type;

use Drupal\cms_content_sync\Entity\Pool;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\eck\EckEntityTypeInterface;

/**
 * Manages discovery and instantiation of entity handler plugins.
 *
 * @see \Drupal\cms_content_sync\Annotation\EntityHandler
 * @see \Drupal\cms_content_sync\Plugin\EntityHandlerBase
 * @see \Drupal\cms_content_sync\Plugin\EntityHandlerInterface
 * @see plugin_api
 */
class EntityHandlerPluginManager extends DefaultPluginManager {

  /**
   * Constructor.
   *
   * Constructs a new
   * \Drupal\cms_content_sync\Plugin\Type\EntityHandlerPluginManager object.
   *
   * @param \Traversable $namespaces
   *   An object that implements \Traversable which contains the root paths
   *                                     keyed by the corresponding namespace to look for plugin implementations.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   Cache backend instance to use.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler to invoke the alter hook with.
   */
  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    parent::__construct('Plugin/cms_content_sync/entity_handler', $namespaces, $module_handler, 'Drupal\cms_content_sync\Plugin\EntityHandlerInterface', 'Drupal\cms_content_sync\Annotation\EntityHandler');

    $this->setCacheBackend($cache_backend, 'cms_content_sync_entity_handler_plugins');
    $this->alterInfo('cms_content_sync_entity_handler');
  }

  /**
   * Check if an entity type is fieldable.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface|string $type
   *   The entity type to check.
   *
   * @return bool
   *   True if the entity type is fieldable.
   */
  public static function isEntityTypeFieldable($type) {
    if (is_string($type)) {
      /**
       * @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
       */
      $entityTypeManager = \Drupal::service('entity_type.manager');
      $type = $entityTypeManager->getDefinition($type, FALSE);
    }

    return $type ? $type->entityClassImplements('Drupal\Core\Entity\FieldableEntityInterface') : FALSE;
  }

  /**
   * Check for a config entit type.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface|string $type
   *   The entity type to check.
   *
   * @return bool
   *   True if the entity type is a config entity.
   */
  public static function isEntityTypeConfiguration($type) {
    if (is_string($type)) {
      /**
       * @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
       */
      $entityTypeManager = \Drupal::service('entity_type.manager');
      $type = $entityTypeManager->getDefinition($type);
    }

    return $type->entityClassImplements('Drupal\Core\Config\Entity\ConfigEntityInterface');
  }

  /**
   * Check for a config entit type.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $type
   *   The entity type to check.
   *
   * @return string|null
   *   The property name for the status/published/enabled property.
   */
  public static function getEntityTypeStatusProperty(EntityTypeInterface $type, ?string $bundle) {
    // Nodes and most other types.
    $status_property = $type->getKey('status');
    if ($status_property) {
      return $status_property;
    }
    // Menu items, ECK and other types implementing Drupal's EntityPublishedInterface.
    $status_property = $type->getKey('published');
    if ($status_property) {
      // ECK says it has the property but doesn't always have it. So we need to
      // check whether a *field* with such a name exists.
      if ($type->getProvider() === "eck") {
        // Can't safely say that we have the property, so we say that we don't.
        if (!$bundle) {
          return NULL;
        }

        $fields = \Drupal::service('entity_field.manager')->getFieldDefinitions($type->id(), $bundle);
        if (empty($fields[$status_property])) {
          return NULL;
        }
      }
      return $status_property;
    }

    return NULL;
  }

  /**
   * Map entity by ID.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface|string $type
   *   The entity type to map.
   *
   * @return bool
   *   Whether the ID is identical across sites. If not, the UUID is used.
   */
  public static function mapById($type) {
    $is_entity_subqueue = is_string($type) ? 'entity_subqueue' === $type : 'entity_subqueue' === $type->id();

    return $is_entity_subqueue || self::isEntityTypeConfiguration($type);
  }

  /**
   * Get the shared entity Id.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   *
   * @return string
   *   The shared Id.
   */
  public static function getSharedId(EntityInterface $entity) {
    return self::mapById($entity->getEntityType()) ? $entity->id() : $entity->uuid();
  }

  /**
   * Get entity uuid or null.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   *
   * @return null|string
   *   The entity uuid or null if not found.
   */
  public static function getUuidOrNull(EntityInterface $entity) {
    return self::mapById($entity->getEntityType()) ? NULL : $entity->uuid();
  }

  /**
   * Get entity id or null.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   *
   * @return null|string
   *   The entity id or null if not found.
   */
  public static function getIdOrNull(EntityInterface $entity) {
    return self::mapById($entity->getEntityType()) ? $entity->id() : NULL;
  }

  /**
   * Get the entity type info.
   *
   * @param mixed $type_key
   *   The entity type key.
   * @param mixed $entity_bundle_name
   *   The entity bundle name.
   */
  public static function getEntityTypeInfo($type_key, $entity_bundle_name) {
    static $cache = [];

    if (!empty($cache[$type_key][$entity_bundle_name])) {
      return $cache[$type_key][$entity_bundle_name];
    }

    $info = [
      'entity_type' => $type_key,
      'bundle' => $entity_bundle_name,
      'required_field_not_supported' => FALSE,
      'optional_field_not_supported' => FALSE,
    ];

    /**
     * @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
     */
    $entityTypeManager = \Drupal::service('entity_type.manager');
    $type = $entityTypeManager->getDefinition($type_key);

    /**
     * @var EntityHandlerPluginManager $entityPluginManager
     */
    $entityPluginManager = \Drupal::service('plugin.manager.cms_content_sync_entity_handler');

    $entity_handlers = $entityPluginManager->getHandlerOptions($type_key, $entity_bundle_name, TRUE);
    if (empty($entity_handlers)) {
      $info['no_entity_type_handler'] = TRUE;
      if ('user' == $type_key) {
        $info['security_concerns'] = TRUE;
      }
      elseif ($type instanceof ConfigEntityType) {
        $info['is_config_entity'] = TRUE;
      }
    }
    else {
      $info['no_entity_type_handler'] = FALSE;

      if ('block_content' == $type_key) {
        $info['hint'] = 'except for config like block placement';
      }
      elseif ('paragraph' == $type_key) {
        $info['hint'] = 'Paragraphs version >= 8.x-1.3';
      }

      $entity_handlers = array_keys($entity_handlers);

      $handler = $entityPluginManager->createInstance(reset($entity_handlers), [
        'entity_type_name' => $type_key,
        'bundle_name' => $entity_bundle_name,
        'settings' => [],
        'sync' => NULL,
      ]);

      $reserved = [];
      $pools = Pool::getAll();
      if (count($pools)) {
        // Avoid exception if the site wasn't registered yet but has
        // active Flows, e.g. from a config import.
        try {
          $reserved = reset($pools)
            ->getClient()
            ->getReservedPropertyNames();
        }
        catch (\Exception $e) {
        }
      }
      $forbidden_fields = array_merge(
            $handler->getForbiddenFields(),
            // These are standard fields defined by the Flow
            // Entity type that entities may not override (otherwise
            // these fields will collide with CMS Content Sync functionality)
            $reserved
            );

      $info['unsupported_required_fields'] = [];
      $info['unsupported_optional_fields'] = [];

      /**
       * @var FieldHandlerPluginManager $fieldPluginManager
       */
      $fieldPluginManager = \Drupal::service('plugin.manager.cms_content_sync_field_handler');

      /**
       * @var \Drupal\Core\Entity\EntityFieldManager $entityFieldManager
       */
      $entityFieldManager = \Drupal::service('entity_field.manager');

      if (!self::isEntityTypeFieldable($type)) {
        $info['fieldable'] = FALSE;
      }
      else {
        $info['fieldable'] = TRUE;

        /**
         * @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields
         */
        $fields = $entityFieldManager->getFieldDefinitions($type_key, $entity_bundle_name);

        foreach ($fields as $key => $field) {
          if (in_array($key, $forbidden_fields)) {
            continue;
          }

          $field_handlers = $fieldPluginManager->getHandlerOptions($type_key, $entity_bundle_name, $key, $field, TRUE);
          if (!empty($field_handlers)) {
            continue;
          }

          // This is a virtual field added by the token module. We update menu items along with entities (given the setting is active), so
          // showing this as "not supported" would confuse users.
          if ('menu_link' === $key && 'entity_reference' === $field->getType()) {
            continue;
          }

          $name = $key . ' (' . $field->getType() . ')';

          if ($field->isRequired()) {
            $info['unsupported_required_fields'][] = $name;
          }
          else {
            $info['unsupported_optional_fields'][] = $name;
          }
        }

        $info['required_field_not_supported'] = count($info['unsupported_required_fields']) > 0;
        $info['optional_field_not_supported'] = count($info['unsupported_optional_fields']) > 0;
      }
    }

    $info['is_supported'] = !$info['no_entity_type_handler'] && !$info['required_field_not_supported'];

    return $cache[$type_key][$entity_bundle_name] = $info;
  }

  /**
   * Check whether or not the given entity type is supported.
   *
   * @param string $type_key
   *   The entity type key.
   * @param string $entity_bundle_name
   *   The entity bundle name.
   *
   * @return mixed
   *   True if the entity type is supported, false otherwise.
   */
  public static function isSupported($type_key, $entity_bundle_name) {
    return self::getEntityTypeInfo($type_key, $entity_bundle_name)['is_supported'];
  }

  /**
   * Get the handlers options.
   *
   * @param string $entity_type
   *   The entity type of the processed entity.
   * @param string $bundle
   *   The bundle of the processed entity.
   * @param bool $labels_only
   *   Whether to return labels instead of the whole definition.
   *
   * @return array
   *   An associative array $id=>$label|$handlerDefinition to display options
   */
  public function getHandlerOptions($entity_type, $bundle, $labels_only = FALSE) {
    $options = [];

    foreach ($this->getDefinitions() as $id => $definition) {
      if (!$definition['class']::supports($entity_type, $bundle)) {
        continue;
      }
      $options[$id] = $labels_only ? $definition['label']->render() : $definition;
    }

    return $options;
  }

  /**
   * Return a list of all entity types and which are supported.
   *
   * @return array
   *   Return a list of all entity types and which are supported.
   */
  public static function getEntityTypes() {
    $supported_entity_types = [];

    $entity_types = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo();
    ksort($entity_types);
    foreach ($entity_types as $type_key => $entity_type) {
      if ('cms_content_sync' == substr($type_key, 0, 16)) {
        continue;
      }
      ksort($entity_type);

      foreach ($entity_type as $entity_bundle_name => $entity_bundle) {
        $supported_entity_types[] = EntityHandlerPluginManager::getEntityTypeInfo($type_key, $entity_bundle_name);
      }
    }

    return $supported_entity_types;
  }

  /**
   * {@inheritdoc}
   */
  protected function findDefinitions() {
    $definitions = parent::findDefinitions();
    uasort($definitions, function ($a, $b) {
        return $a['weight'] <=> $b['weight'];
    });

    return $definitions;
  }

}

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

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