ate_api = $ate_api;
$this->ate_jobs = $ate_jobs;
$this->sitepress = $sitepress;
$this->current_screen = $current_screen;
$this->translator_activation_records = $translator_activation_records;
}
public function add_hooks() {
add_action( 'wpml_added_translation_job', array( $this, 'added_translation_job' ), 10, 2 );
add_action( 'wpml_added_translation_jobs', array( $this, 'added_translation_jobs' ) );
add_action( 'admin_notices', array( $this, 'handle_messages' ) );
add_action( 'current_screen', array( $this, 'update_jobs_on_current_screen' ) );
add_action( 'wp', array( $this, 'update_jobs_on_current_screen' ) );
add_filter( 'wpml_tm_ate_jobs_data', array( $this, 'get_ate_jobs_data_filter' ), 10, 2 );
add_filter( 'wpml_tm_translation_queue_jobs_require_update', array( $this, 'update_jobs' ), 10, 3 );
add_filter( 'wpml_tm_ate_jobs_editor_url', array( $this, 'get_editor_url' ), 10, 3 );
}
public function handle_messages() {
if ( $this->current_screen->id_ends_with( WPML_TM_FOLDER . '/menu/translations-queue') ) {
if ( array_key_exists( 'message', $_GET ) ) {
if ( array_key_exists( 'ate_job_id', $_GET ) ) {
$ate_job_id = filter_var( $_GET['ate_job_id'], FILTER_SANITIZE_NUMBER_INT );
$this->resign_job_on_error( $ate_job_id );
}
$message = filter_var( $_GET['message'], FILTER_SANITIZE_STRING );
?>
added_translation_jobs( array( $translation_service => array( $job_id ) ) );
}
/**
* @param array $jobs
*
* @return bool|void
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
public function added_translation_jobs( array $jobs ) {
if ( ! $jobs || ! array_key_exists( 'local', $jobs ) || ! $jobs['local'] ) {
return;
}
/** @var array $job_ids */
$job_ids = $jobs['local'];
$jobs = array();
foreach ( $job_ids as $job_id ) {
$jobs[] = wpml_tm_create_ATE_job_creation_model( $job_id );
}
$response = $this->create_jobs( $jobs );
try {
$this->check_response_error( $response );
} catch ( RuntimeException $ex ) {
do_action( 'wpml_tm_basket_add_message', 'error', $ex->getMessage() );
return;
}
$has_valid_response = $response && isset( $response->jobs );
$response_jobs = null;
if ( $has_valid_response ) {
$response_jobs = $response->jobs;
}
if ( $response_jobs ) {
if ( is_object( $response_jobs ) ) {
$response_jobs = json_decode( wp_json_encode( $response_jobs, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES ), true );
}
$jobs_with_errors = 0;
foreach ( $response_jobs as $wpml_job_id => $ate_job_id ) {
$result = $this->ate_jobs->store( $wpml_job_id, array( 'ateJobId' => $ate_job_id ) );
if ( array_key_exists( 'error', $result ) ) {
$jobs_with_errors++;
$this->add_message( 'error', $result['error']['code'] . ': ' . $result['error']['message'], 'wpml_tm_ate_create_job' );
}
}
$message = __( '%1$s jobs added to the Advanced Translation Editor.', 'wpml-translation-management' );
$this->add_message( 'updated', sprintf( $message, count( $response_jobs ) ), 'wpml_tm_ate_create_job' );
if ( $jobs_with_errors ) {
$message = __( 'Advanced Translation Editor returned errors for %1$s job.', 'wpml-translation-management' );
$this->add_message( 'updated', sprintf( $message, $jobs_with_errors ), 'wpml_tm_ate_create_job' );
}
} else {
$this->add_message(
'error',
__( 'Jobs could not be created in Advanced Translation Editor. Please try again or contact the WPML support for help.',
'wpml-translation-management' ), 'wpml_tm_ate_create_job' );
}
}
/**
* @param string $type
* @param string $message
* @param string|null $id
*/
private function add_message( $type, $message, $id = null ) {
do_action( 'wpml_tm_basket_add_message', $type, $message, $id );
}
/**
* @param WPML_TM_ATE_Models_Job_Create[] $jobs
*
* @return mixed
* @throws \InvalidArgumentException
*/
private function create_jobs( array $jobs ) {
$params = json_decode( wp_json_encode( array( 'jobs' => $jobs ) ), true );
return $this->ate_api->create_jobs( $params );
}
/**
* @param string $default_url
* @param int $job_id
* @param null|string $return_url
*
* @return string
* @throws \InvalidArgumentException
*/
public function get_editor_url( $default_url, $job_id, $return_url = null ) {
if ( $this->translator_activation_records->is_current_user_activated() ) {
$ate_job_id = $this->ate_jobs->get_ate_job_id( $job_id );
if ( $ate_job_id ) {
if ( ! $return_url ) {
$return_url = add_query_arg(
array(
'page' => WPML_TM_FOLDER . '/menu/translations-queue.php',
'ate-return-job' => $job_id,
),
admin_url( '/admin.php' ) );
}
$ate_job_url = $this->ate_api->get_editor_url( $ate_job_id, $return_url );
if ( $ate_job_url && ! is_wp_error( $ate_job_url ) ) {
return $ate_job_url;
}
}
}
return $default_url;
}
/**
* @param $ignore
* @param array $translation_jobs
*
* @return array
*/
public function get_ate_jobs_data_filter( $ignore, array $translation_jobs ) {
return $this->get_get_ate_jobs_data( $translation_jobs );
}
private function get_get_ate_jobs_data( array $translation_jobs ) {
$ate_jobs_data = array();
$skip_getting_data = false;
$ate_jobs_to_create = array();
foreach ( $translation_jobs as $translation_job ) {
if ( $this->is_a_local_translation_job( $translation_job ) ) {
$ate_job_id = $this->get_ate_job_id( $translation_job->job_id );
if ( ! $ate_job_id ) {
$ate_jobs_to_create[] = $translation_job->job_id;
$skip_getting_data = true;
}
if ( ! $skip_getting_data ) {
$ate_jobs_data[ $translation_job->job_id ] = array(
'ate_job_id' => $ate_job_id,
'progress' => $this->get_ate_job_progress( $translation_job->job_id ),
);
}
}
}
if (
! $this->is_second_attempt_to_get_jobs_data &&
$ate_jobs_to_create &&
$this->added_translation_jobs( array( 'local' => $ate_jobs_to_create ) )
) {
$ate_jobs_data = $this->get_get_ate_jobs_data( $translation_jobs );
$this->is_second_attempt_to_get_jobs_data = true;
}
return $ate_jobs_data;
}
private function get_ate_job_id( $job_id ) {
return $this->ate_jobs->get_ate_job_id( $job_id );
}
private function get_ate_job_progress( $job_id ) {
return $this->ate_jobs->get_ate_job_progress( $job_id );
}
public function update_jobs_on_current_screen() {
if ( $this->is_edit_list_page_of_a_translatable_type() || $this->is_edit_page_of_a_translatable_type() ) {
$translation_jobs = $this->get_local_jobs_from_posts( $this->current_screen->get_posts() );
if ( $translation_jobs ) {
$this->update_jobs( null, $translation_jobs, true );
}
}
}
/**
* @param bool $updated
* @param array|stdClass $translation_jobs
* @param bool $ignore_errors
*
* @return bool
*/
public function update_jobs( $updated, $translation_jobs, $ignore_errors = false ) {
/**
* We should only expect an array of objects.
* However, this method can be called by an action and a known issue may cause to pass a single object instead
*
* @see https://developer.wordpress.org/reference/functions/do_action/#comment-2371
*/
if ( is_object( $translation_jobs ) ) {
if ( isset( $translation_jobs->job_id ) ) {
$translation_jobs = array( $translation_jobs );
} else {
$translation_jobs = null;
}
}
if ( $translation_jobs ) {
$ate_jobs_data = $this->get_get_ate_jobs_data( $translation_jobs );
if ( ! $ate_jobs_data ) {
return false;
}
$job_ids_map = array();
foreach ( $translation_jobs as $translation_job ) {
if ( $this->is_a_local_translation_job( $translation_job ) ) {
$ate_job_id = null;
if ( isset( $ate_jobs_data[ $translation_job->job_id ]['ate_job_id'] ) ) {
$ate_job_id = $ate_jobs_data[ $translation_job->job_id ]['ate_job_id'];
$job_ids_map[ $ate_job_id ] = $translation_job->job_id;
}
}
}
if ( $job_ids_map ) {
$ate_job_ids = array_keys( $job_ids_map );
$response = $this->ate_api->get_non_delivered_ate_jobs( $ate_job_ids );
$this->check_response_error( $response );
$processed = json_decode( wp_json_encode( $response ), true );
if ( $processed ) {
$update_errors = 0;
$ack_errors = 0;
foreach ( $processed as $ate_job_id => $job_status ) {
if ( array_key_exists( $ate_job_id, $job_ids_map ) ) {
$job_stored = $this->ate_jobs->store( $job_ids_map[ $ate_job_id ], $job_status );
$job_updated = ! array_key_exists( 'error', $job_stored );
if ( ! $job_updated ) {
if ( ! $ignore_errors ) {
/** @var WP_Error $response */
throw new RuntimeException( $job_stored['error']['message'], $job_stored['error']['code'] );
}
$update_errors++;
}
if ( $job_updated ) {
if ( $this->must_acknowledge_ATE( $job_status ) ) {
if ( ! $this->confirm_received_job( $ate_job_id, $ignore_errors ) ) {
$ack_errors++;
}
}
}
}
}
$updated = ( $update_errors + $ack_errors ) === 0;
}
}
}
return $updated;
}
/**
* @param $ate_job_id
* @param $ignore_errors
*
* @return bool
*/
private function confirm_received_job( $ate_job_id, $ignore_errors ) {
$confirmation_response = $this->ate_api->confirm_received_job( $ate_job_id );
try {
$this->check_response_error( $confirmation_response );
return true;
} catch ( Exception $ex ) {
if ( ! $ignore_errors ) {
throw new $ex;
}
return false;
}
}
/**
* @param mixed $response
*
* @throws \RuntimeException
*/
protected function check_response_error( $response ) {
if ( is_wp_error( $response ) ) {
$code = 0;
$message = $response->get_error_message();
if ( $response->error_data && is_array( $response->error_data ) ) {
foreach ( $response->error_data as $http_code => $error_data ) {
$code = $error_data[0]['status'];
$message = '';
switch ( (int) $code ) {
case self::RESPONSE_ATE_NOT_ACTIVE_ERROR:
$wp_admin_url = admin_url( 'admin.php' );
$mcsetup_page = add_query_arg( array(
'page' => WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_SETTINGS,
'sm' => 'mcsetup',
), $wp_admin_url );
$mcsetup_page .= '#ml-content-setup-sec-1';
$resend_link = ''
. esc_html__( 'Resend that email', 'wpml-translation-management' )
. '';
$message .= ''
. esc_html__( 'WPML cannot send these documents to translation because the Advanced Translation Editor is not fully set-up yet.', 'wpml-translation-management' )
. '
'
. esc_html__( 'Please open the confirmation email that you received and click on the link inside it to confirm your email.', 'wpml-translation-management' )
. '
'
. $resend_link
. '
';
break;
case self::RESPONSE_ATE_DUPLICATED_SOURCE_ID:
case self::RESPONSE_ATE_UNEXPECTED_ERROR:
default:
$message = ''
. __( 'Advanced Translation Editor error:', 'wpml-translation-management' )
. '
'
. $error_data[0]['message']
. '
';
}
$message = '' . $message . '
';
}
}
/** @var WP_Error $response */
throw new RuntimeException( $message, $code );
}
}
/**
* @param $ate_job_id
*/
private function resign_job_on_error( $ate_job_id ) {
$job_id = $this->ate_jobs->get_wpml_job_id( $ate_job_id );
if ( $job_id ) {
wpml_load_core_tm()->resign_translator( $job_id );
}
}
/**
* @param array $posts
*
* @return array
*/
private function get_local_jobs_from_posts( array $posts ) {
$translation_jobs = array();
if ( $posts ) {
$tm_core = wpml_load_core_tm();
$languages = $this->sitepress->get_active_languages();
$languages_codes = array_keys( $languages );
/** @var WP_Post|stdClass $post */
foreach ( $posts as $post ) {
$post = $this->get_wp_post( $post );
if ( $post ) {
$trid = $this->sitepress->get_element_trid( $post->ID, 'post_' . $post->post_type );
$original_element = $this->get_original_element( $trid, 'post_' . $post->post_type );
if ( $trid && $original_element && (int) $original_element->element_id === $post->ID ) {
foreach ( $languages_codes as $language_code ) {
$job_id = $tm_core->get_translation_job_id( $trid, $language_code );
if ( $job_id ) {
$translation_job = $tm_core->get_translation_job( $job_id );
if ( $translation_job && $this->is_a_local_translation_job( $translation_job ) ) {
$translation_jobs[] = $translation_job;
}
}
}
}
}
}
}
return $translation_jobs;
}
/**
* @param $translation_job
*
* @return bool
*/
private function is_a_local_translation_job( $translation_job ) {
return 'local' === $translation_job->translation_service;
}
/**
* @param $post
*
* @return array|null|WP_Post
*/
private function get_wp_post( $post ) {
if ( ! $post instanceof WP_Post ) {
if ( isset( $post->ID ) ) {
$post = get_post( $post->ID );
} else {
$post = null;
}
}
return $post;
}
/**
* @return bool
*/
private function is_edit_list_page_of_a_translatable_type() {
return $this->current_screen->is_edit_posts_list()
&& $this->sitepress->is_translated_post_type( $this->current_screen->get_post_type() );
}
/**
* @return bool
*/
private function is_edit_page_of_a_translatable_type() {
return $this->current_screen->is_edit_post()
&& $this->sitepress->is_translated_post_type( $this->current_screen->get_post_type() );
}
/**
* @param int $trid
* @param string $element_type
*
* @return mixed
*/
private function get_original_element( $trid, $element_type ) {
if ( ! array_key_exists( $trid, $this->trid_original_element_map ) ) {
$element_translation = $this->sitepress->get_original_element_translation( $trid, $element_type );
if ( $element_translation ) {
$this->trid_original_element_map[ $trid ] = $element_translation;
return $element_translation;
}
}
return $this->trid_original_element_map[ $trid ];
}
/**
* @param $job_status
*
* @return bool
*/
private function must_acknowledge_ATE( $job_status ) {
return $job_status['status_id'] === WPML_TM_ATE_AMS_Endpoints::ATE_JOB_STATUS_DELIVERING;
}
}