/**
  * Set the entity of this source.
  */
 public function setEntity(ContentEntityInterface $entity)
 {
     $this->entity = $entity;
     if ($this->entity->hasTranslation($this->getLanguage())) {
         $this->entity = $this->entity->getTranslation($this->getLanguage());
     }
 }
Example #2
0
 /**
  * Ensures correct entity URLs with the method language-content-entity enabled.
  *
  * Test case with the method language-content-entity enabled and configured
  * with higher and also with lower priority than the method language-url.
  */
 public function testEntityUrlLanguageWithLanguageContentEnabled()
 {
     // Define the method language-content-entity with a higher priority than
     // language-url.
     $config = $this->config('language.types');
     $config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
     $config->set('negotiation.language_content.enabled', [LanguageNegotiationContentEntity::METHOD_ID => 0, LanguageNegotiationUrl::METHOD_ID => 1]);
     $config->save();
     // Without being on an content entity route the default entity URL tests
     // should still pass.
     $this->testEntityUrlLanguage();
     // Now switching to an entity route, so that the URL links are generated
     // while being on an entity route.
     $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
     // The method language-content-entity should run before language-url and
     // append query parameter for the content language and prevent language-url
     // from overwriting the url.
     $this->assertTrue(strpos($this->entity->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=en') !== FALSE);
     $this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=es') !== FALSE);
     $this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=fr') !== FALSE);
     // Define the method language-url with a higher priority than
     // language-content-entity. This configuration should match the default one,
     // where the language-content-entity is turned off.
     $config->set('negotiation.language_content.enabled', [LanguageNegotiationUrl::METHOD_ID => 0, LanguageNegotiationContentEntity::METHOD_ID => 1]);
     $config->save();
     // The default entity URL tests should pass again with the current
     // configuration.
     $this->testEntityUrlLanguage();
 }
 /**
  * {@inheritdoc}
  */
 public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode, $original_langcode = NULL)
 {
     $translations = $entity->getTranslationLanguages();
     $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
     // If we have no information about what to sync to, if we are creating a new
     // entity, if we have no translations for the current entity and we are not
     // creating one, then there is nothing to synchronize.
     if (empty($sync_langcode) || $entity->isNew() || count($translations) < 2) {
         return;
     }
     // If the entity language is being changed there is nothing to synchronize.
     $entity_type = $entity->getEntityTypeId();
     $entity_unchanged = isset($entity->original) ? $entity->original : $this->entityManager->getStorage($entity_type)->loadUnchanged($entity->id());
     if ($entity->getUntranslated()->language()->getId() != $entity_unchanged->getUntranslated()->language()->getId()) {
         return;
     }
     /** @var \Drupal\Core\Field\FieldItemListInterface $items */
     foreach ($entity as $field_name => $items) {
         $field_definition = $items->getFieldDefinition();
         $field_type_definition = $field_type_manager->getDefinition($field_definition->getType());
         $column_groups = $field_type_definition['column_groups'];
         // Sync if the field is translatable, not empty, and the synchronization
         // setting is enabled.
         if ($field_definition instanceof ThirdPartySettingsInterface && $field_definition->isTranslatable() && !$items->isEmpty() && ($translation_sync = $field_definition->getThirdPartySetting('content_translation', 'translation_sync'))) {
             // Retrieve all the untranslatable column groups and merge them into
             // single list.
             $groups = array_keys(array_diff($translation_sync, array_filter($translation_sync)));
             if (!empty($groups)) {
                 $columns = array();
                 foreach ($groups as $group) {
                     $info = $column_groups[$group];
                     // A missing 'columns' key indicates we have a single-column group.
                     $columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : array($group));
                 }
                 if (!empty($columns)) {
                     $values = array();
                     foreach ($translations as $langcode => $language) {
                         $values[$langcode] = $entity->getTranslation($langcode)->get($field_name)->getValue();
                     }
                     // If a translation is being created, the original values should be
                     // used as the unchanged items. In fact there are no unchanged items
                     // to check against.
                     $langcode = $original_langcode ?: $sync_langcode;
                     $unchanged_items = $entity_unchanged->getTranslation($langcode)->get($field_name)->getValue();
                     $this->synchronizeItems($values, $unchanged_items, $sync_langcode, array_keys($translations), $columns);
                     foreach ($translations as $langcode => $language) {
                         $entity->getTranslation($langcode)->get($field_name)->setValue($values[$langcode]);
                     }
                 }
             }
         }
     }
 }
 /**
  * Tests enabling the language negotiator language_content_entity.
  */
 public function testEnabledLanguageContentNegotiator()
 {
     // Define the method language-url with a higher priority than
     // language-content-entity. This configuration should match the default one,
     // where the language-content-entity is turned off.
     $config = $this->config('language.types');
     $config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
     $config->set('negotiation.language_content.enabled', [LanguageNegotiationUrl::METHOD_ID => 0, LanguageNegotiationContentEntity::METHOD_ID => 1]);
     $config->save();
     // In order to reflect the changes for a multilingual site in the container
     // we have to rebuild it.
     $this->rebuildContainer();
     // The tests for the default configuration should still pass.
     $this->testDefaultConfiguration();
     // Define the method language-content-entity with a higher priority than
     // language-url.
     $config->set('negotiation.language_content.enabled', [LanguageNegotiationContentEntity::METHOD_ID => 0, LanguageNegotiationUrl::METHOD_ID => 1]);
     $config->save();
     // In order to reflect the changes for a multilingual site in the container
     // we have to rebuild it.
     $this->rebuildContainer();
     // The method language-content-entity should run before language-url and
     // append query parameter for the content language and prevent language-url
     // from overwriting the URL.
     $default_site_langcode = $this->config('system.site')->get('default_langcode');
     // Now switching to an entity route, so that the URL links are generated
     // while being on an entity route.
     $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
     $translation = $this->entity;
     $this->drupalGet($translation->urlInfo());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
     $this->assertTrue($last_interface_language == $default_site_langcode && $last_interface_language == $last_content_language && $last_content_language == $translation->language()->getId(), 'Interface language and Content language are the same as the default translation language of the entity.');
     $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
     $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
     $translation = $this->entity->getTranslation('es');
     $this->drupalGet($translation->urlInfo());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
     $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
     $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
     $translation = $this->entity->getTranslation('fr');
     $this->drupalGet($translation->urlInfo());
     $last = $this->container->get('state')->get('language_test.language_negotiation_last');
     $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
     $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
     $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
     $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
 }
 /**
  * Populates target values with the source values.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity being translated.
  * @param \Drupal\Core\Language\LanguageInterface $source
  *   The language to be used as source.
  * @param \Drupal\Core\Language\LanguageInterface $target
  *   The language to be used as target.
  */
 public function prepareTranslation(ContentEntityInterface $entity, LanguageInterface $source, LanguageInterface $target)
 {
     /* @var \Drupal\Core\Entity\ContentEntityInterface $source_translation */
     $source_translation = $entity->getTranslation($source->getId());
     $target_translation = $entity->addTranslation($target->getId(), $source_translation->toArray());
     // Make sure we do not inherit the affected status from the source values.
     if ($entity->getEntityType()->isRevisionable()) {
         $target_translation->setRevisionTranslationAffected(NULL);
     }
     /** @var \Drupal\user\UserInterface $user */
     $user = $this->entityManager()->getStorage('user')->load($this->currentUser()->id());
     $metadata = $this->manager->getTranslationMetadata($target_translation);
     // Update the translation author to current user, as well the translation
     // creation time.
     $metadata->setAuthor($user);
     $metadata->setCreatedTime(REQUEST_TIME);
 }
 /**
  * Saves fields that use the shared tables.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity object.
  * @param string $table_name
  *   (optional) The table name to save to. Defaults to the data table.
  * @param bool $new_revision
  *   (optional) Whether we are dealing with a new revision. By default fetches
  *   the information from the entity object.
  */
 protected function saveToSharedTables(ContentEntityInterface $entity, $table_name = NULL, $new_revision = NULL)
 {
     if (!isset($table_name)) {
         $table_name = $this->dataTable;
     }
     if (!isset($new_revision)) {
         $new_revision = $entity->isNewRevision();
     }
     $revision = $table_name != $this->dataTable;
     if (!$revision || !$new_revision) {
         $key = $revision ? $this->revisionKey : $this->idKey;
         $value = $revision ? $entity->getRevisionId() : $entity->id();
         // Delete and insert to handle removed values.
         $this->database->delete($table_name)->condition($key, $value)->execute();
     }
     $query = $this->database->insert($table_name);
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
         $translation = $entity->getTranslation($langcode);
         $record = $this->mapToDataStorageRecord($translation, $table_name);
         $values = (array) $record;
         $query->fields(array_keys($values))->values($values);
     }
     $query->execute();
 }
 /**
  * Builds the entity translation for the provided translation data.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity for which the translation should be returned.
  * @param array $data
  *   The translation data for the fields.
  * @param string $target_langcode
  *   The target language.
  *
  * @return \Drupal\Core\Entity\ContentEntityInterface $translation
  *   Translation data.
  */
 protected function makePreview(ContentEntityInterface $entity, array $data, $target_langcode)
 {
     // If the translation for this language does not exist yet, initialize it.
     if (!$entity->hasTranslation($target_langcode)) {
         $entity->addTranslation($target_langcode, $entity->toArray());
     }
     $embeded_fields = $this->config('tmgmt_content.settings')->get('embedded_fields');
     $translation = $entity->getTranslation($target_langcode);
     foreach ($data as $name => $field_data) {
         foreach (Element::children($field_data) as $delta) {
             $field_item = $field_data[$delta];
             foreach (Element::children($field_item) as $property) {
                 $property_data = $field_item[$property];
                 // If there is translation data for the field property, save it.
                 if (isset($property_data['#translation']['#text']) && $property_data['#translate']) {
                     $translation->get($name)->offsetGet($delta)->set($property, $property_data['#translation']['#text']);
                 } elseif (isset($embeded_fields[$entity->getEntityTypeId()][$name])) {
                     $this->makePreview($translation->get($name)->offsetGet($delta)->{$property}, $property_data, $target_langcode);
                 }
             }
         }
     }
     return $translation;
 }
 /**
  * Populates the affected flag for all the revision translations.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   An entity object being saved.
  */
 protected function populateAffectedRevisionTranslations(ContentEntityInterface $entity)
 {
     if ($this->entityType->isTranslatable() && $this->entityType->isRevisionable()) {
         $languages = $entity->getTranslationLanguages();
         foreach ($languages as $langcode => $language) {
             $translation = $entity->getTranslation($langcode);
             // Avoid populating the value if it was already manually set.
             $affected = $translation->isRevisionTranslationAffected();
             if (!isset($affected) && $translation->hasTranslationChanges()) {
                 $translation->setRevisionTranslationAffected(TRUE);
             }
         }
     }
 }
 /**
  * Saves values of fields that use dedicated tables.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity.
  * @param bool $update
  *   TRUE if the entity is being updated, FALSE if it is being inserted.
  */
 protected function saveToDedicatedTables(ContentEntityInterface $entity, $update = TRUE)
 {
     $vid = $entity->getRevisionId();
     $id = $entity->id();
     $bundle = $entity->bundle();
     $entity_type = $entity->getEntityTypeId();
     $default_langcode = $entity->getUntranslated()->language()->getId();
     $translation_langcodes = array_keys($entity->getTranslationLanguages());
     $table_mapping = $this->getTableMapping();
     if (!isset($vid)) {
         $vid = $id;
     }
     foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_definition) {
         $storage_definition = $field_definition->getFieldStorageDefinition();
         if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
             continue;
         }
         $table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
         $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
         // Delete and insert, rather than update, in case a value was added.
         if ($update) {
             // Only overwrite the field's base table if saving the default revision
             // of an entity.
             if ($entity->isDefaultRevision()) {
                 $this->database->delete($table_name)->condition('entity_id', $id)->execute();
             }
             if ($this->entityType->isRevisionable()) {
                 $this->database->delete($revision_name)->condition('entity_id', $id)->condition('revision_id', $vid)->execute();
             }
         }
         // Prepare the multi-insert query.
         $do_insert = FALSE;
         $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
         foreach ($storage_definition->getColumns() as $column => $attributes) {
             $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column);
         }
         $query = $this->database->insert($table_name)->fields($columns);
         if ($this->entityType->isRevisionable()) {
             $revision_query = $this->database->insert($revision_name)->fields($columns);
         }
         $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode);
         foreach ($langcodes as $langcode) {
             $delta_count = 0;
             $items = $entity->getTranslation($langcode)->get($field_name);
             $items->filterEmptyItems();
             foreach ($items as $delta => $item) {
                 // We now know we have something to insert.
                 $do_insert = TRUE;
                 $record = array('entity_id' => $id, 'revision_id' => $vid, 'bundle' => $bundle, 'delta' => $delta, 'langcode' => $langcode);
                 foreach ($storage_definition->getColumns() as $column => $attributes) {
                     $column_name = $table_mapping->getFieldColumnName($storage_definition, $column);
                     // Serialize the value if specified in the column schema.
                     $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->{$column}) : $item->{$column};
                 }
                 $query->values($record);
                 if ($this->entityType->isRevisionable()) {
                     $revision_query->values($record);
                 }
                 if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) {
                     break;
                 }
             }
         }
         // Execute the query if we have values to insert.
         if ($do_insert) {
             // Only overwrite the field's base table if saving the default revision
             // of an entity.
             if ($entity->isDefaultRevision()) {
                 $query->execute();
             }
             if ($this->entityType->isRevisionable()) {
                 $revision_query->execute();
             }
         }
     }
 }
 /**
  * Populates target values with the source values.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity being translated.
  * @param \Drupal\Core\Language\LanguageInterface $source
  *   The language to be used as source.
  * @param \Drupal\Core\Language\LanguageInterface $target
  *   The language to be used as target.
  */
 public function prepareTranslation(ContentEntityInterface $entity, LanguageInterface $source, LanguageInterface $target)
 {
     /* @var \Drupal\Core\Entity\ContentEntityInterface $source_translation */
     $source_translation = $entity->getTranslation($source->getId());
     $entity->addTranslation($target->getId(), $source_translation->toArray());
 }
 /**
  * Invokes a method on the Field objects within an entity.
  *
  * @param string $method
  *   The method name.
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity object.
  */
 protected function invokeFieldMethod($method, ContentEntityInterface $entity)
 {
     foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
         $translation = $entity->getTranslation($langcode);
         foreach ($translation->getFields() as $field) {
             $field->{$method}();
         }
     }
 }
 /**
  * Saves translation data in an entity translation.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity for which the translation should be saved.
  * @param array $data
  *   The translation data for the fields.
  * @param string $target_langcode
  *   The target language.
  */
 protected function doSaveTranslations(ContentEntityInterface $entity, array $data, $target_langcode)
 {
     // If the translation for this language does not exist yet, initialize it.
     if (!$entity->hasTranslation($target_langcode)) {
         $entity->addTranslation($target_langcode, $entity->toArray());
     }
     $embeded_fields = \Drupal::config('tmgmt_content.settings')->get('embedded_fields');
     $translation = $entity->getTranslation($target_langcode);
     $manager = \Drupal::service('content_translation.manager');
     $manager->getTranslationMetadata($translation)->setSource($entity->language()->getId());
     foreach ($data as $name => $field_data) {
         foreach (Element::children($field_data) as $delta) {
             $field_item = $field_data[$delta];
             foreach (Element::children($field_item) as $property) {
                 $property_data = $field_item[$property];
                 // If there is translation data for the field property, save it.
                 if (isset($property_data['#translation']['#text']) && $property_data['#translate']) {
                     $translation->get($name)->offsetGet($delta)->set($property, $property_data['#translation']['#text']);
                 } elseif (isset($embeded_fields[$entity->getEntityTypeId()][$name])) {
                     $this->doSaveTranslations($translation->get($name)->offsetGet($delta)->{$property}, $property_data, $target_langcode);
                 }
             }
         }
     }
     $translation->save();
 }
 /**
  * Builds a table row for overview form.
  *
  * @param array ContentEntityInterface $entity
  *   Data needed to build the list row.
  * @param array $bundles
  *   The array of bundles.
  *
  * @return array
  */
 public function overviewRow(ContentEntityInterface $entity, array $bundles)
 {
     $label = $entity->label() ?: $this->t('@type: @id', array('@type' => $entity->getEntityTypeId(), '@id' => $entity->id()));
     // Get existing translations and current job items for the entity
     // to determine translation statuses
     $translations = $entity->getTranslationLanguages();
     $source_lang = $entity->language()->getId();
     $current_job_items = tmgmt_job_item_load_latest('content', $entity->getEntityTypeId(), $entity->id(), $source_lang);
     $row = array('id' => $entity->id(), 'title' => $entity->hasLinkTemplate('canonical') ? $entity->toLink($label, 'canonical')->toString() : ($entity->label() ?: $entity->id()));
     if (count($bundles) > 1) {
         $row['bundle'] = isset($bundles[$entity->bundle()]) ? $bundles[$entity->bundle()] : t('Unknown');
     }
     // Load entity translation specific data.
     $manager = \Drupal::service('content_translation.manager');
     foreach (\Drupal::languageManager()->getLanguages() as $langcode => $language) {
         $translation_status = 'current';
         if ($langcode == $source_lang) {
             $translation_status = 'original';
         } elseif (!isset($translations[$langcode])) {
             $translation_status = 'missing';
         } elseif ($translation = $entity->getTranslation($langcode)) {
             $metadata = $manager->getTranslationMetadata($translation);
             if ($metadata->isOutdated()) {
                 $translation_status = 'outofdate';
             }
         }
         $build = $this->buildTranslationStatus($translation_status, isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL);
         $row['langcode-' . $langcode] = ['data' => \Drupal::service('renderer')->render($build), 'class' => array('langstatus-' . $langcode)];
     }
     return $row;
 }
 /**
  * Checks whether the field values changed compared to the original entity.
  *
  * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
  *   Field definition of field to compare for changes.
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   Entity to check for field changes.
  * @param \Drupal\Core\Entity\ContentEntityInterface $original
  *   Original entity to compare against.
  *
  * @return bool
  *   True if the field value changed from the original entity.
  */
 protected function hasFieldValueChanged(FieldDefinitionInterface $field_definition, ContentEntityInterface $entity, ContentEntityInterface $original)
 {
     $field_name = $field_definition->getName();
     $langcodes = array_keys($entity->getTranslationLanguages());
     if ($langcodes !== array_keys($original->getTranslationLanguages())) {
         // If the list of langcodes has changed, we need to save.
         return TRUE;
     }
     foreach ($langcodes as $langcode) {
         $items = $entity->getTranslation($langcode)->get($field_name)->filterEmptyItems();
         $original_items = $original->getTranslation($langcode)->get($field_name)->filterEmptyItems();
         // If the field items are not equal, we need to save.
         if (!$items->equals($original_items)) {
             return TRUE;
         }
     }
     return FALSE;
 }