simple_tmgmt-1.0.x-dev/simple_tmgmt.module

simple_tmgmt.module
<?php

/**
 * @file
 * Contains simple_tmgmt.module.
 */

use Drupal\workflows\WorkflowInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Core\GeneratedLink;
use Drupal\Core\Render\Markup;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\JobItemInterface;
use Drupal\simple_tmgmt\SimpleTmgmt;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Implements hook_help().
 */
function simple_tmgmt_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the simple_tmgmt module.
    case 'help.page.simple_tmgmt':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('UX experiments to simplify the workflow for basic use cases and integration with other modules.') . '</p>';
      // @todo add Simple TMGMT help page on DO
      $output .= '<p><a href="https://www.drupal.org/docs/8/modules/translation-management-tool">' . t('Translation Management documentation.') . '</a></p>';

      return $output;

    default:
  }
}

/**
 * Implements hook_theme().
 */
function simple_tmgmt_theme() {
  return [
    // Custom mail templates.
    'mail__job_create' => [
      'variables' => [
        'content' => [],
      ],
    ],
    'mail__job_error' => [
      'variables' => [
        'content' => [],
      ],
    ],
    // Override tmgmt_data_items_form to optionally
    // remove the validation tick boxes.
    'tmgmt_data_items_form' => [
      'render element' => 'element',
    ],
  ];
}

/**
 * Implements hook_mail().
 */
