first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
<?php
function translation_service_details( $service, $show_project = false ) {
$service_details = '';
if ( defined( 'OTG_SANDBOX_DEBUG' ) && OTG_SANDBOX_DEBUG ) {
$service_details .= '<h3>Service details:</h3>' . PHP_EOL;
$service_details .= '<pre>' . PHP_EOL;
$service_details .= print_r( $service, true );
$service_details .= '</pre>' . PHP_EOL;
if ( $show_project ) {
$project = TranslationProxy::get_current_project();
echo '<pre>$project' . PHP_EOL;
echo print_r( $project, true );
echo '</pre>';
}
}
return $service_details;
}
if ( ! function_exists( 'object_to_array' ) ) {
function object_to_array( $obj ) {
if ( is_object( $obj ) ) {
$obj = (array) $obj;
}
if ( is_array( $obj ) ) {
$new = array();
foreach ( $obj as $key => $val ) {
$new[ $key ] = object_to_array( $val );
}
} else {
$new = $obj;
}
return $new;
}
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* @package wpml-core
* @subpackage wpml-core
*/
if( class_exists( 'TranslationProxy_Api' ) ) {
// Workaround for UnitTests.
return;
}
class TranslationProxy_Api {
const API_VERSION = 1.1;
public static function proxy_request(
$path,
$params = array(),
$method = 'GET',
$multi_part = false,
$has_return_value = true
) {
return wpml_tm_load_tp_networking()->send_request(
OTG_TRANSLATION_PROXY_URL . $path,
$params,
$method,
$has_return_value
);
}
public static function proxy_download( $path, $params ) {
return wpml_tm_load_tp_networking()->send_request(
OTG_TRANSLATION_PROXY_URL . $path,
$params,
'GET',
true,
false
);
}
public static function service_request(
$url,
$params = array(),
$method = 'GET',
$has_return_value = true,
$json_response = false,
$has_api_response = false
) {
return wpml_tm_load_tp_networking()->send_request(
$url,
$params,
$method,
$has_return_value,
$json_response,
$has_api_response
);
}
public static function add_parameters_to_url( $url, $params ) {
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 ] ) ) {
$value = $params[ $index ];
$url = preg_replace( preg_quote( "/$symb/" ), $value, $url );
}
}
}
}
}
return $url;
}
}
if ( ! function_exists( 'gzdecode' ) ) {
/**
* Inflates a string enriched with gzip headers. Counterpart to gzencode().
* Extracted from upgradephp
* http://include-once.org/p/upgradephp/
*
* officially available by default in php @since 5.4.
*/
function gzdecode( $gzdata, $maxlen = null ) {
// -- decode header
$len = strlen( $gzdata );
if ( $len < 20 ) {
return;
}
$head = substr( $gzdata, 0, 10 );
$head = unpack( 'n1id/C1cm/C1flg/V1mtime/C1xfl/C1os', $head );
list( $ID, $CM, $FLG, $MTIME, $XFL, $OS ) = array_values( $head );
$FTEXT = 1 << 0;
$FHCRC = 1 << 1;
$FEXTRA = 1 << 2;
$FNAME = 1 << 3;
$FCOMMENT = 1 << 4;
$head = unpack( 'V1crc/V1isize', substr( $gzdata, $len - 8, 8 ) );
list( $CRC32, $ISIZE ) = array_values( $head );
// -- check gzip stream identifier
if ( $ID != 0x1f8b ) {
trigger_error( 'gzdecode: not in gzip format', E_USER_WARNING );
return;
}
// -- check for deflate algorithm
if ( $CM != 8 ) {
trigger_error( 'gzdecode: cannot decode anything but deflated streams', E_USER_WARNING );
return;
}
// -- start of data, skip bonus fields
$s = 10;
if ( $FLG & $FEXTRA ) {
$s += $XFL;
}
if ( $FLG & $FNAME ) {
$s = strpos( $gzdata, "\000", $s ) + 1;
}
if ( $FLG & $FCOMMENT ) {
$s = strpos( $gzdata, "\000", $s ) + 1;
}
if ( $FLG & $FHCRC ) {
$s += 2; // cannot check
}
// -- get data, uncompress
$gzdata = substr( $gzdata, $s, $len - $s );
if ( $maxlen ) {
$gzdata = gzinflate( $gzdata, $maxlen );
return ( $gzdata ); // no checks(?!)
} else {
$gzdata = gzinflate( $gzdata );
}
// -- check+fin
$chk = crc32( $gzdata );
if ( $CRC32 != $chk ) {
trigger_error( "gzdecode: checksum failed (real$chk != comp$CRC32)", E_USER_WARNING );
} elseif ( $ISIZE != strlen( $gzdata ) ) {
trigger_error( 'gzdecode: stream size mismatch', E_USER_WARNING );
} else {
return ( $gzdata );
}
}
}

View File

