tmgmt_xtm-8.x-5.x-dev/src/XtmTranslatorUi.php

src/XtmTranslatorUi.php
<?php

namespace Drupal\tmgmt_xtm;

use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\tmgmt\Entity\Job;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\TranslatorPluginUiBase;
use Drupal\tmgmt_xtm\Plugin\tmgmt\Translator\Connector;
use Drupal\tmgmt_xtm\Plugin\tmgmt\Translator\Helper;
use Drupal\tmgmt_xtm\Plugin\tmgmt\Translator\XtmTranslator;

/**
 * Provides the user interface for XTM translator settings.
 *
 * @package Drupal\tmgmt_xtm
 */
class XtmTranslatorUi extends TranslatorPluginUiBase {

  /**
   * XTM API Service constants.
   */
  const XTM_STATE_ERROR = 'ERROR';
  const XTM_STATE_FINISHED = 'FINISHED';
  const XTM_STATE_DELETED = 'DELETED';
  const XTM_STATE_DATA_NOT_FOUND = 'DATA_NOT_FOUND';
  const TMGMT_JOB_STATE_ACTIVE = '1';
  const XTM_STATE_ACTIVE = 'ACTIVE';
  const XTM_STATE_IN_PROGRESS = 'IN_PROGRESS';
  const XTM_STATE_PARTIALLY_FINISHED = 'PARTIALLY_FINISHED';

  /**
   * Builds the settings form for the XTM translator.
   *
   * @param array $form
   *   An associative array containing the form structure.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state object.
   * @param \Drupal\tmgmt\JobInterface $job
   *   The job entity for which settings are being configured.
   *
   * @return array
   *   The modified form array with settings options.
   */
  public function checkoutSettingsForm(array $form, FormStateInterface $formState, JobInterface $job) {
    $translator = $job->getTranslator();
    $connector = new Connector();
    $templates = $connector->getTemplates($translator);

    if (empty($templates)) {
      $form['#description'] = t(
        "The @translator translator doesn't have any templates.",
        ['@translator' => $job->getTranslator()->label()]
      );
    }
    else {
      $form[Connector::API_TEMPLATE_ID] = [
        '#type'          => 'select',
        '#title'         => t('XTM project template'),
        '#default_value' => $job->getSetting(Connector::API_TEMPLATE_ID) ?? '',
        '#description'   => t('Select an XTM template for this project.'),
        '#options'       => ['' => t('none')] + $templates,
      ];
    }

    $helper = new Helper();
    $projectMode = $translator->getSetting('default_project_mode') ?? 0;
    $form[Connector::API_PROJECT_MODE] = [
      '#type'          => 'radios',
      '#title'         => t('Project mode'),
      '#default_value' => $job->getSetting(Connector::API_PROJECT_MODE) ?? $projectMode,
      '#options'       => $helper->getProjectModes(),
    ];
    return parent::checkoutSettingsForm($form, $formState, $job);
  }

  /**
   * Handles AJAX requests, updating the checkout info form with project status.
   *
   * @param array $form
   *   An associative array containing the form structure.
   *   Passed by reference and is modified to include status messages.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state object,
   *   which contains information about the form state and submission.
   *
   * @return array
   *   The updated form array with the status message.
   */
  public function checkoutInfoAjax(array &$form, FormStateInterface $formState) {
    $parameters = \Drupal::routeMatch()->getParameters();
    $job = $parameters->get('tmgmt_job');
    $connector = new Connector();
    $response = $connector->checkProjectStatus($job);
    $message = '';

    if (empty($response)) {
      $message = t('Could not get project status.');
    }
    else {
      if ($response->status == self::XTM_STATE_ERROR) {
        \Drupal::messenger()->addError(
          t('The project has an error.') .
          ' ' . t('Please check the project in XTM for more details.')
        );
      }
      else {
        [$finished, $jobsErrors] = $this->checkCheckoutJobs($response);

        if (empty($jobsErrors)) {
          $message = $this->prepareCheckoutMessage($response, $job, $connector, $finished);
        }
        else {
          $error = \Drupal::translation()->formatPlural(
            count($jobsErrors),
            'The @files file has an error.',
            'The following files: @files has an errors.',
            ['@files' => implode(', ', $jobsErrors)]
          );
          \Drupal::messenger()->addError($error . ' ' . t('Please check the project in XTM for more details.'));
        }
      }
    }

    $messageFormated = '
    <div class="region region-highlighted">
      <div role="contentinfo" aria-label="Status message" class="messages messages--status">
        <h2 class="visually-hidden">Status message</h2>
        ' . $message . '
      </div>
    </div>';

    $form['translator_wrapper']['info']['status']['message']['wrapper'] = [
      '#markup' => $messageFormated,
    ];

    return $form['translator_wrapper']['info']['status']['message'];
  }

