cms_content_sync-3.0.x-dev/src/Plugin/cms_content_sync/entity_handler/DefaultConfigEntityHandler.php
src/Plugin/cms_content_sync/entity_handler/DefaultConfigEntityHandler.php
<?php
namespace Drupal\cms_content_sync\Plugin\cms_content_sync\entity_handler;
use Drupal\cms_content_sync\Plugin\EntityHandlerBase;
use Drupal\cms_content_sync\PullIntent;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncIntent;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineEntityType;
use EdgeBox\SyncCore\V2\Configuration\DefineProperty;
/**
* Class DefaultConfigEntityHandler.
*
* Providing a minimalistic implementation for any config entity type.
*
* @EntityHandler(
* id = "cms_content_sync_default_config_entity_handler",
* label = @Translation("Default Config"),
* weight = 100
* )
*/
class DefaultConfigEntityHandler extends EntityHandlerBase {
/**
* {@inheritdoc}
*/
public static function supports($entity_type, $bundle) {
// Whitelist supported entity types.
$entity_types = [
'block',
'entity_queue',
'view',
'webform',
];
return in_array($entity_type, $entity_types);
}
/**
* Update the entity type definition.
*
* @param \EdgeBox\SyncCore\Interfaces\Configuration\IDefineEntityType|EdgeBox\SyncCore\V2\Configuration\DefineProperty $definition
* The entity type definition.
* @param array $config_properties
* The configuration properties.
*/
public function updateEntityTypeDefinition(IDefineEntityType|DefineProperty &$definition, ?array $config_properties = NULL) {
if (!$config_properties) {
$config_properties = $this->getConfigProperties();
}
// TODO: Export additional constraints like allowed values.
foreach ($config_properties as $key => $config) {
$remoteType = $this->getPropertyTypeForConfigType($config['type']);
$multiple = FALSE;
if ('array' === $remoteType) {
$multiple = TRUE;
$remoteType = $this->getPropertyTypeForConfigType($config['sequence']['type']);
}
if (empty($remoteType) || 'ignore' === $remoteType) {
continue;
}
$label = empty($config['label']) ? $key : $config['label'];
if ('string' === $remoteType) {
$definition->addStringProperty($key, $label, $multiple, FALSE, $config['type']);
}
elseif ('boolean' === $remoteType) {
$definition->addBooleanProperty($key, $label, $multiple, FALSE, $config['type']);
}
elseif ('integer' === $remoteType) {
$definition->addIntegerProperty($key, $label, $multiple, FALSE, $config['type']);
}
elseif ('float' === $remoteType) {
$definition->addFloatProperty($key, $label, $multiple, FALSE, $config['type']);
}
elseif ('object' === $remoteType) {
$property = $definition->addObjectProperty($key, $label, $multiple, FALSE, $config['type']);
if (!empty($config['mapping'])) {
$this->updateEntityTypeDefinition($property, $config['mapping']);
}
elseif (!empty($config['sequence']['type'])) {
$this->updateEntityTypeDefinition($property, ['*' => $config['sequence']]);
}
// TODO: Add support for nested types (type references).
}
}
}
/**
* {@inheritdoc}
*/
public function getAllowedPreviewOptions() {
return [];
}
/**
* {@inheritdoc}
*/
public function push(PushIntent $intent, ?EntityInterface $entity = NULL) {
if (!parent::push($intent, $entity)) {
return FALSE;
}
if (!$entity) {
$entity = $intent->getEntity();
}
/**
* @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
*/
foreach ($this->getConfigProperties() as $property => $config) {
$remoteType = $this->getPropertyTypeForConfigType($config['type']);
if (empty($remoteType) || 'ignore' === $remoteType) {
continue;
}
$entity_property = $entity->get($property);
// Avoid incorrect serialization of empty objects. As PHP uses associative
// arrays instead of objects, an empty associative array will become
// "[]" in JSON although it should be "{}" which will lead to a validation
// error response from the Sync Core that expects an object and not
// an array.
if (is_array($entity_property) && !count($entity_property)) {
$entity_property = NULL;
}
$intent->setProperty($property, $entity_property);
}
if ('block' === $this->entityTypeName) {
$plugin = $entity->get('plugin');
// Add block content as a dependency.
if ('block_content:' === substr($plugin, 0, 14)) {
$block_uuid = substr($plugin, 14);
$block_content = \Drupal::service('entity.repository')
->loadEntityByUuid('block_content', $block_uuid);
$intent->addDependency($block_content);
}
}
return TRUE;
}
/**
* Pull the remote entity.
*
* {@inheritdoc}
*/
public function pull(PullIntent $intent) {
$action = $intent->getAction();
if (!parent::pull($intent)) {
return FALSE;
}
if (SyncIntent::ACTION_DELETE === $action) {
return TRUE;
}
/**
* @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
*/
$entity = $intent->getEntity();
$forbidden_fields = $this->getForbiddenFields();
foreach ($this->getConfigProperties() as $property => $config) {
if (in_array($property, $forbidden_fields)) {
continue;
}
$type = $this->getPropertyTypeForConfigType($config['type']);
if (empty($type) || $type === 'ignore') {
continue;
}
$value = $intent->getProperty($property);
if (NULL === $value) {
if ('object' === $type || 'array' === $type) {
$value = [];
}
}
$entity->set($property, $value);
}
$entity->save();
return TRUE;
}
/**
* {@inheritDoc}
*/
public function getViewUrl(EntityInterface $entity) {
if ('view' === $entity->getEntityTypeId()) {
return Url::fromRoute('entity.view.edit_form', ['view' => $entity->id()], ['absolute' => TRUE])->toString();
}
if ('block' === $entity->getEntityTypeId()) {
return Url::fromRoute('entity.block.edit_form', ['block' => $entity->id()], ['absolute' => TRUE])->toString();
}
return parent::getViewUrl($entity);
}
/**
* Get the property type for a config type.
*
* @param string $type
* The config type to check for.
*/
protected function getPropertyTypeForConfigType($type) {
// Add properties that are listed as "config_export" keys from the entity
// type annotation.
static $typeMapping = [
'uuid' => 'string',
'boolean' => 'boolean',
'email' => 'string',
'integer' => 'integer',
'float' => 'float',
'string' => 'string',
'id' => 'string',
'text' => 'string',
'label' => 'string',
'langcode' => 'string',
'uri' => 'string',
'mapping' => 'object',
'sequence' => 'object',
'config_dependencies' => 'object',
'block.settings.[%parent.plugin]' => 'object',
'entityqueue_handler_configuration.[%parent.handler]' => 'object',
'_core_config_info' => 'ignore',
];
return empty($typeMapping[$type]) ? NULL : $typeMapping[$type];
}
/**
* {@inheritDoc}
*/
protected function getBaseEntityProperties(PullIntent $intent) {
$properties = parent::getBaseEntityProperties($intent);
$properties['plugin'] = $intent->getProperty('plugin');
return $properties;
}
/**
* Get all config properties for this entity type.
*
* @return array
* The config properties for this entity type.
*/
protected function getConfigProperties() {
/**
* @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type
*/
$entity_type = \Drupal::entityTypeManager()->getDefinition($this->entityTypeName);
$properties = $entity_type->getPropertiesToExport();
if (!$properties) {
return [];
}
$prefix = $entity_type->getConfigPrefix();
$config_definition = \Drupal::service('config.typed')->getDefinition($prefix . '.' . $this->bundleName . '.*');
if (empty($config_definition['mapping'])) {
return [];
}
$mapping = $config_definition['mapping'];
$result = [];
foreach ($properties as $property) {
// Wrong information from webform schema definition...
// Associative arrays are NOT sequences.
if ('webform' === $this->entityTypeName && 'access' === $property) {
$mapping[$property]['type'] = 'mapping';
}
$result[$property] = $mapping[$property];
}
return $result;
}
/**
* Check whether the entity type supports having a label.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*
* @return bool
* True if the entity type supports having a label.
*/
protected function hasLabelProperty() {
return TRUE;
}
}
