You are here

public function LingotekContentTranslationService::getSourceData in Lingotek Translation 3.7.x

Same name and namespace in other branches
  1. 8 src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  2. 8.2 src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  3. 4.0.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  4. 3.0.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  5. 3.1.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  6. 3.2.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  7. 3.3.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  8. 3.4.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  9. 3.5.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  10. 3.6.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()
  11. 3.8.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::getSourceData()

Returns the source data that will be uploaded to the Lingotek service.

Only those fields that have actual translatable text, and have marked for upload will be included.

Parameters

\Drupal\Core\Entity\ContentEntityInterface &$entity: The entity which we want the source data.

Return value

mixed

Overrides LingotekContentTranslationServiceInterface::getSourceData

4 calls to LingotekContentTranslationService::getSourceData()
LingotekContentTranslationService::hasEntityChanged in src/LingotekContentTranslationService.php
Checks if the source entity data has changed from last time we uploaded it.
LingotekContentTranslationService::updateDocument in src/LingotekContentTranslationService.php
Resends a document to the translation service.
LingotekContentTranslationService::updateEntityHash in src/LingotekContentTranslationService.php
Updates the entity hash.
LingotekContentTranslationService::uploadDocument in src/LingotekContentTranslationService.php
Uploads a document to the Lingotek service.

File

src/LingotekContentTranslationService.php, line 578

Class

LingotekContentTranslationService
Service for managing Lingotek content translations.

Namespace

Drupal\lingotek

Code

