l10n_server-2.x-dev/l10n_community/src/Form/ExportForm.php

l10n_community/src/Form/ExportForm.php
<?php

declare(strict_types=1);

namespace Drupal\l10n_community\Form;

use Drupal\Core\Entity\Element\EntityAutocomplete;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\l10n_community\L10nExporter;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Translation export form.
 */
class ExportForm extends FormBase {

  /**
   * The project entity storage.
   */
  protected EntityStorageInterface $projectStorage;

  /**
   * The release entity storage.
   */
  protected EntityStorageInterface $releaseStorage;

  /**
   * The language manager serviec.
   */
  protected LanguageManagerInterface $languageManager;

  /**
   * The exporter service.
   */
  protected L10nExporter $communityExporter;

  /**
   * Constructs a ExportForm object.
   *
   * @param \Drupal\Core\Entity\EntityStorageInterface $project_storage
   *   The current route match.
   * @param \Drupal\Core\Entity\EntityStorageInterface $release_storage
   *   The current route match.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager serviec.
   * @param \Drupal\l10n_community\L10nExporter $community_exporter
   *   The exporter service.
   */
  public function __construct(
    EntityStorageInterface $project_storage,
    EntityStorageInterface $release_storage,
    LanguageManagerInterface $language_manager,
    L10nExporter $community_exporter,
  ) {
    $this->projectStorage = $project_storage;
    $this->releaseStorage = $release_storage;
    $this->languageManager = $language_manager;
    $this->communityExporter = $community_exporter;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')->getStorage('l10n_server_project'),
      $container->get('entity_type.manager')->getStorage('l10n_server_release'),
      $container->get('language_manager'),
      $container->get('l10n_community.exporter'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'l10n_community_export';
  }

  /**
   * Form declaration.
   *
   * This form can be displayed in two different use cases.
   * 1st: you access to the export page of a given project. The project ID
   * will be forwarded as an URL query argument.
   * 2nd: You can access only to the export page of a group (language). In
   * this use case, you will first need to select a project to export strings
   * from.
   *
   * @param array $form
   *   Form declaration.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   FormState object.
   *
   * @return array
   *   Form definition to show.
   */
  public function buildForm(array $form, FormStateInterface $form_state) : array {
    $preselected_project = FALSE;

    // First, make sure that the group language is set and valid.
    if (empty($form_state->get('export_language'))) {
      $langcode = \Drupal::routeMatch()->getParameter('group')->field_translation_language->target_id;
      if ($language = $this->languageManager->getLanguage($langcode)) {
        $form_state->set('export_language', $language);
      }
      else {
        $this->messenger()->addError($this->t('There is no language matching the provided "@langcode" langcode. Please warn an administrator about this issue.', ['@langcode' => $langcode]));
        $form['language_unavailable'] = [
          '#type' => 'html_tag',
          '#tag' => 'strong',
          '#value' => $this->t('The group language "@langcode" is missing.', ['@langcode' => $langcode]),
        ];
        return $form;
      }
    }
    $project_id = $this->computeProjectId();

    // Determine if the project was preset or it has just been populated through
    // AJAX interactions.
    if (!empty($project_id) && (
      !isset($form_state->getUserInput()['_drupal_ajax']) ||
      (isset($form_state->getUserInput()['_drupal_ajax']) && $form_state->getUserInput()['_drupal_ajax'] != '1')
    )) {
      $preselected_project = TRUE;
    }

    // When project was preset from URL query, disable the project selector.
    if ($preselected_project) {
      $form['project'] = [
        '#type' => 'value',
        '#value' => $project_id,
      ];
      $project = $this->projectStorage->load($project_id);
      $form['project_name'] = [
        '#type' => 'html_tag',
        '#tag' => 'h3',
        '#value' => $this->t('@project project', ['@project' => $project->label()]),
      ];
    }
    else {
      $form['project'] = [
        '#title' => t('Project'),
        '#required' => TRUE,
        '#default_value' => $project_id ?? NULL,
        '#ajax' => [
          'callback' => [$this, 'ajaxReleases'],
          'event' => 'change',
          'wrapper' => 'l10n-server-releases',
          'effect' => 'fade',
          'progress' => [
            'type' => 'throbber',
            'message' => $this->t('Getting releases...'),
          ],
        ],
      ];
      $project_count = $this->projectStorage
        ->getQuery()
        ->accessCheck(TRUE)
        ->condition('status', 1)
        ->count()
        ->accessCheck(FALSE)
        ->execute();

      if ($project_count <= 30) {
        // Radio box widget for as much as 5 projects, select widget for 5-30
        // projects.
        $form['project']['#type'] = ($project_count <= 5 ? 'radios' : 'select');
        $form['project']['#options'] = [];
        $projects = $this->projectStorage->loadMultiple();
        foreach ($projects as $project) {
          // Title used to conform to the autocomplete behavior.
          $form['project']['#options'][$project->id()] = $project->label();
        }
      }
      else {
        // Autocomplete field for more than 30 projects.
        $form['project'] += [
          '#type' => 'entity_autocomplete',
          '#target_type' => 'l10n_server_project',
        ];
      }
    }

    $release_options = [];
    if (!empty($project_id)) {
      $releases = $this->releaseStorage->loadByProperties(['pid' => $project_id]);
      foreach ($releases as $release) {
        $release_options[$release->id()] = t('@version only', [
          '@version' => $release->getVersion(),
        ]);
      }
      // Sort by semver most recent releases on top.
      uasort($release_options, 'version_compare');
      $release_options = array_reverse($release_options, TRUE);
    }
    $release_options = ['all' => t('All releases merged')] + $release_options;

    $release_id = $this->computeReleaseId();
    $form['release'] = [
      '#title' => $this->t('Release'),
      '#required' => TRUE,
      '#type' => 'select',
      '#options' => $release_options,
      '#default_value' => $release_id ?? 'all',
      '#prefix' => '<div id="l10n-server-releases">',
      '#suffix' => '</div>',
    ];

    $form['download_type'] = [
      '#title' => $this->t('Would you like to download the full strings available or limit to translations?'),
      '#type' => 'radios',
      '#options' => [
        $this->t('Download untranslated and translated strings'),
        $this->t('Download only translated strings'),
      ],
      '#description' => $this->t("Untranslated strings are useful to work on the file translation."),
      '#default_value' => 1,
    ];
    $form['suggestions'] = [
      '#title' => $this->t('Inject @language suggestions?', ['@language' => $form_state->get('export_language')->getName()]),
      '#type' => 'checkbox',
      '#description' => $this->t('If checked, extra translations will be added. The first suggestion will be exported as a fuzzy translation. All other suggestions are exported in comments.'),
    ];
    $form['compact'] = [
      '#title' => $this->t('Inject metadata?'),
      '#type' => 'radios',
      '#options' => [$this->t('No metadata'), $this->t('Include metadata')],
      '#description' => $this->t('Metadata are useful to understand string context usages (it includes files and line numbers where used). It is only useful for translators.'),
      '#default_value' => 0,
    ];

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Export Gettext file'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $project = FALSE;
    if ($selected_project = $form_state->getValue('project')) {
      $project = $this->projectStorage->load($selected_project);
    }

    if (empty($project)) {
      $form_state->setError($form['project'], $this->t('This project can not be loaded. Is its ID valid?'));
    }
    else {
      // Store the project for the submit callback.
      $form_state->set('export_project', $project);

      if ($form_state->getValue('release') != 'all') {
        $releases = $this->releaseStorage->loadByProperties([
          'pid' => $project->id(),
          'rid' => $form_state->getValue('release'),
        ]);
        if (!$releases) {
          $form_state->setError($form['release'], $this->t('Invalid release chosen.'));
        }
        else {
          $form_state->set('export_release', array_shift($releases));
        }
      }
      // If all the release should be exported, set the release value to NULL
      // since NULL represent a placeholder value in internal APIs.
      // This should be updated to a more explicit value at some point.
      elseif ($form_state->getValue('release') == 'all') {
        $form_state->set('export_release', NULL);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Generate tarball or PO file and get file name.
    $export_result = $this->communityExporter->export(
      $form_state->get('export_project'),
      $form_state->get('export_language'),
      $form_state->get('export_release'),
      empty($form_state->getValue('download_type')),
      empty($form_state->getValue('compact')),
      FALSE,
      !empty($form_state->getValue('suggestions'))
    );

    if (isset($export_result) && is_array($export_result)) {
      // If we got an array back from the export build, tear that into pieces.
      [$mime_type, $file_name, $serve_name, $sid_count] = $export_result;
      header('Content-Disposition: attachment; filename=' . $serve_name);
      header('Content-Type: ' . $mime_type);
      echo file_get_contents($file_name);
      unlink($file_name);
      die();
    }
  }

  /**
   * Ajax entry point to get the releases of the selected project.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form_state object.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function ajaxReleases(array $form, FormStateInterface $form_state) {
    return $form['release'];
  }

  /**
   * Helper to extract project ID.
   *
   * Extract project ID from Request or Autocomplete response if available.
   * Careful, since our form is AJAXified, the buildForm() method can be
   * executed several times and Request attributes availability might change
   * between calls.
   *
   * @return string|null
   *   The project ID.
   */
  protected function computeProjectId(): ?string {
    $project_id = NULL;
    if (!empty($_REQUEST['project']) && preg_match('`^[\d]+$`', $_REQUEST['project'])) {
      $project_id = $_REQUEST['project'];
    }
    elseif (!empty($_REQUEST['project'])) {
      $project_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($_REQUEST['project']);
    }
    return $project_id;
  }

  /**
   * Helper to extract release ID.
   *
   * @return string|null
   *   The release ID.
   */
  protected function computeReleaseId(): ?string {
    $release_id = NULL;
    if (!empty($_REQUEST['release']) && preg_match('`^[\d]+$`', $_REQUEST['release'])) {
      $release_id = $_REQUEST['release'];
    }
    return $release_id;
  }

}

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

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