public function LingotekContentTranslationService::saveTargetData in Lingotek Translation 3.1.x
Same name and namespace in other branches
- 8 src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 8.2 src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 4.0.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.0.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.2.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.3.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.4.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.5.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.6.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.7.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
- 3.8.x src/LingotekContentTranslationService.php \Drupal\lingotek\LingotekContentTranslationService::saveTargetData()
Save the entity translation.
Parameters
\Drupal\Core\Entity\ContentEntityInterface &$entity: The entity we want to save a translation for.
$locale: The locale of the translation being saved.
$data: The data being saved.
Return value
\Drupal\Core\Entity\ContentEntityInterface Returns the entity which translations are saved.
Overrides LingotekContentTranslationServiceInterface::saveTargetData
2 calls to LingotekContentTranslationService::saveTargetData()
- LingotekContentTranslationService::downloadDocument in src/
LingotekContentTranslationService.php - Downloads a document from the Lingotek service for a given locale.
- LingotekContentTranslationService::downloadDocuments in src/
LingotekContentTranslationService.php - Downloads a document from the Lingotek service for all available locales.
File
- src/
LingotekContentTranslationService.php, line 1272
Class
- LingotekContentTranslationService
- Service for managing Lingotek content translations.
Namespace
Drupal\lingotekCode
public function saveTargetData(ContentEntityInterface &$entity, $langcode, $data) {
// Without a defined langcode, we can't proceed
if (!$langcode) {
// TODO: log warning that downloaded translation's langcode is not enabled.
return FALSE;
}
$storage_definitions = $this->entityFieldManager
->getFieldStorageDefinitions($entity
->getEntityTypeId());
try {
// We need to load the revision that was uploaded for consistency. For that,
// we check if we have a valid revision in the response, and if not, we
// check the date of the uploaded document.
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$revision = isset($data['_lingotek_metadata']) && isset($data['_lingotek_metadata']['_entity_revision']) ? $data['_lingotek_metadata']['_entity_revision'] : NULL;
$revision = $this
->loadUploadedRevision($entity, $revision);
// Initialize the translation on the Drupal side, if necessary.
/** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
if (!$entity
->hasTranslation($langcode)) {
$entity
->addTranslation($langcode, $revision
->toArray());
}
$translation = $entity
->getTranslation($langcode);
foreach ($data as $name => $field_data) {
if (strpos($name, '_') === 0) {
// Skip special fields underscored.
break;
}
$field_definition = $entity
->getFieldDefinition($name);
if ($field_definition && ($field_definition
->isTranslatable() || $field_definition
->getType() === 'entity_reference_revisions') && $this->lingotekConfiguration
->isFieldLingotekEnabled($entity
->getEntityTypeId(), $entity
->bundle(), $name)) {
// First we check if this is a entity reference, and save the translated entity.
$field_type = $field_definition
->getType();
if ($field_type === 'entity_reference' || $field_type === 'er_viewmode' || $field_type === 'bricks') {
$target_entity_type_id = $field_definition
->getFieldStorageDefinition()
->getSetting('target_type');
$translation->{$name} = NULL;
$delta = 0;
foreach ($field_data as $index => $field_item) {
if (isset($field_item['_lingotek_metadata'])) {
$target_entity_type_id = $field_item['_lingotek_metadata']['_entity_type_id'];
$embedded_entity_id = $field_item['_lingotek_metadata']['_entity_id'];
$embedded_entity_revision_id = $field_item['_lingotek_metadata']['_entity_revision'];
}
else {
// Try to get it from the revision itself. It may have been
// modified, so this can be a source of errors, but we need this
// because we didn't have metadata before.
$embedded_entity_id = $revision->{$name}
->get($index)
->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) {
// ToDo: It can be a content entity, or a config entity.
if ($embedded_entity instanceof ContentEntityInterface) {
if ($this->lingotekConfiguration
->isEnabled($embedded_entity
->getEntityTypeId(), $embedded_entity
->bundle())) {
$this
->saveTargetData($embedded_entity, $langcode, $field_item);
}
else {
\Drupal::logger('lingotek')
->warning($this
->t('Field %field not saved as its referenced entity is not translatable by Lingotek', [
'%field' => $name,
]));
}
}
elseif ($embedded_entity instanceof ConfigEntityInterface) {
$this->lingotekConfigTranslation
->saveTargetData($embedded_entity, $langcode, $field_item);
}
// Now the embedded entity is saved, but we need to ensure
// the reference will be saved too.
$translation->{$name}
->set($delta, $embedded_entity_id);
$delta++;
}
}
}
elseif ($field_type === 'entity_reference_revisions') {
$paragraphTranslatable = $field_definition
->isTranslatable();
$target_entity_type_id = $field_definition
->getFieldStorageDefinition()
->getSetting('target_type');
if ($paragraphTranslatable) {
$translation->{$name} = NULL;
}
$delta = 0;
$fieldValues = [];
foreach ($field_data as $index => $field_item) {
$embedded_entity_id = $revision->{$name}
->get($index)
->get('target_id')
->getValue();
/** @var \Drupal\Core\Entity\RevisionableInterface $embedded_entity */
$embedded_entity = $this->entityTypeManager
->getStorage($target_entity_type_id)
->load($embedded_entity_id);
if ($embedded_entity !== NULL) {
// If there is asymmetrical paragraphs enabled, we need a new one duplicated and stored.
if ($paragraphTranslatable && \Drupal::moduleHandler()
->moduleExists('paragraphs_asymmetric_translation_widgets')) {
/** @var \Drupal\paragraphs\ParagraphInterface $duplicate */
$duplicate = $embedded_entity
->createDuplicate();
if ($duplicate
->isTranslatable()) {
// If there is already a translation for the language we
// want to set as default, we have to remove it. This should
// never happen, but there may different previous approaches
// to translating paragraphs, so we need to make sure the
// download does not break because of this.
if ($duplicate
->hasTranslation($langcode)) {
$duplicate
->removeTranslation($langcode);
$duplicate
->save();
}
$duplicate
->set('langcode', $langcode);
foreach ($duplicate
->getTranslationLanguages(FALSE) as $translationLanguage) {
try {
$duplicate
->removeTranslation($translationLanguage
->getId());
} catch (\InvalidArgumentException $e) {
// Should never happen.
}
}
}
$embedded_entity = $duplicate;
}
$this
->saveTargetData($embedded_entity, $langcode, $field_item);
// Now the embedded entity is saved, but we need to ensure
// the reference will be saved too. Ensure it's the same revision.
$fieldValues[$delta] = [
'target_id' => $embedded_entity
->id(),
'target_revision_id' => $embedded_entity
->getRevisionId(),
];
$delta++;
}
}
// If the paragraph was not translatable, we avoid at all costs to modify the field,
// as this will override the source and may have unintended consequences.
if ($paragraphTranslatable) {
$translation->{$name} = $fieldValues;
}
}
elseif ($field_type === 'path') {
$pid = NULL;
$source = '/' . $entity
->toUrl()
->getInternalPath();
/** @var \Drupal\Core\Entity\EntityStorageInterface $aliasStorage */
$alias_storage = $this->entityTypeManager
->getStorage('path_alias');
/** @var \Drupal\path_alias\PathAliasInterface[] $original_paths */
$original_paths = $alias_storage
->loadByProperties([
'path' => $source,
'langcode' => $entity
->getUntranslated()
->language()
->getId(),
]);
$original_path = NULL;
$alias = $field_data[0]['alias'];
// Validate the alias before saving.
if (!UrlHelper::isValid($alias)) {
\Drupal::logger('lingotek')
->warning($this
->t('Alias for %type %label in language %langcode not saved, invalid uri "%uri"', [
'%type' => $entity
->getEntityTypeId(),
'%label' => $entity
->label(),
'%langcode' => $langcode,
'%uri' => $alias,
]));
// Default to the original path.
if (count($original_paths) > 0) {
$original_path = reset($original_paths);
$alias = $original_path
->getAlias();
}
else {
$alias = $source;
}
if (\Drupal::moduleHandler()
->moduleExists('pathauto')) {
$alias = '';
$translation
->get($name)
->offsetGet(0)
->set('alias', $alias);
$translation
->get($name)
->offsetGet(0)
->set('pathauto', TRUE);
}
}
if ($alias !== NULL) {
$translation
->get($name)
->offsetGet(0)
->set('alias', $alias);
if (\Drupal::moduleHandler()
->moduleExists('pathauto') && !empty($alias) && $alias !== $original_path) {
$translation
->get($name)
->offsetGet(0)
->set('pathauto', FALSE);
}
}
}
elseif ($field_type === 'metatag') {
$index = 0;
foreach ($field_data as $field_item) {
$metatag_value = serialize($field_item);
$translation->{$name}
->set($index, $metatag_value);
++$index;
}
}
elseif ($field_type === 'block_field') {
$translation->{$name} = NULL;
foreach ($field_data as $index => $field_item) {
/** @var \Drupal\Core\Block\BlockPluginInterface $block */
$block = $revision
->get($name)
->get($index)
->getBlock();
if ($block !== NULL) {
$entityData = NULL;
if (isset($field_item['entity'])) {
$entityData = $field_item['entity'];
unset($field_item['entity']);
}
$configuration = $block
->getConfiguration();
$newConfiguration = array_replace($configuration, $field_item);
$translation->{$name}
->set($index, [
'plugin_id' => $block
->getPluginId(),
'settings' => $newConfiguration,
]);
if ($entityData !== NULL) {
$embedded_entity_id = NULL;
if (isset($entityData['_lingotek_metadata'])) {
$target_entity_type_id = $entityData['_lingotek_metadata']['_entity_type_id'];
$embedded_entity_id = $entityData['_lingotek_metadata']['_entity_id'];
$embedded_entity_revision_id = $entityData['_lingotek_metadata']['_entity_revision'];
$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) {
if ($this->lingotekConfiguration
->isEnabled($embedded_entity
->getEntityTypeId(), $embedded_entity
->bundle())) {
$this
->saveTargetData($embedded_entity, $langcode, $entityData);
}
else {
\Drupal::logger('lingotek')
->warning($this
->t('Field %field not saved as its referenced entity is not translatable by Lingotek', [
'%field' => $name,
]));
}
}
}
}
}
}
}
}
else {
// Initialize delta in case there are no items in $field_data.
$delta = -1;
// Save regular fields.
foreach ($field_data as $delta => $delta_data) {
foreach ($delta_data as $property => $property_data) {
$property_definition = $storage_definitions[$name]
->getPropertyDefinition($property);
$data_type = $property_definition
->getDataType();
if ($data_type === 'uri') {
// Validate an uri.
if (!\Drupal::pathValidator()
->isValid($property_data)) {
\Drupal::logger('lingotek')
->warning($this
->t('Field %field for %type %label in language %langcode not saved, invalid uri "%uri"', [
'%field' => $name,
'%type' => $entity
->getEntityTypeId(),
'%label' => $entity
->label(),
'%langcode' => $langcode,
'%uri' => $property_data,
]));
// Let's default to the original value given that there was a problem.
$property_data = $revision
->get($name)
->offsetGet($delta)->{$property};
}
}
if (method_exists($translation
->get($name)
->offsetGet($delta), "set")) {
$translation
->get($name)
->offsetGet($delta)
->set($property, html_entity_decode($property_data));
}
elseif ($translation
->get($name)) {
$translation
->get($name)
->appendItem()
->set($property, html_entity_decode($property_data));
}
}
}
// Remove the rest of deltas that were no longer found in the document downloaded from lingotek.
$continue = TRUE;
while ($continue) {
if ($translation
->get($name)
->offsetExists($delta + 1)) {
$translation
->get($name)
->removeItem($delta + 1);
}
else {
$continue = FALSE;
}
}
}
}
}
// We need to set the content_translation source so the files are synced
// properly. See https://www.drupal.org/node/2544696 for more information.
$translation
->set('content_translation_source', $entity
->getUntranslated()
->language()
->getId());
$entity->lingotek_processed = TRUE;
// Allow other modules to alter the translation before is saved.
\Drupal::moduleHandler()
->invokeAll('lingotek_content_entity_translation_presave', [
&$translation,
$langcode,
$data,
]);
$status_field = $entity
->getEntityType()
->getKey('status');
$status_field_definition = $entity
->getFieldDefinition($status_field);
if ($status_field_definition !== NULL && $status_field_definition
->isTranslatable()) {
$status_setting = $this->lingotekConfiguration
->getPreference('target_download_status');
if ($status_setting !== "same-as-source") {
$status_value = $status_setting === 'published' ? NodeInterface::PUBLISHED : NodeInterface::NOT_PUBLISHED;
$translation
->set($status_field, $status_value);
}
}
// If there is any content moderation module is enabled, we may need to
// perform a transition in their workflow.
/** @var \Drupal\lingotek\Moderation\LingotekModerationFactoryInterface $moderation_factory */
$moderation_factory = \Drupal::service('lingotek.moderation_factory');
$moderation_handler = $moderation_factory
->getModerationHandler();
$moderation_handler
->performModerationTransitionIfNeeded($translation);
if ($moderation_handler
->isModerationEnabled($translation) && $translation instanceof RevisionLogInterface) {
$requestTime = \Drupal::time()
->getRequestTime();
$translation
->setNewRevision(TRUE);
$translation
->setRevisionUserId(\Drupal::currentUser()
->id());
$translation
->setRevisionCreationTime($requestTime);
$translation
->setRevisionTranslationAffected(TRUE);
$translation
->setChangedTime($requestTime);
$translation
->setRevisionLogMessage((string) new FormattableMarkup('Document translated into @langcode by Lingotek.', [
'@langcode' => strtoupper($langcode),
]));
}
$translation
->save();
return $entity;
} catch (EntityStorageException $storage_exception) {
$this
->setTargetStatus($entity, $langcode, Lingotek::STATUS_ERROR);
throw new LingotekContentEntityStorageException($entity, $storage_exception, $storage_exception
->getMessage());
}
}