public function getSourceData(ContentEntityInterface &$entity, &$visited = []) {

  // Logic adapted from Content Translation core module and TMGMT contrib
  // module for pulling translatable field info from content entities.
  $source_entity = NULL;
  if ($entity instanceof RevisionableInterface) {
    $storage = $this->entityTypeManager
      ->getStorage($entity
      ->getEntityTypeId());
    if ($revision_id = $storage
      ->getLatestTranslationAffectedRevisionId($entity
      ->id(), $entity
      ->getUntranslated()
      ->language()
      ->getId())) {
      $source_entity = $storage
        ->loadRevision($revision_id);
    }
    else {
      $source_entity = $entity
        ->getUntranslated();
    }
  }
  $isParentEntity = count($visited) === 0;
  $visited[$entity
    ->bundle()][] = $entity
    ->id();
  $entity_type = $entity
    ->getEntityType();
  $field_definitions = $this->entityFieldManager
    ->getFieldDefinitions($entity
    ->getEntityTypeId(), $entity
    ->bundle());
  $storage_definitions = $entity_type instanceof ContentEntityTypeInterface ? $this->entityFieldManager
    ->getFieldStorageDefinitions($entity_type
    ->id()) : [];
  $translatable_fields = [];

  // We need to include computed fields, as we may have a URL alias.
  foreach ($entity
    ->getFields(TRUE) as $field_name => $definition) {
    if ($this->lingotekConfiguration
      ->isFieldLingotekEnabled($entity
      ->getEntityTypeId(), $entity
      ->bundle(), $field_name) && $field_name != $entity_type
      ->getKey('langcode') && $field_name != $entity_type
      ->getKey('default_langcode')) {
      $translatable_fields[$field_name] = $definition;
    }
  }
  $default_display = $this->entityTypeManager
    ->getStorage('entity_view_display')
    ->load($entity_type
    ->id() . '.' . $entity
    ->bundle() . '.' . 'default');
  if ($default_display !== NULL) {
    uksort($translatable_fields, function ($a, $b) use ($default_display) {
      return SortArray::sortByKeyString($default_display
        ->getComponent($a), $default_display
        ->getComponent($b), 'weight');
    });
  }
  $data = [];
  foreach ($translatable_fields as $k => $definition) {

    // If there is only one relevant attribute, upload it.
    // Get the column translatability configuration.
    module_load_include('inc', 'content_translation', 'content_translation.admin');
    $column_element = content_translation_field_sync_widget($field_definitions[$k]);
    $field = $source_entity
      ->get($k);
    $field_type = $field_definitions[$k]
      ->getType();
    if ($field
      ->isEmpty()) {
      $data[$k] = [];
    }
    foreach ($field as $fkey => $fval) {

      // If we have only one relevant column, upload that. If not, check our
      // settings.
      if (!$column_element) {
        $properties = $fval
          ->getProperties();
        foreach ($properties as $property_name => $property_value) {
          if (isset($storage_definitions[$k])) {
            $property_definition = $storage_definitions[$k]
              ->getPropertyDefinition($property_name);
            $data_type = $property_definition
              ->getDataType();
            if (($data_type === 'string' || $data_type === 'uri') && !$property_definition
              ->isComputed()) {
              if (isset($fval->{$property_name}) && !empty($fval->{$property_name})) {
                $data[$k][$fkey][$property_name] = $fval
                  ->get($property_name)
                  ->getValue();
              }

              // If there is a path item, we need to handle that the pid is a
              // string but we don't want to upload it. See
              // https://www.drupal.org/node/2689253.
              if ($field_type === 'path') {
                unset($data[$k][$fkey]['pid']);
              }
            }
          }
        }
      }
      else {
        $configured_properties = $this->lingotekConfiguration
          ->getFieldPropertiesLingotekEnabled($entity
          ->getEntityTypeId(), $entity
          ->bundle(), $k);
        $properties = $fval
          ->getProperties();
        foreach ($properties as $pkey => $pval) {
          if (isset($configured_properties[$pkey]) && $configured_properties[$pkey]) {
            $data[$k][$fkey][$pkey] = $pval
              ->getValue();
          }
        }
      }
    }
    if ($field_type === 'block_field') {
      foreach ($source_entity->{$k} as $field_item) {
        $pluginId = $field_item
          ->get('plugin_id')
          ->getValue();
        $block_instance = $field_item
          ->getBlock();
        $lingotekConfigTranslation = \Drupal::service('lingotek.config_translation');

        /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager */
        $typedConfigManager = \Drupal::service('config.typed');
        $pluginIDName = $block_instance
          ->getPluginDefinition()['id'];
        $blockConfig = $block_instance
          ->getConfiguration();
        $definition = $typedConfigManager
          ->getDefinition('block.settings.' . $pluginIDName);
        if ($definition['type'] == 'undefined') {
          $definition = $typedConfigManager
            ->getDefinition('block_settings');
        }
        $dataDefinition = $typedConfigManager
          ->buildDataDefinition($definition, $blockConfig);
        $schema = $typedConfigManager
          ->create($dataDefinition, $blockConfig);
        $properties = $lingotekConfigTranslation
          ->getTranslatableProperties($schema, NULL);
        $embedded_data = [];
        foreach ($properties as $property) {
          $propertyParts = explode('.', $property);
          $embedded_data[$property] = NestedArray::getValue($blockConfig, $propertyParts);
        }
        if (strpos($pluginId, 'block_content') === 0) {
          $uuid = $block_instance
            ->getDerivativeId();
          if ($block = \Drupal::service('entity.repository')
            ->loadEntityByUuid('block_content', $uuid)) {
            $embedded_data['entity'] = $this
              ->getSourceData($block, $visited);
          }
        }
        $data[$k][$field_item
          ->getName()] = $embedded_data;
      }
    }
    if ($field_type === 'layout_section') {

      // This means that we are using layout:builder_at. We verify it anyway.
      $layoutBuilderAT = \Drupal::moduleHandler()
        ->moduleExists('layout_builder_at');
      if ($layoutBuilderAT) {
        $block_manager = \Drupal::service('plugin.manager.block');
        $lingotekConfigTranslation = \Drupal::service('lingotek.config_translation');

        /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager */
        $typedConfigManager = \Drupal::service('config.typed');
        $data[$k] = [
          'components' => [],
        ];
        foreach ($source_entity->{$k} as $field_item) {
          $sectionObject = $field_item
            ->getValue()['section'];
          $components = $sectionObject
            ->getComponents();

          /** @var \Drupal\layout_builder\SectionComponent $component */
          foreach ($components as $componentUuid => $component) {

            /** @var \Drupal\Core\Block\BlockPluginInterface $block_instance */

            // TODO: Change this to getConfiguration() when is safe to do so.
            // See https://www.drupal.org/project/drupal/issues/3180555.
            $block_instance = $block_manager
              ->createInstance($component
              ->getPluginId(), $component
              ->get('configuration'));
            $pluginIDName = $block_instance
              ->getPluginDefinition()['id'];
            $blockConfig = $block_instance
              ->getConfiguration();
            $definition = $typedConfigManager
              ->getDefinition('block.settings.' . $pluginIDName);
            if ($definition['type'] == 'undefined') {
              $definition = $typedConfigManager
                ->getDefinition('block_settings');
            }
            $dataDefinition = $typedConfigManager
              ->buildDataDefinition($definition, $blockConfig);
            $schema = $typedConfigManager
              ->create($dataDefinition, $blockConfig);
            $properties = $lingotekConfigTranslation
              ->getTranslatableProperties($schema, NULL);
            $embedded_data = [];
            foreach ($properties as $property) {

              // The data definition will return nested keys as dot separated.
              $propertyParts = explode('.', $property);
              $embedded_data[$property] = NestedArray::getValue($blockConfig, $propertyParts);
            }
            $data[$k]['components'][$componentUuid] = $embedded_data;
            if (strpos($pluginIDName, 'inline_block') === 0) {
              $blockRevisionId = $blockConfig['block_revision_id'];
              if ($block = $this->entityTypeManager
                ->getStorage('block_content')
                ->loadRevision($blockRevisionId)) {
                $data[$k]['entities']['block_content'][$blockRevisionId] = $this
                  ->getSourceData($block, $visited);
              }
            }
          }
        }
      }
    }
    if ($field_type === 'layout_translation') {

      // We need to get the original data from the layout.
      $layoutBuilderST = \Drupal::moduleHandler()
        ->moduleExists('layout_builder_st');
      if ($layoutBuilderST) {
        $data[$k] = [
          'components' => [],
        ];
        $layoutField = $source_entity->{OverridesSectionStorage::FIELD_NAME};
        $block_manager = \Drupal::service('plugin.manager.block');
        $lingotekConfigTranslation = \Drupal::service('lingotek.config_translation');

        /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager */
        $typedConfigManager = \Drupal::service('config.typed');
        $layout = $layoutField
          ->getValue();

        /** @var \Drupal\layout_builder\Section $sectionObject */
        foreach ($layout as $sectionIndex => $section) {
          $sectionObject = $section['section'];
          $components = $sectionObject
            ->getComponents();

          /** @var \Drupal\layout_builder\SectionComponent $component */
          foreach ($components as $componentUuid => $component) {

            /** @var \Drupal\Core\Block\BlockPluginInterface $block_instance */

            // TODO: Change this to getConfiguration() when is safe to do so.
            // See https://www.drupal.org/project/drupal/issues/3180555.
            $block_instance = $block_manager
              ->createInstance($component
              ->getPluginId(), $component
              ->get('configuration'));
            $pluginIDName = $block_instance
              ->getPluginDefinition()['id'];
            $blockConfig = $block_instance
              ->getConfiguration();
            $definition = $typedConfigManager
              ->getDefinition('block.settings.' . $pluginIDName);
            if ($definition['type'] == 'undefined') {
              $definition = $typedConfigManager
                ->getDefinition('block_settings');
            }
            $dataDefinition = $typedConfigManager
              ->buildDataDefinition($definition, $blockConfig);
            $schema = $typedConfigManager
              ->create($dataDefinition, $blockConfig);
            $properties = $lingotekConfigTranslation
              ->getTranslatableProperties($schema, NULL);
            $embedded_data = [];
            foreach ($properties as $property) {

              // The data definition will return nested keys as dot separated.
              $propertyParts = explode('.', $property);
              $embedded_data[$property] = NestedArray::getValue($blockConfig, $propertyParts);
            }
            $data[$k]['components'][$componentUuid] = $embedded_data;
            if (strpos($pluginIDName, 'inline_block') === 0) {
              $blockRevisionId = $blockConfig['block_revision_id'];
              if ($block = $this->entityTypeManager
                ->getStorage('block_content')
                ->loadRevision($blockRevisionId)) {
                $data[$k]['entities']['block_content'][$blockRevisionId] = $this
                  ->getSourceData($block, $visited);
              }
            }
          }
        }
      }
    }

    // If we have an entity reference, we may want to embed it.
    if ($field_type === 'entity_reference' || $field_type === 'er_viewmode' || $field_type === 'bricks') {
      $target_entity_type_id = $field_definitions[$k]
        ->getFieldStorageDefinition()
        ->getSetting('target_type');
      foreach ($source_entity->{$k} as $field_item) {
        $embedded_entity_id = $field_item
          ->get('target_id')
          ->getValue();
        $embedded_entity = $this->entityTypeManager
          ->getStorage($target_entity_type_id)
          ->load($embedded_entity_id);

        // We may have orphan references, so ensure that they exist before
        // continuing.
        if ($embedded_entity !== NULL) {
          if ($embedded_entity instanceof ContentEntityInterface) {

            // We need to avoid cycles if we have several entity references
            // referencing each other.
            if (!isset($visited[$embedded_entity
              ->bundle()]) || !in_array($embedded_entity
              ->id(), $visited[$embedded_entity
              ->bundle()])) {
              $embedded_data = $this
                ->getSourceData($embedded_entity, $visited);
              $data[$k][$field_item
                ->getName()] = $embedded_data;
            }
            else {

              // We don't want to embed the data, but still will need the
              // references, so let's include the metadata.
              $metadata = [];
              $this
                ->includeMetadata($embedded_entity, $metadata, FALSE);
              $data[$k][$field_item
                ->getName()] = $metadata;
            }
          }
          elseif ($embedded_entity instanceof ConfigEntityInterface) {
            $embedded_data = $this->lingotekConfigTranslation
              ->getSourceData($embedded_entity);
            $data[$k][$field_item
              ->getName()] = $embedded_data;
          }
        }
        else {

          // If the referenced entity doesn't exist, remove the target_id
          // that may be already set.
          unset($data[$k]);
        }
      }
    }
    elseif ($field_type === 'entity_reference_revisions' || $field_type === 'cohesion_entity_reference_revisions') {
      $target_entity_type_id = $field_definitions[$k]
        ->getFieldStorageDefinition()
        ->getSetting('target_type');
      foreach ($source_entity->{$k} as $field_item) {
        $embedded_entity_id = $field_item
          ->get('target_id')
          ->getValue();
        $embedded_entity_revision_id = $field_item
          ->get('target_revision_id')
          ->getValue();
        $embedded_entity = $this->entityTypeManager
          ->getStorage($target_entity_type_id)
          ->loadRevision($embedded_entity_revision_id);

        // Handle the unlikely case where a paragraph has lost its parent.
        if (!empty($embedded_entity)) {
          $embedded_data = $this
            ->getSourceData($embedded_entity, $visited);
          $data[$k][$field_item
            ->getName()] = $embedded_data;
        }
        else {

          // If the referenced entity doesn't exist, remove the target_id
          // that may be already set.
          unset($data[$k]);
        }
      }
    }
    elseif ($field_type === 'tablefield') {
      foreach ($source_entity->{$k} as $index => $field_item) {
        $tableValue = $field_item->value;
        $embedded_data = [];
        foreach ($tableValue as $row_index => $row) {
          if ($row_index === 'caption') {
            $embedded_data[$index]['caption'] = $row;
          }
          else {
            foreach ($row as $col_index => $cell) {
              $embedded_data[$index]['row:' . $row_index]['col:' . $col_index] = $cell;
            }
          }
        }
        $data[$k] = $embedded_data;
      }
    }
    elseif ($field_type === 'string_long' && $field
      ->getName() === 'json_values' && $field
      ->getEntity()
      ->getEntityTypeId() === 'cohesion_layout') {
      $value = $source_entity->{$k}->value;
      $layout_canvas = new LayoutCanvas($value);
      foreach ($layout_canvas
        ->iterateCanvas() as $element) {
        $data_layout = [];
        if ($element
          ->isComponent() && ($component = Component::load($element
          ->getComponentID()))) {

          // Get the models of each form field of the component as an array keyed by their uuid
          $component_model = $component
            ->getLayoutCanvasInstance()
            ->iterateModels('component_form');
          if ($element
            ->getModel()) {
            $data_layout = array_merge($data_layout, $this
              ->extractCohesionComponentValues($component_model, $element
              ->getModel()
              ->getValues()));
          }
        }
        if (!empty($data_layout)) {
          $data[$k][$element
            ->getModelUUID()] = $data_layout;
        }
      }
      unset($data[$k][0]);
    }
    elseif ($field_type === 'metatag') {
      foreach ($source_entity->{$k} as $field_item) {
        $metatag_serialized = $field_item
          ->get('value')
          ->getValue();
        $metatags = unserialize($metatag_serialized);
        if ($metatags) {
          $data[$k][$field_item
            ->getName()] = $metatags;
        }
      }
    }
    elseif ($field_type === 'path') {
      if ($source_entity
        ->id()) {
        $source = '/' . $source_entity
          ->toUrl()
          ->getInternalPath();

        /** @var \Drupal\Core\Entity\EntityStorageInterface $aliasStorage */
        $alias_storage = $this->entityTypeManager
          ->getStorage('path_alias');

        /** @var \Drupal\path_alias\PathAliasInterface[] $paths */
        $paths = $alias_storage
          ->loadByProperties([
          'path' => $source,
          'langcode' => $source_entity
            ->language()
            ->getId(),
        ]);
        if (count($paths) > 0) {
          $path = reset($paths);
          $alias = $path
            ->getAlias();
          if ($alias !== NULL) {
            $data[$k][0]['alias'] = $alias;
          }
        }
      }
    }
  }

  // Embed entity metadata. We need to exclude intelligence metadata if it is
  // a child entity.
  $this
    ->includeMetadata($source_entity, $data, $isParentEntity);
  return $data;
}