| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 | <?php/** * @file * Batch process to check the availability of remote or local po files. */use Drupal\Core\File\FileSystemInterface;use Drupal\Core\Url;use GuzzleHttp\Exception\RequestException;use Psr\Http\Message\RequestInterface;use Psr\Http\Message\ResponseInterface;use Psr\Http\Message\UriInterface;/** * Load the common translation API. */// @todo Combine functions differently in files to avoid unnecessary includes.// Follow-up issue: https://www.drupal.org/node/1834298.require_once __DIR__ . '/locale.translation.inc';/** * Implements callback_batch_operation(). * * Checks the presence and creation time po translation files in located at * remote server location and local file system. * * @param string $project *   Machine name of the project for which to check the translation status. * @param string $langcode *   Language code of the language for which to check the translation. * @param array $options *   An array with options that can have the following elements: *   - 'finish_feedback': Whether or not to give feedback to the user when the *     batch is finished. Optional, defaults to TRUE. *   - 'use_remote': Whether or not to check the remote translation file. *     Optional, defaults to TRUE. * @param array|\ArrayAccess $context *   The batch context. */function locale_translation_batch_status_check($project, $langcode, array $options, &$context) {  $failure = $checked = FALSE;  $options += [    'finish_feedback' => TRUE,    'use_remote' => TRUE,  ];  $source = locale_translation_get_status([$project], [$langcode]);  $source = $source[$project][$langcode];  // Check the status of local translation files.  if (isset($source->files[LOCALE_TRANSLATION_LOCAL])) {    if ($file = locale_translation_source_check_file($source)) {      locale_translation_status_save($source->name, $source->langcode, LOCALE_TRANSLATION_LOCAL, $file);    }    $checked = TRUE;  }  // Check the status of remote translation files.  if ($options['use_remote'] && isset($source->files[LOCALE_TRANSLATION_REMOTE])) {    $remote_file = $source->files[LOCALE_TRANSLATION_REMOTE];    if ($result = locale_translation_http_check($remote_file->uri)) {      // Update the file object with the result data. In case of a redirect we      // store the resulting uri.      if (isset($result['last_modified'])) {        $remote_file->uri = isset($result['location']) ? $result['location'] : $remote_file->uri;        $remote_file->timestamp = $result['last_modified'];        locale_translation_status_save($source->name, $source->langcode, LOCALE_TRANSLATION_REMOTE, $remote_file);      }      // @todo What to do with when the file is not found (404)? To prevent      //   re-checking within the TTL (1day, 1week) we can set a last_checked      //   timestamp or cache the result.      $checked = TRUE;    }    else {      $failure = TRUE;    }  }  // Provide user feedback and record success or failure for reporting at the  // end of the batch.  if ($options['finish_feedback'] && $checked) {    $context['results']['files'][] = $source->name;  }  if ($failure && !$checked) {    $context['results']['failed_files'][] = $source->name;  }  $context['message'] = t('Checked %langcode translation for %project.', ['%langcode' => $langcode, '%project' => $source->project]);}/** * Implements callback_batch_finished(). * * Set result message. * * @param bool $success *   TRUE if batch successfully completed. * @param array $results *   Batch results. */function locale_translation_batch_status_finished($success, $results) {  if ($success) {    if (isset($results['failed_files'])) {      if (\Drupal::moduleHandler()->moduleExists('dblog') && \Drupal::currentUser()->hasPermission('access site reports')) {        $message = \Drupal::translation()->formatPlural(count($results['failed_files']), 'One translation file could not be checked. <a href=":url">See the log</a> for details.', '@count translation files could not be checked. <a href=":url">See the log</a> for details.', [':url' => Url::fromRoute('dblog.overview')->toString()]);      }      else {        $message = \Drupal::translation()->formatPlural(count($results['failed_files']), 'One translation files could not be checked. See the log for details.', '@count translation files could not be checked. See the log for details.');      }      \Drupal::messenger()->addError($message);    }    if (isset($results['files'])) {      \Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural(        count($results['files']),        'Checked available interface translation updates for one project.',        'Checked available interface translation updates for @count projects.'      ));    }    if (!isset($results['failed_files']) && !isset($results['files'])) {      \Drupal::messenger()->addStatus(t('Nothing to check.'));    }    \Drupal::state()->set('locale.translation_last_checked', REQUEST_TIME);  }  else {    \Drupal::messenger()->addError(t('An error occurred trying to check available interface translation updates.'));  }}/** * Implements callback_batch_operation(). * * Downloads a remote gettext file into the translations directory. When * successfully the translation status is updated. * * @param object $project *   Source object of the translatable project. * @param string $langcode *   Language code. * @param array $context *   The batch context. * * @see locale_translation_batch_fetch_import() */function locale_translation_batch_fetch_download($project, $langcode, &$context) {  $sources = locale_translation_get_status([$project], [$langcode]);  if (isset($sources[$project][$langcode])) {    $source = $sources[$project][$langcode];    if (isset($source->type) && $source->type == LOCALE_TRANSLATION_REMOTE) {      if ($file = locale_translation_download_source($source->files[LOCALE_TRANSLATION_REMOTE], 'translations://')) {        $context['message'] = t('Downloaded %langcode translation for %project.', ['%langcode' => $langcode, '%project' => $source->project]);        locale_translation_status_save($source->name, $source->langcode, LOCALE_TRANSLATION_LOCAL, $file);      }      else {        $context['results']['failed_files'][] = $source->files[LOCALE_TRANSLATION_REMOTE];      }    }  }}/** * Implements callback_batch_operation(). * * Imports a gettext file from the translation directory. When successfully the * translation status is updated. * * @param object $project *   Source object of the translatable project. * @param string $langcode *   Language code. * @param array $options *   Array of import options. * @param array $context *   The batch context. * * @see locale_translate_batch_import_files() * @see locale_translation_batch_fetch_download() */function locale_translation_batch_fetch_import($project, $langcode, $options, &$context) {  $sources = locale_translation_get_status([$project], [$langcode]);  if (isset($sources[$project][$langcode])) {    $source = $sources[$project][$langcode];    if (isset($source->type)) {      if ($source->type == LOCALE_TRANSLATION_REMOTE || $source->type == LOCALE_TRANSLATION_LOCAL) {        $file = $source->files[LOCALE_TRANSLATION_LOCAL];        module_load_include('bulk.inc', 'locale');        $options += [          'message' => t('Importing %langcode translation for %project.', ['%langcode' => $langcode, '%project' => $source->project]),        ];        // Import the translation file. For large files the batch operations is        // progressive and will be called repeatedly until finished.        locale_translate_batch_import($file, $options, $context);        // The import is finished.        if (isset($context['finished']) && $context['finished'] == 1) {          // The import is successful.          if (isset($context['results']['files'][$file->uri])) {            $context['message'] = t('Imported %langcode translation for %project.', ['%langcode' => $langcode, '%project' => $source->project]);            // Save the data of imported source into the {locale_file} table and            // update the current translation status.            locale_translation_status_save($project, $langcode, LOCALE_TRANSLATION_CURRENT, $source->files[LOCALE_TRANSLATION_LOCAL]);          }        }      }    }  }}/** * Implements callback_batch_finished(). * * Set result message. * * @param bool $success *   TRUE if batch successfully completed. * @param array $results *   Batch results. */function locale_translation_batch_fetch_finished($success, $results) {  module_load_include('bulk.inc', 'locale');  if ($success) {    \Drupal::state()->set('locale.translation_last_checked', REQUEST_TIME);  }  return locale_translate_batch_finished($success, $results);}/** * Check if remote file exists and when it was last updated. * * @param string $uri *   URI of remote file. * * @return array|bool *   Associative array of file data with the following elements: *   - last_modified: Last modified timestamp of the translation file. *   - (optional) location: The location of the translation file. Is only set *     when a redirect (301) has occurred. *   TRUE if the file is not found. FALSE if a fault occurred. */function locale_translation_http_check($uri) {  $logger = \Drupal::logger('locale');  try {    $actual_uri = NULL;    $response = \Drupal::service('http_client_factory')->fromOptions([      'allow_redirects' => [        'on_redirect' => function (RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) {          $actual_uri = (string) $request_uri;        },      ],    ])->head($uri);    $result = [];    // Return the effective URL if it differs from the requested.    if ($actual_uri && $actual_uri !== $uri) {      $result['location'] = $actual_uri;    }    $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeaderLine('Last-Modified')) : 0;    return $result;  }  catch (RequestException $e) {    // Handle 4xx and 5xx http responses.    if ($response = $e->getResponse()) {      if ($response->getStatusCode() == 404) {        // File not found occurs when a translation file is not yet available        // at the translation server. But also if a custom module or custom        // theme does not define the location of a translation file. By default        // the file is checked at the translation server, but it will not be        // found there.        $logger->notice('Translation file not found: @uri.', ['@uri' => $uri]);        return TRUE;      }      $logger->notice('HTTP request to @url failed with error: @error.', ['@url' => $uri, '@error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()]);    }  }  return FALSE;}/** * Downloads a translation file from a remote server. * * @param object $source_file *   Source file object with at least: *   - "uri": uri to download the file from. *   - "project": Project name. *   - "langcode": Translation language. *   - "version": Project version. *   - "filename": File name. * @param string $directory *   Directory where the downloaded file will be saved. Defaults to the *   temporary file path. * * @return object *   File object if download was successful. FALSE on failure. */function locale_translation_download_source($source_file, $directory = 'temporary://') {  if ($uri = system_retrieve_file($source_file->uri, $directory, FALSE, FileSystemInterface::EXISTS_REPLACE)) {    $file = clone($source_file);    $file->type = LOCALE_TRANSLATION_LOCAL;    $file->uri = $uri;    $file->directory = $directory;    $file->timestamp = filemtime($uri);    return $file;  }  \Drupal::logger('locale')->error('Unable to download translation file @uri.', ['@uri' => $source_file->uri]);  return FALSE;}
 |