From ad528c7375bf71d71604ba1264b14f05e7ab096b Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Mon, 18 Mar 2019 09:28:56 +0000 Subject: [PATCH] Issue #2599228 by tstoeckler, Taran2L, poornachandran, catch, vacho, johan.s, Berdir, hchonov, osman, tim.plunkett, GoZ, Voidtek, neetu morwani, psf_, alexpott, dxvargas, nkoporec, plach, andy@andyhawks.com, joey-santiago: Programmatically created translatable content type returns SQL error on content creation (cherry picked from commit 46424a89ef6386336a7806095f1494e2b79d7992) --- .../content_translation.module | 58 ++++++++++-- .../content_translation.services.yml | 5 +- .../d7_entity_translation_settings.yml | 5 -- .../migrations/d7_node_translation.yml | 2 - .../src/ContentTranslationManager.php | 24 ++--- .../src/ContentTranslationUpdatesManager.php | 60 ++----------- .../entity_test.entity_test_bundle.test.yml | 6 ++ ..._settings.entity_test_with_bundle.test.yml | 17 ++++ .../ContentTranslationModuleInstallTest.php | 88 +++++++++++++++++++ .../d6_language_content_settings.yml | 2 - ...e_content_taxonomy_vocabulary_settings.yml | 2 - .../d7_language_content_settings.yml | 2 - .../src/Entity/EntityTestWithBundle.php | 2 + 13 files changed, 183 insertions(+), 90 deletions(-) create mode 100644 core/modules/content_translation/tests/modules/content_translation_test/config/install/entity_test.entity_test_bundle.test.yml create mode 100644 core/modules/content_translation/tests/modules/content_translation_test/config/install/language.content_settings.entity_test_with_bundle.test.yml create mode 100644 core/modules/content_translation/tests/src/Kernel/ContentTranslationModuleInstallTest.php diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index d6f6f0c60399..50d7b846d89e 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -16,6 +16,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\language\ContentLanguageSettingsInterface; /** * Implements hook_help(). @@ -172,27 +173,72 @@ function content_translation_entity_type_alter(array &$entity_types) { /** * Implements hook_ENTITY_TYPE_insert(). * - * Clear the bundle information cache so that the bundle's translatability will - * be set properly. + * Installs Content Translation's field storage definitions for the target + * entity type, if required. + * + * Also clears the bundle information cache so that the bundle's translatability + * will be set properly. * * @see content_translation_entity_bundle_info_alter() + * @see \Drupal\content_translation\ContentTranslationManager::isEnabled() */ -function content_translation_language_content_settings_insert(EntityInterface $entity) { +function content_translation_language_content_settings_insert(ContentLanguageSettingsInterface $settings) { + if ($settings->getThirdPartySetting('content_translation', 'enabled', FALSE)) { + _content_translation_install_field_storage_definitions($settings->getTargetEntityTypeId()); + } + \Drupal::service('entity_type.bundle.info')->clearCachedBundles(); } /** * Implements hook_ENTITY_TYPE_update(). * - * Clear the bundle information cache so that the bundle's translatability will - * be changed properly. + * Installs Content Translation's field storage definitions for the target + * entity type, if required. + * + * Also clears the bundle information cache so that the bundle's translatability + * will be changed properly. * * @see content_translation_entity_bundle_info_alter() + * @see \Drupal\content_translation\ContentTranslationManager::isEnabled() */ -function content_translation_language_content_settings_update(EntityInterface $entity) { +function content_translation_language_content_settings_update(ContentLanguageSettingsInterface $settings) { + $original_settings = $settings->original; + if ($settings->getThirdPartySetting('content_translation', 'enabled', FALSE) + && !$original_settings->getThirdPartySetting('content_translation', 'enabled', FALSE) + ) { + _content_translation_install_field_storage_definitions($settings->getTargetEntityTypeId()); + } \Drupal::service('entity_type.bundle.info')->clearCachedBundles(); } +/** + * Installs Content Translation's fields for a given entity type. + * + * @param string $entity_type_id + * The entity type ID. + * + * @todo Generalize this code in https://www.drupal.org/node/2346013. + */ +function _content_translation_install_field_storage_definitions($entity_type_id) { + /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */ + $field_manager = \Drupal::service('entity_field.manager'); + /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $schema_repository */ + $schema_repository = \Drupal::service('entity.last_installed_schema.repository'); + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + + $field_manager->useCaches(FALSE); + $storage_definitions = $field_manager->getFieldStorageDefinitions($entity_type_id); + $field_manager->useCaches(TRUE); + $installed_storage_definitions = $schema_repository->getLastInstalledFieldStorageDefinitions($entity_type_id); + foreach (array_diff_key($storage_definitions, $installed_storage_definitions) as $storage_definition) { + /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition */ + if ($storage_definition->getProvider() == 'content_translation') { + $definition_update_manager->installFieldStorageDefinition($storage_definition->getName(), $entity_type_id, 'content_translation', $storage_definition); + } + } +} + /** * Implements hook_entity_bundle_info_alter(). */ diff --git a/core/modules/content_translation/content_translation.services.yml b/core/modules/content_translation/content_translation.services.yml index 71b800ec005e..dae66869ff31 100644 --- a/core/modules/content_translation/content_translation.services.yml +++ b/core/modules/content_translation/content_translation.services.yml @@ -29,10 +29,9 @@ services: content_translation.manager: class: Drupal\content_translation\ContentTranslationManager - arguments: ['@entity_type.manager', '@content_translation.updates_manager', '@entity_type.bundle.info'] + arguments: ['@entity_type.manager', '@entity_type.bundle.info'] content_translation.updates_manager: class: Drupal\content_translation\ContentTranslationUpdatesManager arguments: ['@entity_type.manager', '@entity.definition_update_manager', '@entity_field.manager', '@entity.last_installed_schema.repository'] - tags: - - { name: event_subscriber } + deprecated: The "%service_id%" service is deprecated. Definitions are updated automatically now so no replacement is needed. See https://www.drupal.org/node/2973222. diff --git a/core/modules/content_translation/migrations/d7_entity_translation_settings.yml b/core/modules/content_translation/migrations/d7_entity_translation_settings.yml index bd82446339d2..7ea34a390ded 100644 --- a/core/modules/content_translation/migrations/d7_entity_translation_settings.yml +++ b/core/modules/content_translation/migrations/d7_entity_translation_settings.yml @@ -25,11 +25,6 @@ process: third_party_settings/content_translation/bundle_settings/untranslatable_fields_hide: untranslatable_fields_hide destination: plugin: entity:language_content_settings - content_translation_update_definitions: - - comment - - node - - taxonomy_term - - user migration_dependencies: optional: - d7_comment_type diff --git a/core/modules/content_translation/migrations/d7_node_translation.yml b/core/modules/content_translation/migrations/d7_node_translation.yml index 90a101fec13d..6e89fb827e1c 100644 --- a/core/modules/content_translation/migrations/d7_node_translation.yml +++ b/core/modules/content_translation/migrations/d7_node_translation.yml @@ -33,8 +33,6 @@ process: destination: plugin: entity:node translations: true - content_translation_update_definitions: - - node destination_module: content_translation migration_dependencies: required: diff --git a/core/modules/content_translation/src/ContentTranslationManager.php b/core/modules/content_translation/src/ContentTranslationManager.php index 5149318f01ce..eaa64a7f918d 100644 --- a/core/modules/content_translation/src/ContentTranslationManager.php +++ b/core/modules/content_translation/src/ContentTranslationManager.php @@ -17,7 +17,10 @@ class ContentTranslationManager implements ContentTranslationManagerInterface, B /** * {@inheritdoc} */ - protected $deprecatedProperties = ['entityManager' => 'entity.manager']; + protected $deprecatedProperties = [ + 'entityManager' => 'entity.manager', + 'updatesManager' => 'content_translation.updates_manager', + ]; /** * The entity type bundle info provider. @@ -33,28 +36,19 @@ class ContentTranslationManager implements ContentTranslationManagerInterface, B */ protected $entityTypeManager; - /** - * The updates manager. - * - * @var \Drupal\content_translation\ContentTranslationUpdatesManager - */ - protected $updatesManager; - /** * Constructs a ContentTranslationManageAccessCheck object. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. - * @param \Drupal\content_translation\ContentTranslationUpdatesManager $updates_manager - * The updates manager. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info * The entity type bundle info provider. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentTranslationUpdatesManager $updates_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, $entity_type_bundle_info) { $this->entityTypeManager = $entity_type_manager; - $this->updatesManager = $updates_manager; - if (!$entity_type_bundle_info) { - @trigger_error('The entity_type.bundle.info service must be passed to ContentTranslationManager::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + + if (!($entity_type_bundle_info instanceof EntityTypeBundleInfoInterface)) { + @trigger_error('The entity_type.bundle.info service should be passed to ContentTranslationManager::__construct() instead of the content_translation.updates_manager service since 8.7.0. This will be required in Drupal 9.0.0. See https://www.drupal.org/node/2549139 and https://www.drupal.org/node/2973222.', E_USER_DEPRECATED); $entity_type_bundle_info = \Drupal::service('entity_type.bundle.info'); } $this->entityTypeBundleInfo = $entity_type_bundle_info; @@ -104,8 +98,6 @@ public function getSupportedEntityTypes() { public function setEnabled($entity_type_id, $bundle, $value) { $config = $this->loadContentLanguageSettings($entity_type_id, $bundle); $config->setThirdPartySetting('content_translation', 'enabled', $value)->save(); - $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); - $this->updatesManager->updateDefinitions([$entity_type_id => $entity_type]); } /** diff --git a/core/modules/content_translation/src/ContentTranslationUpdatesManager.php b/core/modules/content_translation/src/ContentTranslationUpdatesManager.php index d62a164ed864..9bb7fd07974a 100644 --- a/core/modules/content_translation/src/ContentTranslationUpdatesManager.php +++ b/core/modules/content_translation/src/ContentTranslationUpdatesManager.php @@ -2,28 +2,22 @@ namespace Drupal\content_translation; -use Drupal\Component\Utility\NestedArray; -use Drupal\Core\Config\ConfigEvents; -use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\migrate\Event\MigrateEvents; -use Drupal\migrate\Event\MigrateImportEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +@trigger_error('\Drupal\content_translation\ContentTranslationUpdatesManager is scheduled for removal in Drupal 9.0.0. Definitions are updated automatically now so no replacement is needed. See https://www.drupal.org/node/2973222.', E_USER_DEPRECATED); /** * Provides the logic needed to update field storage definitions when needed. + * + * @deprecated in Drupal 8.7.x, to be removed before Drupal 9.0.0. + * Definitions are updated automatically now so no replacement is needed. + * + * @see https://www.drupal.org/node/2973222 */ -class ContentTranslationUpdatesManager implements EventSubscriberInterface { - use DeprecatedServicePropertyTrait; - - /** - * {@inheritdoc} - */ - protected $deprecatedProperties = ['entityManager' => 'entity.manager']; +class ContentTranslationUpdatesManager { /** * The entity field manager. @@ -69,12 +63,10 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Ent $this->entityTypeManager = $entity_type_manager; $this->updateManager = $update_manager; if (!$entity_field_manager) { - @trigger_error('The entity_field.manager service must be passed to ContentTranslationUpdatesManager::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); $entity_field_manager = \Drupal::service('entity_field.manager'); } $this->entityFieldManager = $entity_field_manager; if (!$entity_last_installed_schema_repository) { - @trigger_error('The entity.last_installed_schema.repository service must be passed to ContentTranslationUpdatesManager::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); $entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository'); } $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository; @@ -88,8 +80,6 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Ent */ 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->entityFieldManager->getFieldStorageDefinitions($entity_type_id); @@ -104,38 +94,4 @@ public function updateDefinitions(array $entity_types) { } } - /** - * Listener for the ConfigImporter import event. - */ - public function onConfigImporterImport() { - $entity_types = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) { - return $entity_type->isTranslatable(); - }); - $this->updateDefinitions($entity_types); - } - - /** - * Listener for migration imports. - */ - public function onMigrateImport(MigrateImportEvent $event) { - $migration = $event->getMigration(); - $configuration = $migration->getDestinationConfiguration(); - $entity_types = NestedArray::getValue($configuration, ['content_translation_update_definitions']); - if ($entity_types) { - $entity_types = array_intersect_key($this->entityTypeManager->getDefinitions(), array_flip($entity_types)); - $this->updateDefinitions($entity_types); - } - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents() { - $events[ConfigEvents::IMPORT][] = ['onConfigImporterImport', 60]; - if (class_exists('\Drupal\migrate\Event\MigrateEvents')) { - $events[MigrateEvents::POST_IMPORT][] = ['onMigrateImport']; - } - return $events; - } - } diff --git a/core/modules/content_translation/tests/modules/content_translation_test/config/install/entity_test.entity_test_bundle.test.yml b/core/modules/content_translation/tests/modules/content_translation_test/config/install/entity_test.entity_test_bundle.test.yml new file mode 100644 index 000000000000..83720bb8b81f --- /dev/null +++ b/core/modules/content_translation/tests/modules/content_translation_test/config/install/entity_test.entity_test_bundle.test.yml @@ -0,0 +1,6 @@ +langcode: en +status: true +dependencies: { } +id: test +label: null +description: null diff --git a/core/modules/content_translation/tests/modules/content_translation_test/config/install/language.content_settings.entity_test_with_bundle.test.yml b/core/modules/content_translation/tests/modules/content_translation_test/config/install/language.content_settings.entity_test_with_bundle.test.yml new file mode 100644 index 000000000000..da9f0355d8c8 --- /dev/null +++ b/core/modules/content_translation/tests/modules/content_translation_test/config/install/language.content_settings.entity_test_with_bundle.test.yml @@ -0,0 +1,17 @@ +langcode: en +status: true +dependencies: + config: + - entity_test.entity_test_bundle.test + module: + - content_translation +third_party_settings: + content_translation: + enabled: true + bundle_settings: + untranslatable_fields_hide: '0' +id: entity_test_with_bundle.test +target_entity_type_id: entity_test_with_bundle +target_bundle: test +default_langcode: site_default +language_alterable: true diff --git a/core/modules/content_translation/tests/src/Kernel/ContentTranslationModuleInstallTest.php b/core/modules/content_translation/tests/src/Kernel/ContentTranslationModuleInstallTest.php new file mode 100644 index 000000000000..9caca99fe920 --- /dev/null +++ b/core/modules/content_translation/tests/src/Kernel/ContentTranslationModuleInstallTest.php @@ -0,0 +1,88 @@ +<?php + +namespace Drupal\Tests\content_translation\Kernel; + +use Drupal\entity_test\Entity\EntityTestWithBundle; +use Drupal\KernelTests\KernelTestBase; +use Drupal\language\Entity\ConfigurableLanguage; + +/** + * Tests content translation for modules that provide translatable bundles. + * + * @group content_translation + */ +class ContentTranslationModuleInstallTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'content_translation', + 'content_translation_test', + 'entity_test', + 'language', + 'user', + ]; + + /** + * The content translation manager. + * + * @var \Drupal\content_translation\ContentTranslationManagerInterface + */ + protected $contentTranslationManager; + + /** + * The language code of the source language for this test. + * + * @var string + */ + protected $sourceLangcode = 'en'; + + /** + * The language code of the translation language for this test. + * + * @var string + */ + protected $translationLangcode = 'af'; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installEntitySchema('entity_test_with_bundle'); + ConfigurableLanguage::createFromLangcode($this->translationLangcode)->save(); + + $this->contentTranslationManager = $this->container->get('content_translation.manager'); + } + + /** + * Test that content translation fields are created upon module installation. + */ + public function testFieldUpdates() { + // The module ships a translatable bundle of the 'entity_test_with_bundle' + // entity type. + $this->installConfig(['content_translation_test']); + + $entity = EntityTestWithBundle::create([ + 'type' => 'test', + 'langcode' => $this->sourceLangcode, + ]); + $entity->save(); + + // Add a translation with some translation metadata. + $translation = $entity->addTranslation($this->translationLangcode); + $translation_metadata = $this->contentTranslationManager->getTranslationMetadata($translation); + $translation_metadata->setSource($this->sourceLangcode)->setOutdated(TRUE); + $translation->save(); + + // Make sure the translation metadata has been saved correctly. + $entity = EntityTestWithBundle::load($entity->id()); + $translation = $entity->getTranslation($this->translationLangcode); + $translation_metadata = $this->contentTranslationManager->getTranslationMetadata($translation); + $this->assertSame($this->sourceLangcode, $translation_metadata->getSource()); + $this->assertSame(TRUE, $translation_metadata->isOutdated()); + } + +} diff --git a/core/modules/language/migrations/d6_language_content_settings.yml b/core/modules/language/migrations/d6_language_content_settings.yml index 6d9171ae32de..0eb7bbecbc7b 100644 --- a/core/modules/language/migrations/d6_language_content_settings.yml +++ b/core/modules/language/migrations/d6_language_content_settings.yml @@ -40,8 +40,6 @@ process: 2: true destination: plugin: entity:language_content_settings - content_translation_update_definitions: - - node migration_dependencies: required: - d6_node_type diff --git a/core/modules/language/migrations/d6_language_content_taxonomy_vocabulary_settings.yml b/core/modules/language/migrations/d6_language_content_taxonomy_vocabulary_settings.yml index 9af288c3bbb7..29a459e8d630 100644 --- a/core/modules/language/migrations/d6_language_content_taxonomy_vocabulary_settings.yml +++ b/core/modules/language/migrations/d6_language_content_taxonomy_vocabulary_settings.yml @@ -46,8 +46,6 @@ process: source: language destination: plugin: entity:language_content_settings - content_translation_update_definitions: - - taxonomy_term migration_dependencies: required: - d6_taxonomy_vocabulary diff --git a/core/modules/language/migrations/d7_language_content_settings.yml b/core/modules/language/migrations/d7_language_content_settings.yml index 9428f42d4dfd..58e547ae5f1a 100644 --- a/core/modules/language/migrations/d7_language_content_settings.yml +++ b/core/modules/language/migrations/d7_language_content_settings.yml @@ -43,8 +43,6 @@ process: 4: true destination: plugin: entity:language_content_settings - content_translation_update_definitions: - - node migration_dependencies: required: - d7_node_type diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php index e6cb5428b97c..42d26e0e0a0a 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php @@ -25,8 +25,10 @@ * }, * }, * base_table = "entity_test_with_bundle", + * data_table = "entity_test_with_bundle_field_data", * admin_permission = "administer entity_test_with_bundle content", * persistent_cache = FALSE, + * translatable = TRUE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", -- GitLab