function simple_tmgmt_mail($key, &$message, $params) {
  switch ($key) {
    case 'job_create':
      $message['headers'] = $params['headers'];
      $message['headers']['Content-Type'] = SWIFTMAILER_FORMAT_HTML;
      $message['from'] = $params['from'];
      $message['subject'] = $params['subject'];
      $message['body'][] = $params['body'];
      $message['options'] = $params['options'];
      $message['files'] = $params['files'];
      break;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Step 1, translation form.
 */
function simple_tmgmt_form_tmgmt_content_translate_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  $entity = $form_state->get('entity');

  // Does not cover yet forms for configuration entities.
  if (!$entity instanceof ContentEntityInterface) {
    return;
  }

  $config = \Drupal::configFactory()->get('simple_tmgmt.settings');

  // Remove the add to cart operation.
  // @todo provide configuration/permission
  // if (array_key_exists('add_to_cart', $form['actions'])) {
  //  $form['actions']['add_to_cart']['#access'] = FALSE;
  // }.
  // Remove disabled languages from the translation form.
  // This needs to be covered by https://www.drupal.org/project/disable_language/issues/3091554
  // But as we are already making use of the TMGMT form override this issue
  // fix has an easy workaround.
  if (
    Drupal::moduleHandler()->moduleExists('disable_language') &&
    !Drupal::currentUser()->hasPermission('create content in disabled language')
  ) {
    $disabledLanguages = \Drupal::service('disable_language.disable_language_manager')->getDisabledLanguages();
    foreach (array_keys($disabledLanguages) as $langCode) {
      if (array_key_exists($langCode, $form['languages']['#options'])) {
        unset($form['languages']['#options'][$langCode]);
      }
    }
  }

  // Add the help link.
  /** @var \Drupal\simple_tmgmt\SimpleTmgmt $simpleTmgmt */
  $simpleTmgmt = \Drupal::service('simple_tmgmt');
  $form['actions']['help'] = [
    '#type' => 'markup',
    '#markup' => $simpleTmgmt->getHelpLinkMarkup(),
  ];

  // Add the manual and machine providers actions only to translatable items.
  if (array_key_exists('languages', $form) && array_key_exists('#options', $form['languages'])) {
    // Get untranslatable items.
    // Source and languages that are already the subject of a Job
    // are already excluded by TMGMT.
    $availableLanguages = \Drupal::languageManager()->getLanguages();
    $availableLanguageIds = array_keys($availableLanguages);
    $untranslatableLanguages = [];
    foreach ($form['languages'] as $langCode => $tableSelectElement) {
      if (
        in_array($langCode, $availableLanguageIds) &&
        $form['languages'][$langCode]['#disabled'] === TRUE
      ) {
        $untranslatableLanguages[] = $langCode;
      }
    }

    // @todo refactor so it is the responsibility of tmgmt_content_moderation.
    if (\Drupal::moduleHandler()->moduleExists('tmgmt_content_moderation')) {
      /** @var \Drupal\tmgmt_content_moderation\TmgmtContentModerationInterface $tmgmtContentModeration */
      $tmgmtContentModeration = \Drupal::service('tmgmt_content_moderation');
      if (!$tmgmtContentModeration->isSourceStateValidForTranslation($entity)) {
        $untranslatableLanguages = $availableLanguageIds;
      }
    }

    // Disable checkboxes if 'translate again' is not set.
    if ($config->get('translate_again') !== 1) {
      $availableEntityTranslations = array_keys($entity->getTranslationLanguages(FALSE));
      foreach ($availableEntityTranslations as $availableEntityTranslation) {
        $form['languages'][$availableEntityTranslation] = [
          '#type' => 'checkbox',
          '#disabled' => TRUE,
        ];
        $untranslatableLanguages[] = $availableEntityTranslation;
      }
    }

    $hasAllTranslations = count($untranslatableLanguages) === count($availableLanguages);
    // When all translations have been completed, remove the translation request action.
    if ($hasAllTranslations) {
      $form['actions']['request']['#access'] = FALSE;
      $form['actions']['translations_completed'] = [
        '#type' => 'markup',
        '#markup' => t('All translation requests are pending or have been completed.'),
        '#weight' => -10,
      ];
    }
    // Remove operation links when several translations are requested.
    else {
      $form['#attached']['library'][] = 'simple_tmgmt/entity_translate_form_behaviors';
    }

    // Get available / active translation providers (Translators).
    $activeProviders = $simpleTmgmt->getActiveTranslators();
    // Get column indexes.
    $headerIndex = 0;
    foreach ($form['languages']['#header'] as $header) {
      $headerIndex++;
      if ($header instanceof TranslatableMarkup) {
        if ($header->getUntranslatedString() === 'Pending Translations') {
          $pendingTranslationsColumnIndex = $headerIndex;
        }
      }
    }
    // Modify links and operations with available providers.
    foreach ($form['languages']['#options'] as $langCode => &$languageRow) {
      $currentColumnIndex = 0;
      foreach ($languageRow as $rowKey => &$rowItem) {
        $currentColumnIndex++;

        // Modify "Pending translations" links.
        if (
          $rowItem instanceof GeneratedLink &&
          $currentColumnIndex === $pendingTranslationsColumnIndex
        ) {
          $link = $rowItem->getGeneratedLink();
          $transformedLink = $simpleTmgmt->transformTranslationLink($link);
          $rowItem->setGeneratedLink($transformedLink);
        }

        // Modify "Operations" links.
        if (
          is_array($rowItem) &&
          array_key_exists('data', $rowItem) &&
          array_key_exists('#type', $rowItem['data']) &&
          $rowItem['data']['#type'] === 'operations'
        ) {
          $addTmgmtOperations = FALSE;
          $isNewContent = array_key_exists('add', $languageRow[$rowKey]['data']['#links']);
          // Only add TMGMT translators operations if 'translate again' is set
          // for existing content.
          if (
            !$isNewContent &&
            $config->get('translate_again') === 1
          ) {
            $addTmgmtOperations = TRUE;
          }
          // Always add TMGMT translators operations while creating content.
          else {
            $addTmgmtOperations = TRUE;
          }

          if ($addTmgmtOperations) {
            // Add providers.
            if (!in_array($langCode, $untranslatableLanguages)) {
              // Change 'add' operation title.
              if ($isNewContent) {
                $languageRow[$rowKey]['data']['#links']['add']['title'] = $config->get('operation_add');
              }
              // Check if the translation is possible to add the provider link.
              $supportedProviders = [];
              foreach ($activeProviders as $activeProvider) {
                if ($simpleTmgmt->isTranslationSupported($activeProvider, $entity->language(), $langCode)) {
                  $supportedProviders[$activeProvider->id()] = [
                    'title' => empty($overriddenLabel) ? $activeProvider->label() : $overriddenLabel,
                    'url' => $simpleTmgmt->getJobCreateUrl($entity, $langCode, $activeProvider->id()),
                  ];
                }
              }
              if (!empty($supportedProviders)) {
                $insertBeforeKey = 'add';
                $index = array_search($insertBeforeKey, array_keys($languageRow[$rowKey]['data']['#links']));
                array_splice($languageRow[$rowKey]['data']['#links'], $index, 0, $supportedProviders);
              }
            }
            // Do not allow to add a manual translation when a Job is in progress.
            else {
              unset($languageRow[$rowKey]['data']['#links']['add']);
            }
          }
        }
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Step 2, Job edit form.
 */
function simple_tmgmt_form_tmgmt_job_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  /** @var \Drupal\simple_tmgmt\SimpleTmgmtInterface $simpleTmgmt */
  $simpleTmgmt = \Drupal::service('simple_tmgmt');

  // Remove disabled actions.
  $disabledActions = $simpleTmgmt->getJobFormDisabledActions();
  foreach ($disabledActions as $action) {
    if (array_key_exists($action, $form['actions'])) {
      $form['actions'][$action]['#access'] = FALSE;
    }
  }

  // Add redirect to the node translation form for the "Delete" action.
  if (array_key_exists('delete', $form['actions'])) {
    $simpleTmgmt->setTranslationFormUrl($form_state);
    $form['actions']['delete']['#submit'][] = '_simple_tmgmt_delete_job_form_submit_handler';
  }

  // Disable target language select element.
  // @todo make it configurable.
  //   if (array_key_exists('target_language', $form['info'])) {
  //    $form['info']['target_language']['#disabled'] = TRUE;
  //  }.
  if (array_key_exists('translator', $form['translator_wrapper'])) {
    // Override default settings title.
    $form['translator_wrapper']['settings']['#title'] = t('Settings');

    $translatorsToRemove = SimpleTmgmt::translatorsToDisable();
    foreach ($form['translator_wrapper']['translator']['#options'] as $optionKey => $option) {
      if (in_array($optionKey, $translatorsToRemove)) {
        unset($form['translator_wrapper']['translator']['#options'][$optionKey]);
      }
    }
    if (array_key_exists('submit_all', $form['translator_wrapper'])) {
      // Depending on the state of several Job Items for the same entity,
      // it can happen that the "Submit all" checkbox
      // that applies to several Jobs is displayed even if they
      // are not in the scope of a batch.
      // Holding an internal state for this case so we can safely remove
      // this checkbox if necessary.
      $hasSeveralJobs = FALSE;
      if (
        array_key_exists('progress_details', $form) &&
        array_key_exists('job_list', $form['progress_details'])
      ) {
        $hasSeveralJobs = $form['progress_details']['job_list']['#items'] > 1;
      }
      $form['translator_wrapper']['submit_all']['#access'] = $hasSeveralJobs;

      // Check by default 'submit all translations jobs with the same settings'.
      $form['translator_wrapper']['submit_all']['#default_value'] = TRUE;
    }

    // Add custom submit handler
    // for redirection based on the translator provider.
    $form['actions']['submit']['#submit'][] = '_simple_tmgmt_form_tmgmt_job_edit_form_submit_handler';

    // Select default provider from the url parameter if any.
    // Redirection based on the provider are set by the destination
    // from the JobController in this case.
    // @todo possibly refactor redirection with the custom submit handler.

    // @todo fails when the first provider is unsupported (e.g. DeepL not translatable)
    $defaultProvider = \Drupal::request()->get('default_provider');
    if (
      !empty($defaultProvider) &&
      // in_array($defaultProvider, $validProviders) &&.
      $form_state->getValue('translator') !== $defaultProvider
    ) {
      /** @var \Drupal\tmgmt\JobInterface $job */
      $job = \Drupal::routeMatch()->getParameter('tmgmt_job');
      $form['translator_wrapper']['translator']['#default_value'] = $defaultProvider;
      // @todo ideally trigger ajax change, disabling translator to prevent on change issues.
      // $form['translator_wrapper']['translator']['#disabled'] = TRUE;
      // Unset the logo if the provider is manual.
      if (
        array_key_exists('logo', $form['translator_wrapper']) &&
        $defaultProvider === 'simple_tmgmt_file_mail'
      ) {
        unset($form['translator_wrapper']['logo']);
      }

      /** @var \Drupal\tmgmt\TranslatorManager $translatorManager */
      $translatorManager = \Drupal::service('plugin.manager.tmgmt.translator');
      $settingsForm = [];
      if (!$job->hasTranslator()) {
        return $settingsForm;
      }
      $translator = $job->getTranslator();
      $translatorResult = $translator->checkAvailable();
      if (!$translatorResult->getSuccess()) {
        $settingsForm['#description'] = $translatorResult->getReason();
        return $settingsForm;
      }
      $translatorResult = $translator->checkTranslatable($job);
      if ($job->getTargetLangcode() && !$translatorResult->getSuccess()) {
        $settingsForm['#description'] = $translatorResult->getReason();
        return $settingsForm;
      }
      $plugin_ui = $translatorManager->createUIInstance($defaultProvider);
      $settingsForm = $plugin_ui->checkoutSettingsForm($settingsForm, $form_state, $job);

      $form['translator_wrapper']['settings'] = [
        '#type' => 'details',
        '#title' => t('Settings'),
        '#prefix' => '<div id="tmgmt-ui-translator-settings">',
        '#suffix' => '</div>',
        '#tree' => TRUE,
        '#open' => TRUE,
      ] + $settingsForm;
    }
  }

  if (array_key_exists('job_items_wrapper', $form)) {
    // Hide suggestions details.
    if (array_key_exists('suggestions', $form['job_items_wrapper'])) {
      $form['job_items_wrapper']['suggestions']['#access'] = FALSE;
    }
    if (array_key_exists('items', $form['job_items_wrapper'])) {
      // Open Job Items by default.
      $form['job_items_wrapper']['items']['#open'] = TRUE;

      // Some changes are only possible if there is one single job item.
      // As a mail is sent with the node title, prevent sending multiple items
      // while using manual translation.
      $itemsAmount = count($form['job_items_wrapper']['items']['view']['#rows']);
      if ($itemsAmount === 1) {
        /** @var \Drupal\views\ViewExecutable $view */
        $view = $form['job_items_wrapper']['items']['view']['#view'];
        $view->execute();
        $result = $view->result;
        /** @var \Drupal\tmgmt\JobItemInterface $jobItem */
        $jobItem = $result[0]->_entity;

        // Replace the label with markup, and set the default value
        // using the node title.
        $form['label']['widget'][0]['value']['#access'] = FALSE;
        $form['label']['widget'][0]['value']['#default_value'] = $jobItem->getSourceLabel();
        $form['label']['#markup'] = '<h1>' . $jobItem->getSourceLabel() . '</h1>';
        // In this case the Job items are redundant, remove them.
        $form['job_items_wrapper']['items']['#access'] = FALSE;
      }
    }
  }
}

/**
 * Custom submit handler to redirect based on the selected translator provider.
 */
function _simple_tmgmt_form_tmgmt_job_edit_form_submit_handler(array &$form, FormStateInterface $form_state) {
  // Set form redirect for Machine translator.
  /** @var \Drupal\simple_tmgmt\SimpleTmgmtInterface $simpleTmgmt */
  $simpleTmgmt = \Drupal::service('simple_tmgmt');
  if ($simpleTmgmt->isMachineTranslator($form_state->getValue('translator'))) {
    $job = \Drupal::routeMatch()->getParameter('tmgmt_job');
    if ($job instanceof JobInterface) {
      try {
        $jobItemStorage = \Drupal::entityTypeManager()->getStorage('tmgmt_job_item');
        $jobItems = $jobItemStorage->loadByProperties([
          'tjid' => $job->id(),
        ]);
        if (!empty($jobItems)) {
          $jobItem = reset($jobItems);
          if ($jobItem instanceof JobItemInterface) {
            $form_state->setRedirect('entity.tmgmt_job_item.canonical', [
              'tmgmt_job_item' => $jobItem->id(),
            ]);
          }
        }
      }
      catch (\Exception $exception) {
        \Drupal::messenger()->addError($exception->getMessage());
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Step 3, Job item edit form.
 */
function simple_tmgmt_form_tmgmt_job_item_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  /** @var \Drupal\simple_tmgmt\SimpleTmgmtInterface $simpleTmgmt */
  $simpleTmgmt = \Drupal::service('simple_tmgmt');

  // Remove disabled actions.
  $disabledActions = $simpleTmgmt->getJobItemFormDisabledActions();
  foreach ($disabledActions as $action) {
    if (array_key_exists($action, $form['actions'])) {
      $form['actions'][$action]['#access'] = FALSE;
    }
  }

  // Changes the UX for the tick boxes status (red/green, message).
  $form['#attached']['library'][] = 'simple_tmgmt/job_item_edit_form_behaviors';

  /** @var \Drupal\tmgmt\JobItemInterface $jobItem */
  $jobItem = \Drupal::routeMatch()->getParameter('tmgmt_job_item');
  if (
    !$jobItem instanceof JobItemInterface ||
    !$jobItem->getSourceUrl() instanceof Url
  ) {
    return;
  }

  // There might be another way to get the entity?
  $options = $jobItem->getSourceUrl()->getOptions();
  if (array_key_exists('entity', $options)) {
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $options['entity'];
    // Add a custom submit handler if not handled by tmgmt_content_moderation.
    // @todo cover other cases for other entity types:
    if ($entity instanceof ContentEntityInterface) {
      $hasEntityWorkflow = FALSE;
      if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
        /** @var \Drupal\content_moderation\ModerationInformationInterface $contentModerationInformation */
        $contentModerationInformation = \Drupal::service('content_moderation.moderation_information');
        $hasEntityWorkflow = $contentModerationInformation->getWorkflowForEntityTypeAndBundle($entity->getEntityTypeId(), $entity->bundle()) instanceof WorkflowInterface;
      }
      if (!$hasEntityWorkflow) {
        // When the job is saved as completed, redirect to the translated node.
        $form['actions']['accept']['#submit'][] = '_simple_tmgmt_form_tmgmt_job_item_edit_form_submit_handler';
      }
    }
  }
}

/**
 * Custom submit handler to redirect to the translated entity once the Job Item is completed.
 */
function _simple_tmgmt_form_tmgmt_job_item_edit_form_submit_handler(array &$form, FormStateInterface $form_state) {
  /** @var \Drupal\tmgmt\JobItemInterface $jobItem */
  $jobItem = \Drupal::routeMatch()->getParameter('tmgmt_job_item');
  // Source entity url (e.g. node).
  $entityUrl = $jobItem->getSourceUrl();
  $targetLanguage = $jobItem->getJob()->getTargetLanguage();
  $entityUrl->setOption('language', $targetLanguage);
  $form_state->setRedirectUrl($entityUrl);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Transforms translation links.
 *
 * Step 4, translation overview form (/admin/tmgmt/sources).
 */
function simple_tmgmt_form_tmgmt_overview_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (!empty($form['items']['#options'])) {
    /** @var \Drupal\simple_tmgmt\SimpleTmgmtInterface $simpleTmgmt */
    $simpleTmgmt = \Drupal::service('simple_tmgmt');
    foreach ($form['items']['#options'] as $entityItemKey => $entityItem) {
      foreach ($entityItem as $entryKey => $entryValue) {
        if (
          strpos($entryKey, 'langcode') === 0 &&
          array_key_exists('data', $entryValue)
        ) {
          $markup = (string) $entryValue['data'];
          $transformedMarkup = $simpleTmgmt->transformTranslationLink($markup);
          $form['items']['#options'][$entityItemKey][$entryKey]['data'] = Markup::create($transformedMarkup);
        }
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * When the job is deleted, redirect to the node translation form.
 */
function simple_tmgmt_form_tmgmt_job_delete_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  /** @var \Drupal\simple_tmgmt\SimpleTmgmtInterface $simpleTmgmt */
  $simpleTmgmt = \Drupal::service('simple_tmgmt');
  $simpleTmgmt->setTranslationFormUrl($form_state);
  $form['actions']['submit']['#submit'][] = '_simple_tmgmt_delete_job_form_submit_handler';
}

/**
 * Custom submit handler to redirect to the entity translation form once the Job and Job item are deleted.
 */
function _simple_tmgmt_delete_job_form_submit_handler(array &$form, FormStateInterface $form_state) {
  $entityTypeManager = \Drupal::entityTypeManager();
  if (
    $form_state->get('job_item_to_delete') instanceof JobItemInterface &&
    $form_state->get('job_to_delete') instanceof JobInterface &&
    $form_state->get('translation_form_url') instanceof Url
  ) {
    try {
      $tmgmtJobItemStorage = $entityTypeManager->getStorage('tmgmt_job_item');
      $tmgmtJobStorage = $entityTypeManager->getStorage('tmgmt_job');
      $tmgmtJobItemStorage->delete([$form_state->get('job_item_to_delete')]);
      $tmgmtJobStorage->delete([$form_state->get('job_to_delete')]);

      $translationFormUrl = $form_state->get('translation_form_url');
      $form_state->setRedirectUrl($translationFormUrl);
    }
    catch (\Exception $exception) {
      \Drupal::messenger()->addError($exception->getMessage());
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Removes the TMGMT status message that relates to semantic integrity
 * as it could be misleading for some roles.
 */
// Function simple_tmgmt_preprocess_status_messages(&$variables) {
//  if (array_key_exists('status', $variables['message_list'])) {
//    $statusMessages = $variables['message_list']['status'];
//    $alteredMessages = array_filter($statusMessages, function ($message, $key) {
//      $messagesToRemove = 'Please check also the HTML code of the element in the review process.';
//      // Cast Markup instances.
//      $message = (string) $message;
//      return strpos($message, $messagesToRemove) === FALSE;
//    }, ARRAY_FILTER_USE_BOTH);
//    if (empty($alteredMessages)) {
//      // Prevent to display empty status message box.
//      unset($variables['message_list']['status']);
//    }
//    else {
//      $variables['message_list']['status'] = $alteredMessages;
//    }
//  }
// }.
/**
 * Implements hook_theme_registry_alter().
 */
// Function simple_tmgmt_theme_registry_alter(&$theme_registry) {
//  // Override tmgmt_data_items_form to optionally remove the validation tick boxes.
//  // They might not be wanted in some use cases (already translated content that will be reviewed in one step).
//  // @todo make it configurable first.
//  $theme_registry['tmgmt_data_items_form']['path'] = drupal_get_path('module', 'simple_tmgmt') . '/templates';
// }.

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

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