Skip to content
Snippets Groups Projects
Commit 15a3f8f7 authored by Paul Smith's avatar Paul Smith Committed by Marcus Johansson
Browse files

Resolve #3513409 "Compatibility regression with"

parent af1f6a36
No related branches found
No related tags found
1 merge request!516Resolve #3513409 "Compatibility regression with"
Pipeline #496913 passed with warnings
......@@ -2,10 +2,17 @@
namespace Drupal\ai_translate\Controller;
use Drupal\ai\AiProviderPluginManager;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\content_translation\ContentTranslationManager;
use Drupal\content_translation\ContentTranslationManagerInterface;
use Drupal\Core\Controller\ControllerResolver;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\ai_translate\Form\AiTranslateForm;
use Drupal\content_translation\Controller\ContentTranslationController;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Overridden class for entity translation controllers.
......@@ -13,18 +20,53 @@ use Drupal\content_translation\Controller\ContentTranslationController;
class ContentTranslationControllerOverride extends ContentTranslationController {
/**
* Helper to get a controller resolver service.
* The time service.
*
* @return \Drupal\Core\Controller\ControllerResolver
* The Controller Resolver service.
* To maintain compatibility with Drupal 10, the time service cannot have
* constructor promotion yet.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
public function getControllerResolver(): ControllerResolver {
protected TimeInterface $time;
/** @var \Drupal\Core\Controller\ControllerResolver $service */
// @codingStandardsIgnoreLine @phpstan-ignore-next-line
$service = \Drupal::service('controller_resolver');
/**
* Initializes a content translation controller.
*
* @param \Drupal\content_translation\ContentTranslationManagerInterface $manager
* A content translation manager instance.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager service.
* @param \Drupal\ai\AiProviderPluginManager $providerManager
* The AI provider manager.
* @param \Drupal\Core\Controller\ControllerResolver $controllerResolver
* The controller resolver.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(
ContentTranslationManagerInterface $manager,
EntityFieldManagerInterface $entity_field_manager,
protected readonly AiProviderPluginManager $providerManager,
protected readonly ControllerResolver $controllerResolver,
?TimeInterface $time,
) {
if ($time instanceof TimeInterface) {
$this->time = $time;
}
parent::__construct($manager, $entity_field_manager, $time);
}
return $service;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('content_translation.manager'),
$container->get('entity_field.manager'),
$container->get('ai.provider'),
$container->get('controller_resolver'),
$container->get('datetime.time'),
);
}
/**
......@@ -36,7 +78,7 @@ class ContentTranslationControllerOverride extends ContentTranslationController
$parent_controller_id = $route_match->getRouteObject()->getDefault('_parent_controller');
// Let the original controller build the form it wants to.
if ($parent_controller = $this->getControllerResolver()->getControllerFromDefinition($parent_controller_id, $route_match->getRouteObject()->getPath())) {
if ($parent_controller = $this->controllerResolver->getControllerFromDefinition($parent_controller_id, $route_match->getRouteObject()->getPath())) {
$build = call_user_func([$parent_controller[0], $parent_controller[1]], $route_match, $entity_type_id);
}
......@@ -47,7 +89,123 @@ class ContentTranslationControllerOverride extends ContentTranslationController
$build = parent::overview($route_match, $entity_type_id);
}
return $this->formBuilder()->getForm(AiTranslateForm::class, $build);
$this->alterForm($build, $route_match, $entity_type_id);
return $build;
}
/**
* Helper to alter whatever page exists at our route.
*
* @param array $form
* The built form.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param mixed $entity_type_id
* The entity type ID.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function alterForm(array &$form, RouteMatchInterface $route_match, mixed $entity_type_id = NULL): void {
_ai_translate_check_default_provider_and_model();
$overview = NULL;
foreach (Element::children($form) as $child) {
if (!$overview) {
if (isset($form[$child]['#theme']) && $form[$child]['#theme'] == 'table') {
$overview = &$form[$child];
}
elseif (isset($form[$child]['#type']) && $form[$child]['#type'] == 'tableselect') {
$overview = &$form[$child];
}
}
}
if ($overview) {
// Inject our additional column into the header.
array_splice($overview['#header'], -1, 0, [$this->t('AI Translations')]);
$languages = $this->languageManager()->getLanguages();
$entity = $route_match->getParameter($entity_type_id);
$entity_id = $entity->id();
$storage = $this->entityTypeManager()->getStorage($entity_type_id);
$default_revision = $storage->load($entity_id);
$entity_type = $entity->getEntityType();
$use_latest_revisions = $entity_type->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $entity->bundle());
$lang_from = $entity->getUntranslated()->language()->getId();
$config = $this->config('ai_translate.settings');
$key = 0;
foreach ($languages as $langcode => $language) {
if (isset($overview['#rows'][$key])) {
$row = &$overview['#rows'][$key];
$key++;
}
elseif (isset($overview['#options'][$langcode])) {
$row = &$overview['#options'][$langcode];
}
else {
$row = NULL;
}
// If we don't have a row, we cannot do anything.
if ($row) {
// Get the latest revision.
// This logic comes from web/core/modules/content_translation/src/Controller/ContentTranslationController.php.
if ($use_latest_revisions) {
$entity = $default_revision;
$latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode);
if ($latest_revision_id) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $latest_revision */
$latest_revision = $storage->loadRevision($latest_revision_id);
// Make sure we do not list removed translations, i.e.
// translations that have been part of a default revision but no
// longer are.
if (!$latest_revision->wasDefaultRevision() || $default_revision->hasTranslation($langcode)) {
$entity = $latest_revision;
}
}
}
$ai_model = FALSE;
$additional = '';
if ($lang_from !== $langcode && !$entity->hasTranslation($langcode)) {
$model = $config->get($langcode . '_model') ?? '';
$parts = explode('__', $model);
if ($model == "" || empty($parts[0])) {
$default_model = $this->providerManager->getSimpleDefaultProviderOptions('chat');
if ($default_model !== "") {
$parts1 = explode('__', $default_model);
$ai_model = $parts1[1];
}
}
else {
$ai_model = $parts[1];
}
if ($ai_model) {
$additional = Link::createFromRoute($this->t('Translate using @ai', ['@ai' => $ai_model]),
'ai_translate.translate_content', [
'entity_type' => $entity_type_id,
'entity_id' => $entity_id,
'lang_from' => $lang_from,
'lang_to' => $langcode,
]
)->toString();
}
}
else {
$additional = $this->t('NA');
}
array_splice($row, -1, 0, [$additional]);
}
}
}
}
}
<?php
namespace Drupal\ai_translate\Form;
use Drupal\ai\AiProviderPluginManager;
use Drupal\content_translation\ContentTranslationManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the class for Ai Translate Form.
*/
class AiTranslateForm extends FormBase {
/**
* Config settings.
*/
const CONFIG_NAME = 'ai_translate.settings';
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected LanguageManagerInterface $languageManager;
/**
* The AI Provider service.
*
* @var \Drupal\ai\AiProviderPluginManager
*/
protected AiProviderPluginManager $providerManager;
/**
* Constructor for the class.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\ai\AiProviderPluginManager $provider_manager
* The AI provider manager.
*/
final public function __construct(EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager, AiProviderPluginManager $provider_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->languageManager = $language_manager;
$this->providerManager = $provider_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('language_manager'),
$container->get('ai.provider')
);
}
/**
* {@inheritdoc}
*/
public function getFormId(): string {
return 'ai_translate_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ?array $build = NULL): ?array {
_ai_translate_check_default_provider_and_model();
$form_state->set('entity', $build['#entity']);
$overview = NULL;
// If our build has a table in it, we'll assume it is the section of the
// page we want to update.
foreach (Element::children($build) as $child) {
if (isset($build[$child]['#theme']) && $build[$child]['#theme'] == 'table') {
$overview = $build[$child];
}
}
if ($overview) {
$form['#title'] = $this->t('Translations of @title', ['@title' => $build['#entity']->label()]);
// Inject our additional column into the header.
array_splice($overview['#header'], -1, 0, [$this->t('AI Translations')]);
// Make this a tableselect form.
$form['languages'] = [
'#type' => 'tableselect',
'#header' => $overview['#header'],
'#options' => [],
];
$languages = $this->languageManager->getLanguages();
$entity = $build['#entity'];
$entity_id = $entity->id();
$entity_type_id = $entity->getEntityTypeId();
$storage = $this->entityTypeManager->getStorage($entity_type_id);
$default_revision = $storage->load($entity_id);
$entity_type = $entity->getEntityType();
$use_latest_revisions = $entity_type->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $entity->bundle());
$lang_from = $entity->getUntranslated()->language()->getId();
$config = $this->config(static::CONFIG_NAME);
foreach ($languages as $langcode => $language) {
$option = array_shift($overview['#rows']);
// Get the latest revision.
// This logic comes from web/core/modules/content_translation/src/Controller/ContentTranslationController.php.
if ($use_latest_revisions) {
$entity = $default_revision;
$latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode);
if ($latest_revision_id) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $latest_revision */
$latest_revision = $storage->loadRevision($latest_revision_id);
// Make sure we do not list removed translations, i.e. translations
// that have been part of a default revision but no longer are.
if (!$latest_revision->wasDefaultRevision() || $default_revision->hasTranslation($langcode)) {
$entity = $latest_revision;
}
}
}
$ai_model = FALSE;
$additional = '';
if ($lang_from !== $langcode && !$entity->hasTranslation($langcode)) {
$model = $config->get($langcode . '_model') ?? '';
$parts = explode('__', $model);
if ($model == "" || empty($parts[0])) {
$default_model = $this->providerManager->getSimpleDefaultProviderOptions('chat');
if ($default_model == "") {
}
else {
$parts1 = explode('__', $default_model);
$ai_model = $parts1[1];
}
}
else {
$ai_model = $parts[1];
}
if ($ai_model) {
$additional = Link::createFromRoute($this->t('Translate using @ai', ['@ai' => $ai_model]),
'ai_translate.translate_content', [
'entity_type' => $entity_type_id,
'entity_id' => $entity_id,
'lang_from' => $lang_from,
'lang_to' => $langcode,
]
)->toString();
}
}
else {
$additional = $this->t('NA');
}
// Inject the additional column into the array.
// The generated form structure has changed, support both an additional
// 'data' key (that is not supported by tableselect) and the old version
// without.
if (isset($option['data'])) {
array_splice($option['data'], -1, 0, [$additional]);
// Append the current option array to the form.
$form['languages']['#options'][$langcode] = $option['data'];
}
else {
array_splice($option, -1, 0, [$additional]);
// Append the current option array to the form.
$form['languages']['#options'][$langcode] = $option;
}
}
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
/**
* Function to call the translate API and get the result.
*/
public function aiTranslateResult(array &$form, FormStateInterface $form_state) {
return [];
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment