tmgmt_globalsight-8.x-1.x-dev/src/GlobalsightConnector.php

src/GlobalsightConnector.php
<?php

namespace Drupal\tmgmt_globalsight;

use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\TranslatorInterface;
use Masterminds\HTML5\Exception;
use SoapFault;
use Symfony\Component\DependencyInjection\SimpleXMLElement;

/**
 * GlobalSight connector.
 */
class GlobalsightConnector {
  public $base_url = '';
  private $username = '';
  private $password = '';
  private $endpoint = '';
  private $file_profile_id = '';

  /** @var string */
  private $token = '';

  /** @var \SoapClient */
  private $webservice;

  public function init($endpoint, $username, $password, $file_profile_id, $wsdl = FALSE) {
    $this->endpoint = $endpoint;
    $this->username = $username;
    $this->password = $password;
    $this->file_profile_id = $file_profile_id;
    $this->base_url = $GLOBALS ['base_url'];

    ini_set('soap.wsdl_cache_enabled', 0);
    ini_set('soap.wsdl_cache_ttl', 0);

    // Bad idea -- remote wsdl's may not be working in some instances.
    if (!$wsdl) {
      $wsdl = $this->endpoint . '?wsdl';
    }

    $this->webservice = new \SoapClient($wsdl, [
      'trace' => FALSE,
      'exceptions' => TRUE,
      'encoding' => 'ISO-8859-1',
      'soap_version' => SOAP_1_1,
    ]);
    $this->webservice->__setLocation($this->endpoint);

    $this->login();

    return $this->token;
  }

  /**
   * Authenticate with GS.
   */
  private function login() {
    // We don't want the code execution to continue if auth failed.
    // @todo: Figure out how to handle this better.
    $token = $this->call('login', [
      'p_username' => $this->username,
      'p_password' => $this->password,
    ]);

    if ($token instanceof \Exception) {
      $this->token = FALSE;
    }
    else {
      $this->token = $token;
    }
  }

  /**
   * getLocales method sends 'getFileProfileInfoEx' API request and parses a list of available languages.
   */
  function getLocales() {
    $fileProfiles = $this->getFileProfiles();
    if (isset($fileProfiles[$this->file_profile_id])) {
      $locales = [
        'source' => [$fileProfiles[$this->file_profile_id]['localeInfo']['sourceLocale']],
        'target' => $fileProfiles[$this->file_profile_id]['localeInfo']['targetLocale'],
      ];

      return $locales;
    }

    return FALSE;
  }


  public function getFileProfiles() {
    $result = $this->call('getFileProfileInfoEx', [
      'p_accessToken' => $this->token
    ]);

    $profiles = simplexml_load_string($result);

    $fpids = [];
    foreach ($profiles->fileProfile as $profile) {
      $fpids[(string) $profile->id] = $this->xml2array($profile);
    }

    return $fpids;
  }

  /**
   * GlobalSight is picky about its job labels. Sanitation FTW!
   *
   * @param $label string
   * @return string
   */
  public static function sanitizeLabel($label) {
    $label = trim($label);
    $label = str_replace(['\\', '/', ':', ';', '`', '?', '|', '"', '<', '>', '%', '&'], '', $label);
    $label = substr($label, 0, 80);


    if(!$label) {
      $label = uniqid('translation_');
    }

    return $label;
  }

  /**
   * Send method encodes and sends translation job to GlobalSight service.
   * Essentially, it runs 4 subsequent API methods in order to upload files
   * and check whether the upload succeeded:
   *   - getUniqueJobName
   *   - uploadFile
   *   - createJob
   *   - getStatus
   *
   * @param int $jobId
   *   An ID of the job.
   * @todo: Consider removing $jobId, it may be useless.
   * @param string $label
   *   Job label.
   * @param string $target_locale
   *   locale code (e.g. en_US).
   * @param array $translation_strings
   *   A key/value array of strings to be translated.
   *
   * @return FALSE|string
   *   Job title (which is an amended job label). FALSE if the upload failed.
   */
  public function send($jobId, $label, $target_locale, $translation_strings) {

    $fileProfiles = $this->getFileProfiles();
    if (!isset($fileProfiles[$this->file_profile_id])) {
      // @todo: COME ON -- ERROR MESSAGES!
      return FALSE;
    }

    $name = $this->call('getUniqueJobName', [
      'accessToken' => $this->token,
      'jobName' => self::sanitizeLabel($label),
    ]);

    if ($name instanceof \Exception) {
      var_dump($name->getMessage());
      die();
    }


    $xml = $this->prepareXML($jobId, $translation_strings);

    try {
      // Void :(
      $response = $this->webservice->uploadFile(
        $this->soapVar('accessToken', $this->token),
        $this->soapVar('jobName', $name),
        $this->soapVar('filePath', $name . '.xml'),
        $this->soapVar('fileProfileId', $this->file_profile_id),
        $this->soapVar('content', $xml, XSD_BASE64BINARY)
      );
    }
    catch (SoapFault $sf) {
      $this->errorLog($sf, 'uploadFile');

      return FALSE;
    }

    // At this point file was "probably" successfully uploaded.
    $response = $this->call('createJob', [
      'accessToken' => $this->token,
      'jobName' => $name,
      'comment' => 'Drupal GlobalSight Translation Module',
      'filePaths' => $name . '.xml',
      'fileProfileIds' => $this->file_profile_id,
      'targetLocales' => $target_locale
    ]);
    if ($response instanceof \Exception) {
      return FALSE;
    }

    // Finally, the only way to really tell if the job upload succeeded
    // is the getStatus method.
    if ($this->getStatus($name)) {
      return $name;
    };

    return FALSE;
  }

  /**
   *
   * @param string $job_name
   *            GlobalSight job title.
   * @return mixed - FALSE : Ignore the status, move on...
   *         - "PERMANENT ERROR" : There is a permanent error at GS. Cancel the job.
   *         - API response converted to the array.
   */
  function getStatus($job_name) {

    $result = $this->call('getStatus', [
      'p_accessToken' => $this->token,
      'p_jobName' => $job_name
    ]);

    if ($result instanceof \Exception) {
      return FALSE;
    }

    try {
      $xml = new \SimpleXMLElement($result);

      return $this->xml2array($xml);
    }
    catch (Exception $err) {
      \Drupal::logger('tmgmt_globalsight')
        ->error("Error parsing XML for %job_name. <br> <b>Error message:</b><br> %err", array(
          '%job_name' => $job_name,
          '%err' => $err
        ));

      return FALSE;
    }
  }

  /**
   * Method cancel requests job deletion in GlobalSight.
   *
   * @param string $job_name
   *            GlobalSight job title.
   * @return mixed - FALSE: on any API error
   *         - API response in form of array
   */
  function cancel($job_name) {

    $result = $this->call('cancelJob', [
      'p_accessToken' => $this->token,
      'p_jobName' => $job_name
    ]);

    if ($result instanceof Exception) {
      return FALSE;
    }

    $xml = new \SimpleXMLElement($result);

    // @todo: will this work?
    return $this->xml2array($xml);
  }

  /**
   * This method downloads translations for a given GlobalSight job name.
   *
   * @param $job_name Title
   *            of the GlobalSight job
   * @return array|bool - FALSE: if API request failed due to any reason
   *         - API response in form of array
   */
  function receive($job_name) {
    $result = $this->call("getJobExportFiles", [
      'p_accessToken' => $this->token,
      'p_jobName' => $job_name
    ]);

    if ($result instanceof \Exception) {
      return FALSE;
    }

    $xml = new \SimpleXMLElement($result);

    $jobFiles = (array) $xml;
    if (!isset($jobFiles['root']) || !isset($jobFiles['paths'])) {
      return FALSE;
    }

    $data = file_get_contents($jobFiles['root'] . '/' . $jobFiles['paths']);

    $xmlObject = new \SimpleXMLElement($data);

    $results = [];
    foreach ($xmlObject->field as $field) {
      $value = (string) $field->value;
      $key = (string) $field->name;
      $results[$key]['#text'] = $value;
    }

    return $results;
  }

  /**
   * Helper method translating GlobalSight status codes into integers.
   */
  function code2status($code) {
    $a = array(
      0 => 'ARCHIVED',
      1 => 'DISPATCHED',
      2 => 'EXPORTED',
      3 => 'LOCALIZED',
      4 => 'CANCELED'
    );

    return $a [intval($code)];
  }

  /**
   * Helper method recursively converting xml documents to array.
   */
  function xml2array($xmlObject, $out = array()) {
    foreach (( array ) $xmlObject as $index => $node) {
      $out [$index] = (is_object($node)) ? $this->xml2array($node) : $node;
    }

    return $out;
  }

  /**
   * Method checks if job upload to GlobalSight succeeded.
   *
   * @param $jobName Title
   *            of the GlobalSight job
   *
   * @return bool TRUE: if job import succeeded
   *         FALSE: if job import failed
   */
  public function uploadErrorHandler($jobName) {
    $status = $this->getStatus($jobName);
    $status = $status['status'];

    // LEVERAGING appears to be normal status right after successful upload

    switch ($status) {

      case 'LEVERAGING':
        return TRUE;
        break;

      // IMPORT_FAILED appears to be status when XML file is corrupt.
      case 'IMPORT_FAILED':
        \Drupal::logger('tmgmt_globalsight')
          ->error("Error uploading file to GlobalSight. XML file appears to be corrupt or GlobalSight server timed out. Translation job canceled.", array());
        drupal_set_message(t('Error uploading file to GlobalSight. Translation job canceled.'), 'error');

        return FALSE;
        break;

      // UPLOADING can be normal message if translation upload did not finish, but, if unchanged for a period of time,
      // it can also be interpreted as "upload failed" message. So we need to have ugly time testing here.
      case 'UPLOADING':
        // Wait for 5 seconds and check status again.
        sleep(5);
        $revised_status = $this->getStatus($jobName);
        $revised_status = $revised_status['status'];
        if ($revised_status == 'UPLOADING') {

          // Consolidate this messaging into an error handler and inject it as dependency
          \Drupal::logger('tmgmt_globalsight')
            ->error("Error creating job at GlobalSight. Translation job canceled.", array());
          drupal_set_message(t('Error creating job at GlobalSight. Translation job canceled.'), 'error');

          return FALSE;
        }
        break;
    };

    return TRUE;
  }

  /**
   * Let this function run SOAP errands for us.
   *
   * @param $function_name
   * @param $params
   * @return string|Exception
   *
   * @todo: Document it a bit better.
   */
  public function call($function_name, $params = []) {
    try {
      $result = $this->webservice->__soapCall($function_name, $params);
    }
    catch (\SoapFault $sf) {
      \Drupal::logger('tmgmt_globalsight')
        ->notice("<b>Function call: </b>%func<br>
                    <b>Parameters:</b><br><pre>%params</pre><br>
                    <b>SOAP Fault code:</b> %faultcode.<br>
                    <b>Error message:</b><br>%err", [
          '%func' => $function_name,
          '%params' => json_encode($params),
          '%faultcode' => $sf->getCode(),
          '%err' => $sf->getMessage(),
        ]);

      return $sf;
    }

    return $result;
  }

  /**
   * Prepare XML document to be sent to GlobalSight.
   *
   * @param string $job_id
   *   Pretty self-explanatory.
   * @param array $translation_strings
   *   Key/value array of strings to be translated.
   * @return string
   *   XML document to be fed to GlobalSight.
   */
  private function prepareXML($job_id, $translation_strings) {
    $xml = "<?xml version='1.0' encoding='UTF-8' ?>";
    $xml .= "<fields id='" . $job_id . "'>";
    foreach ($translation_strings as $key => $value) {
      $xml .= "<field>";
      $xml .= "<name>" . $key . "</name>";
      $xml .= "<value><![CDATA[" . $value . "]]></value>";
      $xml .= "</field>";
    };
    $xml .= "</fields>";

    return $xml;
  }

  /**
   * @todo: Documentation.
   */
  private function soapVar($name, $value, $encoding = XSD_STRING) {
    return new \SoapVar($value, $encoding, NULL, NULL, $name);
  }

  /**
   * @todo: Documentation.
   */
  private function errorLog(\SoapFault $sf, $function_name = NULL, $params = NULL) {
    $message = '';
    $args = [
      '%faultcode' => $sf->getCode(),
      '%err' => $sf->getMessage()
    ];

    if ($function_name) {
      $message .= '"<b>Function call: </b>%func<br>';
      $args['%func'] = $function_name;
    }

    if ($params) {
      $message .= '"<b>Parameters:</b><br><pre>%params</pre><br>';
      $args['%params'] = json_encode($params);
    }

    $message .= '<b>SOAP Fault code:</b> %faultcode.<br>';
    $message .= '<b>Error message:</b><br>%err<br>';

    \Drupal::logger('tmgmt_globalsight')->notice($message, $args);
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc