wordsonline_connector-1.0.x-dev/src/WordsOnlineConnectorManager.php
src/WordsOnlineConnectorManager.php
<?php
namespace Drupal\wordsonline_connector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\wordsonline_connector\WordsOnlineConst;
use Drupal\tmgmt\Entity\Job;
use Drupal\tmgmt\JobInterface;
use Drupal\wordsonline_connector\Entity\WOFile;
use Drupal\wordsonline_connector\Common\ZipHandle;
use Drupal\wordsonline_connector\WordsOnlineState;
use Drupal\wordsonline_connector\WordsOnlineStatus;
use Symfony\Component\DependencyInjection\ContainerInterface;
use GuzzleHttp\ClientInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Archiver\ArchiverManager;
use Drupal\Core\File\FileSystemInterface;
use Drupal\tmgmt_file\Format\FormatManager;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\wordsonline_connector\Plugin\tmgmt\Translator\WordsOnlineTranslator;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\tmgmt\Entity\JobItem;
use Drupal\tmgmt\Data;
/**
* A service manager for wordsonline jobs.
*/
class WordsOnlineConnectorManager {
/**
* Guzzle HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $client;
/**
* Database.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* ModuleExtensionList.
*
* @var Drupal\Core\Extension\ModuleExtensionList
*/
protected $moduleExtensionList;
/**
* Messenger.
*
* @var \Drupal\Core\Messenger\Messenger
*/
protected $messenger;
/**
* Archiver.
*
* @var Drupal\Core\Archiver\ArchiverManager
*/
protected $archiver;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The file format manager.
*
* @var \Drupal\tmgmt_file\Format\FormatManager
*/
protected $formatManager;
/**
* The tmgmt data.
*
* @var \Drupal\tmgmt\Data
*/
protected $tmgmt_data;
/**
* Constructor.
*
* @param \GuzzleHttp\ClientInterface $client
* Http client.
* @param \Drupal\Core\Database\Connection $database
* Database connection.
* @param \Drupal\Core\Messenger\Messenger $messenger
* Messenger.
* @param \Drupal\Core\Archiver\ArchiverManager $archiver
* Archiver manager.
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
* File System.
* @param \Drupal\tmgmt_file\Format\FormatManager $formatManager
* The file format manager.
* @param \Drupal\Core\Extension\ModuleExtensionList
* The list module extension list.
* @param \Drupal\tmgmt\Data $tmgmt_data
* The tmgmt data service.
*/
public function __construct(ClientInterface $client, Connection $database, Messenger $messenger, ArchiverManager $archiver, FileSystemInterface $fileSystem, FormatManager $formatManager, ModuleExtensionList $moduleExtensionList, Data $tmgmt_data) {
$this->client = $client;
$this->database = $database;
$this->messenger = $messenger;
$this->archiver = $archiver;
$this->fileSystem = $fileSystem;
$this->formatManager = $formatManager;
$this->moduleExtensionList = $moduleExtensionList;
$this->tmgmt_data = $tmgmt_data;
}
/**
* Update status of WordsOnline Job table
*
* @param int $job_id
* Job id.
* @param string $status
* Status.
*/
public function updateWordsOnlineJobStatus($job_id, $status) {
$this->database
->update(WordsOnlineConst::JOB_TABLE)
->condition("job_id", $job_id)
->fields(
[
"status" => $status,
]
)
->execute();
}
/**
* Get the token.
*/
public function getToken(){
$results = $this->database->query(WordsOnlineConst::SELECT_CONFIG)->fetchAssoc();
$token = NULL;
if ($results != NULL) {
$auth["username"] = $results["username"];
$auth["password"] = $results["password"];
$auth["scope"] = $results["scope"];
$auth["grant_type"] = $results["grant_type"];
$response = wordsonline_connector_get_token('POST', $auth);
$response = json_decode($response, TRUE);
$token = $response['access_token'];
}
else {
$this->messenger->addError(
$this->pleaseContactHelpDeskMessage()
);
}
return $token;
}
/**
* Imports the given translation data.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job to import translations for.
* @param string $translation
* The translation data.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
* Throws an exception if XLF plugin does not exist.
* @throws \Drupal\tmgmt\TMGMTException
* Throws an exception in case of a neeror.
*/
public function importTranslation(JobInterface $job, $translation) {
$xliff = $this->formatManager->createInstance("xlf");
$messages = $this->getXliffError($job, $translation);
if ( $messages != NULL && !empty($messages) && $messages != ""){
return $messages;
}
if ($data = $xliff->import($translation, FALSE)) {
$job->addTranslatedData(
$data,
NULL,
TMGMT_DATA_ITEM_STATE_TRANSLATED
);
$job->addMessage("The translation has been received.");
}
return "";
}
/**
* Get Error of xliff xml data.
*
* @param \Drupal\tmgmt\JobInterface $job
* Job.
* @param string $importedXML
* Xliff content.
*/
public function getXliffError($job, $importedXML){
$xml = simplexml_load_string($importedXML);
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
$phase = $xml->xpath("//xliff:phase[@phase-name='extraction']");
if ($phase) {
$phase = reset($phase);
}
else {
return 'The imported file is missing required XLIFF phase information.';
}
// Check if the job has a valid job reference.
if (!isset($phase['job-id'])) {
return 'The imported file does not contain a job reference.';
}
// Attempt to load the job if none passed.
$job = (Job::load((int) $phase['job-id']));
if (empty($job)) {
return "The imported file job id " . $phase['job-id'] . " is not available.";
}
// Compare source language.
if (!isset($xml->file['source-language']) || $job->getRemoteSourceLanguage() != $xml->file['source-language']) {
$file_language = empty($xml->file['source-language']) ? t('none') : $xml->file['source-language'];
$messages = 'The imported file source language ' . $file_language . ' does not match the job source language ' . $job->getRemoteSourceLanguage() . '.';
return $messages;
}
// Compare target language.
if (!isset($xml->file['target-language']) || $job->getRemoteTargetLanguage() != $xml->file['target-language']) {
$file_language = empty($xml->file['target-language']) ? t('none') : $xml->file['target-language'];
$messages = 'The imported file target language ' . $file_language . ' does not match the job target language ' . $job->getRemoteTargetLanguage() . '.';
return $messages;
}
$targets = $this->getImportedTargets($xml, $job);
if (empty($targets)|| $targets == NULL) {
return 'The imported file seems to be missing translation.';
}
return "";
}
/**
* Get imported targets.
*
* @param SimpleXMLElement $importedXML
* Xml element.
* @param \Drupal\tmgmt\JobInterface $job
* Job.
*
*/
public function getImportedTargets($importedXML,$job){
$importedTransUnits = NULL;
$reader = new \XMLReader();
foreach ($importedXML->xpath('//xliff:trans-unit') as $unit) {
if (!$job->getSetting('xliff_processing')) {
$importedTransUnits[(string) $unit['id']]['#text'] = (string) $unit->target;
continue;
}
$reader->XML($unit->target->asXML());
$reader->read();
$importedTransUnits[(string) $unit['id']]['#text'] =
$this->processForImport($reader->readInnerXML(), $job);
}
return $importedTransUnits;
}
/**
* Processes trans-unit/target to rebuild back the HTML.
*
* @param string $translation
* Job data array.
* @param \Drupal\tmgmt\JobInterface $job
* Translation job.
*
* @return string
*/
public function processForImport($translation, JobInterface $job) {
// In case we do not want to do xliff processing return the translation as
// is.
if (!$job->getSetting('xliff_processing')) {
return $translation;
}
$reader = new \XMLReader();
$reader->XML('<translation>' . $translation . '</translation>');
$text = '';
while ($reader->read()) {
// If the current element is text append it to the result text.
if ($reader->name == '#text' || $reader->name == '#cdata-section') {
$text .= $reader->value;
}
elseif ($reader->name == 'x') {
if ($reader->getAttribute('ctype') == 'lb') {
$text .= '<br />';
}
}
elseif ($reader->name == 'ph') {
if ($reader->getAttribute('ctype') == 'image') {
$text .= '<img';
while ($reader->moveToNextAttribute()) {
// @todo - we have to use x-html: prefixes for attributes.
if ($reader->name != 'ctype' && $reader->name != 'id') {
$text .= " {$reader->name}=\"{$reader->value}\"";
}
}
$text .= ' />';
}
}
}
return $text;
}
/**
* Translate action.
*/
public function translate(){
$token = $this->getToken();
if ($token != NULL) {
$records = $this->getRecordsForTranslate(WordsOnlineConst::JOB_TABLE);
$data = $this->GetDataFromWordsOnline($token);
foreach ($records as $row) {
$job = Job::load($row->job_id);
$job_id = $row->job_id;
$translator = $job->getTranslator();
$is_auto = $translator->isAutoAccept();
if ($job) {
foreach ($data as $d) {
if ($d->requestGuid == $row->request_guid) {
if ($d->state == WordsOnlineState::AUTOMATION_FAILED
&& $row->status != WordsOnlineConst::JOB_AUTOMATION_FAILED
) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_AUTOMATION_FAILED);
continue;
}
if (($d->status == WordsOnlineStatus::UNPAID
|| $d->status == WordsOnlineStatus::QUOTE_SUBMITTED)
&& $row->status != WordsOnlineConst::JOB_QUOTED
) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_QUOTED);
continue;
}
if($d->status == WordsOnlineStatus::IN_PROGRESS_STATUS && $row->status != WordsOnlineConst::JOB_QUOTED){
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_QUOTED);
continue;
}
if ($d->status == WordsOnlineStatus::IMPORTED_STATUS && $row->status == WordsOnlineConst::JOB_DELIVERED) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_IMPORTED);
}
if (($d->state == WordsOnlineState::QUOTE_SUBMITTED
&& ($d->status == WordsOnlineStatus::UNPAID
|| $d->status == WordsOnlineStatus::QUOTE_SUBMITTED)
) && $row->status != WordsOnlineConst::JOB_QUOTED) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_QUOTED);
continue;
}
if ($row->status == WordsOnlineConst::JOB_IMPORTED && $d->status != WordsOnlineStatus::IMPORTED_STATUS) {
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::IMPORTED_STATUS);
continue;
}
if (($d->status == WordsOnlineStatus::DELIVERED
|| $d->status == WordsOnlineStatus::IMPORT_FAIL_STATUS
|| $d->status == WordsOnlineStatus::IMPORT_FAILED_STATUS
) && $row->status != WordsOnlineConst::JOB_DELIVERED){
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_DELIVERED);
continue;
}
}
}
if ($row->status == WordsOnlineConst::JOB_ORDERED || $row->status == WordsOnlineConst::JOB_QUOTED || $row->status == WordsOnlineConst::JOB_DELIVERED) {
$translator->setSetting("xliff_processing", TRUE);
$translator->save();
$auto_import = $job->getSetting("auto_import");
$files = wordsonline_connector_get_request_files_list($row->request_guid, $token);
if ($files != NULL) {
if ($files->result != NULL && count($files->result) > 0) {
if ($auto_import == TRUE || $auto_import == 1
|| $is_auto == TRUE || $is_auto == 1) {
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_DELIVERED);
foreach ($files->result as $fi) {
$file_guid = $fi->guid;
$data = wordsonline_connector_download_file(
$row->request_guid,
$file_guid,
$token
);
$fName = "public://" . $fi->name;
file_put_contents($fName, $data);
$zip = new ZipHandle($fName, $this->archiver, $this->fileSystem);
$fileContent = $zip->fileContent;
$this->fileSystem->delete(
$fName
);
$is_has_error = FALSE;
if ($fileContent != NULL && count($fileContent) > 0) {
foreach ($fileContent as $fcontent) {
$msg = $this->importTranslation(
$job,
$fcontent
);
if($msg != "") {
$this->messenger->addError("an error occured when import job {$row->job_id} with zip file {$fName}");
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::IMPORT_FAIL_STATUS, $msg);
$is_has_error = TRUE;
}
}
}
if ($is_has_error == TRUE) {
continue;
}
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::IMPORTED_STATUS);
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_IMPORTED);
}
}
else {
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_DELIVERED);
}
}
}
}
if( $row->status == WordsOnlineConst::JOB_IMPORTED && ( $is_auto == TRUE || $is_auto == 1) && $job->getState() == WordsOnlineConst::STATE_FINISHED) {
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::FINISHED_STATUS);
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_FINISHED);
continue;
}
}
}
}
}
/**
* Get Data From WordsOnline.
*
* @param string $token
* The token.
*/
public function GetDataFromWordsOnline($token) {
$records =[];
$skip = 0;
$top =100;
$count =0;
$total = 0;
$url = WordsOnlineConst::API_URL . 'Requests?$skip=' . $skip .'&$top=' . $top .'&$orderby=CreatedAt desc ';
do {
$url = WordsOnlineConst::API_URL . 'Requests?$skip=' . $skip .'&$top=' . $top .'&$orderby=CreatedAt desc ';
$result = $this->client->get(
$url,
[
"headers" => [
"Authorization" => "Bearer " . $token,
"Accept" => "application/json",
"Referer" => "ClientAPI",
],
"timeout" => 3600,
]
);
if ($result->getStatusCode() == 200) {
$data = json_decode($result->getBody()->getContents());
if ($data->status == 1) {
$count = $data->result->count;
$res = $data->result->list;
foreach($res as $r){
array_push($records,$r);
}
$skip = $skip + 100;
$total = count($records);
} else {
break;
}
}else {
break;
}
} while ($total < $count);
return $records;
}
/**
* Get records ordered or delivered
*
* @param string $table_name
* The name of table.
*/
public function getRecordsForTranslate($table_name){
$records = $this->database->select($table_name, "wol");
$records->fields("wol");
$records->condition(
"wol.status",
[
WordsOnlineStatus::FINISHED_STATUS
],
"not in"
);
$records->orderBy("wol.job_id", "DESC");
$res = $records->execute()->fetchAll();
return $res;
}
/**
* Call request to WordsOnline api for action.
*
* @param string $token
* Token.
* @param string $request_guid
* Request guid.
* @param string $status
* Status.
* @param string $msg
* Message
*/
public function requestAction($token,$request_guid,$status,$msg =""){
wordsonline_connector_request_action(
$request_guid,
$status,
$msg,
$token
);
}
/**
* Import
*
* @param int $job_id
* Job id.
* @param string $request_guid
* Request guid.
*/
public function import($job_id, $request_guid){
$token = $this->getToken();
$status ="";
if ($token != NULL) {
$files = wordsonline_connector_get_request_files_list(
$request_guid,
$token
);
if ($files != NULL) {
if ($files->result != NULL
&& count($files->result) > 0
) {
$status = "downloaded";
$job = Job::load($job_id);
if ($job) {
$is_import = FALSE;
$translator = $job->getTranslator();
$translator->setSetting(
"xliff_processing",
TRUE
);
$translator->save();
$is_auto = $translator->isAutoAccept();
foreach ($files->result as $fi) {
$file_guid = $fi->guid;
$data = wordsonline_connector_download_file(
$request_guid,
$file_guid,
$token
);
$fName = "public://" . $fi->name;
file_put_contents($fName, $data);
$zip = new ZipHandle($fName, $this->archiver, $this->fileSystem);
$fileContent = $zip->fileContent;
$this->fileSystem->delete(
$fName
);
$is_has_error = FALSE;
if ($fileContent != NULL
&& count($fileContent) > 0
) {
foreach ($fileContent as $fcontent) {
$msg = $this->importTranslation(
$job,
$fcontent
);
if($msg !="") {
$this->messenger->addError("an error occured when import job {$job_id} with zip file {$fName}");
$this->requestAction($token, $request_guid, WordsOnlineStatus::IMPORT_FAIL_STATUS, $msg);
$is_has_error = TRUE;
}
}
}
if ($is_has_error == TRUE) {
continue;
}
$this->requestAction($token, $request_guid, WordsOnlineStatus::IMPORTED_STATUS);
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_IMPORTED);
$is_import = TRUE;
}
if ($is_import) {
$status = "success";
}
}
}
}
} else {
$status ="error";
}
return $status;
}
/**
* Check change delivery status
*
* @param string $token
* The token.
*/
public function isHasDeliveryStatusChange($token) {
$has_change = FALSE;
$records = $this->getRecordsForCheckHasFile(WordsOnlineConst::JOB_TABLE);
foreach ($records as $row) {
$files = wordsonline_connector_get_request_files_list($row->request_guid, $token);
if ($files != NULL) {
if ($files->result != NULL && count($files->result) > 0) {
$job = Job::load($row->job_id);
if ($job) {
$auto_import = $job->getSetting("auto_import");
$translator = $job->getTranslator();
$translator->setSetting("xliff_processing", TRUE);
$translator->save();
$is_auto = $translator->isAutoAccept();
if ($row->status == WordsOnlineConst::JOB_ORDERED || $row->status == WordsOnlineConst::JOB_QUOTED) {
$has_change = TRUE;
}
if ($auto_import == TRUE
|| $auto_import == 1
|| $is_auto == TRUE
|| $is_auto == 1
) {
$is_import = FALSE;
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_DELIVERED);
foreach ($files->result as $fi) {
$file_guid = $fi->guid;
$data = wordsonline_connector_download_file(
$row->request_guid,
$file_guid,
$token
);
$fName = "public://" . $fi->name;
file_put_contents($fName, $data);
$zip = new ZipHandle($fName, $this->archiver, $this->fileSystem);
$fileContent = $zip->fileContent;
$this->fileSystem->delete(
$fName
);
$is_has_error = FALSE;
if ($fileContent != NULL
&& count($fileContent) > 0
) {
foreach ($fileContent as $fcontent) {
$msg = $this->importTranslation(
$job,
$fcontent
);
if($msg != "") {
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::IMPORT_FAIL_STATUS, $msg);
$is_has_error = TRUE;
}
}
}
if ($is_has_error == TRUE) {
continue;
}
$this->requestAction($token, $row->request_guid, WordsOnlineStatus::IMPORTED_STATUS);
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_IMPORTED);
$is_import = TRUE;
}
if ($is_import) {
$has_change = TRUE;
}
}
else {
$this->updateWordsOnlineJobStatus($row->job_id, WordsOnlineConst::JOB_DELIVERED);
}
}
}
}
}
return $has_change;
}
/**
* Get records ordered or delivered
*
* @param string $table_name
* The name of table.
*/
public function getRecordsForCheckHasFile($table_name){
$records = $this->database->select($table_name, "wol");
$records->fields("wol");
$records->condition(
"wol.status",
[
WordsOnlineConst::JOB_ORDERED,
WordsOnlineConst::JOB_DELIVERED,
WordsOnlineConst::JOB_QUOTED,
],
"in"
);
$records->orderBy("wol.job_id", "DESC");
$res = $records->execute()->fetchAll();
return $res;
}
/**
* Check is change quote status.
*
* @param array $data
* The data from WordsOnline.
*/
public function isChangeQuoteStatus($data){
$request_guids =[];
$has_change = FALSE;
foreach($data as $d){
array_push($request_guids,$d->requestGuid);
}
$records = $this->database->select(WordsOnlineConst::JOB_TABLE, "wol");
$records->fields("wol");
$records->condition(
"wol.request_guid",
$request_guids,
"in"
);
$records->orderBy("wol.job_id", "DESC");
$res = $records->execute()->fetchAll();
foreach($data as $row) {
foreach($res as $r) {
if ($row->requestGuid == $r->request_guid) {
$job_id = $r->job_id;
$status = $r->status;
$job = Job::load($job_id);
if ($row->state == WordsOnlineState::AUTOMATION_FAILED
&& $status != WordsOnlineConst::JOB_AUTOMATION_FAILED
) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_AUTOMATION_FAILED);
$has_change = TRUE;
continue;
}
if (($row->status == WordsOnlineStatus::UNPAID
|| $row->status == WordsOnlineStatus::QUOTE_SUBMITTED)
&& $status != WordsOnlineConst::JOB_QUOTED
) {
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_QUOTED);
$has_change = TRUE;
continue;
}
if($row->status == WordsOnlineStatus::IN_PROGRESS_STATUS && $status != WordsOnlineConst::JOB_QUOTED){
$this->updateWordsOnlineJobStatus($job_id, WordsOnlineConst::JOB_QUOTED);
$has_change = TRUE;
continue;
}
if($row->status == WordsOnlineStatus::PREPARING_QUOTE){
$has_change = TRUE;
continue;
}
if ($row->status == WordsOnlineStatus::DELIVERED
|| $row->status == WordsOnlineStatus::IMPORT_FAIL_STATUS
|| $row->status == WordsOnlineStatus::IMPORT_FAILED_STATUS
) {
if ($status != WordsOnlineConst::JOB_DELIVERED
&& $status != WordsOnlineConst::JOB_IMPORTED
&& $status != WordsOnlineConst::JOB_FINISHED
) {
$has_change = TRUE;
continue;
}
}
if ($job) {
$state = $job->getState();
if ($state == WordsOnlineConst::STATE_FINISHED
&& $row->status != WordsOnlineStatus::FINISHED_STATUS
) {
$has_change = TRUE;
continue;
}
}
break;
}
}
}
return $has_change;
}
}