View source
<?php
namespace Drupal\tmgmt_smartling\Plugin\tmgmt\Translator;
use Drupal;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\file\FileUsage\DatabaseFileUsageBackend;
use Drupal\tmgmt\Translator\TranslatableResult;
use Drupal\tmgmt\TranslatorPluginBase;
use Drupal\tmgmt\TranslatorInterface;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt_extension_suit\ExtendedTranslatorPluginInterface;
use Drupal\tmgmt_file\Format\FormatManager;
use GuzzleHttp\ClientInterface;
use Smartling\File\Params\UploadFileParameters;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\tmgmt\Translator\AvailableResult;
use Drupal\tmgmt\ContinuousTranslatorInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\tmgmt_smartling\Event\RequestTranslationEvent;
class SmartlingTranslator extends TranslatorPluginBase implements ExtendedTranslatorPluginInterface, ContainerFactoryPluginInterface, ContinuousTranslatorInterface {
protected $smartlingApi;
protected $client;
protected $formatPluginsManager;
protected $fileUsage;
protected $eventDispatcher;
public function __construct(ClientInterface $client, FormatManager $format_plugin_manager, DatabaseFileUsageBackend $file_usage, EventDispatcherInterface $event_dispatcher, array $configuration, $plugin_id, array $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->client = $client;
$this->formatPluginsManager = $format_plugin_manager;
$this->fileUsage = $file_usage;
$this->eventDispatcher = $event_dispatcher;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($container
->get('http_client'), $container
->get('plugin.manager.tmgmt_file.format'), $container
->get('file.usage'), $container
->get('event_dispatcher'), $configuration, $plugin_id, $plugin_definition);
}
public function checkAvailable(TranslatorInterface $translator) {
if ($translator
->getSetting('user_id') && $translator
->getSetting('token_secret') && $translator
->getSetting('project_id')) {
return AvailableResult::yes();
}
return AvailableResult::no(t('@translator is not available. Make sure it is properly <a href=:configured>configured</a>.', [
'@translator' => $translator
->label(),
':configured' => $translator
->url(),
]));
}
public function checkTranslatable(TranslatorInterface $translator, JobInterface $job) {
return TranslatableResult::yes();
}
private function getCallbackUrl(JobInterface $job) {
$callback_url = Url::fromRoute('tmgmt_smartling.push_callback', [
'job' => $job
->id(),
])
->setOptions([
'absolute' => TRUE,
])
->toString();
$relative_callback_url = Url::fromRoute('tmgmt_smartling.push_callback', [
'job' => $job
->id(),
])
->toString();
$callback_url_host = rtrim($job
->getTranslator()
->getSetting('callback_url_host'), '/');
if (!empty($callback_url_host)) {
$callback_url = Url::fromUserInput($relative_callback_url, [
'base_url' => $callback_url_host,
])
->toString();
}
return $callback_url;
}
public function requestTranslation(JobInterface $job) {
$name = $this
->getFileName($job);
$export_format = pathinfo($name, PATHINFO_EXTENSION);
$export = $this->formatPluginsManager
->createInstance($export_format);
$path = $job
->getSetting('scheme') . '://tmgmt_sources/' . $name;
$dirname = dirname($path);
if (file_prepare_directory($dirname, FILE_CREATE_DIRECTORY)) {
$data = $export
->export($job);
$file = file_save_data($data, $path, FILE_EXISTS_REPLACE);
$this->fileUsage
->add($file, 'tmgmt_smartling', 'tmgmt_job', $job
->id());
$job
->submitted('Exported file can be downloaded <a href="@link">here</a>.', array(
'@link' => file_create_url($path),
));
}
else {
$e = new \Exception('It is not possible to create a directory ' . $dirname);
watchdog_exception('tmgmt_smartling', $e);
$job
->rejected('Job has been rejected with following error: @error', [
'@error' => $e
->getMessage(),
], 'error');
}
try {
$api = $this
->getApi($job
->getTranslator());
$upload_params = new UploadFileParameters();
$upload_params
->setAuthorized(0);
$upload_params
->set('smartling.placeholder_format_custom', $job
->getSetting('custom_regexp_placeholder'));
if ($job
->getSetting('auto_authorize_locales')) {
$upload_params
->setLocalesToApprove($job
->getRemoteTargetLanguage());
}
if ($job
->getTranslator()
->getSetting('callback_url_use')) {
$upload_params
->set('callbackUrl', $this
->getCallbackUrl($job));
}
$api
->uploadFile(\Drupal::service('file_system')
->realpath($file
->getFileUri()), $file
->getFilename(), $export_format === 'xlf' ? 'xliff' : $export_format, $upload_params);
$this->eventDispatcher
->dispatch(RequestTranslationEvent::REQUEST_TRANSLATION_EVENT, new RequestTranslationEvent($job));
Drupal::logger('tmgmt_smartling')
->info(t('File uploaded. Job id: @job_id, file name: @name.', [
'@name' => $job
->getTranslatorPlugin()
->getFileName($job),
'@job_id' => $job
->id(),
]));
} catch (\Exception $e) {
watchdog_exception('tmgmt_smartling', $e);
$job
->rejected('Job has been rejected with following error: @error uploading @file', array(
'@error' => $e
->getMessage(),
'@file' => $file
->getFileUri(),
), 'error');
}
}
public function getSupportedRemoteLanguages(TranslatorInterface $translator) {
$languages = [];
if (!$translator
->getSetting('project_id')) {
return $languages;
}
try {
$smartling_project_details = $this
->getApi($translator, 'project')
->getProjectDetails();
foreach ($smartling_project_details['targetLocales'] as $language) {
$languages[$language['localeId']] = $language['localeId'];
}
} catch (\Exception $e) {
Drupal::logger('tmgmt_smartling')
->error('Can not get languages from the translator: @message', [
'@message' => $e
->getMessage(),
]);
return $languages;
}
return $languages;
}
public function getDefaultRemoteLanguagesMappings() {
return array(
'zh-hans' => 'zh-CH',
'nl' => 'nl-NL',
'en' => 'en-EN',
);
}
public function getSupportedTargetLanguages(TranslatorInterface $translator, $source_language) {
$remote_languages = $this
->getSupportedRemoteLanguages($translator);
unset($remote_languages[$source_language]);
return $remote_languages;
}
public function hasCheckoutSettings(JobInterface $job) {
return FALSE;
}
public function getApi(TranslatorInterface $translator, $api_type = 'file') {
$api_factory = Drupal::service('tmgmt_smartling.smartling_api_factory');
return $api_factory::create($translator
->getSettings(), $api_type);
}
public function getFileName(JobInterface $job) {
try {
$filename = $job
->get('job_file_name');
$filename = !empty($filename
->getValue()) ? $filename
->getValue()[0]['value'] : '';
} catch (\Exception $e) {
$filename = '';
}
if (empty($filename)) {
$extension = $job
->getSetting('export_format');
$name = "JobID" . $job
->id() . '_' . $job
->getSourceLangcode() . '_' . $job
->getTargetLangcode();
$cloned_job = clone $job;
\Drupal::moduleHandler()
->alter('tmgmt_smartling_filename', $name, $cloned_job);
$filename = $name . '.' . $extension;
}
return $filename;
}
private function cleanFileName($filename, $allow_dirs = FALSE) {
$trim_filename = trim($filename);
if (empty($trim_filename)) {
return '';
}
$pattern = '/[^a-zA-Z0-9_\\-\\:]/i';
$info = pathinfo(trim($filename));
$filename = preg_replace($pattern, '_', $info['filename']);
if (isset($info['extension']) && !empty($info['extension'])) {
$filename .= '.' . preg_replace($pattern, '_', $info['extension']);
}
if ($allow_dirs && isset($info['dirname']) && !empty($info['dirname'])) {
$filename = preg_replace('/[^a-zA-Z0-9_\\/\\-\\:]/i', '_', $info['dirname']) . '/' . $filename;
}
return (string) $filename;
}
public function defaultSettings() {
return array(
'export_format' => 'xml',
'allow_override' => TRUE,
'scheme' => 'public',
'retrieval_type' => 'published',
'callback_url_use' => FALSE,
'callback_url_host' => '',
'auto_authorize_locales' => TRUE,
'xliff_processing' => TRUE,
'context_silent_user_switching' => FALSE,
'custom_regexp_placeholder' => '(@|%|!)[\\w-]+',
'context_skip_host_verifying' => FALSE,
'identical_file_name' => FALSE,
'enable_smartling_logging' => TRUE,
'enable_basic_auth' => FALSE,
'basic_auth' => [
'login' => '',
'password' => '',
],
);
}
public function requestJobItemsTranslation(array $job_items) {
$job = reset($job_items)
->getJob();
foreach ($job_items as $job_item) {
$this
->requestTranslation($job_item
->getJob());
if ($job
->isContinuous()) {
$job_item
->active();
}
}
}
public function downloadTranslation(JobInterface $job) {
return tmgmt_smartling_download_file($job);
}
public function isReadyForDownload(JobInterface $job) {
$result = FALSE;
try {
$api = $this
->getApi($job
->getTranslator());
$filename = $this
->getFileName($job);
$locale = $job
->getRemoteTargetLanguage();
$request_result = $api
->getStatus($filename, $locale);
$authorized = intval($request_result['authorizedStringCount']);
$completed = intval($request_result['completedStringCount']);
$progress = $authorized + $completed > 0 ? (int) ($completed / ($authorized + $completed) * 100) : 0;
$file_last_modified = 0;
$job_last_modified = intval($job
->getChangedTime());
$last_modified = $api
->lastModified($filename);
foreach ($last_modified['items'] as $item) {
if ($item['localeId'] == $locale) {
$file_last_modified = $item['lastModified']
->getTimeStamp();
break;
}
}
if ($file_last_modified >= $job_last_modified && $progress === 100) {
if ($file_last_modified > $job_last_modified) {
$job
->set('changed', $file_last_modified);
}
else {
$job
->set('changed', time());
}
$job
->save();
$result = TRUE;
}
else {
Drupal::logger('tmgmt_smartling')
->warning(t('File @file is not ready for download.', [
'@file' => $this
->getFileName($job),
]));
}
} catch (\Exception $e) {
watchdog_exception('tmgmt_smartling', $e);
}
\Drupal::logger('tmgmt_smartling')
->info('Check status for file: @filename found @approved pending words and @completed completed ones', [
'@filename' => $filename,
'@approved' => @$request_result['authorizedStringCount'],
'@completed' => @$request_result['completedStringCount'],
]);
return $result;
}
public function cancelTranslation(JobInterface $job) {
}
}