content_workflow_bynder-1.0.0/content_workflow_bynder_upload/src/Export/Exporter.php

content_workflow_bynder_upload/src/Export/Exporter.php
<?php

namespace Drupal\content_workflow_bynder_upload\Export;

use GatherContent\DataTypes\Item;
use GatherContent\GatherContentClientInterface;
use Drupal;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\content_workflow_bynder\Entity\MappingInterface;
use Drupal\content_workflow_bynder\MetatagQuery;
use Drupal\content_workflow_bynder_upload\Event\ContentWorkflowBynderUploadContentEvents;
use Drupal\content_workflow_bynder_upload\Event\PostNodeUploadEvent;
use Drupal\content_workflow_bynder_upload\Event\PreNodeUploadEvent;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Class for handling import/update logic from Content Workflow to Drupal.
 */
class Exporter implements ContainerInjectionInterface {

  /**
   * Drupal ContentWorkflowBynder Client.
   *
   * @var \Drupal\content_workflow_bynder\DrupalContentWorkflowBynderClient
   */
  protected $client;

  /**
   * Meta tag Query.
   *
   * @var \Drupal\content_workflow_bynder\MetatagQuery
   */
  protected $metatag;

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

  /**
   * Event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * Module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Content translation manager.
   *
   * @var \Drupal\content_translation\ContentTranslationManagerInterface
   */
  protected $contentTranslation;

  /**
   * Filesystem service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * Collected reference revisions.
   *
   * @var array
   */
  protected $collectedReferenceRevisions = [];

  /**
   * Collected file fields.
   *
   * @var array
   */
  protected $collectedFileFields = [];

  /**
   * List of allowed repeatable Drupal field types.
   *
   * @var array
   */
  const ALLOWED_MULTI_VALUE_TYPES = [
    'text',
    'text_long',
    'text_with_summary',
  ];

  /**
   * Exporter constructor.
   *
   * @param \GatherContent\GatherContentClientInterface $client
   * @param \Drupal\content_workflow_bynder\MetatagQuery $metatag
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   */
  public function __construct(
    GatherContentClientInterface $client,
    MetatagQuery $metatag,
    EntityTypeManagerInterface $entityTypeManager,
    EventDispatcherInterface $eventDispatcher,
    ModuleHandlerInterface $moduleHandler,
    FileSystemInterface $fileSystem
  ) {
    $this->client = $client;
    $this->metatag = $metatag;
    $this->entityTypeManager = $entityTypeManager;
    $this->eventDispatcher = $eventDispatcher;
    $this->moduleHandler = $moduleHandler;
    $this->fileSystem = $fileSystem;

    if ($this->moduleHandler->moduleExists('content_translation')) {
      $this->contentTranslation = Drupal::service('content_translation.manager');
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('content_workflow_bynder.client'),
      $container->get('content_workflow_bynder.metatag'),
      $container->get('entity_type.manager'),
      $container->get('event_dispatcher'),
      $container->get('module_handler'),
      $container->get('file_system')
    );
  }

  /**
   * Getter GatherContentClient.
   */
  public function getClient() {
    return $this->client;
  }