@@ -0,0 +1,927 @@
<?php
/**
* @package wpml-core
* @subpackage wpml-core
*/
use WPML\TM\API\Jobs;
if ( ! class_exists( 'TranslationProxy_Basket' ) ) {
/**
* TranslationProxy_basket collects all static methods to operate on
* translations basket (cart)
*/
class TranslationProxy_Basket {
private static $messages;
private static $dashboard_select;
private static $basket;
// The name of the option stored in wp_options table and that
// stores all the basket items
const ICL_TRANSLATION_JOBS_BASKET = 'icl_translation_jobs_basket';
private static $posts_ids;
private static $translate_from;
private static $translation_action;
public static function add_message( $array ) {
self::$messages[] = $array;
}
public static function remove_message( $text ) {
if ( is_array( self::$messages ) ) {
foreach ( self::$messages as $key => $message ) {
if ( array_key_exists( 'text', $message ) && $message['text'] === $text ) {
unset( self::$messages[ $key ] );
}
}
}
}
public static function get_basket( $force = false ) {
if ( ! isset( self::$basket ) || $force ) {
self::$basket = get_option( self::ICL_TRANSLATION_JOBS_BASKET, [] );
}
return self::$basket;
}
public static function update_basket( $basket_portion = array() ) {
if ( ! empty( $basket_portion ) ) {
if ( ! self::$basket || self::get_basket_items_count() == 0 ) {
self::$basket = $basket_portion;
} else {
self::$basket = self::merge_baskets( self::$basket, $basket_portion );
}
}
if ( self::get_basket_items_count( true ) == 0 ) {
self::$basket = [];
}
self::sync_target_languages();
self::update_basket_option( self::$basket );
self::update_basket_notifications();
}
/**
* @param array $basket
*/
private static function update_basket_option( $basket ) {
update_option( self::ICL_TRANSLATION_JOBS_BASKET, $basket, false );
}
private static function merge_baskets( $from, $to ) {
if ( function_exists( 'array_replace_recursive' ) ) {
return array_replace_recursive( $from, $to );
} else {
return self::array_replace_recursive( $from, $to );
}
}
/**
* Return number of items in translation basket by key
*
* @param string $type
* @param bool $skip_cache
*
* @return int number of items in translation basket
*/
public static function get_basket_items_type_count( $type, $skip_cache = false ) {
$cache_key = $type;
$cache_group = 'get_basket_items_type_count';
$cache_found = false;
if ( ! $skip_cache ) {
$basket_items_number = (int) wp_cache_get( $cache_key, $cache_group, false, $cache_found );
} else {
$basket_items_number = 0;
}
if ( $cache_found ) {
return $basket_items_number;
}
self::get_basket();
if ( self::$basket ) {
if ( isset( self::$basket[ $type ] ) ) {
$posts = self::$basket[ $type ];
$basket_items_number += count( $posts );
}
}
if ( ! $skip_cache ) {
wp_cache_set( $cache_key, $basket_items_number, $cache_group );
}
return $basket_items_number;
}
/**
* Return number of items in translation basket
*
* @param bool $skip_cache
*
* @return int number of items in translation basket
*/
public static function get_basket_items_count( $skip_cache = false ) {
$basket_items_number = 0;
$basket_items_types = self::get_basket_items_types();
foreach ( $basket_items_types as $item_type_name => $item_type ) {
$basket_items_number += self::get_basket_items_type_count( $item_type_name, $skip_cache );
}
return $basket_items_number;
}
/**
* Register notification with number of items in basket and link to basket
*/
public static function update_basket_notifications() {
$positions = self::get_basket_notification_positions();
$basket_link = 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=basket';
foreach ( $positions as $position => $group ) {
ICL_AdminNotifier::remove_message_group( $position );
}
self::get_basket();
$basket_items_count = self::get_basket_items_count( true );
$limit_to_page = array();
if ( defined( 'WPML_ST_FOLDER' ) ) {
$limit_to_page[] = WPML_ST_FOLDER . '/menu/string-translation.php';
}
// if we have something in the basket
if ( self::is_st_page() && $basket_items_count > 0 && ( ! isset( $_GET['clear_basket'] ) || $_GET['clear_basket'] != 1 ) && ( ! isset( $_GET['action'] ) || $_GET['action'] != 'delete' ) ) {
$text = __( 'The items you have selected are now in the translation basket &ndash;', 'wpml-translation-management' );
$text .= ' ' . sprintf( __( '<a href="%s">Send to translation &raquo;</a>', 'wpml-translation-management' ), $basket_link );
// translation management pages
$message_args = array(
'id' => $positions['tm_dashboard_top'],
'text' => $text,
'classes' => 'small',
'type' => 'information small',
'group' => $positions['tm_dashboard_top'],
'admin_notice' => false,
'hide_per_user' => false,
'dismiss_per_user' => false,
'limit_to_page' => $limit_to_page,
'capability' => 'manage_translations',
);
ICL_AdminNotifier::add_message( $message_args );
} else {
ICL_AdminNotifier::remove_message( $positions['tm_dashboard_top'] );
}
$admin_basket_message_id = $positions['admin_notice'];
if ( ( self::$messages || $basket_items_count > 0 ) && self::is_st_page() ) {
$additional_messages = array();
if ( isset( self::$messages ) && is_array( self::$messages ) ) {
foreach ( self::$messages as $message ) {
$additional_messages[] = $message['text'];
}
}
$additional_messages_text = '';
if ( count( $additional_messages ) > 0 ) {
$additional_messages_text = '<ul><li>' . implode( '</li><li>', $additional_messages ) . '</li></ul>';
}
$message_args = array(
'id' => $admin_basket_message_id,
'text' => $additional_messages_text,
'classes' => 'small',
'type' => 'information',
'group' => $admin_basket_message_id,
'admin_notice' => true,
'hide_per_user' => false,
'dismiss_per_user' => false,
'limit_to_page' => $limit_to_page,
'show_once' => true,
);
if ( trim( $additional_messages_text ) != '' ) {
ICL_AdminNotifier::add_message( $message_args );
}
} else {
ICL_AdminNotifier::remove_message( $admin_basket_message_id );
}
}
private static function is_st_page() {
return defined( 'WPML_ST_FOLDER' ) && array_key_exists( 'page', $_GET ) && false !== strpos( $_GET['page'], WPML_ST_FOLDER );
}
/**
* Displays div with number of items in basket and link to basket
* Removes notification if basket is empty
*/
public static function display_basket_items_notification() {
ICL_AdminNotifier::display_messages( 'translation-basket-notification' );
}
public static function is_in_basket( $post_id, $source_language, $target_language, $item_type = 'post' ) {
self::get_basket();
if ( ! self::$basket || ! isset( self::$basket[ $item_type ][ $post_id ] ) ) {
return false;
}
$basket_item = self::$basket[ $item_type ][ $post_id ];
return $basket_item['from_lang'] == $source_language && isset( $basket_item['to_langs'][ $target_language ] ) && $basket_item['to_langs'][ $target_language ];
}
/**
* Checks if post with ID $post_id is in the basket for any language
*
* @param int $post_id
* @param string $element_type
* @param array $check_in_languages
* @param bool $original_language_code
*
* @return bool
*/
public static function anywhere_in_basket( $post_id, $element_type = 'post', $check_in_languages = array(), $original_language_code = false ) {
$basket = self::get_basket();
if ( $post_id && isset( $basket[ $element_type ][ $post_id ] ) ) {
if ( $check_in_languages ) {
if ( ! $original_language_code ) {
$original_language_code = $basket['source_language'];
}
foreach ( $check_in_languages as $language_code => $language_data ) {
if ( $language_code != $original_language_code && isset( $basket[ $element_type ][ $post_id ]['to_langs'][ $language_code ] ) ) {
return true;
}
}
return false;
} else {
return true;
}
}
return false;
}
public static function is_string_in_basket_anywhere( $string_id ) {
return self::anywhere_in_basket( $string_id, 'string' );
}
public static function has_any_string() {
return self::has_any_item_type( 'string' );
}
public static function has_any_item_type( $item_type ) {
self::get_basket();
return isset( self::$basket[ $item_type ] ) && count( self::$basket[ $item_type ] );
}
/**** adding items to basket ****/
/**
* Serves Translation Dashboard form submission and adds posts to basket
*
* @param array $data data submitted from form
*
* @return boolean
*/
public static function add_posts_to_basket( $data ) {
self::get_basket();
global $sitepress, $iclTranslationManagement;
$wpml_translation_job_factory = wpml_tm_load_job_factory();
extract( $data, EXTR_OVERWRITE );
ICL_AdminNotifier::remove_message( 'the_basket_items_notification' );
self::$translation_action = null;
if ( isset( $data['tr_action'] ) ) { // adapt new format
self::$translation_action = $data['tr_action'];
}
if ( ! isset( $data['tr_action'] ) && isset( $data['translate_to'] ) ) { // adapt new format
$data['tr_action'] = $data['translate_to'];
self::$translation_action = $data['tr_action'];
unset( $data['translate_to'] );
}
self::$posts_ids = self::get_elements_ids( $data, 'post' );
self::$translate_from = $data ['translate_from']; // language of the submitted posts transported by hidden field
$data_is_valid = self::validate_data( $data );
if ( ! $data_is_valid ) {
return false;
}
// check tr_action and do what user decided
foreach ( self::$translation_action as $language_code => $status ) {
$language_name = $sitepress->get_display_language_name( $language_code );
// if he decided duplicate or not to translate for this particular language,
// try to remove it from wp_options
$basket_item_type = 'post';
if ( $status == 2 ) {
// iterate posts ids, check if they are in wp_options
// if they are set to translate for this particular language
// end then remove it
foreach ( self::$posts_ids as $id ) {
if ( isset( self::$basket[ $basket_item_type ][ $id ]['to_langs'][ $language_code ] ) ) {
unset( self::$basket[ $basket_item_type ][ $id ]['to_langs'][ $language_code ] );
}
// if user want to duplicate this post, lets do this
if ( $status == 2 ) {
$iclTranslationManagement->make_duplicate( $id, $language_code );
}
}
} elseif ( $status == 1 ) {
foreach ( self::$posts_ids as $id ) {
$send_to_basket = true;
$post = self::get_post( $id );
$post_type = $post->post_type;
$messages = new \WPML\TM\Jobs\Dispatch\Messages();
global $wpdb;
$source_language_code = $wpdb->get_var(
$wpdb->prepare(
" SELECT source_language_code
FROM {$wpdb->prefix}icl_translations
WHERE element_type LIKE 'post_%%'
AND element_id = %d",
$post->ID
)
);
if ( $source_language_code != $language_code ) {
$job = Jobs::getPostJob( $id, $post_type, $language_code );
if ( $job && ICL_TM_IN_PROGRESS === $job->status && ! $job->needs_update ) {
self::$messages[] = array(
'type' => 'update',
'text' => $messages->ignoreInProgressPostMessage( $post, $language_name ),
);
$send_to_basket = false;
}
} else {
self::$messages[] = array(
'type' => 'update',
'text' => $messages->ignoreOriginalPostMessage( $post, $language_name ),
);
$send_to_basket = false;
}
if ( $send_to_basket ) {
self::$basket[ $basket_item_type ][ $id ]['from_lang'] = self::$translate_from;
self::$basket[ $basket_item_type ][ $id ]['to_langs'][ $language_code ] = 1;
// set basket language if not already set
if ( ! isset( self::$basket['source_language'] ) ) {
self::$basket['source_language'] = self::$translate_from;
}
}
}
}
}
self::update_basket();
return true;
}
/**
* Serves WPML > String translation form submission and adds strings to basket
*
* @param array $string_ids identifiers of strings
* @param $source_language
* @param array $target_languages selected target languages
* @return bool
* @todo: [WPML 3.3] move to ST and handle with hooks
*/
public static function add_strings_to_basket( $string_ids, $source_language, $target_languages ) {
global $wpdb, $sitepress;
self::get_basket();
ICL_AdminNotifier::remove_message( 'the_basket_items_notification' );
/*
structure of cart in get_option:
* [posts]
* [element_id]
* [to_langs]
* [language_code] fr | pl | de ... with value 1
* [strings]
* [string_id]
* [to_langs]
* [language_code]
*/
// no post selected ?
if ( empty( $string_ids ) ) {
self::$messages[] = array(
'type' => 'error',
'text' => __( 'Please select at least one document to translate.', 'wpml-translation-management' ),
);
self::update_basket();
return false;
}
// no language selected ?
if ( empty( $target_languages ) ) {
self::$messages[] = array(
'type' => 'error',
'text' => __( 'Please select at least one language to translate into.', 'wpml-translation-management' ),
);
self::update_basket();
return false;
}
if ( self::get_basket() && self::get_source_language() ) {
/*
we do not add items that are not in the source language of the current basket
we cannot yet set its source language though since update_basket would set the basket
to false oso long as we do not have any elements in the basket*/
if ( $source_language != self::get_source_language() ) {
self::$messages[] = array(
'type' => 'update',
'text' => __(
'You cannot add strings in this language to the basket since it already contains posts or strings of another source language!
Either submit the current basket and then add the post or delete the posts of differing language in the current basket',
'wpml-translation-management'
),
);
self::update_basket();
return false;
}
}
foreach ( $target_languages as $target_language => $selected ) {
if ( $target_language == $source_language ) {
continue;
}
$target_language_name = $sitepress->get_display_language_name( $target_language );
foreach ( $string_ids as $id ) {
$send_to_basket = true;
$query = " SELECT {$wpdb->prefix}icl_string_translations.status,
{$wpdb->prefix}icl_strings.value
FROM {$wpdb->prefix}icl_string_translations
INNER JOIN {$wpdb->prefix}icl_strings
ON {$wpdb->prefix}icl_string_translations.string_id = {$wpdb->prefix}icl_strings.id
WHERE {$wpdb->prefix}icl_string_translations.string_id=%d
AND {$wpdb->prefix}icl_string_translations.language=%s";
$string_translation = $wpdb->get_row( $wpdb->prepare( $query, $id, $target_language ) );
if ( ! is_null( $string_translation ) && $string_translation->status == ICL_TM_WAITING_FOR_TRANSLATOR ) {
self::$messages[] = array(
'type' => 'update',
'text' => sprintf(
__(
'String "%1$s" will be ignored for %2$s, because translation is already waiting for translator.',
'wpml-translation-management'
),
$string_translation->value,
$target_language_name
),
);
$send_to_basket = false;
}
if ( $send_to_basket ) {
self::$basket['string'][ $id ]['from_lang'] = $source_language;
self::$basket['string'][ $id ]['to_langs'][ $target_language ] = 1;
// set basket language if not already set
if ( ! isset( self::$basket['source_language'] ) ) {
self::$basket['source_language'] = $source_language;
}
}
}
}
self::update_basket();
return true;
}
/**
* Serves deletion of items from basket, triggered from WPML TM > Translation
* Jobs
*
* @param array $items Array of items ids, in two separate parts: ['post']
* and ['string']
*/
public static function delete_items_from_basket( $items ) {
self::get_basket();
$basket_items_types = self::get_basket_items_types();
foreach ( $basket_items_types as $item_type_name => $item_type ) {
if ( ! empty( $items[ $item_type_name ] ) ) {
foreach ( $items[ $item_type_name ] as $id ) {
self::delete_item_from_basket( $id, $item_type_name, false );
}
}
}
self::update_basket();
}
/**
* Removes one item from basket
*
* @param int $id Item ID
* @param string $type Item type (strings | posts | ...)
* @param bool $update_option do update_option('icl_translation_jobs_cart' ?
*/
public static function delete_item_from_basket( $id, $type = 'post', $update_option = true ) {
self::get_basket();
if ( isset( self::$basket[ $type ][ $id ] ) ) {
unset( self::$basket[ $type ][ $id ] );
if ( count( self::$basket[ $type ] ) == 0 ) {
unset( self::$basket[ $type ] );
}
}
if ( self::get_basket_items_count( true ) == 0 ) {
self::$basket = array();
}
if ( $update_option ) {
self::update_basket( self::$basket );
} else {
self::update_basket_notifications();
}
}
// TODO: [WPML 3.3] implement this in the troubleshooting page
public static function delete_all_items_from_basket() {
self::$basket = [];
delete_option( self::ICL_TRANSLATION_JOBS_BASKET );
self::update_basket();
}
/**
* @param WPML_TP_Batch|null $batch
*/
public static function set_batch_data( $batch ) {
self::get_basket();
self::$basket['batch'] = $batch;
self::update_basket();
}
/**
* @return false|null|WPML_TP_Batch
*/
public static function get_batch_data() {
self::get_basket();
return isset( self::$basket['batch'] ) ? self::$basket['batch'] : false;
}
public static function set_basket_name( $basket_name ) {
self::get_basket();
self::$basket['name'] = $basket_name;
self::update_basket();
}
public static function get_basket_name() {
self::get_basket();
return isset( self::$basket['name'] ) ? self::$basket['name'] : false;
}
public static function set_options( array $options ) {
self::$basket['options'] = $options;
}
/** @return array */
public static function get_options() {
return isset( self::$basket['options'] ) ? self::$basket['options'] : array();
}
public static function get_basket_extra_fields() {
if ( isset( $_REQUEST['extra_fields'] ) ) {
$extra_fields_string = urldecode( $_REQUEST['extra_fields'] );
if ( strlen( $extra_fields_string ) > 0 ) {
$extra_fields_rows = explode( '|', $extra_fields_string );
$result = array();
foreach ( $extra_fields_rows as $row ) {
$row_data = explode( ':', $row );
if ( count( $row_data ) == 2 ) {
$result[ $row_data[0] ] = $row_data[1];
}
}
}
}
if ( isset( $result ) && count( $result ) > 0 ) {
return $result;
}
return false;
}
private static function array_replace_recursive( $array, $array1 ) {
if ( function_exists( 'array_replace_recursive' ) ) {
$array = array_replace_recursive( $array, $array1 );
} else {
// handle the arguments, merge one by one
$args = func_get_args();
$array = $args[0];
if ( ! is_array( $array ) ) {
return $array;
}
for ( $i = 1; $i < count( $args ); $i ++ ) {
if ( is_array( $args[ $i ] ) ) {
$array = self::recurse( $array, $args[ $i ] );
}
}
}
return $array;
}
private static function recurse( $array, $array1 ) {
foreach ( $array1 as $key => $value ) {
// create new key in $array, if it is empty or not an array
if ( ! isset( $array[ $key ] ) || ( isset( $array[ $key ] ) && ! is_array( $array[ $key ] ) ) ) {
$array[ $key ] = array();
}
// overwrite the value in the base array
if ( is_array( $value ) ) {
$value = self::recurse( $array[ $key ], $value );
}
$array[ $key ] = $value;
}
return $array;
}
public static function get_basket_items_types() {
return apply_filters(
'wpml_tm_basket_items_types',
[
'string' => 'core',
'post' => 'core',
'package' => 'custom',
]
);
}
/**
* @param $post_id
*
* @return mixed|null|void|WP_Post
*/
private static function get_post( $post_id ) {
if ( is_string( $post_id ) && strcmp( substr( $post_id, 0, strlen( 'external_' ) ), 'external_' ) === 0 ) {
$item = apply_filters( 'wpml_get_translatable_item', null, $post_id );
} else {
$item = get_post( $post_id );
}
return $item;
}
/**
* @param array $selected_elements
*
* @param bool|string $type
* @return array[]|int[]
*/
public static function get_elements_ids( $selected_elements, $type = false ) {
$element_ids = array();
$legal_item_types = $type ? array( $type ) : array_keys( self::get_basket_items_types() );
foreach ( $legal_item_types as $item_type ) {
if ( ! isset( $selected_elements[ $item_type ] ) ) {
continue;
}
$element_ids[ $item_type ] = isset( $element_ids[ $item_type ] ) ? $element_ids[ $item_type ] : array();
$items = $selected_elements[ $item_type ];
foreach ( $items as $element_id => $action_data ) {
if ( isset( $action_data['checked'] ) && $action_data['checked'] ) {
$element_ids[ $item_type ][] = $element_id;
}
}
}
return $type && isset( $element_ids[ $type ] ) ? $element_ids[ $type ] : $element_ids;
}
public static function get_source_language() {
self::get_basket();
return isset( self::$basket['source_language'] ) ? self::$basket['source_language'] : false;
}
private static function sync_target_languages() {
self::get_basket();
if ( ! isset( self::$basket['target_languages'] ) ) {
self::$basket['target_languages'] = array();
}
$basket_items_types = self::get_basket_items_types();
foreach ( $basket_items_types as $item_type_name => $item_type ) {
if ( isset( self::$basket[ $item_type_name ] ) ) {
$posts_in_basket = self::$basket[ $item_type_name ];
foreach ( (array) $posts_in_basket as $post_in_basket ) {
foreach ( (array) $post_in_basket['to_langs'] as $key => $target_language ) {
if ( $target_language && ! in_array( $key, self::$basket['target_languages'] ) ) {
self::$basket['target_languages'] [] = $key;
}
}
}
}
}
}
/**
* @return bool|array
*/
public static function get_target_languages() {
self::get_basket();
self::sync_target_languages();
return isset( self::$basket['target_languages'] ) ? self::$basket['target_languages'] : false;
}
/**
* Sets target languages for remote service
*
* @param $remote_target_languages
*/
public static function set_remote_target_languages( $remote_target_languages ) {
self::get_basket();
self::$basket['remote_target_languages'] = $remote_target_languages;
self::update_basket();
}
/**
* Get target languages for remote service
*
* @return array | false
*/
public static function get_remote_target_languages() {
self::get_basket();
if ( isset( self::$basket['remote_target_languages'] ) ) {
return self::$basket['remote_target_languages'];
} else {
return self::get_target_languages();
}
}
/**
* @return array
*/
public static function get_basket_notification_positions() {
return array(
'admin_notice' => 'basket_status_update',
'tm_dashboard_top' => 'translation-basket-notification',
'st_dashboard_top' => 'string-translation-top',
'st_dashboard_bottom' => 'string-translation-under',
);
}
public static function get_basket_extra_fields_section() {
$extra_fields = TranslationProxy::get_extra_fields_local();
$html = '';
if ( $extra_fields ) {
$html .= '<h3>3. ' . __( 'Select additional options', 'wpml-translation-management' ) . ' <a href="#" id="basket_extra_fields_refresh">(' . __( 'Refresh', 'wpml-translation-management' ) . ')</a></h3>';
$html .= '<div id="basket_extra_fields_list">';
$html .= self::get_basket_extra_fields_inputs( $extra_fields, false );
$html .= '</div>';
}
return $html;
}
public static function get_basket_extra_fields_inputs( array $extra_fields = array(), $force_refresh = false ) {
if ( ! $extra_fields ) {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
$force_refresh = true;
}
$extra_fields = self::get_basket_extra_fields_array( $force_refresh );
}
return self::extra_fields_build_inputs( $extra_fields );
}
public static function get_basket_extra_fields_array( $force_refresh = false ) {
if ( $force_refresh ) {
$networking = wpml_tm_load_tp_networking();
$project = TranslationProxy::get_current_project();
$extra_fields = $networking->get_extra_fields_remote( $project );
TranslationProxy::save_extra_fields( $extra_fields );
} else {
$extra_fields = TranslationProxy::get_extra_fields_local();
}
return TranslationProxy::maybe_convert_extra_fields( $extra_fields );
}
public static function extra_fields_build_inputs( array $extra_fields ) {
if ( ! $extra_fields ) {
return '';
}
$rows = array();
/** @var WPML_TP_Extra_Field $field */
$field_diplay = new WPML_TP_Extra_Field_Display();
foreach ( $extra_fields as $field ) {
$rows[] = $field_diplay->render( $field );
}
$rows = array_filter( $rows );
$html = '';
if ( $rows ) {
$html = '<table class="form-table">';
$html .= '<tbody>';
$html .= implode( PHP_EOL, $rows );
$html .= '</tbody>';
$html .= '</table>';
}
return $html;
}
/**
* @param $data
*
* @return bool
*/
private static function validate_data( $data ) {
$data_is_valid = true;
if ( self::get_basket() && self::get_source_language() ) {
/*
we do not add items that are not in the source language of the current basket
we cannot yet set its source language though since update_basket would set the basket
to false as long as we do not have any elements in the basket*/
if ( self::$translate_from != self::get_source_language() ) {
self::$messages[] = array(
'type' => 'update',
'text' => __(
'You cannot add posts in this language to the basket since it already contains posts or strings of another source language!
Either submit the current basket and then add the post or delete the posts of differing language in the current basket',
'wpml-translation-management'
),
);
self::update_basket();
$data_is_valid = false;
}
}
// no language selected ?
if ( ! isset( self::$translation_action ) || empty( self::$translation_action ) ) {
self::$messages[] = array(
'type' => 'error',
'text' => __( 'Please select at least one language to translate into.', 'wpml-translation-management' ),
);
self::$dashboard_select = $data; // pre fill dashboard
$data_is_valid = false;
}
if ( $data_is_valid ) {
$data_is_valid = false;
$basket_items_types = self::get_basket_items_types();
// nothing selected ?
foreach ( $basket_items_types as $basket_items_type => $basket_type ) {
if ( isset( $data[ $basket_items_type ] ) && $data[ $basket_items_type ] ) {
$data_is_valid = true;
break;
}
}
}
if ( ! $data_is_valid ) {
self::$messages[] = array(
'type' => 'error',
'text' => __( 'Please select at least one document to translate.', 'wpml-translation-management' ),
);
self::$dashboard_select = $data; // pre-populate dashboard
$data_is_valid = false;
return $data_is_valid;
}
return $data_is_valid;
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
use \WPML\Collect\Support\Traits\Macroable;
use function \WPML\FP\curryN;
use \WPML\LIB\WP\Cache;
use \WPML\FP\Logic;
/**
* Class TranslationProxy_Batch
*
* @method static callable|int getBatchId( ...$name ) :: string → int
*/
class TranslationProxy_Batch {
use Macroable;
public static function update_translation_batch(
$batch_name = false,
$tp_id = false
) {
$batch_name = $batch_name
? $batch_name
: ( ( (bool) $tp_id === false || $tp_id === 'local' )
? self::get_generic_batch_name() : TranslationProxy_Basket::get_basket_name() );
if ( ! $batch_name ) {
return null;
}
$getBatchId = function( $batch_name, $tp_id ) {
$batch_id = self::getBatchId( $batch_name );
return $batch_id ? $batch_id : self::createBatchRecord( $batch_name, $tp_id );
};
$cache = Cache::memorizeWithCheck( 'update_translation_batch', Logic::isNotNull(), $getBatchId );
return $cache( $batch_name, $tp_id );
}
/**
* returns the name of a generic batch
* name is built based on the current's date
*
* @param bool $isAuto
*
* @return string
*/
public static function get_generic_batch_name( $isAuto = false ) {
return ( $isAuto ? 'Automatic Translations from ' : 'Manual Translations from ' ) . date( 'F \t\h\e jS\, Y' );
}
/**
* returns the id of a generic batch
*
* @return int
*/
private static function create_generic_batch() {
$batch_name = self::get_generic_batch_name();
$batch_id = self::update_translation_batch( $batch_name );
return $batch_id;
}
public static function maybe_assign_generic_batch( $data ) {
global $wpdb;
$batch_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT batch_id
FROM {$wpdb->prefix}icl_translation_status
WHERE translation_id=%d",
$data['translation_id']
)
);
// if the batch id is smaller than 1 we assign the translation to the generic manual translations batch for today if the translation_service is local
if ( ( $batch_id < 1 ) && isset( $data ['translation_service'] ) && $data ['translation_service'] == 'local' ) {
// first we retrieve the batch id for today's generic translation batch
$batch_id = self::create_generic_batch();
// then we update the entry in the icl_translation_status table accordingly
$data_where = array( 'rid' => $data['rid'] );
$wpdb->update(
$wpdb->prefix . 'icl_translation_status',
array( 'batch_id' => $batch_id ),
$data_where
);
}
}
/**
* @param $batch_name
* @param $tp_id
*
* @return mixed
*/
private static function createBatchRecord( $batch_name, $tp_id ) {
global $wpdb;
$data = [
'batch_name' => $batch_name,
'last_update' => date( 'Y-m-d H:i:s' ),
];
if ( $tp_id ) {
$data['tp_id'] = $tp_id === 'local' ? 0 : $tp_id;
}
$wpdb->insert( $wpdb->prefix . 'icl_translation_batches', $data );
return $wpdb->insert_id;
}
}
/**
* @param $batch_name
*
* @return mixed
*/
TranslationProxy_Batch::macro(
'getBatchId',
curryN(
1,
function( $batch_name ) {
global $wpdb;
$batch_id_sql = "SELECT id FROM {$wpdb->prefix}icl_translation_batches WHERE batch_name=%s";
$batch_id_prepared = $wpdb->prepare( $batch_id_sql, $batch_name );
return $wpdb->get_var( $batch_id_prepared );
}
)
);

