diff --git a/composer.json b/composer.json index 2d41740298e85a1388155e53c8cb3f5e602a1b9b..133fdb2851af7f54799ece5a05792e629d8e0a9f 100644 --- a/composer.json +++ b/composer.json @@ -464,7 +464,8 @@ "SVG is not valid or cannot be sanitized, return an error. @see https://www.drupal.org/i/3350696": "resources/patch/php/drupal/svg_image/3350696.patch" }, "drupal/tmgmt_ec_etranslation": { - "Remove requesterCallback parameter see https://www.drupal.org/i/3504767": "resources/patch/php/drupal/tmgmt_ec_etranslation/21.diff" + "Remove requesterCallback parameter see https://www.drupal.org/i/3504767": "resources/patch/php/drupal/tmgmt_ec_etranslation/3504767.diff", + "Don't queue responses see https://www.drupal.org/i/3508220": "resources/patch/php/drupal/tmgmt_ec_etranslation/3508220.diff" }, "drupal/video_embed_field": { "Add title attribute to YouTube, Vimeo, and Playlist embeds @see https://www.drupal.org/project/video_embed_field/issues/3200253": "resources/patch/php/drupal/video_embed_field/3200253-26.patch", diff --git a/composer.lock b/composer.lock index bf020777132775280e0c7f48e981ef0ae5add2c3..26d19a85e29ce95bab7ab892bc57839ae320dbfc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e60e49f0724681136758f2e4c210b574", + "content-hash": "4d283028f48279026bc62df0e6f5f302", "packages": [ { "name": "asm89/stack-cors", diff --git a/config/sync/tmgmt.translator.ec_etranslation.yml b/config/sync/tmgmt.translator.ec_etranslation.yml index 83c62234970384b70bf2031a4e14fb9b7ae38f9a..c933e113fc8ced6c9812ce984e16f2cf5fdec127 100644 --- a/config/sync/tmgmt.translator.ec_etranslation.yml +++ b/config/sync/tmgmt.translator.ec_etranslation.yml @@ -16,6 +16,7 @@ settings: domain: SPD email: '' callback_hostname: '' + use_queue: false remote_languages_mappings: cs: cs da: da diff --git a/resources/patch/php/drupal/tmgmt_ec_etranslation/21.diff b/resources/patch/php/drupal/tmgmt_ec_etranslation/3504767.diff similarity index 100% rename from resources/patch/php/drupal/tmgmt_ec_etranslation/21.diff rename to resources/patch/php/drupal/tmgmt_ec_etranslation/3504767.diff diff --git a/resources/patch/php/drupal/tmgmt_ec_etranslation/3508220.diff b/resources/patch/php/drupal/tmgmt_ec_etranslation/3508220.diff new file mode 100644 index 0000000000000000000000000000000000000000..5c4d4ea832ac312df25d7ede44c1534124f65f02 --- /dev/null +++ b/resources/patch/php/drupal/tmgmt_ec_etranslation/3508220.diff @@ -0,0 +1,534 @@ +diff --git a/config/schema/tmgmt_ec_etranslation.schema.yml b/config/schema/tmgmt_ec_etranslation.schema.yml +index e6e4a5cd04e5f80dc2674f27f3baf436588e7f11..cc10c2da97fd0b96309a3b13874684ae443117f5 100644 +--- a/config/schema/tmgmt_ec_etranslation.schema.yml ++++ b/config/schema/tmgmt_ec_etranslation.schema.yml +@@ -16,3 +16,6 @@ tmgmt.translator.settings.ec_etranslation: + callback_hostname: + type: string + label: 'Callback hostname' ++ use_queue: ++ type: boolean ++ label: 'Queue the process for incoming translations.' +diff --git a/src/Controller/CallbackController.php b/src/Controller/CallbackController.php +index 943f0087a580eb6dd5cadabd4680e1c5eeeab1e6..24937bf362dbabc7b82b8b7af2d7f420f39d8616 100644 +--- a/src/Controller/CallbackController.php ++++ b/src/Controller/CallbackController.php +@@ -13,6 +13,7 @@ use Drupal\Core\Queue\QueueFactory; + use Drupal\tmgmt\JobItemInterface; + use Drupal\tmgmt_ec_etranslation\HashGeneratorInterface; + use Drupal\tmgmt_ec_etranslation\Plugin\QueueWorker\TranslationQueueWorker; ++use Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface; + use Symfony\Component\DependencyInjection\ContainerInterface; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; +@@ -43,14 +44,23 @@ class CallbackController implements ContainerInjectionInterface { + */ + protected HashGeneratorInterface $hashGenerator; + ++ /** ++ * The translation response processor. ++ * ++ * @var \Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface ++ */ ++ protected TranslationResponseProcessorInterface $translationResponseProcessor; ++ + public function __construct( + QueueFactory $queue, + LoggerChannelInterface $logger, + HashGeneratorInterface $hashGenerator, ++ TranslationResponseProcessorInterface $translationResponseProcessor, + ) { + $this->queue = $queue; + $this->logger = $logger; + $this->hashGenerator = $hashGenerator; ++ $this->translationResponseProcessor = $translationResponseProcessor; + } + + /** +@@ -60,7 +70,8 @@ class CallbackController implements ContainerInjectionInterface { + return new static( + $container->get('queue'), + $container->get('logger.channel.tmgmt_ec_etranslation'), +- $container->get('tmgmt_ec_etranslation.hash_generator') ++ $container->get('tmgmt_ec_etranslation.hash_generator'), ++ $container->get('tmgmt_ec_etranslation.translation_response_processor'), + ); + } + +@@ -68,11 +79,23 @@ class CallbackController implements ContainerInjectionInterface { + * Handles the translation callback from eTranslation. + */ + public function callback(JobItemInterface $jobItem, Request $request): Response { +- $this->queue->get(TranslationQueueWorker::ID)->createItem([ +- $jobItem->id(), +- $request->query->get('external-reference'), +- $request->getContent(), +- ]); ++ $useQueue = $jobItem->getJob() ++ ->getTranslator()->getSetting('use_queue') ?? TRUE; ++ ++ if ($useQueue) { ++ $this->queue->get(TranslationQueueWorker::ID)->createItem([ ++ $jobItem->id(), ++ $request->query->get('external-reference'), ++ $request->getContent(), ++ ]); ++ } ++ else { ++ $this->translationResponseProcessor->process( ++ $jobItem, ++ $request->query->get('external-reference'), ++ $request->getContent() ++ ); ++ } + + return new Response('', Response::HTTP_NO_CONTENT); + } +@@ -95,7 +118,7 @@ class CallbackController implements ContainerInjectionInterface { + * Access callback for the route. + * + * @param \Drupal\tmgmt\JobItemInterface $jobItem +- * Jon item. ++ * Job item. + * @param string $hash + * Hash. + * +diff --git a/src/ETranslationTranslatorUi.php b/src/ETranslationTranslatorUi.php +index a305c0089aa7b51ad044c8991a6d89c82d7b09e6..ac86b187ff93deae6282c1fcb00330d10827b28b 100644 +--- a/src/ETranslationTranslatorUi.php ++++ b/src/ETranslationTranslatorUi.php +@@ -54,6 +54,12 @@ class ETranslationTranslatorUi extends TranslatorPluginUiBase { + '#required' => FALSE, + ]; + ++ $form['use_queue'] = [ ++ '#type' => 'checkbox', ++ '#title' => $this->t('Use queue'), ++ '#description' => $this->t('Queue the process for incoming translations.'), ++ ]; ++ + $config = \Drupal::config($translator->getConfigDependencyName()); + foreach (Element::children($form) as $child) { + $key = 'settings.' . $child; +diff --git a/src/Plugin/QueueWorker/TranslationQueueWorker.php b/src/Plugin/QueueWorker/TranslationQueueWorker.php +index 5fc8c8d41d6e6728a3a76641dca62c3b84c44641..6dc737ea6aa8b249a8a5a9232659959ffb47fa93 100644 +--- a/src/Plugin/QueueWorker/TranslationQueueWorker.php ++++ b/src/Plugin/QueueWorker/TranslationQueueWorker.php +@@ -5,11 +5,9 @@ declare(strict_types=1); + namespace Drupal\tmgmt_ec_etranslation\Plugin\QueueWorker; + + use Drupal\Core\Entity\EntityTypeManagerInterface; +-use Drupal\Core\Logger\LoggerChannelInterface; + use Drupal\Core\Plugin\ContainerFactoryPluginInterface; + use Drupal\Core\Queue\QueueWorkerBase; +-use Drupal\tmgmt\Data; +-use Drupal\tmgmt_ec_etranslation\TextValidatorInterface; ++use Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface; + use Symfony\Component\DependencyInjection\ContainerInterface; + + /** +@@ -33,40 +31,22 @@ class TranslationQueueWorker extends QueueWorkerBase implements ContainerFactory + protected EntityTypeManagerInterface $entityTypeManager; + + /** +- * The logger channel. ++ * The translation response processor. + * +- * @var \Drupal\Core\Logger\LoggerChannelInterface ++ * @var \Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface + */ +- protected LoggerChannelInterface $logger; +- +- /** +- * The tmgmt data service. +- * +- * @var \Drupal\tmgmt\Data +- */ +- protected Data $tmgmtData; +- +- /** +- * Text validator service. +- * +- * @var \Drupal\tmgmt_ec_etranslation\TextValidatorInterface +- */ +- protected TextValidatorInterface $textValidator; ++ protected TranslationResponseProcessorInterface $translationResponseProcessor; + + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + EntityTypeManagerInterface $entity_type_manager, +- LoggerChannelInterface $logger, +- Data $tmgmtData, +- TextValidatorInterface $textValidator, ++ TranslationResponseProcessorInterface $translation_response_processor, + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->entityTypeManager = $entity_type_manager; +- $this->logger = $logger; +- $this->tmgmtData = $tmgmtData; +- $this->textValidator = $textValidator; ++ $this->translationResponseProcessor = $translation_response_processor; + } + + /** +@@ -83,9 +63,7 @@ class TranslationQueueWorker extends QueueWorkerBase implements ContainerFactory + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), +- $container->get('logger.channel.tmgmt_ec_etranslation'), +- $container->get('tmgmt.data'), +- $container->get('tmgmt_ec_etranslation.text_validator'), ++ $container->get('tmgmt_ec_etranslation.translation_response_processor'), + ); + } + +@@ -102,24 +80,7 @@ class TranslationQueueWorker extends QueueWorkerBase implements ContainerFactory + return; + } + +- $translatableFields = $this->tmgmtData->filterTranslatable($jobItem->getData()); +- +- if ($this->textValidator->isHtml($translatableFields[$key]['#text']) || +- !$this->textValidator->canTextToTranslateBeUsed($translatableFields[$key]['#text'])) { +- $translation['#text'] = base64_decode($response); +- } +- else { +- $translation['#text'] = $response; +- } +- +- $this->logger->notice('Translation received for job item @id: @translation', [ +- '@id' => $jobItem->id(), +- '@translation' => $translation['#text'], +- ]); +- +- $jobItem->addTranslatedData($translation, $key); +- $jobItem->addMessage('Translation received: @translation', ['@translation' => $translation['#text']]); +- $jobItem->save(); ++ $this->translationResponseProcessor->process($jobItem, $key, $response); + } + + } +diff --git a/src/Plugin/tmgmt/Translator/ETranslationTranslator.php b/src/Plugin/tmgmt/Translator/ETranslationTranslator.php +index 3b6cc0806b64e0c64645163b9a9f36c4612bf294..c6b57d44cdab9d20f6bc8372421a609b7f38e8fc 100644 +--- a/src/Plugin/tmgmt/Translator/ETranslationTranslator.php ++++ b/src/Plugin/tmgmt/Translator/ETranslationTranslator.php +@@ -115,6 +115,7 @@ class ETranslationTranslator extends TranslatorPluginBase implements ContainerFa + 'domain' => 'SPD', + 'email' => NULL, + 'callback_hostname' => NULL, ++ 'use_queue' => TRUE, + ]; + } + +diff --git a/src/TranslationResponseProcessor.php b/src/TranslationResponseProcessor.php +new file mode 100644 +index 0000000000000000000000000000000000000000..7e9a29cddeff7155ba1293324d55667f5674c3d1 +--- /dev/null ++++ b/src/TranslationResponseProcessor.php +@@ -0,0 +1,76 @@ ++<?php ++ ++namespace Drupal\tmgmt_ec_etranslation; ++ ++use Drupal\tmgmt\Data; ++use Drupal\tmgmt\JobItemInterface; ++use Psr\Log\LoggerInterface; ++ ++/** ++ * Processes translation responses. ++ */ ++class TranslationResponseProcessor implements TranslationResponseProcessorInterface { ++ ++ /** ++ * Logger service. ++ * ++ * @var \Psr\Log\LoggerInterface ++ */ ++ protected LoggerInterface $logger; ++ ++ /** ++ * The tmgmt data service. ++ * ++ * @var \Drupal\tmgmt\Data ++ */ ++ protected Data $tmgmtData; ++ ++ /** ++ * Text validator service. ++ * ++ * @var \Drupal\tmgmt_ec_etranslation\TextValidatorInterface ++ */ ++ protected TextValidatorInterface $textValidator; ++ ++ public function __construct( ++ LoggerInterface $logger, ++ Data $tmgmtData, ++ TextValidatorInterface $textValidator, ++ ) { ++ $this->logger = $logger; ++ $this->tmgmtData = $tmgmtData; ++ $this->textValidator = $textValidator; ++ } ++ ++ /** ++ * Processes a translation response. ++ * ++ * @param \Drupal\tmgmt\JobItemInterface $jobItem ++ * Job item. ++ * @param string $key ++ * The response key or identifier (external-reference). ++ * @param string $response ++ * The raw JSON response content. ++ */ ++ public function process(JobItemInterface $jobItem, string $key, string $response): void { ++ $translatableFields = $this->tmgmtData->filterTranslatable($jobItem->getData()); ++ ++ if ($this->textValidator->isHtml($translatableFields[$key]['#text']) || ++ !$this->textValidator->canTextToTranslateBeUsed($translatableFields[$key]['#text'])) { ++ $translation['#text'] = base64_decode($response); ++ } ++ else { ++ $translation['#text'] = $response; ++ } ++ ++ $this->logger->notice('Translation received for job item @id: @translation', [ ++ '@id' => $jobItem->id(), ++ '@translation' => $translation['#text'], ++ ]); ++ ++ $jobItem->addTranslatedData($translation, $key); ++ $jobItem->addMessage('Translation received: @translation', ['@translation' => $translation['#text']]); ++ $jobItem->save(); ++ } ++ ++} +diff --git a/src/TranslationResponseProcessorInterface.php b/src/TranslationResponseProcessorInterface.php +new file mode 100644 +index 0000000000000000000000000000000000000000..6c0ae77542f2767eb92fe27e50dfa9f75594564a +--- /dev/null ++++ b/src/TranslationResponseProcessorInterface.php +@@ -0,0 +1,26 @@ ++<?php ++ ++declare(strict_types=1); ++ ++namespace Drupal\tmgmt_ec_etranslation; ++ ++use Drupal\tmgmt\JobItemInterface; ++ ++/** ++ * Provides an interface for translation response processor. ++ */ ++interface TranslationResponseProcessorInterface { ++ ++ /** ++ * Processes a translation response. ++ * ++ * @param \Drupal\tmgmt\JobItemInterface $jobItem ++ * Job item. ++ * @param string $key ++ * The response key or identifier (external-reference). ++ * @param string $response ++ * The raw JSON response content. ++ */ ++ public function process(JobItemInterface $jobItem, string $key, string $response): void; ++ ++} +diff --git a/tests/src/Unit/CallbackControllerTest.php b/tests/src/Unit/CallbackControllerTest.php +index 48b4dbc76ba2c8a732b7e201ae0f7da2e539f069..7e782952313abbe9733d89df458cab0c3c2c338e 100644 +--- a/tests/src/Unit/CallbackControllerTest.php ++++ b/tests/src/Unit/CallbackControllerTest.php +@@ -7,10 +7,13 @@ namespace Drupal\Tests\tmgmt_ec_etranslation\Unit; + use Drupal\Core\Logger\LoggerChannelInterface; + use Drupal\Core\Queue\QueueFactory; + use Drupal\Core\Queue\QueueInterface; ++use Drupal\tmgmt\JobInterface; + use Drupal\tmgmt\JobItemInterface; ++use Drupal\tmgmt\TranslatorInterface; + use Drupal\tmgmt_ec_etranslation\Controller\CallbackController; + use Drupal\tmgmt_ec_etranslation\HashGeneratorInterface; + use Drupal\tmgmt_ec_etranslation\Plugin\QueueWorker\TranslationQueueWorker; ++use Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface; + use PHPUnit\Framework\TestCase; + use Symfony\Component\HttpFoundation\InputBag; + use Symfony\Component\HttpFoundation\Request; +@@ -53,6 +56,13 @@ class CallbackControllerTest extends TestCase { + */ + protected HashGeneratorInterface $hashGenerator; + ++ /** ++ * The translation response processor. ++ * ++ * @var \Drupal\tmgmt_ec_etranslation\TranslationResponseProcessorInterface ++ */ ++ protected TranslationResponseProcessorInterface $translationResponseProcessor; ++ + /** + * Set up the test environment. + */ +@@ -62,33 +72,58 @@ class CallbackControllerTest extends TestCase { + $this->queueFactory = $this->createMock(QueueFactory::class); + $this->logger = $this->createMock(LoggerChannelInterface::class); + $this->hashGenerator = $this->createMock(HashGeneratorInterface::class); +- +- $this->controller = new CallbackController($this->queueFactory, $this->logger, $this->hashGenerator); ++ $this->translationResponseProcessor = $this->createMock(TranslationResponseProcessorInterface::class); ++ ++ $this->controller = new CallbackController( ++ $this->queueFactory, ++ $this->logger, ++ $this->hashGenerator, ++ $this->translationResponseProcessor ++ ); + } + + /** + * @covers ::callback ++ * ++ * @dataProvider callbackDataProvider + */ +- public function testCallback(): void { ++ public function testCallback(bool $useQueue): void { ++ $translator = $this->createMock(TranslatorInterface::class); ++ $translator->expects($this->once()) ++ ->method('getSetting') ++ ->with('use_queue') ++ ->willReturn($useQueue); ++ $job = $this->createMock(JobInterface::class); ++ $job->expects($this->once()) ++ ->method('getTranslator') ++ ->willReturn($translator); + $jobItem = $this->createMock(JobItemInterface::class); +- $jobItem->expects($this->once()) ++ $jobItem->expects($this->any()) + ->method('id') + ->willReturn(123); ++ $jobItem->expects($this->once()) ++ ->method('getJob') ++ ->willReturn($job); + +- $queryBag = new InputBag(['external-reference' => 'test-reference']); ++ // Process immediately. ++ $this->translationResponseProcessor->expects($this->exactly((int) !$useQueue)) ++ ->method('process') ++ ->with($jobItem, 'test-reference', 'test-content'); + ++ $queryBag = new InputBag(['external-reference' => 'test-reference']); + $request = $this->createMock(Request::class); + $request->query = $queryBag; + $request->expects($this->once()) + ->method('getContent') + ->willReturn('test-content'); + ++ // Queue process. + $queue = $this->createMock(QueueInterface::class); +- $queue->expects($this->once()) ++ $queue->expects($this->exactly((int) $useQueue)) + ->method('createItem') +- ->with([123, 'test-reference', 'test-content']); +- +- $this->queueFactory->expects($this->once()) ++ ->with([123, 'test-reference', 'test-content']) ++ ->willReturn($queue); ++ $this->queueFactory->expects($this->exactly((int) $useQueue)) + ->method('get') + ->with(TranslationQueueWorker::ID) + ->willReturn($queue); +@@ -154,4 +189,14 @@ class CallbackControllerTest extends TestCase { + $this->assertTrue($result->isNeutral()); + } + ++ /** ++ * Provider for ::testCallback. ++ */ ++ public function callbackDataProvider(): array { ++ return [ ++ 'enable queue' => [TRUE], ++ 'disable queue' => [FALSE], ++ ]; ++ } ++ + } +diff --git a/tests/src/Unit/TranslationQueueWorkerTest.php b/tests/src/Unit/TranslationQueueWorkerTest.php +index 81d2d8b9a29225c00a5a3faaf759382e4234f806..3cfb09daaefd0f11eb6341cbee011de65a59b055 100644 +--- a/tests/src/Unit/TranslationQueueWorkerTest.php ++++ b/tests/src/Unit/TranslationQueueWorkerTest.php +@@ -11,6 +11,7 @@ use Drupal\tmgmt\Data; + use Drupal\tmgmt\JobItemInterface; + use Drupal\tmgmt_ec_etranslation\Plugin\QueueWorker\TranslationQueueWorker; + use Drupal\tmgmt_ec_etranslation\TextValidatorInterface; ++use Drupal\tmgmt_ec_etranslation\TranslationResponseProcessor; + use PHPUnit\Framework\TestCase; + + /** +@@ -68,14 +69,18 @@ class TranslationQueueWorkerTest extends TestCase { + $this->tmgmtData = $this->createMock(Data::class); + $this->textValidator = $this->createMock(TextValidatorInterface::class); + ++ $translationResponseProcessor = new TranslationResponseProcessor( ++ $this->logger, ++ $this->tmgmtData, ++ $this->textValidator, ++ ); ++ + $this->queueWorker = new TranslationQueueWorker( + [], + TranslationQueueWorker::ID, + [], + $this->entityTypeManager, +- $this->logger, +- $this->tmgmtData, +- $this->textValidator, ++ $translationResponseProcessor, + ); + } + +diff --git a/tmgmt_ec_etranslation.install b/tmgmt_ec_etranslation.install +new file mode 100644 +index 0000000000000000000000000000000000000000..7eac20090b7d589925d9709e5bc2bbbf1a175773 +--- /dev/null ++++ b/tmgmt_ec_etranslation.install +@@ -0,0 +1,26 @@ ++<?php ++ ++/** ++ * @file ++ * Install, update and uninstall functions for the eTranslation. ++ */ ++ ++/** ++ * Sets default 'use_queue' value for all ec_etranslation providers. ++ */ ++function tmgmt_ec_etranslation_update_9001(): void { ++ $config_factory = \Drupal::configFactory(); ++ foreach ($config_factory->listAll('tmgmt.translator') as $id) { ++ $config = $config_factory->getEditable($id); ++ ++ if (!$config || $config->get('plugin') !== 'ec_etranslation') { ++ continue; ++ } ++ ++ $settings = $config->get('settings') ?? []; ++ if (!isset($settings['use_queue'])) { ++ $settings['use_queue'] = TRUE; ++ $config->set('settings', $settings)->save(); ++ } ++ } ++} +diff --git a/tmgmt_ec_etranslation.services.yml b/tmgmt_ec_etranslation.services.yml +index eea27e25f84c886193d969bd62c92e9129db07c8..43fff2d240e77af8e7da368d63b3e991ba58faac 100644 +--- a/tmgmt_ec_etranslation.services.yml ++++ b/tmgmt_ec_etranslation.services.yml +@@ -6,6 +6,10 @@ services: + tmgmt_ec_etranslation.text_validator: + class: Drupal\tmgmt_ec_etranslation\TextValidator + ++ tmgmt_ec_etranslation.translation_response_processor: ++ class: Drupal\tmgmt_ec_etranslation\TranslationResponseProcessor ++ arguments: [ '@logger.channel.tmgmt_ec_etranslation', '@tmgmt.data', '@tmgmt_ec_etranslation.text_validator' ] ++ + logger.channel.tmgmt_ec_etranslation: + parent: logger.channel_base + arguments: [ 'tmgmt_ec_etranslation' ]