  /**
   * Exports the changes made in Drupal contents.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity entity object.
   * @param \Drupal\content_workflow_bynder\Entity\MappingInterface $mapping
   *   Mapping object.
   * @param int|null $cwbId
   *   ContentWorkflowBynder ID.
   * @param array $context
   *   Batch context.
   *
   * @return int|null|string
   *   Returns entity ID.
   *
   * @throws \Exception
   */
  public function export(EntityInterface $entity, MappingInterface $mapping, $cwbId = NULL, array &$context = []) {
    $this->collectedReferenceRevisions = [];
    $data = $this->processGroups($entity, $mapping);

    $event = $this->eventDispatcher
      ->dispatch(new PreNodeUploadEvent($entity, $data), ContentWorkflowBynderUploadContentEvents::PRE_NODE_UPLOAD);

    /** @var \Drupal\content_workflow_bynder_upload\Event\PreNodeUploadEvent $event */
    $data = $event->getGathercontentValues();

    if (!empty($cwbId)) {
      $item = $this->client->itemUpdatePost($cwbId, $data['content'], $data['assets']);
      $this->updateFileCwbIds($item->assets);
    }
    else {
      $data['name'] = $entity->label();
      $data['template_id'] = $mapping->getGathercontentTemplateId();
      $item = $this->client->itemPost($mapping->getGathercontentProjectId(), new Item($data));
      $cwbId = $item['data']->id;
      $this->updateFileCwbIds($item['meta']->assets);
    }

    $this->eventDispatcher
      ->dispatch(new PostNodeUploadEvent($entity, $data), ContentWorkflowBynderUploadContentEvents::POST_NODE_UPLOAD);

    if (empty($context['results']['mappings'][$mapping->id()])) {
      $context['results']['mappings'][$mapping->id()] = [
        'mapping' => $mapping,
        'cwbIds' => [
          $cwbId => [],
        ],
      ];
    }

    $context['results']['mappings'][$mapping->id()]['cwbIds'][$cwbId][] = $entity;

    foreach ($this->collectedReferenceRevisions as $reference) {
      $context['results']['mappings'][$mapping->id()]['cwbIds'][$cwbId][] = $reference;
    }

    return $entity->id();
  }

  /**
   * Manages the panes and changes the Item object values.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity object.
   * @param \Drupal\content_workflow_bynder\Entity\MappingInterface $mapping
   *   Mappig object.
   *
   * @return array
   *   Returns Content array.
   *
   * @throws \Exception
   */
  public function processGroups(EntityInterface $entity, MappingInterface $mapping) {
    $mappingData = unserialize($mapping->getData());

    if (empty($mappingData)) {
      throw new Exception("Mapping data is empty.");
    }

    $templateData = unserialize($mapping->getTemplate());
    $data = [
      'content' => [],
      'assets' => [],
    ];

    foreach ($templateData->related->structure->groups as $group) {
      $isTranslatable = $this->moduleHandler->moduleExists('content_translation')
        && $this->contentTranslation->isEnabled($mapping->getMappedEntityType(), $mapping->getContentType())
        && isset($mappingData[$group->uuid]['language'])
        && ($mappingData[$group->uuid]['language'] != Language::LANGCODE_NOT_SPECIFIED);

      if ($isTranslatable) {
        $language = $mappingData[$group->uuid]['language'];
      }
      else {
        $language = Language::LANGCODE_NOT_SPECIFIED;
      }

      $fields = $this->processFields($group, $entity, $mappingData, $isTranslatable, $language);
      $data['content'] += $fields['content'];
      $data['assets'] += $fields['assets'];
    }

    return $data;
  }

  /**
   * Processes field data.
   *
   * @param object $group
   *   Group object.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity.
   * @param array $mappingData
   *   Mapping array.
   * @param bool $isTranslatable
   *   Translatable.
   * @param string $language
   *   Language.
   *
   * @return array
   *   Returns data.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function processFields(object $group, EntityInterface $entity, array $mappingData, bool $isTranslatable, string $language) {
    $exportedFields = [];
    $fields = [];
    $assets = [];

    foreach ($group->fields as $field) {
      // Skip field if it is not mapped.
      if (empty($mappingData[$group->uuid]['elements'][$field->uuid])) {
        continue;
      }

      $localFieldId = $mappingData[$group->uuid]['elements'][$field->uuid];
      if ((isset($mappingData[$group->uuid]['type'])
          && $mappingData[$group->uuid]['type'] === 'content')
        || !isset($mappingData[$group->uuid]['type'])
      ) {
        $localIdArray = explode('||', $localFieldId);
        /** @var \Drupal\field\Entity\FieldConfig $fieldInfo */
        $fieldInfo = FieldConfig::load($localIdArray[0]);
        $currentEntity = $entity;
        $type = '';
        $bundle = '';
        $titleField = $currentEntity->getEntityTypeId() . '.' . $currentEntity->bundle() . '.title';

        if ($localIdArray[0] === $titleField
          || $localIdArray[0] === 'title'
        ) {
          $currentFieldName = 'title';
        }
        else {
          $currentFieldName = $fieldInfo->getName();
          $type = $fieldInfo->getType();
          $bundle = $fieldInfo->getTargetBundle();
        }