View File

@@ -0,0 +1,73 @@
<?php
class TranslationProxy_Popup {
public static function display() {
include_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-popup.php';
exit;
}
public static function get_link( $link, $args = array(), $just_url = false ) {
$defaults = array(
'title' => null,
'class' => '',
'id' => '',
'ar' => 0, // auto_resize
'unload_cb' => false, // onunload callback
);
$args = array_merge($defaults, $args);
/**
* @var title string
*/
$title = $args['title'];
/**
* @var $class string
*/
$class = $args['class'];
/**
* @var $id int
*/
$id = $args['id'];
/**
* @var $ar int
*/
$ar = $args['ar'];
/**
* @var $unload_cb bool
*/
$unload_cb = $args['unload_cb'];
if ( !empty( $ar ) ) {
$auto_resize = '&amp;auto_resize=1';
} else {
$auto_resize = '';
}
$unload_cb = isset( $unload_cb ) ? '&amp;unload_cb=' . $unload_cb : '';
$url_glue = false !== strpos( $link, '?' ) ? '&' : '?';
$link .= $url_glue . 'compact=1';
$nonce_snippet = '&amp;_icl_nonce=' . wp_create_nonce( 'reminder_popup_nonce' );
$action_and_nonce = 'admin.php?page=' . ICL_PLUGIN_FOLDER
. "/menu/languages.php&amp;icl_action=reminder_popup{$nonce_snippet}{$auto_resize}{$unload_cb}"
. "&amp;target=" . urlencode( $link );
if ( ! empty( $id ) ) {
$id = ' id="' . $id . '"';
}
if ( ! $just_url ) {
return '<a class="icl_thickbox ' . $class . '" title="' . $title . '" href="' . $action_and_nonce . '"' . $id . '>';
} else {
if ( ! $just_url ) {
return '<a class="icl_thickbox ' . $class . '" href="' . $action_and_nonce . '"' . $id . '>';
} else {
return $action_and_nonce;
}
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
define( 'ICL_LANGUAGE_NOT_SUPPORTED', 3 );
global $wpdb, $sitepress;
$target = filter_input( INPUT_GET, 'target', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$auto_resize = filter_input( INPUT_GET, 'auto_resize', FILTER_VALIDATE_BOOLEAN | FILTER_NULL_ON_FAILURE );
$unload_cb = filter_input( INPUT_GET, 'unload_cb', FILTER_SANITIZE_FULL_SPECIAL_CHARS | FILTER_NULL_ON_FAILURE );
// Adding a translator
if ( preg_match( '|^@select-translators;([^;]+);([^;]+)@|', $target, $matches ) ) {
$source_language = $matches[1];
$target_language = $matches[2];
$project = TranslationProxy::get_current_project();
try {
$lp_setting_index = 'language_pairs';
$language_pairs = $sitepress->get_setting( $lp_setting_index, array() );
if ( ! isset( $language_pairs[ $source_language ][ $target_language ] ) || $language_pairs[ $source_language ][ $target_language ] == 0 ) {
$language_pairs[ $source_language ][ $target_language ] = 1;
TranslationProxy_Translator::update_language_pairs( $project, $language_pairs );
$sitepress->set_setting( $lp_setting_index, $language_pairs, true );
}
$target = $project->select_translator_iframe_url( $source_language, $target_language );
} catch ( Exception $e ) {
if ( $e->getCode() == ICL_LANGUAGE_NOT_SUPPORTED ) {
printf( __( '<p>Requested languages are not supported by the translation service (%s). Please <a%s>contact us</a> for support. </p>', 'wpml-translation-management' ), $e->getMessage(), ' target="_blank" href="http://wpml.org/?page_id=5255"' );
} else {
printf( __( '<p>Could not add the requested languages. Please <a%s>contact us</a> for support. </p><p>Show <a%s>debug information</a>.</p>', 'wpml-translation-management' ), ' target="_blank" href="http://wpml.org/?page_id=5255"',
' a href="admin.php?page=' .
ICL_PLUGIN_FOLDER .
'/menu/troubleshooting.php&icl_action=icl-connection-test' .
'#icl-connection-test"' );
}
exit;
}
}
$target .= ( strpos( $target, '?' ) === false ) ? '?' : '&';
$target .= "lc=" . $sitepress->get_admin_language();
?>
<iframe src="<?php echo $target; ?>" style="width:100%; height:92%" onload=" var TB_window = jQuery('#TB_window');
<?php if ( $auto_resize ): ?>
TB_window.css('width','90%').css('margin-left', '-45%');
<?php endif; ?>
<?php if ( $unload_cb ){
$unload_cb = esc_js($unload_cb);
?>
TB_window.unbind('unload').bind('tb_unload', function(){<?php echo $unload_cb; ?>});
<?php } ?>
">

View File

@@ -0,0 +1,513 @@
<?php
/**
* @package wpml-core
* @subpackage wpml-core
*/
if( class_exists( 'TranslationProxy_Project' ) ) {
// Workaround for UnitTests.
return;
}
require_once dirname( __FILE__ ) . '/translationproxy-api.class.php';
require_once dirname( __FILE__ ) . '/translationproxy-service.class.php';
require_once dirname( __FILE__ ) . '/translationproxy-batch.class.php';
/**
* Class TranslationProxy_Project
*/
class TranslationProxy_Project {
public $id;
/**
* @var string
*
* `access_key` used when sending **any request** to TP
*/
public $access_key;
/**
* @var int
*
* `ts_id` (aka `website_id`) is used **exclusively** when sending request directly to ICL
*/
public $ts_id;
/**
* @var string
*
* `ts_access_key` is used **exclusively** when sending request directly to ICL
*/
public $ts_access_key;
/**
* @var object
*/
public $service;
/** @var WPML_TP_Client $tp_client */
public $tp_client;
public $errors = array();
/**
* @param TranslationProxy_Service $service
* @param string $delivery
* @param WPML_TP_Client $tp_client
*/
public function __construct( $service, $delivery, WPML_TP_Client $tp_client ) {
$this->service = $service;
$this->tp_client = $tp_client;
$icl_translation_projects = TranslationProxy::get_translation_projects();
$project_index = self::generate_service_index( $service );
if ( $project_index && $icl_translation_projects && isset( $icl_translation_projects [ $project_index ] ) ) {
$project = $icl_translation_projects[ $project_index ];
$this->id = $project['id'];
$this->access_key = $project['access_key'];
$this->ts_id = $project['ts_id'];
$this->ts_access_key = $project['ts_access_key'];
$this->service->delivery_method = $delivery;
}
}
/**
* @return TranslationProxy_Service
*/
public function service() {
return $this->service;
}
/**
* Returns the index by which a translation service can be found in the array returned by
* \TranslationProxy::get_translation_projects
*
* @param $service object
*
* @return bool|string
*/
public static function generate_service_index( $service ) {
$index = false;
if ( $service ) {
$service->custom_fields_data = isset( $service->custom_fields_data ) ? $service->custom_fields_data : array();
if ( isset( $service->id ) ) {
$index = md5( $service->id . serialize( $service->custom_fields_data ) );
}
}
return $index;
}
/**
* Convert WPML language code to service language
*
* @param $language string
*
* @return bool|string
*/
private function service_language( $language ) {
return TranslationProxy_Service::get_language( $this->service, $language );
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Get information about the project (Translation Service)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public function custom_text( $location, $locale = 'en' ) {
$response = '';
if ( ! $this->ts_id || ! $this->ts_access_key ) {
return '';
}
// Sending Translation Service (ts_) id and access_key, as we are talking directly to the Translation Service
// Todo: use project->id and project->access_key once this call is moved to TP
$params = array(
'project_id' => $this->ts_id,
'accesskey' => $this->ts_access_key,
'location' => $location,
'lc' => $locale,
);
if ( $this->service->custom_text_url ) {
try {
$response = TranslationProxy_Api::service_request(
$this->service->custom_text_url,
$params,
'GET',
true,
true,
true
);
} catch ( Exception $e ) {
throw new RuntimeException(
'error getting custom text from Translation Service: ' . serialize( $params ) . ' url: ' . $this->service->custom_text_url,
0,
$e
);
}
}
return $response;
}
function current_service_name() {
return TranslationProxy::get_current_service_name();
}
function current_service() {
return TranslationProxy::get_current_service();
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* IFrames to display project info (Translation Service)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public function select_translator_iframe_url( $source_language, $target_language ) {
// Sending Translation Service (ts_) id and access_key, as we are talking directly to the Translation Service
$params['project_id'] = $this->ts_id;
$params['accesskey'] = $this->ts_access_key;
$params['source_language'] = $this->service_language( $source_language );
$params['target_language'] = $this->service_language( $target_language );
$params['compact'] = 1;
return $this->_create_iframe_url( $this->service->select_translator_iframe_url, $params );
}
public function translator_contact_iframe_url( $translator_id ) {
// Sending Translation Service (ts_) id and access_key, as we are talking directly to the Translation Service
$params['project_id'] = $this->ts_id;
$params['accesskey'] = $this->ts_access_key;
$params['translator_id'] = $translator_id;
$params['compact'] = 1;
if ( $this->service->translator_contact_iframe_url ) {
return $this->_create_iframe_url( $this->service->translator_contact_iframe_url, $params );
}
return false;
}
private function _create_iframe_url( $url, $params ) {
if ( $params ) {
$url = TranslationProxy_Api::add_parameters_to_url( $url, $params );
$url .= '?' . http_build_query( $params );
}
return $url;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Jobs handling (Translation Proxy)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* @throws WPML_TP_Batch_Exception
*
* @param bool $source_language
* @param bool $target_languages
*
* @internal param bool $name
* @return false|WPML_TP_Batch
*/
function get_batch_job(
$source_language = false,
$target_languages = false
) {
$batch_data = TranslationProxy_Basket::get_batch_data();
if ( ! $batch_data ) {
if ( ! $source_language ) {
$source_language = TranslationProxy_Basket::get_source_language();
}
if ( ! $target_languages ) {
$target_languages = TranslationProxy_Basket::get_remote_target_languages();
}
if ( ! $source_language || ! $target_languages ) {
return false;
}
$batch_data = $this->create_batch_job( $source_language, $target_languages );
if ( $batch_data ) {
TranslationProxy_Basket::set_batch_data( $batch_data );
}
}
return $batch_data;
}
/**
* @throws WPML_TP_Batch_Exception
*
* @return false|int
*/
function get_batch_job_id() {
$ret = false;
$batch_data = $this->get_batch_job();
if ( $batch_data ) {
$ret = $batch_data->get_id();
}
return $ret;
}
/**
* @throws WPML_TP_Batch_Exception
*
* @param bool $source_language
* @param $target_languages
*
* @internal param bool $name
* @return false|WPML_TP_Batch
*/
public function create_batch_job( $source_language, $target_languages ) {
$batch_name = TranslationProxy_Basket::get_basket_name();
$batch_options = TranslationProxy_Basket::get_options();
$extra_fields = TranslationProxy_Basket::get_basket_extra_fields();
$batch_data = array(
'source_language' => $source_language,
'target_languages' => $target_languages,
'name' => $batch_name,
);
if ( ! $batch_data['source_language'] ) {
$batch_data['source_language'] = TranslationProxy_Basket::get_source_language();
}
if ( ! $batch_data['target_languages'] ) {
$batch_data['target_languages'] = TranslationProxy_Basket::get_remote_target_languages();
}
if ( ! $batch_data['source_language'] || ! $batch_data['target_languages'] ) {
return false;
}
if ( ! $batch_data['name'] ) {
$batch_data['name'] = sprintf(
__(
'%s: WPML Translation Jobs',
'wpml-translation-management'
),
get_option( 'blogname' )
);
}
TranslationProxy_Basket::set_basket_name( $batch_data['name'] );
if ( isset( $batch_options['deadline_date'] ) ) {
$batch_data['deadline'] = strtotime( $batch_options['deadline_date'] );
}
return $this->tp_client->batches()->create( $batch_data, $extra_fields );
}
/**
*
* Add Files Batch Job
*
* @throws WPML_TP_Batch_Exception
*
* @param string $file
* @param string $title
* @param string $cms_id
* @param string $url
* @param string $source_language
* @param string $target_language
* @param int $word_count
* @param int $translator_id
* @param string $note
*
* @return bool|int
*/
public function send_to_translation_batch_mode(
$file,
$title,
$cms_id,
$url,
$source_language,
$target_language,
$word_count,
$translator_id = 0,
$note = '',
$uuid = null
) {
$batch_id = $this->get_batch_job_id();
if ( ! $batch_id ) {
return false;
}
$job_data = array(
'file' => $file,
'word_count' => $word_count,
'title' => $title,
'cms_id' => $cms_id,
'udid' => $uuid,
'url' => $url,
'translator_id' => $translator_id,
'note' => $note,
'source_language' => $source_language,
'target_language' => $target_language,
);
$tp_job = $this->tp_client->batches()->add_job( $batch_id, $job_data );
return $tp_job ? $tp_job->get_id() : false;
}
/**
* @param bool|int $tp_batch_id
*
* @link http://git.icanlocalize.com/onthego/translation_proxy/wikis/commit_batch_job
*
* @return array|bool|mixed|null|stdClass|string
*/
function commit_batch_job( $tp_batch_id = false ) {
$tp_batch_id = $tp_batch_id ? $tp_batch_id : $this->get_batch_job_id();
if ( ! $tp_batch_id ) {
return true;
}
$params = array(
'api_version' => TranslationProxy_Api::API_VERSION,
'project_id' => $this->id,
'accesskey' => $this->access_key,
'batch_id' => $tp_batch_id,
);
$response = TranslationProxy_Api::proxy_request( '/batches/{batch_id}/commit.json', $params, 'PUT', false );
$basket_name = TranslationProxy_Basket::get_basket_name();
if ( $basket_name ) {
global $wpdb;
$batch_id_sql = "SELECT id FROM {$wpdb->prefix}icl_translation_batches WHERE batch_name=%s";
$batch_id_prepared = $wpdb->prepare(
$batch_id_sql,
array( $basket_name )
);
$batch_id = $wpdb->get_var( $batch_id_prepared );
$batch_data = array(
'batch_name' => $basket_name,
'tp_id' => $tp_batch_id,
'last_update' => date( 'Y-m-d H:i:s' ),
);
if ( isset( $response ) && $response ) {
$batch_data['ts_url'] = serialize( $response );
}
if ( ! $batch_id ) {
$wpdb->insert(
$wpdb->prefix . 'icl_translation_batches',
$batch_data
);
} else {
$wpdb->update(
$wpdb->prefix . 'icl_translation_batches',
$batch_data,
array( 'id' => $batch_id )
);
}
}
return isset( $response ) ? $response : false;
}
/**
*
* @return object[]
*/
public function jobs() {
return $this->get_jobs( 'any' );
}
/**
* @return object[]
*/
public function finished_jobs() {
return $this->get_jobs( 'translation_ready' );
}
public function set_delivery_method( $method ) {
$params = array(
'project_id' => $this->id,
'accesskey' => $this->access_key,
'project' => array( 'delivery_method' => $method ),
);
TranslationProxy_Api::proxy_request( '/projects.json', $params, 'put' );
return true;
}
public function fetch_translation( $job_id ) {
$params = array(
'project_id' => $this->id,
'accesskey' => $this->access_key,
'job_id' => $job_id,
);
return TranslationProxy_Api::proxy_download(
'/jobs/{job_id}/xliff.json',
$params
);
}
public function update_job( $job_id, $url = null, $state = 'delivered' ) {
$params = array(
'job_id' => $job_id,
'project_id' => $this->id,
'accesskey' => $this->access_key,
'job' => array(
'state' => $state,
),
);
if ( $url ) {
$params['job']['url'] = $url;
}
TranslationProxy_Api::proxy_request(
'/jobs/{job_id}.json',
$params,
'PUT'
);
}
/**
* @param string $state
*
* @return mixed
*/
private function get_jobs( $state = 'any' ) {
$batch = TranslationProxy_Basket::get_batch_data();
$params = array(
'project_id' => $this->id,
'accesskey' => $this->access_key,
'state' => $state,
);
if ( $batch ) {
$params['batch_id'] = $batch ? $batch->get_id() : false;
return TranslationProxy_Api::proxy_request(
'/batches/{batch_id}/jobs.json',
$params
);
} else {
// FIXME: remove this once TP will accept the TP Project ID: https://icanlocalize.basecamphq.com/projects/11113143-translation-proxy/todo_items/182251206/comments
$params['project_id'] = $this->id;
}
return TranslationProxy_Api::proxy_request( '/jobs.json', $params );
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* @package wpml-core
* @subpackage wpml-core
*/
require_once dirname( __FILE__ ) . '/translationproxy-api.class.php';
class TranslationProxy_Service {
public $id;
public $name;
public $description;
public $default_service;
public $has_translator_selection = true; // Todo: read this from service properties
public $delivery_method;
public $project_details_url;
public $custom_text_url;
public $has_language_pairs;
public $languages_map;
public $url;
public $logo_url;
public $create_project_url;
public $add_language_pair_url;
public $new_job_url;
public $custom_fields;
public $custom_fields_data;
public $select_translator_iframe_url;
public $translator_contact_iframe_url;
public $quote_iframe_url;
public $batch_name_max_length;
public static function is_authenticated( $service ) {
// for services that do not require authentication return true by default
if ( ! TranslationProxy::service_requires_authentication( $service ) ) {
return true;
}
return isset( $service->custom_fields_data ) && $service->custom_fields_data ? true : false;
}
public static function list_services() {
return TranslationProxy_Api::proxy_request( '/services.json' );
}
public static function get_service( $service_id ) {
$service = TranslationProxy_Api::proxy_request( "/services/$service_id.json" );
$service->languages_map = self::languages_map( $service );
return $service;
}
public static function get_service_by_suid( $suid ) {
$service = TranslationProxy_Api::proxy_request( "/services/$suid.json" );
$service->languages_map = self::languages_map( $service );
return $service;
}
public static function languages_map( $service ) {
$languages_map = array();
$languages = TranslationProxy_Api::proxy_request( "/services/{$service->id}/language_identifiers.json" );
foreach ( $languages as $language ) {
$languages_map[ $language->iso_code ] = $language->value;
}
return $languages_map;
}
public static function get_language( $service, $language ) {
if ( ! empty( $service->languages_map ) and array_key_exists( $language, $service->languages_map ) ) {
$language = $service->languages_map[ $language ];
}
return $language;
}
/**
* Returns a WPML readable string that allows to tell translation service and translator id
* (typically used for translators dropdowns)
*
* @param int|bool $translation_service_id
* @param int|bool $translator_id
*
* @return string
*/
public static function get_wpml_translator_id( $translation_service_id = false, $translator_id = false ) {
if ( $translation_service_id === false ) {
$translation_service_id = TranslationProxy::get_current_service_id();
}
$result = 'ts-' . $translation_service_id;
if ( $translator_id !== false ) {
$result .= '-' . $translator_id;
}
return $result;
}
/**
* @param string $translator_id
*
* @return array Returns a two elements array, respectively containing translation_service and translator_id
*/
public static function get_translator_data_from_wpml( $translator_id ) {
$result = array();
if ( is_numeric( $translator_id ) ) {
$result['translation_service'] = 'local';
$result['translator_id'] = $translator_id;
} else {
$translator_data = explode( '-', $translator_id );
$result = array();
$result['translation_service'] = $translator_data[1];
$result['translator_id'] = isset( $translator_data[2] ) ? $translator_data[2] : 0;
}
return $result;
}
}

View File

@@ -0,0 +1,267 @@
<?php
class TranslationProxy_Translator {
/**
* Get information about translators from current project. Works only for ICL as a Translation Service
*
* @param bool $force
*
* @return array|bool
*/
public static function get_icl_translator_status( $force = false ) {
/** @var SitePress $sitepress */
/** @var WPML_Pro_Translation $ICL_Pro_Translation */
global $sitepress, $ICL_Pro_Translation;
if ( ! $ICL_Pro_Translation ) {
$job_factory = wpml_tm_load_job_factory();
$ICL_Pro_Translation = new WPML_Pro_Translation( $job_factory );
}
if ( ! TranslationProxy::translator_selection_available() ) {
return array();
}
$project = TranslationProxy::get_current_project();
if ( ! $project ) {
return array();
}
$cache_key = md5( serialize( $project ) );
$cache_group = 'get_icl_translator_status';
$found = false;
$result = wp_cache_get( $cache_key, $cache_group, false, $found );
if ( $found ) {
return $result;
}
$translator_status = array();
$website_details = self::get_website_details(
new TranslationProxy_Project( TranslationProxy::get_current_service(), 'xmlrpc', TranslationProxy::get_tp_client() ),
$force
);
if ( false === (bool) $website_details ) {
return array();
}
$language_pairs = array();
if ( isset( $website_details['translation_languages']['translation_language'] ) ) {
$translation_languages = $website_details['translation_languages']['translation_language'];
if ( ! isset( $translation_languages[0] ) ) {
$buf = $translation_languages;
$translation_languages = array( 0 => $buf );
}
foreach ( $translation_languages as $lang ) {
$translators = $_tr = array();
$max_rate = false;
if ( isset( $lang['translators'], $lang['translators']['translator'] ) && ! empty( $lang['translators'] ) ) {
if ( ! isset( $lang['translators']['translator'][0] ) ) {
$_tr[0] = $lang['translators']['translator'];
} else {
$_tr = $lang['translators']['translator'];
}
foreach ( $_tr as $t ) {
if ( false === $max_rate || $t['attr']['amount'] > $max_rate ) {
$max_rate = $t['attr']['amount'];
}
$translators[] = array(
'id' => $t['attr']['id'],
'nickname' => $t['attr']['nickname'],
'contract_id' => $t['attr']['contract_id'],
);
}
}
$language_pairs[] = array(
'from' => $sitepress->get_language_code( $ICL_Pro_Translation->server_languages_map( $lang['attr']['from_language_name'], true ) ),
'to' => $sitepress->get_language_code( $ICL_Pro_Translation->server_languages_map( $lang['attr']['to_language_name'], true ) ),
'have_translators' => $lang['attr']['have_translators'],
'available_translators' => $lang['attr']['available_translators'],
'applications' => $lang['attr']['applications'],
'contract_id' => $lang['attr']['contract_id'],
'id' => $lang['attr']['id'],
'translators' => $translators,
'max_rate' => $max_rate,
);
}
}
$translator_status['icl_lang_status'] = $language_pairs;
$translator_status['icl_support_ticket_id'] = null;
wp_cache_set( $cache_key, $translator_status, $cache_group );
return $translator_status;
}
private static function get_popup_link( $matches ) {
global $sitepress;
return TranslationProxy_Popup::get_link( $matches[2] );
}
/**
*
* Get information about language pairs (including translators). Works only for ICL as a Translation Service
*
* @return array
*/
public static function get_language_pairs() {
global $sitepress;
$icl_lang_status = $sitepress->get_setting( 'icl_lang_status', array() );
if ( ! empty( $icl_lang_status ) ) {
$missing_translators = false;
foreach ( $icl_lang_status as $lang ) {
if ( empty( $lang['translators'] ) ) {
$missing_translators = true;
break;
}
}
if ( ! $missing_translators ) {
$icl_lang_sub_status = $icl_lang_status;
}
}
if ( ! isset( $icl_lang_sub_status ) ) {
$translator_status = self::get_icl_translator_status();
$icl_lang_sub_status = isset( $translator_status['icl_lang_status'] )
? $translator_status['icl_lang_status'] : array();
}
foreach ( $icl_lang_sub_status as $key => $status ) {
if ( ! isset( $status['from'] ) ) {
unset( $icl_lang_sub_status[ $key ] );
}
}
array_filter( $icl_lang_sub_status );
return $icl_lang_sub_status;
}
/**
* Sends request to ICL to get website details (including language pairs)
*
* @param TranslationProxy_Project $project
* @param bool $force
*
* @return array
*/
private static function get_website_details( $project, $force = false ) {
require_once ICL_PLUGIN_PATH . '/inc/utilities/xml2array.php';
require_once ICL_PLUGIN_PATH . '/lib/icl_api.php';
$site_id = $project->ts_id;
$access_key = $project->ts_access_key;
$default = array();
if ( ! $site_id ) {
return $default;
}
$icl_query = new ICanLocalizeQuery( $site_id, $access_key );
return $icl_query->get_website_details( $force );
}
/**
* @param $translator_id
*
* @return string|false
*/
public static function get_translator_name( $translator_id ) {
if ( TranslationProxy::translator_selection_available() ) {
$lang_status = self::get_language_pairs();
if ( $lang_status ) {
foreach ( $lang_status as $lp ) {
$lp_trans = ! empty( $lp['translators'] ) ? $lp['translators'] : array();
foreach ( $lp_trans as $tr ) {
$translators[ $tr['id'] ] = $tr['nickname'];
}
}
}
}
return isset( $translators[ $translator_id ] ) ? $translators[ $translator_id ] : false;
}
/**
* Synchronizes language pairs with ICL
*
* @global object $sitepress
*
* @param $project
* @param $language_pairs
*/
public static function update_language_pairs( $project, $language_pairs ) {
/** @var WPML_Pro_Translation $ICL_Pro_Translation */
global $sitepress, $ICL_Pro_Translation;
$params = array(
'site_id' => $project->ts_id,
'accesskey' => $project->ts_access_key,
'create_account' => 0,
);
$lang_server = array();
foreach ( $sitepress->get_active_languages() as $lang ) {
$lang_server[ $lang['code'] ] = $ICL_Pro_Translation->server_languages_map( $lang['english_name'] );
}
// update account - add language pair
$incr = 0;
foreach ( $language_pairs as $k => $v ) {
if ( ! array_key_exists( $k, $lang_server ) ) {
unset( $language_pairs[ $k ] );
continue;
}
foreach ( $v as $k2 => $v2 ) {
if ( ! array_key_exists( $k2, $lang_server ) ) {
unset( $language_pairs[ $k ][ $k2 ] );
if ( (bool) $language_pairs[ $k ] === false ) {
unset( $language_pairs[ $k ] );
}
continue;
}
$incr ++;
$params[ 'from_language' . $incr ] = $lang_server[ $k ];
$params[ 'to_language' . $incr ] = $lang_server[ $k2 ];
}
}
require_once ICL_PLUGIN_PATH . '/inc/utilities/xml2array.php';
require_once ICL_PLUGIN_PATH . '/lib/icl_api.php';
$icl_query = new ICanLocalizeQuery();
$icl_query->updateAccount( $params );
}
public static function flush_website_details_cache() {
delete_transient( WEBSITE_DETAILS_TRANSIENT_KEY );
}
public static function flush_website_details_cache_action() {
$nonce = array_key_exists( 'nonce', $_POST ) ? $_POST['nonce'] : null;
$action = array_key_exists( 'action', $_POST ) ? $_POST['action'] : null;
$nonce_is_valid = wp_verify_nonce( $nonce, $action );
if ( $nonce_is_valid ) {
self::flush_website_details_cache();
$query_args = array(
'page' => urlencode( WPML_TM_FOLDER . '/menu/main.php' ),
'sm' => urlencode( 'translators' ),
);
$link_url = add_query_arg( $query_args, get_admin_url( null, 'admin.php' ) );
wp_send_json_success( array( 'redirectTo' => $link_url ) );
} else {
wp_send_json_error( 'Nonce is not valid.' );
}
}
}

View File

@@ -0,0 +1,671 @@
<?php
/**
* @package wpml-core
* @subpackage wpml-core
*/
use WPML\TM\TranslationProxy\Services\AuthorizationFactory;
use WPML\FP\Obj;
require_once WPML_TM_PATH . '/inc/translation-proxy/functions.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-basket.class.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-api.class.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-project.class.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-service.class.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-popup.class.php';
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy-translator.class.php';
define( 'CUSTOM_TEXT_MAX_LENGTH', 1000 );
class TranslationProxy {
private static $tp_client;
/**
* @param bool $reload
*
* @return WPML_TP_Service[]
*/
public static function services( $reload = true ) {
return self::get_tp_client()->services()->get_all( $reload );
}
public static function get_tp_default_suid() {
if ( defined( 'WPML_TP_DEFAULT_SUID' ) ) {
return WPML_TP_DEFAULT_SUID;
}
return self::get_preferred_translation_service() ?: false;
}
/**
* @param string $suid
* @return 'wpml_list'|'config'|'account'
*/
public static function get_service_linked_by_suid( $suid ) {
if ( defined( 'WPML_TP_DEFAULT_SUID' ) && WPML_TP_DEFAULT_SUID === $suid ) {
return 'config';
}
if ( self::get_preferred_translation_service() === $suid ) {
return 'account';
}
return 'wpml_list';
}
public static function has_preferred_translation_service() {
return self::get_tp_default_suid() !== false;
}
public static function clear_preferred_translation_service() {
WP_Installer_API::set_preferred_ts( 'clear' );
}
/**
* @param int $service_id
*
* @return stdClass
*/
public static function get_service( $service_id ) {
// @todo: implement usage of WPML_TP_Service for the active service
return (object) (array) self::get_tp_client()->services()->get_service( $service_id, true );
}
/**
* @param int $service_id
*
* @return TranslationProxy_Project|WP_Error
* @throws \WPMLTranslationProxyApiException
* @throws \InvalidArgumentException
*/
public static function select_service( $service_id, $credentials = null ) {
global $sitepress;
$service_selected = false;
$error = false;
/** @var TranslationProxy_Service $service */
$service = self::get_service( $service_id );
if ( $service ) {
self::deselect_active_service();
$service = self::build_and_store_active_translation_service( $service, $credentials );
$result = $service;
$service_selected = true;
// Force authentication if no user input is needed
if ( ! self::service_requires_authentication( $service ) ) {
( new AuthorizationFactory() )->create()->authorize( new \stdClass() );
}
} else {
$result = new WP_Error(
'0',
'No service selected',
array( 'service_id' => $service_id )
);
}
// Do not store selected service if this operation failed;
if ( $error || ! $service_selected ) {
$sitepress->set_setting( 'translation_service', false, true );
}
$sitepress->save_settings();
return $result;
}
public static function deselect_active_service() {
global $sitepress;
$sitepress->set_setting( 'translation_service', false );
$sitepress->set_setting( 'translator_choice', false );
$sitepress->set_setting( 'icl_lang_status', false );
$sitepress->set_setting( 'icl_html_status', false );
$sitepress->set_setting( 'icl_current_session', false );
$sitepress->set_setting( 'last_icl_reminder_fetch', false );
$sitepress->set_setting( 'translators_management_info', false );
$sitepress->set_setting( 'language_pairs', false );
$sitepress->save_settings();
do_action( 'wpml_tp_service_dectivated', self::get_current_service() );
}
/**
* @param $service
* @param bool $custom_fields_data
*
* @return mixed
* @throws \WPMLTranslationProxyApiException
*/
public static function build_and_store_active_translation_service( $service, $custom_fields_data = false ) {
global $sitepress;
// set language map
$service->languages_map = self::languages_map( $service );
// set information about custom fields
$service->custom_fields = self::get_custom_fields( $service->id, true );
$service->custom_fields_data = $custom_fields_data;
$service->last_refresh = time();
$sitepress->set_setting( 'translation_service', $service, true );
return $service;
}
/**
* @return bool|TranslationProxy_Project
*/
public static function get_current_project() {
$translation_service = self::get_current_service();
if ( $translation_service ) {
return new TranslationProxy_Project( $translation_service, 'xmlrpc', self::get_tp_client() );
}
return false;
}
public static function get_current_service_info( array $info = array() ) {
global $sitepress;
if ( ! $sitepress->get_setting( 'translation_service' ) ) {
$sitepress->set_setting( 'translation_service', false, true );
}
$service = self::get_current_service();
if ( $service ) {
$service_info = array();
if ( icl_do_not_promote() ) {
$service_info['name'] = __( 'Translation Service', 'wpml-translation-management' );
$service_info['logo'] = false;
$service_info['header'] = __( 'Translation Service', 'wpml-translation-management' );
$service_info['description'] = false;
$service_info['contact_url'] = false;
} else {
$service_info['name'] = $service->name;
$service_info['logo'] = $service->logo_url;
$service_info['header'] = $service->name;
$service_info['description'] = $service->description;
$service_info['contact_url'] = $service->url;
}
$service_info['setup_url'] = TranslationProxy_Popup::get_link( '@select-translators;from_replace;to_replace@', array( 'ar' => 1 ), true );
$service_info['has_quote'] = $service->quote_iframe_url !== '';
$service_info['has_translator_selection'] = $service->has_translator_selection;
$info[ $service->id ] = $service_info;
}
return $info;
}
public static function get_service_promo() {
global $sitepress;
if ( icl_do_not_promote() ) {
return '';
}
$cache_key = 'get_service_promo';
$cache_found = false;
$output = wp_cache_get( $cache_key, '', false, $cache_found );
if ( $cache_found ) {
return $output;
}
$icl_translation_services = apply_filters( 'icl_translation_services', array() );
$icl_translation_services = array_merge( $icl_translation_services, self::get_current_service_info() );
$output = '';
if ( ! empty( $icl_translation_services ) ) {
$sitepress_settings = $sitepress->get_settings();
$icl_dashboard_settings = isset( $sitepress_settings['dashboard'] ) ? $sitepress_settings['dashboard'] : array();
if ( empty( $icl_dashboard_settings['hide_icl_promo'] ) ) {
$exp_hidden = '';
$col_hidden = ' hidden';
} else {
$exp_hidden = ' hidden';
$col_hidden = '';
}
$output .= '<div class="icl-translation-services' . $exp_hidden . '">';
foreach ( $icl_translation_services as $service ) {
$output .= '<div class="icl-translation-services-inner">';
$output .= '<p class="icl-translation-services-logo"><span><img src="' . $service['logo'] . '" alt="' . $service['name'] . '" /></span></p>';
$output .= '<h3 class="icl-translation-services-header"> ' . $service['header'] . '</h3>';
$output .= '<div class="icl-translation-desc"> ' . $service['description'] . '</div>';
$output .= '</div>';
$output .= '<p class="icl-translation-links">';
$output .= '<a class="icl-mail-ico" href="' . $service['contact_url'] . '" target="_blank">' . __( 'Contact', 'wpml-translation-management' ) . " {$service['name']}</a>";
$output .= '<a id="icl_hide_promo" href="#">' . __( 'Hide this', 'wpml-translation-management' ) . '</a>';
$output .= '</p>';
}
$output .= '</div>';
$output .= '<a class="' . $col_hidden . '" id="icl_show_promo" href="#">' . __( 'Need translators?', 'wpml-translation-management' ) . '</a>';
}
wp_cache_set( $cache_key, $output );
return $output;
}
public static function get_service_dashboard_info() {
global $sitepress;
return self::get_custom_html(
'dashboard',
$sitepress->get_current_language(),
array(
'TranslationProxy_Popup',
'get_link',
)
);
}
public static function get_service_translators_info() {
global $sitepress;
return self::get_custom_html(
'translators',
$sitepress->get_current_language(),
array(
'TranslationProxy_Popup',
'get_link',
)
);
}
/**
* @param string $location
* @param string $locale
* @param callable $popup_link_callback
* @param int $max_count
* @param bool $paragraph
*
* @return string
*/
public static function get_custom_html(
$location,
$locale,
$popup_link_callback,
$max_count = 1000,
$paragraph = true
) {
/** @var $project TranslationProxy_Project */
$project = self::get_current_project();
if ( ! $project ) {
return '';
}
$cache_key = $project->id . ':' . md5(
serialize(
array(
$location,
$locale,
serialize( $popup_link_callback ),
$max_count,
$paragraph,
)
)
);
$cache_group = 'get_custom_html';
$cache_found = false;
$output = wp_cache_get( $cache_key, $cache_group, false, $cache_found );
if ( $cache_found ) {
return $output;
}
try {
$text = $project->custom_text( $location, $locale );
} catch ( Exception $e ) {
return 'Error getting custom text from Translation Service: ' . $e->getMessage();
}
$count = 0;
if ( $text ) {
foreach ( $text as $string ) {
$format_string = self::sanitize_custom_text( $string->format_string );
if ( $paragraph ) {
$format = '<p>' . $format_string . '</p>';
} else {
$format = '<div>' . $format_string . '</div>';
}
$links = array();
/** @var array $string_links */
$string_links = $string->links;
foreach ( $string_links as $link ) {
$url = self::sanitize_custom_text( $link->url );
$text = self::sanitize_custom_text( $link->text );
if ( isset( $link->dismiss ) && (int) $link->dismiss === 1 ) {
$links[] = '<a href="' . $url . '" class="wpml_tp_custom_dismiss_able">' . $text . '</a>';
} else {
$links[] = call_user_func(
$popup_link_callback,
$url
) . $text . '</a>';
}
}
$output .= vsprintf( $format, $links );
$count ++;
if ( $count >= $max_count ) {
break;
}
}
}
return $output;
}
public static function get_current_service_name() {
if ( icl_do_not_promote() ) {
return __( 'Translation Service', 'wpml-translation-management' );
}
$translation_service = self::get_current_service();
if ( $translation_service ) {
return $translation_service->name;
}
return false;
}
public static function get_current_service_id() {
$translation_service = self::get_current_service();
if ( $translation_service ) {
return $translation_service->id;
}
return false;
}
public static function get_current_service_batch_name_max_length() {
$translation_service = self::get_current_service();
if ( $translation_service && isset( $translation_service->batch_name_max_length )
&& null
!== $translation_service->batch_name_max_length ) {
return $translation_service->batch_name_max_length;
}
return 40;
}
/**
* @param bool|TranslationProxy_Service|WP_Error $service
*
* @return bool
* @throws \InvalidArgumentException
* @throws \WPMLTranslationProxyApiException
*/
public static function service_requires_authentication( $service = false ) {
if ( ! $service ) {
$service = self::get_current_service();
}
$custom_fields = false;
if ( false !== (bool) $service ) {
$custom_fields = self::get_custom_fields( $service->id );
}
return $custom_fields && isset( $custom_fields->custom_fields ) && count( $custom_fields->custom_fields ) > 0;
}
/**
* Return true if $service has been successfully authenticated
* Services that do not require authentication are by default authenticated
*
* @param bool|WP_Error|TranslationProxy_Service $service
*
* @return bool
* @throws \InvalidArgumentException
*/
public static function is_service_authenticated( $service = false ) {
if ( ! $service ) {
$service = self::get_current_service();
}
if ( ! $service ) {
return false;
}
if ( ! self::service_requires_authentication( $service ) ) {
return true;
}
$has_custom_fields = self::has_custom_fields();
$custom_fields_data = self::get_custom_fields_data();
return $has_custom_fields && $custom_fields_data;
}
/**
* @return bool|TranslationProxy_Service|WP_Error
*/
public static function get_current_service() {
global $sitepress;
/** @var TranslationProxy_Service $ts */
$ts = $sitepress->get_setting( 'translation_service' );
if ( is_array( $ts ) ) {
return new WP_Error( 'translation-proxy-service-misconfiguration', 'translation_service is stored as array!', $ts );
}
return $ts;
}
/**
*
* @return bool
* @throws \InvalidArgumentException
*/
public static function is_current_service_active_and_authenticated() {
$active_service = self::get_current_service();
return $active_service && TranslationProxy_Service::is_authenticated( $active_service );
}
/**
* @return mixed
*/
public static function get_translation_projects() {
global $sitepress;
return $sitepress->get_setting( 'icl_translation_projects', null );
}
public static function get_service_name( $service_id = false ) {
if ( $service_id ) {
$name = false;
$services = self::services( false );
foreach ( $services as $service ) {
if ( $service->id === $service_id ) {
$name = $service->name;
}
}
} else {
$name = self::get_current_service_name();
}
return $name;
}
public static function has_custom_fields( $service_id = false ) {
$custom_fields = self::get_custom_fields( $service_id );
if ( $custom_fields ) {
return isset( $custom_fields->custom_fields ) && is_array( $custom_fields->custom_fields ) && count( $custom_fields->custom_fields );
}
return false;
}
/**
* @param int|bool $service_id If not given, will use the current service ID (if any)
* @param bool $force_reload Force reload custom fields from Translation Service
*
* @throws WPMLTranslationProxyApiException
* @throws InvalidArgumentException
* @return array|mixed|null|string
*/
public static function get_custom_fields( $service_id = false, $force_reload = false ) {
if ( ! $service_id ) {
$service_id = self::get_current_service_id();
}
if ( ! $service_id ) {
return false;
}
$translation_service = self::get_current_service();
if ( $translation_service && ! $force_reload ) {
return $translation_service->custom_fields ?: false;
}
return self::get_tp_client()->services()->get_custom_fields( $service_id );
}
/**
* @return array
*/
public static function get_extra_fields_local() {
global $sitepress;
$service = self::get_current_service();
$icl_translation_projects = $sitepress->get_setting( 'icl_translation_projects' );
if ( isset( $icl_translation_projects[ TranslationProxy_Project::generate_service_index( $service ) ]['extra_fields'] ) && ! empty( $icl_translation_projects[ TranslationProxy_Project::generate_service_index( $service ) ]['extra_fields'] ) ) {
return $icl_translation_projects[ TranslationProxy_Project::generate_service_index( $service ) ]['extra_fields'];
}
return array();
}
/**
* @param $extra_fields
*/
public static function save_extra_fields( $extra_fields ) {
global $sitepress;
$service = self::get_current_service();
$icl_translation_projects = $sitepress->get_setting( 'icl_translation_projects' );
$icl_translation_project_id = TranslationProxy_Project::generate_service_index( $service );
if ( is_array( Obj::prop( $icl_translation_project_id, $icl_translation_projects ) ) ) {
$icl_translation_projects[ $icl_translation_project_id ]['extra_fields'] = $extra_fields;
$sitepress->set_setting( 'icl_translation_projects', $icl_translation_projects );
$sitepress->save_settings();
}
}
public static function maybe_convert_extra_fields( $extra_fields ) {
$extra_fields_typed = array();
if ( $extra_fields && is_array( $extra_fields ) ) {
/** @var array $extra_fields */
/** @var stdClass $extra_field */
foreach ( $extra_fields as $extra_field ) {
if ( $extra_field instanceof WPML_TP_Extra_Field ) {
$extra_field_typed = $extra_field;
} else {
$extra_field_typed = new WPML_TP_Extra_Field();
if ( isset( $extra_field->type ) ) {
$extra_field_typed->type = $extra_field->type;
}
if ( isset( $extra_field->label ) ) {
$extra_field_typed->label = $extra_field->label;
}
if ( isset( $extra_field->name ) ) {
$extra_field_typed->name = $extra_field->name;
}
if ( isset( $extra_field->items ) ) {
$extra_field_typed->items = $extra_field->items;
}
}
$extra_fields_typed[] = $extra_field_typed;
}
}
return $extra_fields_typed;
}
public static function get_custom_fields_data() {
$service = self::get_current_service();
return null !== $service->custom_fields_data ? $service->custom_fields_data : false;
}
/**
* @return bool true if the current translation service allows selection of specific translators
* @throws \InvalidArgumentException
*/
public static function translator_selection_available() {
$res = false;
$translation_service = self::get_current_service();
if ( $translation_service && $translation_service->has_translator_selection && self::is_service_authenticated() ) {
$res = true;
}
return $res;
}
private static function sanitize_custom_text( $text ) {
$text = substr( $text, 0, CUSTOM_TEXT_MAX_LENGTH );
$text = esc_html( $text );
// Service sends html tags as [tag]
$text = str_replace( array( '[', ']' ), array( '<', '>' ), $text );
return $text;
}
private static function languages_map( $service ) {
$languages_map = array();
$languages = self::get_tp_client()->services()->get_languages_map( $service->id );
if ( ! empty( $languages ) ) {
foreach ( $languages as $language ) {
$languages_map[ $language->iso_code ] = $language->value;
}
}
return $languages_map;
}
private static function get_preferred_translation_service() {
$tp_default_suid = false;
$preferred_translation_service_from_installer = self::get_preferred_translation_service_from_installer();
if ( 'clear' !== $preferred_translation_service_from_installer ) {
$tp_default_suid = $preferred_translation_service_from_installer;
}
return $tp_default_suid;
}
private static function get_preferred_translation_service_from_installer() {
return WP_Installer_API::get_preferred_ts();
}
public static function get_tp_client() {
if ( ! self::$tp_client ) {
$tp_api_factory = new WPML_TP_Client_Factory();
self::$tp_client = $tp_api_factory->create();
}
return self::$tp_client;
}
}

View File

@@ -0,0 +1,611 @@
<?php
/**
* @package wpml-core
* @package wpml-core-pro-translation
*/
use WPML\FP\Fns;
use WPML\FP\Lst;
use WPML\FP\Str;
use function WPML\Container\make;
use function WPML\FP\pipe;
/**
* Class WPML_Pro_Translation
*/
class WPML_Pro_Translation extends WPML_TM_Job_Factory_User {
public $errors = array();
/** @var TranslationManagement $tmg */
private $tmg;
/** @var WPML_TM_CMS_ID $cms_id_helper */
private $cms_id_helper;
/** @var WPML_TM_Xliff_Reader_Factory $xliff_reader_factory */
private $xliff_reader_factory;
private $sitepress;
private $update_pm;
/**
* WPML_Pro_Translation constructor.
*
* @param WPML_Translation_Job_Factory $job_factory
*/
function __construct( &$job_factory ) {
parent::__construct( $job_factory );
global $iclTranslationManagement, $wpdb, $sitepress, $wpml_post_translations, $wpml_term_translations;
$this->tmg =& $iclTranslationManagement;
$this->xliff_reader_factory = new WPML_TM_Xliff_Reader_Factory( $this->job_factory );
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
$this->cms_id_helper = new WPML_TM_CMS_ID( $wpml_tm_records, $job_factory );
$this->sitepress = $sitepress;
add_filter( 'xmlrpc_methods', array( $this, 'custom_xmlrpc_methods' ) );
add_action(
'post_submitbox_start',
array(
$this,
'post_submitbox_start',
)
);
add_action(
'icl_ajx_custom_call',
array(
$this,
'ajax_calls',
),
10,
2
);
add_action( 'wpml_minor_edit_for_gutenberg', array( $this, 'gutenberg_minor_edit' ), 10, 0 );
$this->update_pm = new WPML_Update_PickUp_Method( $this->sitepress );
}
/**
* @return WPML_TM_CMS_ID
*/
public function &get_cms_id_helper() {
return $this->cms_id_helper;
}
/**
* @param string $call
* @param array $data
*/
function ajax_calls( $call, $data ) {
switch ( $call ) {
case 'set_pickup_mode':
$response = $this->update_pm->update_pickup_method( $data, $this->get_current_project() );
if ( 'no-ts' === $response ) {
wp_send_json_error( array( 'message' => __( 'Please activate translation service first.', 'wpml-translation-management' ) ) );
}
if ( 'cant-update' === $response ) {
wp_send_json_error( array( 'message' => __( 'Could not update the translation pickup mode.', 'wpml-translation-management' ) ) );
}
wp_send_json_success( array( 'message' => __( 'Ok', 'wpml-translation-management' ) ) );
break;
}
}
public function get_current_project() {
return TranslationProxy::get_current_project();
}
/**
* @param WP_Post|WPML_Package $post
* @param array $target_languages
* @param int $translator_id
* @param int $job_id
*
* @return bool|int
*/
function send_post( $post, $target_languages, $translator_id, $job_id ) {
/** @var TranslationManagement $iclTranslationManagement */
global $sitepress, $iclTranslationManagement;
$this->maybe_init_translation_management( $iclTranslationManagement );
if ( is_numeric( $post ) ) {
$post = get_post( $post );
}
if ( ! $post ) {
return false;
}
$post_id = $post->ID;
$post_type = $post->post_type;
$element_type_prefix = $iclTranslationManagement->get_element_type_prefix_from_job_id( $job_id );
$element_type = $element_type_prefix . '_' . $post_type;
$note = WPML_TM_Translator_Note::get( $post_id );
if ( ! $note ) {
$note = null;
}
$err = false;
$tp_job_id = false;
$source_language = $sitepress->get_language_for_element( $post_id, $element_type );
$target_language = is_array( $target_languages ) ? end( $target_languages ) : $target_languages;
if ( empty( $target_language ) || $target_language === $source_language ) {
return false;
}
$translation = $this->tmg->get_element_translation( $post_id, $target_language, $element_type );
if ( ! $translation ) { // translated the first time
$err = true;
}
if ( ! $err && ( $translation->needs_update || $translation->status == ICL_TM_NOT_TRANSLATED || $translation->status == ICL_TM_WAITING_FOR_TRANSLATOR ) ) {
$project = TranslationProxy::get_current_project();
if ( $iclTranslationManagement->is_external_type( $element_type_prefix ) ) {
$job_object = new WPML_External_Translation_Job( $job_id );
} else {
$job_object = new WPML_Post_Translation_Job( $job_id );
$job_object->load_terms_from_post_into_job();
}
list( $err, $project, $tp_job_id ) = $job_object->send_to_tp( $project, $translator_id, $this->cms_id_helper, $this->tmg, $note );
if ( $err ) {
$this->enqueue_project_errors( $project );
}
}
return $err ? false : $tp_job_id; // last $ret
}
function server_languages_map( $language_name, $server2plugin = false ) {
if ( is_array( $language_name ) ) {
return array_map( array( $this, 'server_languages_map' ), $language_name );
}
$map = array(
'Norwegian Bokmål' => 'Norwegian',
'Portuguese, Brazil' => 'Portuguese',
'Portuguese, Portugal' => 'Portugal Portuguese',
);
$map = $server2plugin ? array_flip( $map ) : $map;
return isset( $map[ $language_name ] ) ? $map[ $language_name ] : $language_name;
}
/**
* @param $methods
*
* @return array
*/
public function custom_xmlrpc_methods( $methods ) {
$icl_methods['translationproxy.test_xmlrpc'] = '__return_true';
$icl_methods['translationproxy.updated_job_status'] = array(
$this,
'xmlrpc_updated_job_status',
);
$methods = array_merge( $methods, $icl_methods );
if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && preg_match( '#<methodName>([^<]+)</methodName>#i', $this->sitepress->get_wp_api()->get_raw_post_data(), $matches ) ) {
$method = $matches[1];
if ( array_key_exists( $method, $icl_methods ) ) {
set_error_handler( array( $this, 'translation_error_handler' ), E_ERROR | E_USER_ERROR );
}
}
return $methods;
}
/**
* @param array $args
*
* @return int|IXR_Error
*/
public function xmlrpc_updated_job_status( $args ) {
global $wpdb;
$tp_id = isset( $args[0] ) ? $args[0] : 0;
$cms_id = isset( $args[1] ) ? $args[1] : 0;
$status = isset( $args[2] ) ? $args[2] : '';
$signature = isset( $args[3] ) ? $args[3] : '';
if ( ! $this->authenticate_request( $tp_id, $cms_id, $status, $signature ) ) {
return new IXR_Error( 401, 'Wrong signature' );
}
try {
/** @var WPML_TM_Jobs_Repository $jobs_repository */
$jobs_repository = wpml_tm_get_jobs_repository();
$job_match = $jobs_repository->get(
new WPML_TM_Jobs_Search_Params(
array(
'scope' => 'remote',
'tp_id' => $tp_id,
)
)
);
if ( $job_match ) {
$jobs_array = $job_match->toArray();
$job = $jobs_array[0];
$job->set_status( WPML_TP_Job_States::map_tp_state_to_local( $status ) );
$tp_sync_updated_job = new WPML_TP_Sync_Update_Job( $wpdb, $this->sitepress );
$job_updated = $tp_sync_updated_job->update_state( $job );
if ( $job_updated && WPML_TP_Job_States::CANCELLED !== $status ) {
$apply_tp_translation = new WPML_TP_Apply_Single_Job(
wpml_tm_get_tp_translations_repository(),
new WPML_TP_Apply_Translation_Strategies( $wpdb )
);
$apply_tp_translation->apply( $job );
}
return 1;
}
} catch ( Exception $e ) {
return new IXR_Error( $e->getCode(), $e->getMessage() );
}
return 0;
}
/**
* @return bool
*/
private function authenticate_request( $tp_id, $cms_id, $status, $signature ) {
$project = TranslationProxy::get_current_project();
return sha1( $project->id . $project->access_key . $tp_id . $cms_id . $status ) === $signature;
}
/**
* @return WPML_WP_API
*/
function get_wpml_wp_api() {
return $this->sitepress->get_wp_api();
}
/**
*
* Cancel translation for given cms_id
*
* @param $rid
* @param $cms_id
*
* @return bool
*/
function cancel_translation( $rid, $cms_id ) {
/**
* @var WPML_String_Translation|null $WPML_String_Translation
* @var TranslationManagement $iclTranslationManagement
*/
global $WPML_String_Translation, $iclTranslationManagement;
$res = false;
if ( empty( $cms_id ) ) { // it's a string
if ( $WPML_String_Translation ) {
$res = $WPML_String_Translation->cancel_remote_translation( $rid );
}
} else {
$translation_id = $this->cms_id_helper->get_translation_id( $cms_id );
if ( $translation_id ) {
$iclTranslationManagement->cancel_translation_request( $translation_id );
$res = true;
}
}
return $res;
}
/**
*
* Downloads translation from TP and updates its document
*
* @param $translation_proxy_job_id
* @param $cms_id
*
* @return bool|string
*/
function download_and_process_translation( $translation_proxy_job_id, $cms_id ) {
global $wpdb;
if ( empty( $cms_id ) ) { // it's a string
// TODO: [WPML 3.3] this should be handled as any other element type in 3.3
$target = $wpdb->get_var( $wpdb->prepare( "SELECT target FROM {$wpdb->prefix}icl_core_status WHERE rid=%d", $translation_proxy_job_id ) );
return $this->process_translated_string( $translation_proxy_job_id, $target );
} else {
$translation_id = $this->cms_id_helper->get_translation_id( $cms_id, TranslationProxy::get_current_service() );
return ! empty( $translation_id ) && $this->add_translated_document( $translation_id, $translation_proxy_job_id );
}
}
/**
* @param int $translation_id
* @param int $translation_proxy_job_id
*
* @return bool
*/
function add_translated_document( $translation_id, $translation_proxy_job_id ) {
global $wpdb, $sitepress;
$project = TranslationProxy::get_current_project();
$translation_info = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_translations WHERE translation_id=%d", $translation_id ) );
$translation = $project->fetch_translation( $translation_proxy_job_id );
if ( ! $translation ) {
$this->errors = array_merge( $this->errors, $project->errors );
} else {
$translation = apply_filters( 'icl_data_from_pro_translation', $translation );
}
$ret = true;
if ( ! empty( $translation ) && strpos( $translation, 'xliff' ) !== false ) {
try {
/** @var $job_xliff_translation WP_Error|array */
$job_xliff_translation = $this->xliff_reader_factory
->general_xliff_import()->import( $translation, $translation_id );
if ( is_wp_error( $job_xliff_translation ) ) {
$this->add_error( $job_xliff_translation->get_error_message() );
return false;
}
kses_remove_filters();
wpml_tm_save_data( $job_xliff_translation );
kses_init();
$translations = $sitepress->get_element_translations( $translation_info->trid, $translation_info->element_type, false, true, true );
if ( isset( $translations[ $translation_info->language_code ] ) ) {
$translation = $translations[ $translation_info->language_code ];
if ( isset( $translation->element_id ) && $translation->element_id ) {
$translation_post_type_prepared = $wpdb->prepare( "SELECT post_type FROM $wpdb->posts WHERE ID=%d", array( $translation->element_id ) );
$translation_post_type = $wpdb->get_var( $translation_post_type_prepared );
} else {
$translation_post_type = implode( '_', array_slice( explode( '_', $translation_info->element_type ), 1 ) );
}
if ( $translation_post_type == 'page' ) {
$url = get_option( 'home' ) . '?page_id=' . $translation->element_id;
} else {
$url = get_option( 'home' ) . '?p=' . $translation->element_id;
}
$project->update_job( $translation_proxy_job_id, $url );
} else {
$project->update_job( $translation_proxy_job_id );
}
} catch ( Exception $e ) {
$ret = false;
}
}
return $ret;
}
private static function content_get_link_paths( $body ) {
$regexp_links = array(
"/<a[^>]*href\s*=\s*([\"\']??)([^\"^>]+)[\"\']??([^>]*)>/i",
);
$links = array();
foreach ( $regexp_links as $regexp ) {
if ( preg_match_all( $regexp, $body, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $match ) {
$links[] = $match;
}
}
}
return $links;
}
public function fix_links_to_translated_content( $element_id, $target_lang_code, $element_type = 'post' ) {
global $wpdb, $sitepress;
$sitepress->switch_lang( $target_lang_code );
$wpml_element_type = $element_type;
$body = false;
$string_type = null;
if ( strpos( $element_type, 'post' ) === 0 ) {
$post_prepared = $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID=%d", array( $element_id ) );
$post = $wpdb->get_row( $post_prepared );
$body = $post->post_content;
$wpml_element_type = 'post_' . $post->post_type;
} elseif ( $element_type == 'string' ) {
$string_prepared = $wpdb->prepare( "SELECT string_id, value FROM {$wpdb->prefix}icl_string_translations WHERE id=%d", array( $element_id ) );
$data = $wpdb->get_row( $string_prepared );
$body = $data->value;
$original_string_id = $data->string_id;
$string_type = $wpdb->get_var( $wpdb->prepare( "SELECT type FROM {$wpdb->prefix}icl_strings WHERE id=%d", $original_string_id ) );
if ( 'LINK' === $string_type ) {
$body = '<a href="' . $body . '">removeit</a>';
}
}
$translate_link_targets = make( 'WPML_Translate_Link_Targets' );
$absolute_links = make( 'AbsoluteLinks' );
$getTranslatedLink = function ( $link ) use ( $translate_link_targets, $absolute_links, $element_type, $target_lang_code ) {
if ( $absolute_links->is_home( $link[2] ) ) {
$translatedLink = $absolute_links->convert_url( $link[2], $target_lang_code );
$translatedLink = Str::replace( $link[2], $translatedLink, $link[0] );
} else {
add_filter( 'wpml_force_translated_permalink', '__return_true' );
$translatedLink = $translate_link_targets->convert_text( $link[0] );
remove_filter( 'wpml_force_translated_permalink', '__return_true' );
if ( self::should_links_be_converted_back_to_sticky( $element_type ) ) {
$translatedLink = $absolute_links->convert_text( $translatedLink );
}
}
return $translatedLink !== $link[0]
? [
'from' => $link[0],
'to' => $translatedLink,
]
: null;
};
$getTranslatedLinks = pipe(
Fns::map( $getTranslatedLink ),
Fns::filter( Fns::identity() )
);
$links = self::content_get_link_paths( $body );
$translatedLinks = $getTranslatedLinks( $links );
$replaceLink = function ( $body, $link ) {
return str_replace( $link['from'], $link['to'], $body );
};
$new_body = Fns::reduce( $replaceLink, $body, $translatedLinks );
if ( $new_body != $body ) {
if ( strpos( $element_type, 'post' ) === 0 ) {
$wpdb->update( $wpdb->posts, array( 'post_content' => $new_body ), array( 'ID' => $element_id ) );
} elseif ( $element_type == 'string' ) {
if ( 'LINK' === $string_type ) {
$new_body = str_replace( array( '<a href="', '">removeit</a>' ), array( '', '' ), $new_body );
$wpdb->update(
$wpdb->prefix . 'icl_string_translations',
array(
'value' => $new_body,
'status' => ICL_TM_COMPLETE,
),
array( 'id' => $element_id )
);
do_action( 'icl_st_add_string_translation', $element_id );
} else {
$wpdb->update( $wpdb->prefix . 'icl_string_translations', array( 'value' => $new_body ), array( 'id' => $element_id ) );
}
}
}
$links_fixed_status_factory = new WPML_Links_Fixed_Status_Factory( $wpdb, new WPML_WP_API() );
$links_fixed_status = $links_fixed_status_factory->create( $element_id, $wpml_element_type );
$links_fixed_status->set( Lst::length( $links ) === Lst::length( $translatedLinks ) );
$sitepress->switch_lang();
return sizeof( $translatedLinks );
}
function translation_error_handler( $error_number, $error_string, $error_file, $error_line ) {
switch ( $error_number ) {
case E_ERROR:
case E_USER_ERROR:
throw new Exception( $error_string . ' [code:e' . $error_number . '] in ' . $error_file . ':' . $error_line );
case E_WARNING:
case E_USER_WARNING:
return true;
default:
return true;
}
}
private static function should_links_be_converted_back_to_sticky( $element_type ) {
return 'string' !== $element_type && ! empty( $GLOBALS['WPML_Sticky_Links'] );
}
function post_submitbox_start() {
$show_box_style = $this->get_show_minor_edit_style();
if ( false !== $show_box_style ) {
?>
<p id="icl_minor_change_box" style="float:left;padding:0;margin:3px;<?php echo $show_box_style; ?>">
<label><input type="checkbox" name="icl_minor_edit" value="1" style="min-width:15px;"/>&nbsp;
<?php esc_html_e( 'Minor edit - don\'t update translation', 'wpml-translation-management' ); ?>
</label>
<br clear="all"/>
</p>
<?php
}
}
public function gutenberg_minor_edit() {
$show_box_style = $this->get_show_minor_edit_style();
if ( false !== $show_box_style ) {
?>
<div id="icl_minor_change_box" style="<?php echo $show_box_style; ?>" class="icl_box_paragraph">
<p>
<strong><?php esc_html_e( 'Minor edit', 'wpml-translation-management' ); ?></strong>
</p>
<label><input type="checkbox" name="icl_minor_edit" value="1" style="min-width:15px;"/>&nbsp;
<?php esc_html_e( "Don't update translation", 'wpml-translation-management' ); ?>
</label>
</div>
<?php
}
}
private function get_show_minor_edit_style() {
global $post, $iclTranslationManagement;
if ( empty( $post ) || ! $post->ID ) {
return false;
}
$translations = $iclTranslationManagement->get_element_translations( $post->ID, 'post_' . $post->post_type );
$show_box_style = 'display:none';
foreach ( $translations as $t ) {
if ( $t->element_id == $post->ID ) {
return false;
}
if ( $t->status == ICL_TM_COMPLETE && ! $t->needs_update ) {
$show_box_style = '';
break;
}
}
return $show_box_style;
}
private function process_translated_string( $translation_proxy_job_id, $language ) {
$project = TranslationProxy::get_current_project();
$translation = $project->fetch_translation( $translation_proxy_job_id );
$translation = apply_filters( 'icl_data_from_pro_translation', $translation );
$ret = false;
$translation = $this->xliff_reader_factory->string_xliff_reader()->get_data( $translation );
if ( $translation ) {
$ret = icl_translation_add_string_translation( $translation_proxy_job_id, $translation, $language );
if ( $ret ) {
$project->update_job( $translation_proxy_job_id );
}
}
return $ret;
}
private function add_error( $project_error ) {
$this->errors[] = $project_error;
}
/**
* @param $project TranslationProxy_Project
*/
function enqueue_project_errors( $project ) {
if ( isset( $project ) && isset( $project->errors ) && $project->errors ) {
foreach ( $project->errors as $project_error ) {
$this->add_error( $project_error );
}
}
}
/**
* @param TranslationManagement $iclTranslationManagement
*/
private function maybe_init_translation_management( $iclTranslationManagement ) {
if ( empty( $this->tmg->settings ) ) {
$iclTranslationManagement->init();
}
}
}

View File

@@ -0,0 +1,283 @@
<?php
use WPML\LIB\WP\Cache;
use \WPML\Collect\Support\Traits\Macroable;
use WPML\FP\Str;
use function \WPML\FP\System\sanitizeString;
use function \WPML\FP\pipe;
use function \WPML\FP\curryN;
/**
* @method static int get_batch_id_from_name( string $basket_name )
*/
class WPML_Translation_Basket {
use Macroable;
/** @var wpdb $wpdb */
private $wpdb;
public function __construct( \wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
/**
* Returns an array representation of the current translation basket
*
* @param bool|false $force if true reloads the baskets contents from the database
*
* @return array
*/
function get_basket( $force = false ) {
$basket = TranslationProxy_Basket::get_basket( $force );
$basket = $basket ? $basket : array();
return $basket;
}
/**
* @return bool|TranslationProxy_Project
*/
public function get_project() {
return TranslationProxy::get_current_project();
}
function get_item_types() {
return TranslationProxy_Basket::get_basket_items_types();
}
/**
* Returns a batch instance by basket- or batch-name
*
* @param string $basket_name
*
* @return WPML_Translation_Batch
*/
function get_basket_batch( $basket_name ) {
return new WPML_Translation_Batch( $this->wpdb, self::get_batch_id_from_name( $basket_name ) );
}
/**
* Sets the remote target languages before committing the basket to a translation service.
*
* @param array $remote_languages
*/
function set_remote_target_languages( $remote_languages ) {
TranslationProxy_Basket::set_remote_target_languages( $remote_languages );
}
/**
* Removes all items from the current translation basket.
*/
function delete_all_items() {
TranslationProxy_Basket::delete_all_items_from_basket();
}
/**
* Returns the name of the current translation basket.
*
* @return bool|string
*/
function get_name() {
return TranslationProxy_Basket::get_basket_name();
}
function set_name( $basket_name ) {
TranslationProxy_Basket::set_basket_name( $basket_name );
}
function set_options( array $batch_options ) {
TranslationProxy_Basket::set_options( $batch_options );
}
/** @return array */
function get_options() {
return TranslationProxy_Basket::get_options();
}
/**
* @param string $basket_name
* @param int $basket_name_max_length
*
* @return array
*/
function check_basket_name( $basket_name, $basket_name_max_length ) {
$result = array(
'modified' => false,
'valid' => true,
'message' => '',
'new_value' => '',
);
$old_value = $basket_name;
$basket_name = strip_tags( $basket_name );
if ( mb_strlen( $basket_name ) > $basket_name_max_length ) {
$result['valid'] = true;
$result['message'] = sprintf(
__( 'The length of the batch name exceeds the maximum length of %s', 'wpml-translation-management' ),
$basket_name_max_length
);
$result['new_value'] = $this->get_unique_basket_name( $basket_name, $basket_name_max_length );
} elseif ( self::get_batch_id_from_name( $basket_name ) ) {
$result['valid'] = true;
$result['new_value'] = $this->get_unique_basket_name( $basket_name, $basket_name_max_length );
$result['message'] = __(
'This batch name already exists and was modified to ensure unique naming',
'wpml-translation-management'
);
} elseif ( count( $basket_name_array = explode( '|', $basket_name ) ) === 1 ) {
$result['valid'] = true;
$result['new_value'] = $this->get_unique_basket_name( $basket_name, $basket_name_max_length );
$result['message'] = __(
'The batch name was appended with the source language of its elements.',
'wpml-translation-management'
);
}
$result['modified'] = $result['new_value'] !== '' && $result['new_value'] !== $old_value;
$result['message'] = $result['modified'] || ! $result['valid'] ? $result['message'] : '';
return $result;
}
/**
* Returns a unique name derived from an input name for a Translation Proxy Basket
*
* @param bool $name
* @param bool|int $max_length
*
* @return bool|string
*/
function get_unique_basket_name( $name, $max_length ) {
$basket_name_array = explode( '|', $name );
$name = count( $basket_name_array ) === 1
|| ( ! is_numeric( $basket_name_array[ count( $basket_name_array ) - 1 ] )
&& $basket_name_array[ count( $basket_name_array ) - 1 ] !== $this->get_source_language() )
|| ( is_numeric( $basket_name_array[ count( $basket_name_array ) - 1 ] )
&& $basket_name_array[ count( $basket_name_array ) - 2 ] !== $this->get_source_language() )
? $name . '|' . $this->get_source_language() : $name;
$name = mb_strlen( $name ) > $max_length
? $this->sanitize_basket_name( $name, $max_length ) : $name;
if ( self::get_batch_id_from_name( $name ) ) {
$suffix = 2;
$name = $this->sanitize_basket_name( $name, $max_length - mb_strlen( (string) $suffix ) - 1 );
while ( self::get_batch_id_from_name( $name . '|' . $suffix ) ) {
$suffix ++;
$name = $this->sanitize_basket_name( $name, $max_length - mb_strlen( (string) $suffix ) - 1 );
}
$name .= '|' . $suffix;
}
return $name;
}
/**
* @return string
*/
public function get_source_language() {
return TranslationProxy_Basket::get_source_language();
}
/**
* @param int $package_id
*/
public function remove_package( $package_id ) {
TranslationProxy_Basket::delete_item_from_basket( $package_id, 'package' );
}
/**
* @param int $id
* @param string $kind
*/
public function remove_item( $id, $kind ) {
TranslationProxy_Basket::delete_item_from_basket( $id, $kind );
}
/**
* Merge the basket portion with the saved basket
*
* @param array $basket_portion
*/
public function update_basket( $basket_portion = array() ) {
TranslationProxy_Basket::update_basket( $basket_portion );
}
private function sanitize_basket_name( $basket_name, $max_length ) {
// input basket name is separated by pipes so we explode it
$to_trim = mb_strlen( $basket_name ) - $max_length;
if ( $to_trim <= 0 ) {
return $basket_name;
}
$basket_name_array = explode( '|', $basket_name );
$wpml_flag = count( $basket_name_array ) < 3;
if ( $wpml_flag === false && count( $basket_name_array ) < 2 ) {
return mb_substr( $basket_name, $max_length - 1 );
}
// first we trim the middle part holding the "WPML"
if ( $wpml_flag ) {
list( $basket_name_array, $to_trim ) = $this->shorten_basket_name( $basket_name_array, 1, $to_trim );
}
// then trim the site name first, if that's not enough move the array index and also trim the language
for ( $i = 0; $i <= 1; $i ++ ) {
if ( $to_trim > 0 ) {
list( $basket_name_array, $to_trim ) = $this->shorten_basket_name( $basket_name_array, 0, $to_trim );
$basket_name_array = array_filter( $basket_name_array );
} else {
break;
}
}
$basket_name_array = array_filter( $basket_name_array );
return implode( '|', $basket_name_array );
}
private function shorten_basket_name( $name_array, $index, $to_trim ) {
if ( mb_strlen( $name_array [ $index ] ) > $to_trim ) {
$name_array[ $index ] = mb_substr( $name_array[ $index ], 0, mb_strlen( $name_array [ $index ] ) - $to_trim - 1 );
$name_array = array_filter( $name_array );
$to_trim = 0;
} else {
$to_trim = $to_trim - mb_strlen( $name_array [ $index ] ) - 1; // subtract one here since we lose a downstroke
unset( $name_array [ $index ] );
}
return array( $name_array, $to_trim );
}
}
/**
* Returns the batch id for a given basket or batch name
*
* @param string $basket_name
*
* @return int|bool
*/
WPML_Translation_Basket::macro(
'get_batch_id_from_name',
curryN(
1,
Cache::memorize(
'get_batch_id_from_name',
pipe(
Str::replace( '"', '\\"' ),
sanitizeString(),
TranslationProxy_Batch::getBatchId()
)
)
)
);

View File

@@ -0,0 +1,140 @@
<?php
/**
* Class WPML_Translation_Proxy_Basket_Networking
*/
class WPML_Translation_Proxy_Basket_Networking {
/** @var WPML_Translation_Basket $basket */
private $basket;
/** @var TranslationManagement $tm_instance */
private $tm_instance;
/**
* @param WPML_Translation_Basket $basket
* @param TranslationManagement $tm_instance
*/
function __construct( $basket, &$tm_instance ) {
$this->basket = $basket;
$this->tm_instance = $tm_instance;
}
/**
* @param WPML_TM_Translation_Batch $batch
*
* @uses \WPML_Translation_Basket::get_basket Gets the array representation of the translation basket
* @uses \WPML_Translation_Proxy_Basket_Networking::generate_batch generates the batch in case no chunk was given for the commit from the basket
* @uses \WPML_Translation_Proxy_Basket_Networking::get_batch_name
* @uses \WPML_Translation_Proxy_Basket_Networking::send_all_jobs
*
* @return array
*/
function commit_basket_chunk( WPML_TM_Translation_Batch $batch ) {
$result = $this->send_all_jobs( $batch );
$error_messages = $this->tm_instance->messages_by_type( 'error' );
if ( ( $has_error = (bool) $error_messages ) === true ) {
\WPML\TM\API\Batch::rollback( $batch->get_basket_name() );
$result['message'] = '';
$result['additional_messages'] = $error_messages;
}
return array( $has_error, $result, $error_messages );
}
/**
* Checks if an array of translators has any remote translators in it.
*
* @param array $translators
*
* @return bool
*/
function contains_remote_translators( array $translators ) {
return count( array_filter( $translators, 'is_numeric' ) ) < count( $translators );
}
/**
* Sends all jobs from basket in batch mode to translation proxy
*
* @param WPML_TM_Translation_Batch $batch
* @param array $translators
* @param array $batch_options
*
* @return bool false in case of errors (read from TranslationManagement::get_messages('error') to get errors details)
*/
private function send_all_jobs( WPML_TM_Translation_Batch $batch ) {
$this->basket->set_options( $batch->get_batch_options() );
$this->basket->set_name( $batch->get_basket_name() );
$this->basket->set_remote_target_languages( $batch->get_remote_target_languages() );
$basket_items_types = $this->basket->get_item_types();
foreach ( $basket_items_types as $item_type_name => $item_type ) {
do_action(
'wpml_tm_send_' . $item_type_name . '_jobs',
$batch,
$item_type_name,
\WPML\TM\API\Jobs::SENT_VIA_BASKET
);
}
// check if there were no errors
return ! $this->tm_instance->messages_by_type( 'error' );
}
/**
* Generates the batch array for posts in the basket.
*
* @param array $basket
*
* @return array
*/
private function generate_batch( array $basket ) {
$batch = array();
$posts = isset( $basket['post'] ) ? $basket['post'] : array();
foreach ( $posts as $post_id => $post ) {
$batch[] = array(
'type' => 'post',
'post_id' => $post_id,
);
}
return $batch;
}
/**
* Returns the name of the batch that contains the given post_id.
*
* @param int $post_id
*
* @return null|string
*/
private function get_batch_name( $post_id ) {
global $wpdb;
$name = $wpdb->get_var(
$wpdb->prepare(
" SELECT b.batch_name
FROM {$wpdb->prefix}icl_translation_batches b
JOIN {$wpdb->prefix}icl_translation_status s
ON s.batch_id = b.id
JOIN {$wpdb->prefix}icl_translations t
ON t.translation_id = s.translation_id
JOIN {$wpdb->prefix}icl_translations o
ON o.trid = t.trid
AND o.language_code = t.source_language_code
JOIN {$wpdb->posts} p
ON o.element_id = p.ID
AND o.element_type = CONCAT('post_', p.post_type)
WHERE o.element_id = %d
ORDER BY b.id
LIMIT 1",
$post_id
)
);
$this->basket->set_name( $name );
return $name;
}
}