View source
<?php
namespace Drupal\lingotek;
use Drupal\Component\Serialization\Json;
use Drupal\lingotek\Exception\LingotekApiException;
use Drupal\lingotek\Exception\LingotekDocumentAlreadyCompletedException;
use Drupal\lingotek\Exception\LingotekDocumentArchivedException;
use Drupal\lingotek\Exception\LingotekDocumentLockedException;
use Drupal\lingotek\Exception\LingotekDocumentTargetAlreadyCompletedException;
use Drupal\lingotek\Exception\LingotekPaymentRequiredException;
use Drupal\lingotek\Remote\LingotekApiInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
class Lingotek implements LingotekInterface {
protected static $instance;
protected $api;
protected $configFactory;
protected $languageLocaleMapper;
protected $lingotekFilterManager;
protected $lingotekConfiguration;
const STATUS_EDITED = 'EDITED';
const STATUS_IMPORTING = 'IMPORTING';
const STATUS_NONE = 'NONE';
const STATUS_REQUEST = 'REQUEST';
const STATUS_PENDING = 'PENDING';
const STATUS_INTERMEDIATE = 'INTERMEDIATE';
const STATUS_CURRENT = 'CURRENT';
const STATUS_READY = 'READY';
const STATUS_DISABLED = 'DISABLED';
const STATUS_ERROR = 'ERROR';
const STATUS_CANCELLED = 'CANCELLED';
const STATUS_UNTRACKED = 'UNTRACKED';
const STATUS_DELETED = 'DELETED';
const STATUS_ARCHIVED = 'ARCHIVED';
const PROGRESS_COMPLETE = 100;
const PROFILE_AUTOMATIC = 'automatic';
const PROFILE_MANUAL = 'manual';
const PROFILE_DISABLED = 'disabled';
const SETTINGS = 'lingotek.settings';
public function __construct(LingotekApiInterface $api, LanguageLocaleMapperInterface $language_locale_mapper, ConfigFactoryInterface $config_factory, LingotekFilterManagerInterface $lingotek_filter_manager, LingotekConfigurationServiceInterface $lingotek_configuration = NULL) {
$this->api = $api;
$this->languageLocaleMapper = $language_locale_mapper;
$this->configFactory = $config_factory;
$this->lingotekFilterManager = $lingotek_filter_manager;
if (!$lingotek_configuration) {
@trigger_error('The lingotek.configuration service must be passed to Lingotek::__construct, it is included in lingotek:3.1.0 and required for lingotek:4.0.0.', E_USER_DEPRECATED);
$lingotek_configuration = \Drupal::service('lingotek.configuration');
}
$this->lingotekConfiguration = $lingotek_configuration;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('lingotek.api'), $container
->get('lingotek.language_locale_mapper'), $container
->get('config.factory'), $container
->get('lingotek.filter_manager'), $container
->get('lingotek.configuration'));
}
public function get($key) {
return $this->configFactory
->get(static::SETTINGS)
->get($key);
}
public function getEditable($key) {
return $this->configFactory
->getEditable(static::SETTINGS)
->get($key);
}
public function set($key, $value) {
$this->configFactory
->getEditable(static::SETTINGS)
->set($key, $value)
->save();
}
public function getAccountInfo() {
try {
$response = $this->api
->getAccountInfo();
} catch (LingotekApiException $e) {
return FALSE;
}
if ($response) {
return json_decode($response
->getBody(), TRUE);
}
return FALSE;
}
public function getResources($force = FALSE) {
return [
'community' => $this
->getCommunities($force),
'project' => $this
->getProjects($force),
'vault' => $this
->getVaults($force),
'workflow' => $this
->getWorkflows($force),
'filter' => $this
->getFilters($force),
];
}
public function getDefaults() {
return $this->configFactory
->get('lingotek.account')
->get('default');
}
public function getCommunities($force = FALSE) {
$resources_key = 'account.resources.community';
$data = $this->configFactory
->get('lingotek.account')
->get('resources.community');
if (empty($data) || $force) {
$data = $this->api
->getCommunities($force);
$this->configFactory
->getEditable('lingotek.account')
->set('resources.community', $data)
->save();
}
return $data;
}
public function getVaults($force = FALSE) {
return $this
->getResource('resources.vault', 'getVaults', $force);
}
public function getProjects($force = FALSE) {
return $this
->getResource('resources.project', 'getProjects', $force);
}
public function getWorkflows($force = FALSE) {
return $this
->getResource('resources.workflow', 'getWorkflows', $force);
}
public function getProject($project_id) {
return $this->api
->getProject($project_id);
}
public function getFilters($force = FALSE) {
return $this
->getResource('resources.filter', 'getFilters', $force);
}
public function setProjectCallBackUrl($project_id, $callback_url) {
$args = [
'format' => 'JSON',
'callback_url' => $callback_url,
];
$response = $this->api
->setProjectCallBackUrl($project_id, $args);
if ($response
->getStatusCode() == Response::HTTP_NO_CONTENT) {
return TRUE;
}
return FALSE;
}
public function uploadDocument($title, $content, $locale, $url = NULL, LingotekProfileInterface $profile = NULL, $job_id = NULL, &$process_id = NULL) {
if (!is_array($content)) {
$data = json_decode($content, TRUE);
$content = $data === NULL ? $content : $data;
}
$defaults = [
'format' => 'JSON',
'project_id' => $this->configFactory
->get('lingotek.account')
->get('default.project'),
'fprm_id' => $this->lingotekFilterManager
->getFilterId($profile),
'fprm_subfilter_id' => $this->lingotekFilterManager
->getSubfilterId($profile),
'external_application_id' => 'e39e24c7-6c69-4126-946d-cf8fbff38ef0',
];
$defaults = array_filter($defaults);
$workflow_id = NULL;
$metadata = $this
->getIntelligenceMetadata($content);
if ($profile !== NULL && ($project = $profile
->getProject())) {
if ($project !== 'default') {
$defaults['project_id'] = $project;
}
}
if ($profile !== NULL && ($vault = $profile
->getVault())) {
if ($vault === 'default') {
$vault = $this->configFactory
->get('lingotek.account')
->get('default.vault');
}
$defaults['vault_id'] = $vault;
if ($vault === 'project_default') {
unset($defaults['vault_id']);
}
}
if ($profile !== NULL && ($workflow_id = $profile
->getWorkflow()) && $workflow_id !== 'default') {
$defaults['translation_workflow_id'] = $workflow_id;
}
else {
$defaults['translation_workflow_id'] = $this->configFactory
->get('lingotek.account')
->get('default.workflow');
}
$args = array_merge($metadata, $defaults);
$request_locales = [];
$request_workflows = [];
$request_translation_vaults = [];
if ($profile) {
$languages = $this->lingotekConfiguration
->getEnabledLanguages();
if (!empty($languages)) {
foreach ($languages as $language) {
if ($profile
->hasAutomaticRequestForTarget($language
->getId())) {
$target_locale = $this->languageLocaleMapper
->getLocaleForLangcode($language
->getId());
if ($target_locale !== $locale) {
$workflow_id = $profile
->getWorkflowForTarget($language
->getId());
if ($workflow_id === 'default') {
$workflow_id = $this->configFactory
->get('lingotek.account')
->get('default.workflow');
}
$translation_vault_id = $profile
->getVaultForTarget($language
->getId());
if ($translation_vault_id === 'default') {
$translation_vault_id = $this->configFactory
->get('lingotek.account')
->get('default.vault');
}
$request_locales[] = $target_locale;
$request_workflows[] = $workflow_id;
$request_translation_vaults[] = $translation_vault_id;
}
}
}
}
}
if (!empty($request_locales) && !empty($request_workflows)) {
$args['translation_locale_code'] = $request_locales;
$args['translation_workflow_id'] = $request_workflows;
$args['translation_vault_id'] = $request_translation_vaults;
}
if ($workflow_id && $workflow_id === 'project_default' || empty($request_locales)) {
unset($args['translation_workflow_id']);
}
$args = array_merge([
'content' => json_encode($content),
'title' => $title,
'locale_code' => $locale,
], $args);
if ($url !== NULL) {
$args['external_url'] = $url;
}
if ($job_id !== NULL) {
$args['job_id'] = $job_id;
}
$response = $this->api
->addDocument($args);
$statusCode = $response
->getStatusCode();
if ($statusCode == Response::HTTP_ACCEPTED) {
$responseBody = Json::decode($response
->getBody(), TRUE);
if (!empty($responseBody) && !empty($responseBody['properties']['id'])) {
$process_id = $responseBody['properties']['process_id'];
return $responseBody['properties']['id'];
}
else {
return FALSE;
}
}
elseif ($statusCode == Response::HTTP_PAYMENT_REQUIRED) {
$responseBody = Json::decode($response
->getBody());
$message = '';
if (!empty($responseBody) && isset($responseBody['messages'])) {
$message = $responseBody['messages'][0];
}
throw new LingotekPaymentRequiredException($message);
}
else {
return FALSE;
}
return FALSE;
}
public function updateDocument($doc_id, $content, $url = NULL, $title = NULL, LingotekProfileInterface $profile = NULL, $job_id = NULL, $locale = NULL, &$process_id = NULL) {
if (!is_array($content)) {
if ($content !== NULL) {
$data = json_decode($content, TRUE);
$content = $data === NULL ? $content : $data;
}
}
$defaults = [
'format' => 'JSON',
'fprm_id' => $this->lingotekFilterManager
->getFilterId($profile),
'fprm_subfilter_id' => $this->lingotekFilterManager
->getSubfilterId($profile),
'external_application_id' => 'e39e24c7-6c69-4126-946d-cf8fbff38ef0',
];
if ($profile && ($project = $profile
->getProject())) {
if ($project !== 'default') {
$defaults['project_id'] = $project;
}
}
$metadata = $this
->getIntelligenceMetadata($content);
if ($profile !== NULL && ($workflow_id = $profile
->getWorkflow()) && $workflow_id !== 'default') {
$defaults['translation_workflow_id'] = $workflow_id;
}
else {
$defaults['translation_workflow_id'] = $this->configFactory
->get('lingotek.account')
->get('default.workflow');
}
$args = array_merge($metadata, $defaults);
if ($url !== NULL) {
$args['external_url'] = $url;
}
if ($title !== NULL) {
$args['title'] = $title;
}
if ($job_id !== NULL) {
$args['job_id'] = $job_id;
}
if ($content !== NULL && !empty($content)) {
$args = array_merge([
'content' => json_encode($content),
], $args);
}
else {
unset($args['format']);
unset($args['fprm_id']);
unset($args['fprm_subfilter_id']);
unset($args['external_application_id']);
}
$request_locales = [];
$request_workflows = [];
$workflow_id = NULL;
if ($profile) {
$languages = $this->lingotekConfiguration
->getEnabledLanguages();
if (!empty($languages)) {
foreach ($languages as $language) {
if ($profile
->hasAutomaticRequestForTarget($language
->getId())) {
$target_locale = $this->languageLocaleMapper
->getLocaleForLangcode($language
->getId());
if ($locale !== NULL && $target_locale !== $locale) {
$workflow_id = $profile
->getWorkflowForTarget($language
->getId());
if ($workflow_id === 'default') {
$workflow_id = $this->configFactory
->get('lingotek.account')
->get('default.workflow');
}
$request_locales[] = $target_locale;
$request_workflows[] = $workflow_id;
}
}
}
}
}
if (!empty($request_locales) && !empty($request_workflows)) {
$args['translation_locale_code'] = $request_locales;
$args['translation_workflow_id'] = $request_workflows;
}
if ($workflow_id && $workflow_id === 'project_default' || empty($request_locales)) {
unset($args['translation_workflow_id']);
}
$response = $this->api
->patchDocument($doc_id, $args);
$statusCode = $response
->getStatusCode();
if ($statusCode == Response::HTTP_ACCEPTED) {
$responseBody = Json::decode($response
->getBody(), TRUE);
if (empty($responseBody)) {
return TRUE;
}
else {
$nextDocId = $responseBody['next_document_id'];
$process_id = $responseBody['process_id'];
return $nextDocId;
}
}
elseif ($statusCode == Response::HTTP_PAYMENT_REQUIRED) {
$responseBody = Json::decode($response
->getBody());
$message = '';
if (!empty($responseBody) && isset($responseBody['messages'])) {
$message = $responseBody['messages'][0];
}
throw new LingotekPaymentRequiredException($message);
}
elseif ($statusCode == Response::HTTP_GONE) {
throw new LingotekDocumentArchivedException($doc_id, sprintf('Document %s has been archived.', $doc_id));
}
elseif ($statusCode == Response::HTTP_LOCKED) {
$responseBody = Json::decode($response
->getBody());
$nextDocId = '';
if (!empty($responseBody) && isset($responseBody['next_document_id'])) {
$nextDocId = $responseBody['next_document_id'];
}
throw new LingotekDocumentLockedException($doc_id, $nextDocId, sprintf('Document %s has been updated with a new version. Use document %s for all future interactions.', $doc_id, $nextDocId));
}
return FALSE;
}
public function getIntelligenceMetadata(&$data) {
$metadata = [];
if (is_array($data) && isset($data['_lingotek_metadata']['_intelligence'])) {
$metadata = $data['_lingotek_metadata']['_intelligence'];
unset($data['_lingotek_metadata']['_intelligence']);
}
return $metadata;
}
public function cancelDocument($doc_id) {
$result = FALSE;
try {
$response = $this->api
->cancelDocument($doc_id);
$status_code = $response
->getStatusCode();
if ($status_code == Response::HTTP_NO_CONTENT) {
$result = TRUE;
}
} catch (LingotekApiException $ltkException) {
if ($ltkException
->getCode() === Response::HTTP_BAD_REQUEST) {
if (strpos($ltkException
->getMessage(), '"Unable to cancel documents which are already in a completed state.') >= 0) {
throw new LingotekDocumentAlreadyCompletedException($ltkException
->getMessage(), $ltkException
->getCode());
}
}
}
return $result;
}
public function cancelDocumentTarget($doc_id, $locale) {
$result = FALSE;
try {
$response = $this->api
->cancelDocumentTarget($doc_id, $locale);
$status_code = $response
->getStatusCode();
if ($status_code == Response::HTTP_NO_CONTENT) {
$result = TRUE;
}
} catch (LingotekApiException $ltkException) {
if ($ltkException
->getCode() === Response::HTTP_BAD_REQUEST) {
if (strpos($ltkException
->getMessage(), '"Unable to cancel translations which are already in a completed state.') >= 0) {
throw new LingotekDocumentTargetAlreadyCompletedException($ltkException
->getMessage(), $ltkException
->getCode());
}
}
throw $ltkException;
}
return $result;
}
public function addTarget($doc_id, $locale, LingotekProfileInterface $profile = NULL) {
$workflow_id = NULL;
$vault_id = NULL;
$drupal_language = $this->languageLocaleMapper
->getConfigurableLanguageForLocale($locale);
if ($profile !== NULL && ($workflow_id = $profile
->getWorkflowForTarget($drupal_language
->getId()))) {
switch ($workflow_id) {
case 'project_default':
$workflow_id = NULL;
break;
case 'default':
$workflow_id = $this->configFactory
->get('lingotek.account')
->get('default.workflow');
break;
}
}
if ($profile !== NULL && ($vault_id = $profile
->getVaultForTarget($drupal_language
->getId()))) {
if ($vault_id === 'default') {
$vault_id = NULL;
}
}
$response = $this->api
->addTranslation($doc_id, $locale, $workflow_id, $vault_id);
$statusCode = $response
->getStatusCode();
if ($statusCode == Response::HTTP_CREATED) {
return TRUE;
}
elseif ($statusCode == Response::HTTP_PAYMENT_REQUIRED) {
$responseBody = Json::decode($response
->getBody());
$message = '';
if (!empty($responseBody) && isset($responseBody['messages'])) {
$message = $responseBody['messages'][0];
}
throw new LingotekPaymentRequiredException($message);
}
elseif ($statusCode == Response::HTTP_GONE) {
return FALSE;
}
elseif ($statusCode == Response::HTTP_LOCKED) {
$responseBody = Json::decode($response
->getBody());
if (empty($responseBody)) {
return FALSE;
}
else {
$nextDocId = $responseBody['next_document_id'];
return FALSE;
}
}
return FALSE;
}
public function getLocales() {
$data = $this->api
->getLocales();
$locales = [];
if ($data) {
foreach ($data['entities'] as $locale) {
$locales[] = $locale['properties']['code'];
}
}
return $locales;
}
public function getLocalesInfo() {
$data = $this->api
->getLocales();
$locales = [];
if ($data) {
foreach ($data['entities'] as $locale) {
$languageCode = $locale['properties']['language_code'];
$countryCode = $locale['properties']['country_code'];
$title = $locale['properties']['title'];
$language = $locale['properties']['language'];
$country = $locale['properties']['country'];
$code = $locale['properties']['code'];
$locales[$code] = [
'code' => $code,
'language_code' => $languageCode,
'title' => $title,
'language' => $language,
'country_code' => $countryCode,
'country' => $country,
];
}
}
return $locales;
}
protected function getResource($resources_key, $func, $force = FALSE) {
$config_key = str_replace('account.', '', $resources_key);
$data = $this->configFactory
->get('lingotek.account')
->get($config_key);
if (empty($data) || $force) {
$community_id = $this->configFactory
->get('lingotek.account')
->get('default.community');
$data = $this->api
->{$func}($community_id);
$this->configFactory
->getEditable('lingotek.account')
->set($config_key, $data)
->save();
$keys = explode(".", $resources_key);
$default_key = 'default.' . end($keys);
$this
->setValidDefaultIfNotSet($default_key, $data);
}
return $data;
}
protected function setValidDefaultIfNotSet($default_key, $resources) {
$default_value = $this->configFactory
->get('lingotek.account')
->get($default_key);
$valid_resource_ids = array_keys($resources);
if ($default_key === 'default.filter') {
$valid_resource_ids[] = 'drupal_default';
}
if (empty($default_value) || !in_array($default_value, $valid_resource_ids)) {
$value = current($valid_resource_ids);
$this->configFactory
->getEditable('lingotek.account')
->set($default_key, $value)
->save();
}
}
public function getUploadedTimestamp($doc_id) {
$modified_date = FALSE;
try {
$response = $this->api
->getDocumentInfo($doc_id);
if ($response
->getStatusCode() == Response::HTTP_OK) {
$response_json = json_decode($response
->getBody(), TRUE);
$modified_date = intval(floor($response_json['properties']['last_uploaded_date'] / 1000));
}
} catch (LingotekApiException $exception) {
return FALSE;
}
return $modified_date;
}
public function getDocumentStatus($doc_id) {
try {
$response = $this->api
->getDocumentStatus($doc_id);
if ($response
->getStatusCode() == Response::HTTP_OK) {
return TRUE;
}
} catch (LingotekApiException $exception) {
return FALSE;
}
return FALSE;
}
public function getDocumentTranslationStatus($doc_id, $locale) {
$response = $this->api
->getDocumentTranslationStatus($doc_id, $locale);
$progress = FALSE;
$readyToDownload = FALSE;
if ($response
->getStatusCode() == Response::HTTP_OK) {
$progress_json = json_decode($response
->getBody(), TRUE);
$lingotek_locale = str_replace("_", "-", $locale);
if (!empty($progress_json['entities'])) {
foreach ($progress_json['entities'] as $index => $data) {
if ($data['properties']['locale_code'] === $lingotek_locale) {
$progress = $data['properties']['percent_complete'];
if (isset($data['properties']['ready_to_download'])) {
$readyToDownload = $data['properties']['ready_to_download'];
}
else {
$readyToDownload = $progress === self::PROGRESS_COMPLETE;
}
if ($data['properties']['status'] === Lingotek::STATUS_CANCELLED) {
$progress = $data['properties']['status'];
$readyToDownload = FALSE;
}
break;
}
}
}
if ($readyToDownload) {
return TRUE;
}
}
return $progress;
}
public function getDocumentTranslationStatuses($doc_id) {
$statuses = [];
try {
$response = $this->api
->getDocumentTranslationStatuses($doc_id);
} catch (LingotekApiException $e) {
return $statuses;
}
if ($response
->getStatusCode() == Response::HTTP_OK) {
$progress_json = json_decode($response
->getBody(), TRUE);
if (!empty($progress_json['entities'])) {
foreach ($progress_json['entities'] as $index => $data) {
$lingotek_locale = $data['properties']['locale_code'];
$statuses[$lingotek_locale] = $data['properties']['percent_complete'];
if ($data['properties']['status'] === Lingotek::STATUS_CANCELLED) {
$statuses[$lingotek_locale] = $data['properties']['status'];
}
}
}
}
return $statuses;
}
public function downloadDocument($doc_id, $locale) {
$response = $this->api
->getTranslation($doc_id, $locale, $this->configFactory
->get(static::SETTINGS)
->get('preference.enable_download_source'));
$statusCode = $response
->getStatusCode();
if ($statusCode == Response::HTTP_OK) {
return json_decode($response
->getBody(), TRUE);
}
elseif ($statusCode == Response::HTTP_GONE) {
return FALSE;
}
return FALSE;
}
public function getProcessStatus($process_id) {
try {
$response = $this->api
->getProcess($process_id);
if ($response
->getStatusCode() == Response::HTTP_OK) {
$bodyResponse = Json::decode($response
->getBody(), TRUE);
$progress = $bodyResponse['properties']['progress'];
$completed = $progress === self::PROGRESS_COMPLETE && $bodyResponse['properties']['status'] === 'COMPLETED';
return $completed ? TRUE : $progress;
}
} catch (LingotekApiException $exception) {
return FALSE;
}
return FALSE;
}
}