        // Get the deepest field's value, we need this to collect
        // the referenced entities values.
        $this->processTargets($currentEntity, $currentFieldName, $type, $bundle, $exportedFields, $localIdArray, $isTranslatable, $language);
        $this->collectedReferenceRevisions[] = $currentEntity;

        $isRepeatable = FALSE;
        if ($fieldInfo) {
          $fieldType = $fieldInfo->getType();

          // Field can be an entity reference.
          if (!in_array($fieldType, self::ALLOWED_MULTI_VALUE_TYPES)) {
            if (!empty($localIdArray[1])) {
              $fieldInfo = FieldConfig::load($localIdArray[1]);
            }
          }

          if ($fieldInfo) {
            $fieldType = $fieldInfo->getType();
            $isMultiple = $fieldInfo->getFieldStorageDefinition()->isMultiple();

            $isCwbFieldRepeatable = FALSE;
            if (property_exists($field, 'metadata')) {
              if (!empty($field->metadata) && property_exists($field->metadata, 'repeatable')) {
                $isCwbFieldRepeatable = $field->metadata->repeatable->isRepeatable;
              }
            }

            if ($isMultiple
              && $isCwbFieldRepeatable
              && in_array($fieldType, self::ALLOWED_MULTI_VALUE_TYPES)
            ) {
              $isRepeatable = TRUE;
            }
          }
        }

        $value = $this->processSetFields($field, $currentEntity, $isTranslatable, $language, $currentFieldName, $bundle, $isRepeatable);

        if (!empty($value)) {
          $fields[$field->uuid] = $value;
        }

        $asset = $this->processSetAssets($field, $currentEntity, $isTranslatable, $language, $currentFieldName);

        if (!empty($asset)) {
          $assets[$field->uuid] = $asset;
        }
      }
      elseif ($mappingData[$group->uuid]['type'] === 'metatag') {
        if ($this->moduleHandler->moduleExists('metatag')
          && $this->metatag->checkMetatag($entity->getEntityTypeId(), $entity->bundle())
        ) {
          $fields[$field->uuid] = $this->processMetaTagFields($entity, $localFieldId, $isTranslatable, $language);
        }
      }
    }

    return [
      'content' => $fields,
      'assets' => $assets,
    ];
  }

  /**
   * Processes the target ids for a field.
   *
   * @param \Drupal\Core\Entity\EntityInterface $currentEntity
   *   Entity object.
   * @param string $currentFieldName
   *   Current field name.
   * @param string $type
   *   Current type name.
   * @param string $bundle
   *   Current bundle name.
   * @param array $exportedFields
   *   Array of exported fields, preventing duplications.
   * @param array $localIdArray
   *   Array of mapped embedded field id array.
   * @param bool $isTranslatable
   *   Translatable.
   * @param string $language
   *   Language.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function processTargets(EntityInterface &$currentEntity, string &$currentFieldName, string &$type, string &$bundle, array &$exportedFields, array $localIdArray, bool $isTranslatable, string $language) {
    $idCount = count($localIdArray);

    // Loop through the references, going deeper and deeper.
    for ($i = 0; $i < $idCount - 1; $i++) {
      $localId = $localIdArray[$i];
      $fieldInfo = FieldConfig::load($localId);
      $currentFieldName = $fieldInfo->getName();
      $type = $fieldInfo->getType();
      $bundle = $fieldInfo->getTargetBundle();

      if ($isTranslatable && $currentEntity->hasTranslation($language)) {
        $targetFieldValue = $currentEntity->getTranslation($language)->get($currentFieldName)->getValue();
      }
      else {
        $targetFieldValue = $currentEntity->get($currentFieldName)->getValue();
      }

      // Load the targeted entity and process the data.
      if (!empty($targetFieldValue)) {
        $fieldTargetInfo = FieldConfig::load($localIdArray[$i + 1]);
        $entityStorage = $this->entityTypeManager
          ->getStorage($fieldTargetInfo->getTargetEntityTypeId());
        $childFieldName = $fieldTargetInfo->getName();
        $childType = $fieldInfo->getType();
        $childBundle = $fieldInfo->getTargetBundle();

        foreach ($targetFieldValue as $target) {
          $exportKey = $target['target_id'] . '_' . $childFieldName;

          // The field is already collected.
          if (!empty($exportedFields[$exportKey])) {
            continue;
          }

          $childEntity = $entityStorage->loadByProperties([
            'id' => $target['target_id'],
            'type' => $fieldTargetInfo->getTargetBundle(),
          ]);

          if (!empty($childEntity[$target['target_id']])) {
            $currentEntity = $childEntity[$target['target_id']];
            $currentFieldName = $childFieldName;
            $type = $childType;
            $bundle = $childBundle;

            if ($i == ($idCount - 2)) {
              $exportedFields[$exportKey] = TRUE;
            }
            break;
          }
        }
      }
    }
  }

  /**
   * Processes meta fields.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity object.
   * @param string $localFieldName
   *   Field name.
   * @param bool $isTranslatable
   *   Translatable bool.
   * @param string $language
   *   Language string.
   *
   * @return string
   *   Returns value.
   */
  public function processMetaTagFields(EntityInterface $entity, string $localFieldName, bool $isTranslatable, string $language) {
    $fieldName = $this->metatag->getFirstMetatagField($entity->getEntityTypeId(), $entity->bundle());

    if ($isTranslatable && $entity->hasTranslation($language)) {
      $currentValue = unserialize($entity->getTranslation($language)->{$fieldName}->value);
    }
    else {
      $currentValue = unserialize($entity->{$fieldName}->value);
    }

    return $currentValue[$localFieldName] ?? '';
  }

  /**
   * Set value of the field.
   *
   * @param object $field
   *   Field object.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity object.
   * @param bool $isTranslatable
   *   Translatable bool.
   * @param string $language
   *   Language string.
   * @param string $localFieldName
   *   Field Name.
   * @param string $bundle
   *   Local field Info bundle string.
   * @param bool $isRepeatable
   *   Repeatable bool.
   *
   * @return array|string
   *   Returns value.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function processSetFields(object $field, EntityInterface $entity, bool $isTranslatable, string $language, string $localFieldName, string $bundle, bool $isRepeatable) {
    $value = NULL;

    switch ($field->field_type) {
      case 'attachment':
        // Fetch file targets.
        if ($isTranslatable && $entity->hasTranslation($language)) {
          $targets = $entity->getTranslation($language)->{$localFieldName}->getValue();
        }
        else {
          $targets = $entity->{$localFieldName}->getValue();
        }

        $value = [];
        foreach ($targets as $target) {
          $file = $this->entityTypeManager
            ->getStorage('file')
            ->load($target['target_id']);

          if (empty($file) || $file->get('cwb_file_id')->isEmpty()) {
            continue;
          }

          $value[] = $file->get('cwb_file_id')->first()->getValue()['value'];
        }
        break;

      case 'choice_radio':
      case 'choice_checkbox':
        // Fetch local selected option.
        if ($isTranslatable && $entity->hasTranslation($language)) {
          $targets = $entity->getTranslation($language)->{$localFieldName}->getValue();
        }
        else {
          $targets = $entity->{$localFieldName}->getValue();
        }

        $value = [];

        foreach ($targets as $target) {
          $conditionArray = [
            'tid' => $target['target_id'],
          ];

          if (
            $isTranslatable &&
            $this->moduleHandler->moduleExists('content_translation') &&
            $this->contentTranslation->isEnabled('taxonomy_term', $bundle) &&
            $language !== LanguageInterface::LANGCODE_NOT_SPECIFIED
          ) {
            $conditionArray['langcode'] = $language;
          }

          $terms = $this->entityTypeManager
            ->getStorage('taxonomy_term')
            ->loadByProperties($conditionArray);

          /** @var \Drupal\taxonomy\Entity\Term $term */
          $term = array_shift($terms);
          if (!empty($term)) {
            $optionIds = $term->contentworkflowbynder_option_ids->getValue();
            $options = $field->metadata->choice_fields->options;

            foreach ($optionIds as $optionId) {
              if (!$this->validOptionId(
                $options,
                $optionId['value'])
              ) {
                continue;
              }

              $value[] = [
                'id' => $optionId['value'],
              ];
            }
          }
        }
        break;

      case 'guidelines':
        // We don't upload this because this field shouldn't be
        // edited.
        break;

      default:
        if ($localFieldName === 'title') {
          if ($isTranslatable && $entity->hasTranslation($language)) {
            $value = $entity->getTranslation($language)->getTitle();
          }
          else {
            $value = $entity->getTitle();
          }
        }
        else {
          if ($isTranslatable && $entity->hasTranslation($language)) {
            if ($isRepeatable) {
              $value = $this->getRepeatableFieldValues($entity->getTranslation($language)->{$localFieldName});
            }
            else {
              $value = $entity->getTranslation($language)->{$localFieldName}->value;
            }
          }
          else {
            if ($isRepeatable) {
              $value = $this->getRepeatableFieldValues($entity->{$localFieldName});
            }
            else {
              $value = $entity->{$localFieldName}->value;
            }
          }
        }
        break;
    }

    return $value;
  }

  /**
   * Set assets.
   *
   * @param object $field
   *   Field object.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   Entity object.
   * @param bool $isTranslatable
   *   Translatable bool.
   * @param string $language
   *   Language string.
   * @param string $localFieldName
   *   Field Name.
   *
   * @return array|string
   *   Returns value.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function processSetAssets(object $field, EntityInterface $entity, bool $isTranslatable, string $language, string $localFieldName) {
    $value = NULL;

    switch ($field->field_type) {
      case 'attachment':
        // Fetch file targets.
        if ($isTranslatable && $entity->hasTranslation($language)) {
          $targets = $entity->getTranslation($language)->{$localFieldName}->getValue();
        }
        else {
          $targets = $entity->{$localFieldName}->getValue();
        }

        $value = [];
        foreach ($targets as $target) {
          /** @var \Drupal\file\FileInterface $file */
          $file = $this->entityTypeManager
            ->getStorage('file')
            ->load($target['target_id']);

          if (empty($file) || !$file->get('cwb_file_id')->isEmpty()) {
            continue;
          }

          $value[] = $this->fileSystem->realpath($file->getFileUri());
        }

        $this->collectedFileFields[$field->uuid] = $targets;
        break;
    }

    return $value;
  }

  /**
   * Updates the file managed table to include the new CWB ID for a given file.
   *
   * @param array $returnedAssets
   *   The assets returned by CWB.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function updateFileCwbIds(array $returnedAssets) {
    if (empty($this->collectedFileFields) || empty($returnedAssets)) {
      return;
    }

    foreach ($this->collectedFileFields as $fieldUuid => $fileField) {
      if (empty($returnedAssets[$fieldUuid])) {
        continue;
      }

      foreach ($fileField as $delta => $target) {
        /** @var \Drupal\file\FileInterface $file */
        $file = $this->entityTypeManager
          ->getStorage('file')
          ->load($target['target_id']);

        if (empty($file) || empty($returnedAssets[$fieldUuid][$delta])) {
          continue;
        }

        $file->set('cwb_file_id', $returnedAssets[$fieldUuid][$delta]);
        $file->save();
      }
    }
  }

  /**
   * Check if the given option ID is valid for the template.
   *
   * @param array $options
   *   Options array.
   * @param string $optionId
   *   Option ID.
   *
   * @return bool
   *   Returns if the option ID is valid for a given template.
   */
  protected function validOptionId(array $options, string $optionId) {
    foreach ($options as $option) {
      if ($option->optionId === $optionId) {
        return TRUE;
      }
    }

    return FALSE;
  }

  /**
   * Moves field values into an array.
   *
   * @param \Drupal\Core\Field\FieldItemListInterface $fieldItemList
   *   The list of field values.
   *
   * @return array
   *   Field values in an array.
   */
  protected function getRepeatableFieldValues(FieldItemListInterface $fieldItemList): array {
    $fieldValues = $fieldItemList->getValue();
    $values = [];

    foreach ($fieldValues as $fieldValue) {
      $values[] = $fieldValue['value'];
    }

    return $values;
  }

}

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

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