tmgmt_smartling-8.x-4.11/src/Plugin/tmgmt/Translator/SmartlingTranslator.php
src/Plugin/tmgmt/Translator/SmartlingTranslator.php
<?php
/**
* @file
* Contains \Drupal\tmgmt_smartling\Plugin\tmgmt\Translator\SmartlingTranslator.
*/
namespace Drupal\tmgmt_smartling\Plugin\tmgmt\Translator;
use Drupal;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Url;
use Drupal\file\FileInterface;
use Drupal\file\FileUsage\DatabaseFileUsageBackend;
use Drupal\tmgmt\ContinuousTranslatorInterface;
use Drupal\tmgmt\Data;
use Drupal\tmgmt\Entity\JobItem;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\JobItemInterface;
use Drupal\tmgmt\Translator\AvailableResult;
use Drupal\tmgmt\Translator\TranslatableResult;
use Drupal\tmgmt\TranslatorInterface;
use Drupal\tmgmt\TranslatorPluginBase;
use Drupal\tmgmt_extension_suit\ExtendedTranslatorPluginInterface;
use Drupal\tmgmt_extension_suit\Utils\FlowScheduler;
use Drupal\tmgmt_file\Format\FormatManager;
use Drupal\tmgmt_smartling\Event\RequestTranslationEvent;
use Drupal\tmgmt_smartling\Smartling\ConnectorInfo;
use Drupal\tmgmt_smartling\Smartling\SmartlingApiWrapper;
use Drupal\tmgmt_smartling\Smartling\Submission\TranslationRequestManager;
use Exception;
use GuzzleHttp\ClientInterface;
use Psr\Log\LoggerInterface;
use Smartling\AuditLog\Params\CreateRecordParameters;
use Smartling\BaseApiAbstract;
use Smartling\File\Params\DownloadFileParameters;
use Smartling\File\Params\UploadFileParameters;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Smartling translator plugin.
*
* @TranslatorPlugin(
* id = "smartling",
* label = @Translation("Smartling translator"),
* description = @Translation("Smartling Translator service."),
* ui = "Drupal\tmgmt_smartling\SmartlingTranslatorUi",
* files = TRUE,
* )
*/
class SmartlingTranslator extends TranslatorPluginBase implements
ExtendedTranslatorPluginInterface,
ContainerFactoryPluginInterface,
ContinuousTranslatorInterface {
/**
* @var \GuzzleHttp\ClientInterface
*/
protected $client;
/**
* @var \Drupal\tmgmt_file\Format\FormatManager
*/
protected $formatPluginsManager;
/**
* @var \Drupal\file\FileUsage\DatabaseFileUsageBackend
*/
protected $fileUsage;
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var SmartlingApiWrapper
*/
private $smartlingApiWrapper;
/**
* @var TranslationRequestManager
*/
private $translationRequestManager;
/**
* @var ModuleHandlerInterface
*/
protected $moduleHandler;
protected $currentUser;
protected $fileUrlGenerator;
/**
* @var Data
*/
protected $dataService;
/**
* @var FlowScheduler
*/
protected $flowScheduler;
/**
* Constructs a LocalActionBase object.
*
* @param \GuzzleHttp\ClientInterface $client
* The Guzzle HTTP client.
* @param \Drupal\tmgmt_file\Format\FormatManager $format_plugin_manager
* @param \Drupal\file\FileUsage\DatabaseFileUsageBackend $file_usage
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Psr\Log\LoggerInterface $logger
* @param \Drupal\tmgmt_smartling\Smartling\SmartlingApiWrapper $api_wrapper
* @param \Drupal\tmgmt_smartling\Smartling\Submission\TranslationRequestManager $translation_request_manager
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
*/
public function __construct(
ClientInterface $client,
FormatManager $format_plugin_manager,
DatabaseFileUsageBackend $file_usage,
EventDispatcherInterface $event_dispatcher,
array $configuration,
$plugin_id,
array $plugin_definition,
LoggerInterface $logger,
SmartlingApiWrapper $api_wrapper,
TranslationRequestManager $translation_request_manager,
ModuleHandlerInterface $module_handler,
AccountProxyInterface $current_user,
FileUrlGeneratorInterface $file_url_generator,
Data $data_service,
FlowScheduler $flow_scheduler
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->client = $client;
$this->formatPluginsManager = $format_plugin_manager;
$this->fileUsage = $file_usage;
$this->eventDispatcher = $event_dispatcher;
$this->logger = $logger;
$this->smartlingApiWrapper = $api_wrapper;
$this->translationRequestManager = $translation_request_manager;
$this->moduleHandler = $module_handler;
$this->currentUser = $current_user;
$this->fileUrlGenerator = $file_url_generator;
$this->dataService = $data_service;
$this->flowScheduler = $flow_scheduler;
}
/**
* {@inheritdoc}
*/
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,
$container->get('logger.channel.smartling'),
$container->get('tmgmt_smartling.smartling_api_wrapper'),
$container->get('tmgmt_smartling.translation_request_manager'),
$container->get('module_handler'),
$container->get('current_user'),
$container->get('file_url_generator'),
$container->get('tmgmt.data'),
$container->get('tmgmt_extension_suit.utils.flow_scheduler')
);
}
/**
* {@inheritdoc}
*/
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->toUrl()->toString(),
]));
}
/**
* {@inheritdoc}
*/
public function checkTranslatable(TranslatorInterface $translator, JobInterface $job) {
// Anything can be exported.
return TranslatableResult::yes();
}
/**
* Returns callback url.
*
* Host value can be overridden by value defined in translator settings.
*
* @param JobInterface $job
*
* @return Drupal\Core\GeneratedUrl|string
*/
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;
}
/**
* Returns callback url for attachment.
*
* Host value can be overridden by value defined in translator settings.
*
* @param JobInterface $job
* @param FileInterface $file
*
* @return Drupal\Core\GeneratedUrl|string
*/
private function getCallbackAttachmentUrl(JobInterface $job, FileInterface $file) {
$params = [
'job' => $job->id(),
'file' => $file->id(),
];
$callback_url = Url::fromRoute('tmgmt_smartling.push_callback_attachment', $params)->setOptions(['absolute' => TRUE])->toString();
$relative_callback_url = Url::fromRoute('tmgmt_smartling.push_callback_attachment', $params)->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;
}
/**
* @param array $settings
* @return \Drupal\tmgmt_smartling\Smartling\SmartlingApiWrapper
*/
public function getApiWrapper(array $settings) {
ConnectorInfo::setUpCurrentClientInfo();
$this->smartlingApiWrapper->setSettings($settings);
return $this->smartlingApiWrapper;
}
/**
* {@inheritdoc}
*/
public function requestTranslation(JobInterface $job) {
$batch_uid = $job->getSetting('batch_uid');
$api_wrapper = $this->getApiWrapper($job->getTranslator()->getSettings());
$error_notification_message = t('File @name (job id = @job_id) wasn\'t uploaded. Please see logs for more info.', [
'@name' => $this->getFileName($job),
'@job_id' => $job->id(),
])->render();
$api_wrapper->createAuditLogRecord(
$job,
NULL,
$this->currentUser,
CreateRecordParameters::ACTION_TYPE_UPLOAD
);
// Skip processing if job/batch hasn't been created.
if (empty($batch_uid)) {
$this->logger->error(t('File @name (job id = @job_id) wasn\'t uploaded due to previous error(s).', [
'@name' => $this->getFileName($job),
'@job_id' => $job->id(),
])->render());
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => $error_notification_message,
"type" => "error",
]);
return;
}
$translation_request = $this->translationRequestManager->upsertTranslationRequest($job);
if (empty($translation_request)) {
$this->logger->error('Can\'t upsert translation request for file @name (job id = @job_id).', [
'@name' => $this->getFileName($job),
'@job_id' => $job->id(),
]);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => 'Can\'t upsert translation request. ' . $error_notification_message,
"type" => "error",
]);
return;
}
$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 (\Drupal::service('file_system')->prepareDirectory($dirname, FileSystemInterface::CREATE_DIRECTORY)) {
$data = $export->export($job);
$file = \Drupal::service('file.repository')->writeData($data, $path, FileSystemInterface::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' => $this->fileUrlGenerator->generateAbsoluteString($path)));
}
else {
$e = new \Exception('It is not possible to create a directory ' . $dirname);
if (class_exists(\Drupal\Component\Utility\DeprecationHelper::class)) {
\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('tmgmt_smartling'), $e), fn() => watchdog_exception('tmgmt_smartling', $e));
} else {
watchdog_exception('tmgmt_smartling', $e);
}
$job->rejected('Job has been rejected with following error: @error',
['@error' => $e->getMessage()], 'error');
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => $error_notification_message,
"type" => "error",
]);
$this->translationRequestManager->commitError($job, $translation_request, $e);
return;
}
try {
$upload_params = new UploadFileParameters();
$upload_params->setClientLibId(BaseApiAbstract::getCurrentClientId(), BaseApiAbstract::getCurrentClientVersion());
$upload_params->setAuthorized(0);
if ($job->getTranslator()->getSetting('callback_url_use')) {
$upload_params->set('callbackUrl', $this->getCallbackUrl($job));
}
$file_type = $export_format === 'xlf' ? 'xliff' : $export_format;
$upload_params->setLocalesToApprove($job->getRemoteTargetLanguage());
$api_wrapper->getApi('batch')->uploadBatchFile(
$file->getFileUri(),
$file->getFilename(),
$file_type,
$batch_uid,
$this->addSmartlingDirectives($upload_params, $job)
);
$message = t('File uploaded. Job id: @job_id, file name: @name.', [
'@name' => $this->getFileName($job),
'@job_id' => $job->id(),
]);
$this->logger->info($message);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => $message->render(),
"type" => "status",
]);
// Upload attachments through the same batch.
// TODO: maybe upload through the queue?
if (method_exists($this->dataService, 'getTranslatableFiles')) {
foreach ($job->getItems() as $item) {
foreach ($this->dataService->getTranslatableFiles($item->getData()) as $file) {
$this->requestTranslationAttachment($job, $file);
}
}
}
if ($job->id() == $job->getSetting('batch_execute_on_job')) {
$api_wrapper->executeBatch($batch_uid);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t("Finished: content is in the job. You may need to wait a few seconds before content is authorized (if you checked 'authorize' checkbox).")
->render(),
"type" => "status",
]);
}
$this->eventDispatcher->dispatch(new RequestTranslationEvent($job), RequestTranslationEvent::REQUEST_TRANSLATION_EVENT);
if (!$this->translationRequestManager->commitSuccessfulUpload($job, $translation_request)) {
$warning_message = 'Can\'t update submitted date for translation request = @translation_request.';
$warning_message_context = [
'@translation_request' => json_encode($translation_request),
];
$this->logger->warning($warning_message, $warning_message_context);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => 'Can\'t update submitted date for translation request. See logs for more info.',
"type" => "warning",
]);
}
}
catch (Exception $e) {
if (class_exists(\Drupal\Component\Utility\DeprecationHelper::class)) {
\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('tmgmt_smartling'), $e), fn() => watchdog_exception('tmgmt_smartling', $e));
} else {
watchdog_exception('tmgmt_smartling', $e);
}
$job->rejected('Job has been rejected with following error: @error uploading @file', [
'@error' => $e->getMessage(),
'@file' => $file->getFileUri()
], 'error');
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t('Error while uploading @file. Please see logs for more info.', [
'@file' => $file->getFileUri()
])->render(),
"type" => "error",
]);
$this->translationRequestManager->commitError($job, $translation_request, $e);
}
// @todo disallow to submit translation to unsupported language.
}
/**
* {@inheritdoc}
*/
public function requestTranslationAttachment(JobInterface $job, FileInterface $file) {
$api_wrapper = $this->getApiWrapper($job->getTranslator()->getSettings());
try {
$translation_request = $this->translationRequestManager->upsertTranslationRequest($job, $file);
$attachment_file_name = $this->getAttachmentFileName($job, $file);
try {
$upload_params = new UploadFileParameters();
$upload_params->setClientLibId(BaseApiAbstract::getCurrentClientId(), BaseApiAbstract::getCurrentClientVersion());
$upload_params->setAuthorized(0);
if ($job->getTranslator()->getSetting('callback_url_use')) {
$upload_params->set('callbackUrl', $this->getCallbackAttachmentUrl($job, $file));
}
$upload_params->setLocalesToApprove($job->getRemoteTargetLanguage());
$this->smartlingApiWrapper->getApi('batch')->uploadBatchFile(
$file->getFileUri(),
$attachment_file_name,
$this->mapFileExtensionToFileType(
pathinfo($file->getFileUri(), PATHINFO_EXTENSION)
),
$job->getSetting('batch_uid'),
$upload_params,
);
$message = t('Attachment file uploaded. Job id: @job_id, file name: @name.', [
'@name' => $attachment_file_name,
'@job_id' => $job->id(),
]);
$job->addMessage($message);
$this->logger->info($message);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => $message->render(),
"type" => "status",
]);
$this->translationRequestManager->commitSuccessfulUpload($job, $translation_request);
} catch (Exception $e) {
if (class_exists(\Drupal\Component\Utility\DeprecationHelper::class)) {
\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('tmgmt_smartling'), $e), fn() => watchdog_exception('tmgmt_smartling', $e));
} else {
watchdog_exception('tmgmt_smartling', $e);
}
$job->addMessage('Error "@error" while uploading attachment @file', [
'@error' => $e->getMessage(),
'@file' => $attachment_file_name
], 'error');
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t('Error while uploading file attachment @file. Please see logs for more info.', [
'@file' => $attachment_file_name
])->render(),
"type" => "error",
]);
$this->translationRequestManager->commitError($job, $translation_request, $e);
}
} catch (Exception $e) {
if (class_exists(\Drupal\Component\Utility\DeprecationHelper::class)) {
\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('tmgmt_smartling'), $e), fn() => watchdog_exception('tmgmt_smartling', $e));
} else {
watchdog_exception('tmgmt_smartling', $e);
}
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t('Error while upserting translation request. Please see logs for more info.')->render(),
"type" => "error",
]);
}
}
/**
* @param string $fileExtension
*
* @return string
*/
protected function mapFileExtensionToFileType($file_extension) {
$mapping = [
'po' => 'gettext',
'pot' => 'gettext',
'properties' => 'java_properties',
'txt' => 'plain_text',
];
return empty($mapping[$file_extension]) ? $file_extension : $mapping[$file_extension];
}
/**
* Adds smartling directives to upload parameters.
*
* Array of directives can be altered by `tmgmt_smartling_directives_alter`
* hook.
*
* @param \Smartling\File\Params\UploadFileParameters $params
* @param \Drupal\tmgmt\JobInterface $job
*
* @return \Smartling\File\Params\UploadFileParameters
*/
protected function addSmartlingDirectives(UploadFileParameters $params, JobInterface $job) {
$directives = [
'smartling.translate_paths' => 'html/body/div/div, html/body/div/span',
'smartling.string_format_paths' => 'html : html/body/div/div, @default : html/body/div/span',
'smartling.variants_enabled' => 'true',
'smartling.source_key_paths' => 'html/body/div/{div.sl-variant}, html/body/div/{span.sl-variant}',
'smartling.character_limit_paths' => 'html/body/div/limit',
'smartling.placeholder_format_custom' => $job->getSetting('custom_regexp_placeholder'),
'smartling.include_translatable_attributes' => $job->getSetting('translatable_attributes'),
'smartling.exclude_translatable_attributes' => $job->getSetting('exclude_translatable_attributes'),
'smartling.force_block_for_tags' => $job->getSetting('force_block_for_tags')
];
$this->moduleHandler->alter('tmgmt_smartling_directives', $directives);
if (is_array($directives)) {
$directives = $this->filterDirectives($directives);
foreach ($directives as $directive_name => $directive_value) {
$params->set($directive_name, $directive_value);
}
}
return $params;
}
/**
* @param array $directives
* @return array
*/
protected function filterDirectives(array $directives) {
$allowed_directives_for_xml_file = [
'smartling.entity_escaping',
'smartling.variants_enabled',
'smartling.translate_paths',
'smartling.string_format_paths',
'smartling.placeholder_format_custom',
'smartling.placeholder_format',
'smartling.sltrans',
'smartling.source_key_paths',
'smartling.pseudo_inflation',
'smartling.instruction_paths',
'smartling.character_limit_paths',
'smartling.force_inline_for_tags',
'smartling.include_translatable_attributes',
'smartling.exclude_translatable_attributes',
'smartling.force_block_for_tags'
];
$result = [];
foreach ($directives as $directive_name => $directive_value) {
if (in_array($directive_name, $allowed_directives_for_xml_file)) {
$result[$directive_name] = $directive_value;
}
}
return $result;
}
/**
* {@inheritdoc}
*/
public function getSupportedRemoteLanguages(TranslatorInterface $translator) {
if (\Drupal::isConfigSyncing()) {
return [];
}
$languages = [];
// Prevent access if the translator isn't configured yet.
if (!$translator->getSetting('project_id')) {
// @todo should be implemented by an Exception.
return $languages;
}
try {
$smartling_project_details = $this->getApiWrapper($translator->getSettings())->getApi('project')->getProjectDetails();
foreach ($smartling_project_details['targetLocales'] as $language) {
$languages[$language['localeId']] = $language['localeId'];
}
}
catch (\Exception $e) {
$this->logger->error('Can not get languages from the translator: @message', [
'@message' => $e->getMessage(),
]);
return $languages;
}
return $languages;
}
/**
* {@inheritdoc}
*/
public function getDefaultRemoteLanguagesMappings() {
return array(
'zh-hans' => 'zh-CN',
'nl' => 'nl-NL',
'en' => 'en-EN'
);
}
/**
* {@inheritdoc}
*/
public function getSupportedTargetLanguages(TranslatorInterface $translator, $source_language) {
$remote_languages = $this->getSupportedRemoteLanguages($translator);
unset($remote_languages[$source_language]);
return $remote_languages;
}
/**
* {@inheritdoc}
*/
public function hasCheckoutSettings(JobInterface $job) {
return TRUE;
}
/**
* Returns file name.
*
* @param \Drupal\tmgmt\JobInterface $job
* @return string
*/
public function getFileName(JobInterface $job) {
// TODO: identical filename task.
// $extension = $job->getSetting('export_format');
//
// try {
// // Try to load existing file name from tmgmt_job table.
// $filename = $job->get('job_file_name');
//
// if (!empty($filename->getValue())) {
// $filename = $filename->getValue()[0]['value'];
// }
// // Job item title should be included into a filename only if there is a
// // single JobItem in a Job. If there are 3 JobItems in a file - file name
// // should be "@entity_type_@entity_id>". And finally for every Job with
// // more than 3 JobItems - standard "JobId@id"
// elseif ($extension == 'xml') {
// $file_names = [];
// $job_items = $job->getItems();
// $job_items_count = count($job_items);
//
// if ($job_items_count == 1) {
// $file_name_type = 'expanded';
// }
// else if ($job_items_count > 1 && $job_items_count <= 3) {
// $file_name_type = 'simplified';
// }
// else {
// $file_name_type = 'default';
// }
//
// foreach ($job_items as $job_item) {
// $job_item_id = $job_item->getItemId();
// $job_item_type = $job_item->getItemType();
//
// switch ($file_name_type) {
// case 'expanded':
// $temp_name = $job_item->getSourceLabel() . '_' . $job_item_type . '_' . $job_item_id;
//
// break;
//
// case 'simplified':
// $temp_name = $job_item_type . '_' . $job_item_id;
//
// break;
//
// default:
// $file_names[$job_item_id] = 'JobID' . $job->id() . '_' . $job->getSourceLangcode() . '_' . $job->getTargetLangcode();
//
// break 2;
// }
//
// $file_names[$job_item_id] = $temp_name;
// }
//
// ksort($file_names);
// $filename = $this->cleanFileName(implode('_', $file_names) . '.' . $extension);
// }
// else {
// $filename = '';
// }
// } catch (\Exception $e) {
// $filename = '';
// }
//
// // Fallback to default file name.
// if (empty($filename) || !$job->getSetting('identical_file_name')) {
// $filename = "JobID" . $job->id() . '_' . $job->getSourceLangcode() . '_' . $job->getTargetLangcode() . '.' . $extension;
// }
//
// return $filename;
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');
// Continuous job doesn't return id for some reason but returns uuid.
// Let's use UUID instead.
$id = $job->isContinuous() ? $job->uuid() : $job->id();
$name = "JobID" . $id . '_' . $job->getSourceLangcode() . '_' . $job->getTargetLangcode();
// Alter name before saving it into database.
$cloned_job = clone $job;
\Drupal::moduleHandler()->alter('tmgmt_smartling_filename', $name, $cloned_job);
$filename = $name . '.' . $extension;
}
return $filename;
}
/**
* {@inheritdoc}
*/
public function getAttachmentFileName(JobInterface $job, FileInterface $file) {
// Continuous job doesn't return id for some reason but returns uuid.
// Let's use UUID instead.
$id = $job->isContinuous() ? $job->uuid() : $job->id();
$name = "JobID" . $id . '_' . $job->getSourceLangcode() . '_' . $job->getTargetLangcode();
$cloned_file = clone $file;
$attachmentFilename = pathinfo($file->getFilename(), PATHINFO_FILENAME);
$attachmentFileExtension = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
$composedAttachmentFilename = $name . '_' . $this->cleanFileName($attachmentFilename);
\Drupal::moduleHandler()->alter('tmgmt_smartling_attachment_filename', $composedAttachmentFilename, $cloned_job, $cloned_file);
$filename = mb_substr(
$composedAttachmentFilename,
0,
255 - mb_strlen($attachmentFileExtension) - 1
) . '.' . $attachmentFileExtension;
return $filename;
}
/**
* Return clean filename, sanitized for path traversal vulnerability.
*
* Url (https://code.google.com/p/teenage-mutant-ninja-turtles
* /wiki/AdvancedObfuscationPathtraversal).
*
* @param string $filename
* File name.
*
* @return string
* Return clean filename.
*/
private function cleanFileName($filename) {
$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']);
}
return (string) $filename;
}
/**
* {@inheritdoc}
*/
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,
'custom_regexp_placeholder' => '(@|%|!)[\w-]+',
'translatable_attributes' => 'title, alt',
'exclude_translatable_attributes' => '',
'force_block_for_tags' => '',
'context_skip_host_verifying' => FALSE,
'context_url_host' => '',
'identical_file_name' => FALSE,
'enable_smartling_logging' => TRUE,
'enable_notifications' => TRUE,
'async_mode' => FALSE,
'enable_basic_auth' => FALSE,
'download_by_job_items' => FALSE,
'basic_auth' => [
'login' => '',
'password' => '',
],
'exclude_context_options' => [],
);
}
/**
* {@inheritdoc}
*/
public function requestJobItemsTranslation(array $job_items) {
/** @var \Drupal\tmgmt\Entity\Job $job */
$job = reset($job_items)->getJob();
$this->flowScheduler->scheduleUpload(
$job->id(),
$this->moduleHandler->invokeAll('tmgmt_extension_suit_updated_entity_jobs', [[$job->id()], $job->getTranslatorId()]),
TRUE
);
foreach ($job_items as $job_item) {
if ($job->isContinuous()) {
$job_item->active();
}
}
}
/**
* {@inheritdoc}
*/
public function isReadyForDownload(JobInterface $job) {
return $this->translationRequestManager->isTranslationRequestReadyForDownload($job);
}
/**
* {@inheritdoc}
*/
public function isAttachmentReadyForDownload(JobInterface $job, FileInterface $file) {
return $this->translationRequestManager->isTranslationRequestReadyForDownload($job, $file);
}
public function abortTranslation(JobInterface $job) {
$api_wrapper = $this->getApiWrapper($job->getTranslator()->getSettings());
$api_wrapper->createAuditLogRecord(
$job,
NULL,
$this->currentUser,
CreateRecordParameters::ACTION_TYPE_CANCEL
);
return parent::abortTranslation($job);
}
/**
* @inheritdoc
*/
public function downloadTranslation(JobInterface $job, ?JobItemInterface $jobItem = NULL) {
return tmgmt_smartling_download_file($job, $jobItem);
}
/**
* @inheritdoc
*/
public function downloadAttachmentTranslation(JobInterface $job, FileInterface $file) {
// TODO: maybe download through the queue?
if (method_exists($this->dataService, 'getTranslatableFiles') && method_exists($this->dataService, 'createFileTranslation')) {
foreach ($job->getItems() as $item) {
foreach ($this->dataService->getTranslatableFiles($item->getData()) as $key => $attachedFile) {
if ($attachedFile->id() === $file->id()) {
$api_wrapper = $this->getApiWrapper($job->getTranslator()->getSettings());
$translation_request = $this->translationRequestManager->getTranslationRequest($job, $file);
if (empty($translation_request)) {
$job->addMessage('File download failed for job = @job_id: can\'t find related translation request.', ['@job' => $job->id()], 'error');
$this->logger->error('Can\'t retrieve translation request for file @name (job id = @job_id).', [
'@name' => $this->getAttachmentFileName($job, $file),
'@job_id' => $job->id(),
]);
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t('File @name (job id = @job_id) wasn\'t downloaded: can\'t find related translation request. See logs for more info.', [
'@name' => $this->getAttachmentFileName($job, $file),
'@job_id' => $job->id(),
])->render(),
"type" => "error",
]);
return FALSE;
}
$api_wrapper->createAuditLogRecord(
$job,
NULL,
Drupal::currentUser(),
CreateRecordParameters::ACTION_TYPE_DOWNLOAD
);
try {
$smartling_api = $api_wrapper->getApi('file');
$retrieval_type = $job->getTranslator()->getSetting('retrieval_type');
$filename = $translation_request["fileUri"];
$download_parameters = new DownloadFileParameters();
$download_parameters->set('retrievalType', $retrieval_type);
$content = $smartling_api->downloadFile($filename, $job->getRemoteTargetLanguage(), $download_parameters);
$translation_file = $this->dataService->createFileTranslation($file, $job->getTargetLangcode(), $content);
$item->setState(JobItem::STATE_ACTIVE);
$item->addTranslatedData($this->dataService->unflatten([
$key => ['#file' => $translation_file->id()]
]));
$this->translationRequestManager->commitSuccessfulDownload($job, $translation_request);
}
catch (\Exception $e) {
$api_wrapper->createFirebaseRecord("tmgmt_smartling", "notifications", 10, [
"message" => t('File @name (job id = @job_id) wasn\'t downloaded. Please see logs for more info.', [
'@name' => $filename,
'@job_id' => $job->id(),
])->render(),
"type" => "error",
]);
$this->translationRequestManager->commitError($job, $translation_request, $e);
return FALSE;
}
return TRUE;
}
}
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function cancelTranslation(JobInterface $job) {
// TODO: Implement cancelTranslation() method.
}
/**
* Requests translation.
*
* @param \Drupal\tmgmt\JobInterface $job
* @param array $data
*
* @return mixed
*/
public function requestTranslationExtended(JobInterface $job, array $data) {
// Pass queue item data into job settings.
$settings_map_item = $job->settings->get(0);
if ($settings_map_item) {
$settings = $settings_map_item->getValue();
if (isset($data['batch_uid'])) {
$settings['batch_uid'] = $data['batch_uid'];
}
if ($data['batch_execute_on_job']) {
$settings['batch_execute_on_job'] = $data['batch_execute_on_job'];
}
$job->settings->set(0, $settings);
}
$this->requestTranslation($job);
}
}
