Issue #1916790 by plach, YesCT, penyaskito, Gábor Hojtsy, das-peter, herom, larowlan: Convert translation metadata into regular entity fields
parent
c15cf7d234
commit
17df2c5cf9
|
@ -158,11 +158,9 @@ class BlockContentTranslationUITest extends ContentTranslationUITest {
|
|||
$enabled_block_content = $this->createBlockContent();
|
||||
$disabled_block_content = $this->createBlockContent(FALSE, $bundle->id());
|
||||
|
||||
// Make sure that only a single row was inserted into the
|
||||
// {content_translation} table.
|
||||
$rows = db_query('SELECT * FROM {content_translation}')->fetchAll();
|
||||
// Make sure that only a single row was inserted into the block table.
|
||||
$rows = db_query('SELECT * FROM {block_content_field_data} WHERE id = :id', array(':id' => $enabled_block_content->id()))->fetchAll();
|
||||
$this->assertEqual(1, count($rows));
|
||||
$this->assertEqual($enabled_block_content->id(), reset($rows)->entity_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,12 +9,28 @@ namespace Drupal\comment;
|
|||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\content_translation\ContentTranslationHandler;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Defines the translation handler for comments.
|
||||
*/
|
||||
class CommentTranslationHandler extends ContentTranslationHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
|
||||
parent::entityFormAlter($form, $form_state, $entity);
|
||||
|
||||
if (isset($form['content_translation'])) {
|
||||
// We do not need to show these values on comment forms: they inherit the
|
||||
// basic comment property values.
|
||||
$form['content_translation']['status']['#access'] = FALSE;
|
||||
$form['content_translation']['name']['#access'] = FALSE;
|
||||
$form['content_translation']['created']['#access'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -22,4 +38,17 @@ class CommentTranslationHandler extends ContentTranslationHandler {
|
|||
return t('Edit comment @subject', array('@subject' => $entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
if ($form_state->hasValue('content_translation')) {
|
||||
$translation = &$form_state->getValue('content_translation');
|
||||
/** @var \Drupal\comment\CommentInterface $entity */
|
||||
$translation['status'] = $entity->isPublished();
|
||||
$translation['name'] = $entity->getAuthorName();
|
||||
}
|
||||
parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\comment\Tests;
|
|||
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\content_translation\Tests\ContentTranslationUITest;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the Comment Translation UI.
|
||||
|
@ -108,30 +109,58 @@ class CommentTranslationUITest extends ContentTranslationUITest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::assertPublishedStatus().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertPublishedStatus() {
|
||||
parent::assertPublishedStatus();
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId);
|
||||
$user = $this->drupalCreateUser(array('access comments'));
|
||||
$this->drupalLogin($user);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
protected function doTestPublishedStatus() {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$storage = $entity_manager->getStorage($this->entityTypeId);
|
||||
|
||||
// Check that simple users cannot see unpublished field translations.
|
||||
$storage->resetCache();
|
||||
$entity = $storage->load($this->entityId);
|
||||
|
||||
// Unpublish translations.
|
||||
foreach ($this->langcodes as $index => $langcode) {
|
||||
$translation = $this->getTranslation($entity, $langcode);
|
||||
$value = $this->getValue($translation, 'comment_body', $langcode);
|
||||
$this->drupalGet($entity->urlInfo(), array('language' => $languages[$langcode]));
|
||||
if ($index > 0) {
|
||||
$this->assertNoRaw($value, 'Unpublished field translation is not shown.');
|
||||
}
|
||||
else {
|
||||
$this->assertRaw($value, 'Published field translation is shown.');
|
||||
$edit = array('status' => 0);
|
||||
$url = $entity->urlInfo('edit-form', array('language' => ConfigurableLanguage::load($langcode)));
|
||||
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
|
||||
$storage->resetCache();
|
||||
$entity = $storage->load($this->entityId);
|
||||
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($langcode))->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Login as translator again to ensure subsequent tests do not break.
|
||||
$this->drupalLogin($this->translator);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestAuthoringInfo() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$path = $entity->getSystemPath('edit-form');
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$values = array();
|
||||
|
||||
// Post different authoring information for each translation.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$user = $this->drupalCreateUser();
|
||||
$values[$langcode] = array(
|
||||
'uid' => $user->id(),
|
||||
'created' => REQUEST_TIME - mt_rand(0, 1000),
|
||||
);
|
||||
$edit = array(
|
||||
'name' => $user->getUsername(),
|
||||
'date[date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
|
||||
'date[time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
|
||||
);
|
||||
$this->drupalPostForm($path, $edit, $this->getFormSubmitAction($entity, $langcode), array('language' => $languages[$langcode]));
|
||||
}
|
||||
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -97,7 +97,8 @@ function _content_translation_form_language_content_settings_form_alter(array &$
|
|||
if ($fields) {
|
||||
foreach ($fields as $field_name => $definition) {
|
||||
// Allow to configure only fields supporting multilingual storage.
|
||||
if (!empty($storage_definitions[$field_name]) && $storage_definitions[$field_name]->isTranslatable()) {
|
||||
// We skip our own fields as they are always translatable.
|
||||
if (!empty($storage_definitions[$field_name]) && $storage_definitions[$field_name]->isTranslatable() && $storage_definitions[$field_name]->getProvider() != 'content_translation') {
|
||||
$form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = array(
|
||||
'#label' => $definition->getLabel(),
|
||||
'#type' => 'checkbox',
|
||||
|
@ -338,4 +339,5 @@ function content_translation_form_language_content_settings_submit(array $form,
|
|||
// Ensure entity and menu router information are correctly rebuilt.
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder_indicator')->setRebuildNeeded();
|
||||
\Drupal::service('content_translation.updates_manager')->updateDefinitions($entity_types);
|
||||
}
|
||||
|
|
|
@ -5,81 +5,9 @@
|
|||
* Installation functions for Content Translation module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function content_translation_schema() {
|
||||
$schema['content_translation'] = array(
|
||||
'description' => 'Table to track content translations',
|
||||
'fields' => array(
|
||||
'entity_type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => EntityTypeInterface::ID_MAX_LENGTH,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The entity type this translation relates to',
|
||||
),
|
||||
'entity_id' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The entity id this translation relates to',
|
||||
),
|
||||
'langcode' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The target language for this translation.',
|
||||
),
|
||||
'source' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The source language from which this translation was created.',
|
||||
),
|
||||
'outdated' => array(
|
||||
'description' => 'A boolean indicating whether this translation needs to be updated.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The author of this translation.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'Boolean indicating whether the translation is visible to non-translators.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'The Unix timestamp when the translation was created.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the translation was most recently saved.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('entity_type', 'entity_id', 'langcode'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityFormInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
@ -74,8 +75,9 @@ function content_translation_language_types_info_alter(array &$language_types) {
|
|||
* Implements hook_entity_type_alter().
|
||||
*
|
||||
* The content translation UI relies on the entity info to provide its features.
|
||||
* See the documentation of hook_entity_type_build() in the Entity API documentation
|
||||
* for more details on all the entity info keys that may be defined.
|
||||
* See the documentation of hook_entity_type_build() in the Entity API
|
||||
* documentation for more details on all the entity info keys that may be
|
||||
* defined.
|
||||
*
|
||||
* To make Content Translation automatically support an entity type some keys
|
||||
* may need to be defined, but none of them is required unless the entity path
|
||||
|
@ -83,17 +85,27 @@ function content_translation_language_types_info_alter(array &$language_types) {
|
|||
* "/taxonomy/term/{taxonomy_term}"), in which case at least the 'canonical' key
|
||||
* in the 'links' entity info property must be defined.
|
||||
*
|
||||
* Every entity type needs a translation controller to be translated. This can
|
||||
* be specified through the 'translation' key in the 'handlers' entity
|
||||
* annotation property. If an entity type is translatable and no translation
|
||||
* handler is defined, \Drupal\content_translation\ContentTranslationHandler
|
||||
* will be assumed. Every translation handler must implement
|
||||
* Every entity type needs a translation handler to be translated. This can be
|
||||
* specified through the 'translation' key in the 'handlers' entity annotation
|
||||
* property. If an entity type is translatable and no translation handler is
|
||||
* defined, \Drupal\content_translation\ContentTranslationHandler will be
|
||||
* assumed. Every translation handler must implement
|
||||
* \Drupal\content_translation\ContentTranslationHandlerInterface.
|
||||
*
|
||||
* To implement its business logic the content translation UI relies on various
|
||||
* metadata items describing the translation state. The default implementation
|
||||
* is provided by \Drupal\content_translation\ContentTranslationMetadataWrapper,
|
||||
* which is relying on one field for each metadata item (field definitions are
|
||||
* provided by the translation handler). Entity types needing to customize this
|
||||
* behavior can specify an alternative class through the
|
||||
* 'content_translation_metadata' key in the entity type definition. Every
|
||||
* content translation metadata wrapper needs to implement
|
||||
* \Drupal\content_translation\ContentTranslationMetadataWrapperInterface.
|
||||
*
|
||||
* If the entity paths match the default pattern above and there is no need for
|
||||
* an entity-specific translation handler, Content Translation will
|
||||
* provide built-in support for the entity. However enabling translation for
|
||||
* each translatable bundle will be required.
|
||||
* an entity-specific translation handler, Content Translation will provide
|
||||
* built-in support for the entity. However enabling translation for each
|
||||
* translatable bundle will be required.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Annotation\EntityType
|
||||
*/
|
||||
|
@ -105,6 +117,9 @@ function content_translation_entity_type_alter(array &$entity_types) {
|
|||
if (!$entity_type->hasHandlerClass('translation')) {
|
||||
$entity_type->setHandlerClass('translation', 'Drupal\content_translation\ContentTranslationHandler');
|
||||
}
|
||||
if (!$entity_type->get('content_translation_metadata')) {
|
||||
$entity_type->set('content_translation_metadata', 'Drupal\content_translation\ContentTranslationMetadataWrapper');
|
||||
}
|
||||
|
||||
$translation = $entity_type->get('translation');
|
||||
if (!$translation || !isset($translation['content_translation'])) {
|
||||
|
@ -138,6 +153,29 @@ function content_translation_entity_bundle_info_alter(&$bundles) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
function content_translation_entity_base_field_info(EntityTypeInterface $entity_type) {
|
||||
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */
|
||||
$manager = \Drupal::service('content_translation.manager');
|
||||
$entity_type_id = $entity_type->id();
|
||||
if ($manager->isSupported($entity_type_id)) {
|
||||
$definitions = $manager->getTranslationHandler($entity_type_id)->getFieldDefinitions();
|
||||
$installed_storage_definitions = \Drupal::entityManager()->getLastInstalledFieldStorageDefinitions($entity_type_id);
|
||||
// We return metadata storage fields whenever content translation is enabled
|
||||
// or it was enabled before, so that we keep translation metadata around
|
||||
// when translation is disabled.
|
||||
// @todo Re-evaluate this approach and consider removing field storage
|
||||
// definitions and the related field data if the entity type has no bundle
|
||||
// enabled for translation, once base field purging is supported.
|
||||
// See https://www.drupal.org/node/2282119.
|
||||
if ($manager->isEnabled($entity_type_id) || array_intersect_key($definitions, $installed_storage_definitions)) {
|
||||
return $definitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_info_alter().
|
||||
*
|
||||
|
@ -241,133 +279,23 @@ function content_translation_language_fallback_candidates_entity_view_alter(&$ca
|
|||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $context['data'];
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$entity_type = $entity->getEntityType();
|
||||
$permission = $entity_type->getPermissionGranularity() == 'bundle' ? $permission = "translate {$entity->bundle()} $entity_type_id" : "translate $entity_type_id";
|
||||
$current_user = \Drupal::currentuser();
|
||||
if (!$current_user->hasPermission('translate any entity') && !$current_user->hasPermission($permission)) {
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
if (empty($entity->translation[$langcode]['status'])) {
|
||||
unset($candidates[$langcode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_storage_load().
|
||||
*/
|
||||
function content_translation_entity_storage_load(array $entities, $entity_type) {
|
||||
$enabled_entities = array();
|
||||
|
||||
if (\Drupal::service('content_translation.manager')->isEnabled($entity_type)) {
|
||||
foreach ($entities as $entity) {
|
||||
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
|
||||
$enabled_entities[$entity->id()] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($enabled_entities)) {
|
||||
content_translation_load_translation_metadata($enabled_entities, $entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads translation data into the given entities.
|
||||
*
|
||||
* @param array $entities
|
||||
* The entities keyed by entity ID.
|
||||
* @param string $entity_type
|
||||
* The type of the entities.
|
||||
*/
|
||||
function content_translation_load_translation_metadata(array $entities, $entity_type) {
|
||||
$query = 'SELECT * FROM {content_translation} te WHERE te.entity_type = :entity_type AND te.entity_id IN (:entity_id)';
|
||||
$result = db_query($query, array(':entity_type' => $entity_type, ':entity_id' => array_keys($entities)));
|
||||
$exclude = array('entity_type', 'entity_id', 'langcode');
|
||||
foreach ($result as $record) {
|
||||
$entity = $entities[$record->entity_id];
|
||||
// @todo Declare these as entity (translation?) properties.
|
||||
foreach ($record as $field_name => $value) {
|
||||
if (!in_array($field_name, $exclude)) {
|
||||
$langcode = $record->langcode;
|
||||
$entity->translation[$langcode][$field_name] = $value;
|
||||
if (!$entity->hasTranslation($langcode)) {
|
||||
$entity->initTranslation($langcode);
|
||||
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */
|
||||
$manager = \Drupal::service('content_translation.manager');
|
||||
if ($manager->isEnabled($entity_type_id, $entity->bundle())) {
|
||||
$entity_type = $entity->getEntityType();
|
||||
$permission = $entity_type->getPermissionGranularity() == 'bundle' ? $permission = "translate {$entity->bundle()} $entity_type_id" : "translate $entity_type_id";
|
||||
$current_user = \Drupal::currentuser();
|
||||
if (!$current_user->hasPermission('translate any entity') && !$current_user->hasPermission($permission)) {
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$metadata = $manager->getTranslationMetadata($entity->getTranslation($langcode));
|
||||
if (!$metadata->isPublished()) {
|
||||
unset($candidates[$langcode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_insert().
|
||||
*/
|
||||
function content_translation_entity_insert(EntityInterface $entity) {
|
||||
// Only do something if translation support for the given entity is enabled.
|
||||
if (!($entity instanceof ContentEntityInterface) || !$entity->isTranslatable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = array('entity_type', 'entity_id', 'langcode', 'source', 'outdated', 'uid', 'status', 'created', 'changed');
|
||||
$query = db_insert('content_translation')->fields($fields);
|
||||
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$translation = isset($entity->translation[$langcode]) ? $entity->translation[$langcode] : array();
|
||||
|
||||
$translation += array(
|
||||
'source' => '',
|
||||
'uid' => \Drupal::currentUser()->id(),
|
||||
'outdated' => FALSE,
|
||||
'status' => TRUE,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
);
|
||||
|
||||
$translation['entity_type'] = $entity->getEntityTypeId();
|
||||
$translation['entity_id'] = $entity->id();
|
||||
$translation['langcode'] = $langcode;
|
||||
|
||||
// Reorder values to match the schema.
|
||||
$values = array();
|
||||
foreach ($fields as $field_name) {
|
||||
$value = is_bool($translation[$field_name]) ? intval($translation[$field_name]) : $translation[$field_name];
|
||||
$values[$field_name] = $value;
|
||||
}
|
||||
$query->values($values);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_delete().
|
||||
*/
|
||||
function content_translation_entity_delete(EntityInterface $entity) {
|
||||
// Only do something if translation support for the given entity is enabled.
|
||||
if (!($entity instanceof ContentEntityInterface) || !$entity->isTranslatable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
db_delete('content_translation')
|
||||
->condition('entity_type', $entity->getEntityTypeId())
|
||||
->condition('entity_id', $entity->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_update().
|
||||
*/
|
||||
function content_translation_entity_update(EntityInterface $entity) {
|
||||
// Only do something if translation support for the given entity is enabled.
|
||||
if (!($entity instanceof ContentEntityInterface) || !$entity->isTranslatable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete and create to ensure no stale value remains behind.
|
||||
content_translation_entity_delete($entity);
|
||||
content_translation_entity_insert($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_extra_field_info().
|
||||
*/
|
||||
|
@ -429,11 +357,18 @@ function content_translation_form_field_ui_field_edit_form_alter(array &$form, F
|
|||
* Implements hook_entity_presave().
|
||||
*/
|
||||
function content_translation_entity_presave(EntityInterface $entity) {
|
||||
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
|
||||
// @todo Avoid using request attributes once translation metadata become
|
||||
// regular fields.
|
||||
$attributes = \Drupal::request()->attributes;
|
||||
\Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $attributes->get('source_langcode'));
|
||||
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && !$entity->isNew()) {
|
||||
// If we are creating a new translation we need to use the source language
|
||||
// as original language, since source values are the only ones available to
|
||||
// compare against.
|
||||
if (!isset($entity->original)) {
|
||||
$entity->original = entity_load_unchanged($entity->entityType(), $entity->id());
|
||||
}
|
||||
$langcode = $entity->language()->getId();
|
||||
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */
|
||||
$manager = \Drupal::service('content_translation.manager');
|
||||
$source_langcode = !$entity->original->hasTranslation($langcode) ? $manager->getTranslationMetadata($entity)->getSource() : NULL;
|
||||
\Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $langcode, $source_langcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,3 +24,9 @@ services:
|
|||
content_translation.manager:
|
||||
class: Drupal\content_translation\ContentTranslationManager
|
||||
arguments: ['@entity.manager']
|
||||
|
||||
content_translation.updates_manager:
|
||||
class: Drupal\content_translation\ContentTranslationUpdatesManager
|
||||
arguments: ['@entity.manager', '@entity.definition_update_manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
|
|
@ -8,13 +8,16 @@
|
|||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -23,6 +26,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* @ingroup entity_api
|
||||
*/
|
||||
class ContentTranslationHandler implements ContentTranslationHandlerInterface, EntityHandlerInterface {
|
||||
use DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* The type of the entity being translated.
|
||||
|
@ -46,14 +50,11 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* The content translation manager.
|
||||
*
|
||||
* @var \Drupal\content_translation\ContentTranslationManagerInterface
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the content translation controller.
|
||||
|
@ -62,11 +63,124 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
* The info array of the given entity type.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\content_translation\ContentTranslationManagerInterface $manager
|
||||
* The content translation manager service.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, LanguageManagerInterface $language_manager) {
|
||||
public function __construct(EntityTypeInterface $entity_type, LanguageManagerInterface $language_manager, ContentTranslationManagerInterface $manager) {
|
||||
$this->entityTypeId = $entity_type->id();
|
||||
$this->entityType = $entity_type;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('language_manager'),
|
||||
$container->get('content_translation.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDefinitions() {
|
||||
$definitions = array();
|
||||
|
||||
$definitions['content_translation_source'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Translation source'))
|
||||
->setDescription(t('The source language from which this translation was created.'))
|
||||
->setDefaultValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
$definitions['content_translation_outdated'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Translation outdated'))
|
||||
->setDescription(t('A boolean indicating whether this translation needs to be updated.'))
|
||||
->setDefaultValue(FALSE)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
if (!$this->hasAuthor()) {
|
||||
$definitions['content_translation_uid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Translation author'))
|
||||
->setDescription(t('The author of this translation.'))
|
||||
->setSetting('target_type', 'user')
|
||||
->setSetting('handler', 'default')
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
}
|
||||
|
||||
if (!$this->hasPublishedStatus()) {
|
||||
$definitions['content_translation_status'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Translation status'))
|
||||
->setDescription(t('A boolean indicating whether the translation is visible to non-translators.'))
|
||||
->setDefaultValue(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
}
|
||||
|
||||
if (!$this->hasCreatedTime()) {
|
||||
$definitions['content_translation_created'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Translation created time'))
|
||||
->setDescription(t('The Unix timestamp when the translation was created.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
}
|
||||
|
||||
if (!$this->hasChangedTime()) {
|
||||
$definitions['content_translation_changed'] = BaseFieldDefinition::create('changed')
|
||||
->setLabel(t('Translation changed time'))
|
||||
->setDescription(t('The Unix timestamp when the translation was most recently saved.'))
|
||||
->setPropertyConstraints('value', array('EntityChanged' => array()))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the entity type supports author natively.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if metadata is natively supported, FALSE otherwise.
|
||||
*/
|
||||
protected function hasAuthor() {
|
||||
return is_subclass_of($this->entityType->getClass(), '\Drupal\user\EntityOwnerInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the entity type supports published status natively.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if metadata is natively supported, FALSE otherwise.
|
||||
*/
|
||||
protected function hasPublishedStatus() {
|
||||
return array_key_exists('status', \Drupal::entityManager()->getLastInstalledFieldStorageDefinitions($this->entityType->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the entity type supports modification time natively.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if metadata is natively supported, FALSE otherwise.
|
||||
*/
|
||||
protected function hasChangedTime() {
|
||||
return is_subclass_of($this->entityType->getClass(), '\Drupal\Core\Entity\EntityChangedInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the entity type supports creation time natively.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if metadata is natively supported, FALSE otherwise.
|
||||
*/
|
||||
protected function hasCreatedTime() {
|
||||
return array_key_exists('created', \Drupal::entityManager()->getLastInstalledFieldStorageDefinitions($this->entityType->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,9 +188,9 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
*/
|
||||
public function retranslate(EntityInterface $entity, $langcode = NULL) {
|
||||
$updated_langcode = !empty($langcode) ? $langcode : $entity->language()->getId();
|
||||
$translations = $entity->getTranslationLanguages();
|
||||
foreach ($translations as $langcode => $language) {
|
||||
$entity->translation[$langcode]['outdated'] = $langcode != $updated_langcode;
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$this->manager->getTranslationMetadata($entity->getTranslation($langcode))
|
||||
->setOutdated($langcode != $updated_langcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,20 +345,16 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
);
|
||||
|
||||
// A new translation is enabled by default.
|
||||
$status = $new_translation || $entity->translation[$form_langcode]['status'];
|
||||
$metadata = $this->manager->getTranslationMetadata($entity);
|
||||
$status = $new_translation || $metadata->isPublished();
|
||||
// If there is only one published translation we cannot unpublish it,
|
||||
// since there would be nothing left to display.
|
||||
$enabled = TRUE;
|
||||
if ($status) {
|
||||
// A new translation is not available in the translation metadata, hence
|
||||
// it should count as one more.
|
||||
$published = $new_translation;
|
||||
// When creating a brand new translation, $entity->translation is not
|
||||
// set.
|
||||
if (!$new_translation) {
|
||||
foreach ($entity->translation as $translation) {
|
||||
$published += $translation['status'];
|
||||
}
|
||||
$published = 0;
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$published += $this->manager->getTranslationMetadata($entity->getTranslation($langcode))
|
||||
->isPublished();
|
||||
}
|
||||
$enabled = $published > 1;
|
||||
}
|
||||
|
@ -260,7 +370,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
'#disabled' => !$enabled,
|
||||
);
|
||||
|
||||
$translate = !$new_translation && $entity->translation[$form_langcode]['outdated'];
|
||||
$translate = !$new_translation && $metadata->isOutdated();
|
||||
if (!$translate) {
|
||||
$form['content_translation']['retranslate'] = array(
|
||||
'#type' => 'checkbox',
|
||||
|
@ -284,8 +394,8 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
if ($new_translation) {
|
||||
$name = \Drupal::currentUser()->getUsername();
|
||||
}
|
||||
elseif ($entity->translation[$form_langcode]['uid']) {
|
||||
$name = user_load($entity->translation[$form_langcode]['uid'])->getUsername();
|
||||
elseif (($account = $metadata->getAuthor()) && $account->id()) {
|
||||
$name = $account->getUsername();
|
||||
}
|
||||
$form['content_translation']['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
|
@ -296,13 +406,13 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
'#description' => t('Leave blank for %anonymous.', array('%anonymous' => \Drupal::config('user.settings')->get('anonymous'))),
|
||||
);
|
||||
|
||||
$date = $new_translation ? REQUEST_TIME : $entity->translation[$form_langcode]['created'];
|
||||
$date = $new_translation ? REQUEST_TIME : $metadata->getCreatedTime();
|
||||
$form['content_translation']['created'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Authored on'),
|
||||
'#maxlength' => 25,
|
||||
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => format_date($date, 'custom', 'Y-m-d H:i:s O'), '%timezone' => format_date($date, 'custom', 'O'))),
|
||||
'#default_value' => $new_translation ? '' : format_date($date, 'custom', 'Y-m-d H:i:s O'),
|
||||
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => format_date(REQUEST_TIME, 'custom', 'Y-m-d H:i:s O'), '%timezone' => format_date(REQUEST_TIME, 'custom', 'O'))),
|
||||
'#default_value' => $new_translation || !$date ? '' : format_date($date, 'custom', 'Y-m-d H:i:s O'),
|
||||
);
|
||||
|
||||
if (isset($language_widget)) {
|
||||
|
@ -418,35 +528,27 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
|||
public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
$form_object = $form_state->getFormObject();
|
||||
$form_langcode = $form_object->getFormLangcode($form_state);
|
||||
$values = &$form_state->getValue('content_translation', array());
|
||||
|
||||
if (!isset($entity->translation[$form_langcode])) {
|
||||
$entity->translation[$form_langcode] = array();
|
||||
if ($values['name'] == \Drupal::config('user.settings')->get('anonymous')) {
|
||||
$values['name'] = '';
|
||||
}
|
||||
$values = $form_state->getValue('content_translation', array());
|
||||
$translation = &$entity->translation[$form_langcode];
|
||||
|
||||
// @todo Use the entity setter when all entities support multilingual
|
||||
// properties.
|
||||
$translation['uid'] = !empty($values['name']) && ($account = user_load_by_name($values['name'])) ? $account->id() : 0;
|
||||
$translation['status'] = !empty($values['status']);
|
||||
$translation['created'] = !empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME;
|
||||
$translation['changed'] = REQUEST_TIME;
|
||||
$metadata = $this->manager->getTranslationMetadata($entity);
|
||||
$metadata->setAuthor(!empty($values['name']) && ($account = user_load_by_name($values['name'])) ? $account : User::load(0));
|
||||
$metadata->setPublished(!empty($values['status']));
|
||||
$metadata->setCreatedTime(!empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME);
|
||||
$metadata->setChangedTime(REQUEST_TIME);
|
||||
|
||||
$source_langcode = $this->getSourceLangcode($form_state);
|
||||
if ($source_langcode) {
|
||||
$translation['source'] = $source_langcode;
|
||||
$metadata->setSource($source_langcode);
|
||||
}
|
||||
|
||||
$translation['outdated'] = !empty($values['outdated']);
|
||||
$metadata->setOutdated(!empty($values['outdated']));
|
||||
if (!empty($values['retranslate'])) {
|
||||
$this->retranslate($entity, $form_langcode);
|
||||
}
|
||||
|
||||
// Set contextual information that can be reused during the storage phase.
|
||||
// @todo Remove this once translation metadata are converted to regular
|
||||
// fields.
|
||||
$attributes = \Drupal::request()->attributes;
|
||||
$attributes->set('source_langcode', $source_langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,13 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
*/
|
||||
interface ContentTranslationHandlerInterface {
|
||||
|
||||
/**
|
||||
* Returns a set of field definitions to be used to store metadata items.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
|
||||
*/
|
||||
public function getFieldDefinitions();
|
||||
|
||||
/**
|
||||
* Checks if the user can perform the given operation on translations of the
|
||||
* wrapped entity.
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
|
||||
/**
|
||||
|
@ -32,6 +34,23 @@ class ContentTranslationManager implements ContentTranslationManagerInterface {
|
|||
$this->entityManager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function getTranslationHandler($entity_type_id) {
|
||||
return $this->entityManager->getHandler($entity_type_id, 'translation');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationMetadata(EntityInterface $translation) {
|
||||
// We need a new instance of the metadata handler wrapping each translation.
|
||||
$entity_type = $translation->getEntityType();
|
||||
$class = $entity_type->get('content_translation_metadata');
|
||||
return new $class($translation, $this->getTranslationHandler($entity_type->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for common functionality for content translation.
|
||||
*/
|
||||
|
@ -31,6 +34,28 @@ interface ContentTranslationManagerInterface {
|
|||
*/
|
||||
public function isSupported($entity_type_id);
|
||||
|
||||
/**
|
||||
* Returns an instance of the Content translation handler.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The type of the entity being translated.
|
||||
*
|
||||
* @return \Drupal\content_translation\ContentTranslationHandlerInterface
|
||||
* An instance of the content translation handler.
|
||||
*/
|
||||
public function getTranslationHandler($entity_type_id);
|
||||
|
||||
/**
|
||||
* Returns an instance of the Content translation metadata.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $translation
|
||||
* The entity translation whose metadata needs to be retrieved.
|
||||
*
|
||||
* @return \Drupal\content_translation\ContentTranslationMetadataWrapperInterface
|
||||
* An instance of the content translation metadata.
|
||||
*/
|
||||
public function getTranslationMetadata(EntityInterface $translation);
|
||||
|
||||
/**
|
||||
* Sets the value for translatability of the given entity type bundle.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\content_translation\ContentTranslationMetadata.
|
||||
*/
|
||||
|
||||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Base class for content translation metadata wrappers.
|
||||
*/
|
||||
class ContentTranslationMetadataWrapper implements ContentTranslationMetadataWrapperInterface {
|
||||
|
||||
/**
|
||||
* The wrapped entity translation.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\FieldableEntityInterface|\Drupal\Core\TypedData\TranslatableInterface
|
||||
*/
|
||||
protected $translation;
|
||||
|
||||
/**
|
||||
* The content translation handler.
|
||||
*
|
||||
* @var \Drupal\content_translation\ContentTranslationHandlerInterface
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the content translation metadata handler.
|
||||
*
|
||||
* @param EntityInterface $translation
|
||||
* The entity translation to be wrapped.
|
||||
* @param ContentTranslationHandlerInterface $handler
|
||||
* The content translation handler.
|
||||
*/
|
||||
public function __construct(EntityInterface $translation, ContentTranslationHandlerInterface $handler) {
|
||||
$this->translation = $translation;
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource() {
|
||||
return $this->translation->get('content_translation_source')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSource($source) {
|
||||
$this->translation->set('content_translation_source', $source);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isOutdated() {
|
||||
return (bool) $this->translation->get('content_translation_outdated')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOutdated($outdated) {
|
||||
$this->translation->set('content_translation_outdated', $outdated);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuthor() {
|
||||
return $this->translation->hasField('content_translation_uid') ? $this->translation->get('content_translation_uid')->entity : $this->translation->getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAuthor(UserInterface $account) {
|
||||
if ($this->translation->hasField('content_translation_uid')) {
|
||||
$this->translation->set('content_translation_uid', $account->id());
|
||||
}
|
||||
else {
|
||||
$this->translation->setOwner($account);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPublished() {
|
||||
$field_name = $this->translation->hasField('content_translation_status') ? 'content_translation_status' : 'status';
|
||||
return (bool) $this->translation->get($field_name)->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPublished($published) {
|
||||
$field_name = $this->translation->hasField('content_translation_status') ? 'content_translation_status' : 'status';
|
||||
$this->translation->set($field_name, $published);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCreatedTime() {
|
||||
$field_name = $this->translation->hasField('content_translation_created') ? 'content_translation_created' : 'created';
|
||||
return $this->translation->get($field_name)->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCreatedTime($timestamp) {
|
||||
$field_name = $this->translation->hasField('content_translation_created') ? 'content_translation_created' : 'created';
|
||||
$this->translation->set($field_name, $timestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChangedTime() {
|
||||
return $this->translation->hasField('content_translation_changed') ? $this->translation->get('content_translation_changed')->value : $this->translation->getChangedTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setChangedTime($timestamp) {
|
||||
$field_name = $this->translation->hasField('content_translation_changed') ? 'content_translation_changed' : 'changed';
|
||||
$this->translation->set($field_name, $timestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\content_translation\ContentTranslationMetadataInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Common interface for content translation metadata wrappers.
|
||||
*
|
||||
* This acts as a wrapper for an entity translation object, encapsulating the
|
||||
* logic needed to retrieve translation metadata.
|
||||
*/
|
||||
interface ContentTranslationMetadataWrapperInterface extends EntityChangedInterface {
|
||||
|
||||
/**
|
||||
* Retrieves the source language for this translation.
|
||||
*
|
||||
* @return string
|
||||
* The source language code.
|
||||
*/
|
||||
public function getSource();
|
||||
|
||||
/**
|
||||
* Sets the source language for this translation.
|
||||
*
|
||||
* @param string $source
|
||||
* The source language code.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSource($source);
|
||||
|
||||
/**
|
||||
* Returns the translation outdated status.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the translation is outdated, FALSE otherwise.
|
||||
*/
|
||||
public function isOutdated();
|
||||
|
||||
/**
|
||||
* Sets the translation outdated status.
|
||||
*
|
||||
* @param bool $outdated
|
||||
* TRUE if the translation is outdated, FALSE otherwise.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOutdated($outdated);
|
||||
|
||||
/**
|
||||
* Returns the translation author.
|
||||
*
|
||||
* @return \Drupal\user\UserInterface
|
||||
* The user entity for the translation author.
|
||||
*/
|
||||
public function getAuthor();
|
||||
|
||||
/**
|
||||
* Sets the translation author.
|
||||
*
|
||||
* @param \Drupal\user\UserInterface $account
|
||||
* The translation author user entity.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthor(UserInterface $account);
|
||||
|
||||
/**
|
||||
* Returns the translation published status.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the translation is published, FALSE otherwise.
|
||||
*/
|
||||
public function isPublished();
|
||||
|
||||
/**
|
||||
* Sets the translation published status.
|
||||
*
|
||||
* @param bool $published
|
||||
* TRUE if the translation is published, FALSE otherwise.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPublished($published);
|
||||
|
||||
/**
|
||||
* Returns the translation creation timestamp.
|
||||
*
|
||||
* @return int
|
||||
* The UNIX timestamp of when the translation was created.
|
||||
*/
|
||||
public function getCreatedTime();
|
||||
|
||||
/**
|
||||
* Sets the translation creation timestamp.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The UNIX timestamp of when the translation was created.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedTime($timestamp);
|
||||
|
||||
/**
|
||||
* Sets the translation modification timestamp.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The UNIX timestamp of when the translation was last modified.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChangedTime($timestamp);
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Contains \Drupal\content_translation\ContentTranslationUpdatesManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Provides the logic needed to update field storage definitions when needed.
|
||||
*/
|
||||
class ContentTranslationUpdatesManager implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The entity definition update manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
|
||||
*/
|
||||
protected $updateManager;
|
||||
|
||||
/**
|
||||
* Constructs an updates manager instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $update_manager
|
||||
* The entity definition update manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, EntityDefinitionUpdateManagerInterface $update_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->updateManager = $update_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes field storage definition updates if needed.
|
||||
*
|
||||
* @param array $entity_types
|
||||
* A list of entity type definitions to be processed.
|
||||
*/
|
||||
public function updateDefinitions(array $entity_types) {
|
||||
// Handle field storage definition creation, if needed.
|
||||
// @todo Generalize this code in https://www.drupal.org/node/2346013.
|
||||
// @todo Handle initial values in https://www.drupal.org/node/2346019.
|
||||
if ($this->updateManager->needsUpdates()) {
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
$storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
|
||||
$installed_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
|
||||
foreach (array_diff_key($storage_definitions, $installed_storage_definitions) as $storage_definition) {
|
||||
/** @var $storage_definition \Drupal\Core\Field\FieldStorageDefinitionInterface */
|
||||
if ($storage_definition->getProvider() == 'content_translation') {
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the ConfigImporter import event.
|
||||
*/
|
||||
public function onConfigImporterImport() {
|
||||
$entity_types = array_filter($this->entityManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->isTranslatable();
|
||||
});
|
||||
$this->updateDefinitions($entity_types);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::IMPORT][] = ['onConfigImporterImport', 60];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,17 +7,43 @@
|
|||
|
||||
namespace Drupal\content_translation\Controller;
|
||||
|
||||
use Drupal\content_translation\ContentTranslationManagerInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base class for entity translation controllers.
|
||||
*/
|
||||
class ContentTranslationController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The content translation manager.
|
||||
*
|
||||
* @var \Drupal\content_translation\ContentTranslationManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Initializes a content translation controller.
|
||||
*
|
||||
* @param \Drupal\content_translation\ContentTranslationManagerInterface
|
||||
* A content translation manager instance.
|
||||
*/
|
||||
public function __construct(ContentTranslationManagerInterface $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('content_translation.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates target values with the source values.
|
||||
*
|
||||
|
@ -48,6 +74,7 @@ class ContentTranslationController extends ControllerBase {
|
|||
$entity = $route_match->getParameter($entity_type_id);
|
||||
$account = $this->currentUser();
|
||||
$handler = $this->entityManager()->getHandler($entity_type_id, 'translation');
|
||||
$manager = $this->manager;
|
||||
|
||||
$languages = $this->languageManager()->getLanguages();
|
||||
$original = $entity->getUntranslated()->language()->getId();
|
||||
|
@ -68,8 +95,9 @@ class ContentTranslationController extends ControllerBase {
|
|||
}
|
||||
|
||||
// Show source-language column if there are non-original source langcodes.
|
||||
$additional_source_langcodes = array_filter($entity->translation, function ($translation) use ($original) {
|
||||
return !empty($translation['source']) && $translation['source'] != $original;
|
||||
$additional_source_langcodes = array_filter(array_keys($translations), function ($langcode) use ($entity, $original, $manager) {
|
||||
$source = $manager->getTranslationMetadata($entity->getTranslation($langcode))->getSource();
|
||||
return $source != $original && $source != LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
});
|
||||
$show_source_column = !empty($additional_source_langcodes);
|
||||
|
||||
|
@ -118,7 +146,9 @@ class ContentTranslationController extends ControllerBase {
|
|||
$links = &$operations['data']['#links'];
|
||||
if (array_key_exists($langcode, $translations)) {
|
||||
// Existing translation in the translation set: display status.
|
||||
$source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : '';
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$metadata = $manager->getTranslationMetadata($translation);
|
||||
$source = $metadata->getSource() ?: LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
$is_original = $langcode == $original;
|
||||
$label = $entity->getTranslation($langcode)->label();
|
||||
$link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : array('url' => $entity->urlInfo());
|
||||
|
@ -145,13 +175,12 @@ class ContentTranslationController extends ControllerBase {
|
|||
if (isset($links['edit'])) {
|
||||
$links['edit']['title'] = $this->t('Edit');
|
||||
}
|
||||
$translation = $entity->translation[$langcode];
|
||||
$status = array('data' => array(
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<span class="status">{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}</span>{% if outdated %} <span class="marker">{{ "outdated"|t }}</span>{% endif %}',
|
||||
'#context' => array(
|
||||
'status' => $translation['status'],
|
||||
'outdated' => $translation['outdated'],
|
||||
'status' => $metadata->isPublished(),
|
||||
'outdated' => $metadata->isOutdated(),
|
||||
),
|
||||
));
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\content_translation\Tests\ContentTranslationConfigImportTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\content_translation\Tests;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests content translation updates performed during config import.
|
||||
*
|
||||
* @group content_translation
|
||||
*/
|
||||
class ContentTranslationConfigImportTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'entity_test', 'language', 'content_translation');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_mul');
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config import updates.
|
||||
*/
|
||||
function testConfigImportUpdates() {
|
||||
$entity_type_id = 'entity_test_mul';
|
||||
$config_id = $entity_type_id . '.' . $entity_type_id;
|
||||
$config_name = 'language.content_settings.' . $config_id;
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
// Verify the configuration to create does not exist yet.
|
||||
$this->assertIdentical($storage->exists($config_name), FALSE, $config_name . ' not found.');
|
||||
|
||||
// Create new config entity.
|
||||
$data = array(
|
||||
'uuid' => 'a019d89b-c4d9-4ed4-b859-894e4e2e93cf',
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => array(
|
||||
'module' => array('content_translation')
|
||||
),
|
||||
'id' => $config_id,
|
||||
'target_entity_type_id' => 'entity_test_mul',
|
||||
'target_bundle' => 'entity_test_mul',
|
||||
'default_langcode' => 'site_default',
|
||||
'language_alterable' => FALSE,
|
||||
'third_party_settings' => array(
|
||||
'content_translation' => array('enabled' => TRUE),
|
||||
),
|
||||
);
|
||||
$staging->write($config_name, $data);
|
||||
$this->assertIdentical($staging->exists($config_name), TRUE, $config_name . ' found.');
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the values appeared.
|
||||
$config = $this->config($config_name);
|
||||
$this->assertIdentical($config->get('id'), $config_id);
|
||||
|
||||
// Verify that updates were performed.
|
||||
$entity_type = $this->container->get('entity.manager')->getDefinition($entity_type_id);
|
||||
$table = $entity_type->getDataTable();
|
||||
$db_schema = $this->container->get('database')->schema();
|
||||
$result = $db_schema->fieldExists($table, 'content_translation_source') && $db_schema->fieldExists($table, 'content_translation_outdated');
|
||||
$this->assertTrue($result, 'Content translation updates were successfully performed during config import.');
|
||||
}
|
||||
|
||||
}
|
|
@ -116,10 +116,6 @@ class ContentTranslationSyncImageTest extends ContentTranslationTestBase {
|
|||
$default_langcode = $this->langcodes[0];
|
||||
$langcode = $this->langcodes[1];
|
||||
|
||||
// Populate the required contextual values.
|
||||
$attributes = \Drupal::request()->attributes;
|
||||
$attributes->set('source_langcode', $default_langcode);
|
||||
|
||||
// Populate the test entity with some random initial values.
|
||||
$values = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
|
@ -187,6 +183,7 @@ class ContentTranslationSyncImageTest extends ContentTranslationTestBase {
|
|||
|
||||
// Perform synchronization: the translation language is used as source,
|
||||
// while the default language is used as target.
|
||||
$this->manager->getTranslationMetadata($translation)->setSource($default_langcode);
|
||||
$entity = $this->saveEntity($translation);
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
|
||||
|
@ -216,8 +213,6 @@ class ContentTranslationSyncImageTest extends ContentTranslationTestBase {
|
|||
'title' => $langcode . '_' . $removed_fid . '_' . $this->randomMachineName(),
|
||||
);
|
||||
$translation->{$this->fieldName}->setValue(array_values($values[$langcode]));
|
||||
// When updating an entity we do not have a source language defined.
|
||||
$attributes->remove('source_langcode');
|
||||
$entity = $this->saveEntity($translation);
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
|
||||
|
|
|
@ -79,6 +79,11 @@ abstract class ContentTranslationTestBase extends WebTestBase {
|
|||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @var \Drupal\content_translation\ContentTranslationManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
@ -88,7 +93,8 @@ abstract class ContentTranslationTestBase extends WebTestBase {
|
|||
$this->setupUsers();
|
||||
$this->setupTestFields();
|
||||
|
||||
$this->controller = \Drupal::entityManager()->getHandler($this->entityTypeId, 'translation');
|
||||
$this->manager = $this->container->get('content_translation.manager');
|
||||
$this->controller = $this->manager->getTranslationHandler($this->entityTypeId);
|
||||
|
||||
// Rebuild the container so that the new languages are picked up by services
|
||||
// that hold a list of languages.
|
||||
|
@ -167,6 +173,7 @@ abstract class ContentTranslationTestBase extends WebTestBase {
|
|||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -176,7 +176,7 @@ abstract class ContentTranslationUITest extends ContentTranslationTestBase {
|
|||
$this->drupalGet($url);
|
||||
$this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$this->assertFalse($entity->translation[$added_langcode]['outdated'], 'The "outdated" status has been correctly stored.');
|
||||
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($added_langcode))->isOutdated(), 'The "outdated" status has been correctly stored.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ abstract class ContentTranslationUITest extends ContentTranslationTestBase {
|
|||
$edit = array('content_translation[status]' => FALSE);
|
||||
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$this->assertFalse($entity->translation[$langcode]['status'], 'The translation has been correctly unpublished.');
|
||||
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($langcode))->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,8 +227,9 @@ abstract class ContentTranslationUITest extends ContentTranslationTestBase {
|
|||
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
}
|
||||
|
||||
// Try to post non valid values and check that they are rejected.
|
||||
|
@ -240,8 +241,9 @@ abstract class ContentTranslationUITest extends ContentTranslationTestBase {
|
|||
);
|
||||
$this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
|
||||
$this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
|
||||
$this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly kept.');
|
||||
$this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly kept.');
|
||||
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly kept.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -176,6 +176,7 @@ class EntityReferenceFieldTranslatedReferenceViewTest extends WebTestBase {
|
|||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,6 +68,7 @@ class PrivateFileOnTranslatedEntityTest extends FileFieldTestBase {
|
|||
"settings[node][page][fields][$this->fieldName]" => 1,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -113,9 +113,8 @@ class NodeTranslationUITest extends ContentTranslationUITest {
|
|||
// The node is created as unpublished thus we switch to the published
|
||||
// status first.
|
||||
$status = !$index;
|
||||
$this->assertEqual($status, $entity->translation[$langcode]['status'], 'The translation has been correctly unpublished.');
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$this->assertEqual($status, $translation->isPublished(), 'The status of the translation has been correctly saved.');
|
||||
$this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,11 +150,10 @@ class NodeTranslationUITest extends ContentTranslationUITest {
|
|||
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$this->assertEqual($translation->getOwnerId(), $values[$langcode]['uid'], 'Author of translation correctly stored.');
|
||||
$this->assertEqual($translation->getCreatedTime(), $values[$langcode]['created'], 'Date of Translation correctly stored.');
|
||||
$metadata = $this->manager->getTranslationMetadata($translation);
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
$this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.');
|
||||
$this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.');
|
||||
}
|
||||
|
@ -202,7 +200,7 @@ class NodeTranslationUITest extends ContentTranslationUITest {
|
|||
));
|
||||
|
||||
// Make sure that nothing was inserted into the {content_translation} table.
|
||||
$rows = db_query('SELECT * FROM {content_translation}')->fetchAll();
|
||||
$rows = db_query('SELECT nid, count(nid) AS count FROM {node_field_data} WHERE type <> :type GROUP BY nid HAVING count >= 2', array(':type' => $this->bundle))->fetchAll();
|
||||
$this->assertEqual(0, count($rows));
|
||||
|
||||
// Ensure the translation tab is not accessible.
|
||||
|
|
|
@ -66,6 +66,7 @@ class PathLanguageTest extends PathTestBase {
|
|||
'settings[node][page][settings][language][language_alterable]' => 1,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'page');
|
||||
$this->assertTrue($definitions['path']->isTranslatable(), 'Node path is translatable.');
|
||||
|
|
|
@ -69,6 +69,7 @@ trait TaxonomyTranslationTestTrait {
|
|||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +81,6 @@ trait TaxonomyTranslationTestTrait {
|
|||
'entity_type' => 'node',
|
||||
'type' => 'taxonomy_term_reference',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'allowed_values' => array(
|
||||
array(
|
||||
|
@ -95,6 +95,7 @@ trait TaxonomyTranslationTestTrait {
|
|||
'field_name' => $this->termFieldName,
|
||||
'bundle' => 'article',
|
||||
'entity_type' => 'node',
|
||||
'translatable' => FALSE,
|
||||
));
|
||||
$field->save();
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
|
|
|
@ -95,9 +95,9 @@ class TermTranslationUITest extends ContentTranslationUITest {
|
|||
|
||||
// Make sure that no row was inserted for taxonomy vocabularies which do
|
||||
// not have translations enabled.
|
||||
$rows = db_query('SELECT * FROM {content_translation}')->fetchAll();
|
||||
$rows = db_query('SELECT tid, count(tid) AS count FROM {taxonomy_term_field_data} WHERE vid <> :vid GROUP BY tid', array(':vid' => $this->bundle))->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
$this->assertEqual('taxonomy_term', $row->entity_type, 'Row contains a taxonomy term.');
|
||||
$this->assertTrue($row->count < 2, 'Term does not have translations.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ class TaxonomyTermViewTest extends TaxonomyTestBase {
|
|||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
|
||||
$edit['title[0][value]'] = $translated_title = $this->randomMachineName();
|
||||
|
||||
|
@ -137,6 +138,7 @@ class TaxonomyTermViewTest extends TaxonomyTestBase {
|
|||
// Uninstall language module and ensure that the language is not part of the
|
||||
// query anymore.
|
||||
// @see \Drupal\views\Plugin\views\filter\LanguageFilter::query()
|
||||
$node->delete();
|
||||
\Drupal::service('module_installer')->uninstall(['content_translation', 'language']);
|
||||
|
||||
$view = Views::getView('taxonomy_term');
|
||||
|
|
|
@ -56,7 +56,7 @@ use Drupal\user\UserInterface;
|
|||
* "cancel-form" = "/user/{user}/cancel",
|
||||
* "collection" = "/admin/people",
|
||||
* },
|
||||
* field_ui_base_route = "entity.user.admin_form",
|
||||
* field_ui_base_route = "entity.user.admin_form"
|
||||
* )
|
||||
*/
|
||||
class User extends ContentEntityBase implements UserInterface {
|
||||
|
|
|
@ -16,6 +16,22 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
*/
|
||||
class ProfileTranslationHandler extends ContentTranslationHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hasPublishedStatus() {
|
||||
// User status has nothing to do with translations visibility.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hasCreatedTime() {
|
||||
// User creation date has nothing to do with translation creation date.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -31,7 +47,7 @@ class ProfileTranslationHandler extends ContentTranslationHandler {
|
|||
*
|
||||
* @see \Drupal\Core\Entity\EntityForm::build().
|
||||
*/
|
||||
function entityFormSave(array $form, FormStateInterface $form_state) {
|
||||
public function entityFormSave(array $form, FormStateInterface $form_state) {
|
||||
if ($this->getSourceLangcode($form_state)) {
|
||||
$entity = $form_state->getFormObject()->getEntity();
|
||||
// We need a redirect here, otherwise we would get an access denied page
|
||||
|
@ -40,4 +56,5 @@ class ProfileTranslationHandler extends ContentTranslationHandler {
|
|||
$form_state->setRedirectUrl($entity->urlInfo());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ class SearchMultilingualTest extends ViewTestBase {
|
|||
'settings[node][' . $type->id() . '][fields][body]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
|
||||
// Add a node in English, with title "sandwich".
|
||||
$values = array(
|
||||
|
|
Loading…
Reference in New Issue