  /**
   * Prepares the checkout information form for a given job.
   *
   * @param \Drupal\tmgmt\JobInterface $job
   *   The job entity for which the checkout information form is being prepared.
   *
   * @return array
   *   The form array containing form elements for checking the project status.
   */
  public function checkoutInfo(JobInterface $job) {
    if (!$job->isActive()) {
      return [];
    }
    parent::checkoutInfo($job);

    $form['status']['desc'] = [
      '#markup' => t('Check for the project status in XTM. If the translation has been completed in XTM,
             but it is not available there, XTM translator will automatically retrieve the translation after
             clicking the button below.'),
      '#prefix' => '<div class="fieldset-description" style="margin-bottom:15px">',
      '#suffix' => '</div>',
    ];
    $form['status']['message'] = [
      '#type'   => 'container',
      '#prefix' => '<div id="message-wrapper">',
      '#suffix' => '</div>',
    ];
    $form['status']['check'] = [
      '#type'  => 'button',
      '#value' => t('Check project status'),
      '#ajax'  => [
        'callback' => [$this, 'checkoutInfoAjax'],
        'method'   => 'after',
        'wrapper'  => 'message-wrapper',
        'effect'   => 'fade',
        'progress' => [
          'type'    => 'throbber',
          'message' => t('Checking project status...'),
        ],
      ],
    ];

    return $form;
  }

  /**
   * Builds the configuration form for the XTM translator plugin.
   *
   * This method extends the base form to include additional settings
   * specific to the XTM translator. It adds a field for the XTM API URL,
   * which is required, and allows users to configure it within the form.
   *
   * @param array $form
   *   An associative array containing the form structure.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state object used to manage the form's state and submission.
   *
   * @return array
   *   The modified form array,
   *   including the configuration fields for the XTM translator.
   */
  public function buildConfigurationForm(array $form, FormStateInterface $formState) {
    $form = parent::buildConfigurationForm($form, $formState);
    $formEntity = $formState->getFormObject();
    if (!$formEntity instanceof EntityFormInterface) {
      throw new \DomainException('The form entity must implement EntityFormInterface.');
    }
    /** @var \Drupal\tmgmt\TranslatorInterface $translator */
    $translator = $formEntity->getEntity();
    if ($translator === NULL) {
      throw new \LogicException('The translator entity could not be found.');
    }
    $form[Connector::XTM_API_URL] = [
      '#type'          => 'url',
      '#title'         => t('XTM API URL'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting(Connector::XTM_API_URL),
      '#description'   => t('Please enter the XTM URL'),
    ];
    $form[Connector::XTM_API_CLIENT_NAME] = [
      '#type'          => 'textfield',
      '#title'         => t('XTM API Client name'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting(Connector::XTM_API_CLIENT_NAME),
      '#description'   => t('Please enter your Client name for XTM.'),
    ];
    $form[Connector::XTM_API_USER_ID] = [
      '#type'          => 'textfield',
      '#title'         => t('XTM API User ID'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting(Connector::XTM_API_USER_ID),
      '#description'   => t('Please enter your User ID for XTM.'),
    ];
    $form[Connector::XTM_API_PASSWORD] = [
      '#type'        => 'password',
      '#title'       => t('XTM API Password'),
      '#required'    => TRUE,
      '#description' => t('Please enter your Password for XTM.'),
      '#attributes'  => ['value' => $translator->getSetting(Connector::XTM_API_PASSWORD)],
    ];
    $form[Connector::XTM_PROJECT_CUSTOMER_ID] = [
      '#type'          => 'textfield',
      '#title'         => t('XTM project Customer ID'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting(Connector::XTM_PROJECT_CUSTOMER_ID),
      '#description'   => t('Please enter the project Customer ID'),
    ];
    $form[Connector::PROJECT_NAME_PREFIX] = [
      '#type'          => 'textfield',
      '#title'         => t('Project name prefix'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting(Connector::PROJECT_NAME_PREFIX),
      '#description'   => t('Enter a name prefix for new projects in XTM. Leave blank to disable prefix.'),
    ];
    $form['default_project_mode'] = [
      '#type'          => 'radios',
      '#title'         => t('Default project mode'),
      '#required'      => TRUE,
      '#default_value' => $translator->getSetting('default_project_mode'),
      '#description'   => t('Default mode of the project selected in job checkout.'),
      '#options'       => [
        t('Single file - translation returned at the end of the project'),
        t('Multiple files - translation returned when each file is complete'),
        t('Multiple files - translation returned when all files are complete'),
      ],
    ];
    $form['multiple_language_xtm_project'] = [
      '#type' => 'checkbox',
      '#title' => "<b>" . t('Enable multilingual projects in XTM') . "</b>",
      '#description'   => t('There will be one project in XTM for all target languages.'),
      '#default_value' => $translator->getSetting('multiple_language_xtm_project'),
    ];
    $helper = new Helper();
    foreach ($translator->getRemoteLanguagesMappings() as $localLanguage => $remoteLanguage) {
      $language = $helper->getXtmLanguage();
      $options = [];
      if (isset($language[$localLanguage][0])) {
        foreach ($language[$localLanguage] as $language) {
          $langNames = array_values($language);
          $options[key($language)] = reset($langNames);
        }
      }
      else {
        if (isset($language[$localLanguage])) {
          $langNames = array_values($language[$localLanguage]);
          $options[key($language[$localLanguage])] = reset($langNames);
        }
      }
      $form['plugin_wrapper']['remote_languages_mappings'][$localLanguage] = [
        '#attributes'    => ['style' => 'min-width:220px'],
        '#title'         => $localLanguage,
        '#default_value' => (string) $helper->mapLanguageToXTMFormat($localLanguage, $translator),
        '#type'          => 'select',
        '#options'       => $options,
      ];

      if (TRUE === empty($options)) {
        $form['plugin_wrapper']['remote_languages_mappings'][$localLanguage]['#type'] = 'textfield';
        $form['plugin_wrapper']['remote_languages_mappings'][$localLanguage]['#size'] = 10;
        $form['plugin_wrapper']['remote_languages_mappings'][$localLanguage]['#default_value']
          = $helper->mapLanguageToXTMFormat($localLanguage, $translator);
        unset($form['plugin_wrapper']['remote_languages_mappings'][$localLanguage]['#options']);
      }
    }
    $form['url'] = [
      '#type'          => 'hidden',
      '#default_value' => $translator->getSetting('url'),
    ];
    $form += $this->addConnectButton();
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function addConnectButton() {
    return parent::addConnectButton();
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $formState) {
    parent::validateConfigurationForm($form, $formState);

    $formEntity = $formState->getFormObject();
    $translator = NULL;
    if ($formEntity instanceof EntityFormInterface) {
      /** @var \Drupal\tmgmt\TranslatorInterface $translator */
      $translator = $formEntity->getEntity();
    }

    $endpoint = parse_url($translator->getSetting(Connector::XTM_API_URL));
    if (strpos($endpoint['host'], "xtm-cloud.com") !== FALSE && preg_match('/^(https:\/\/|www\.)?xtm-cloud\.com/', $endpoint['host'])) {
      $endpoint['host'] = 'api.xtm-cloud.com';
      $endpoint = $this->unparseUrl($endpoint);
      $input = $formState->getUserInput();
      $input['settings'][Connector::XTM_API_URL] = $endpoint;
      $translator->setSetting(Connector::XTM_API_URL, $endpoint);
      $formState->setUserInput($input);
      $complete_form = $formState->getCompleteForm();
      $complete_form['plugin_wrapper']['settings']['xtm_api_url']['#value'] = $endpoint;
      $formState->setValueForElement($form, ['value' => $endpoint]);
      $formState->setCompleteForm($complete_form);
    }

    if ("" === $translator->getSetting('api_key')) {
      return;
    }
    if (!empty($translator->getSetting(Connector::XTM_PROJECT_CUSTOMER_ID))) {
      $xtmTranslator = $translator->getPlugin();
      if ($xtmTranslator instanceof XtmTranslator) {
        $customer = $xtmTranslator->findCustomer(
        $translator,
        $translator->getSetting(Connector::XTM_PROJECT_CUSTOMER_ID)
        );
      }
    }
    /** @var \Drupal\tmgmt\TranslatorInterface $translator */
    if (empty($customer)) {
      $formState->setErrorByName('settings][api_key', t('Could not connect to XTM.'));
    }
  }

  /**
   * Converts the internal state to a human-readable string.
   *
   * This method translates the internal state values into user-friendly text.
   *
   * @param string $action
   *   The internal state identifier.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup|string
   *   The translated string representing the state,
   *   or an empty string for unknown states.
   */
  private function getReadableState($action) {
    switch ($action) {
      case self::XTM_STATE_ACTIVE:
        return t('active');

      case self::XTM_STATE_IN_PROGRESS:
        return t('in progress');

      case self::XTM_STATE_FINISHED:
        return t('finished');

      case self::XTM_STATE_ERROR:
        return t('error');

      case self::XTM_STATE_PARTIALLY_FINISHED:
        return t('partially finished');

      default:
        return '';
    }
  }

  /**
   * Prepares a message based on the project status and job details.
   *
   * This method generates a message that summarizes the project status
   * and any actions that need to be taken based on the response from XTM.
   *
   * @param mixed $response
   *   The response object from XTM containing project status and job details.
   * @param \Drupal\tmgmt\Entity\Job $job
   *   The job entity associated with the translation project.
   * @param \Drupal\tmgmt_xtm\Plugin\tmgmt\Translator\Connector $connector
   *   The connector object used to interact with XTM.
   * @param int $finished
   *   The number of finished tasks.
   *
   * @return string
   *   The formatted message describing the current status of the project.
   */
  private function prepareCheckoutMessage($response, Job $job, Connector $connector, $finished) {
    $message = [
      t(
        'The project status is <b>@state</b>.',
        ['@state' => $this->getReadableState($response->status)]
      ),
    ];
    if ($job->getState() == self::TMGMT_JOB_STATE_ACTIVE
      && $response->status == self::XTM_STATE_FINISHED
    ) {
      if (TRUE === $connector->retrieveTranslation($job)) {
        $message[] = t('The translation has been received. Please refresh the page (F5).');
      }
      else {
        $message[] = t('<b>We were unable to retrieve translation. Check XTM settings</b>');
      }
    }
    else {
      if ($finished > 0) {
        $message[] = t(
          'Finished tasks: @jobs.',
          ['@jobs' => $finished . '/' . count($response->jobs)]
        );
      }
    }

    return implode(' ', $message);
  }

  /**
   * Checks the status of jobs and collects jobs information.
   *
   * @param object $response
   *   The response object containing job status information.
   *
   * @return array
   *   An array with two elements:
   *     - An integer representing the number of finished jobs.
   *     - An array of file names that encountered errors.
   */
  private function checkCheckoutJobs($response) {
    $finished = 0;
    $jobsErrors = [];

    foreach ($response->jobs as $jobFile) {
      if ($jobFile->status == self::XTM_STATE_FINISHED) {
        $finished++;
      }
      else {
        if ($jobFile->status == self::XTM_STATE_ERROR) {
          $jobsErrors[] = $jobFile->fileName;
        }
      }
    }
    return [$finished, $jobsErrors];
  }

  /**
   * Reconstructs a URL from its parsed components.
   *
   * @param array $parsed_url
   *   An associative array containing the components of a URL,
   *   such as scheme, host, port, user, pass, path, query, and fragment.
   *
   * @return string
   *   The reconstructed URL as a string.
   */
  public static function unparseUrl($parsed_url) {
    $scheme   = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
    $host     = $parsed_url['host'] ?? '';
    $port     = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
    $user     = $parsed_url['user'] ?? '';
    $pass     = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
    $pass     = ($user || $pass) ? "$pass@" : '';
    $path     = $parsed_url['path'] ?? '';
    $query    = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
    $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
    return "$scheme$user$pass$host$port$path$query$fragment";
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc