first commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Log implements WPML_TP_API_Log_Interface {
|
||||
const LOG_WP_OPTION = '_wpml_tp_api_Logger';
|
||||
const LOG_MAX_SIZE = 500;
|
||||
|
||||
public function log( $action, $data = array() ) {
|
||||
$log_base_data = array(
|
||||
'timestamp' => false,
|
||||
'action' => false,
|
||||
);
|
||||
|
||||
$log_item = array_merge( $log_base_data, $data );
|
||||
|
||||
$log_item['timestamp'] = date( 'Y-m-d H:i:s' );
|
||||
$log_item['action'] = $action;
|
||||
|
||||
$log = $this->get_log_data();
|
||||
|
||||
$log = array_slice( $log, - ( self::LOG_MAX_SIZE - 1 ) );
|
||||
|
||||
$log[] = $log_item;
|
||||
|
||||
$this->update_log( $log );
|
||||
}
|
||||
|
||||
private function update_log( $log ) {
|
||||
return update_option( self::LOG_WP_OPTION, $log, false );
|
||||
}
|
||||
|
||||
public function flush_log() {
|
||||
return update_option( self::LOG_WP_OPTION, [], false );
|
||||
}
|
||||
|
||||
public function get_log_data() {
|
||||
$log = get_option( self::LOG_WP_OPTION, [] );
|
||||
|
||||
return is_array( $log ) ? $log : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_API_Client {
|
||||
/** @var string */
|
||||
private $proxy_url;
|
||||
|
||||
/** @var WP_Http $http */
|
||||
private $http;
|
||||
|
||||
/** @var WPML_TP_Lock $tp_lock */
|
||||
private $tp_lock;
|
||||
|
||||
/** @var WPML_TP_HTTP_Request_Filter */
|
||||
private $request_filter;
|
||||
|
||||
public function __construct(
|
||||
$proxy_url,
|
||||
WP_Http $http,
|
||||
WPML_TP_Lock $tp_lock,
|
||||
WPML_TP_HTTP_Request_Filter $request_filter
|
||||
) {
|
||||
$this->proxy_url = $proxy_url;
|
||||
$this->http = $http;
|
||||
$this->tp_lock = $tp_lock;
|
||||
$this->request_filter = $request_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TP_API_Request $request
|
||||
* @param bool $raw_json_response
|
||||
*
|
||||
* @return array|mixed|stdClass|string
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function send_request( WPML_TP_API_Request $request, $raw_json_response = false ) {
|
||||
if ( $this->tp_lock->is_locked( $request->get_url() ) ) {
|
||||
throw new WPML_TP_API_Exception( 'Communication with translation proxy is not allowed.', $request );
|
||||
}
|
||||
WPML_TranslationProxy_Com_Log::log_call( $request->get_url(), $request->get_params() );
|
||||
$response = $this->call_remote_api( $request );
|
||||
|
||||
if ( ! $response || is_wp_error( $response ) || ( isset( $response['response']['code'] ) && $response['response']['code'] >= 400 ) ) {
|
||||
throw new WPML_TP_API_Exception( 'Communication error', $request, $response );
|
||||
}
|
||||
|
||||
if ( isset( $response['headers']['content-type'] ) ) {
|
||||
$content_type = $response['headers']['content-type'];
|
||||
$response = $response['body'];
|
||||
|
||||
if ( strpos( $content_type, 'zip' ) !== false ) {
|
||||
$response = gzdecode( $response );
|
||||
} else {
|
||||
WPML_TranslationProxy_Com_Log::log_response( $response );
|
||||
}
|
||||
|
||||
$json_response = json_decode( $response );
|
||||
|
||||
if ( $json_response ) {
|
||||
if ( $raw_json_response ) {
|
||||
$response = $json_response;
|
||||
} else {
|
||||
$response = $this->handle_json_response( $request, $json_response );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WPML_TP_API_Request $request
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function call_remote_api( WPML_TP_API_Request $request ) {
|
||||
$context = $this->filter_request_params( $request->get_params(), $request->get_method() );
|
||||
|
||||
return $this->http->request( $this->proxy_url . $request->get_url(), $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params request parameters
|
||||
* @param string $method HTTP request method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filter_request_params( $params, $method ) {
|
||||
return $this->request_filter->build_request_context(
|
||||
array(
|
||||
'method' => $method,
|
||||
'body' => $params,
|
||||
'sslverify' => true,
|
||||
'timeout' => 60,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TP_API_Request $request
|
||||
* @param stdClass $response
|
||||
*
|
||||
* @return mixed
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
private function handle_json_response( WPML_TP_API_Request $request, $response ) {
|
||||
if ( $request->has_api_response() ) {
|
||||
if ( ! isset( $response->status->code ) || $response->status->code !== 0 ) {
|
||||
throw new WPML_TP_API_Exception(
|
||||
$this->generate_error_message_from_status_field( $response ),
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
}
|
||||
$response = $response->response;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function generate_error_message_from_status_field( $response ) {
|
||||
$message = '';
|
||||
if ( isset( $response->status->message ) ) {
|
||||
if ( isset( $response->status->code ) ) {
|
||||
$message = '(' . $response->status->code . ') ';
|
||||
}
|
||||
$message .= $response->status->message;
|
||||
} else {
|
||||
$message = 'Unknown error';
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_API_Exception extends Exception {
|
||||
|
||||
public function __construct( $message, WPML_TP_API_Request $request = null, $response = null ) {
|
||||
if ( $request ) {
|
||||
$message .= ' ' . $this->get_exception_message(
|
||||
$request->get_url(),
|
||||
$request->get_method(),
|
||||
$request->get_params(),
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
parent::__construct( $message );
|
||||
}
|
||||
|
||||
private function get_exception_message( $url, $method, $params, $response ) {
|
||||
return 'Details: |'
|
||||
. ' url: '
|
||||
. '`'
|
||||
. $url
|
||||
. '`'
|
||||
. ' method: '
|
||||
. '`'
|
||||
. $method
|
||||
. '`'
|
||||
. ' param: '
|
||||
. '`'
|
||||
. json_encode( $this->filter_params( $params ) )
|
||||
. '`'
|
||||
. ' response: '
|
||||
. '`'
|
||||
. json_encode( $response )
|
||||
. '`';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return array mixed
|
||||
*/
|
||||
private function filter_params( $params ) {
|
||||
return wpml_collect( $params )->forget( 'accesskey' )->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
interface WPML_TP_API_Log_Interface {
|
||||
|
||||
public function log( $action, $data = array() );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_API_Request {
|
||||
const API_VERSION = 1.1;
|
||||
|
||||
/** @var string */
|
||||
private $url;
|
||||
|
||||
/** @var array */
|
||||
private $params = array( 'api_version' => self::API_VERSION );
|
||||
|
||||
/** @var string */
|
||||
private $method = 'GET';
|
||||
|
||||
/** @var bool */
|
||||
private $has_api_response = true;
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*/
|
||||
public function __construct( $url ) {
|
||||
if ( empty( $url ) ) {
|
||||
throw new InvalidArgumentException( 'Url cannot be empty' );
|
||||
}
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*/
|
||||
public function set_params( array $params ) {
|
||||
$this->params = array_merge( $this->params, $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
*/
|
||||
public function set_method( $method ) {
|
||||
if ( ! in_array( $method, array( 'GET', 'POST', 'PUT', 'DELETE', 'HEAD' ), true ) ) {
|
||||
throw new InvalidArgumentException( 'HTTP request method has invalid value' );
|
||||
}
|
||||
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $has_api_response
|
||||
*/
|
||||
public function set_has_api_response( $has_api_response ) {
|
||||
$this->has_api_response = (bool) $has_api_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_url() {
|
||||
$url = $this->url;
|
||||
if ( $this->get_params() ) {
|
||||
list( $url, $params_used_in_path ) = $this->add_parameters_to_path( $url, $this->get_params() );
|
||||
if ( 'GET' === $this->get_method() ) {
|
||||
$url = $this->add_query_parameters( $params_used_in_path, $url );
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_params() {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_method() {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function has_api_response() {
|
||||
return $this->has_api_response;
|
||||
}
|
||||
|
||||
private function add_parameters_to_path( $url, array $params ) {
|
||||
$used_params = array();
|
||||
|
||||
if ( preg_match_all( '/\{.+?\}/', $url, $symbs ) ) {
|
||||
foreach ( $symbs[0] as $symb ) {
|
||||
$without_braces = preg_replace( '/\{|\}/', '', $symb );
|
||||
if ( preg_match_all( '/\w+/', $without_braces, $indexes ) ) {
|
||||
foreach ( $indexes[0] as $index ) {
|
||||
if ( isset( $params[ $index ] ) ) {
|
||||
$used_params[] = $index;
|
||||
|
||||
$value = $params[ $index ];
|
||||
$url = preg_replace( preg_quote( "/$symb/" ), $value, $url );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array( $url, $used_params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $params_used_in_path
|
||||
* @param $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function add_query_parameters( $params_used_in_path, $url ) {
|
||||
$url .= '?' . preg_replace(
|
||||
'/\%5B\d+\%5D/',
|
||||
'%5B%5D',
|
||||
wpml_http_build_query(
|
||||
array_diff_key(
|
||||
$this->get_params(),
|
||||
array_fill_keys( $params_used_in_path, 1 )
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TP_API {
|
||||
/** @var WPML_TP_API_Client */
|
||||
protected $client;
|
||||
|
||||
/** @var WPML_TP_Project */
|
||||
protected $project;
|
||||
|
||||
/** @var WPML_TP_API_Log_Interface */
|
||||
protected $logger;
|
||||
|
||||
public function __construct( WPML_TP_API_Client $client, WPML_TP_Project $project, WPML_TP_API_Log_Interface $logger = null ) {
|
||||
$this->client = $client;
|
||||
$this->project = $project;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
protected function log( $action, array $params = array() ) {
|
||||
if ( null !== $this->logger ) {
|
||||
$this->logger->log( $action, $params );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Batch_Sync_API extends WPML_TP_API {
|
||||
|
||||
const INIT_SYNC = '/batches/sync.json';
|
||||
const CHECK_STATUS = '/batches/sync/status.json';
|
||||
|
||||
/**
|
||||
* @param array $batch_ids
|
||||
*
|
||||
* @return int[]
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function init_synchronization( array $batch_ids ) {
|
||||
$request = new WPML_TP_API_Request( self::INIT_SYNC );
|
||||
$request->set_params(
|
||||
array(
|
||||
'batch_id' => $batch_ids,
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->handle_response( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function check_progress() {
|
||||
$request = new WPML_TP_API_Request( self::CHECK_STATUS );
|
||||
$request->set_params( array( 'accesskey' => $this->project->get_access_key() ) );
|
||||
|
||||
return $this->handle_response( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TP_API_Request $request
|
||||
*
|
||||
* @return array
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
private function handle_response( WPML_TP_API_Request $request ) {
|
||||
$result = $this->client->send_request( $request, true );
|
||||
if ( ! $result || empty( $result ) || ! isset( $result->queued_batches ) ) {
|
||||
throw new WPML_TP_API_Exception( 'Batch synchronization could not be initialized' );
|
||||
}
|
||||
|
||||
return array_map( 'intval', $result->queued_batches );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Job_States {
|
||||
|
||||
const RECEIVED = 'received';
|
||||
const WAITING_TRANSLATIONS = 'waiting_translation';
|
||||
const TRANSLATION_READY = 'translation_ready';
|
||||
const DELIVERED = 'delivered';
|
||||
const CANCELLED = 'cancelled';
|
||||
const ANY = 'any';
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_possible_states() {
|
||||
return array(
|
||||
self::RECEIVED,
|
||||
self::WAITING_TRANSLATIONS,
|
||||
self::TRANSLATION_READY,
|
||||
self::DELIVERED,
|
||||
self::CANCELLED,
|
||||
self::ANY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function get_default_state() {
|
||||
return self::WAITING_TRANSLATIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_finished_states() {
|
||||
return array(
|
||||
self::TRANSLATION_READY,
|
||||
self::DELIVERED,
|
||||
self::CANCELLED,
|
||||
);
|
||||
}
|
||||
|
||||
public static function map_tp_state_to_local( $tp_state ) {
|
||||
switch ( $tp_state ) {
|
||||
case self::TRANSLATION_READY:
|
||||
case self::DELIVERED:
|
||||
return ICL_TM_TRANSLATION_READY_TO_DOWNLOAD;
|
||||
case self::CANCELLED:
|
||||
return ICL_TM_NOT_TRANSLATED;
|
||||
default:
|
||||
return ICL_TM_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Job_Status {
|
||||
/** @var int */
|
||||
private $tp_id;
|
||||
|
||||
/** @var int */
|
||||
private $batch_id;
|
||||
|
||||
/** @var string */
|
||||
private $status;
|
||||
|
||||
/** @var int */
|
||||
private $revision;
|
||||
|
||||
/** @var WPML_TM_Job_TS_Status|null */
|
||||
private $ts_status;
|
||||
|
||||
/**
|
||||
* @param int $tp_id
|
||||
* @param int $batch_id
|
||||
* @param string $state
|
||||
* @param WPML_TM_Job_TS_Status|null $ts_status
|
||||
* @param int $revision
|
||||
*/
|
||||
public function __construct( $tp_id, $batch_id, $state, $revision = 1, $ts_status = null ) {
|
||||
$this->tp_id = (int) $tp_id;
|
||||
$this->batch_id = (int) $batch_id;
|
||||
|
||||
if ( ! in_array( $state, WPML_TP_Job_States::get_possible_states(), true ) ) {
|
||||
$state = 'any';
|
||||
}
|
||||
$this->status = $state;
|
||||
$this->revision = (int) $revision;
|
||||
$this->ts_status = $ts_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_tp_id() {
|
||||
return $this->tp_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_batch_id() {
|
||||
return $this->batch_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_status() {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_revision() {
|
||||
return $this->revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Job_TS_Status|null
|
||||
*/
|
||||
public function get_ts_status() {
|
||||
return $this->ts_status;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Jobs_API extends WPML_TP_API {
|
||||
|
||||
const CHUNK_SIZE = 100;
|
||||
|
||||
/**
|
||||
* @param int[] $tp_job_ids
|
||||
*
|
||||
* @return WPML_TP_Job_Status[]
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_jobs_statuses( array $tp_job_ids ) {
|
||||
$this->log( 'Get jobs status', $tp_job_ids );
|
||||
|
||||
$chunks = array();
|
||||
|
||||
while ( $tp_job_ids ) {
|
||||
$chunk_ids = array_splice( $tp_job_ids, 0, self::CHUNK_SIZE );
|
||||
$chunks[] = $this->get_chunk_of_job_statuses( $chunk_ids );
|
||||
}
|
||||
|
||||
$result = call_user_func_array( 'array_merge', $chunks );
|
||||
|
||||
return array_map( array( $this, 'build_job_status' ), $result );
|
||||
}
|
||||
|
||||
private function get_chunk_of_job_statuses( $tp_job_ids ) {
|
||||
$request = new WPML_TP_API_Request( '/jobs.json' );
|
||||
$request->set_params(
|
||||
array(
|
||||
'filter' => array(
|
||||
'job_ids' => array_filter( $tp_job_ids, 'is_int' ),
|
||||
'archived' => 1,
|
||||
),
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->client->send_request( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $cms_ids
|
||||
* @param bool $archived
|
||||
*
|
||||
* @return array|mixed|stdClass|string
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_jobs_per_cms_ids( array $cms_ids, $archived = false ) {
|
||||
$request = new WPML_TP_API_Request( '/jobs.json' );
|
||||
|
||||
$filter = array(
|
||||
'filter' => array(
|
||||
'cms_ids' => $cms_ids,
|
||||
),
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
);
|
||||
|
||||
if ( $archived ) {
|
||||
$filter['filter']['archived'] = (int) $archived;
|
||||
$request->set_params( $filter );
|
||||
}
|
||||
|
||||
return $this->client->send_request( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param string $state
|
||||
* @param string $post_url
|
||||
*
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function update_job_state(
|
||||
WPML_TM_Job_Entity $job,
|
||||
$state = WPML_TP_Job_States::DELIVERED,
|
||||
$post_url = null
|
||||
) {
|
||||
$params = array(
|
||||
'job_id' => $job->get_tp_id(),
|
||||
'project_id' => $this->project->get_id(),
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
'job' => array(
|
||||
'state' => $state,
|
||||
),
|
||||
);
|
||||
if ( $post_url ) {
|
||||
$params['job']['url'] = $post_url;
|
||||
}
|
||||
|
||||
$request = new WPML_TP_API_Request( '/jobs/{job_id}.json' );
|
||||
$request->set_params( $params );
|
||||
$request->set_method( 'PUT' );
|
||||
|
||||
$this->client->send_request( $request );
|
||||
}
|
||||
|
||||
private function build_job_status( stdClass $raw_data ) {
|
||||
return new WPML_TP_Job_Status(
|
||||
$raw_data->id,
|
||||
isset( $raw_data->batch->id ) ? $raw_data->batch->id : 0,
|
||||
$raw_data->job_state,
|
||||
isset( $raw_data->translation_revision ) ? $raw_data->translation_revision : 1,
|
||||
$raw_data->ts_status ? new WPML_TM_Job_TS_Status(
|
||||
$raw_data->ts_status->status,
|
||||
$raw_data->ts_status->links
|
||||
) : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TP_Job_Status[]
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_revised_jobs() {
|
||||
$this->log( 'Get revised jobs' );
|
||||
|
||||
$request = new WPML_TP_API_Request( '/jobs.json' );
|
||||
$request->set_params(
|
||||
array(
|
||||
'filter' => array(
|
||||
'job_state' => WPML_TP_Job_States::TRANSLATION_READY,
|
||||
'archived' => 0,
|
||||
'revision_greater_than' => 1,
|
||||
),
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
)
|
||||
);
|
||||
|
||||
$result = $this->client->send_request( $request );
|
||||
|
||||
return array_map( array( $this, 'build_job_status' ), $result );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\TranslationProxy\Services\Project\Project;
|
||||
use WPML\TM\TranslationProxy\Services\Project\SiteDetails;
|
||||
|
||||
class WPML_TP_Project_API extends WPML_TP_API {
|
||||
|
||||
const API_VERSION = 1.1;
|
||||
const PROJECTS_ENDPOINT = '/projects.json';
|
||||
|
||||
/**
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function refresh_language_pairs() {
|
||||
$this->log( 'Refresh language pairs -> Request sent' );
|
||||
|
||||
$request = new WPML_TP_API_Request( self::PROJECTS_ENDPOINT );
|
||||
$request->set_method( 'PUT' );
|
||||
$request->set_params(
|
||||
[
|
||||
'project' => [ 'refresh_language_pairs' => 1 ],
|
||||
'refresh_language_pairs' => 1,
|
||||
'project_id' => $this->project->get_id(),
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
]
|
||||
);
|
||||
|
||||
$this->client->send_request( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $service
|
||||
* @param SiteDetails $site_details
|
||||
*
|
||||
* @return stdClass
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function create_project( \stdClass $service, SiteDetails $site_details ) {
|
||||
$project_data = array_merge(
|
||||
$site_details->getBlogInfo(),
|
||||
[
|
||||
'delivery_method' => $site_details->getDeliveryMethod(),
|
||||
'sitekey' => WP_Installer_API::get_site_key( 'wpml' ),
|
||||
'client_external_id' => WP_Installer_API::get_ts_client_id(),
|
||||
]
|
||||
);
|
||||
|
||||
$params = [
|
||||
'api_version' => self::API_VERSION,
|
||||
'service' => [ 'id' => $service->id ],
|
||||
'project' => $project_data,
|
||||
'custom_fields' => $service->custom_fields_data,
|
||||
'client' => $site_details->getClientData(),
|
||||
'linked_by' => \TranslationProxy::get_service_linked_by_suid( $service->suid ),
|
||||
];
|
||||
|
||||
$request = new WPML_TP_API_Request( self::PROJECTS_ENDPOINT );
|
||||
$request->set_method( 'POST' );
|
||||
$request->set_params( $params );
|
||||
|
||||
return $this->client->send_request( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project $project
|
||||
* @param \stdClass $credentials
|
||||
*
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function update_project_credentials( Project $project, \stdClass $credentials ) {
|
||||
$request = new WPML_TP_API_Request( self::PROJECTS_ENDPOINT );
|
||||
$request->set_method( 'PUT' );
|
||||
$request->set_params(
|
||||
[
|
||||
'api_version' => self::API_VERSION,
|
||||
'accesskey' => $project->accessKey,
|
||||
'project' => [
|
||||
'custom_fields' => (array) $credentials,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->client->send_request( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project $project
|
||||
*
|
||||
* @return array|mixed|stdClass|string
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_extra_fields( Project $project ) {
|
||||
$params = [
|
||||
'accesskey' => $project->accessKey,
|
||||
'api_version' => self::API_VERSION,
|
||||
'project_id' => $project->id,
|
||||
];
|
||||
|
||||
$request = new WPML_TP_API_Request( '/projects/{project_id}/extra_fields.json' );
|
||||
$request->set_method( 'GET' );
|
||||
$request->set_params( $params );
|
||||
|
||||
return $this->client->send_request( $request );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Services {
|
||||
public function get_current_project() {
|
||||
return TranslationProxy::get_current_project();
|
||||
}
|
||||
|
||||
public function get_current_service() {
|
||||
return TranslationProxy::get_current_service();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $service_id
|
||||
* @param bool $custom_fields
|
||||
*
|
||||
* @throws WPMLTranslationProxyApiException
|
||||
*/
|
||||
public function select_service( $service_id, $custom_fields = false ) {
|
||||
TranslationProxy::select_service( $service_id, $custom_fields );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_XLIFF_API extends WPML_TP_API {
|
||||
/** @var WPML_TP_Xliff_Parser */
|
||||
private $xliff_parser;
|
||||
|
||||
public function __construct(
|
||||
WPML_TP_API_Client $client,
|
||||
WPML_TP_Project $project,
|
||||
WPML_TP_API_Log_Interface $logger,
|
||||
WPML_TP_Xliff_Parser $xliff_parser
|
||||
) {
|
||||
parent::__construct( $client, $project, $logger );
|
||||
$this->xliff_parser = $xliff_parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $tp_job_id
|
||||
* @param bool $parse
|
||||
*
|
||||
* @return WPML_TP_Translation_Collection|string
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_remote_translations( $tp_job_id, $parse = true ) {
|
||||
$request = new WPML_TP_API_Request( '/jobs/{job_id}/xliff.json' );
|
||||
$request->set_params(
|
||||
array(
|
||||
'job_id' => $tp_job_id,
|
||||
'accesskey' => $this->project->get_access_key(),
|
||||
)
|
||||
);
|
||||
|
||||
$result = $this->client->send_request( $request );
|
||||
if ( ! $result || empty( $result ) || false === strpos( $result, 'xliff' ) ) {
|
||||
throw new WPML_TP_API_Exception( 'XLIFF file could not be fetched for tp_job: ' . $tp_job_id, $request );
|
||||
}
|
||||
|
||||
$result = apply_filters( 'wpml_tm_data_from_pro_translation', $result );
|
||||
if ( ! $parse ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$xliff = @simplexml_load_string( $result );
|
||||
if ( ! $xliff ) {
|
||||
throw new WPML_TP_API_Exception( 'XLIFF file could not be parsed.' );
|
||||
}
|
||||
|
||||
return $this->xliff_parser->parse( $xliff );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Xliff_Parser {
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $xliff
|
||||
*
|
||||
* @return WPML_TP_Translation_Collection
|
||||
*/
|
||||
public function parse( SimpleXMLElement $xliff ) {
|
||||
$source_lang = (string) $xliff->file->attributes()->{'source-language'};
|
||||
$target_lang = (string) $xliff->file->attributes()->{'target-language'};
|
||||
|
||||
$translations = array();
|
||||
foreach ( $xliff->file->body->children() as $node ) {
|
||||
$translations[] = new WPML_TP_Translation(
|
||||
(string) $node->attributes()->id,
|
||||
$this->get_cdata_value( $node, 'source' ),
|
||||
$this->get_cdata_value( $node, 'target' )
|
||||
);
|
||||
}
|
||||
|
||||
return new WPML_TP_Translation_Collection(
|
||||
$translations,
|
||||
$source_lang,
|
||||
$target_lang
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $xliff_node
|
||||
* @param string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_cdata_value( SimpleXMLElement $xliff_node, $field ) {
|
||||
$value = '';
|
||||
if ( isset( $xliff_node->$field->mrk ) ) {
|
||||
$value = (string) $xliff_node->$field->mrk;
|
||||
} elseif ( isset( $xliff_node->$field ) ) {
|
||||
$value = (string) $xliff_node->$field;
|
||||
}
|
||||
|
||||
return self::restore_new_line( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function restore_new_line( $string ) {
|
||||
return preg_replace( '/<br class="xliff-newline"\s*\/>/i', "\n", $string );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPMLTranslationProxyApiException extends Exception {
|
||||
|
||||
public function __construct( $message, $code = 0 ) {
|
||||
WPML_TranslationProxy_Com_Log::log_error( $message );
|
||||
|
||||
parent::__construct( $message, $code );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TM_CMS_ID
|
||||
*/
|
||||
class WPML_TM_CMS_ID extends WPML_TM_Record_User {
|
||||
|
||||
private $cms_id_parts_glue = '_';
|
||||
private $cms_id_parts_fallback_glue = '|||';
|
||||
|
||||
/** @var WPML_Translation_Job_Factory $tm_job_factory */
|
||||
private $job_factory;
|
||||
|
||||
/** @var wpdb $wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* WPML_TM_CMS_ID constructor.
|
||||
*
|
||||
* @param WPML_TM_Records $tm_records
|
||||
* @param WPML_Translation_Job_Factory $job_factory
|
||||
*/
|
||||
public function __construct( &$tm_records, &$job_factory ) {
|
||||
parent::__construct( $tm_records );
|
||||
$this->job_factory = &$job_factory;
|
||||
$this->wpdb = $this->tm_records->wpdb();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
* @param string $post_type
|
||||
* @param string $source_language
|
||||
* @param string $target_language
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function build_cms_id( $post_id, $post_type, $source_language, $target_language ) {
|
||||
$cms_id_parts = array( $post_type, $post_id, $source_language, $target_language );
|
||||
|
||||
return implode( $this->cms_id_parts_glue, $cms_id_parts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cms_id for a given job
|
||||
*
|
||||
* @param int $job_id
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
function cms_id_from_job_id( $job_id ) {
|
||||
$original_element_row = $this->wpdb->get_row(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT o.element_id,
|
||||
o.element_type,
|
||||
o.language_code as source_lang,
|
||||
i.language_code as target_lang
|
||||
FROM {$this->wpdb->prefix}icl_translations o
|
||||
JOIN {$this->wpdb->prefix}icl_translations i
|
||||
ON i.trid = o.trid
|
||||
AND i.source_language_code = o.language_code
|
||||
JOIN {$this->wpdb->prefix}icl_translation_status s
|
||||
ON s.translation_id = i.translation_id
|
||||
JOIN {$this->wpdb->prefix}icl_translate_job j
|
||||
ON j.rid = s.rid
|
||||
WHERE j.job_id = %d
|
||||
LIMIT 1",
|
||||
$job_id
|
||||
)
|
||||
);
|
||||
|
||||
$type_parts = (bool) $original_element_row === true ? explode( '_', $original_element_row->element_type, 2 ) : false;
|
||||
|
||||
return is_array( $type_parts ) && count( $type_parts ) === 2
|
||||
? $this->build_cms_id( $original_element_row->element_id, end( $type_parts ), $original_element_row->source_lang, $original_element_row->target_lang )
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cms_id
|
||||
*
|
||||
* @return array;
|
||||
*/
|
||||
public function parse_cms_id( $cms_id ) {
|
||||
if ( $this->is_standard_format( $cms_id ) ) {
|
||||
$parts = array_filter( explode( $this->cms_id_parts_glue, $cms_id ) );
|
||||
while ( count( $parts ) > 4 ) {
|
||||
$parts_copy = $parts;
|
||||
$parts[0] = $parts_copy[0] . $this->cms_id_parts_glue . $parts_copy[1];
|
||||
unset( $parts_copy[0] );
|
||||
unset( $parts_copy[1] );
|
||||
$parts = array_merge( array( $parts[0] ), array_values( array_filter( $parts_copy ) ) );
|
||||
}
|
||||
} else {
|
||||
$parts = explode( $this->cms_id_parts_fallback_glue, $cms_id );
|
||||
}
|
||||
|
||||
return array_pad( array_slice( $parts, 0, 4 ), false, 4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cms_id
|
||||
* @param bool|TranslationProxy_Service $translation_service
|
||||
*
|
||||
* @return int|null translation id for the given cms_id's target
|
||||
*/
|
||||
public function get_translation_id( $cms_id, $translation_service = false ) {
|
||||
list( $post_type, $element_id, , $target_lang ) = $this->parse_cms_id( $cms_id );
|
||||
$translation = $this->wpdb->get_row(
|
||||
$this->wpdb->prepare(
|
||||
"
|
||||
SELECT t.translation_id, j.job_id, t.element_id
|
||||
FROM {$this->wpdb->prefix}icl_translations t
|
||||
JOIN {$this->wpdb->prefix}icl_translations o
|
||||
ON o.trid = t.trid
|
||||
AND o.element_type = t.element_type
|
||||
LEFT JOIN {$this->wpdb->prefix}icl_translation_status st
|
||||
ON st.translation_id = t.translation_id
|
||||
LEFT JOIN {$this->wpdb->prefix}icl_translate_job j
|
||||
ON j.rid = st.rid
|
||||
WHERE o.element_id=%d
|
||||
AND t.language_code=%s
|
||||
AND o.element_type LIKE %s
|
||||
LIMIT 1",
|
||||
$element_id,
|
||||
$target_lang,
|
||||
'%_' . $post_type
|
||||
)
|
||||
);
|
||||
$translation_id = $this->maybe_cleanup_broken_row( $translation, $translation_service );
|
||||
if ( $translation_service && ! isset( $translation_id ) && $translation_service ) {
|
||||
$job_id = $this->job_factory->create_local_post_job( $element_id, $target_lang );
|
||||
$job = $this->job_factory->get_translation_job( $job_id, false, false, true );
|
||||
$translation_id = $job ? $job->get_translation_id() : 0;
|
||||
if ( $translation_id ) {
|
||||
$this->tm_records->icl_translation_status_by_translation_id( $translation_id )->update(
|
||||
array(
|
||||
'status' => ICL_TM_IN_PROGRESS,
|
||||
'translation_service' => $translation_service->id,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $translation_id;
|
||||
}
|
||||
|
||||
private function maybe_cleanup_broken_row( $translation, $translation_service ) {
|
||||
if ( $translation
|
||||
&& ( $translation_id = $translation->translation_id )
|
||||
&& ! $translation->element_id
|
||||
&& $translation_service
|
||||
&& ! $translation->job_id
|
||||
) {
|
||||
$this->tm_records->icl_translations_by_translation_id( $translation_id )->delete();
|
||||
$translation_id = null;
|
||||
}
|
||||
|
||||
return isset( $translation_id ) ? $translation_id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cms_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_standard_format( $cms_id ) {
|
||||
|
||||
return count( array_filter( explode( $this->cms_id_parts_fallback_glue, $cms_id ) ) ) < 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_HTTP_Request_Filter {
|
||||
|
||||
/**
|
||||
* @return array filtered response
|
||||
*/
|
||||
public function build_request_context( array $request ) {
|
||||
if ( ! $this->contains_resource( $request ) ) {
|
||||
$request['headers'] = 'Content-type: application/json';
|
||||
$request['body'] = wp_json_encode( $request['body'] );
|
||||
} else {
|
||||
list( $headers, $body ) = $this->_prepare_multipart_request( $request['body'] );
|
||||
$request['headers'] = $headers;
|
||||
$request['body'] = $body;
|
||||
}
|
||||
|
||||
if ( $request['method'] === 'GET' ) {
|
||||
unset( $request['body'] );
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a request contains a file resource handle
|
||||
*
|
||||
* @param array $request_snippet
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function contains_resource( array $request_snippet ) {
|
||||
foreach ( $request_snippet as $part ) {
|
||||
if ( is_resource( $part ) === true || ( is_array( $part ) && $this->contains_resource( $part ) ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function _prepare_multipart_request( $params ) {
|
||||
$boundary = '----' . microtime();
|
||||
$header = "Content-Type: multipart/form-data; boundary=$boundary";
|
||||
$content = self::_add_multipart_contents( $boundary, $params );
|
||||
$content .= "--$boundary--\r\n";
|
||||
|
||||
return array( $header, $content );
|
||||
}
|
||||
|
||||
private function _add_multipart_contents(
|
||||
$boundary,
|
||||
$params,
|
||||
$context = array()
|
||||
) {
|
||||
$initial_context = $context;
|
||||
$content = '';
|
||||
|
||||
foreach ( $params as $key => $value ) {
|
||||
$context = $initial_context;
|
||||
$context[] = $key;
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
$content .= self::_add_multipart_contents(
|
||||
$boundary,
|
||||
$value,
|
||||
$context
|
||||
);
|
||||
} else {
|
||||
$pieces = array_slice( $context, 1 );
|
||||
if ( $pieces ) {
|
||||
$name = "{$context[0]}[" . implode( '][', $pieces ) . ']';
|
||||
} else {
|
||||
$name = "{$context[0]}";
|
||||
}
|
||||
|
||||
$content .= "--$boundary\r\n" . "Content-Disposition: form-data; name=\"$name\"";
|
||||
|
||||
if ( is_resource( $value ) ) {
|
||||
$filename = self::get_file_name( $params, $key );
|
||||
$content .= "; filename=\"$filename\"\r\n" . "Content-Type: application/octet-stream\r\n\r\n" . gzencode( stream_get_contents( $value ) ) . "\r\n";
|
||||
} else {
|
||||
$content .= "\r\n\r\n$value\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function get_file_name( $params, $default = 'file' ) {
|
||||
|
||||
$title = isset( $params['title'] ) ? sanitize_title_with_dashes(
|
||||
strtolower(
|
||||
filter_var(
|
||||
$params['title'],
|
||||
FILTER_SANITIZE_FULL_SPECIAL_CHARS,
|
||||
FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH
|
||||
)
|
||||
)
|
||||
)
|
||||
: '';
|
||||
if ( str_replace( array( '-', '_' ), '', $title ) == '' ) {
|
||||
$title = $default;
|
||||
}
|
||||
$source_language = isset( $params['source_language'] ) ? $params['source_language'] : '';
|
||||
$target_language = isset( $params['target_language'] ) ? $params['target_language'] : '';
|
||||
|
||||
$filename = implode(
|
||||
'-',
|
||||
array_filter(
|
||||
array(
|
||||
$title,
|
||||
$source_language,
|
||||
$target_language,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $filename . '.xliff.gz';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TP_Project_User {
|
||||
|
||||
/** @var TranslationProxy_Project $project */
|
||||
protected $project;
|
||||
|
||||
/**
|
||||
* WPML_TP_Project_User constructor.
|
||||
*
|
||||
* @param TranslationProxy_Project $project
|
||||
*/
|
||||
public function __construct( &$project ) {
|
||||
$this->project = &$project;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Refresh_Language_Pairs {
|
||||
|
||||
const AJAX_ACTION = 'wpml-tp-refresh-language-pairs';
|
||||
|
||||
/**
|
||||
* @var WPML_TP_Project_API
|
||||
*/
|
||||
private $tp_api;
|
||||
|
||||
/**
|
||||
* WPML_TP_AJAX constructor.
|
||||
*
|
||||
* @param WPML_TP_Project_API $wpml_tp_api
|
||||
*/
|
||||
public function __construct( WPML_TP_Project_API $wpml_tp_api ) {
|
||||
$this->tp_api = $wpml_tp_api;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'refresh_language_pairs' ) );
|
||||
}
|
||||
|
||||
public function refresh_language_pairs() {
|
||||
if ( $this->is_valid_request() ) {
|
||||
try {
|
||||
$this->tp_api->refresh_language_pairs();
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'msg' => __( 'Language pairs refreshed', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'msg' => __( 'Language pairs not refreshed, please try again', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'msg' => __( 'Invalid Request', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_request() {
|
||||
return array_key_exists( 'nonce', $_POST ) &&
|
||||
wp_verify_nonce( filter_var( $_POST['nonce'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ), self::AJAX_ACTION );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_String_Job extends WPML_WPDB_User {
|
||||
|
||||
/** @var WPML_Translation_Basket $basket */
|
||||
private $basket;
|
||||
|
||||
/** @var WPML_Translation_Job_Factory $job_factory */
|
||||
private $job_factory;
|
||||
|
||||
/**
|
||||
* WPML_TP_String_Job constructor.
|
||||
*
|
||||
* @param wpdb $wpdb
|
||||
* @param WPML_Translation_Basket $basket
|
||||
* @param WPML_Translation_Job_Factory $job_factory
|
||||
*/
|
||||
public function __construct( &$wpdb, &$basket, &$job_factory ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->basket = &$basket;
|
||||
$this->job_factory = &$job_factory;
|
||||
}
|
||||
|
||||
function send_strings_to_translation_service( $string_ids, $target_language, $translator_id ) {
|
||||
/** @var WPML_String_Translation $WPML_String_Translation */
|
||||
global $WPML_String_Translation;
|
||||
|
||||
if ( sizeof( $string_ids ) > 0 ) {
|
||||
$project = $this->basket->get_project();
|
||||
$strings = array();
|
||||
$word_count = 0;
|
||||
$source_language = $this->basket->get_source_language();
|
||||
foreach ( $string_ids as $string_id ) {
|
||||
$string_data_query = "SELECT id, context, name, value FROM {$this->wpdb->prefix}icl_strings WHERE id=%d";
|
||||
$string_data_prepare = $this->wpdb->prepare( $string_data_query, $string_id );
|
||||
$string_data = $this->wpdb->get_row( $string_data_prepare );
|
||||
$word_count += $WPML_String_Translation->estimate_word_count( $string_data->value, $source_language );
|
||||
$strings[] = $string_data;
|
||||
}
|
||||
$xliff = new WPML_TM_Xliff_Writer( $this->job_factory );
|
||||
try {
|
||||
$tp_job_id = $project->send_to_translation_batch_mode(
|
||||
$xliff->get_strings_xliff_file(
|
||||
$strings,
|
||||
$source_language,
|
||||
$target_language
|
||||
),
|
||||
'String Translations',
|
||||
'',
|
||||
'',
|
||||
$source_language,
|
||||
$target_language,
|
||||
$word_count
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
return [ 'errors' => [ $e->getMessage() ] ];
|
||||
}
|
||||
|
||||
if ( $tp_job_id ) {
|
||||
foreach ( $strings as $string_data ) {
|
||||
$translation_service = TranslationProxy_Service::get_translator_data_from_wpml( $translator_id );
|
||||
$string_translation_id = icl_add_string_translation(
|
||||
$string_data->id,
|
||||
$target_language,
|
||||
null,
|
||||
ICL_TM_IN_PROGRESS,
|
||||
$translation_service['translator_id'],
|
||||
$translation_service['translation_service'],
|
||||
TranslationProxy_Batch::update_translation_batch(
|
||||
$this->basket->get_name()
|
||||
)
|
||||
);
|
||||
if ( $string_translation_id ) {
|
||||
$data = array(
|
||||
'rid' => $tp_job_id,
|
||||
'string_translation_id' => $string_translation_id,
|
||||
'timestamp' => date( 'Y-m-d H:i:s' ),
|
||||
'md5' => md5( $string_data->value ),
|
||||
);
|
||||
|
||||
$current_rid = $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT rid FROM {$this->wpdb->prefix}icl_string_status WHERE string_translation_id=%d",
|
||||
$string_translation_id
|
||||
)
|
||||
);
|
||||
if ( $current_rid ) {
|
||||
$this->wpdb->update( $this->wpdb->prefix . 'icl_string_status', $data, array( 'rid' => $current_rid ) );
|
||||
} else {
|
||||
$this->wpdb->insert( $this->wpdb->prefix . 'icl_string_status', $data ); // insert rid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'rid' => $tp_job_id,
|
||||
'module' => '',
|
||||
'origin' => $source_language,
|
||||
'target' => $target_language,
|
||||
'status' => ICL_TM_IN_PROGRESS,
|
||||
);
|
||||
if ( ! empty( $current_rid ) ) {
|
||||
$data['ts_status'] = null;
|
||||
$this->wpdb->update( $this->wpdb->prefix . 'icl_core_status', $data, array( 'rid' => $current_rid ) );
|
||||
} else {
|
||||
$this->wpdb->insert( $this->wpdb->prefix . 'icl_core_status', $data );
|
||||
}
|
||||
|
||||
if ( $project->errors && count( $project->errors ) ) {
|
||||
$tp_job_id['errors'] = $project->errors;
|
||||
}
|
||||
|
||||
return $tp_job_id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_TP_Translator
|
||||
*/
|
||||
class WPML_TP_Translator {
|
||||
|
||||
/**
|
||||
* Return translator status array.
|
||||
*
|
||||
* @param bool $force
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_icl_translator_status( $force = false ) {
|
||||
return TranslationProxy_Translator::get_icl_translator_status( $force );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Proxy_Networking {
|
||||
|
||||
const API_VERSION = 1.1;
|
||||
|
||||
/** @var WP_Http $http */
|
||||
private $http;
|
||||
|
||||
/** @var WPML_TP_Lock $tp_lock */
|
||||
private $tp_lock;
|
||||
|
||||
public function __construct( WP_Http $http, WPML_TP_Lock $tp_lock ) {
|
||||
$this->http = $http;
|
||||
$this->tp_lock = $tp_lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @param string $method
|
||||
* @param bool|true $has_return_value
|
||||
* @param bool|true $json_response
|
||||
* @param bool|true $has_api_response
|
||||
*
|
||||
* @return array|mixed|stdClass|string
|
||||
* @throws WPMLTranslationProxyApiException
|
||||
*/
|
||||
public function send_request(
|
||||
$url,
|
||||
$params = array(),
|
||||
$method = 'GET',
|
||||
$has_return_value = true,
|
||||
$json_response = true,
|
||||
$has_api_response = true
|
||||
) {
|
||||
if ( $this->tp_lock->is_locked( $url ) ) {
|
||||
throw new WPMLTranslationProxyApiException( 'Communication with translation proxy is not allowed.' );
|
||||
}
|
||||
|
||||
if ( ! $url ) {
|
||||
throw new WPMLTranslationProxyApiException( 'Empty target URL given!' );
|
||||
}
|
||||
|
||||
$response = null;
|
||||
$method = strtoupper( $method );
|
||||
|
||||
if ( $params ) {
|
||||
$url = TranslationProxy_Api::add_parameters_to_url( $url, $params );
|
||||
if ( 'GET' === $method ) {
|
||||
$url .= '?' . wpml_http_build_query( $params );
|
||||
}
|
||||
}
|
||||
if ( ! isset( $params['api_version'] ) || ! $params['api_version'] ) {
|
||||
$params['api_version'] = self::API_VERSION;
|
||||
}
|
||||
|
||||
WPML_TranslationProxy_Com_Log::log_call( $url, $params );
|
||||
$api_response = $this->call_remote_api( $url, $params, $method, $has_return_value );
|
||||
|
||||
if ( $has_return_value ) {
|
||||
if ( ! isset( $api_response['headers']['content-type'] ) ) {
|
||||
throw new WPMLTranslationProxyApiException( 'Invalid HTTP response, no content type in header given!' );
|
||||
}
|
||||
$content_type = $api_response['headers']['content-type'];
|
||||
$api_response = $api_response['body'];
|
||||
$api_response = strpos( $content_type, 'zip' ) !== false ? gzdecode( $api_response ) : $api_response;
|
||||
WPML_TranslationProxy_Com_Log::log_response( $json_response ? $api_response : 'XLIFF received' );
|
||||
if ( $json_response ) {
|
||||
$response = json_decode( $api_response );
|
||||
if ( $has_api_response ) {
|
||||
if ( ! $response || ! isset( $response->status->code ) || $response->status->code !== 0 ) {
|
||||
$exception_message = $this->get_exception_message(
|
||||
$url,
|
||||
$method,
|
||||
$params,
|
||||
$response
|
||||
);
|
||||
if ( isset( $response->status->message ) ) {
|
||||
$exception_message = '';
|
||||
if ( isset( $response->status->code ) ) {
|
||||
$exception_message = '(' . $response->status->code . ') ';
|
||||
}
|
||||
$exception_message .= $response->status->message;
|
||||
}
|
||||
throw new WPMLTranslationProxyApiException( $exception_message );
|
||||
}
|
||||
$response = $response->response;
|
||||
}
|
||||
} else {
|
||||
$response = $api_response;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function get_extra_fields_remote( $project ) {
|
||||
|
||||
$params = array(
|
||||
'accesskey' => $project->access_key,
|
||||
'api_version' => self::API_VERSION,
|
||||
'project_id' => $project->id,
|
||||
);
|
||||
|
||||
return TranslationProxy_Api::proxy_request( '/projects/{project_id}/extra_fields.json', $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @param string $method
|
||||
* @param bool $has_return_value
|
||||
*
|
||||
* @throws \WPMLTranslationProxyApiException
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function call_remote_api(
|
||||
$url,
|
||||
$params,
|
||||
$method,
|
||||
$has_return_value = true
|
||||
) {
|
||||
$context = $this->filter_request_params( $params, $method );
|
||||
$response = $this->http->request( $url, $context );
|
||||
if ( ( $has_return_value && (bool) $response === false )
|
||||
|| is_wp_error( $response )
|
||||
|| ( isset( $response['response']['code'] ) && $response['response']['code'] > 400 ) ) {
|
||||
throw new WPMLTranslationProxyApiException(
|
||||
$this->get_exception_message(
|
||||
$url,
|
||||
$method,
|
||||
$context,
|
||||
$response
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function get_exception_message( $url, $method, $context, $response ) {
|
||||
$sanitized_url = WPML_TranslationProxy_Com_Log::sanitize_url( $url );
|
||||
$sanitized_context = WPML_TranslationProxy_Com_Log::sanitize_data( $context );
|
||||
$sanitized_response = WPML_TranslationProxy_Com_Log::sanitize_data( $response );
|
||||
|
||||
return 'Cannot communicate with the remote service |'
|
||||
. ' url: '
|
||||
. '`'
|
||||
. $sanitized_url
|
||||
. '`'
|
||||
. ' method: '
|
||||
. '`'
|
||||
. $method
|
||||
. '`'
|
||||
. ' param: '
|
||||
. '`'
|
||||
. wp_json_encode( $sanitized_context )
|
||||
. '`'
|
||||
. ' response: '
|
||||
. '`'
|
||||
. wp_json_encode( $sanitized_response )
|
||||
. '`';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params request parameters
|
||||
* @param string $method HTTP request method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filter_request_params( $params, $method ) {
|
||||
$request_filter = new WPML_TP_HTTP_Request_Filter();
|
||||
|
||||
return $request_filter->build_request_context(
|
||||
array(
|
||||
'method' => $method,
|
||||
'body' => $params,
|
||||
'sslverify' => true,
|
||||
'timeout' => 60,
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
class WPML_TranslationProxy_Com_Log {
|
||||
private static $wrapped_class;
|
||||
|
||||
/**
|
||||
* @return WPML_TranslationProxy_Communication_Log
|
||||
*/
|
||||
private static function get_wrapped_class_instance() {
|
||||
if ( null === self::$wrapped_class ) {
|
||||
global $sitepress;
|
||||
self::$wrapped_class = new WPML_TranslationProxy_Communication_Log( $sitepress );
|
||||
}
|
||||
|
||||
return self::$wrapped_class;
|
||||
}
|
||||
|
||||
public static function log_call( $url, $params ) {
|
||||
self::get_wrapped_class_instance()->log_call( $url, $params );
|
||||
}
|
||||
|
||||
public static function get_keys_to_block() {
|
||||
return self::get_wrapped_class_instance()->get_keys_to_block();
|
||||
}
|
||||
|
||||
public static function log_response( $response ) {
|
||||
self::get_wrapped_class_instance()->log_response( $response );
|
||||
}
|
||||
|
||||
public static function log_error( $message ) {
|
||||
self::get_wrapped_class_instance()->log_error( $message );
|
||||
}
|
||||
|
||||
public static function log_xml_rpc( $data ) {
|
||||
self::get_wrapped_class_instance()->log_xml_rpc( $data );
|
||||
}
|
||||
|
||||
public static function get_log() {
|
||||
return self::get_wrapped_class_instance()->get_log();
|
||||
}
|
||||
|
||||
public static function clear_log() {
|
||||
self::get_wrapped_class_instance()->clear_log();
|
||||
}
|
||||
|
||||
public static function is_logging_enabled() {
|
||||
return self::get_wrapped_class_instance()->is_logging_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array|stdClass $params
|
||||
*
|
||||
* @return array|stdClass
|
||||
*/
|
||||
public static function sanitize_data( $params ) {
|
||||
return self::get_wrapped_class_instance()->sanitize_data( $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function sanitize_url( $url ) {
|
||||
return self::get_wrapped_class_instance()->sanitize_url( $url );
|
||||
}
|
||||
|
||||
public static function set_logging_state( $state ) {
|
||||
self::get_wrapped_class_instance()->set_logging_state( $state );
|
||||
}
|
||||
|
||||
public static function add_com_log_link() {
|
||||
self::get_wrapped_class_instance()->add_com_log_link();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
class WPML_Update_PickUp_Method {
|
||||
|
||||
private $sitepress;
|
||||
|
||||
public function __construct( $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
public function update_pickup_method( $data, $project = false ) {
|
||||
$method = isset( $data['icl_translation_pickup_method'] ) ? intval( $data['icl_translation_pickup_method'] ) : null;
|
||||
$iclsettings['translation_pickup_method'] = $method;
|
||||
$response = 'ok';
|
||||
try {
|
||||
if ( $project ) {
|
||||
$project->set_delivery_method( ICL_PRO_TRANSLATION_PICKUP_XMLRPC == $method ? 'xmlrpc' : 'polling' );
|
||||
$this->sitepress->save_settings( $iclsettings );
|
||||
} elseif ( ICL_PRO_TRANSLATION_PICKUP_XMLRPC == $method ) {
|
||||
$response = 'no-ts';
|
||||
}
|
||||
} catch ( RuntimeException $e ) {
|
||||
$response = 'cant-update';
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Lock_Factory {
|
||||
|
||||
public function create() {
|
||||
return new WPML_TP_Lock( new WPML_WP_API() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Lock_Notice_Factory implements IWPML_Backend_Action_Loader {
|
||||
|
||||
public function create() {
|
||||
$tp_lock_factory = new WPML_TP_Lock_Factory();
|
||||
$notices = wpml_get_admin_notices();
|
||||
return new WPML_TP_Lock_Notice( $tp_lock_factory->create(), $notices );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Lock_Notice implements IWPML_Action {
|
||||
|
||||
const NOTICE_GROUP = 'tp-lock';
|
||||
const NOTICE_LOCKED = 'locked';
|
||||
|
||||
/** @var WPML_TP_Lock $tp_lock */
|
||||
private $tp_lock;
|
||||
|
||||
/** @var WPML_Notices $notices */
|
||||
private $notices;
|
||||
|
||||
public function __construct( WPML_TP_Lock $tp_lock, WPML_Notices $notices ) {
|
||||
$this->tp_lock = $tp_lock;
|
||||
$this->notices = $notices;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'admin_init', array( $this, 'handle_notice' ) );
|
||||
}
|
||||
|
||||
public function handle_notice() {
|
||||
$locker_reason = $this->tp_lock->get_locker_reason();
|
||||
|
||||
if ( (bool) $locker_reason ) {
|
||||
$text = '<p>' . __( 'Some communications with the translation proxy are locked.', 'wpml-translation-management' ) . '</p>';
|
||||
$text .= '<p>' . $locker_reason . '</p>';
|
||||
$notice = $this->notices->create_notice( self::NOTICE_LOCKED, $text, self::NOTICE_GROUP );
|
||||
$notice->set_css_class_types( 'notice-warning' );
|
||||
$this->notices->add_notice( $notice );
|
||||
} else {
|
||||
$this->notices->remove_notice( self::NOTICE_GROUP, self::NOTICE_LOCKED );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Lock {
|
||||
|
||||
private $lockable_endpoints = array(
|
||||
'/jobs/{job_id}/xliff.json',
|
||||
);
|
||||
|
||||
/** @var WPML_WP_API $wp_api */
|
||||
private $wp_api;
|
||||
|
||||
public function __construct( WPML_WP_API $wp_api ) {
|
||||
$this->wp_api = $wp_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked( $url ) {
|
||||
return $this->get_locker_reason() && $this->is_lockable( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public function get_locker_reason() {
|
||||
if ( 'test' === $this->wp_api->constant( 'WPML_ENVIRONMENT' ) ) {
|
||||
return __( 'The constant WPML_ENVIRONMENT is set to "Test".', 'wpml-translation-management' );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_lockable( $url ) {
|
||||
$endpoint = preg_replace( '#^' . OTG_TRANSLATION_PROXY_URL . '#', '', $url, 1 );
|
||||
return in_array( $endpoint, $this->lockable_endpoints, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
class WPML_TranslationProxy_Communication_Log {
|
||||
private $keys_to_block;
|
||||
private $sitepress;
|
||||
|
||||
public function __construct( SitePress $sitepress ) {
|
||||
$this->keys_to_block = array(
|
||||
'api_token',
|
||||
'username',
|
||||
'api_key',
|
||||
'sitekey',
|
||||
'accesskey',
|
||||
'file',
|
||||
);
|
||||
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
public function log_call( $url, $params ) {
|
||||
$sanitized_params = $this->sanitize_data( $params );
|
||||
$sanitized_url = $this->sanitize_url( $url );
|
||||
|
||||
$this->add_to_log( 'call - ' . $sanitized_url . ' - ' . json_encode( $sanitized_params ) );
|
||||
}
|
||||
|
||||
public function get_keys_to_block() {
|
||||
return $this->keys_to_block;
|
||||
}
|
||||
|
||||
public function log_response( $response ) {
|
||||
$this->add_to_log( 'response - ' . $response );
|
||||
}
|
||||
|
||||
public function log_error( $message ) {
|
||||
$this->add_to_log( 'error - ' . $message );
|
||||
}
|
||||
|
||||
public function log_xml_rpc( $data ) {
|
||||
$this->add_to_log( 'xml-rpc - ' . json_encode( $data ) );
|
||||
}
|
||||
|
||||
public function get_log() {
|
||||
return get_option( 'wpml_tp_com_log', '' );
|
||||
}
|
||||
|
||||
public function clear_log() {
|
||||
$this->save_log( '' );
|
||||
}
|
||||
|
||||
public function is_logging_enabled() {
|
||||
return $this->sitepress->get_setting( 'tp-com-logging', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array|stdClass $params
|
||||
*
|
||||
* @return array|stdClass
|
||||
*/
|
||||
public function sanitize_data( $params ) {
|
||||
$sanitized_params = $params;
|
||||
|
||||
if ( is_object( $sanitized_params ) ) {
|
||||
$sanitized_params = get_object_vars( $sanitized_params );
|
||||
}
|
||||
|
||||
if ( is_array( $sanitized_params ) ) {
|
||||
foreach ( $sanitized_params as $key => $value ) {
|
||||
$sanitized_params[ $key ] = $this->sanitize_data_item( $key, $sanitized_params[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string|array|stdClass $item
|
||||
*
|
||||
* @return string|array|stdClass
|
||||
*/
|
||||
private function sanitize_data_item( $key, $item ) {
|
||||
if ( is_array( $item ) || is_object( $item ) ) {
|
||||
$item = $this->sanitize_data( $item );
|
||||
} elseif ( in_array( $key, $this->get_keys_to_block(), true ) ) {
|
||||
$item = 'UNDISCLOSED';
|
||||
} elseif ( $this->is_json( $item ) ) {
|
||||
$item = json_encode( $this->sanitize_data_item( $key, json_decode( $item ) ) );
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function sanitize_url( $url ) {
|
||||
$original_url_parsed = (string) wpml_parse_url( $url, PHP_URL_QUERY );
|
||||
parse_str( $original_url_parsed, $original_query_vars );
|
||||
|
||||
$sanitized_query_vars = $this->sanitize_data( $original_query_vars );
|
||||
|
||||
return add_query_arg( $sanitized_query_vars, $url );
|
||||
}
|
||||
|
||||
public function set_logging_state( $state ) {
|
||||
$this->sitepress->set_setting( 'tp-com-logging', $state );
|
||||
$this->sitepress->save_settings();
|
||||
}
|
||||
|
||||
public function add_com_log_link() {
|
||||
$url = esc_attr( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=com-log' );
|
||||
?>
|
||||
<?php
|
||||
printf(
|
||||
__(
|
||||
'For retrieving debug information for communication between your%1$s site and the translation system, use the <a href="%2$s">communication log</a> page.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
'<br>',
|
||||
$url
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function now() {
|
||||
return date( 'm/d/Y h:i:s a', time() );
|
||||
}
|
||||
|
||||
private function add_to_log( $string ) {
|
||||
|
||||
if ( $this->is_logging_enabled() ) {
|
||||
|
||||
$max_log_length = 10000;
|
||||
|
||||
$string = $this->now() . ' - ' . $string;
|
||||
|
||||
$log = $this->get_log();
|
||||
$log .= $string;
|
||||
$log .= PHP_EOL;
|
||||
|
||||
$log_length = strlen( $log );
|
||||
if ( $log_length > $max_log_length ) {
|
||||
$log = substr( $log, $log_length - $max_log_length );
|
||||
}
|
||||
|
||||
$this->save_log( $log );
|
||||
}
|
||||
}
|
||||
|
||||
private function save_log( $log ) {
|
||||
update_option( 'wpml_tp_com_log', $log, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $item
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_json( $item ) {
|
||||
return is_string( $item ) && json_decode( $item ) && json_last_error() === JSON_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TP_Extra_Field {
|
||||
/** @var string */
|
||||
public $type = 'text';
|
||||
/** @var string */
|
||||
public $label;
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array */
|
||||
public $items;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services;
|
||||
|
||||
use WPML\TM\TranslationProxy\Services\Project\Manager;
|
||||
|
||||
class Authorization {
|
||||
/** @var Storage */
|
||||
private $storage;
|
||||
|
||||
/** @var Manager */
|
||||
private $projectManager;
|
||||
|
||||
/**
|
||||
* @param Storage $storage
|
||||
* @param Manager $projectManager
|
||||
*/
|
||||
public function __construct( Storage $storage, Manager $projectManager ) {
|
||||
$this->storage = $storage;
|
||||
$this->projectManager = $projectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $credentials
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \WPML_TP_API_Exception
|
||||
*/
|
||||
public function authorize( \stdClass $credentials ) {
|
||||
$service = $this->getCurrentService();
|
||||
$service->custom_fields_data = $credentials;
|
||||
|
||||
$project = $this->projectManager->create( $service );
|
||||
$this->storage->setCurrentService( $service );
|
||||
|
||||
do_action( 'wpml_tm_translation_service_authorized', $service, $project );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $credentials
|
||||
*
|
||||
* @throws \WPML_TP_API_Exception
|
||||
*/
|
||||
public function updateCredentials( \stdClass $credentials ) {
|
||||
$service = $this->getCurrentService();
|
||||
|
||||
$this->projectManager->updateCredentials( $service, $credentials );
|
||||
|
||||
$service->custom_fields_data = $credentials;
|
||||
$this->storage->setCurrentService( $service );
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function deauthorize() {
|
||||
$service = $this->getCurrentService();
|
||||
$service->custom_fields_data = null;
|
||||
|
||||
$this->storage->setCurrentService( $service );
|
||||
|
||||
do_action( 'wpml_tp_service_de_authorized', $service );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \stdClass
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function getCurrentService() {
|
||||
$service = $this->storage->getCurrentService();
|
||||
if ( (bool) $service === false ) {
|
||||
throw new \RuntimeException( 'Tried to authenticate a service, but no service is active!' );
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services;
|
||||
|
||||
use WPML\TM\TranslationProxy\Services\Project\Manager;
|
||||
use function WPML\Container\make;
|
||||
|
||||
class AuthorizationFactory {
|
||||
/**
|
||||
* @return Authorization
|
||||
* @throws \Auryn\InjectionException
|
||||
*/
|
||||
public function create() {
|
||||
$projectManager = make(
|
||||
Manager::class,
|
||||
[
|
||||
':projectApi' => wpml_tm_get_tp_project_api(),
|
||||
]
|
||||
);
|
||||
|
||||
return make( Authorization::class, [ ':projectManager' => $projectManager ] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services\Project;
|
||||
|
||||
class Manager {
|
||||
|
||||
/** @var \WPML_TP_Project_API */
|
||||
private $projectApi;
|
||||
|
||||
/** @var Storage */
|
||||
private $projectStorage;
|
||||
|
||||
/** @var SiteDetails */
|
||||
private $siteDetails;
|
||||
|
||||
/**
|
||||
* @param \WPML_TP_Project_API $projectApi
|
||||
* @param Storage $projectStorage
|
||||
* @param SiteDetails $siteDetails
|
||||
*/
|
||||
public function __construct(
|
||||
\WPML_TP_Project_API $projectApi,
|
||||
Storage $projectStorage,
|
||||
SiteDetails $siteDetails
|
||||
) {
|
||||
$this->projectApi = $projectApi;
|
||||
$this->projectStorage = $projectStorage;
|
||||
$this->siteDetails = $siteDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
*
|
||||
* @return Project
|
||||
* @throws \WPML_TP_API_Exception
|
||||
*/
|
||||
public function create( \stdClass $service ) {
|
||||
$project = $this->projectStorage->getByService( $service ) ?: $this->fromTranslationProxy( $service );
|
||||
|
||||
$project->extraFields = $this->projectApi->get_extra_fields( $project );
|
||||
$this->projectStorage->save( $service, $project );
|
||||
|
||||
do_action( 'wpml_tp_project_created', $service, $project, $this->projectStorage->getProjects()->toArray() );
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
* @param \stdClass $credentials
|
||||
*
|
||||
* @return Project|null
|
||||
* @throws \WPML_TP_API_Exception
|
||||
*/
|
||||
public function updateCredentials( \stdClass $service, \stdClass $credentials ) {
|
||||
$project = $this->projectStorage->getByService( $service );
|
||||
if ( ! $project ) {
|
||||
throw new \RuntimeException( 'Project does not exist' );
|
||||
}
|
||||
$this->projectApi->update_project_credentials( $project, $credentials );
|
||||
|
||||
$project->extraFields = $this->projectApi->get_extra_fields( $project );
|
||||
$this->projectStorage->save( $this->createServiceWithNewCredentials( $service, $credentials ), $project );
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
*
|
||||
* @return Project
|
||||
* @throws \WPML_TP_API_Exception
|
||||
*/
|
||||
private function fromTranslationProxy( \stdClass $service ) {
|
||||
$response = $this->projectApi->create_project( $service, $this->siteDetails );
|
||||
|
||||
return Project::fromResponse( $response->project );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
* @param \stdClass $credentials
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
private function createServiceWithNewCredentials( \stdClass $service, \stdClass $credentials ) {
|
||||
$updatedService = clone $service;
|
||||
$updatedService->custom_fields_data = $credentials;
|
||||
|
||||
return $updatedService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services\Project;
|
||||
|
||||
class Project {
|
||||
/** @var int */
|
||||
public $id;
|
||||
|
||||
/** @var string */
|
||||
public $accessKey;
|
||||
|
||||
/** @var string */
|
||||
public $tsId;
|
||||
|
||||
/** @var string */
|
||||
public $tsAccessKey;
|
||||
|
||||
/** @var \stdClass */
|
||||
public $extraFields;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray() {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'access_key' => $this->accessKey,
|
||||
'ts_id' => $this->tsId,
|
||||
'ts_access_key' => $this->tsAccessKey,
|
||||
'extra_fields' => $this->extraFields,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Project
|
||||
*/
|
||||
public static function fromArray( array $data ) {
|
||||
$project = new Project();
|
||||
|
||||
$project->id = $data['id'];
|
||||
$project->accessKey = $data['access_key'];
|
||||
$project->tsId = $data['ts_id'];
|
||||
$project->tsAccessKey = $data['ts_access_key'];
|
||||
$project->extraFields = $data['extra_fields'];
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
public static function fromResponse( \stdClass $response ) {
|
||||
$project = new Project();
|
||||
|
||||
$project->id = $response->id;
|
||||
$project->accessKey = $response->accesskey;
|
||||
$project->tsId = $response->ts_id;
|
||||
$project->tsAccessKey = $response->ts_accesskey;
|
||||
|
||||
return $project;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services\Project;
|
||||
|
||||
class SiteDetails {
|
||||
/** @var \SitePress */
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* @param \SitePress $sitepress
|
||||
*/
|
||||
public function __construct( \SitePress $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDeliveryMethod() {
|
||||
return (int) $this->sitepress->get_setting( 'translation_pickup_method' ) === ICL_PRO_TRANSLATION_PICKUP_XMLRPC
|
||||
? 'xmlrpc'
|
||||
: 'polling';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBlogInfo() {
|
||||
return [
|
||||
'url' => get_option( 'siteurl' ),
|
||||
'name' => get_option( 'blogname' ),
|
||||
'description' => get_option( 'blogdescription' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getClientData() {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
return [
|
||||
'email' => $current_user->user_email,
|
||||
'name' => $current_user->display_name,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services\Project;
|
||||
|
||||
use WPML\Collect\Support\Collection;
|
||||
|
||||
class Storage {
|
||||
/** @var \SitePress */
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* @param \SitePress $sitepress
|
||||
*/
|
||||
public function __construct( \SitePress $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
*
|
||||
* @return Project|null
|
||||
*/
|
||||
public function getByService( \stdClass $service ) {
|
||||
return $this->getProjects()->get( \TranslationProxy_Project::generate_service_index( $service ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $service
|
||||
* @param Project $project
|
||||
*/
|
||||
public function save( \stdClass $service, Project $project ) {
|
||||
$index = \TranslationProxy_Project::generate_service_index( $service );
|
||||
$this->sitepress->set_setting(
|
||||
'icl_translation_projects',
|
||||
$this->getProjects()->put( $index, $project )->map(
|
||||
function ( Project $project ) {
|
||||
return $project->toArray();
|
||||
}
|
||||
)->toArray(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getProjects() {
|
||||
$projects = $this->sitepress->get_setting( 'icl_translation_projects', [] );
|
||||
if ( ! is_array( $projects ) ) {
|
||||
$projects = [];
|
||||
}
|
||||
|
||||
return \wpml_collect( $projects )->map(
|
||||
function ( array $rawProject ) {
|
||||
return Project::fromArray( $rawProject );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\TranslationProxy\Services;
|
||||
|
||||
class Storage {
|
||||
/** @var \SitePress $sitepress */
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* @param \SitePress $sitepress
|
||||
*/
|
||||
public function __construct( \SitePress $sitepress ) {
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current translation service
|
||||
*
|
||||
* @return bool|\stdClass
|
||||
*/
|
||||
public function getCurrentService() {
|
||||
return $this->sitepress->get_setting( 'translation_service' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the input service as the current translation service setting.
|
||||
*
|
||||
* @param \stdClass $service
|
||||
*/
|
||||
public function setCurrentService( \stdClass $service ) {
|
||||
do_action( 'wpml_tm_before_set_translation_service', $service );
|
||||
$this->sitepress->set_setting( 'translation_service', $service, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Sync_Ajax_Handler {
|
||||
|
||||
const AJAX_ACTION = 'wpml-tp-sync-job-states';
|
||||
|
||||
/** @var WPML_TP_Sync_Jobs */
|
||||
private $tp_sync;
|
||||
|
||||
/** @var WPML_TM_Last_Picked_Up $wpml_tm_last_picked_up */
|
||||
private $wpml_tm_last_picked_up;
|
||||
|
||||
/**
|
||||
* WPML_TP_Sync_Jobs constructor.
|
||||
*
|
||||
* @param WPML_TP_Sync_Jobs $tp_sync
|
||||
* @param WPML_TM_Last_Picked_Up $wpml_tm_last_picked_up
|
||||
*/
|
||||
public function __construct( WPML_TP_Sync_Jobs $tp_sync, WPML_TM_Last_Picked_Up $wpml_tm_last_picked_up ) {
|
||||
$this->tp_sync = $tp_sync;
|
||||
$this->wpml_tm_last_picked_up = $wpml_tm_last_picked_up;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'handle' ) );
|
||||
}
|
||||
|
||||
public function handle() {
|
||||
try {
|
||||
if ( isset( $_REQUEST['update_last_picked_up'] ) ) {
|
||||
$this->wpml_tm_last_picked_up->set();
|
||||
}
|
||||
|
||||
$jobs = $this->tp_sync->sync();
|
||||
do_action( 'wpml_tm_empty_mail_queue' );
|
||||
|
||||
wp_send_json_success( $jobs->map( array( $this, 'map_job_to_result' ) ) );
|
||||
|
||||
return true;
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error( $e->getMessage(), 503 );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function map_job_to_result( WPML_TM_Job_Entity $job ) {
|
||||
return array(
|
||||
'id' => $job->get_id(),
|
||||
'type' => $job->get_type(),
|
||||
'status' => $job->get_status(),
|
||||
'hasCompletedTranslation' => $job->has_completed_translation(),
|
||||
'needsUpdate' => $job->does_need_update(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Sync_Installer_Wrapper {
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_wpml_registered() {
|
||||
return (bool) WP_Installer::instance()->get_site_key( 'wpml' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Sync_Jobs_Revision {
|
||||
|
||||
/** @var WPML_TM_Jobs_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TP_Jobs_API */
|
||||
private $tp_api;
|
||||
|
||||
/**
|
||||
* WPML_TM_Sync_Jobs_Revision constructor.
|
||||
*
|
||||
* @param WPML_TM_Jobs_Repository $jobs_repository
|
||||
* @param WPML_TP_Jobs_API $tp_api
|
||||
*/
|
||||
public function __construct( WPML_TM_Jobs_Repository $jobs_repository, WPML_TP_Jobs_API $tp_api ) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->tp_api = $tp_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Job_Entity[]
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function sync() {
|
||||
$result = array();
|
||||
|
||||
$tp_statuses_of_revised_jobs = $this->tp_api->get_revised_jobs();
|
||||
if ( $tp_statuses_of_revised_jobs ) {
|
||||
$revised_jobs = array();
|
||||
foreach ( $tp_statuses_of_revised_jobs as $tp_job_status ) {
|
||||
$revised_jobs[ $tp_job_status->get_tp_id() ] = $tp_job_status->get_revision();
|
||||
}
|
||||
|
||||
$job_search = new WPML_TM_Jobs_Search_Params(
|
||||
array(
|
||||
'scope' => WPML_TM_Jobs_Search_Params::SCOPE_REMOTE,
|
||||
'tp_id' => array_keys( $revised_jobs ),
|
||||
)
|
||||
);
|
||||
|
||||
/** @var WPML_TM_Job_Entity $job */
|
||||
foreach ( $this->jobs_repository->get( $job_search ) as $job ) {
|
||||
|
||||
if ( isset( $revised_jobs[ $job->get_tp_id() ] ) && $job->get_revision() < $revised_jobs[ $job->get_tp_id() ] ) {
|
||||
|
||||
$job->set_status( WPML_TP_Job_States::map_tp_state_to_local( WPML_TP_Job_States::TRANSLATION_READY ) );
|
||||
$job->set_revision( $revised_jobs[ $job->get_tp_id() ] );
|
||||
|
||||
$result[] = $job;
|
||||
|
||||
do_action( 'wpml_tm_revised_job_notification', $job->get_id() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Sync_Jobs_Status {
|
||||
|
||||
/** @var WPML_TM_Jobs_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TP_Jobs_API */
|
||||
private $tp_api;
|
||||
|
||||
/**
|
||||
* WPML_TM_Sync_Jobs_Status constructor.
|
||||
*
|
||||
* @param WPML_TM_Jobs_Repository $jobs_repository
|
||||
* @param WPML_TP_Jobs_API $tp_api
|
||||
*/
|
||||
public function __construct( WPML_TM_Jobs_Repository $jobs_repository, WPML_TP_Jobs_API $tp_api ) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->tp_api = $tp_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function sync() {
|
||||
return $this->update_tp_state_of_jobs(
|
||||
$this->jobs_repository->get(
|
||||
new WPML_TM_Jobs_Search_Params(
|
||||
array(
|
||||
'status' => array( ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS ),
|
||||
'scope' => WPML_TM_Jobs_Search_Params::SCOPE_REMOTE,
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Collection $jobs
|
||||
*
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
private function update_tp_state_of_jobs( WPML_TM_Jobs_Collection $jobs ) {
|
||||
$tp_ids = $this->extract_tp_id_from_jobs( $jobs );
|
||||
|
||||
if ( $tp_ids ) {
|
||||
$tp_statuses = $this->tp_api->get_jobs_statuses( $tp_ids );
|
||||
foreach ( $tp_statuses as $job_status ) {
|
||||
$job = $jobs->get_by_tp_id( $job_status->get_tp_id() );
|
||||
if ( $job ) {
|
||||
$job->set_status( WPML_TP_Job_States::map_tp_state_to_local( $job_status->get_status() ) );
|
||||
$job->set_ts_status( $job_status->get_ts_status() );
|
||||
if ( WPML_TP_Job_States::CANCELLED === $job_status->get_status() ) {
|
||||
do_action( 'wpml_tm_canceled_job_notification', $job );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Collection $jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function extract_tp_id_from_jobs( WPML_TM_Jobs_Collection $jobs ) {
|
||||
$tp_ids = array();
|
||||
foreach ( $jobs as $job ) {
|
||||
$tp_ids[] = $job->get_tp_id();
|
||||
}
|
||||
|
||||
return $tp_ids;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Sync_Jobs {
|
||||
|
||||
/** @var WPML_TM_Sync_Jobs_Status */
|
||||
private $jobs_status_sync;
|
||||
|
||||
/** @var WPML_TM_Sync_Jobs_Revision */
|
||||
private $jobs_revision_sync;
|
||||
|
||||
/** @var WPML_TP_Sync_Update_Job */
|
||||
private $update_job;
|
||||
|
||||
/**
|
||||
* WPML_TP_Sync_Jobs constructor.
|
||||
*
|
||||
* @param WPML_TM_Sync_Jobs_Status $jobs_status_sync
|
||||
* @param WPML_TM_Sync_Jobs_Revision $jobs_revision_sync
|
||||
* @param WPML_TP_Sync_Update_Job $update_job
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TM_Sync_Jobs_Status $jobs_status_sync,
|
||||
WPML_TM_Sync_Jobs_Revision $jobs_revision_sync,
|
||||
WPML_TP_Sync_Update_Job $update_job
|
||||
) {
|
||||
$this->jobs_status_sync = $jobs_status_sync;
|
||||
$this->jobs_revision_sync = $jobs_revision_sync;
|
||||
$this->update_job = $update_job;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function sync() {
|
||||
return new WPML_TM_Jobs_Collection(
|
||||
$this->jobs_status_sync
|
||||
->sync()
|
||||
->append( $this->jobs_revision_sync->sync() )
|
||||
->filter_by_status( array( ICL_TM_IN_PROGRESS, ICL_TM_WAITING_FOR_TRANSLATOR ), true )
|
||||
->map( array( $this->update_job, 'update_state' ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Relation;
|
||||
use \WPML\LIB\WP\Post;
|
||||
|
||||
class WPML_TP_Sync_Update_Job {
|
||||
|
||||
private $strategies = array(
|
||||
WPML_TM_Job_Entity::POST_TYPE => 'update_post_job',
|
||||
WPML_TM_Job_Entity::STRING_TYPE => 'update_string_job',
|
||||
WPML_TM_Job_Entity::PACKAGE_TYPE => 'update_post_job',
|
||||
WPML_TM_Job_Entity::STRING_BATCH => 'update_post_job',
|
||||
);
|
||||
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/** @var SitePress */
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb, SitePress $sitepress ) {
|
||||
$this->wpdb = $wpdb;
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TM_Job_Entity
|
||||
*/
|
||||
public function update_state( WPML_TM_Job_Entity $job ) {
|
||||
if ( ! array_key_exists( $job->get_type(), $this->strategies ) ) {
|
||||
return $job;
|
||||
}
|
||||
|
||||
$method = $this->strategies[ $job->get_type() ];
|
||||
|
||||
return call_user_func( array( $this, $method ), $job );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TM_Job_Entity
|
||||
*/
|
||||
private function update_string_job( WPML_TM_Job_Entity $job ) {
|
||||
if ( $job->get_tp_id() ) {
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_core_status',
|
||||
array(
|
||||
'status' => $job->get_status(),
|
||||
'tp_revision' => $job->get_revision(),
|
||||
'ts_status' => $this->get_ts_status_in_ts_format( $job ),
|
||||
),
|
||||
array( 'rid' => $job->get_tp_id() )
|
||||
);
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'status' => $job->get_status(),
|
||||
);
|
||||
if ( ICL_TM_NOT_TRANSLATED === $job->get_status() ) {
|
||||
$data['translator_id'] = null;
|
||||
}
|
||||
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_string_translations',
|
||||
$data,
|
||||
array( 'id' => $job->get_id() )
|
||||
);
|
||||
|
||||
icl_update_string_status( $job->get_original_element_id() );
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TM_Job_Entity
|
||||
*/
|
||||
private function update_post_job( WPML_TM_Job_Entity $job ) {
|
||||
$job_id = $job->get_id();
|
||||
|
||||
if ( $job->get_status() === ICL_TM_NOT_TRANSLATED ) {
|
||||
$prev_status = $this->get_job_prev_status( $job_id );
|
||||
if ( $prev_status && Relation::propEq( 'needs_update', '1', $prev_status ) ) {
|
||||
$this->wpdb->update( $this->wpdb->prefix . 'icl_translation_status', $prev_status, [ 'rid' => $job_id ] );
|
||||
$job->set_needs_update( true );
|
||||
return $job;
|
||||
}
|
||||
}
|
||||
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_translation_status',
|
||||
array(
|
||||
'status' => $job->get_status(),
|
||||
'tp_revision' => $job->get_revision(),
|
||||
'ts_status' => $this->get_ts_status_in_ts_format( $job ),
|
||||
),
|
||||
array( 'rid' => $job_id )
|
||||
);
|
||||
|
||||
if (
|
||||
ICL_TM_NOT_TRANSLATED === $job->get_status()
|
||||
&& ( $post_type = Post::getType( $job->get_original_element_id() ) )
|
||||
) {
|
||||
$this->sitepress->delete_orphan_element(
|
||||
$job->get_original_element_id(),
|
||||
'post_' . $post_type,
|
||||
$job->get_target_language()
|
||||
);
|
||||
}
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
private function get_job_prev_status( $job_id ) {
|
||||
$previous_state = $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT _prevstate
|
||||
FROM {$this->wpdb->prefix}icl_translation_status
|
||||
WHERE rid=%d
|
||||
LIMIT 1",
|
||||
$job_id
|
||||
)
|
||||
);
|
||||
|
||||
return unserialize( $previous_state );
|
||||
}
|
||||
|
||||
/**
|
||||
* In the db, we store the exact json format that we get from TS. It includes an extra ts_status key
|
||||
*
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_ts_status_in_ts_format( WPML_TM_Job_Entity $job ) {
|
||||
$ts_status = $job->get_ts_status();
|
||||
|
||||
return $ts_status ? wp_json_encode( array( 'ts_status' => json_decode( (string) $ts_status ) ) ) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Sync_Orphan_Jobs_Factory {
|
||||
/**
|
||||
* @return WPML_TP_Sync_Orphan_Jobs
|
||||
*/
|
||||
public function create() {
|
||||
global $wpdb, $sitepress;
|
||||
|
||||
return new WPML_TP_Sync_Orphan_Jobs(
|
||||
wpml_tm_get_jobs_repository(),
|
||||
new WPML_TP_Sync_Update_Job( $wpdb, $sitepress )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Sync_Orphan_Jobs {
|
||||
/** @var WPML_TM_Jobs_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TP_Sync_Update_Job */
|
||||
private $update_job;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Repository $jobs_repository
|
||||
* @param WPML_TP_Sync_Update_Job $update_job
|
||||
*/
|
||||
public function __construct( WPML_TM_Jobs_Repository $jobs_repository, WPML_TP_Sync_Update_Job $update_job ) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->update_job = $update_job;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
*/
|
||||
public function cancel_orphans() {
|
||||
$params = new WPML_TM_Jobs_Search_Params(
|
||||
array(
|
||||
'scope' => WPML_TM_Jobs_Search_Params::SCOPE_REMOTE,
|
||||
'tp_id' => 0,
|
||||
'status' => array( ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS ),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->jobs_repository->get( $params )->map( array( $this, 'cancel_job' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TM_Job_Entity
|
||||
*/
|
||||
public function cancel_job( WPML_TM_Job_Entity $job ) {
|
||||
$job->set_status( ICL_TM_NOT_TRANSLATED );
|
||||
|
||||
return $this->update_job->update_state( $job );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Apply_Single_Job {
|
||||
/** @var WPML_TP_Translations_Repository */
|
||||
private $translations_repository;
|
||||
|
||||
/** @var WPML_TP_Apply_Translation_Strategies */
|
||||
private $strategy_dispatcher;
|
||||
|
||||
/**
|
||||
* @param WPML_TP_Translations_Repository $translations_repository
|
||||
* @param WPML_TP_Apply_Translation_Strategies $strategy_dispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TP_Translations_Repository $translations_repository,
|
||||
WPML_TP_Apply_Translation_Strategies $strategy_dispatcher
|
||||
) {
|
||||
$this->translations_repository = $translations_repository;
|
||||
$this->strategy_dispatcher = $strategy_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TM_Job_Entity
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function apply( WPML_TM_Job_Entity $job ) {
|
||||
$translations = $this->translations_repository->get_job_translations_by_job_entity( $job );
|
||||
|
||||
$this->strategy_dispatcher->get( $job )->apply( $job, $translations );
|
||||
$job->set_status( ICL_TM_COMPLETE );
|
||||
|
||||
return $job;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Apply_Translation_Post_Strategy implements WPML_TP_Apply_Translation_Strategy {
|
||||
/** @var WPML_TP_Jobs_API */
|
||||
private $jobs_api;
|
||||
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @param WPML_TP_Jobs_API $jobs_api
|
||||
*/
|
||||
public function __construct( WPML_TP_Jobs_API $jobs_api ) {
|
||||
$this->jobs_api = $jobs_api;
|
||||
|
||||
global $wpdb;
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param WPML_TP_Translation_Collection $translations
|
||||
*
|
||||
* @return void
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function apply( WPML_TM_Job_Entity $job, WPML_TP_Translation_Collection $translations ) {
|
||||
if ( ! $job instanceof WPML_TM_Post_Job_Entity ) {
|
||||
throw new InvalidArgumentException( 'A job must have post type' );
|
||||
}
|
||||
|
||||
kses_remove_filters();
|
||||
wpml_tm_save_data( $this->build_data( $job, $translations ) );
|
||||
kses_init();
|
||||
|
||||
$this->jobs_api->update_job_state( $job, 'delivered' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param WPML_TP_Translation_Collection $translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function build_data( WPML_TM_Post_Job_Entity $job, WPML_TP_Translation_Collection $translations ) {
|
||||
$data = array(
|
||||
'job_id' => $job->get_translate_job_id(),
|
||||
'fields' => array(),
|
||||
'complete' => 1
|
||||
);
|
||||
|
||||
/** @var WPML_TP_Translation $translation */
|
||||
foreach ( $translations as $translation ) {
|
||||
foreach ( $job->get_elements() as $element ) {
|
||||
if ( $element->get_type() === $translation->get_field() ) {
|
||||
$data['fields'][ $element->get_type() ] = array(
|
||||
'data' => $translation->get_target(),
|
||||
'finished' => 1,
|
||||
'tid' => $element->get_id(),
|
||||
'field_type' => $element->get_type(),
|
||||
'format' => $element->get_format()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Apply_Translation_Strategies {
|
||||
/** @var WPML_TP_Apply_Translation_Post_Strategy */
|
||||
private $post_strategy;
|
||||
|
||||
/** @var WPML_TP_Apply_Translation_String_Strategy */
|
||||
private $string_strategy;
|
||||
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
*
|
||||
* @return WPML_TP_Apply_Translation_Strategy
|
||||
*/
|
||||
public function get( WPML_TM_Job_Entity $job ) {
|
||||
switch ( $job->get_type() ) {
|
||||
case WPML_TM_Job_Entity::STRING_TYPE:
|
||||
return $this->get_string_strategy();
|
||||
case WPML_TM_Job_Entity::POST_TYPE:
|
||||
case WPML_TM_Job_Entity::PACKAGE_TYPE:
|
||||
case WPML_TM_Job_Entity::STRING_BATCH:
|
||||
return $this->get_post_strategy();
|
||||
default:
|
||||
throw new InvalidArgumentException( 'Job type: ' . $job->get_type() . ' is not supported' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TP_Apply_Translation_Post_Strategy
|
||||
*/
|
||||
private function get_post_strategy() {
|
||||
if ( ! $this->post_strategy ) {
|
||||
$this->post_strategy = new WPML_TP_Apply_Translation_Post_Strategy( wpml_tm_get_tp_jobs_api() );
|
||||
}
|
||||
|
||||
return $this->post_strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TP_Apply_Translation_String_Strategy
|
||||
*/
|
||||
private function get_string_strategy() {
|
||||
if ( ! $this->string_strategy ) {
|
||||
$this->string_strategy = new WPML_TP_Apply_Translation_String_Strategy(
|
||||
wpml_tm_get_tp_jobs_api(),
|
||||
$this->wpdb
|
||||
);
|
||||
}
|
||||
|
||||
return $this->string_strategy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
interface WPML_TP_Apply_Translation_Strategy {
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param WPML_TP_Translation_Collection $translations
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function apply( WPML_TM_Job_Entity $job, WPML_TP_Translation_Collection $translations );
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Apply_Translation_String_Strategy implements WPML_TP_Apply_Translation_Strategy {
|
||||
/** @var WPML_TP_Jobs_API */
|
||||
private $jobs_api;
|
||||
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @param WPML_TP_Jobs_API $jobs_api
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( WPML_TP_Jobs_API $jobs_api, wpdb $wpdb ) {
|
||||
$this->jobs_api = $jobs_api;
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param WPML_TP_Translation_Collection $translations
|
||||
*
|
||||
* @return void
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function apply( WPML_TM_Job_Entity $job, WPML_TP_Translation_Collection $translations ) {
|
||||
if ( ! icl_translation_add_string_translation(
|
||||
$job->get_tp_id(),
|
||||
$this->map_translations_to_legacy_array( $translations ),
|
||||
$translations->get_target_language()
|
||||
)
|
||||
) {
|
||||
throw new WPML_TP_API_Exception( 'Could not apply string translation!' );
|
||||
}
|
||||
|
||||
$this->update_local_job_status( $job, ICL_TM_COMPLETE );
|
||||
$this->jobs_api->update_job_state( $job, 'delivered' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param int $status
|
||||
*/
|
||||
private function update_local_job_status( WPML_TM_Job_Entity $job, $status ) {
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_core_status',
|
||||
array( 'status' => $status ),
|
||||
array( 'id' => $job->get_id() )
|
||||
);
|
||||
}
|
||||
|
||||
private function map_translations_to_legacy_array( WPML_TP_Translation_Collection $translations ) {
|
||||
$result = array();
|
||||
|
||||
/** @var WPML_TP_Translation $translation */
|
||||
foreach ( $translations as $translation ) {
|
||||
$result[ $translation->get_field() ] = $translation->get_target();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Apply_Translations {
|
||||
/** @var WPML_TM_Jobs_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/** @var WPML_TP_Apply_Single_Job */
|
||||
private $apply_single_job;
|
||||
|
||||
/** @var WPML_TP_Sync_Jobs */
|
||||
private $tp_sync;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Repository $jobs_repository
|
||||
* @param WPML_TP_Apply_Single_Job $apply_single_job
|
||||
* @param WPML_TP_Sync_Jobs $tp_sync
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TM_Jobs_Repository $jobs_repository,
|
||||
WPML_TP_Apply_Single_Job $apply_single_job,
|
||||
WPML_TP_Sync_Jobs $tp_sync
|
||||
) {
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
$this->apply_single_job = $apply_single_job;
|
||||
$this->tp_sync = $tp_sync;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function apply( array $params ) {
|
||||
$jobs = $this->get_jobs( $params );
|
||||
|
||||
if ( $this->has_in_progress_jobs( $jobs ) ) {
|
||||
$jobs = $this->sync_jobs( $jobs );
|
||||
}
|
||||
$cancelled_jobs = $jobs->filter_by_status( ICL_TM_NOT_TRANSLATED );
|
||||
|
||||
$downloaded_jobs = new WPML_TM_Jobs_Collection(
|
||||
$jobs->filter_by_status( ICL_TM_TRANSLATION_READY_TO_DOWNLOAD )
|
||||
->map( array( $this->apply_single_job, 'apply' ) )
|
||||
);
|
||||
|
||||
return $downloaded_jobs->append( $cancelled_jobs );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Collection $jobs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function has_in_progress_jobs( WPML_TM_Jobs_Collection $jobs ) {
|
||||
return count( $jobs->filter_by_status( ICL_TM_IN_PROGRESS ) ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return array|WPML_TM_Jobs_Collection
|
||||
*/
|
||||
private function get_jobs( array $params ) {
|
||||
if ( $params ) {
|
||||
if ( isset( $params['original_element_id'], $params['element_type'] ) ) {
|
||||
$jobs = $this->get_jobs_by_original_element( $params['original_element_id'], $params['element_type'] );
|
||||
} else {
|
||||
$jobs = $this->get_jobs_by_ids( $params );
|
||||
}
|
||||
} else {
|
||||
$jobs = $this->get_all_ready_jobs();
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $original_element_id
|
||||
* @param string $element_type
|
||||
*
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
*/
|
||||
private function get_jobs_by_original_element( $original_element_id, $element_type ) {
|
||||
$params = new WPML_TM_Jobs_Search_Params();
|
||||
$params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
|
||||
$params->set_original_element_id( $original_element_id );
|
||||
$params->set_job_types( $element_type );
|
||||
|
||||
return $this->jobs_repository->get( $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
*/
|
||||
private function get_jobs_by_ids( array $params ) {
|
||||
$jobs = array();
|
||||
foreach ( $params as $param ) {
|
||||
$jobs[] = $this->jobs_repository->get_job( $param['id'], $param['type'] );
|
||||
}
|
||||
|
||||
return new WPML_TM_Jobs_Collection( $jobs );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
*/
|
||||
private function get_all_ready_jobs() {
|
||||
return $this->jobs_repository->get(
|
||||
new WPML_TM_Jobs_Search_Params(
|
||||
array(
|
||||
'status' => array( ICL_TM_TRANSLATION_READY_TO_DOWNLOAD ),
|
||||
'scope' => WPML_TM_Jobs_Search_Params::SCOPE_REMOTE,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Jobs_Collection $jobs
|
||||
*
|
||||
* @return WPML_TM_Jobs_Collection
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
private function sync_jobs( WPML_TM_Jobs_Collection $jobs ) {
|
||||
$synced_jobs = $this->tp_sync->sync();
|
||||
/** @var WPML_TM_Job_Entity $job */
|
||||
foreach ( $jobs as $job ) {
|
||||
foreach ( $synced_jobs as $synced_job ) {
|
||||
if ( $job->is_equal( $synced_job ) ) {
|
||||
$job->set_status( $synced_job->get_status() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Fns;
|
||||
use function WPML\FP\invoke;
|
||||
|
||||
class WPML_TP_Translation_Collection implements IteratorAggregate {
|
||||
/** @var WPML_TP_Translation[] */
|
||||
private $translations;
|
||||
|
||||
/** @var string */
|
||||
private $source_language;
|
||||
|
||||
/** @var string */
|
||||
private $target_language;
|
||||
|
||||
/**
|
||||
* @param WPML_TP_Translation[] $translations
|
||||
* @param string $source_language
|
||||
* @param string $target_language
|
||||
*/
|
||||
public function __construct( array $translations, $source_language, $target_language ) {
|
||||
$this->translations = $translations;
|
||||
$this->source_language = $source_language;
|
||||
$this->target_language = $target_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_source_language() {
|
||||
return $this->source_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_target_language() {
|
||||
return $this->target_language;
|
||||
}
|
||||
|
||||
public function getIterator() {
|
||||
return new ArrayIterator( $this->translations );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function to_array() {
|
||||
return [
|
||||
'source_language' => $this->source_language,
|
||||
'target_language' => $this->target_language,
|
||||
'translations' => Fns::map( invoke( 'to_array' ), $this->translations ),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Translation {
|
||||
/** @var string */
|
||||
private $field;
|
||||
|
||||
/** @var string */
|
||||
private $source;
|
||||
|
||||
/** @var string */
|
||||
private $target;
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
*/
|
||||
public function __construct( $field, $source, $target ) {
|
||||
$this->field = $field;
|
||||
$this->source = $source;
|
||||
$this->target = $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_field() {
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_source() {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_target() {
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
public function to_array() {
|
||||
return get_object_vars( $this );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class WPML_TP_Translations_Repository {
|
||||
|
||||
/** @var WPML_TP_XLIFF_API */
|
||||
private $xliff_api;
|
||||
|
||||
/** @var WPML_TM_Jobs_Repository */
|
||||
private $jobs_repository;
|
||||
|
||||
/**
|
||||
* @param WPML_TP_XLIFF_API $xliff_api
|
||||
* @param WPML_TM_Jobs_Repository $jobs_repository
|
||||
*/
|
||||
public function __construct( WPML_TP_XLIFF_API $xliff_api, WPML_TM_Jobs_Repository $jobs_repository ) {
|
||||
$this->xliff_api = $xliff_api;
|
||||
$this->jobs_repository = $jobs_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
* @param int $job_type
|
||||
* @param bool $parse When true, it returns the parsed translation, otherwise, it returns the raw XLIFF.
|
||||
*
|
||||
* @return WPML_TP_Translation_Collection|string
|
||||
* @throws WPML_TP_API_Exception|InvalidArgumentException
|
||||
*/
|
||||
public function get_job_translations( $job_id, $job_type, $parse = true ) {
|
||||
$job = $this->jobs_repository->get_job( $job_id, $job_type );
|
||||
|
||||
if ( ! $job ) {
|
||||
throw new InvalidArgumentException( 'Cannot find job' );
|
||||
}
|
||||
|
||||
return $this->get_job_translations_by_job_entity( $job, $parse );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Job_Entity $job
|
||||
* @param bool $parse When true, it returns the parsed translation, otherwise, it returns the raw XLIFF.
|
||||
*
|
||||
* @return WPML_TP_Translation_Collection|string
|
||||
* @throws WPML_TP_API_Exception
|
||||
*/
|
||||
public function get_job_translations_by_job_entity( WPML_TM_Job_Entity $job, $parse = true ) {
|
||||
$correct_states = array( ICL_TM_TRANSLATION_READY_TO_DOWNLOAD, ICL_TM_COMPLETE );
|
||||
if ( ! in_array( $job->get_status(), $correct_states, true ) ) {
|
||||
throw new InvalidArgumentException( 'Job\'s translation is not ready.' );
|
||||
}
|
||||
|
||||
if ( ! $job->get_tp_id() ) {
|
||||
throw new InvalidArgumentException( 'This is only a local job.' );
|
||||
}
|
||||
|
||||
$translations = $this->xliff_api->get_remote_translations( $job->get_tp_id(), $parse );
|
||||
|
||||
if ( $parse ) {
|
||||
/**
|
||||
* It filters translations coming from the Translation Proxy.
|
||||
*
|
||||
* @param \WPML_TP_Translation_Collection $translations
|
||||
* @param \WPML_TM_Job_Entity $job
|
||||
*/
|
||||
$translations = apply_filters( 'wpml_tm_proxy_translations', $translations, $job );
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author OnTheGo Systems
|
||||
*/
|
||||
class WPML_TP_Extra_Field_Display {
|
||||
private $fields_with_items = array( 'select', 'radio', 'checkbox' );
|
||||
|
||||
/**
|
||||
* WPML_TP_Extra_Field_Display constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function render( $field ) {
|
||||
if ( $this->must_render( $field ) ) {
|
||||
$field_id = 'wpml-tp-extra-field-' . $field->name;
|
||||
$row = '<tr>';
|
||||
$row .= '<th scope="row">';
|
||||
$row .= '<label for="' . esc_attr( $field_id ) . '">' . esc_html( $field->label ) . '</label>';
|
||||
$row .= '</th>';
|
||||
$row .= '<td>';
|
||||
switch ( $field->type ) {
|
||||
case 'select':
|
||||
$row .= '<select id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field->name ) . '">';
|
||||
foreach ( $field->items as $id => $name ) {
|
||||
$row .= '<option value="' . esc_attr( $id ) . '">' . esc_html( $name ) . '</option>';
|
||||
}
|
||||
$row .= '</select>';
|
||||
break;
|
||||
case 'textarea':
|
||||
$row .= '<textarea id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field->name ) . '"></textarea>';
|
||||
break;
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
$row .= '<ol>';
|
||||
foreach ( $field->items as $id => $name ) {
|
||||
$row .= '<li>';
|
||||
$row .= '<input id="' . esc_attr( $field_id . '-' . $id ) . '" type="' . esc_attr( $field->type ) . '" name="' . esc_attr( $field->name ) . '" value="' . esc_attr( $id ) . '"> ';
|
||||
$row .= '<label for="' . esc_attr( $field_id . '-' . $id ) . '">' . esc_html( $name ) . '</label>';
|
||||
$row .= '</li>';
|
||||
}
|
||||
$row .= '</ol>';
|
||||
break;
|
||||
case 'text':
|
||||
default:
|
||||
$type = null !== $field->type ? $field->type : 'text';
|
||||
$row .= '<input id="' . esc_attr( $field_id ) . '" type="' . esc_attr( $type ) . '" name="' . esc_attr( $field->name ) . '">';
|
||||
break;
|
||||
}
|
||||
$row .= '</td>';
|
||||
$row .= '</tr>';
|
||||
return $row;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $field
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function must_render( $field ) {
|
||||
$must_render = isset( $field->type ) && $field->type;
|
||||
|
||||
if ( $must_render && in_array( $field->type, $this->fields_with_items, true ) ) {
|
||||
$must_render = isset( $field->items ) && $field->items;
|
||||
}
|
||||
|
||||
return $must_render;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user