View source
<?php
namespace Drupal\gathercontent_upload\Export;
use Cheppers\GatherContent\DataTypes\Item;
use Cheppers\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\gathercontent\Entity\MappingInterface;
use Drupal\gathercontent\MetatagQuery;
use Drupal\gathercontent_upload\Event\GatherUploadContentEvents;
use Drupal\gathercontent_upload\Event\PostNodeUploadEvent;
use Drupal\gathercontent_upload\Event\PreNodeUploadEvent;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Exporter implements ContainerInjectionInterface {
protected $client;
protected $metatag;
protected $entityTypeManager;
protected $eventDispatcher;
protected $moduleHandler;
protected $contentTranslation;
protected $fileSystem;
protected $collectedReferenceRevisions = [];
protected $collectedFileFields = [];
const ALLOWED_MULTI_VALUE_TYPES = [
'text',
'text_long',
'text_with_summary',
];
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');
}
}
public static function create(ContainerInterface $container) {
return new static($container
->get('gathercontent.client'), $container
->get('gathercontent.metatag'), $container
->get('entity_type.manager'), $container
->get('event_dispatcher'), $container
->get('module_handler'), $container
->get('file_system'));
}
public function getClient() {
return $this->client;
}
public function export(EntityInterface $entity, MappingInterface $mapping, $gcId = NULL, array &$context = []) {
$this->collectedReferenceRevisions = [];
$data = $this
->processGroups($entity, $mapping);
$event = $this->eventDispatcher
->dispatch(GatherUploadContentEvents::PRE_NODE_UPLOAD, new PreNodeUploadEvent($entity, $data));
$data = $event
->getGathercontentValues();
if (!empty($gcId)) {
$item = $this->client
->itemUpdatePost($gcId, $data['content'], $data['assets']);
$this
->updateFileGcIds($item->assets);
}
else {
$data['name'] = $entity
->label();
$data['template_id'] = $mapping
->getGathercontentTemplateId();
$item = $this->client
->itemPost($mapping
->getGathercontentProjectId(), new Item($data));
$gcId = $item['data']->id;
$this
->updateFileGcIds($item['meta']->assets);
}
$this->eventDispatcher
->dispatch(GatherUploadContentEvents::POST_NODE_UPLOAD, new PostNodeUploadEvent($entity, $data));
if (empty($context['results']['mappings'][$mapping
->id()])) {
$context['results']['mappings'][$mapping
->id()] = [
'mapping' => $mapping,
'gcIds' => [
$gcId => [],
],
];
}
$context['results']['mappings'][$mapping
->id()]['gcIds'][$gcId][] = $entity;
foreach ($this->collectedReferenceRevisions as $reference) {
$context['results']['mappings'][$mapping
->id()]['gcIds'][$gcId][] = $reference;
}
return $entity
->id();
}
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;
}
public function processFields(object $group, EntityInterface $entity, array $mappingData, bool $isTranslatable, string $language) {
$exportedFields = [];
$fields = [];
$assets = [];
foreach ($group->fields as $field) {
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);
$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();
}
$this
->processTargets($currentEntity, $currentFieldName, $type, $bundle, $exportedFields, $localIdArray, $isTranslatable, $language);
$this->collectedReferenceRevisions[] = $currentEntity;
$isRepeatable = FALSE;
if ($fieldInfo) {
$fieldType = $fieldInfo
->getType();
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();
$isGcFieldRepeatable = FALSE;
if (property_exists($field, 'metadata')) {
if (!empty($field->metadata) && property_exists($field->metadata, 'repeatable')) {
$isGcFieldRepeatable = $field->metadata->repeatable->isRepeatable;
}
}
if ($isMultiple && $isGcFieldRepeatable && 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,
];
}
public function processTargets(EntityInterface &$currentEntity, string &$currentFieldName, string &$type, string &$bundle, array &$exportedFields, array $localIdArray, bool $isTranslatable, string $language) {
$idCount = count($localIdArray);
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();
}
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;
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;
}
}
}
}
}
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] ?? '';
}
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':
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('gc_file_id')
->isEmpty()) {
continue;
}
$value[] = $file
->get('gc_file_id')
->first()
->getValue()['value'];
}
break;
case 'choice_radio':
case 'choice_checkbox':
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);
$term = array_shift($terms);
if (!empty($term)) {
$optionIds = $term->gathercontent_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':
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;
}
public function processSetAssets(object $field, EntityInterface $entity, bool $isTranslatable, string $language, string $localFieldName) {
$value = NULL;
switch ($field->field_type) {
case 'attachment':
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('gc_file_id')
->isEmpty()) {
continue;
}
$value[] = $this->fileSystem
->realpath($file
->getFileUri());
}
$this->collectedFileFields[$field->uuid] = $targets;
break;
}
return $value;
}
public function updateFileGcIds(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) {
$file = $this->entityTypeManager
->getStorage('file')
->load($target['target_id']);
if (empty($file) || empty($returnedAssets[$fieldUuid][$delta])) {
continue;
}
$file
->set('gc_file_id', $returnedAssets[$fieldUuid][$delta]);
$file
->save();
}
}
}
protected function validOptionId(array $options, string $optionId) {
foreach ($options as $option) {
if ($option->optionId === $optionId) {
return TRUE;
}
}
return FALSE;
}
protected function getRepeatableFieldValues(FieldItemListInterface $fieldItemList) : array {
$fieldValues = $fieldItemList
->getValue();
$values = [];
foreach ($fieldValues as $fieldValue) {
$values[] = $fieldValue['value'];
}
return $values;
}
}