first commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Action_Helper {
|
||||
|
||||
public function get_tm_instance() {
|
||||
|
||||
return wpml_load_core_tm();
|
||||
}
|
||||
|
||||
public function create_translation_package( $post ) {
|
||||
$package_helper = new WPML_Element_Translation_Package();
|
||||
|
||||
return $package_helper->create_translation_package( $post );
|
||||
}
|
||||
|
||||
public function add_translation_job( $rid, $translator_id, $translation_package, $batch_options = array() ) {
|
||||
|
||||
return $this->get_update_translation_action( $translation_package )
|
||||
->add_translation_job( $rid, $translator_id, $translation_package, $batch_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate post md5
|
||||
*
|
||||
* @param object|int $post
|
||||
*
|
||||
* @return string
|
||||
* @todo full support for custom posts and custom taxonomies
|
||||
*/
|
||||
public function post_md5( $post ) {
|
||||
$post_key = '';
|
||||
|
||||
// TODO: [WPML 3.2] Make it work with PackageTranslation: this is not the right way anymore
|
||||
if ( isset( $post->external_type ) && $post->external_type ) {
|
||||
foreach ( $post->string_data as $key => $value ) {
|
||||
$post_key .= $key . $value;
|
||||
}
|
||||
} else {
|
||||
if ( is_numeric( $post ) ) {
|
||||
$post = get_post( $post );
|
||||
}
|
||||
|
||||
$post_tags = $this->get_post_terms( $post, 'post_tag' );
|
||||
$post_categories = $this->get_post_terms( $post, 'category' );
|
||||
$post_taxonomies = $this->get_post_taxonomies( $post );
|
||||
$custom_fields_values = $this->get_post_custom_fields( $post );
|
||||
|
||||
$content = $post->post_content;
|
||||
$content = apply_filters( 'wpml_pb_shortcode_content_for_translation', $content, $post->ID );
|
||||
|
||||
/**
|
||||
* Filters the post content used to build the post md5.
|
||||
*
|
||||
* @since 2.10.0
|
||||
* @internal
|
||||
*
|
||||
* @param string $content
|
||||
* @param WP_Post $post
|
||||
*/
|
||||
$content = apply_filters( 'wpml_tm_post_md5_content', $content, $post );
|
||||
|
||||
$post_key = $post->post_title . ';' . $content . ';' . $post->post_excerpt . ';' . implode( ',', $post_tags ) . ';' . implode( ',', $post_categories ) . ';' . implode( ',', $custom_fields_values );
|
||||
|
||||
if ( ! empty( $post_taxonomies ) ) {
|
||||
$post_key .= ';' . implode( ';', $post_taxonomies );
|
||||
}
|
||||
if ( wpml_get_setting_filter( false, 'translated_document_page_url' ) === 'translate' ) {
|
||||
$post_key .= $post->post_name . ';';
|
||||
}
|
||||
}
|
||||
|
||||
$post_key = apply_filters( 'wpml_post_md5_key', $post_key, $post );
|
||||
|
||||
return md5( $post_key );
|
||||
}
|
||||
|
||||
private function get_post_terms( $post, $taxonomy, $sort = false ) {
|
||||
global $sitepress;
|
||||
|
||||
$terms = array();
|
||||
// we shouldn't adjust term by current language need get terms by post_id
|
||||
remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1 );
|
||||
|
||||
$post_taxonomy_terms = wp_get_object_terms( $post->ID, $taxonomy );
|
||||
if ( ! is_wp_error( $post_taxonomy_terms ) ) {
|
||||
foreach ( $post_taxonomy_terms as $trm ) {
|
||||
$terms[] = $trm->name;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $terms ) {
|
||||
sort( $terms, SORT_STRING );
|
||||
}
|
||||
|
||||
add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
private function get_post_taxonomies( $post ) {
|
||||
global $wpdb, $sitepress_settings;
|
||||
|
||||
$post_taxonomies = array();
|
||||
|
||||
// get custom taxonomies
|
||||
$taxonomies = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT DISTINCT tx.taxonomy
|
||||
FROM {$wpdb->term_taxonomy} tx JOIN {$wpdb->term_relationships} tr ON tx.term_taxonomy_id = tr.term_taxonomy_id
|
||||
WHERE tr.object_id =%d ",
|
||||
$post->ID
|
||||
)
|
||||
);
|
||||
sort( $taxonomies, SORT_STRING );
|
||||
if ( isset( $sitepress_settings['taxonomies_sync_option'] ) ) {
|
||||
foreach ( $taxonomies as $t ) {
|
||||
if ( taxonomy_exists( $t ) && isset( $sitepress_settings['taxonomies_sync_option'][ $t ] ) && $sitepress_settings['taxonomies_sync_option'][ $t ] == 1 ) {
|
||||
$taxs = $this->get_post_terms( $post, $t );
|
||||
|
||||
if ( $taxs ) {
|
||||
$post_taxonomies[] = '[' . $t . ']:' . implode( ',', $taxs );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $post_taxonomies;
|
||||
}
|
||||
|
||||
private function get_post_custom_fields( $post ) {
|
||||
$custom_fields_values = array();
|
||||
|
||||
foreach ( \WPML\TM\Settings\Repository::getCustomFields() as $cf => $op ) {
|
||||
if ( in_array( (int) $op, array( WPML_TRANSLATE_CUSTOM_FIELD, WPML_COPY_ONCE_CUSTOM_FIELD ), true ) ) {
|
||||
$value = get_post_meta( $post->ID, $cf, true );
|
||||
if ( is_scalar( $value ) ) {
|
||||
$custom_fields_values[ $cf ] = $value;
|
||||
} else {
|
||||
$custom_fields_values[ $cf ] = wp_json_encode( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$custom_fields_values = apply_filters( 'wpml_custom_field_values_for_post_signature', $custom_fields_values, $post->ID );
|
||||
return $custom_fields_values;
|
||||
}
|
||||
|
||||
private function get_update_translation_action( $translation_package ) {
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-update-external-translation-data-action.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-update-post-translation-data-action.class.php';
|
||||
|
||||
return array_key_exists( 'type', $translation_package ) && $translation_package['type'] === 'post'
|
||||
? new WPML_TM_Update_Post_Translation_Data_Action() : new WPML_TM_Update_External_Translation_Data_Action();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Post_Actions extends WPML_Translation_Job_Helper {
|
||||
|
||||
/** @var WPML_TM_Action_Helper $action_helper */
|
||||
private $action_helper;
|
||||
|
||||
/** @var WPML_TM_Blog_Translators $blog_translators */
|
||||
private $blog_translators;
|
||||
|
||||
/** @var WPML_TM_Records $tm_records */
|
||||
private $tm_records;
|
||||
|
||||
/**
|
||||
* WPML_TM_Post_Actions constructor.
|
||||
*
|
||||
* @param WPML_TM_Action_Helper $helper
|
||||
* @param WPML_TM_Blog_Translators $blog_translators
|
||||
* @param WPML_TM_Records $tm_records
|
||||
*/
|
||||
public function __construct( &$helper, &$blog_translators, &$tm_records ) {
|
||||
$this->action_helper = $helper;
|
||||
$this->blog_translators = $blog_translators;
|
||||
$this->tm_records = &$tm_records;
|
||||
}
|
||||
|
||||
public function save_post_actions( $post_id, $post, $force_set_status = false ) {
|
||||
global $wpdb, $sitepress, $current_user;
|
||||
|
||||
$trid = isset( $_POST['icl_trid'] ) && is_numeric( $_POST['icl_trid'] )
|
||||
? $_POST['icl_trid'] : $sitepress->get_element_trid( $post_id, 'post_' . $post->post_type );
|
||||
|
||||
// set trid and lang code if front-end translation creating
|
||||
$trid = apply_filters( 'wpml_tm_save_post_trid_value', isset( $trid ) ? $trid : '', $post_id );
|
||||
$lang = apply_filters( 'wpml_tm_save_post_lang_value', '', $post_id );
|
||||
|
||||
$trid = $this->maybe_retrive_trid_again( $trid, $post );
|
||||
$needs_second_update = array_key_exists( 'needs_second_update', $_POST ) ? (bool) $_POST['needs_second_update'] : false;
|
||||
|
||||
// is this the original document?
|
||||
$is_original = empty( $trid )
|
||||
? false
|
||||
: ! (bool) $this->tm_records
|
||||
->icl_translations_by_element_id_and_type_prefix( $post_id, 'post_' . $post->post_type )
|
||||
->source_language_code();
|
||||
|
||||
if( $is_original ){
|
||||
$this->save_translation_priority( $post_id );
|
||||
}
|
||||
|
||||
if ( ! empty( $trid ) && ! $is_original ) {
|
||||
$lang = $lang ? $lang : $this->get_save_post_lang( $lang, $post_id );
|
||||
$res = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT element_id, language_code FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND source_language_code IS NULL
|
||||
",
|
||||
$trid ) );
|
||||
if ( $res ) {
|
||||
$original_post_id = $res->element_id;
|
||||
$from_lang = $res->language_code;
|
||||
$original_post = get_post( $original_post_id );
|
||||
$md5 = $this->action_helper->post_md5( $original_post );
|
||||
$translation_id = $this->tm_records
|
||||
->icl_translations_by_trid_and_lang( $trid, $lang )
|
||||
->translation_id();
|
||||
$user_id = $current_user->ID;
|
||||
$this->maybe_add_as_translator( $user_id, $lang, $from_lang );
|
||||
if ( $translation_id ) {
|
||||
$translation_package = $this->action_helper->create_translation_package( $original_post_id );
|
||||
list( $rid, $update ) = $this->action_helper->get_tm_instance()->update_translation_status( array(
|
||||
'translation_id' => $translation_id,
|
||||
'status' => isset( $force_set_status ) && $force_set_status > 0 ? $force_set_status : ICL_TM_COMPLETE,
|
||||
'translator_id' => $user_id,
|
||||
'needs_update' => $needs_second_update,
|
||||
'md5' => $md5,
|
||||
'translation_service' => 'local',
|
||||
'translation_package' => serialize( $translation_package )
|
||||
) );
|
||||
if ( ! $update ) {
|
||||
$job_id = $this->action_helper->add_translation_job( $rid, $user_id, $translation_package );
|
||||
} else {
|
||||
$job_id_sql = "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid";
|
||||
$job_id_prepared = $wpdb->prepare( $job_id_sql, $rid );
|
||||
$job_id = $wpdb->get_var( $job_id_prepared );
|
||||
if ( ! $job_id ) {
|
||||
$job_id = $this->action_helper->add_translation_job(
|
||||
$rid,
|
||||
$user_id,
|
||||
$translation_package
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
wpml_tm_load_old_jobs_editor()->set( $job_id, WPML_TM_Editors::WP );
|
||||
|
||||
// saving the translation
|
||||
do_action( 'wpml_save_job_fields_from_post', $job_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $trid ) && empty( $_POST['icl_minor_edit'] ) ) {
|
||||
$is_original = false;
|
||||
$translations = $sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
|
||||
foreach ( $translations as $translation ) {
|
||||
if ( $translation->original == 1 && $translation->element_id == $post_id ) {
|
||||
$is_original = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $is_original ) {
|
||||
$md5 = $this->action_helper->post_md5( $post_id );
|
||||
foreach ( $translations as $translation ) {
|
||||
if ( ! $translation->original ) {
|
||||
$emd5 = $this->tm_records->icl_translation_status_by_translation_id( $translation->translation_id )->md5();
|
||||
if ( $md5 !== $emd5 ) {
|
||||
$translation_package = $this->action_helper->create_translation_package( $post_id );
|
||||
$data = array(
|
||||
'translation_id' => $translation->translation_id,
|
||||
'needs_update' => 1,
|
||||
'md5' => $md5,
|
||||
'translation_package' => serialize( $translation_package ),
|
||||
);
|
||||
$this->action_helper->get_tm_instance()->update_translation_status( $data );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given language pair to the user.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param string $target_lang
|
||||
* @param string $source_lang
|
||||
*
|
||||
* @used-by \WPML_TM_Post_Actions::save_post_actions to add language pairs to admin users automatically when saving
|
||||
* a translation in a given language pair.
|
||||
*/
|
||||
private function maybe_add_as_translator( $user_id, $target_lang, $source_lang ) {
|
||||
|
||||
$user = new WP_User( $user_id );
|
||||
if ( $user->has_cap( 'manage_options' ) && $target_lang && ! $this->blog_translators->is_translator( $user_id,
|
||||
array(
|
||||
'lang_from' => $source_lang,
|
||||
'lang_to' => $target_lang,
|
||||
'admin_override' => false
|
||||
) )
|
||||
) {
|
||||
global $wpdb;
|
||||
|
||||
$user->add_cap( WPML_Translator_Role::CAPABILITY );
|
||||
|
||||
$language_pair_records = new WPML_Language_Pair_Records( $wpdb, new WPML_Language_Records( $wpdb ) );
|
||||
$language_pair_records->store( $user_id, array( $source_lang => array( $target_lang ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
private function get_save_post_lang( $lang, $post_id ) {
|
||||
if ( ( ! isset( $lang ) || ! $lang ) && ! empty( $_POST['icl_post_language'] ) ) {
|
||||
$lang = $_POST['icl_post_language'];
|
||||
} else {
|
||||
global $wpml_post_translations;
|
||||
|
||||
$lang = $wpml_post_translations->get_element_lang_code( $post_id );
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
private function maybe_retrive_trid_again( $trid, $post ) {
|
||||
global $wpdb, $sitepress;
|
||||
$element_type_from_trid = $wpdb->get_var( $wpdb->prepare( "SELECT element_type FROM {$wpdb->prefix}icl_translations WHERE trid=%d", $trid ) );
|
||||
if ( $element_type_from_trid && $post->post_type !== $element_type_from_trid ) {
|
||||
$trid = $sitepress->get_element_trid( $post->ID, 'post_' . $post->post_type );
|
||||
}
|
||||
|
||||
return $trid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
*/
|
||||
public function save_translation_priority( $post_id ) {
|
||||
$translation_priority = (int) filter_var(
|
||||
( isset( $_POST['icl_translation_priority'] ) ? $_POST['icl_translation_priority'] : '' ),
|
||||
FILTER_SANITIZE_NUMBER_INT );
|
||||
|
||||
if ( ! $translation_priority ) {
|
||||
$assigned_priority = $this->get_term_obj( $post_id );
|
||||
|
||||
if ( $assigned_priority ) {
|
||||
$translation_priority = $assigned_priority->term_id;
|
||||
} else {
|
||||
$term = \WPML_TM_Translation_Priorities::get_default_term();
|
||||
if ( $term ) {
|
||||
$translation_priority = $term->term_id;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if ( $translation_priority ) {
|
||||
wp_set_post_terms( $post_id, array( $translation_priority ), \WPML_TM_Translation_Priorities::TAXONOMY );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
*
|
||||
* @return WP_Term|null
|
||||
*/
|
||||
private function get_term_obj( $element_id ) {
|
||||
$terms = wp_get_object_terms( $element_id, \WPML_TM_Translation_Priorities::TAXONOMY );
|
||||
if ( is_wp_error( $terms ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return empty( $terms ) ? null : $terms[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
global $wpdb;
|
||||
|
||||
$basket_ajax = new WPML_Basket_Tab_Ajax(
|
||||
TranslationProxy::get_current_project(),
|
||||
wpml_tm_load_basket_networking(),
|
||||
new WPML_Translation_Basket( $wpdb )
|
||||
);
|
||||
add_action( 'init', array( $basket_ajax, 'init' ) );
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
define( 'WPML_TM_FOLDER', basename( WPML_TM_PATH ) );
|
||||
|
||||
define( 'WPML_TM_URL', plugins_url( '', dirname( __FILE__ ) ) );
|
||||
|
||||
define( 'TP_MIGRATION_NOT_STARTED', 0 );
|
||||
define( 'TP_MIGRATION_REQUESTED', 2 );
|
||||
define( 'TP_MIGRATION_IN_PROGRESS', 3 );
|
||||
define( 'TP_MIGRATION_WAITING_CONFIRMATION', 4 );
|
||||
define( 'TP_MIGRATION_COMPLETED', 1 );
|
||||
|
||||
if ( ! defined( 'TRANSLATION_PROXY_XLIFF_VERSION' ) ) {
|
||||
define( 'TRANSLATION_PROXY_XLIFF_VERSION', '12' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'WPML_XLIFF_TM_URL' ) ) {
|
||||
define( 'WPML_XLIFF_TM_URL', plugins_url( '', dirname( __FILE__ ) ) );
|
||||
}
|
||||
|
||||
if ( ! defined( 'WPML_XLIFF_TM_NEWLINES_REPLACE' ) ) {
|
||||
define( 'WPML_XLIFF_TM_NEWLINES_REPLACE', 1 ); }
|
||||
|
||||
if ( ! defined( 'WPML_XLIFF_TM_NEWLINES_ORIGINAL' ) ) {
|
||||
define( 'WPML_XLIFF_TM_NEWLINES_ORIGINAL', 2 ); }
|
||||
|
||||
if ( ! defined( 'WPML_XLIFF_DEFAULT_VERSION' ) ) {
|
||||
define( 'WPML_XLIFF_DEFAULT_VERSION', '12' );
|
||||
}
|
||||
if ( ! defined( 'TA_URL_ENDPOINT' ) ) {
|
||||
define( 'TA_URL_ENDPOINT', 'https://www.icanlocalize.com' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'TA_SCHEDULE_OCCURENCE' ) ) {
|
||||
define( 'TA_SCHEDULE_OCCURENCE', 'daily' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'JSON_UNESCAPED_UNICODE' ) ) {
|
||||
define( 'JSON_UNESCAPED_UNICODE', 256 );
|
||||
}
|
||||
|
||||
if ( ! defined( 'JSON_UNESCAPED_SLASHES' ) ) {
|
||||
define( 'JSON_UNESCAPED_SLASHES', 64 );
|
||||
}
|
||||
|
||||
global $asian_languages;
|
||||
$asian_languages = array( 'ja', 'ko', 'zh-hans', 'zh-hant', 'mn', 'ne', 'hi', 'pa', 'ta', 'th' );
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Element_Translations extends WPML_TM_Record_User {
|
||||
|
||||
/** @var int[] $trid_cache */
|
||||
private $trid_cache;
|
||||
/** @var int[] $job_id_cache */
|
||||
private $job_id_cache;
|
||||
/** @var int[] $job_id_cache */
|
||||
private $translation_status_cache;
|
||||
/** @var bool[] $update_status_cache */
|
||||
private $update_status_cache;
|
||||
/** @var string[] $element_type_prefix_cache */
|
||||
private $element_type_prefix_cache = array();
|
||||
|
||||
public function init_hooks() {
|
||||
add_action( 'wpml_cache_clear', array( $this, 'reload' ) );
|
||||
add_filter(
|
||||
'wpml_tm_translation_status',
|
||||
array(
|
||||
$this,
|
||||
'get_translation_status_filter',
|
||||
),
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
public function reload() {
|
||||
$this->trid_cache = array();
|
||||
$this->job_id_cache = array();
|
||||
$this->translation_status_cache = array();
|
||||
$this->update_status_cache = array();
|
||||
$this->element_type_prefix_cache = array();
|
||||
}
|
||||
|
||||
public function is_update_needed( $trid, $language_code ) {
|
||||
if ( isset( $this->update_status_cache[ $trid ][ $language_code ] ) ) {
|
||||
$needs_update = $this->update_status_cache[ $trid ][ $language_code ];
|
||||
} else {
|
||||
$this->init_job_id( $trid, $language_code );
|
||||
$needs_update = isset( $this->update_status_cache[ $trid ][ $language_code ] )
|
||||
? $this->update_status_cache[ $trid ][ $language_code ] : 0;
|
||||
}
|
||||
|
||||
return (bool) $needs_update;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $trid
|
||||
* @param string $language_code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_element_type_prefix( $trid, $language_code ) {
|
||||
if ( $trid && $language_code && ! isset( $this->element_type_prefix_cache[ $trid ] ) ) {
|
||||
$this->init_job_id( $trid, $language_code );
|
||||
}
|
||||
|
||||
return $trid && array_key_exists( $trid, $this->element_type_prefix_cache ) ? $this->element_type_prefix_cache[ $trid ] : '';
|
||||
}
|
||||
|
||||
public function get_translation_status_filter( $empty, $args ) {
|
||||
$trid = $args['trid'];
|
||||
$language_code = $args['language_code'];
|
||||
|
||||
return $this->get_translation_status( $trid, $language_code );
|
||||
}
|
||||
/**
|
||||
* @param int $trid
|
||||
* @param string $language_code
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_translation_status( $trid, $language_code ) {
|
||||
if ( isset( $this->translation_status_cache[ $trid ][ $language_code ] ) ) {
|
||||
$status = $this->translation_status_cache[ $trid ][ $language_code ];
|
||||
} else {
|
||||
$this->init_job_id( $trid, $language_code );
|
||||
$status = isset( $this->translation_status_cache[ $trid ][ $language_code ] )
|
||||
? $this->translation_status_cache[ $trid ][ $language_code ] : 0;
|
||||
}
|
||||
|
||||
return (int) $status;
|
||||
}
|
||||
|
||||
public function init_job_id( $trid, $target_lang_code ) {
|
||||
global $wpdb, $wpml_language_resolution;
|
||||
|
||||
if ( ! isset( $this->job_id_cache[ $trid ][ $target_lang_code ] ) ) {
|
||||
$jobs = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT
|
||||
tj.job_id,
|
||||
ts.status,
|
||||
ts.needs_update,
|
||||
t.language_code,
|
||||
SUBSTRING_INDEX(t.element_type, '_', 1)
|
||||
AS element_type_prefix
|
||||
FROM {$wpdb->prefix}icl_translate_job tj
|
||||
JOIN {$wpdb->prefix}icl_translation_status ts
|
||||
ON tj.rid = ts.rid
|
||||
JOIN {$wpdb->prefix}icl_translations t
|
||||
ON ts.translation_id = t.translation_id
|
||||
WHERE t.trid = %d
|
||||
",
|
||||
$trid
|
||||
)
|
||||
);
|
||||
$active_langs = $wpml_language_resolution->get_active_language_codes();
|
||||
|
||||
foreach ( $active_langs as $lang_code ) {
|
||||
$this->cache_job_in_lang( $jobs, $lang_code, $trid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object[] $jobs
|
||||
* @param string $lang
|
||||
* @param string $trid
|
||||
*
|
||||
* @return false|object
|
||||
*/
|
||||
private function cache_job_in_lang( $jobs, $lang, $trid ) {
|
||||
$res = false;
|
||||
foreach ( $jobs as $job ) {
|
||||
if ( $job->language_code === $lang ) {
|
||||
$res = $job;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (bool) $res === true ) {
|
||||
$job_id = $res->job_id;
|
||||
$status = $res->status;
|
||||
$needs_update = (bool) $res->needs_update;
|
||||
$element_type_prefix = $res->element_type_prefix;
|
||||
} else {
|
||||
$job_id = - 1;
|
||||
$status = 0;
|
||||
$needs_update = false;
|
||||
$element_type_prefix = $this->fallback_type_prefix( $trid );
|
||||
}
|
||||
|
||||
$this->cache_job( $trid, $lang, $job_id, $status, $needs_update, $element_type_prefix );
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function fallback_type_prefix( $trid ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( isset( $this->element_type_prefix_cache[ $trid ] ) && (bool) $this->element_type_prefix_cache[ $trid ] === true ) {
|
||||
$prefix = $this->element_type_prefix_cache[ $trid ];
|
||||
} elseif ( (bool) $this->tm_records->get_post_translations()->get_element_translations( null, $trid ) ) {
|
||||
$prefix = 'post';
|
||||
} else {
|
||||
$prefix = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT SUBSTRING_INDEX(element_type, '_', 1)
|
||||
FROM {$wpdb->prefix}icl_translations
|
||||
WHERE trid = %d
|
||||
LIMIT 1",
|
||||
$trid
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $trid
|
||||
* @param string $language_code
|
||||
* @param int $job_id
|
||||
* @param int $status
|
||||
* @param bool $needs_update
|
||||
* @param string $element_type_prefix
|
||||
*/
|
||||
private function cache_job( $trid, $language_code, $job_id, $status, $needs_update, $element_type_prefix ) {
|
||||
if ( (bool) $job_id === true && (bool) $trid === true && (bool) $language_code === true ) {
|
||||
$this->maybe_init_trid_cache( $trid );
|
||||
$this->job_id_cache[ $trid ][ $language_code ] = $job_id;
|
||||
$this->translation_status_cache[ $trid ][ $language_code ] = $status;
|
||||
$this->update_status_cache[ $trid ][ $language_code ] = $needs_update;
|
||||
$this->element_type_prefix_cache[ $trid ] = isset( $this->element_type_prefix_cache[ $trid ] )
|
||||
&& (bool) $this->element_type_prefix_cache[ $trid ] === true
|
||||
? $this->element_type_prefix_cache[ $trid ] : $element_type_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
private function maybe_init_trid_cache( $trid ) {
|
||||
foreach (
|
||||
array(
|
||||
&$this->job_id_cache,
|
||||
&$this->trid_cache,
|
||||
&$this->translation_status_cache,
|
||||
&$this->update_status_cache,
|
||||
) as $cache
|
||||
) {
|
||||
$cache[ $trid ] = isset( $cache[ $trid ] ) ? $cache[ $trid ] : array();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
function deprecated_icl_data_from_pro_translation( $translation ) {
|
||||
return apply_filters( 'icl_data_from_pro_translation', $translation );
|
||||
}
|
||||
|
||||
add_filter( 'wpml_tm_data_from_pro_translation', 'deprecated_icl_data_from_pro_translation', PHP_INT_MAX, 1 );
|
||||
@@ -0,0 +1,869 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\Editor\ClassicEditorActions;
|
||||
use function WPML\Container\make;
|
||||
use WPML\TM\Jobs\Query\LimitQueryHelper;
|
||||
use WPML\TM\Jobs\Query\OrderQueryHelper;
|
||||
use WPML\TM\Jobs\Query\PostQuery;
|
||||
use WPML\TM\Jobs\Query\QueryBuilder;
|
||||
use WPML\TM\Jobs\Query\PackageQuery;
|
||||
use WPML\TM\Jobs\Query\StringQuery;
|
||||
use WPML\TM\Jobs\Query\StringsBatchQuery;
|
||||
use WPML\TM\Jobs\Query\CompositeQuery;
|
||||
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Element_Translations
|
||||
*/
|
||||
function wpml_tm_load_element_translations() {
|
||||
global $wpml_tm_element_translations, $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
if ( ! isset( $wpml_tm_element_translations ) ) {
|
||||
require_once WPML_TM_PATH . '/inc/core/wpml-tm-element-translations.class.php';
|
||||
$tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$wpml_tm_element_translations = new WPML_TM_Element_Translations( $tm_records );
|
||||
$wpml_tm_element_translations->init_hooks();
|
||||
}
|
||||
|
||||
return $wpml_tm_element_translations;
|
||||
}
|
||||
|
||||
function wpml_tm_load_status_display_filter() {
|
||||
global $wpml_tm_status_display_filter, $iclTranslationManagement, $sitepress, $wpdb;
|
||||
|
||||
$blog_translators = wpml_tm_load_blog_translators();
|
||||
$tm_api = new WPML_TM_API( $blog_translators, $iclTranslationManagement );
|
||||
$tm_api->init_hooks();
|
||||
if ( ! isset( $wpml_tm_status_display_filter ) ) {
|
||||
$status_helper = wpml_get_post_status_helper();
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$wpml_tm_status_display_filter = new WPML_TM_Translation_Status_Display(
|
||||
$wpdb,
|
||||
$sitepress,
|
||||
$status_helper,
|
||||
$job_factory,
|
||||
$tm_api
|
||||
);
|
||||
}
|
||||
|
||||
$wpml_tm_status_display_filter->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Page_Builders_Hooks
|
||||
*/
|
||||
function wpml_tm_page_builders_hooks() {
|
||||
static $page_builder_hooks;
|
||||
if ( ! $page_builder_hooks ) {
|
||||
global $sitepress;
|
||||
$page_builder_hooks = new WPML_TM_Page_Builders_Hooks( null, $sitepress );
|
||||
}
|
||||
|
||||
return $page_builder_hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Custom_XML_Factory
|
||||
*/
|
||||
function wpml_tm_custom_xml_factory() {
|
||||
static $tm_custom_xml_factory;
|
||||
// @fixme `class_exists` check is most likely not needed anymore
|
||||
if ( class_exists( 'WPML_TM_Custom_XML_Factory' ) && ! $tm_custom_xml_factory ) {
|
||||
$tm_custom_xml_factory = new WPML_TM_Custom_XML_Factory();
|
||||
}
|
||||
|
||||
return $tm_custom_xml_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Custom_XML_UI_Hooks
|
||||
*/
|
||||
function wpml_tm_custom_xml_ui_hooks() {
|
||||
static $tm_custom_xml_ui_hooks;
|
||||
// @fixme `class_exists` checks are most likely not needed anymore
|
||||
if ( class_exists( 'WPML_TM_Custom_XML_UI_Hooks' ) && ! $tm_custom_xml_ui_hooks ) {
|
||||
global $sitepress;
|
||||
$factory = wpml_tm_custom_xml_factory();
|
||||
if ( $factory ) {
|
||||
$tm_custom_xml_ui_hooks = new WPML_TM_Custom_XML_UI_Hooks( $factory->create_ui(), $factory->create_resources( $sitepress->get_wp_api() ), $factory->create_ajax() );
|
||||
}
|
||||
}
|
||||
|
||||
return $tm_custom_xml_ui_hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_Translations_Queue_Factory
|
||||
*/
|
||||
function wpml_tm_translation_queue_factory() {
|
||||
static $translation_queue_factory;
|
||||
if ( ! $translation_queue_factory ) {
|
||||
$translation_queue_factory = new WPML_Translations_Queue_Factory();
|
||||
}
|
||||
|
||||
return $translation_queue_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_UI_Screen_Options_Factory
|
||||
*/
|
||||
function wpml_ui_screen_options_factory() {
|
||||
static $screen_options_factory;
|
||||
if ( ! $screen_options_factory ) {
|
||||
global $sitepress;
|
||||
$screen_options_factory = new WPML_UI_Screen_Options_Factory( $sitepress );
|
||||
}
|
||||
|
||||
return $screen_options_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Loader
|
||||
*/
|
||||
function wpml_tm_loader() {
|
||||
static $tm_loader;
|
||||
if ( ! $tm_loader ) {
|
||||
$tm_loader = new WPML_TM_Loader();
|
||||
}
|
||||
|
||||
return $tm_loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TP_Translator
|
||||
*/
|
||||
function wpml_tm_translator() {
|
||||
static $tm_translator;
|
||||
if ( ! $tm_translator ) {
|
||||
$tm_translator = new WPML_TP_Translator();
|
||||
}
|
||||
|
||||
return $tm_translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of \WPML_Translation_Management.
|
||||
*
|
||||
* @return \WPML_Translation_Management
|
||||
*/
|
||||
function wpml_translation_management() {
|
||||
global $WPML_Translation_Management;
|
||||
if ( ! $WPML_Translation_Management ) {
|
||||
global $sitepress;
|
||||
$WPML_Translation_Management = new WPML_Translation_Management( $sitepress, wpml_tm_loader(), wpml_load_core_tm(), wpml_tm_translator() );
|
||||
}
|
||||
|
||||
return $WPML_Translation_Management;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_Translation_Basket
|
||||
*/
|
||||
function wpml_translation_basket() {
|
||||
static $translation_basket;
|
||||
if ( ! $translation_basket ) {
|
||||
global $wpdb;
|
||||
$translation_basket = new WPML_Translation_Basket( $wpdb );
|
||||
}
|
||||
|
||||
return $translation_basket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML_TM_Translate_Independently
|
||||
*/
|
||||
function wpml_tm_translate_independently() {
|
||||
static $translate_independently;
|
||||
if ( ! $translate_independently ) {
|
||||
global $sitepress;
|
||||
|
||||
$translate_independently = new WPML_TM_Translate_Independently( wpml_load_core_tm(), wpml_translation_basket(), $sitepress );
|
||||
}
|
||||
|
||||
return $translate_independently;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_Translation_Proxy_Basket_Networking
|
||||
*/
|
||||
function wpml_tm_load_basket_networking() {
|
||||
global $iclTranslationManagement, $wpdb;
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-proxy/wpml-translationproxy-basket-networking.class.php';
|
||||
|
||||
$basket = new WPML_Translation_Basket( $wpdb );
|
||||
|
||||
return new WPML_Translation_Proxy_Basket_Networking( $basket, $iclTranslationManagement );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_Translation_Proxy_Networking
|
||||
*/
|
||||
function wpml_tm_load_tp_networking() {
|
||||
global $wpml_tm_tp_networking;
|
||||
|
||||
if ( ! isset( $wpml_tm_tp_networking ) ) {
|
||||
$tp_lock_factory = new WPML_TP_Lock_Factory();
|
||||
$wpml_tm_tp_networking = new WPML_Translation_Proxy_Networking( new WP_Http(), $tp_lock_factory->create() );
|
||||
}
|
||||
|
||||
return $wpml_tm_tp_networking;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Blog_Translators
|
||||
*/
|
||||
function wpml_tm_load_blog_translators() {
|
||||
global $wpdb, $sitepress, $wpml_post_translations, $wpml_term_translations, $wpml_cache_factory;
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$translator_records = new WPML_Translator_Records( $wpdb, new WPML_WP_User_Query_Factory(), wp_roles() );
|
||||
|
||||
$instance = new WPML_TM_Blog_Translators( $sitepress, $tm_records, $translator_records, $wpml_cache_factory );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Translators_Dropdown
|
||||
*/
|
||||
function wpml_tm_get_translators_dropdown() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new WPML_TM_Translators_Dropdown( wpml_tm_load_blog_translators() );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Mail_Notification
|
||||
*/
|
||||
function wpml_tm_init_mail_notifications() {
|
||||
global $wpml_tm_mailer, $sitepress, $wpdb, $iclTranslationManagement, $wpml_translation_job_factory, $wp_api;
|
||||
|
||||
if ( null === $wp_api ) {
|
||||
$wp_api = new WPML_WP_API();
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
$blog_translators = wpml_tm_load_blog_translators();
|
||||
$email_twig_factory = new WPML_TM_Email_Twig_Template_Factory();
|
||||
$batch_report = new WPML_TM_Batch_Report( $blog_translators );
|
||||
$batch_report_email_template = new WPML_TM_Email_Jobs_Summary_View(
|
||||
$email_twig_factory->create(),
|
||||
$blog_translators,
|
||||
$sitepress
|
||||
);
|
||||
$batch_report_email_builder = new WPML_TM_Batch_Report_Email_Builder(
|
||||
$batch_report,
|
||||
$batch_report_email_template
|
||||
);
|
||||
$batch_report_email_process = new WPML_TM_Batch_Report_Email_Process(
|
||||
$batch_report,
|
||||
$batch_report_email_builder
|
||||
);
|
||||
$batch_report_hooks = new WPML_TM_Batch_Report_Hooks( $batch_report, $batch_report_email_process );
|
||||
$batch_report_hooks->add_hooks();
|
||||
|
||||
$wpml_tm_unsent_jobs = new WPML_TM_Unsent_Jobs( $blog_translators, $sitepress );
|
||||
$wpml_tm_unsent_jobs->add_hooks();
|
||||
|
||||
$wpml_tm_unsent_jobs_notice = new WPML_TM_Unsent_Jobs_Notice( $wp_api );
|
||||
$wpml_tm_unsent_jobs_notice_hooks = new WPML_TM_Unsent_Jobs_Notice_Hooks(
|
||||
$wpml_tm_unsent_jobs_notice,
|
||||
$wp_api,
|
||||
WPML_Notices::DISMISSED_OPTION_KEY
|
||||
);
|
||||
$wpml_tm_unsent_jobs_notice_hooks->add_hooks();
|
||||
|
||||
$user_jobs_notification_settings = new WPML_User_Jobs_Notification_Settings();
|
||||
$user_jobs_notification_settings->add_hooks();
|
||||
|
||||
$email_twig_factory = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/user-profile/' ) );
|
||||
$notification_template = new WPML_User_Jobs_Notification_Settings_Template( $email_twig_factory->get_template() );
|
||||
|
||||
$user_jobs_notification_settings_render = new WPML_User_Jobs_Notification_Settings_Render( $notification_template );
|
||||
$user_jobs_notification_settings_render->add_hooks();
|
||||
}
|
||||
|
||||
if ( ! isset( $wpml_tm_mailer ) ) {
|
||||
$iclTranslationManagement = $iclTranslationManagement ? $iclTranslationManagement : wpml_load_core_tm();
|
||||
if ( empty( $iclTranslationManagement->settings ) ) {
|
||||
$iclTranslationManagement->init();
|
||||
}
|
||||
$settings = isset( $iclTranslationManagement->settings['notification'] ) ? $iclTranslationManagement->settings['notification'] : array();
|
||||
|
||||
$email_twig_factory = new WPML_TM_Email_Twig_Template_Factory();
|
||||
$email_notification_view = new WPML_TM_Email_Notification_View( $email_twig_factory->create() );
|
||||
|
||||
$has_active_remote_service = TranslationProxy::is_current_service_active_and_authenticated();
|
||||
|
||||
$wpml_tm_mailer = new WPML_TM_Mail_Notification(
|
||||
$sitepress,
|
||||
$wpdb,
|
||||
$wpml_translation_job_factory,
|
||||
$email_notification_view,
|
||||
$settings,
|
||||
$has_active_remote_service
|
||||
);
|
||||
}
|
||||
$wpml_tm_mailer->init();
|
||||
|
||||
return $wpml_tm_mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return WPML_Dashboard_Ajax
|
||||
*/
|
||||
function wpml_tm_load_tm_dashboard_ajax() {
|
||||
global $wpml_tm_dashboard_ajax, $sitepress;
|
||||
|
||||
if ( ! isset( $wpml_tm_dashboard_ajax ) ) {
|
||||
require_once WPML_TM_PATH . '/menu/dashboard/wpml-tm-dashboard-ajax.class.php';
|
||||
$wpml_tm_dashboard_ajax = new WPML_Dashboard_Ajax( new WPML_Super_Globals_Validation() );
|
||||
|
||||
if ( defined( 'OTG_TRANSLATION_PROXY_URL' ) && defined( 'ICL_SITEPRESS_VERSION' ) ) {
|
||||
$wpml_tp_api = wpml_tm_get_tp_project_api();
|
||||
|
||||
$wpml_tp_api_ajax = new WPML_TP_Refresh_Language_Pairs( $wpml_tp_api );
|
||||
$wpml_tp_api_ajax->add_hooks();
|
||||
|
||||
$sync_jobs_ajax_handler = new WPML_TP_Sync_Ajax_Handler(
|
||||
wpml_tm_get_tp_sync_jobs(),
|
||||
new WPML_TM_Sync_Installer_Wrapper(),
|
||||
new WPML_TM_Last_Picked_Up( $sitepress )
|
||||
);
|
||||
$sync_jobs_ajax_handler->add_hooks();
|
||||
}
|
||||
}
|
||||
|
||||
return $wpml_tm_dashboard_ajax;
|
||||
}
|
||||
|
||||
function wpml_tm_load_and_intialize_dashboard_ajax() {
|
||||
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
|
||||
if ( defined( 'DOING_AJAX' ) ) {
|
||||
$wpml_tm_dashboard_ajax = wpml_tm_load_tm_dashboard_ajax();
|
||||
add_action( 'init', array( $wpml_tm_dashboard_ajax, 'init_ajax_actions' ) );
|
||||
} elseif (
|
||||
is_admin() && isset( $_GET['page'] ) && WPML_TM_FOLDER . '/menu/main.php' === $_GET['page'] && ( ! isset( $_GET['sm'] ) || $_GET['sm'] === 'dashboard' )
|
||||
) {
|
||||
$wpml_tm_dashboard_ajax = wpml_tm_load_tm_dashboard_ajax();
|
||||
add_action( 'wpml_tm_scripts_enqueued', array( $wpml_tm_dashboard_ajax, 'enqueue_js' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'wpml_tm_load_and_intialize_dashboard_ajax' );
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return WPML_Translation_Job_Factory
|
||||
*/
|
||||
function wpml_tm_load_job_factory() {
|
||||
global $wpml_translation_job_factory, $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
if ( ! $wpml_translation_job_factory ) {
|
||||
$tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$wpml_translation_job_factory = new WPML_Translation_Job_Factory( $tm_records );
|
||||
$wpml_translation_job_factory->init_hooks();
|
||||
}
|
||||
|
||||
return $wpml_translation_job_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return WPML_TM_XLIFF_Factory
|
||||
*/
|
||||
function wpml_tm_xliff_factory() {
|
||||
static $xliff_factory;
|
||||
|
||||
if ( ! $xliff_factory ) {
|
||||
$xliff_factory = new WPML_TM_XLIFF_Factory();
|
||||
}
|
||||
|
||||
return $xliff_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return WPML_TM_XLIFF_Shortcodes
|
||||
*/
|
||||
function wpml_tm_xliff_shortcodes() {
|
||||
static $xliff_shortcodes;
|
||||
|
||||
if ( ! $xliff_shortcodes ) {
|
||||
$xliff_shortcodes = new WPML_TM_XLIFF_Shortcodes();
|
||||
}
|
||||
|
||||
return $xliff_shortcodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an instance of the class.
|
||||
*
|
||||
* @return \WPML_TM_Old_Jobs_Editor
|
||||
*/
|
||||
function wpml_tm_load_old_jobs_editor() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new WPML_TM_Old_Jobs_Editor( wpml_tm_load_job_factory() );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
function tm_after_load() {
|
||||
global $wpml_tm_translation_status, $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
if ( ! isset( $wpml_tm_translation_status ) ) {
|
||||
require_once WPML_TM_PATH . '/inc/translation-proxy/translationproxy.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/ajax.php';
|
||||
|
||||
( new ClassicEditorActions() )->addHooks();
|
||||
|
||||
wpml_tm_load_job_factory();
|
||||
wpml_tm_init_mail_notifications();
|
||||
wpml_tm_load_element_translations();
|
||||
$wpml_tm_translation_status = make( WPML_TM_Translation_Status::class );
|
||||
$wpml_tm_translation_status->init();
|
||||
add_action( 'wpml_pre_status_icon_display', 'wpml_tm_load_status_display_filter' );
|
||||
require_once WPML_TM_PATH . '/inc/wpml-private-actions.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an instance of the class.
|
||||
*
|
||||
* @return WPML_TM_Records
|
||||
*/
|
||||
function wpml_tm_get_records() {
|
||||
global $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
return new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an instance of the class.
|
||||
*
|
||||
* @return WPML_TM_Xliff_Frontend
|
||||
*/
|
||||
function setup_xliff_frontend() {
|
||||
global $xliff_frontend;
|
||||
|
||||
$xliff_factory = new WPML_TM_XLIFF_Factory();
|
||||
$xliff_frontend = $xliff_factory->create_frontend();
|
||||
|
||||
add_action( 'init', array( $xliff_frontend, 'init' ), $xliff_frontend->get_init_priority() );
|
||||
|
||||
return $xliff_frontend;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an instance of the class.
|
||||
*
|
||||
* @param int $job_id The ID of the job.
|
||||
* @param int $rid
|
||||
*
|
||||
* @return WPML_TM_ATE_Models_Job_Create
|
||||
*/
|
||||
function wpml_tm_create_ATE_job_creation_model( $job_id, $rid ) {
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$translation_job = $job_factory->get_translation_job( $job_id, false, 0, true );
|
||||
|
||||
$job = new WPML_TM_ATE_Models_Job_Create();
|
||||
$job->source_id = $rid;
|
||||
$job->source_language->code = $translation_job->get_source_language_code();
|
||||
$job->source_language->name = $translation_job->get_source_language_code( true );
|
||||
$job->target_language->code = $translation_job->get_language_code();
|
||||
$job->target_language->name = $translation_job->get_language_code( true );
|
||||
$job->deadline = strtotime( $translation_job->get_deadline_date() );
|
||||
|
||||
$job->permalink = '#';
|
||||
if ( 'Post' === $translation_job->get_type() ) {
|
||||
$job->permalink = get_permalink( $translation_job->get_original_element_id() );
|
||||
}
|
||||
|
||||
$job->notify_enabled = true;
|
||||
$job->notify_url = WPML_TM_REST_ATE_Public::get_receive_ate_job_url( $job_id );
|
||||
|
||||
$job->site_identifier = wpml_get_site_id( WPML_TM_ATE::SITE_ID_SCOPE );
|
||||
|
||||
$encoded_xliff = base64_encode( wpml_tm_get_job_xliff( $job_id ) );
|
||||
|
||||
$job->file->type = 'data:application/x-xliff;base64';
|
||||
$job->file->name = $translation_job->get_title();
|
||||
|
||||
$job->file->content = $encoded_xliff;
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @param int $job_id The ID of the job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wpml_tm_get_job_xliff( $job_id ) {
|
||||
static $xliff_writer;
|
||||
|
||||
if ( ! $xliff_writer ) {
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$xliff_writer = new WPML_TM_Xliff_Writer( $job_factory );
|
||||
}
|
||||
|
||||
return $xliff_writer->generate_job_xliff( $job_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_Rest
|
||||
*/
|
||||
function wpml_tm_get_wpml_rest() {
|
||||
static $wpml_rest;
|
||||
|
||||
if ( ! $wpml_rest ) {
|
||||
$http = new WP_Http();
|
||||
$wpml_rest = new WPML_Rest( $http );
|
||||
}
|
||||
|
||||
return $wpml_rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_API_Client
|
||||
*/
|
||||
function wpml_tm_get_tp_api_client() {
|
||||
static $client;
|
||||
|
||||
if ( ! $client ) {
|
||||
$client = new WPML_TP_API_Client(
|
||||
OTG_TRANSLATION_PROXY_URL,
|
||||
new WP_Http(),
|
||||
new WPML_TP_Lock( new WPML_WP_API() ),
|
||||
new WPML_TP_HTTP_Request_Filter()
|
||||
);
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_Project
|
||||
*/
|
||||
function wpml_tm_get_tp_project() {
|
||||
static $project;
|
||||
|
||||
if ( ! $project ) {
|
||||
global $sitepress;
|
||||
|
||||
$translation_service = $sitepress->get_setting( 'translation_service' );
|
||||
$translation_projects = $sitepress->get_setting( 'icl_translation_projects' );
|
||||
$project = new WPML_TP_Project( $translation_service, $translation_projects );
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_Jobs_API
|
||||
*/
|
||||
function wpml_tm_get_tp_jobs_api() {
|
||||
static $api;
|
||||
|
||||
if ( ! $api ) {
|
||||
$api = new WPML_TP_Jobs_API(
|
||||
wpml_tm_get_tp_api_client(),
|
||||
wpml_tm_get_tp_project(),
|
||||
new WPML_TM_Log()
|
||||
);
|
||||
}
|
||||
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_Project_API
|
||||
*/
|
||||
function wpml_tm_get_tp_project_api() {
|
||||
static $api;
|
||||
|
||||
if ( ! $api ) {
|
||||
$api = new WPML_TP_Project_API(
|
||||
wpml_tm_get_tp_api_client(),
|
||||
wpml_tm_get_tp_project(),
|
||||
new WPML_TM_Log()
|
||||
);
|
||||
}
|
||||
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_XLIFF_API
|
||||
*/
|
||||
function wpml_tm_get_tp_xliff_api() {
|
||||
static $api;
|
||||
|
||||
if ( ! $api ) {
|
||||
$api = new WPML_TP_XLIFF_API(
|
||||
wpml_tm_get_tp_api_client(),
|
||||
wpml_tm_get_tp_project(),
|
||||
new WPML_TM_Log(),
|
||||
new WPML_TP_Xliff_Parser()
|
||||
);
|
||||
}
|
||||
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TM_Jobs_Repository
|
||||
*/
|
||||
function wpml_tm_get_jobs_repository() {
|
||||
static $repository;
|
||||
|
||||
if ( ! $repository ) {
|
||||
global $wpdb;
|
||||
|
||||
$limit_helper = new LimitQueryHelper();
|
||||
$order_helper = new OrderQueryHelper();
|
||||
|
||||
$subqueries = array(
|
||||
new PostQuery( $wpdb, new QueryBuilder( $limit_helper, $order_helper ) ),
|
||||
);
|
||||
if ( defined( 'WPML_ST_VERSION' ) && get_option( 'wpml-package-translation-db-updates-run' ) ) {
|
||||
$subqueries[] = new PackageQuery(
|
||||
$wpdb,
|
||||
new QueryBuilder( $limit_helper, $order_helper )
|
||||
);
|
||||
$subqueries[] = new StringQuery(
|
||||
$wpdb,
|
||||
new QueryBuilder( $limit_helper, $order_helper )
|
||||
);
|
||||
$subqueries[] = new StringsBatchQuery(
|
||||
$wpdb,
|
||||
new QueryBuilder( $limit_helper, $order_helper )
|
||||
);
|
||||
}
|
||||
|
||||
$repository = new WPML_TM_Jobs_Repository(
|
||||
$wpdb,
|
||||
new CompositeQuery(
|
||||
$subqueries,
|
||||
$limit_helper,
|
||||
$order_helper
|
||||
),
|
||||
new WPML_TM_Job_Elements_Repository( $wpdb )
|
||||
);
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an instance of the class.
|
||||
*
|
||||
* @return WPML_TM_ATE_Job_Repository
|
||||
*/
|
||||
function wpml_tm_get_ate_jobs_repository() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
return new WPML_TM_ATE_Job_Repository(
|
||||
wpml_tm_get_jobs_repository()
|
||||
);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WPML\TM\ATE\JobRecords
|
||||
*/
|
||||
function wpml_tm_get_ate_job_records() {
|
||||
global $wpdb;
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new WPML\TM\ATE\JobRecords( $wpdb );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_Sync_Jobs
|
||||
*/
|
||||
function wpml_tm_get_tp_sync_jobs() {
|
||||
static $sync_jobs;
|
||||
|
||||
if ( ! $sync_jobs ) {
|
||||
global $wpdb, $sitepress;
|
||||
|
||||
$sync_jobs = new WPML_TP_Sync_Jobs(
|
||||
new WPML_TM_Sync_Jobs_Status( wpml_tm_get_jobs_repository(), wpml_tm_get_tp_jobs_api() ),
|
||||
new WPML_TM_Sync_Jobs_Revision( wpml_tm_get_jobs_repository(), wpml_tm_get_tp_jobs_api() ),
|
||||
new WPML_TP_Sync_Update_Job( $wpdb, $sitepress )
|
||||
);
|
||||
}
|
||||
|
||||
return $sync_jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TP_Translations_Repository
|
||||
*/
|
||||
function wpml_tm_get_tp_translations_repository() {
|
||||
static $repository;
|
||||
|
||||
if ( ! $repository ) {
|
||||
$repository = new WPML_TP_Translations_Repository(
|
||||
wpml_tm_get_tp_xliff_api(),
|
||||
wpml_tm_get_jobs_repository()
|
||||
);
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_WP_User_Query_Factory
|
||||
*/
|
||||
function wpml_tm_get_wp_user_query_factory() {
|
||||
static $wp_user_query_factory;
|
||||
|
||||
if ( ! $wp_user_query_factory ) {
|
||||
$wp_user_query_factory = new WPML_WP_User_Query_Factory();
|
||||
}
|
||||
|
||||
return $wp_user_query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_WP_User_Factory
|
||||
*/
|
||||
function wpml_tm_get_wp_user_factory() {
|
||||
static $wp_user_factory;
|
||||
|
||||
if ( ! $wp_user_factory ) {
|
||||
$wp_user_factory = new WPML_WP_User_Factory();
|
||||
}
|
||||
|
||||
return $wp_user_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TM_Email_Twig_Template_Factory
|
||||
*/
|
||||
function wpml_tm_get_email_twig_template_factory() {
|
||||
static $email_twig_template_factory;
|
||||
|
||||
if ( ! $email_twig_template_factory ) {
|
||||
$email_twig_template_factory = new WPML_TM_Email_Twig_Template_Factory();
|
||||
}
|
||||
|
||||
return $email_twig_template_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns a single instance of the class.
|
||||
*
|
||||
* @return \WPML_TM_AMS_ATE_Factories
|
||||
*/
|
||||
function wpml_tm_ams_ate_factories() {
|
||||
static $tm_ams_ate_factories;
|
||||
|
||||
if ( ! $tm_ams_ate_factories ) {
|
||||
$tm_ams_ate_factories = new WPML_TM_AMS_ATE_Factories();
|
||||
}
|
||||
|
||||
return $tm_ams_ate_factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Auryn\InjectionException
|
||||
*/
|
||||
function wpml_tm_get_ams_ate_console_url() {
|
||||
/** @var WPML_TM_Admin_Sections $admin_sections */
|
||||
$admin_sections = WPML\Container\make( 'WPML_TM_Admin_Sections' );
|
||||
|
||||
return $admin_sections->get_item_url( WPML_TM_AMS_ATE_Console_Section::SLUG );
|
||||
}
|
||||
|
||||
function wpml_tm_ate_ams_log( WPML\TM\ATE\Log\Entry $entry ) {
|
||||
make( WPML\TM\ATE\Log\Storage::class )->add( $entry );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $original
|
||||
* @param string $translation
|
||||
* @param bool $finished_state
|
||||
*
|
||||
* @return WPML_TM_Translated_Field
|
||||
*/
|
||||
function wpml_tm_create_translated_field( $original, $translation, $finished_state ) {
|
||||
return new WPML_TM_Translated_Field( $original, $translation, $finished_state );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
* @param \WP_Post $post
|
||||
* @param bool $force_set_status
|
||||
*/
|
||||
function wpml_tm_save_post( $post_id, $post, $force_set_status = false ) {
|
||||
global $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
if ( false === $force_set_status && get_post_meta( $post_id, '_icl_lang_duplicate_of', true ) ) {
|
||||
$force_set_status = ICL_TM_DUPLICATE;
|
||||
}
|
||||
|
||||
$action_helper = new WPML_TM_Action_Helper();
|
||||
$blog_translators = wpml_tm_load_blog_translators();
|
||||
$tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$save_post_action = new WPML_TM_Post_Actions( $action_helper, $blog_translators, $tm_records );
|
||||
if ( 'revision' === $post->post_type || 'auto-draft' === $post->post_status || isset( $_POST['autosave'] ) ) {
|
||||
return;
|
||||
}
|
||||
$save_post_action->save_post_actions( $post_id, $post, $force_set_status );
|
||||
}
|
||||
|
||||
add_action( 'wpml_tm_save_post', 'wpml_tm_save_post', 10, 3 );
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Registers scripts so that they can be reused throughout WPML plugins
|
||||
*/
|
||||
function wpml_tm_register_js_scripts() {
|
||||
wp_register_script(
|
||||
'wpml-tm-editor-templates',
|
||||
WPML_TM_URL . '/res/js/translation-editor/templates.js',
|
||||
array(),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'wpml-tm-editor-job',
|
||||
WPML_TM_URL . '/res/js/translation-editor/wpml-tm-editor-job.js',
|
||||
array( 'underscore', 'backbone' ),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
$scripts = array(
|
||||
'wpml-tm-editor-job-field-view',
|
||||
'wpml-tm-editor-job-basic-field-view',
|
||||
'wpml-tm-editor-job-single-line-field-view',
|
||||
'wpml-tm-editor-job-textarea-field-view',
|
||||
'wpml-tm-editor-job-wysiwyg-field-view',
|
||||
'wpml-tm-editor-field-view-factory',
|
||||
'wpml-tm-editor-section-view',
|
||||
'wpml-tm-editor-group-view',
|
||||
'wpml-tm-editor-image-view',
|
||||
'wpml-tm-editor-main-view',
|
||||
'wpml-tm-editor-header-view',
|
||||
'wpml-tm-editor-note-view',
|
||||
'wpml-tm-editor-footer-view',
|
||||
'wpml-tm-editor-languages-view',
|
||||
'wpml-tm-editor-copy-all-dialog',
|
||||
'wpml-tm-editor-edit-independently-dialog',
|
||||
'wpml-tm-editor-translation-memory',
|
||||
);
|
||||
|
||||
$additional_requirements = array(
|
||||
'wpml-tm-editor-footer-view' => array( 'wpml-tm-progressbar' ),
|
||||
);
|
||||
|
||||
foreach ( $scripts as $script ) {
|
||||
wp_register_script(
|
||||
$script,
|
||||
WPML_TM_URL . '/res/js/translation-editor/' . $script . '.js',
|
||||
array_merge(
|
||||
array( 'wpml-tm-editor-job' ),
|
||||
isset( $additional_requirements[ $script ] ) ? $additional_requirements[ $script ] : array()
|
||||
),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
'wpml-tm-editor-scripts',
|
||||
WPML_TM_URL . '/res/js/translation-editor/translation-editor.js',
|
||||
array_merge( array( 'jquery', 'jquery-ui-dialog', 'wpml-tm-editor-templates', 'wpml-tm-editor-job' ), $scripts ),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'wpml-tp-polling-box-populate',
|
||||
WPML_TM_URL . '/res/js/tp-polling/box-populate.js',
|
||||
array( 'jquery' ),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'wpml-tp-polling',
|
||||
WPML_TM_URL . '/res/js/tp-polling/poll-for-translations.js',
|
||||
array( 'wpml-tp-polling-box-populate' ),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'wpml-tm-mcs',
|
||||
WPML_TM_URL . '/res/js/mcs/wpml-tm-mcs.js',
|
||||
array( 'wpml-tp-polling' ),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
wp_register_script(
|
||||
'wpml-tm-mcs-translate-link-targets',
|
||||
WPML_TM_URL . '/res/js/mcs/wpml-tm-mcs-translate-link-targets.js',
|
||||
array(),
|
||||
WPML_TM_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_action( 'admin_enqueue_scripts', 'wpml_tm_register_js_scripts' );
|
||||
} else {
|
||||
add_action( 'wp_enqueue_scripts', 'wpml_tm_register_js_scripts' );
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Blog_Translators {
|
||||
|
||||
/** @var WPML_TM_Records $tm_records */
|
||||
private $tm_records;
|
||||
|
||||
/**
|
||||
* @var SitePress;
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/** @var WPML_Translator_Records $translator_records */
|
||||
private $translator_records;
|
||||
|
||||
/** @var WPML_Cache_Factory */
|
||||
private $cache_factory;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
* @param WPML_TM_Records $tm_records
|
||||
* @param WPML_Translator_Records $translator_records
|
||||
* @param WPML_Cache_Factory $cache_factory
|
||||
*/
|
||||
public function __construct(
|
||||
SitePress $sitepress,
|
||||
WPML_TM_Records $tm_records,
|
||||
WPML_Translator_Records $translator_records,
|
||||
WPML_Cache_Factory $cache_factory
|
||||
) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->tm_records = $tm_records;
|
||||
$this->translator_records = $translator_records;
|
||||
$this->cache_factory = $cache_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns true if the site has translators.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_translators() {
|
||||
$cache = $this->cache_factory->get( 'WPML_TM_Blog_Translators::has_translators' );
|
||||
|
||||
return $cache->execute_and_cache(
|
||||
'has-translators',
|
||||
function () {
|
||||
return $this->translator_records->has_users_with_capability();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_blog_translators( $args = array() ) {
|
||||
$from = isset( $args['from'] ) ? $args['from'] : false;
|
||||
$to = isset( $args['to'] ) ? $args['to'] : false;
|
||||
|
||||
$all_translators = $this->get_raw_blog_translators();
|
||||
$translators = array();
|
||||
|
||||
foreach ( $all_translators as $key => $translator ) {
|
||||
if ( ! $from || ! $to ) {
|
||||
$translators[] = isset( $translator->data ) ? $translator->data : $translator;
|
||||
} elseif ( $this->translator_has_language_pair( $translator->ID, $from, $to ) ) {
|
||||
$translators[] = isset( $translator->data ) ? $translator->data : $translator;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'blog_translators', $translators, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $translator_id
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function translator_has_language_pair( $translator_id, $from, $to ) {
|
||||
$language_pairs = $this->get_language_pairs( $translator_id );
|
||||
|
||||
if ( isset( $language_pairs[ $from ][ $to ] ) && (bool) $language_pairs[ $from ][ $to ] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_raw_blog_translators() {
|
||||
$cache = $this->cache_factory->get( 'WPML_TM_Blog_Translators::get_raw_blog_translators' );
|
||||
|
||||
return $cache->execute_and_cache(
|
||||
'has-translators',
|
||||
function () {
|
||||
return $this->translator_records->get_users_with_capability();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $user_id
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_translator( $user_id, $args = array() ) {
|
||||
$defaults = [
|
||||
'lang_from' => null,
|
||||
'lang_to' => null,
|
||||
'job_id' => null,
|
||||
'admin_override' => true,
|
||||
];
|
||||
$args = array_merge( $defaults, $args );
|
||||
|
||||
$lang_from = $args['lang_from'];
|
||||
$lang_to = $args['lang_to'];
|
||||
$job_id = $args['job_id'];
|
||||
$admin_override = $args['admin_override'];
|
||||
|
||||
$is_translator = $this->sitepress->get_wp_api()
|
||||
->user_can( $user_id, 'translate' );
|
||||
// check if user is administrator and return true if he is
|
||||
if ( $admin_override && $this->sitepress->get_wp_api()
|
||||
->user_can( $user_id, 'manage_options' )
|
||||
) {
|
||||
$is_translator = true;
|
||||
do_action( 'wpml_tm_ate_enable_subscription', $user_id );
|
||||
} else {
|
||||
if ( $lang_from && $lang_to ) {
|
||||
$user_language_pairs = $this->get_language_pairs( $user_id );
|
||||
if ( ! empty( $user_language_pairs ) ) {
|
||||
foreach ( $user_language_pairs as $user_lang_from => $user_lang_to ) {
|
||||
if ( array_key_exists( $lang_to, $user_lang_to ) ) {
|
||||
$is_translator = true;
|
||||
break;
|
||||
} else {
|
||||
$is_translator = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$is_translator = false;
|
||||
}
|
||||
}
|
||||
if ( $job_id ) {
|
||||
$job_record = $this->tm_records->icl_translate_job_by_job_id( $job_id );
|
||||
$translator_id = in_array(
|
||||
$job_record->service(),
|
||||
array(
|
||||
'local',
|
||||
0,
|
||||
)
|
||||
) ? $job_record->translator_id() : - 1;
|
||||
$is_translator = $translator_id == $user_id
|
||||
|| ( $is_translator && empty( $translator_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_override_is_translator', $is_translator, $user_id, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_language_pairs( $user_id ) {
|
||||
|
||||
return $this->sitepress->get_wp_api()
|
||||
->get_user_meta(
|
||||
$user_id,
|
||||
$this->sitepress->wpdb()->prefix . 'language_pairs',
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Batch_Factory {
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return WPML_Translation_Batch
|
||||
*/
|
||||
public function create( $id ) {
|
||||
global $sitepress;
|
||||
|
||||
$wpdb = $sitepress->get_wpdb();
|
||||
return new WPML_Translation_Batch( $wpdb, $id );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Represents a helper class for building the SQL statement which retrieves the job,
|
||||
* as well as for converting this collection to specific implementations of \WPML_Element_Translation_Job.
|
||||
*
|
||||
* @package WPML\TM
|
||||
*/
|
||||
class WPML_Abstract_Job_Collection {
|
||||
/**
|
||||
* Instance of \wpdb.
|
||||
*
|
||||
* @var \wpdb $wpdb
|
||||
*/
|
||||
public $wpdb;
|
||||
|
||||
/**
|
||||
* Instance of \SitePress.
|
||||
*
|
||||
* @var \SitePress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* WPML_Abstract_Job_Collection constructor.
|
||||
*
|
||||
* @param WPDB $wpdb An instance of \wpdb.
|
||||
*/
|
||||
public function __construct( WPDB $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
global $sitepress;
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the (INNER) JOIN clause of the query.
|
||||
*
|
||||
* @param bool $single It should only return the last job revision.
|
||||
* @param string $icl_translate_alias The alias for `{$this->wpdb->prefix}icl_translate_job`.
|
||||
* @param string $icl_translations_translated_alias The alias for translated documents in `{$this->wpdb->prefix}icl_translations`.
|
||||
* @param string $icl_translations_original_alias The alias for original documents in `{$this->wpdb->prefix}icl_translations`.
|
||||
* @param string $icl_translation_status_alias The alias for `{$this->wpdb->prefix}icl_translation_status`.
|
||||
* @param string $icl_translate_job_alias The alias for `{$this->wpdb->prefix}icl_translate_job`.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_table_join(
|
||||
$single = false,
|
||||
$icl_translate_alias = 'iclt',
|
||||
$icl_translations_translated_alias = 't',
|
||||
$icl_translations_original_alias = 'ito',
|
||||
$icl_translation_status_alias = 's',
|
||||
$icl_translate_job_alias = 'j'
|
||||
) {
|
||||
$wpdb = &$this->wpdb;
|
||||
|
||||
$max_rev_snippet = '';
|
||||
if ( true !== $single ) {
|
||||
$max_rev_snippet = "JOIN (SELECT rid, MAX(job_id) job_id FROM {$wpdb->prefix}icl_translate_job GROUP BY rid ) jobmax
|
||||
ON ( {$icl_translate_job_alias}.revision IS NULL
|
||||
AND {$icl_translate_job_alias}.rid = jobmax.rid)
|
||||
OR ( {$icl_translate_job_alias}.job_id = jobmax.job_id
|
||||
AND {$icl_translate_job_alias}.translated = 1)";
|
||||
}
|
||||
|
||||
return "{$wpdb->prefix}icl_translate_job {$icl_translate_job_alias}
|
||||
JOIN {$wpdb->prefix}icl_translation_status {$icl_translation_status_alias}
|
||||
ON {$icl_translate_job_alias}.rid = {$icl_translation_status_alias}.rid
|
||||
JOIN {$wpdb->prefix}icl_translations {$icl_translations_translated_alias}
|
||||
ON {$icl_translation_status_alias}.translation_id = {$icl_translations_translated_alias}.translation_id
|
||||
JOIN {$wpdb->prefix}icl_translate {$icl_translate_alias}
|
||||
ON {$icl_translate_alias}.job_id = {$icl_translate_job_alias}.job_id
|
||||
JOIN {$wpdb->prefix}icl_translations {$icl_translations_original_alias}
|
||||
ON {$icl_translations_original_alias}.element_id = {$icl_translate_alias}.field_data
|
||||
AND {$icl_translations_original_alias}.trid = {$icl_translations_translated_alias}.trid
|
||||
{$max_rev_snippet}";
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets the LEFT JOIN clause of the query.
|
||||
*
|
||||
* @param string $icl_translations_original_alias The alias for original documents in `{$this->wpdb->prefix}icl_translations`.
|
||||
* @param string $posts_alias The alias for `{$this->wpdb->prefix}posts`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function left_join_post( $icl_translations_original_alias = 'ito', $posts_alias = 'p' ) {
|
||||
|
||||
$join = "LEFT JOIN {$this->wpdb->prefix}posts {$posts_alias}
|
||||
ON {$icl_translations_original_alias}.element_id = {$posts_alias}.ID
|
||||
AND {$icl_translations_original_alias}.element_type = CONCAT('post_', {$posts_alias}.post_type)";
|
||||
$select = "SUBSTRING_INDEX({$icl_translations_original_alias}.element_type, '_', 1 ) as element_type_prefix";
|
||||
|
||||
return array( $select, $join );
|
||||
}
|
||||
|
||||
/**
|
||||
* It converts an array of \stdClass jobs into an array of \WPML_Element_Translation_Job instances.
|
||||
*
|
||||
* @param array $jobs The array of \stdClass jobs.
|
||||
*
|
||||
* @return \WPML_Element_Translation_Job[]|\WPML_Post_Translation_Job[]|\WPML_String_Translation_Job[]|\WPML_External_Translation_Job[]
|
||||
*/
|
||||
protected function plain_objects_to_job_instances( $jobs ) {
|
||||
foreach ( $jobs as $key => $job ) {
|
||||
if ( ! is_object( $job ) || ! isset( $job->element_type_prefix ) || ! isset( $job->job_id ) ) {
|
||||
unset( $jobs[ $key ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'post' === $job->element_type_prefix ) {
|
||||
$post_translation_job = new WPML_Post_Translation_Job( $job->job_id, $job->batch_id );
|
||||
if ( $post_translation_job->is_translatable_post_type() ) {
|
||||
$jobs[ $key ] = $post_translation_job;
|
||||
} else {
|
||||
unset( $jobs[ $key ] );
|
||||
}
|
||||
} elseif ( 'string' === $job->element_type_prefix ) {
|
||||
$jobs[ $key ] = new WPML_String_Translation_Job( $job->job_id );
|
||||
} else {
|
||||
$jobs[ $key ] = new WPML_External_Translation_Job( $job->job_id, $job->batch_id );
|
||||
}
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional arguments to filter the results.
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. An array of arguments.
|
||||
*
|
||||
* @type int translator_id
|
||||
* @type int status
|
||||
* @type int status__not
|
||||
* @type bool include_unassigned
|
||||
* @type int limit_no
|
||||
* @type array language_pairs
|
||||
* @type string service
|
||||
* @type string from
|
||||
* @type string to
|
||||
* @type string type
|
||||
* @type bool overdue
|
||||
* @type string title
|
||||
* }
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function build_where_clause( array $args ) {
|
||||
$defaults_args = array(
|
||||
'translator_id' => 0,
|
||||
'status' => false,
|
||||
'status__not' => false,
|
||||
'include_unassigned' => false,
|
||||
'language_pairs' => array(),
|
||||
'service' => 0,
|
||||
'from' => null,
|
||||
'to' => null,
|
||||
'type' => null,
|
||||
'overdue' => false,
|
||||
'title' => null,
|
||||
);
|
||||
|
||||
$args = array_merge( $defaults_args, $args );
|
||||
|
||||
$translator_id = $args['translator_id'];
|
||||
$status = $args['status'];
|
||||
$status__not = $args['status__not'];
|
||||
$include_unassigned = $args['include_unassigned'];
|
||||
$language_pairs = $args['language_pairs'];
|
||||
$service = $args['service'];
|
||||
$from = $args['from'];
|
||||
$to = $args['to'];
|
||||
$type = $args['type'];
|
||||
$overdue = $args['overdue'];
|
||||
$title = $args['title'];
|
||||
|
||||
$where = ' s.status > ' . ICL_TM_NOT_TRANSLATED;
|
||||
|
||||
if ( $status ) {
|
||||
$where .= $this->wpdb->prepare( ' AND s.status = %d', (int) $status );
|
||||
}
|
||||
if ( ICL_TM_DUPLICATE !== $status ) {
|
||||
$where .= $this->wpdb->prepare( ' AND s.status <> %d ', ICL_TM_DUPLICATE );
|
||||
}
|
||||
if ( false !== $status__not ) {
|
||||
$where .= $this->wpdb->prepare( ' AND s.status <> %d ', $status__not );
|
||||
}
|
||||
if ( $from ) {
|
||||
$where .= $this->wpdb->prepare( ' AND t.source_language_code = %s ', $from );
|
||||
}
|
||||
if ( $to ) {
|
||||
$where .= $this->wpdb->prepare( ' AND t.language_code = %s ', $to );
|
||||
}
|
||||
if ( $title ) {
|
||||
$where .= $this->wpdb->prepare( ' AND p.post_title LIKE %s ', '%' . $title . '%' );
|
||||
}
|
||||
|
||||
if ( '' !== $translator_id ) {
|
||||
if ( ! is_numeric( $translator_id ) ) {
|
||||
$_exp = explode( '-', $translator_id );
|
||||
$service = isset( $_exp[1] ) ? implode( '-', array_slice( $_exp, 1 ) ) : 'local';
|
||||
$translator_id = isset( $_exp[2] ) ? $_exp[2] : false;
|
||||
} elseif ( ! $service && ( ! isset( $args['any_translation_service'] ) || ! $args['any_translation_service'] ) ) {
|
||||
$service = 'local';
|
||||
}
|
||||
|
||||
$language_pairs = empty( $to ) || empty( $from ) ?
|
||||
get_user_meta( $translator_id, $this->wpdb->prefix . 'language_pairs', true )
|
||||
: $language_pairs;
|
||||
|
||||
$translator_id_query_parts = array();
|
||||
if ( 0 !== (int) $translator_id ) {
|
||||
$translator_id_query_parts[] = $this->wpdb->prepare( 'j.translator_id = %d', $translator_id );
|
||||
if ( $include_unassigned ) {
|
||||
$translator_id_query_parts[] = ' j.translator_id = 0 OR j.translator_id IS NULL ';
|
||||
}
|
||||
if ( true === (bool) $translator_id_query_parts ) {
|
||||
$where .= ' AND (' . join( ' OR ', $translator_id_query_parts ) . ') ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$where .= ! empty( $service ) ? $this->wpdb->prepare( ' AND s.translation_service=%s ', $service ) : '';
|
||||
|
||||
if ( $this->sitepress ) {
|
||||
$post_types = array_keys( $this->sitepress->get_translatable_documents() );
|
||||
if ( $post_types ) {
|
||||
$where .= ' AND (p.post_type IS NULL OR p.post_type IN (' . wpml_prepare_in( $post_types, '%s' ) . ' )) ';
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $from ) && false !== (bool) $language_pairs && is_array( $language_pairs ) && $translator_id ) {
|
||||
/**
|
||||
* Only if we filter by translator, make sure to use just the 'from' languages that apply
|
||||
* in no translator_id, omit condition and all will be pulled.
|
||||
*/
|
||||
if ( ! empty( $to ) ) {
|
||||
/**
|
||||
* Get 'from' languages corresponding to $to (to $translator_id).
|
||||
*/
|
||||
$from_languages = array();
|
||||
foreach ( $language_pairs as $fl => $tls ) {
|
||||
if ( isset( $tls[ $to ] ) ) {
|
||||
$from_languages[] = $fl;
|
||||
}
|
||||
}
|
||||
if ( $from_languages ) {
|
||||
$where .= ' AND t.source_language_code IN (' . wpml_prepare_in( $from_languages ) . ') ';
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* All to all case.
|
||||
* Get all possible combinations for $translator_id.
|
||||
*/
|
||||
$from_languages = array_keys( $language_pairs );
|
||||
$where_conditions = array();
|
||||
foreach ( $from_languages as $fl ) {
|
||||
$prepared_in_values = wpml_prepare_in( array_keys( $language_pairs[ $fl ] ) );
|
||||
$where_conditions[] = ' (' . $this->wpdb->prepare( 't.source_language_code = %s', $fl ) . ' AND t.language_code IN (' . $prepared_in_values . ')) ';
|
||||
}
|
||||
|
||||
if ( ! empty( $where_conditions ) ) {
|
||||
$where .= ' AND ( ' . join( ' OR ', $where_conditions ) . ') ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $to ) && $translator_id && ! empty( $from ) && isset( $language_pairs[ $from ] ) && false !== (bool) $language_pairs[ $from ] ) {
|
||||
/**
|
||||
* Only if we filter by translator, make sure to use just the 'from' languages that apply
|
||||
* in no translator_id, omit condition and all will be pulled.
|
||||
* Get languages the user can translate into from $from.
|
||||
*/
|
||||
$where .= ' AND t.language_code IN(' . wpml_prepare_in( array_keys( $language_pairs[ $from ] ) ) . ')';
|
||||
}
|
||||
|
||||
$where .= ! empty( $type ) ? $this->wpdb->prepare( ' AND ito.element_type=%s ', $type ) : '';
|
||||
|
||||
if ( $overdue ) {
|
||||
$today_date = date( 'Y-m-d' );
|
||||
|
||||
$statusCond = wpml_prepare_in( [ ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS ], '%d' );
|
||||
$where .= $this->wpdb->prepare( " AND j.deadline_date IS NOT NULL AND s.status IN ({$statusCond}) AND j.deadline_date < %s AND j.deadline_date <> '0000-00-00 00:00:00'", $today_date );
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
<?php
|
||||
|
||||
class WPML_Save_Translation_Data_Action extends WPML_Translation_Job_Helper_With_API {
|
||||
|
||||
/** @var WPML_TM_Records $tm_records */
|
||||
private $tm_records;
|
||||
|
||||
/** @var array $data */
|
||||
private $data;
|
||||
|
||||
private $redirect_target = false;
|
||||
private $translate_link_targets_in_posts;
|
||||
private $translate_link_targets_in_strings;
|
||||
|
||||
public function __construct( $data, $tm_records ) {
|
||||
global $wpdb, $ICL_Pro_Translation, $sitepress;
|
||||
parent::__construct();
|
||||
$this->data = $data;
|
||||
$this->tm_records = $tm_records;
|
||||
$translate_link_targets_global_state = new WPML_Translate_Link_Target_Global_State( $sitepress );
|
||||
$this->translate_link_targets_in_posts = new WPML_Translate_Link_Targets_In_Posts( $translate_link_targets_global_state, $wpdb, $ICL_Pro_Translation );
|
||||
$this->translate_link_targets_in_strings = new WPML_Translate_Link_Targets_In_Strings( $translate_link_targets_global_state, $wpdb, new WPML_WP_API(), $ICL_Pro_Translation );
|
||||
|
||||
}
|
||||
|
||||
function save_translation() {
|
||||
global $wpdb, $sitepress, $iclTranslationManagement, $wpml_post_translations;
|
||||
|
||||
$new_post_id = false;
|
||||
$is_incomplete = false;
|
||||
$data = $this->data;
|
||||
/** @var stdClass $job */
|
||||
$job = ! empty( $data['job_id'] ) ? $this->get_translation_job( $data['job_id'], true ) : null;
|
||||
$needs_second_update = $job && $job->needs_update ? 1 : 0;
|
||||
$original_post = null;
|
||||
$element_type_prefix = null;
|
||||
if ( is_object( $job ) ) {
|
||||
$element_type_prefix = $iclTranslationManagement->get_element_type_prefix_from_job( $job );
|
||||
$original_post = $iclTranslationManagement->get_post( $job->original_doc_id, $element_type_prefix );
|
||||
}
|
||||
|
||||
$is_external = apply_filters( 'wpml_is_external', false, $element_type_prefix );
|
||||
$data_to_validate = array(
|
||||
'original_post' => $original_post,
|
||||
'type_prefix' => $element_type_prefix,
|
||||
'data' => $data,
|
||||
'is_external' => $is_external,
|
||||
);
|
||||
|
||||
$validation_results = $this->get_validation_results( $job, $data_to_validate );
|
||||
|
||||
if ( ! $validation_results['is_valid'] ) {
|
||||
$this->handle_failed_validation( $validation_results, $data_to_validate );
|
||||
$res = false;
|
||||
} else {
|
||||
foreach ( $data['fields'] as $fieldname => $field ) {
|
||||
if ( substr( $fieldname, 0, 6 ) === 'field-' ) {
|
||||
$field = apply_filters( 'wpml_tm_save_translation_cf', $field, $fieldname, $data );
|
||||
}
|
||||
$this->save_translation_field( $field['tid'], $field );
|
||||
if ( ! isset( $field['finished'] ) || ! $field['finished'] ) {
|
||||
$is_incomplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
$icl_translate_job = $this->tm_records->icl_translate_job_by_job_id( $data['job_id'] );
|
||||
$rid = $icl_translate_job->rid();
|
||||
$translation_status = $this->tm_records->icl_translation_status_by_rid( $rid );
|
||||
$translation_id = $translation_status->translation_id();
|
||||
if ( ( $is_incomplete === true || empty( $data['complete'] ) ) && empty( $data['resign'] ) ) {
|
||||
$iclTranslationManagement->update_translation_status(
|
||||
array(
|
||||
'translation_id' => $translation_id,
|
||||
'status' => ICL_TM_IN_PROGRESS,
|
||||
)
|
||||
);
|
||||
$icl_translate_job->update( array( 'translated' => 0 ) );
|
||||
|
||||
self::notify_job_in_progress( $element_type_prefix, $job );
|
||||
}
|
||||
|
||||
$element_id = $translation_status->element_id();
|
||||
delete_post_meta( $element_id, '_icl_lang_duplicate_of' );
|
||||
|
||||
if ( ! empty( $data['complete'] ) && ! $is_incomplete ) {
|
||||
$icl_translate_job->update(
|
||||
array(
|
||||
'translated' => 1,
|
||||
'completed_date' => date( 'Y-m-d H:i:s' ),
|
||||
)
|
||||
);
|
||||
$job = $this->get_translation_job( $data['job_id'], true );
|
||||
|
||||
if ( $is_external ) {
|
||||
self::save_external( $element_type_prefix, $job, [ $this, 'decode_field_data' ] );
|
||||
} else {
|
||||
if ( $element_id ) {
|
||||
$postarr['ID'] = $_POST['post_ID'] = $element_id;
|
||||
}
|
||||
|
||||
$postarr['post_status'] = ! $sitepress->get_setting( 'translated_document_status' ) ? 'draft' : $original_post->post_status;
|
||||
|
||||
foreach ( $job->elements as $field ) {
|
||||
switch ( $field->field_type ) {
|
||||
case 'title':
|
||||
$postarr['post_title'] = $this->decode_field_data( $field->field_data_translated, $field->field_format );
|
||||
break;
|
||||
case 'body':
|
||||
$postarr['post_content'] = $this->decode_field_data(
|
||||
$field->field_data_translated,
|
||||
$field->field_format
|
||||
);
|
||||
break;
|
||||
case 'excerpt':
|
||||
$postarr['post_excerpt'] = $this->decode_field_data( $field->field_data_translated, $field->field_format );
|
||||
break;
|
||||
case 'URL':
|
||||
$postarr['post_name'] = $this->decode_field_data( $field->field_data_translated, $field->field_format );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$postarr['post_author'] = $original_post->post_author;
|
||||
$postarr['post_type'] = $original_post->post_type;
|
||||
|
||||
if ( $sitepress->get_setting( 'sync_comment_status' ) ) {
|
||||
$postarr['comment_status'] = $original_post->comment_status;
|
||||
}
|
||||
if ( $sitepress->get_setting( 'sync_ping_status' ) ) {
|
||||
$postarr['ping_status'] = $original_post->ping_status;
|
||||
}
|
||||
if ( $sitepress->get_setting( 'sync_page_ordering' ) ) {
|
||||
$postarr['menu_order'] = $original_post->menu_order;
|
||||
}
|
||||
if ( $sitepress->get_setting( 'sync_private_flag' ) && $original_post->post_status == 'private' ) {
|
||||
$postarr['post_status'] = 'private';
|
||||
}
|
||||
if ( $sitepress->get_setting( 'sync_password' ) && $original_post->post_password ) {
|
||||
$postarr['post_password'] = $original_post->post_password;
|
||||
}
|
||||
if ( $sitepress->get_setting( 'sync_post_date' ) ) {
|
||||
$postarr['post_date'] = $original_post->post_date;
|
||||
}
|
||||
|
||||
if ( $original_post->post_parent ) {
|
||||
$parent_id = $wpml_post_translations->element_id_in( $original_post->post_parent, $job->language_code );
|
||||
}
|
||||
|
||||
if ( isset( $parent_id ) && $sitepress->get_setting( 'sync_page_parent' ) ) {
|
||||
$_POST['post_parent'] = $postarr['post_parent'] = $parent_id;
|
||||
$_POST['parent_id'] = $postarr['parent_id'] = $parent_id;
|
||||
}
|
||||
|
||||
$_POST['trid'] = $translation_status->trid();
|
||||
$_POST['lang'] = $job->language_code;
|
||||
$_POST['skip_sitepress_actions'] = true;
|
||||
$_POST['needs_second_update'] = $needs_second_update;
|
||||
|
||||
/* @deprecated Use `wpml_pre_save_pro_translation` instead */
|
||||
$postarr = apply_filters( 'icl_pre_save_pro_translation', $postarr );
|
||||
|
||||
$postarr = apply_filters( 'wpml_pre_save_pro_translation', $postarr, $job );
|
||||
|
||||
// it's an update and user do not want to translate urls so do not change the url
|
||||
if ( $element_id ) {
|
||||
if ( $sitepress->get_setting( 'translated_document_page_url' ) !== 'translate' ) {
|
||||
$postarr['post_name'] = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT post_name
|
||||
FROM {$wpdb->posts}
|
||||
WHERE ID=%d
|
||||
LIMIT 1",
|
||||
$element_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$existing_post = get_post( $element_id );
|
||||
$postarr['post_date'] = $existing_post->post_date;
|
||||
$postarr['post_date_gmt'] = $existing_post->post_date_gmt;
|
||||
}
|
||||
|
||||
$new_post_id = wpml_get_create_post_helper()->insert_post( $postarr, $job->language_code );
|
||||
|
||||
icl_cache_clear( $postarr['post_type'] . 's_per_language' ); // clear post counter per language in cache
|
||||
|
||||
// set taxonomies for users with limited caps
|
||||
if ( ! current_user_can( 'manage-categories' ) && ! empty( $postarr['tax_input'] ) ) {
|
||||
foreach ( $postarr['tax_input'] as $taxonomy => $terms ) {
|
||||
wp_set_post_terms( $new_post_id, $terms, $taxonomy, false ); // true to append to existing tags | false to replace existing tags
|
||||
}
|
||||
}
|
||||
|
||||
$data['fields'] = apply_filters( 'wpml_tm_job_fields', $data['fields'], $job );
|
||||
|
||||
do_action( 'icl_pro_translation_saved', $new_post_id, $data['fields'], $job );
|
||||
do_action( 'wpml_translation_job_saved', $new_post_id, $data['fields'], $job );
|
||||
|
||||
// update body translation with the links fixed
|
||||
$new_post_content = $wpdb->get_var( $wpdb->prepare( "SELECT post_content FROM {$wpdb->posts} WHERE ID=%d", $new_post_id ) );
|
||||
foreach ( $job->elements as $job_element ) {
|
||||
if ( $job_element->field_type === 'body' ) {
|
||||
$fields_data_translated = apply_filters( 'wpml_tm_job_data_post_content', $new_post_content );
|
||||
$fields_data_translated = $this->encode_field_data( $fields_data_translated );
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'icl_translate',
|
||||
array( 'field_data_translated' => $fields_data_translated ),
|
||||
array(
|
||||
'job_id' => $data['job_id'],
|
||||
'field_type' => 'body',
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sitepress->copy_custom_fields( $original_post->ID, $new_post_id );
|
||||
|
||||
// set specific custom fields
|
||||
$copied_custom_fields = array( '_top_nav_excluded', '_cms_nav_minihome' );
|
||||
foreach ( $copied_custom_fields as $ccf ) {
|
||||
$val = get_post_meta( $original_post->ID, $ccf, true );
|
||||
update_post_meta( $new_post_id, $ccf, $val );
|
||||
}
|
||||
|
||||
// sync _wp_page_template
|
||||
if ( $sitepress->get_setting( 'sync_page_template' ) ) {
|
||||
$_wp_page_template = get_post_meta( $original_post->ID, '_wp_page_template', true );
|
||||
if ( ! empty( $_wp_page_template ) ) {
|
||||
update_post_meta( $new_post_id, '_wp_page_template', $_wp_page_template );
|
||||
}
|
||||
}
|
||||
|
||||
$this->package_helper->save_job_custom_fields(
|
||||
$job,
|
||||
$new_post_id,
|
||||
\WPML\TM\Settings\Repository::getCustomFields()
|
||||
);
|
||||
|
||||
$link = get_edit_post_link( $new_post_id );
|
||||
if ( $link == '' ) {
|
||||
// the current user can't edit so just include permalink
|
||||
$link = get_permalink( $new_post_id );
|
||||
}
|
||||
|
||||
if ( ! $element_id ) {
|
||||
$wpdb->delete(
|
||||
$wpdb->prefix . 'icl_translations',
|
||||
array(
|
||||
'element_id' => $new_post_id,
|
||||
'element_type' => 'post_' . $postarr['post_type'],
|
||||
)
|
||||
);
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translations', array( 'element_id' => $new_post_id ), array( 'translation_id' => $translation_id ) );
|
||||
$user_message = __( 'Translation added: ', 'wpml-translation-management' ) . '<a href="' . $link . '">' . $postarr['post_title'] . '</a>.';
|
||||
} else {
|
||||
$user_message = __( 'Translation updated: ', 'wpml-translation-management' ) . '<a href="' . $link . '">' . $postarr['post_title'] . '</a>.';
|
||||
}
|
||||
|
||||
// set stickiness
|
||||
// is the original post a sticky post?
|
||||
$sticky_posts = get_option( 'sticky_posts' );
|
||||
$is_original_sticky = $original_post->post_type == 'post' && in_array( $original_post->ID, $sticky_posts );
|
||||
|
||||
if ( $is_original_sticky && $sitepress->get_setting( 'sync_sticky_flag' ) ) {
|
||||
stick_post( $new_post_id );
|
||||
} else {
|
||||
if ( $original_post->post_type == 'post' && ! is_null( $element_id ) ) {
|
||||
unstick_post( $new_post_id ); // just in case - if this is an update and the original post stickiness has changed since the post was sent for translation
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_message(
|
||||
array(
|
||||
'type' => 'updated',
|
||||
'text' => $user_message,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->get_tm_setting( array( 'notification', 'completed' ) ) != ICL_TM_NOTIFICATION_NONE
|
||||
&& $data['job_id']
|
||||
) {
|
||||
do_action( 'wpml_tm_complete_job_notification', $data['job_id'], ! is_null( $element_id ) );
|
||||
}
|
||||
|
||||
$iclTranslationManagement->set_page_url( $new_post_id );
|
||||
|
||||
if ( isset( $job ) && isset( $job->language_code ) && isset( $job->source_language_code ) ) {
|
||||
$this->save_terms_for_job( $data['job_id'] );
|
||||
}
|
||||
|
||||
// sync post format
|
||||
// Must be after save terms otherwise it gets lost.
|
||||
if ( $sitepress->get_setting( 'sync_post_format' ) ) {
|
||||
$_wp_post_format = get_post_format( $original_post->ID );
|
||||
set_post_format( $new_post_id, $_wp_post_format );
|
||||
}
|
||||
|
||||
do_action( 'icl_pro_translation_completed', $new_post_id, $data['fields'], $job );
|
||||
do_action( 'wpml_pro_translation_completed', $new_post_id, $data['fields'], $job );
|
||||
|
||||
$translation_status->update(
|
||||
array(
|
||||
'status' => ICL_TM_COMPLETE,
|
||||
'needs_update' => $needs_second_update,
|
||||
)
|
||||
);
|
||||
|
||||
$this->translate_link_targets_in_posts->new_content();
|
||||
$this->translate_link_targets_in_strings->new_content();
|
||||
|
||||
if ( ! defined( 'REST_REQUEST' ) && ! defined( 'XMLRPC_REQUEST' ) && ! defined( 'DOING_AJAX' ) && ! isset( $_POST['xliff_upload'] ) ) {
|
||||
$action_type = is_null( $element_id ) ? 'added' : 'updated';
|
||||
$element_id = is_null( $element_id ) ? $new_post_id : $element_id;
|
||||
$this->redirect_target = admin_url( sprintf( 'admin.php?page=%s&%s=%d&element_type=%s', WPML_TM_FOLDER . '/menu/translations-queue.php', $action_type, $element_id, $element_type_prefix ) );
|
||||
}
|
||||
} else {
|
||||
$this->add_message(
|
||||
array(
|
||||
'type' => 'updated',
|
||||
'text' => __( 'Translation (incomplete) saved.', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$res = true;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if after saving the translation no redirection is to happen or the target of the redirection
|
||||
* in case saving the data is followed by a redirect.
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
function get_redirect_target() {
|
||||
|
||||
return $this->redirect_target;
|
||||
}
|
||||
|
||||
private function save_translation_field( $tid, $field ) {
|
||||
global $wpdb;
|
||||
|
||||
$update = [];
|
||||
if ( isset( $field['data'] ) ) {
|
||||
$update['field_data_translated'] = $this->encode_field_data( $field['data'] );
|
||||
}
|
||||
$update['field_finished'] = isset( $field['finished'] ) && $field['finished'] ? 1 : 0;
|
||||
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translate', $update, array( 'tid' => $tid ) );
|
||||
}
|
||||
|
||||
private function handle_failed_validation( $validation_results, $data_to_validate ) {
|
||||
if ( isset( $validation_results['messages'] ) ) {
|
||||
$messages = (array) $validation_results['messages'];
|
||||
if ( $messages ) {
|
||||
foreach ( $messages as $message ) {
|
||||
$this->add_message(
|
||||
array(
|
||||
'type' => 'error',
|
||||
'text' => $message,
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->add_message(
|
||||
array(
|
||||
'type' => 'error',
|
||||
'text' => __( 'Submitted data is not valid.', 'wpml-translation-management' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
do_action( 'wpml_translation_validation_failed', $validation_results, $data_to_validate );
|
||||
}
|
||||
|
||||
private function get_validation_results( $job, $data_to_validate ) {
|
||||
$is_valid = true;
|
||||
$original_post = $data_to_validate['original_post'];
|
||||
$element_type_prefix = $data_to_validate['type_prefix'];
|
||||
$validation_default_results = array(
|
||||
'is_valid' => $is_valid,
|
||||
'messages' => array(),
|
||||
);
|
||||
if ( ! $job || ! $original_post || ! $element_type_prefix ) {
|
||||
$is_valid = false;
|
||||
if ( ! $job ) {
|
||||
$validation_default_results['messages'][] = __( 'Job ID is missing', 'wpml-translation-management' );
|
||||
}
|
||||
if ( ! $original_post ) {
|
||||
$validation_default_results['messages'][] = __( 'The original post cannot be retrieved', 'wpml-translation-management' );
|
||||
}
|
||||
if ( ! $element_type_prefix ) {
|
||||
$validation_default_results['messages'][] = __( 'The type of the post cannot be retrieved', 'wpml-translation-management' );
|
||||
}
|
||||
} elseif ( ! $this->tm_records->icl_translate_job_by_job_id( $job->job_id )->is_open() ) {
|
||||
$is_valid = false;
|
||||
$validation_default_results['messages'][] = __( 'This job cannot be edited anymore because a newer job for this element exists.', 'wpml-translation-management' );
|
||||
}
|
||||
$validation_default_results['is_valid'] = $is_valid;
|
||||
$validation_results = apply_filters( 'wpml_translation_validation_data', $validation_default_results, $data_to_validate );
|
||||
$validation_results = array_merge( $validation_default_results, $validation_results );
|
||||
|
||||
if ( ! $is_valid && $validation_results['is_valid'] ) {
|
||||
$validation_results['is_valid'] = $is_valid;
|
||||
}
|
||||
|
||||
return $validation_results;
|
||||
}
|
||||
|
||||
private function save_terms_for_job( $job_id ) {
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/wpml-translation-jobs-collection.class.php';
|
||||
|
||||
$job = new WPML_Post_Translation_Job( $job_id );
|
||||
$job->save_terms_to_post();
|
||||
}
|
||||
|
||||
private function add_message( $message ) {
|
||||
global $iclTranslationManagement;
|
||||
|
||||
$iclTranslationManagement->add_message( $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $element_type_prefix
|
||||
* @param object $job
|
||||
* @param callable $decoder
|
||||
*/
|
||||
private static function save_external( $element_type_prefix, $job, $decoder ) {
|
||||
do_action( 'wpml_save_external', $element_type_prefix, $job, $decoder );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $element_type_prefix
|
||||
* @param object $job
|
||||
*/
|
||||
private static function notify_job_in_progress( $element_type_prefix, $job ) {
|
||||
/**
|
||||
* The action triggered when a job is marked as in progress
|
||||
*
|
||||
* @param string $element_type_prefix
|
||||
* @param object $job
|
||||
* @since 2.10.0
|
||||
*/
|
||||
do_action( 'wpml_tm_job_in_progress', $element_type_prefix, $job );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Job_Helper_With_API extends WPML_Translation_Job_Helper {
|
||||
|
||||
/** @var WPML_Element_Translation_Package $package_helper */
|
||||
protected $package_helper;
|
||||
|
||||
function __construct() {
|
||||
$this->package_helper = new WPML_Element_Translation_Package();
|
||||
}
|
||||
|
||||
protected function get_translation_job( $job_id, $include_non_translatable_elements = false, $revisions = 0 ) {
|
||||
return wpml_tm_load_job_factory()->get_translation_job( $job_id, $include_non_translatable_elements, $revisions );
|
||||
}
|
||||
|
||||
protected function get_lang_by_rid( $rid ) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT i.language_code
|
||||
FROM {$wpdb->prefix}icl_translations i
|
||||
JOIN {$wpdb->prefix}icl_translation_status s
|
||||
ON s.translation_id = i.translation_id
|
||||
WHERE s.rid = %d
|
||||
LIMIT 1",
|
||||
$rid
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Job_Helper {
|
||||
|
||||
public function encode_field_data( $data ) {
|
||||
return base64_encode( $data );
|
||||
}
|
||||
|
||||
public function decode_field_data( $data, $format ) {
|
||||
return $this->get_core_translation_management()->decode_field_data( $data, $format );
|
||||
}
|
||||
|
||||
protected function get_tm_setting( $indexes ) {
|
||||
$core_tm = $this->get_core_translation_management();
|
||||
if ( empty( $core_tm->settings ) ) {
|
||||
$core_tm->init();
|
||||
}
|
||||
|
||||
$settings = $core_tm->get_settings();
|
||||
|
||||
foreach ( $indexes as $index ) {
|
||||
$settings = isset( $settings[ $index ] ) ? $settings[ $index ] : null;
|
||||
if ( ! isset( $settings ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TranslationManagement
|
||||
*/
|
||||
public static function get_core_translation_management() {
|
||||
/** TranslationManagement $iclTranslationManagement */
|
||||
global $iclTranslationManagement;
|
||||
|
||||
return $iclTranslationManagement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-update-translation-data-action.class.php';
|
||||
|
||||
class WPML_TM_Update_External_Translation_Data_Action extends WPML_TM_Update_Translation_Data_Action {
|
||||
|
||||
protected function populate_prev_translation( $rid, array $package ) {
|
||||
list( $prev_job_id ) = $this->get_prev_job_data( $rid );
|
||||
|
||||
$prev_translation = [];
|
||||
$prev_job = $this->get_translation_job( $prev_job_id );
|
||||
/** @var stdClass $prev_job */
|
||||
if ( isset( $prev_job->original_doc_id ) ) {
|
||||
foreach ( $prev_job->elements as $element ) {
|
||||
$prev_translation[ $element->field_type ] = new WPML_TM_Translated_Field(
|
||||
$element->field_data,
|
||||
$element->field_data_translated,
|
||||
$element->field_finished
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_tm_populate_prev_translation', $prev_translation, $package, $this->get_lang_by_rid( $rid ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-update-translation-data-action.class.php';
|
||||
|
||||
class WPML_TM_Update_Post_Translation_Data_Action extends WPML_TM_Update_Translation_Data_Action {
|
||||
|
||||
protected function populate_prev_translation( $rid, array $package ) {
|
||||
global $wpml_post_translations;
|
||||
|
||||
$prev_translation = array();
|
||||
if ( (bool) ( $lang = $this->get_lang_by_rid( $rid ) ) === true ) {
|
||||
list( $prev_job_id ) = $this->get_prev_job_data( $rid );
|
||||
$prev_job = $this->get_translation_job( $prev_job_id );
|
||||
|
||||
if ( $prev_job ) {
|
||||
foreach ( $package['contents'] as $field_name => $field ) {
|
||||
if ( array_key_exists( 'translate', $field ) && $field['translate'] ) {
|
||||
$element = $this->get_previous_element( $prev_job, $field_name );
|
||||
if ( $element ) {
|
||||
$prev_translation[ $field_name ] = new WPML_TM_Translated_Field(
|
||||
$element->field_data,
|
||||
$element->field_data_translated,
|
||||
$element->field_finished
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$translated_post_id = $wpml_post_translations->element_id_in( $package['contents']['original_id']['data'], $lang );
|
||||
if ( $translated_post_id ) {
|
||||
$package_trans = $this->package_helper->create_translation_package( $translated_post_id );
|
||||
$translated_contents = $package_trans['contents'];
|
||||
|
||||
foreach ( $package['contents'] as $field_name => $field ) {
|
||||
if ( ! array_key_exists( $field_name, $prev_translation )
|
||||
&& array_key_exists( $field_name, $translated_contents )
|
||||
&& array_key_exists( 'data', $translated_contents[ $field_name ] )
|
||||
) {
|
||||
$prev_translation[ $field_name ] = new WPML_TM_Translated_Field(
|
||||
'',
|
||||
$translated_contents[ $field_name ]['data'],
|
||||
false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$prev_translation = apply_filters( 'wpml_tm_populate_prev_translation', $prev_translation, $package, $lang );
|
||||
}
|
||||
|
||||
return $prev_translation;
|
||||
}
|
||||
|
||||
private function get_previous_element( $prev_job, $field_name ) {
|
||||
foreach ( $prev_job->elements as $element ) {
|
||||
if ( $element->field_type == $field_name ) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TM_Update_Translation_Data_Action extends WPML_Translation_Job_Helper_With_API {
|
||||
|
||||
function get_prev_job_data( $rid ) {
|
||||
global $wpdb;
|
||||
|
||||
// if we have a previous job_id for this rid mark it as the top (last) revision
|
||||
return $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT job_id, translated
|
||||
FROM {$wpdb->prefix}icl_translate_job
|
||||
WHERE rid=%d
|
||||
AND revision IS NULL
|
||||
LIMIT 1",
|
||||
$rid
|
||||
),
|
||||
ARRAY_N
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a translation job record in icl_translate_job
|
||||
*
|
||||
* @param mixed $rid
|
||||
* @param mixed $translator_id
|
||||
* @param $translation_package
|
||||
* @param array $batch_options
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
function add_translation_job( $rid, $translator_id, array $translation_package, array $batch_options ) {
|
||||
global $wpdb, $current_user;
|
||||
|
||||
$translation_status = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_translation_status WHERE rid=%d", $rid ) );
|
||||
$prev_translation = $this->get_translated_field_values( $rid, $translation_package );
|
||||
if ( ! $current_user->ID ) {
|
||||
$manager_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT manager_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d ORDER BY job_id DESC LIMIT 1",
|
||||
$rid
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$manager_id = $current_user->ID;
|
||||
}
|
||||
|
||||
$translate_job_insert_data = array(
|
||||
'rid' => $rid,
|
||||
'translator_id' => $translator_id,
|
||||
'translated' => 0,
|
||||
'manager_id' => (int) $manager_id,
|
||||
);
|
||||
|
||||
if ( isset( $batch_options['deadline_date'] ) ) {
|
||||
$translate_job_insert_data['deadline_date'] = $batch_options['deadline_date'];
|
||||
}
|
||||
|
||||
if ( isset( $translation_package['title'] ) ) {
|
||||
$translate_job_insert_data['title'] = mb_substr( $translation_package['title'], 0, 160 );
|
||||
}
|
||||
|
||||
$wpdb->insert( $wpdb->prefix . 'icl_translate_job', $translate_job_insert_data );
|
||||
$job_id = $wpdb->insert_id;
|
||||
|
||||
$this->package_helper->save_package_to_job( $translation_package, $job_id, $prev_translation );
|
||||
if ( (int) $translation_status->status !== ICL_TM_DUPLICATE ) {
|
||||
$this->fire_notification_actions( $job_id, $translation_status, $translator_id );
|
||||
}
|
||||
|
||||
return $job_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $prev_id
|
||||
* @param array $package
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function populate_prev_translation( $prev_id, array $package );
|
||||
|
||||
/**
|
||||
* @param int $rid
|
||||
* @param array $package
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_translated_field_values( $rid, array $package ) {
|
||||
global $wpdb;
|
||||
|
||||
$prev_translations = $this->populate_prev_translation( $rid, $package );
|
||||
|
||||
if ( ! $prev_translations ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// if we have a previous job_id for this rid mark it as the top (last) revision
|
||||
list( $prev_job_id, $prev_job_translated ) = $this->get_prev_job_data( $rid );
|
||||
|
||||
if ( ! is_null( $prev_job_id ) ) {
|
||||
$last_rev_prepare = $wpdb->prepare(
|
||||
"
|
||||
SELECT MAX(revision)
|
||||
FROM {$wpdb->prefix}icl_translate_job
|
||||
WHERE rid=%d
|
||||
AND ( revision IS NOT NULL OR translated = 1 )
|
||||
",
|
||||
$rid
|
||||
);
|
||||
$last_rev = $wpdb->get_var( $last_rev_prepare );
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translate_job', array( 'revision' => $last_rev + 1 ), array( 'job_id' => $prev_job_id ) );
|
||||
}
|
||||
|
||||
return $prev_translations;
|
||||
}
|
||||
|
||||
protected function fire_notification_actions( $job_id, $translation_status, $translator_id ) {
|
||||
global $wpml_translation_job_factory;
|
||||
|
||||
$job = $wpml_translation_job_factory->get_translation_job( $job_id, false, 0, true );
|
||||
if ( $job && $translation_status->translation_service === 'local' ) {
|
||||
if ( $this->get_tm_setting( array( 'notification', 'new-job' ) ) == ICL_TM_NOTIFICATION_IMMEDIATELY ) {
|
||||
if ( $job_id ) {
|
||||
if ( empty( $translator_id ) ) {
|
||||
do_action( 'wpml_tm_new_job_notification', $job );
|
||||
} else {
|
||||
do_action( 'wpml_tm_assign_job_notification', $job, $translator_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
do_action( 'wpml_added_local_translation_job', $job_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-translation-job.class.php';
|
||||
|
||||
abstract class WPML_Element_Translation_Job extends WPML_Translation_Job {
|
||||
|
||||
protected $original_del_text;
|
||||
|
||||
/** @var WPML_Translation_Job_Factory $job_factory */
|
||||
protected $job_factory;
|
||||
|
||||
private $original_doc_id = false;
|
||||
private $translation_id = false;
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
* @param null|int $batch_id
|
||||
* @param null|TranslationManagement $tm_instance
|
||||
* @param null|WPML_Translation_Job_Factory $job_factory
|
||||
*/
|
||||
function __construct( $job_id, $batch_id = null, &$tm_instance = null, &$job_factory = null ) {
|
||||
parent::__construct( $job_id, $batch_id, $tm_instance );
|
||||
$this->original_del_text = __( 'The original has been deleted!', 'sitepress' );
|
||||
if ( ! $job_factory ) {
|
||||
global $wpml_translation_job_factory;
|
||||
$job_factory = &$wpml_translation_job_factory;
|
||||
}
|
||||
$this->job_factory = $job_factory;
|
||||
}
|
||||
|
||||
function get_type() {
|
||||
return 'Post';
|
||||
}
|
||||
|
||||
function to_array() {
|
||||
$this->maybe_load_basic_data();
|
||||
$data_array = $this->basic_data_to_array( $this->basic_data );
|
||||
$data_array['id'] = $this->basic_data->job_id;
|
||||
$data_array['translation_id'] = $this->basic_data->translation_id;
|
||||
$data_array['status'] = $this->get_status();
|
||||
$data_array['translation_edit_url'] = $this->get_url();
|
||||
$data_array['original_url'] = $this->get_url( true );
|
||||
$data_array['post_title'] = esc_html( $this->get_title() );
|
||||
|
||||
return $data_array;
|
||||
}
|
||||
|
||||
function to_xliff_file() {
|
||||
$xliff = new WPML_TM_Xliff_Writer( $this->job_factory );
|
||||
|
||||
return $xliff->get_job_xliff_file( $this->get_id() );
|
||||
}
|
||||
|
||||
function get_original_element_id() {
|
||||
if ( ! $this->original_doc_id ) {
|
||||
$this->original_doc_id = $this->get_iclt_field( 'element_id', false );
|
||||
}
|
||||
|
||||
return $this->original_doc_id;
|
||||
}
|
||||
|
||||
function get_translation_id() {
|
||||
if ( ! $this->translation_id ) {
|
||||
$translation_id = $this->get_iclt_field( 'translation_id', true );
|
||||
$this->translation_id = $translation_id;
|
||||
} else {
|
||||
$translation_id = $this->translation_id;
|
||||
}
|
||||
|
||||
return $translation_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the job data in this object to the database (e.g. to a post)
|
||||
*
|
||||
* @param bool $complete whether or not to set the status
|
||||
* of the target element to complete
|
||||
*/
|
||||
public function save_to_element( $complete = false ) {
|
||||
global $wpdb, $wpml_post_translations, $wpml_term_translations;
|
||||
|
||||
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
|
||||
$save_data_action = new WPML_Save_Translation_Data_Action(
|
||||
array(
|
||||
'job_id' => $this->get_id(),
|
||||
'complete' => $complete,
|
||||
'fields' => array(),
|
||||
),
|
||||
$wpml_tm_records
|
||||
);
|
||||
$save_data_action->save_translation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function estimate_word_count() {
|
||||
$fields = $this->get_original_fields();
|
||||
$combined_string = join( ' ', $fields );
|
||||
$calculator = new WPML_TM_Word_Calculator( new WPML_PHP_Functions() );
|
||||
return $calculator->count_words( $combined_string, $this->get_source_language_code() );
|
||||
}
|
||||
|
||||
function get_original_fields() {
|
||||
global $wpdb;
|
||||
|
||||
$fields = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT field_type, field_data, field_format
|
||||
FROM {$wpdb->prefix}icl_translate
|
||||
WHERE job_id = %d
|
||||
AND field_translate = 1",
|
||||
$this->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
$res = array();
|
||||
foreach ( $fields as $field ) {
|
||||
$res[ $field->field_type ] = base64_decode( $field->field_data );
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function cancel() {
|
||||
global $wpdb;
|
||||
|
||||
$deleted = false;
|
||||
$rid_query = "SELECT rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d";
|
||||
$rid_prepare = $wpdb->prepare( $rid_query, array( $this->job_id ) );
|
||||
$rid = $wpdb->get_var( $rid_prepare );
|
||||
$translation_id_query = "SELECT translation_id FROM {$wpdb->prefix}icl_translation_status WHERE rid=%d";
|
||||
$translation_id_prepare = $wpdb->prepare( $translation_id_query, array( $rid ) );
|
||||
$translation_id = $wpdb->get_var( $translation_id_prepare );
|
||||
if ( $rid ) {
|
||||
$wpdb->delete( $wpdb->prefix . 'icl_translate_job', array( 'job_id' => $this->job_id ) );
|
||||
$wpdb->delete( $wpdb->prefix . 'icl_translate', array( 'job_id' => $this->job_id ) );
|
||||
$deleted = true;
|
||||
}
|
||||
|
||||
if ( $translation_id ) {
|
||||
$wpdb->delete( $wpdb->prefix . 'icl_translations', array( 'translation_id' => $translation_id ) );
|
||||
if ( $rid ) {
|
||||
$wpdb->delete(
|
||||
$wpdb->prefix . 'icl_translation_status',
|
||||
array(
|
||||
'translation_id' => $translation_id,
|
||||
'rid' => $rid,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TranslationProxy_Project $project
|
||||
* @param int $translator_id
|
||||
* @param WPML_TM_CMS_ID $cms_id_helper
|
||||
* @param TranslationManagement $tm_instance
|
||||
* @param null|string $note
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function send_to_tp( $project, $translator_id, &$cms_id_helper, &$tm_instance, $note = null ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
$file = $this->to_xliff_file();
|
||||
$title = $this->get_title();
|
||||
$cms_id = $cms_id_helper->cms_id_from_job_id( $this->get_id() );
|
||||
$url = $this->get_url( true );
|
||||
$word_count = $this->estimate_word_count();
|
||||
$note = isset( $note ) ? $note : '';
|
||||
$source_language = $this->get_source_language_code();
|
||||
$target_language = $this->get_language_code();
|
||||
$uuid = $this->get_uuid();
|
||||
|
||||
try {
|
||||
$tp_job_id = $project->send_to_translation_batch_mode( $file, $title, $cms_id, $url, $source_language, $target_language, $word_count, $translator_id, $note, $uuid );
|
||||
} catch ( Exception $err ) {
|
||||
// The translation entry will be removed
|
||||
$project->errors[] = $err;
|
||||
$tp_job_id = 0;
|
||||
}
|
||||
|
||||
$translation_id = $this->get_translation_id();
|
||||
|
||||
if ( $tp_job_id ) {
|
||||
$tm_instance->update_translation_status(
|
||||
array(
|
||||
'translation_id' => $translation_id,
|
||||
'translator_id' => $translator_id,
|
||||
'status' => ICL_TM_IN_PROGRESS,
|
||||
'needs_update' => 0,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$previous_state = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
" SELECT _prevstate
|
||||
FROM {$wpdb->prefix}icl_translation_status
|
||||
WHERE translation_id=%d
|
||||
LIMIT 1",
|
||||
$translation_id
|
||||
)
|
||||
);
|
||||
if ( ! empty( $previous_state ) ) {
|
||||
$previous_state = unserialize( $previous_state );
|
||||
$data = array(
|
||||
'status' => $previous_state['status'],
|
||||
'translator_id' => $previous_state['translator_id'],
|
||||
'needs_update' => $previous_state['needs_update'],
|
||||
'md5' => $previous_state['md5'],
|
||||
'translation_service' => $previous_state['translation_service'],
|
||||
'translation_package' => $previous_state['translation_package'],
|
||||
'timestamp' => $previous_state['timestamp'],
|
||||
'links_fixed' => $previous_state['links_fixed'],
|
||||
);
|
||||
$data_where = array( 'translation_id' => $translation_id );
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translation_status', $data, $data_where );
|
||||
} else {
|
||||
$data = array(
|
||||
'status' => ICL_TM_NOT_TRANSLATED,
|
||||
'needs_update' => 0,
|
||||
);
|
||||
$data_where = array( 'translation_id' => $translation_id );
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translation_status', $data, $data_where );
|
||||
}
|
||||
$err = true;
|
||||
}
|
||||
|
||||
return array( isset( $err ) ? $err : false, $project, $tp_job_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|false $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract function get_url( $original = false );
|
||||
|
||||
/**
|
||||
* @return WP_Post|WPML_Package|mixed
|
||||
*/
|
||||
abstract function get_original_document();
|
||||
|
||||
protected function load_status() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
$this->basic_data->status = ! empty( $this->basic_data->translated ) ? ICL_TM_COMPLETE : $this->basic_data->status;
|
||||
|
||||
return TranslationManagement::get_job_status_string(
|
||||
$this->basic_data->status,
|
||||
$this->basic_data->needs_update
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
*
|
||||
* @return bool|stdClass|WPML_Element_Translation_Job
|
||||
*/
|
||||
protected function load_job_data( $job_id ) {
|
||||
if ( $this->job_factory ) {
|
||||
return $this->job_factory->get_translation_job( $job_id, false, 1 );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function save_updated_assignment() {
|
||||
global $wpdb;
|
||||
|
||||
$job_id = $this->get_id();
|
||||
$service = $this->get_translation_service();
|
||||
list( $prev_translator_id, $rid ) = $wpdb->get_row( $wpdb->prepare( "SELECT translator_id, rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id ), ARRAY_N );
|
||||
|
||||
$translator_id = $this->get_translator_id();
|
||||
$assigned_correctly = $translator_id == $prev_translator_id;
|
||||
$assigned_correctly = apply_filters( 'wpml_job_assigned_to_after_assignment', $assigned_correctly, $job_id, $translator_id, $service );
|
||||
|
||||
if ( $assigned_correctly ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'translator_id' => $translator_id,
|
||||
'status' => ICL_TM_WAITING_FOR_TRANSLATOR,
|
||||
'translation_service' => $service,
|
||||
);
|
||||
$data_where = array( 'rid' => $rid );
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translation_status', $data, $data_where );
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translate_job', array( 'translator_id' => $translator_id ), array( 'job_id' => $job_id ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the batch ID for job elements using the
|
||||
* `icl_translation_status` and `icl_translate_job` tables
|
||||
*/
|
||||
protected function load_batch_id() {
|
||||
global $wpdb;
|
||||
|
||||
$this->batch_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT batch_id
|
||||
FROM {$wpdb->prefix}icl_translation_status as ts
|
||||
LEFT JOIN {$wpdb->prefix}icl_translate_job as tj ON tj.rid = ts.rid
|
||||
WHERE tj.job_id = %d AND tj.revision IS NULL
|
||||
LIMIT 1",
|
||||
$this->job_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function get_iclt_field( $field_name, $translation ) {
|
||||
global $wpdb;
|
||||
|
||||
$column_name = ( $translation === true ? 'i' : 'o' ) . '.' . $field_name;
|
||||
|
||||
$query = " SELECT {$column_name}
|
||||
FROM {$wpdb->prefix}icl_translations o
|
||||
JOIN {$wpdb->prefix}icl_translations i
|
||||
ON i.trid = o.trid
|
||||
AND i.source_language_code = o.language_code
|
||||
JOIN {$wpdb->prefix}icl_translation_status s
|
||||
ON s.translation_id = i.translation_id
|
||||
JOIN {$wpdb->prefix}icl_translate_job j
|
||||
ON j.rid = s.rid
|
||||
WHERE j.job_id = %d
|
||||
LIMIT 1";
|
||||
$args = array( $this->get_id() );
|
||||
$prepared_query = $wpdb->prepare( $query, $args );
|
||||
return $wpdb->get_var( $prepared_query );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the job does not have deadline date,
|
||||
* we consider that the job was completed on time.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_completed_on_time() {
|
||||
return $this->get_number_of_days_overdue() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|int Negative integer if the job was completed before the deadline, or positive either.
|
||||
* False is the job has no deadline date
|
||||
*/
|
||||
public function get_number_of_days_overdue() {
|
||||
$deadline = $this->get_deadline_date();
|
||||
$completed = $this->get_completed_date();
|
||||
|
||||
if ( ! $deadline ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $completed ) {
|
||||
$completed = strtotime( 'now' );
|
||||
} else {
|
||||
$completed = strtotime( $completed );
|
||||
}
|
||||
|
||||
$deadline = strtotime( $deadline );
|
||||
|
||||
return (int) floor( ( $completed - $deadline ) / DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function get_deadline_date() {
|
||||
return $this->get_basic_data_property( 'deadline_date' );
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function get_completed_date() {
|
||||
return $this->get_basic_data_property( 'completed_date' );
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function get_manager_id() {
|
||||
return $this->get_basic_data_property( 'manager_id' );
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
protected function get_title_from_db() {
|
||||
return $this->get_basic_data_property( 'title' );
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
protected function get_uuid() {
|
||||
return $this->get_basic_data_property( 'uuid' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-translation-job.class.php';
|
||||
|
||||
class WPML_External_Translation_Job extends WPML_Element_Translation_Job {
|
||||
|
||||
function get_original_document() {
|
||||
|
||||
return apply_filters(
|
||||
'wpml_get_translatable_item',
|
||||
null,
|
||||
$this->get_original_element_id(),
|
||||
isset( $this->basic_data->original_post_type ) ? $this->basic_data->original_post_type : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|false $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_url( $original = false ) {
|
||||
|
||||
$url = null;
|
||||
$element_id = null;
|
||||
|
||||
if ( $original ) {
|
||||
$element_id = $this->get_original_element_id();
|
||||
$url = apply_filters( 'wpml_external_item_url', '', $element_id );
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_element_translation_job_url', $url, $original, $element_id, $this->get_original_document() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
$title = $this->get_title_from_db();
|
||||
|
||||
if ( $title ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$original_element = $this->get_original_document();
|
||||
|
||||
return $original_element
|
||||
? apply_filters( 'wpml_tm_external_translation_job_title', $this->title_from_job_fields(), $original_element->ID )
|
||||
: $this->original_del_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_type_title() {
|
||||
$original_element = $this->get_original_document();
|
||||
return $original_element->kind;
|
||||
}
|
||||
|
||||
protected function load_resultant_element_id() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function title_from_job_fields() {
|
||||
global $wpdb;
|
||||
|
||||
$title_and_name = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT n.field_data AS name, t.field_data AS title
|
||||
FROM {$wpdb->prefix}icl_translate AS n
|
||||
JOIN {$wpdb->prefix}icl_translate AS t
|
||||
ON n.job_id = t.job_id
|
||||
WHERE n.job_id = %d
|
||||
AND n.field_type = 'name'
|
||||
AND t.field_type = 'title'
|
||||
LIMIT 1
|
||||
",
|
||||
$this->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
return $title_and_name !== null ? ( $title_and_name->name ?
|
||||
base64_decode( $title_and_name->name )
|
||||
: base64_decode( $title_and_name->title ) ) : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
use WPML\TM\Jobs\FieldId;
|
||||
use WPML\TM\Jobs\TermMeta;
|
||||
use WPML\FP\Lst;
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-translation-job.class.php';
|
||||
|
||||
class WPML_Post_Translation_Job extends WPML_Element_Translation_Job {
|
||||
|
||||
function get_original_document() {
|
||||
|
||||
return get_post( $this->get_original_element_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|false $original
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_url( $original = false ) {
|
||||
$url = null;
|
||||
$element_id = null;
|
||||
|
||||
if ( $original ) {
|
||||
$element_id = $this->get_original_element_id();
|
||||
$url = get_permalink( $element_id );
|
||||
} else {
|
||||
$element_id = $this->get_resultant_element_id();
|
||||
$url = get_edit_post_link( $element_id );
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_element_translation_job_url', $url, $original, $element_id, $this->get_original_document() );
|
||||
}
|
||||
|
||||
/**
|
||||
* It checks that the post type is translatable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_translatable_post_type() {
|
||||
$post_type = $this->get_post_type();
|
||||
|
||||
if ( $post_type ) {
|
||||
/** @var SitePress $sitepress */
|
||||
global $sitepress;
|
||||
if ( $sitepress ) {
|
||||
$post_types = array_keys( $sitepress->get_translatable_documents() );
|
||||
|
||||
return in_array( $post_type, $post_types, true );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function update_fields_from_post() {
|
||||
global $iclTranslationManagement, $wpdb, $wpml_translation_job_factory;
|
||||
|
||||
$job_id = $this->get_id();
|
||||
$post_id = $this->get_resultant_element_id();
|
||||
$data['complete'] = 1;
|
||||
$data['job_id'] = $job_id;
|
||||
$job = $wpml_translation_job_factory->get_translation_job( $job_id, 1 );
|
||||
$term_names = $this->get_term_field_array_for_post();
|
||||
$post = get_post( $post_id );
|
||||
if ( is_object( $job ) && is_array( $job->elements ) && is_object( $post ) ) {
|
||||
foreach ( $job->elements as $element ) {
|
||||
$field_data = '';
|
||||
switch ( $element->field_type ) {
|
||||
case 'title':
|
||||
$field_data = $this->encode_field_data( $post->post_title);
|
||||
break;
|
||||
case 'body':
|
||||
$field_data = $this->encode_field_data( $post->post_content);
|
||||
break;
|
||||
case 'excerpt':
|
||||
$field_data = $this->encode_field_data( $post->post_excerpt);
|
||||
break;
|
||||
case 'URL':
|
||||
$field_data = $this->encode_field_data( $post->post_name);
|
||||
break;
|
||||
default:
|
||||
if ( isset( $term_names[ $element->field_type ] ) ) {
|
||||
$field_data = $this->encode_field_data( $term_names[ $element->field_type ]);
|
||||
}
|
||||
}
|
||||
if ( $field_data ) {
|
||||
$wpdb->update( $wpdb->prefix . 'icl_translate',
|
||||
array(
|
||||
'field_data_translated' => $field_data,
|
||||
'field_finished' => 1
|
||||
),
|
||||
array( 'tid' => $element->tid )
|
||||
);
|
||||
}
|
||||
}
|
||||
$iclTranslationManagement->mark_job_done( $job_id );
|
||||
}
|
||||
}
|
||||
|
||||
function save_terms_to_post() {
|
||||
/** @var SitePress $sitepress */
|
||||
global $sitepress, $wpdb;
|
||||
|
||||
$lang_code = $this->get_language_code();
|
||||
|
||||
if ( $sitepress->get_setting( 'tm_block_retranslating_terms' ) ) {
|
||||
$this->load_terms_from_post_into_job( true );
|
||||
}
|
||||
$terms = $this->get_terms_in_job_rows();
|
||||
foreach ( $terms as $term ) {
|
||||
$new_term_action = new WPML_Update_Term_Action( $wpdb, $sitepress, [
|
||||
'term' => base64_decode( $term->field_data_translated ),
|
||||
'description' => TermMeta::getTermDescription( $this->get_id(), $term->term_taxonomy_id ),
|
||||
'lang_code' => $lang_code,
|
||||
'trid' => $term->trid,
|
||||
'taxonomy' => $term->taxonomy
|
||||
] );
|
||||
$new_term = $new_term_action->execute();
|
||||
|
||||
foreach ( TermMeta::getTermMeta( $this->get_id(), $term->term_taxonomy_id ) as $meta ) {
|
||||
update_term_meta( $new_term['term_taxonomy_id'], FieldId::getTermMetaKey( $meta->field_type ), $meta->field_data_translated );
|
||||
}
|
||||
}
|
||||
|
||||
$term_helper = wpml_get_term_translation_util();
|
||||
$term_helper->sync_terms( $this->get_original_element_id(), $this->get_language_code() );
|
||||
}
|
||||
|
||||
function load_terms_from_post_into_job( $delete = null ) {
|
||||
global $sitepress;
|
||||
|
||||
$delete = isset( $delete ) ? $delete : $sitepress->get_setting( 'tm_block_retranslating_terms' );
|
||||
$this->set_translated_term_values( $delete );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
$title = $this->get_title_from_db();
|
||||
|
||||
if ( $title ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$original_post = $this->get_original_document();
|
||||
|
||||
return is_object( $original_post ) && isset( $original_post->post_title )
|
||||
? $original_post->post_title : $this->original_del_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_type_title() {
|
||||
$post_type = get_post_type_object( $this->get_post_type() );
|
||||
|
||||
return $post_type->labels->singular_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_post_type() {
|
||||
$original_post = $this->get_original_document();
|
||||
|
||||
return $original_post->post_type;
|
||||
}
|
||||
|
||||
protected function load_resultant_element_id() {
|
||||
global $wpdb;
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
return $wpdb->get_var( $wpdb->prepare( "SELECT element_id
|
||||
FROM {$wpdb->prefix}icl_translations
|
||||
WHERE translation_id = %d
|
||||
LIMIT 1",
|
||||
$this->basic_data->translation_id ) );
|
||||
}
|
||||
|
||||
protected function get_terms_in_job_rows(){
|
||||
global $wpdb;
|
||||
|
||||
$query_for_terms_in_job = $wpdb->prepare(" SELECT
|
||||
tt.taxonomy,
|
||||
tt.term_taxonomy_id,
|
||||
iclt.trid,
|
||||
j.field_data_translated
|
||||
FROM {$wpdb->term_taxonomy} tt
|
||||
JOIN {$wpdb->prefix}icl_translations iclt
|
||||
ON iclt.element_id = tt.term_taxonomy_id
|
||||
AND CONCAT('tax_', tt.taxonomy) = iclt.element_type
|
||||
JOIN {$wpdb->prefix}icl_translate j
|
||||
ON j.field_type = CONCAT('t_', tt.term_taxonomy_id)
|
||||
WHERE j.job_id = %d ", $this->get_id());
|
||||
|
||||
return $wpdb->get_results( $query_for_terms_in_job );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an array of all terms associated with a post. This array is indexed by indexes of the for {t_}{term_taxonomy_id}.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_term_field_array_for_post() {
|
||||
global $wpdb;
|
||||
|
||||
$post_id = $this->get_resultant_element_id();
|
||||
$query = $wpdb->prepare( "SELECT o.term_taxonomy_id, t.name
|
||||
FROM {$wpdb->term_relationships} o
|
||||
JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = o.term_taxonomy_id
|
||||
JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
|
||||
WHERE o.object_id = %d",
|
||||
$post_id );
|
||||
$res = $wpdb->get_results( $query );
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ( $res as $term ) {
|
||||
$result[ 't_' . $term->term_taxonomy_id ] = $term->name;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function set_translated_term_values( $delete ) {
|
||||
global $wpdb;
|
||||
|
||||
$translations_table = $wpdb->prefix . 'icl_translations';
|
||||
$translate_table = $wpdb->prefix . 'icl_translate';
|
||||
|
||||
$job_id = $this->get_id();
|
||||
$get_target_terms_for_job_query = $wpdb->prepare( "
|
||||
SELECT
|
||||
t.name,
|
||||
tt.description,
|
||||
iclt_original.element_id ttid,
|
||||
t.term_id tr_ttid
|
||||
FROM {$wpdb->terms} t
|
||||
JOIN {$wpdb->term_taxonomy} tt
|
||||
ON t.term_id = tt.term_id
|
||||
JOIN {$translations_table} iclt_translation
|
||||
ON iclt_translation.element_id = tt.term_taxonomy_id
|
||||
AND CONCAT('tax_', tt.taxonomy) = iclt_translation.element_type
|
||||
JOIN {$translations_table} iclt_original
|
||||
ON iclt_original.trid = iclt_translation.trid
|
||||
JOIN {$translate_table} jobs
|
||||
ON jobs.field_type = CONCAT('t_', iclt_original.element_id)
|
||||
WHERE jobs.job_id = %d
|
||||
AND iclt_translation.language_code = %s",
|
||||
$job_id, $this->get_language_code() );
|
||||
|
||||
$term_values = $wpdb->get_results( $get_target_terms_for_job_query );
|
||||
foreach ( $term_values as $term ) {
|
||||
if ( $delete ) {
|
||||
$conditions = [
|
||||
"field_type LIKE 'tfield-%-{$term->ttid}'", // Term fields
|
||||
"field_type LIKE 'tfield-%-{$term->ttid}\_%'", // Term fields as array
|
||||
"field_type = 't_{$term->ttid}'",
|
||||
"field_type = 'tdesc_{$term->ttid}'",
|
||||
];
|
||||
$wpdb->query(
|
||||
"DELETE FROM {$translate_table} WHERE job_id = $job_id AND "
|
||||
. "(" . Lst::join( ' OR ', $conditions ) . ")"
|
||||
);
|
||||
} else {
|
||||
$wpdb->update(
|
||||
$translate_table,
|
||||
[ 'field_data_translated' => base64_encode( $term->name ), 'field_finished' => 1 ],
|
||||
[ 'field_type' => 't_' . $term->ttid, 'job_id' => $job_id ]
|
||||
);
|
||||
$wpdb->update(
|
||||
$translate_table,
|
||||
[ 'field_data_translated' => base64_encode( $term->description ), 'field_finished' => 1 ],
|
||||
[ 'field_type' => 'tdesc_' . $term->ttid, 'job_id' => $job_id ]
|
||||
);
|
||||
|
||||
$meta_values = $wpdb->get_results( "SELECT meta_key, meta_value FROM {$wpdb->termmeta} WHERE term_id = {$term->tr_ttid}" );
|
||||
foreach ( $meta_values as $meta ) {
|
||||
$wpdb->update(
|
||||
$translate_table,
|
||||
[ 'field_finished' => 1, 'field_data_translated' => base64_encode( $meta->meta_value ) ],
|
||||
[ 'job_id' => $job_id, 'field_type' => 'tfield-' . $meta->meta_key . '-' . $term->ttid ]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-translation-job.class.php';
|
||||
|
||||
class WPML_String_Translation_Job extends WPML_Translation_Job {
|
||||
|
||||
protected function load_job_data( $string_translation_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT st.id,
|
||||
s.language AS source_language_code,
|
||||
st.language AS language_code,
|
||||
IF(cs.status IS NULL, st.status, cs.status) as status,
|
||||
st.string_id,
|
||||
s.name,
|
||||
s.value,
|
||||
tb.id AS batch_id,
|
||||
st.translation_service,
|
||||
st.translator_id,
|
||||
u.display_name as translator_name,
|
||||
COUNT( st.id ) as strings_count
|
||||
FROM {$wpdb->prefix}icl_string_translations AS st
|
||||
INNER JOIN {$wpdb->prefix}icl_strings AS s
|
||||
ON st.string_id = s.id
|
||||
INNER JOIN {$wpdb->prefix}icl_translation_batches AS tb
|
||||
ON tb.id = st.batch_id
|
||||
LEFT JOIN {$wpdb->users} u
|
||||
ON st.translator_id = u.ID
|
||||
LEFT JOIN {$wpdb->prefix}icl_string_status ss ON ss.string_translation_id = st.id
|
||||
LEFT JOIN {$wpdb->prefix}icl_core_status cs ON cs.rid = ss.rid
|
||||
WHERE st.id = %d
|
||||
LIMIT 1",
|
||||
$string_translation_id
|
||||
);
|
||||
|
||||
return $wpdb->get_row( $query );
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
return esc_html( $this->basic_data->value );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
|
||||
return 'string|' . parent::get_id();
|
||||
}
|
||||
|
||||
public function get_type() {
|
||||
return 'String';
|
||||
}
|
||||
|
||||
public function get_original_element_id() {
|
||||
if ( ! $this->basic_data ) {
|
||||
$this->maybe_load_basic_data();
|
||||
}
|
||||
|
||||
return $this->basic_data->string_id;
|
||||
}
|
||||
|
||||
public function cancel() {
|
||||
global $WPML_String_Translation, $wpdb;
|
||||
/** @var WPML_String_Translation $WPML_String_Translation */
|
||||
if ( $WPML_String_Translation ) {
|
||||
$rid = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT rid
|
||||
FROM {$wpdb->prefix}icl_string_status
|
||||
WHERE string_translation_id = %d
|
||||
LIMIT 1",
|
||||
$this->job_id
|
||||
)
|
||||
);
|
||||
if ( $rid ) {
|
||||
$WPML_String_Translation->cancel_remote_translation( $rid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function load_status() {
|
||||
$this->maybe_load_basic_data();
|
||||
$this->status = WPML_Remote_String_Translation::get_string_status_label( $this->basic_data->status );
|
||||
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function to_array() {
|
||||
$this->maybe_load_basic_data();
|
||||
$this->basic_data->value = $this->get_title();
|
||||
$data_array = $this->basic_data_to_array( $this->basic_data );
|
||||
$data_array['job_id'] = 'string|' . $this->job_id;
|
||||
$data_array['translation_id'] = $this->basic_data->id;
|
||||
$data_array['status'] = $this->get_status();
|
||||
$data_array['id'] = $this->get_id();
|
||||
|
||||
return $data_array;
|
||||
}
|
||||
|
||||
protected function load_resultant_element_id() {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT id FROM {$wpdb->prefix}icl_string_translations
|
||||
WHERE string_id = %d AND language = %s",
|
||||
$this->get_original_element_id(),
|
||||
$this->get_language_code()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function save_updated_assignment() {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->update(
|
||||
$wpdb->prefix . 'icl_string_translations',
|
||||
array(
|
||||
'translator_id' => $this->get_translator_id(),
|
||||
'translation_service' => $this->get_translation_service(),
|
||||
),
|
||||
array(
|
||||
'string_id' => $this->get_original_element_id(),
|
||||
'language' => $this->get_language_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the batch ID for a string job
|
||||
*/
|
||||
protected function load_batch_id() {
|
||||
global $wpdb;
|
||||
|
||||
$this->batch_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT batch_id
|
||||
FROM {$wpdb->prefix}icl_string_translations
|
||||
WHERE id = %d
|
||||
LIMIT 1",
|
||||
$this->job_id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_Translation_Job extends WPML_Translation_Job_Helper {
|
||||
protected $basic_data;
|
||||
protected $element_id = - 1;
|
||||
protected $status = - 1;
|
||||
protected $job_id;
|
||||
protected $batch_id;
|
||||
|
||||
/** @var WPML_TM_Blog_Translators $blog_translators */
|
||||
protected $blog_translators;
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
* @param int|null $batch_id
|
||||
* @param WPML_TM_Blog_Translators $blog_translators
|
||||
*/
|
||||
function __construct( $job_id, $batch_id = null, &$blog_translators = null ) {
|
||||
$this->job_id = $job_id;
|
||||
$batch_id = $batch_id ? $batch_id : $this->get_batch_id();
|
||||
$this->batch_id = $batch_id ? $batch_id : TranslationProxy_Batch::update_translation_batch();
|
||||
$this->blog_translators = $blog_translators ? $blog_translators : wpml_tm_load_blog_translators();
|
||||
}
|
||||
|
||||
abstract public function cancel();
|
||||
|
||||
abstract public function get_original_element_id();
|
||||
|
||||
abstract public function to_array();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract function get_title();
|
||||
|
||||
public function get_status() {
|
||||
if ( $this->status == - 1 ) {
|
||||
$this->status = $this->load_status();
|
||||
}
|
||||
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function get_status_value() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
return $this->basic_data->status;
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return $this->job_id;
|
||||
}
|
||||
|
||||
public function get_resultant_element_id( $force = false ) {
|
||||
if ( $this->element_id == - 1 || $force === true ) {
|
||||
$this->element_id = $this->load_resultant_element_id();
|
||||
}
|
||||
|
||||
return $this->element_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the input user is allowed to edit this job
|
||||
*
|
||||
* @param WP_User $user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function user_can_translate( $user ) {
|
||||
$translator_id = $this->get_translator_id();
|
||||
$user_can_take_this_job = 0 === $translator_id
|
||||
|| $this->is_current_user_allowed_to_translate(
|
||||
$user,
|
||||
$translator_id
|
||||
);
|
||||
|
||||
$translator_has_job_language_pairs = $this->blog_translators->is_translator(
|
||||
$user->ID,
|
||||
array(
|
||||
'lang_from' => $this->get_source_language_code(),
|
||||
'lang_to' => $this->get_language_code(),
|
||||
)
|
||||
);
|
||||
|
||||
$user_can_translate = ( $user_can_take_this_job && $translator_has_job_language_pairs )
|
||||
|| user_can( $user, 'manage_options' );
|
||||
return apply_filters( 'wpml_user_can_translate', $user_can_translate, $user );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WP_User $user
|
||||
* @param int $translator_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_current_user_allowed_to_translate( WP_User $user, $translator_id ) {
|
||||
$allowed_translators = apply_filters( 'wpml_tm_allowed_translators_for_job', array(), $this );
|
||||
$allowed_translators[] = $translator_id;
|
||||
|
||||
return in_array( (int) $user->ID, $allowed_translators, true );
|
||||
}
|
||||
|
||||
public function get_batch_id() {
|
||||
if ( ! isset( $this->batch_id ) ) {
|
||||
$this->load_batch_id();
|
||||
}
|
||||
|
||||
return $this->batch_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|false $as_name if true will return the language's display name if applicable
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function get_language_code( $as_name = false ) {
|
||||
$this->maybe_load_basic_data();
|
||||
$code = isset( $this->basic_data->language_code ) ? $this->basic_data->language_code : false;
|
||||
|
||||
return $code && $as_name ? $this->lang_code_to_name( $code ) : $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|false $as_name if true will return the language's display name if applicable
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
function get_source_language_code( $as_name = false ) {
|
||||
$this->maybe_load_basic_data();
|
||||
$code = isset( $this->basic_data->source_language_code ) ? $this->basic_data->source_language_code : false;
|
||||
|
||||
return $code && $as_name ? $this->lang_code_to_name( $code ) : $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public function get_translator_name() {
|
||||
$this->maybe_load_basic_data();
|
||||
if ( $this->basic_data->translation_service == TranslationProxy::get_current_service_id() ) {
|
||||
$this->basic_data->translator_name = TranslationProxy_Translator::get_translator_name( $this->basic_data->translator_id );
|
||||
} else {
|
||||
$this->basic_data->translator_name = false;
|
||||
}
|
||||
|
||||
return $this->basic_data->translator_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the assigned translator or 0 if no translator is assigned to the job
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_translator_id() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
$this->basic_data->translator_id = ! empty( $this->basic_data->translator_id )
|
||||
? $this->basic_data->translator_id : 0;
|
||||
|
||||
return (int) $this->basic_data->translator_id;
|
||||
}
|
||||
|
||||
public function get_basic_data() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
return $this->basic_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $translator_id
|
||||
* @param string $service
|
||||
*
|
||||
* @return bool true on success false on failure
|
||||
*/
|
||||
public function assign_to( $translator_id, $service = 'local' ) {
|
||||
$this->maybe_load_basic_data();
|
||||
$prev_translator_id = $this->get_translator_id();
|
||||
$prev_service = $this->get_translation_service();
|
||||
if ( $translator_id == $prev_translator_id && $service = $this->get_translation_service() ) {
|
||||
|
||||
return true;
|
||||
}
|
||||
$this->basic_data->translator_id = $translator_id;
|
||||
$this->basic_data->translation_service = $service;
|
||||
|
||||
if ( $this->save_updated_assignment() === false ) {
|
||||
$this->basic_data->translator_id = $prev_translator_id;
|
||||
$this->basic_data->translation_service = $prev_service;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$job_id = $this->get_id();
|
||||
if ( $this->get_tm_setting( array( 'notification', 'resigned' ) ) == ICL_TM_NOTIFICATION_IMMEDIATELY
|
||||
&& ! empty( $prev_translator_id )
|
||||
&& $prev_translator_id != $translator_id
|
||||
&& $job_id ) {
|
||||
do_action( 'wpml_tm_remove_job_notification', $prev_translator_id, $this );
|
||||
}
|
||||
|
||||
if ( $this->get_tm_setting( array( 'notification', 'new-job' ) ) == ICL_TM_NOTIFICATION_IMMEDIATELY ) {
|
||||
if ( empty( $translator_id ) ) {
|
||||
do_action( 'wpml_tm_new_job_notification', $this );
|
||||
} else {
|
||||
do_action( 'wpml_tm_assign_job_notification', $this, $translator_id );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either the translation service id for the job or 'local' for local jobs
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function get_translation_service() {
|
||||
$this->maybe_load_basic_data();
|
||||
$this->basic_data->translation_service = ! empty( $this->basic_data->translation_service )
|
||||
? $this->basic_data->translation_service : 'local';
|
||||
|
||||
return $this->basic_data->translation_service;
|
||||
}
|
||||
|
||||
abstract protected function save_updated_assignment();
|
||||
|
||||
abstract protected function load_resultant_element_id();
|
||||
|
||||
abstract protected function load_status();
|
||||
|
||||
abstract protected function load_job_data( $id );
|
||||
|
||||
abstract function get_type();
|
||||
|
||||
protected function basic_data_to_array( $job_data ) {
|
||||
$this->maybe_load_basic_data();
|
||||
$data_array = (array) $job_data;
|
||||
if ( isset( $data_array['post_title'] ) ) {
|
||||
$data_array['post_title'] = esc_html( $data_array['post_title'] );
|
||||
}
|
||||
$data_array['translator_name'] = $this->get_translator_name();
|
||||
$data_array['batch_id'] = $job_data->batch_id;
|
||||
$data_array['source_language_code'] = $this->basic_data->source_language_code;
|
||||
$data_array['language_code'] = $this->basic_data->language_code;
|
||||
$data_array['translator_html'] = $this->get_translator_html( $this->basic_data );
|
||||
$data_array['type'] = $this->get_type();
|
||||
$data_array['lang_text'] = $this->generate_lang_text();
|
||||
|
||||
return $data_array;
|
||||
}
|
||||
|
||||
protected function maybe_load_basic_data() {
|
||||
if ( ! $this->basic_data ) {
|
||||
$this->basic_data = $this->load_job_data( $this->job_id );
|
||||
$this->basic_data = $this->basic_data ? $this->basic_data : new stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
private function get_inactive_translation_service( $translation_service_id ) {
|
||||
$cache_key = $translation_service_id;
|
||||
$cache_group = 'get_inactive_translation_service';
|
||||
$cache_found = false;
|
||||
|
||||
$service = wp_cache_get( $cache_key, $cache_group, false, $cache_found );
|
||||
|
||||
if ( ! $cache_found ) {
|
||||
try {
|
||||
$service = TranslationProxy_Service::get_service( $translation_service_id );
|
||||
} catch ( WPMLTranslationProxyApiException $ex ) {
|
||||
$service = false;
|
||||
}
|
||||
if ( ! $service ) {
|
||||
$service = new stdClass();
|
||||
$service->name = __( '(inactive and unknown service)', 'wpml-translation-management' );
|
||||
}
|
||||
wp_cache_set( $cache_key, $service, $cache_group );
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
protected function get_translator_html( $job ) {
|
||||
|
||||
$job = (object) $job;
|
||||
$current_service_name = TranslationProxy::get_current_service_name();
|
||||
$translation_services = array( 'local', TranslationProxy::get_current_service_id() );
|
||||
|
||||
if ( isset( $job->translation_service ) && ! in_array( $job->translation_service, $translation_services ) ) {
|
||||
$inactive_service = $this->get_inactive_translation_service( $job->translation_service );
|
||||
$current_service_name = $inactive_service->name;
|
||||
}
|
||||
$translator = '';
|
||||
|
||||
if ( $job->translation_service && $job->translation_service !== 'local' ) {
|
||||
try {
|
||||
$project = TranslationProxy::get_current_project();
|
||||
if ( $project ) {
|
||||
$translator .= $current_service_name;
|
||||
} else {
|
||||
$translator .= esc_html( $job->translator_name );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
// Just doesn't create the output
|
||||
}
|
||||
} elseif ( $job->status == ICL_TM_COMPLETE ) {
|
||||
$translator_data = get_userdata( $job->translator_id );
|
||||
$translator_name = $translator_data ? $translator_data->display_name : '';
|
||||
$translator = '<span class="icl-finished-local-name">' . $translator_name . '</span>';
|
||||
} else {
|
||||
$translator .= '<span class="icl_tj_select_translator">';
|
||||
$selected_translator = isset( $job->translator_id ) ? $job->translator_id : false;
|
||||
$disabled = false;
|
||||
if ( $job->translation_service
|
||||
&& $job->translation_service !== 'local'
|
||||
&& is_numeric( $job->translation_service ) ) {
|
||||
$selected_translator = TranslationProxy_Service::get_wpml_translator_id(
|
||||
$job->translation_service,
|
||||
$job->translator_id
|
||||
);
|
||||
$disabled = true;
|
||||
}
|
||||
|
||||
$job_id = isset( $job->job_id ) ? $job->job_id : $job->id;
|
||||
$local_only = isset( $job->local_only ) ? $job->local_only : true;
|
||||
$args = array(
|
||||
'id' => 'icl_tj_translator_for_' . $job_id,
|
||||
'name' => 'icl_tj_translator_for_' . ( $job_id ),
|
||||
'from' => $job->source_language_code,
|
||||
'to' => $job->language_code,
|
||||
'selected' => $selected_translator,
|
||||
'services' => $translation_services,
|
||||
'disabled' => $disabled,
|
||||
'echo' => false,
|
||||
'local_only' => $local_only,
|
||||
);
|
||||
|
||||
$translator .= wpml_tm_get_translators_dropdown()->render( $args );
|
||||
$translator .= '<input type="hidden" id="icl_tj_ov_'
|
||||
. $job_id
|
||||
. '" value="'
|
||||
. (int) $job->translator_id
|
||||
. '" />';
|
||||
$translator .= '<input type="hidden" id="icl_tj_ty_'
|
||||
. $job_id
|
||||
. '" value="'
|
||||
. strtolower( $this->get_type() )
|
||||
. '" />';
|
||||
$translator .= '<span class="icl_tj_select_translator_controls" id="icl_tj_tc_' . ( $job_id ) . '">';
|
||||
$translator .= '<input type="button" class="button-secondary icl_tj_ok" value="'
|
||||
. __(
|
||||
'Send',
|
||||
'wpml-translation-management'
|
||||
)
|
||||
. '" /> ';
|
||||
$translator .= '<input type="button" class="button-secondary icl_tj_cancel" value="'
|
||||
. __(
|
||||
'Cancel',
|
||||
'wpml-translation-management'
|
||||
)
|
||||
. '" />';
|
||||
$translator .= '</span>';
|
||||
}
|
||||
|
||||
return $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the batch ID associated to the job ID
|
||||
*/
|
||||
abstract protected function load_batch_id();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_lang_text() {
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
return $this->lang_code_to_name( $this->get_source_language_code() )
|
||||
. html_entity_decode( ' » ' )
|
||||
. $this->lang_code_to_name( $this->get_language_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function lang_code_to_name( $code ) {
|
||||
global $sitepress;
|
||||
|
||||
$lang_details = $sitepress->get_language_details( $code );
|
||||
|
||||
return isset( $lang_details['display_name'] ) ? $lang_details['display_name'] : $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_basic_data_property( $name ) {
|
||||
$value = null;
|
||||
$this->maybe_load_basic_data();
|
||||
|
||||
if ( isset( $this->basic_data->{$name} ) ) {
|
||||
$value = $this->basic_data->{$name};
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
class WPML_Translation_Batch extends WPML_Abstract_Job_Collection {
|
||||
|
||||
private $name = false;
|
||||
private $id = false;
|
||||
private $url = false;
|
||||
/** @var WPML_Translation_Job[] $job_objects */
|
||||
private $job_objects = array();
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
* @param int $batch_id
|
||||
*/
|
||||
public function __construct( &$wpdb, $batch_id = 0 ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->id = $batch_id > 0 ? $batch_id : $this->retrieve_generic_batch_id();
|
||||
$this->name = $batch_id <= 0 ? $this->generate_generic_batch_name() : false;
|
||||
}
|
||||
|
||||
public function reload() {
|
||||
global $wpdb;
|
||||
|
||||
list( $type_select, $post_join ) = $this->left_join_post();
|
||||
|
||||
$jobs = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
" SELECT j.job_id,
|
||||
s.batch_id,
|
||||
{$type_select}
|
||||
FROM " . $this->get_table_join() . "
|
||||
{$post_join}
|
||||
WHERE s.batch_id = %d
|
||||
AND j.revision IS NULL",
|
||||
$this->id
|
||||
)
|
||||
);
|
||||
$jobs = $this->plain_objects_to_job_instances( $jobs );
|
||||
foreach ( $jobs as $job ) {
|
||||
$this->add_job( $job );
|
||||
}
|
||||
}
|
||||
|
||||
public function get_batch_url() {
|
||||
if ( $this->url === false ) {
|
||||
$this->url = TranslationManagement::get_batch_url( $this->id );
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function get_batch_meta_array() {
|
||||
$in_active_ts = $this->belongs_to_active_ts();
|
||||
$notifications = $this->ts_supports_notifications();
|
||||
$batch_id = $this->get_batch_tp_id();
|
||||
$batch_url = $this->get_batch_url();
|
||||
$batch_name = $batch_url ? $this->get_batch_name() : '';
|
||||
$item_count = $this->get_item_count();
|
||||
|
||||
return array(
|
||||
'in_active_ts' => $in_active_ts,
|
||||
'notifications' => $notifications,
|
||||
'batch_id' => $batch_id,
|
||||
'batch_url' => $batch_url,
|
||||
'batch_name' => $batch_name,
|
||||
'item_count' => $item_count,
|
||||
'last_update' => $this->get_last_update(),
|
||||
'status_array' => $this->get_status_array(),
|
||||
'display_from' => 1,
|
||||
'display_to' => $item_count,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all translation jobs in this batch
|
||||
*/
|
||||
public function cancel_all_jobs() {
|
||||
/**
|
||||
* @var wpdb $wpdb
|
||||
* @var TranslationManagement $iclTranslationManagement
|
||||
* @var WPML_String_Translation $WPML_String_Translation
|
||||
*/
|
||||
global $wpdb, $iclTranslationManagement, $WPML_String_Translation;
|
||||
|
||||
$translation_ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT translation_id
|
||||
FROM {$wpdb->prefix}icl_translation_status
|
||||
WHERE batch_id = %d",
|
||||
$this->id
|
||||
)
|
||||
);
|
||||
foreach ( $translation_ids as $translation_id ) {
|
||||
$iclTranslationManagement->cancel_translation_request( $translation_id );
|
||||
}
|
||||
|
||||
$string_translation_ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT id FROM {$wpdb->prefix}icl_string_translations WHERE batch_id = %d",
|
||||
$this->id
|
||||
)
|
||||
);
|
||||
foreach ( $string_translation_ids as $st_trans_id ) {
|
||||
$rid = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT MAX(rid)
|
||||
FROM {$wpdb->prefix}icl_string_status
|
||||
WHERE string_translation_id = %d",
|
||||
$st_trans_id
|
||||
)
|
||||
);
|
||||
if ( $rid ) {
|
||||
$WPML_String_Translation->cancel_remote_translation( $rid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: [WPML 3.2.1] This method and other similar methods can likely be removed
|
||||
public function get_last_update() {
|
||||
return TranslationManagement::get_batch_last_update( $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Translation_Job $job
|
||||
*/
|
||||
public function add_job( $job ) {
|
||||
$this->job_objects[ $job->get_id() ] = $job;
|
||||
}
|
||||
|
||||
public function get_jobs_as_array() {
|
||||
$res = array();
|
||||
krsort( $this->job_objects );
|
||||
foreach ( $this->job_objects as $job ) {
|
||||
$res[] = $job->to_array();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function get_item_count() {
|
||||
|
||||
return count( $this->job_objects );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function get_batch_name() {
|
||||
if ( $this->name == false ) {
|
||||
$this->name = TranslationManagement::get_batch_name( $this->get_id() );
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function get_batch_tp_id() {
|
||||
return TranslationManagement::get_batch_tp_id( $this->id );
|
||||
}
|
||||
|
||||
public function get_status_array() {
|
||||
$status_array = array();
|
||||
foreach ( $this->job_objects as $job ) {
|
||||
if ( ! isset( $status_array[ $job->get_status() ] ) ) {
|
||||
$status_array[ $job->get_status() ] = 0;
|
||||
}
|
||||
$status_array[ $job->get_status() ] ++;
|
||||
}
|
||||
|
||||
return $status_array;
|
||||
}
|
||||
|
||||
private function retrieve_generic_batch_id() {
|
||||
return TranslationProxy_Batch::update_translation_batch( $this->generate_generic_batch_name() );
|
||||
}
|
||||
|
||||
private function generate_generic_batch_name() {
|
||||
return 'Manual Translations from ' . date( 'F \t\h\e jS\, Y' );
|
||||
}
|
||||
|
||||
private function belongs_to_active_ts() {
|
||||
global $wpdb;
|
||||
|
||||
$service_id = TranslationProxy::get_current_service_id();
|
||||
$batch_id = $this->get_id();
|
||||
|
||||
$result = false;
|
||||
if ( $service_id ) {
|
||||
$result = $wpdb->get_var( $wpdb->prepare( "SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE batch_id = %d AND translation_service = %s LIMIT 1", array( $batch_id, $service_id ) ) );
|
||||
$result |= $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}icl_string_translations WHERE batch_id = %d AND translation_service = %s LIMIT 1", array( $batch_id, $service_id ) ) );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function ts_supports_notifications() {
|
||||
$translation_service = TranslationProxy::get_current_service();
|
||||
return $supports_notifications = isset( $translation_service->notification ) ? $translation_service->notification : true;
|
||||
}
|
||||
|
||||
public function clear_batch_data() {
|
||||
TranslationProxy_Basket::set_batch_data( null );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-element-translation-job.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-external-translation-job.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-post-translation-job.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-string-translation-job.class.php';
|
||||
|
||||
class WPML_Translation_Jobs_Collection extends WPML_Abstract_Job_Collection {
|
||||
|
||||
/** @var WPML_Translation_Batch[] $translation_batches */
|
||||
private $translation_batches = array();
|
||||
|
||||
private $count = 0;
|
||||
private $first_count;
|
||||
private $last_count;
|
||||
private $before_count = 0;
|
||||
private $after_count = 0;
|
||||
/** @var array $filter */
|
||||
private $filter;
|
||||
|
||||
/** @var string $jobs_union_table_sql */
|
||||
private $jobs_union_table_sql;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
* @param array $icl_translation_filter
|
||||
*/
|
||||
public function __construct( &$wpdb, $icl_translation_filter ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->filter = $icl_translation_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @param int $per_page
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_paginated_batches( $page, $per_page ) {
|
||||
$this->load_translation_jobs( $page, $per_page );
|
||||
|
||||
$metrics = array();
|
||||
$batches = array();
|
||||
if ( $this->translation_batches ) {
|
||||
krsort( $this->translation_batches );
|
||||
foreach ( $this->translation_batches as $id => $batch ) {
|
||||
$metrics[ $id ] = $batch->get_batch_meta_array();
|
||||
$batches[ $id ] = $batch;
|
||||
}
|
||||
}
|
||||
|
||||
$first_batch_metric = array_shift( $metrics );
|
||||
$first_batch_metric['display_from'] = $this->before_count + 1;
|
||||
$first_batch_metric['item_count'] = $this->first_count;
|
||||
array_unshift( $metrics, $first_batch_metric );
|
||||
$last_batch_metric = array_pop( $metrics );
|
||||
$last_batch_metric['display_to'] = $this->last_count - $this->after_count;
|
||||
$last_batch_metric['item_count'] = $this->last_count;
|
||||
$metrics[] = $last_batch_metric;
|
||||
|
||||
return array(
|
||||
'batches' => $batches,
|
||||
'metrics' => $metrics,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of jobs that meet the filter \WPML_Translation_Jobs_Collection::$filter in the database
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_count() {
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WPML_Translation_Job $job
|
||||
*/
|
||||
public function add_job( $job ) {
|
||||
$batch_id = $job->get_batch_id();
|
||||
$batch = array_key_exists( $batch_id, $this->translation_batches )
|
||||
? $this->translation_batches[ $batch_id ] : new WPML_Translation_Batch( $this->wpdb, $batch_id );
|
||||
|
||||
$batch->add_job( $job );
|
||||
$this->translation_batches[ $batch->get_id() ] = $batch;
|
||||
}
|
||||
|
||||
private function load_translation_jobs( $page, $per_page ) {
|
||||
$this->translation_batches = array();
|
||||
list( $jobs, $count, $before_count, $after_count, $first_count, $last_count ) = $this->get_jobs_table(
|
||||
$this->filter,
|
||||
array(
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
)
|
||||
);
|
||||
$this->count = $count;
|
||||
$this->after_count = $after_count;
|
||||
$this->before_count = $before_count;
|
||||
$this->first_count = $first_count;
|
||||
$this->last_count = $last_count;
|
||||
if ( is_array( $jobs ) ) {
|
||||
foreach ( $jobs as $job ) {
|
||||
$this->add_job( $job );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @param array $pagination_args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_jobs_table( array $args = array(), array $pagination_args = array(
|
||||
'page' => 1,
|
||||
'per_page' => 10,
|
||||
) ) {
|
||||
list( $data, $found_rows ) = $this->get_jobs_in_db( $args, $pagination_args );
|
||||
|
||||
if ( $data ) {
|
||||
$result = $this->calculate_batch_counts( $data, $found_rows, $pagination_args );
|
||||
} else {
|
||||
$result = $this->get_default_batch_counts( $found_rows );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $found_rows
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_batch_counts( $found_rows ) {
|
||||
return array( array(), $found_rows, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
private function get_jobs_in_db( array $args = array(), array $pagination_args = null ) {
|
||||
$where_jobs = $this->build_where_clause( $args );
|
||||
$jobs_table_union = $this->get_jobs_union_table_sql( $where_jobs, $args );
|
||||
|
||||
$only_ids_query = 'SELECT SQL_CALC_FOUND_ROWS ';
|
||||
$only_ids_query .= 'jobs.job_id, jobs.translator_id, jobs.job_id, jobs.batch_id, jobs.element_type_prefix ';
|
||||
$only_ids_query .= 'FROM ' . $jobs_table_union . ' ';
|
||||
|
||||
if ( $pagination_args ) {
|
||||
$only_ids_query .= 'LIMIT %d, %d';
|
||||
$prepare_args[] = max( ( $pagination_args['page'] - 1 ), 0 ) * $pagination_args['per_page'];
|
||||
$prepare_args[] = $pagination_args['per_page'];
|
||||
$only_ids_query = $this->wpdb->prepare( $only_ids_query, $prepare_args );
|
||||
}
|
||||
|
||||
$data = $this->wpdb->get_results( $only_ids_query );
|
||||
$found_rows = (int) $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
|
||||
|
||||
return array( $data, $found_rows );
|
||||
}
|
||||
|
||||
public function get_jobs( array $args = array() ) {
|
||||
list( $jobs_in_db, $found_rows ) = $this->get_jobs_in_db( $args );
|
||||
$this->count = $found_rows;
|
||||
return $this->plain_objects_to_job_instances( $jobs_in_db );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param int $count
|
||||
* @param array $pagination_args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function calculate_batch_counts( $data, $count, $pagination_args ) {
|
||||
$first_job = reset( $data );
|
||||
$last_job = end( $data );
|
||||
$first_batch = $first_job->batch_id;
|
||||
$last_batch = $last_job->batch_id;
|
||||
$count_select_from_snippet = 'SELECT COUNT(jdata.job_id) FROM (SELECT job_id, batch_id FROM ' . $this->jobs_union_table_sql;
|
||||
$count_where_snippet = ' ) AS jdata WHERE jdata.batch_id = %d';
|
||||
$before_count_query = $count_select_from_snippet . ' LIMIT %d' . $count_where_snippet;
|
||||
$page = $pagination_args['page'];
|
||||
$per_page = $pagination_args['per_page'];
|
||||
$count_before = $page > 1 ? $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
$before_count_query,
|
||||
array(
|
||||
( $page - 1 ) * $per_page,
|
||||
$first_batch,
|
||||
)
|
||||
)
|
||||
) : 0;
|
||||
$count_first = $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
$before_count_query,
|
||||
array( PHP_INT_MAX, $first_batch )
|
||||
)
|
||||
);
|
||||
$after_count_query = $count_select_from_snippet . ' LIMIT %d, %d' . $count_where_snippet;
|
||||
$count_after = $page * $per_page > $count ? 0 : $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
$after_count_query,
|
||||
array(
|
||||
$page * $per_page,
|
||||
PHP_INT_MAX,
|
||||
$last_batch,
|
||||
)
|
||||
)
|
||||
);
|
||||
$count_last = $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
$after_count_query,
|
||||
array( 0, PHP_INT_MAX, $last_batch )
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
$this->plain_objects_to_job_instances( $data ),
|
||||
$count,
|
||||
$count_before,
|
||||
$count_after,
|
||||
$count_first,
|
||||
$count_last,
|
||||
);
|
||||
}
|
||||
|
||||
private function get_jobs_union_table_sql( $where_jobs, $args ) {
|
||||
$union_sql = '';
|
||||
$sql_statements = array();
|
||||
|
||||
if ( $where_jobs ) {
|
||||
$sql_statements[] = "SELECT s.translator_id,
|
||||
j.job_id,
|
||||
IF(p.post_type IS NOT NULL, 'post', 'package') AS element_type_prefix,
|
||||
p.post_type AS post_type,
|
||||
s.batch_id
|
||||
FROM {$this->wpdb->prefix}icl_translation_status s
|
||||
JOIN {$this->wpdb->prefix}icl_translations t
|
||||
ON t.translation_id = s.translation_id
|
||||
JOIN {$this->wpdb->prefix}icl_translate_job j
|
||||
ON j.rid = s.rid
|
||||
AND j.revision IS NULL
|
||||
JOIN {$this->wpdb->prefix}icl_translations o
|
||||
ON o.trid = t.trid
|
||||
AND o.language_code = t.source_language_code
|
||||
JOIN " . apply_filters( 'wpml_post_translation_original_table', $this->wpdb->posts ) . " p
|
||||
ON o.element_id = p.ID
|
||||
AND ( o.element_type = CONCAT('post_', p.post_type) OR p.post_type IS NULL )
|
||||
JOIN {$this->wpdb->prefix}icl_translate tr_rows
|
||||
ON tr_rows.job_id = j.job_id
|
||||
AND tr_rows.field_type = 'original_id'
|
||||
AND tr_rows.field_data = o.element_id
|
||||
WHERE " . $where_jobs;
|
||||
}
|
||||
|
||||
$sql_statements = apply_filters( 'wpml_tm_jobs_union_table_sql', $sql_statements, $args );
|
||||
|
||||
if ( count( $sql_statements ) > 0 ) {
|
||||
$union_sql = '(' . implode( "\nUNION ALL\n", $sql_statements ) . ") jobs
|
||||
INNER JOIN {$this->wpdb->prefix}icl_translation_batches b
|
||||
ON b.id = jobs.batch_id
|
||||
ORDER BY jobs.batch_id DESC, jobs.element_type_prefix, jobs.job_id DESC";
|
||||
}
|
||||
|
||||
$this->jobs_union_table_sql = $union_sql;
|
||||
|
||||
return $union_sql;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?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;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* @package wpml-core
|
||||
* @subpackage wpml-core
|
||||
*/
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,943 @@
|
||||
<?php
|
||||
/**
|
||||
* @package wpml-core
|
||||
* @subpackage wpml-core
|
||||
*/
|
||||
|
||||
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 = false;
|
||||
}
|
||||
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 –', 'wpml-translation-management' );
|
||||
$text .= ' ' . sprintf( __( '<a href="%s">Send to translation »</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;
|
||||
|
||||
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;
|
||||
$post_title = $post->post_title;
|
||||
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 ) {
|
||||
$trid = $sitepress->get_element_trid( $id, 'post_' . $post_type );
|
||||
$job_id = $iclTranslationManagement->get_translation_job_id( $trid, $language_code );
|
||||
|
||||
if ( $job_id ) {
|
||||
/** @var stdClass $job_details */
|
||||
$job_details = $wpml_translation_job_factory->get_translation_job( $job_id );
|
||||
if ( ICL_TM_IN_PROGRESS === $job_details->status && ! $job_details->needs_update ) {
|
||||
self::$messages[] = array(
|
||||
'type' => 'update',
|
||||
'text' => sprintf(
|
||||
__(
|
||||
'Post "%1$s" will be ignored for %2$s, because translation is already in progress.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$post_title,
|
||||
$language_name
|
||||
),
|
||||
);
|
||||
$send_to_basket = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self::$messages[] = array(
|
||||
'type' => 'update',
|
||||
'text' => sprintf(
|
||||
__(
|
||||
'Post "%1$s" will be ignored for %2$s, because it is an original post.',
|
||||
'wpml-translation-management'
|
||||
),
|
||||
$post_title,
|
||||
$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 = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_generic_batch_name() {
|
||||
$batch_name = 'Manual Translations from ' . date( 'F \t\h\e jS\, Y' );
|
||||
|
||||
return $batch_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -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 = '&auto_resize=1';
|
||||
} else {
|
||||
$auto_resize = '';
|
||||
}
|
||||
|
||||
$unload_cb = isset( $unload_cb ) ? '&unload_cb=' . $unload_cb : '';
|
||||
|
||||
$url_glue = false !== strpos( $link, '?' ) ? '&' : '?';
|
||||
$link .= $url_glue . 'compact=1';
|
||||
|
||||
$nonce_snippet = '&_icl_nonce=' . wp_create_nonce( 'reminder_popup_nonce' );
|
||||
$action_and_nonce = 'admin.php?page=' . ICL_PLUGIN_FOLDER
|
||||
. "/menu/languages.php&icl_action=reminder_popup{$nonce_snippet}{$auto_resize}{$unload_cb}"
|
||||
. "&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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 } ?>
|
||||
">
|
||||
@@ -0,0 +1,510 @@
|
||||
<?php
|
||||
/**
|
||||
* @package wpml-core
|
||||
* @subpackage wpml-core
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
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 );
|
||||
|
||||
if ( $tp_job ) {
|
||||
return $tp_job->get_id();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 );
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
<?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.' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
/**
|
||||
* @package wpml-core
|
||||
* @subpackage wpml-core
|
||||
*/
|
||||
|
||||
use WPML\TM\TranslationProxy\Services\AuthorizationFactory;
|
||||
|
||||
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() {
|
||||
$tp_default_suid = false;
|
||||
|
||||
if ( defined( 'WPML_TP_DEFAULT_SUID' ) ) {
|
||||
$tp_default_suid = WPML_TP_DEFAULT_SUID;
|
||||
}
|
||||
|
||||
$preferred_translation_service = self::get_preferred_translation_service();
|
||||
if ( $preferred_translation_service && ! $tp_default_suid ) {
|
||||
$tp_default_suid = $preferred_translation_service;
|
||||
}
|
||||
|
||||
return $tp_default_suid;
|
||||
}
|
||||
|
||||
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 && ( ! self::service_requires_authentication() || 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 null !== $translation_service->custom_fields ? $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' );
|
||||
|
||||
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'] ) ) {
|
||||
$icl_translation_projects[ TranslationProxy_Project::generate_service_index( $service ) ]['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;
|
||||
}
|
||||
}
|
||||
@@ -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 $WPML_String_Translation
|
||||
* @var TranslationManagement $iclTranslationManagement
|
||||
*/
|
||||
global $WPML_String_Translation, $iclTranslationManagement;
|
||||
|
||||
$res = false;
|
||||
if ( empty( $cms_id ) ) { // it's a string
|
||||
if ( isset( $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;"/>
|
||||
<?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;"/>
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
<?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
|
||||
* @uses \WPML_Translation_Proxy_Basket_Networking::rollback_basket_commit rolls back the whole commit action in case of an error.
|
||||
*
|
||||
* @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 ) {
|
||||
$this->rollback_basket_commit( $batch->get_basket_name() );
|
||||
$result['message'] = '';
|
||||
$result['additional_messages'] = $error_messages;
|
||||
}
|
||||
|
||||
return array( $has_error, $result, $error_messages );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all remote jobs in the requested batch.
|
||||
*
|
||||
* @param string $posted_basket_name basket name in the currently handled request
|
||||
*
|
||||
* @uses \WPML_Translation_Batch::cancel_all_jobs
|
||||
*/
|
||||
function rollback_basket_commit( $posted_basket_name ) {
|
||||
$this->basket->get_basket( true );
|
||||
$basket_name = $this->basket->get_name();
|
||||
$basket_name = $basket_name ? $basket_name : $posted_basket_name;
|
||||
$batch = $this->basket->get_basket_batch( $basket_name );
|
||||
$batch->cancel_all_jobs();
|
||||
$batch->clear_batch_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
use function \WPML\Container\make;
|
||||
use \WPML\FP\Obj;
|
||||
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-translation-job-helper.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-translation-job-helper-with-api.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/wpml-translation-jobs-collection.class.php';
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-save-translation-data-action.class.php';
|
||||
|
||||
function wpml_tm_save_job_fields_from_post( $job_id ) {
|
||||
$job = new WPML_Post_Translation_Job( $job_id );
|
||||
$job->update_fields_from_post();
|
||||
}
|
||||
|
||||
add_action( 'wpml_save_job_fields_from_post', 'wpml_tm_save_job_fields_from_post', 10, 1 );
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param bool $redirect_after_saving
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function wpml_tm_save_data( array $data, $redirect_after_saving = true ) {
|
||||
$job_factory = wpml_tm_load_job_factory();
|
||||
$save_factory = new WPML_TM_Job_Action_Factory( $job_factory );
|
||||
$save_data_action = $save_factory->save_action( $data );
|
||||
$result = $save_data_action->save_translation();
|
||||
$redirect_target = $redirect_after_saving ? $save_data_action->get_redirect_target() : false;
|
||||
if ( (bool) $redirect_target === true ) {
|
||||
wp_redirect( $redirect_target );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
add_action( 'wpml_save_translation_data', 'wpml_tm_save_data', 10, 1 );
|
||||
|
||||
function wpml_tm_add_translation_job( $rid, $translator_id, $translation_package, $batch_options ) {
|
||||
|
||||
$helper = new WPML_TM_Action_Helper();
|
||||
$helper->add_translation_job( $rid, $translator_id, $translation_package, $batch_options );
|
||||
}
|
||||
|
||||
add_action( 'wpml_add_translation_job', 'wpml_tm_add_translation_job', 10, 4 );
|
||||
|
||||
require_once dirname( __FILE__ ) . '/wpml-private-filters.php';
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
*/
|
||||
function wpml_set_job_translated_term_values( $job_id ) {
|
||||
global $sitepress;
|
||||
|
||||
$delete = $sitepress->get_setting( 'tm_block_retranslating_terms' );
|
||||
$job_object = new WPML_Post_Translation_Job( $job_id );
|
||||
$job_object->load_terms_from_post_into_job( $delete );
|
||||
}
|
||||
|
||||
add_action( 'wpml_added_local_translation_job', 'wpml_set_job_translated_term_values' );
|
||||
|
||||
function wpml_tm_assign_translation_job( $job_id, $translator_id, $service, $type ) {
|
||||
global $wpml_translation_job_factory;
|
||||
|
||||
$job = $type === 'string'
|
||||
? new WPML_String_Translation_Job( $job_id )
|
||||
: $wpml_translation_job_factory->get_translation_job(
|
||||
$job_id,
|
||||
false,
|
||||
0,
|
||||
true
|
||||
);
|
||||
if ( $job ) {
|
||||
return $job->assign_to( $translator_id, $service );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
add_action( 'wpml_tm_assign_translation_job', 'wpml_tm_assign_translation_job', 10, 4 );
|
||||
|
||||
/**
|
||||
* Potentially handles the request to add strings to the translation basket,
|
||||
* triggered by String Translation.
|
||||
*/
|
||||
function wpml_tm_add_strings_to_basket() {
|
||||
if (
|
||||
Obj::prop( 'icl_st_action', $_POST ) === 'send_strings'
|
||||
&& wpml_is_action_authenticated( 'icl-string-translation' )
|
||||
) {
|
||||
WPML_TM_String_Basket_Request::send_to_basket( $_POST, [ TranslationProxy_Basket::class, 'add_strings_to_basket' ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_action( 'init', 'wpml_tm_add_strings_to_basket' );
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @param mixed $default
|
||||
* @param int $rid
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function wpml_filter_rid_to_untranslated_job_id( $default, $rid ) {
|
||||
require_once WPML_TM_PATH . '/inc/translation-jobs/helpers/wpml-update-post-translation-data-action.class.php';
|
||||
|
||||
$save_data_action = new WPML_TM_Update_Post_Translation_Data_Action();
|
||||
list( $job_id, $translated ) = $save_data_action->get_prev_job_data( $rid );
|
||||
|
||||
return $job_id && ! $translated ? $job_id : $default;
|
||||
}
|
||||
|
||||
add_filter( 'wpml_rid_to_untranslated_job_id', 'wpml_filter_rid_to_untranslated_job_id', 10, 2 );
|
||||
|
||||
|
||||
/**
|
||||
* @param int|object $element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wpml_tm_element_md5( $element ) {
|
||||
$helper = new WPML_TM_Action_Helper();
|
||||
|
||||
return $helper->post_md5( $element );
|
||||
}
|
||||
|
||||
add_filter( 'wpml_tm_element_md5', 'wpml_tm_element_md5', 10, 1 );
|
||||
|
||||
/**
|
||||
* Filters the possible target languages for creating a new post translation
|
||||
* on the post edit screen.
|
||||
*
|
||||
* @param string[] $allowed_langs
|
||||
* @param int $element_id
|
||||
* @param string $element_type_prefix
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
function wpml_tm_filter_post_target_langs(
|
||||
$allowed_langs,
|
||||
$element_id,
|
||||
$element_type_prefix
|
||||
) {
|
||||
global $wpml_tm_translation_status, $wpml_post_translations;
|
||||
$tm_records = wpml_tm_get_records();
|
||||
|
||||
$allowed_langs_filter = new WPML_TM_Post_Target_Lang_Filter(
|
||||
$tm_records,
|
||||
$wpml_tm_translation_status,
|
||||
$wpml_post_translations
|
||||
);
|
||||
|
||||
return $allowed_langs_filter->filter_target_langs(
|
||||
$allowed_langs,
|
||||
$element_id,
|
||||
$element_type_prefix
|
||||
);
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'wpml_allowed_target_langs',
|
||||
'wpml_tm_filter_post_target_langs',
|
||||
10,
|
||||
3
|
||||
);
|
||||
684
wp-content/plugins/wpml-translation-management/inc/wpml_zip.php
Normal file
684
wp-content/plugins/wpml-translation-management/inc/wpml_zip.php
Normal file
@@ -0,0 +1,684 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to create and manage a Zip file.
|
||||
*
|
||||
* Initially inspired by CreateZipFile by Rochak Chauhan www.rochakchauhan.com (http://www.phpclasses.org/browse/package/2322.html)
|
||||
* and
|
||||
* http://www.pkware.com/documents/casestudies/APPNOTE.TXT Zip file specification.
|
||||
*
|
||||
* License: GNU LGPL 2.1.
|
||||
*
|
||||
* @author A. Grandt <php@grandt.com>
|
||||
* @copyright 2009-2014 A. Grandt
|
||||
* @license GNU LGPL 2.1
|
||||
* @link http://www.phpclasses.org/package/6110
|
||||
* @link https://github.com/Grandt/PHPZip
|
||||
* @version 1.62
|
||||
*/
|
||||
class wpml_zip {
|
||||
const VERSION = 1.62;
|
||||
|
||||
const ZIP_LOCAL_FILE_HEADER = "\x50\x4b\x03\x04"; // Local file header signature
|
||||
const ZIP_CENTRAL_FILE_HEADER = "\x50\x4b\x01\x02"; // Central file header signature
|
||||
const ZIP_END_OF_CENTRAL_DIRECTORY = "\x50\x4b\x05\x06\x00\x00\x00\x00"; // end of Central directory record
|
||||
|
||||
const EXT_FILE_ATTR_DIR = 010173200020; // Permission 755 drwxr-xr-x = (((S_IFDIR | 0755) << 16) | S_DOS_D);
|
||||
const EXT_FILE_ATTR_FILE = 020151000040; // Permission 644 -rw-r--r-- = (((S_IFREG | 0644) << 16) | S_DOS_A);
|
||||
|
||||
const ATTR_VERSION_TO_EXTRACT = "\x14\x00"; // Version needed to extract
|
||||
const ATTR_MADE_BY_VERSION = "\x1E\x03"; // Made By Version
|
||||
|
||||
// UID 1000, GID 0
|
||||
const EXTRA_FIELD_NEW_UNIX_GUID = "\x75\x78\x0B\x00\x01\x04\xE8\x03\x00\x00\x04\x00\x00\x00\x00";
|
||||
|
||||
// Unix file types
|
||||
const S_IFIFO = 0010000; // named pipe (fifo)
|
||||
const S_IFCHR = 0020000; // character special
|
||||
const S_IFDIR = 0040000; // directory
|
||||
const S_IFBLK = 0060000; // block special
|
||||
const S_IFREG = 0100000; // regular
|
||||
const S_IFLNK = 0120000; // symbolic link
|
||||
const S_IFSOCK = 0140000; // socket
|
||||
|
||||
// setuid/setgid/sticky bits, the same as for chmod:
|
||||
|
||||
const S_ISUID = 0004000; // set user id on execution
|
||||
const S_ISGID = 0002000; // set group id on execution
|
||||
const S_ISTXT = 0001000; // sticky bit
|
||||
|
||||
// And of course, the other 12 bits are for the permissions, the same as for chmod:
|
||||
// When addding these up, you can also just write the permissions as a simgle octal number
|
||||
// ie. 0755. The leading 0 specifies octal notation.
|
||||
const S_IRWXU = 0000700; // RWX mask for owner
|
||||
const S_IRUSR = 0000400; // R for owner
|
||||
const S_IWUSR = 0000200; // W for owner
|
||||
const S_IXUSR = 0000100; // X for owner
|
||||
const S_IRWXG = 0000070; // RWX mask for group
|
||||
const S_IRGRP = 0000040; // R for group
|
||||
const S_IWGRP = 0000020; // W for group
|
||||
const S_IXGRP = 0000010; // X for group
|
||||
const S_IRWXO = 0000007; // RWX mask for other
|
||||
const S_IROTH = 0000004; // R for other
|
||||
const S_IWOTH = 0000002; // W for other
|
||||
const S_IXOTH = 0000001; // X for other
|
||||
const S_ISVTX = 0001000; // save swapped text even after use
|
||||
|
||||
// Filetype, sticky and permissions are added up, and shifted 16 bits left BEFORE adding the DOS flags.
|
||||
|
||||
// DOS file type flags, we really only use the S_DOS_D flag.
|
||||
|
||||
const S_DOS_A = 0000040; // DOS flag for Archive
|
||||
const S_DOS_D = 0000020; // DOS flag for Directory
|
||||
const S_DOS_V = 0000010; // DOS flag for Volume
|
||||
const S_DOS_S = 0000004; // DOS flag for System
|
||||
const S_DOS_H = 0000002; // DOS flag for Hidden
|
||||
const S_DOS_R = 0000001; // DOS flag for Read Only
|
||||
|
||||
private $zipMemoryThreshold = 1048576; // Autocreate tempfile if the zip data exceeds 1048576 bytes (1 MB)
|
||||
|
||||
private $zipData = null;
|
||||
private $zipFile = null;
|
||||
private $zipComment = null;
|
||||
private $cdRec = array(); // central directory
|
||||
private $offset = 0;
|
||||
private $isFinalized = false;
|
||||
private $addExtraField = true;
|
||||
|
||||
private $streamChunkSize = 65536;
|
||||
private $streamFilePath = null;
|
||||
private $streamTimestamp = null;
|
||||
private $streamFileComment = null;
|
||||
private $streamFile = null;
|
||||
private $streamData = null;
|
||||
private $streamFileLength = 0;
|
||||
private $streamExtFileAttr = null;
|
||||
/**
|
||||
* A custom temporary folder, or a callable that returns a custom temporary file.
|
||||
*
|
||||
* @var string|callable
|
||||
*/
|
||||
public static $temp = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param boolean $useZipFile Write temp zip data to tempFile? Default FALSE
|
||||
*/
|
||||
function __construct( $useZipFile = false ) {
|
||||
if ( $useZipFile ) {
|
||||
$this->zipFile = tmpfile();
|
||||
} else {
|
||||
$this->zipData = '';
|
||||
}
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
if ( is_resource( $this->zipFile ) ) {
|
||||
fclose( $this->zipFile );
|
||||
}
|
||||
$this->zipData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Zip archive comment.
|
||||
*
|
||||
* @param string $newComment New comment. NULL to clear.
|
||||
* @return bool $success
|
||||
*/
|
||||
public function setComment( $newComment = null ) {
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
$this->zipComment = $newComment;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set zip file to write zip data to.
|
||||
* This will cause all present and future data written to this class to be written to this file.
|
||||
* This can be used at any time, even after the Zip Archive have been finalized. Any previous file will be closed.
|
||||
* Warning: If the given file already exists, it will be overwritten.
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return bool $success
|
||||
*/
|
||||
public function setZipFile( $fileName ) {
|
||||
if ( is_file( $fileName ) ) {
|
||||
unlink( $fileName );
|
||||
}
|
||||
$fd = fopen( $fileName, 'x+b' );
|
||||
if ( is_resource( $this->zipFile ) ) {
|
||||
rewind( $this->zipFile );
|
||||
while ( ! feof( $this->zipFile ) ) {
|
||||
fwrite( $fd, fread( $this->zipFile, $this->streamChunkSize ) );
|
||||
}
|
||||
|
||||
fclose( $this->zipFile );
|
||||
} else {
|
||||
fwrite( $fd, $this->zipData );
|
||||
$this->zipData = null;
|
||||
}
|
||||
$this->zipFile = $fd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an empty directory entry to the zip archive.
|
||||
* Basically this is only used if an empty directory is added.
|
||||
*
|
||||
* @param string $directoryPath Directory Path and name to be added to the archive.
|
||||
* @param int $timestamp (Optional) Timestamp for the added directory, if omitted or set to 0, the current time will be used.
|
||||
* @param string $fileComment (Optional) Comment to be added to the archive for this directory. To use fileComment, timestamp must be given.
|
||||
* @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
|
||||
* @return bool $success
|
||||
*/
|
||||
public function addDirectory( $directoryPath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_DIR ) {
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
$directoryPath = str_replace( '\\', '/', $directoryPath );
|
||||
$directoryPath = rtrim( $directoryPath, '/' );
|
||||
|
||||
if ( strlen( $directoryPath ) > 0 ) {
|
||||
$this->buildZipEntry( $directoryPath . '/', $fileComment, "\x00\x00", "\x00\x00", $timestamp, "\x00\x00\x00\x00", 0, 0, $extFileAttr );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file to the archive at the specified location and file name.
|
||||
*
|
||||
* @param string $data File data.
|
||||
* @param string $filePath Filepath and name to be used in the archive.
|
||||
* @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
|
||||
* @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
|
||||
* @param bool $compress (Optional) Compress file, if set to FALSE the file will only be stored. Default TRUE.
|
||||
* @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
|
||||
* @return bool $success
|
||||
*/
|
||||
public function addFile( $data, $filePath, $timestamp = 0, $fileComment = null, $compress = true, $extFileAttr = self::EXT_FILE_ATTR_FILE ) {
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_resource( $data ) && get_resource_type( $data ) == 'stream' ) {
|
||||
$this->addLargeFile( $data, $filePath, $timestamp, $fileComment, $extFileAttr );
|
||||
return false;
|
||||
}
|
||||
|
||||
$gzData = '';
|
||||
$gzType = "\x08\x00"; // Compression type 8 = deflate
|
||||
$gpFlags = "\x00\x00"; // General Purpose bit flags for compression type 8 it is: 0=Normal, 1=Maximum, 2=Fast, 3=super fast compression.
|
||||
$dataLength = strlen( $data );
|
||||
$fileCRC32 = pack( 'V', crc32( $data ) );
|
||||
|
||||
if ( $compress ) {
|
||||
$gzTmp = gzcompress( $data );
|
||||
$gzData = substr( substr( $gzTmp, 0, strlen( $gzTmp ) - 4 ), 2 ); // gzcompress adds a 2 byte header and 4 byte CRC we can't use.
|
||||
// The 2 byte header does contain useful data, though in this case the 2 parameters we'd be interrested in will always be 8 for compression type, and 2 for General purpose flag.
|
||||
$gzLength = strlen( $gzData );
|
||||
} else {
|
||||
$gzLength = $dataLength;
|
||||
}
|
||||
|
||||
if ( $gzLength >= $dataLength ) {
|
||||
$gzLength = $dataLength;
|
||||
$gzData = $data;
|
||||
$gzType = "\x00\x00"; // Compression type 0 = stored
|
||||
$gpFlags = "\x00\x00"; // Compression type 0 = stored
|
||||
}
|
||||
|
||||
if ( ! is_resource( $this->zipFile ) && ( $this->offset + $gzLength ) > $this->zipMemoryThreshold ) {
|
||||
$this->zipflush();
|
||||
}
|
||||
|
||||
$this->buildZipEntry( $filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr );
|
||||
|
||||
$this->zipwrite( $gzData );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file to the archive at the specified location and file name.
|
||||
*
|
||||
* @param string $dataFile File name/path.
|
||||
* @param string $filePath Filepath and name to be used in the archive.
|
||||
* @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
|
||||
* @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
|
||||
* @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
|
||||
* @return bool $success
|
||||
*/
|
||||
public function addLargeFile( $dataFile, $filePath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_FILE ) {
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_string( $dataFile ) && is_file( $dataFile ) ) {
|
||||
$this->processFile( $dataFile, $filePath, $timestamp, $fileComment, $extFileAttr );
|
||||
} elseif ( is_resource( $dataFile ) && get_resource_type( $dataFile ) == 'stream' ) {
|
||||
$fh = $dataFile;
|
||||
$this->openStream( $filePath, $timestamp, $fileComment, $extFileAttr );
|
||||
|
||||
while ( ! feof( $fh ) ) {
|
||||
$this->addStreamData( fread( $fh, $this->streamChunkSize ) );
|
||||
}
|
||||
$this->closeStream();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stream to be used for large entries.
|
||||
*
|
||||
* @param string $filePath Filepath and name to be used in the archive.
|
||||
* @param int $timestamp (Optional) Timestamp for the added file, if omitted or set to 0, the current time will be used.
|
||||
* @param string $fileComment (Optional) Comment to be added to the archive for this file. To use fileComment, timestamp must be given.
|
||||
* @param int $extFileAttr (Optional) The external file reference, use generateExtAttr to generate this.
|
||||
* @throws Exception Throws an exception in case of errors
|
||||
* @return bool $success
|
||||
*/
|
||||
public function openStream( $filePath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_FILE ) {
|
||||
if ( ! function_exists( 'sys_get_temp_dir' ) ) {
|
||||
throw new Exception( 'Zip ' . self::VERSION . ' requires PHP version 5.2.1 or above if large files are used.' );
|
||||
}
|
||||
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->zipflush();
|
||||
|
||||
if ( strlen( $this->streamFilePath ) > 0 ) {
|
||||
$this->closeStream();
|
||||
}
|
||||
|
||||
$this->streamFile = self::getTemporaryFile();
|
||||
$this->streamData = fopen( $this->streamFile, 'wb' );
|
||||
$this->streamFilePath = $filePath;
|
||||
$this->streamTimestamp = $timestamp;
|
||||
$this->streamFileComment = $fileComment;
|
||||
$this->streamFileLength = 0;
|
||||
$this->streamExtFileAttr = $extFileAttr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data to the open stream.
|
||||
*
|
||||
* @param string $data
|
||||
* @throws Exception Throws an exception in case of errors
|
||||
* @return mixed length in bytes added or FALSE if the archive is finalized or there are no open stream.
|
||||
*/
|
||||
public function addStreamData( $data ) {
|
||||
if ( $this->isFinalized || strlen( $this->streamFilePath ) == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$length = fwrite( $this->streamData, $data, strlen( $data ) );
|
||||
if ( $length != strlen( $data ) ) {
|
||||
throw new Exception( 'File IO: Error writing; Length mismatch: Expected ' . strlen( $data ) . ' bytes, wrote ' . ( $length === false ? 'NONE!' : $length ) );
|
||||
}
|
||||
$this->streamFileLength += $length;
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current stream.
|
||||
*
|
||||
* @return bool $success
|
||||
*/
|
||||
public function closeStream() {
|
||||
if ( $this->isFinalized || strlen( $this->streamFilePath ) == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fflush( $this->streamData );
|
||||
fclose( $this->streamData );
|
||||
|
||||
$this->processFile( $this->streamFile, $this->streamFilePath, $this->streamTimestamp, $this->streamFileComment, $this->streamExtFileAttr );
|
||||
|
||||
$this->streamData = null;
|
||||
$this->streamFilePath = null;
|
||||
$this->streamTimestamp = null;
|
||||
$this->streamFileComment = null;
|
||||
$this->streamFileLength = 0;
|
||||
$this->streamExtFileAttr = null;
|
||||
|
||||
// Windows is a little slow at times, so a millisecond later, we can unlink this.
|
||||
unlink( $this->streamFile );
|
||||
|
||||
$this->streamFile = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processFile( $dataFile, $filePath, $timestamp = 0, $fileComment = null, $extFileAttr = self::EXT_FILE_ATTR_FILE ) {
|
||||
if ( $this->isFinalized ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tempzip = self::getTemporaryFile();
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ( $zip->open( $tempzip ) === true ) {
|
||||
$zip->addFile( $dataFile, 'file' );
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
$file_handle = fopen( $tempzip, 'rb' );
|
||||
$stats = fstat( $file_handle );
|
||||
$eof = $stats['size'] - 72;
|
||||
|
||||
fseek( $file_handle, 6 );
|
||||
|
||||
$gpFlags = fread( $file_handle, 2 );
|
||||
$gzType = fread( $file_handle, 2 );
|
||||
fread( $file_handle, 4 );
|
||||
$fileCRC32 = fread( $file_handle, 4 );
|
||||
$v = unpack( 'Vval', fread( $file_handle, 4 ) );
|
||||
$gzLength = $v['val'];
|
||||
$v = unpack( 'Vval', fread( $file_handle, 4 ) );
|
||||
$dataLength = $v['val'];
|
||||
|
||||
$this->buildZipEntry( $filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr );
|
||||
|
||||
fseek( $file_handle, 34 );
|
||||
$pos = 34;
|
||||
|
||||
while ( ! feof( $file_handle ) && $pos < $eof ) {
|
||||
$datalen = $this->streamChunkSize;
|
||||
if ( $pos + $this->streamChunkSize > $eof ) {
|
||||
$datalen = $eof - $pos;
|
||||
}
|
||||
$data = fread( $file_handle, $datalen );
|
||||
$pos += $datalen;
|
||||
|
||||
$this->zipwrite( $data );
|
||||
}
|
||||
|
||||
fclose( $file_handle );
|
||||
|
||||
unlink( $tempzip );
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the archive.
|
||||
* A closed archive can no longer have new files added to it.
|
||||
*
|
||||
* @return bool $success
|
||||
*/
|
||||
public function finalize() {
|
||||
if ( ! $this->isFinalized ) {
|
||||
if ( strlen( $this->streamFilePath ) > 0 ) {
|
||||
$this->closeStream();
|
||||
}
|
||||
$cd = implode( '', $this->cdRec );
|
||||
|
||||
$cdRecSize = pack( 'v', sizeof( $this->cdRec ) );
|
||||
$cdRec = $cd . self::ZIP_END_OF_CENTRAL_DIRECTORY
|
||||
. $cdRecSize . $cdRecSize
|
||||
. pack( 'VV', strlen( $cd ), $this->offset );
|
||||
if ( ! empty( $this->zipComment ) ) {
|
||||
$cdRec .= pack( 'v', strlen( $this->zipComment ) ) . $this->zipComment;
|
||||
} else {
|
||||
$cdRec .= "\x00\x00";
|
||||
}
|
||||
|
||||
$this->zipwrite( $cdRec );
|
||||
|
||||
$this->isFinalized = true;
|
||||
$this->cdRec = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the zip file contents
|
||||
* If the zip haven't been finalized yet, this will cause it to become finalized
|
||||
*
|
||||
* @return zip data
|
||||
*/
|
||||
public function getZipData() {
|
||||
if ( ! $this->isFinalized ) {
|
||||
$this->finalize();
|
||||
}
|
||||
if ( ! is_resource( $this->zipFile ) ) {
|
||||
return $this->zipData;
|
||||
} else {
|
||||
rewind( $this->zipFile );
|
||||
$filestat = fstat( $this->zipFile );
|
||||
return fread( $this->zipFile, $filestat['size'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the archive as a zip download
|
||||
*
|
||||
* @param String $fileName The name of the Zip archive, in ISO-8859-1 (or ASCII) encoding, ie. "archive.zip". Optional, defaults to NULL, which means that no ISO-8859-1 encoded file name will be specified.
|
||||
* @param String $contentType Content mime type. Optional, defaults to "application/zip".
|
||||
* @param String $utf8FileName The name of the Zip archive, in UTF-8 encoding. Optional, defaults to NULL, which means that no UTF-8 encoded file name will be specified.
|
||||
* @param bool $inline Use Content-Disposition with "inline" instead of "attached". Optional, defaults to FALSE.
|
||||
* @throws Exception Throws an exception in case of errors
|
||||
* @return bool Always returns true (for backward compatibility).
|
||||
*/
|
||||
function sendZip( $fileName = null, $contentType = 'application/zip', $utf8FileName = null, $inline = false ) {
|
||||
if ( ! $this->isFinalized ) {
|
||||
$this->finalize();
|
||||
}
|
||||
$headerFile = null;
|
||||
$headerLine = null;
|
||||
if ( headers_sent( $headerFile, $headerLine ) ) {
|
||||
throw new Exception( "Unable to send file '$fileName'. Headers have already been sent from '$headerFile' in line $headerLine" );
|
||||
}
|
||||
if ( ob_get_contents() !== false && strlen( ob_get_contents() ) ) {
|
||||
throw new Exception( "Unable to send file '$fileName'. Output buffer contains the following text (typically warnings or errors):\n" . ob_get_contents() );
|
||||
}
|
||||
if ( @ini_get( 'zlib.output_compression' ) ) {
|
||||
@ini_set( 'zlib.output_compression', 'Off' );
|
||||
}
|
||||
header( 'Pragma: public' );
|
||||
header( 'Last-Modified: ' . @gmdate( 'D, d M Y H:i:s T' ) );
|
||||
header( 'Expires: 0' );
|
||||
header( 'Accept-Ranges: bytes' );
|
||||
header( 'Connection: close' );
|
||||
header( 'Content-Type: ' . $contentType );
|
||||
$cd = 'Content-Disposition: ';
|
||||
if ( $inline ) {
|
||||
$cd .= 'inline';
|
||||
} else {
|
||||
$cd .= 'attached';
|
||||
}
|
||||
if ( $fileName ) {
|
||||
$cd .= '; filename="' . $fileName . '"';
|
||||
}
|
||||
if ( $utf8FileName ) {
|
||||
$cd .= "; filename*=UTF-8''" . rawurlencode( $utf8FileName );
|
||||
}
|
||||
header( $cd );
|
||||
header( 'Content-Length: ' . $this->getArchiveSize() );
|
||||
if ( ! is_resource( $this->zipFile ) ) {
|
||||
echo $this->zipData;
|
||||
} else {
|
||||
rewind( $this->zipFile );
|
||||
while ( ! feof( $this->zipFile ) ) {
|
||||
echo fread( $this->zipFile, $this->streamChunkSize );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current size of the archive
|
||||
*
|
||||
* @return $size Size of the archive
|
||||
*/
|
||||
public function getArchiveSize() {
|
||||
if ( ! is_resource( $this->zipFile ) ) {
|
||||
return strlen( $this->zipData );
|
||||
}
|
||||
$filestat = fstat( $this->zipFile );
|
||||
|
||||
return $filestat['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the 2 byte dostime used in the zip entries.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return 2-byte encoded DOS Date
|
||||
*/
|
||||
private function getDosTime( $timestamp = 0 ) {
|
||||
$timestamp = (int) $timestamp;
|
||||
$oldTZ = @date_default_timezone_get();
|
||||
date_default_timezone_set( 'UTC' );
|
||||
$date = ( $timestamp == 0 ? getdate() : getdate( $timestamp ) );
|
||||
date_default_timezone_set( $oldTZ );
|
||||
if ( $date['year'] >= 1980 ) {
|
||||
return pack(
|
||||
'V',
|
||||
( ( $date['mday'] + ( $date['mon'] << 5 ) + ( ( $date['year'] - 1980 ) << 9 ) ) << 16 ) |
|
||||
( ( $date['seconds'] >> 1 ) + ( $date['minutes'] << 5 ) + ( $date['hours'] << 11 ) )
|
||||
);
|
||||
}
|
||||
return "\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Zip file structures
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param string $fileComment
|
||||
* @param string $gpFlags
|
||||
* @param string $gzType
|
||||
* @param int $timestamp
|
||||
* @param string $fileCRC32
|
||||
* @param int $gzLength
|
||||
* @param int $dataLength
|
||||
* @param int $extFileAttr Use self::EXT_FILE_ATTR_FILE for files, self::EXT_FILE_ATTR_DIR for Directories.
|
||||
*/
|
||||
private function buildZipEntry( $filePath, $fileComment, $gpFlags, $gzType, $timestamp, $fileCRC32, $gzLength, $dataLength, $extFileAttr ) {
|
||||
$filePath = str_replace( '\\', '/', $filePath );
|
||||
$fileCommentLength = ( empty( $fileComment ) ? 0 : strlen( $fileComment ) );
|
||||
$timestamp = (int) $timestamp;
|
||||
$timestamp = ( $timestamp == 0 ? time() : $timestamp );
|
||||
|
||||
$dosTime = $this->getDosTime( $timestamp );
|
||||
$tsPack = pack( 'V', $timestamp );
|
||||
|
||||
if ( ! isset( $gpFlags ) || strlen( $gpFlags ) != 2 ) { //@phpstan-ignore-line
|
||||
$gpFlags = "\x00\x00";
|
||||
}
|
||||
|
||||
$isFileUTF8 = mb_check_encoding( $filePath, 'UTF-8' ) && ! mb_check_encoding( $filePath, 'ASCII' );
|
||||
$isCommentUTF8 = ! empty( $fileComment ) && mb_check_encoding( $fileComment, 'UTF-8' ) && ! mb_check_encoding( $fileComment, 'ASCII' );
|
||||
|
||||
$localExtraField = '';
|
||||
$centralExtraField = '';
|
||||
|
||||
if ( $this->addExtraField ) {
|
||||
$localExtraField .= "\x55\x54\x09\x00\x03" . $tsPack . $tsPack . self::EXTRA_FIELD_NEW_UNIX_GUID;
|
||||
$centralExtraField .= "\x55\x54\x05\x00\x03" . $tsPack . self::EXTRA_FIELD_NEW_UNIX_GUID;
|
||||
}
|
||||
|
||||
if ( $isFileUTF8 || $isCommentUTF8 ) {
|
||||
$flag = 0;
|
||||
$gpFlagsV = unpack( 'vflags', $gpFlags );
|
||||
if ( isset( $gpFlagsV['flags'] ) ) {
|
||||
$flag = $gpFlagsV['flags'];
|
||||
}
|
||||
$gpFlags = pack( 'v', $flag | ( 1 << 11 ) );
|
||||
|
||||
if ( $isFileUTF8 ) {
|
||||
$utfPathExtraField = "\x75\x70"
|
||||
. pack( 'v', ( 5 + strlen( $filePath ) ) )
|
||||
. "\x01"
|
||||
. pack( 'V', crc32( $filePath ) )
|
||||
. $filePath;
|
||||
|
||||
$localExtraField .= $utfPathExtraField;
|
||||
$centralExtraField .= $utfPathExtraField;
|
||||
}
|
||||
if ( $isCommentUTF8 ) {
|
||||
$centralExtraField .= "\x75\x63" // utf8 encoded file comment extra field
|
||||
. pack( 'v', ( 5 + strlen( $fileComment ) ) )
|
||||
. "\x01"
|
||||
. pack( 'V', crc32( $fileComment ) )
|
||||
. $fileComment;
|
||||
}
|
||||
}
|
||||
|
||||
$header = $gpFlags . $gzType . $dosTime . $fileCRC32
|
||||
. pack( 'VVv', $gzLength, $dataLength, strlen( $filePath ) ); // File name length
|
||||
|
||||
$zipEntry = self::ZIP_LOCAL_FILE_HEADER
|
||||
. self::ATTR_VERSION_TO_EXTRACT
|
||||
. $header
|
||||
. pack( 'v', strlen( $localExtraField ) ) // Extra field length
|
||||
. $filePath // FileName
|
||||
. $localExtraField; // Extra fields
|
||||
|
||||
$this->zipwrite( $zipEntry );
|
||||
|
||||
$cdEntry = self::ZIP_CENTRAL_FILE_HEADER
|
||||
. self::ATTR_MADE_BY_VERSION
|
||||
. ( $dataLength === 0 ? "\x0A\x00" : self::ATTR_VERSION_TO_EXTRACT )
|
||||
. $header
|
||||
. pack( 'v', strlen( $centralExtraField ) ) // Extra field length
|
||||
. pack( 'v', $fileCommentLength ) // File comment length
|
||||
. "\x00\x00" // Disk number start
|
||||
. "\x00\x00" // internal file attributes
|
||||
. pack( 'V', $extFileAttr ) // External file attributes
|
||||
. pack( 'V', $this->offset ) // Relative offset of local header
|
||||
. $filePath // FileName
|
||||
. $centralExtraField; // Extra fields
|
||||
|
||||
if ( ! empty( $fileComment ) ) {
|
||||
$cdEntry .= $fileComment; // Comment
|
||||
}
|
||||
|
||||
$this->cdRec[] = $cdEntry;
|
||||
$this->offset += strlen( $zipEntry ) + $gzLength;
|
||||
}
|
||||
|
||||
private function zipwrite( $data ) {
|
||||
if ( ! is_resource( $this->zipFile ) ) {
|
||||
$this->zipData .= $data;
|
||||
} else {
|
||||
fwrite( $this->zipFile, $data );
|
||||
fflush( $this->zipFile );
|
||||
}
|
||||
}
|
||||
|
||||
private function zipflush() {
|
||||
if ( ! is_resource( $this->zipFile ) ) {
|
||||
$this->zipFile = tmpfile();
|
||||
fwrite( $this->zipFile, $this->zipData );
|
||||
$this->zipData = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to a temporary file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getTemporaryFile() {
|
||||
if ( is_callable( self::$temp ) ) {
|
||||
$temporaryFile = @call_user_func( self::$temp );
|
||||
if ( is_string( $temporaryFile ) && strlen( $temporaryFile ) && is_writable( $temporaryFile ) ) {
|
||||
return $temporaryFile;
|
||||
}
|
||||
}
|
||||
$temporaryDirectory = ( is_string( self::$temp ) && strlen( self::$temp ) ) ? self::$temp : sys_get_temp_dir();
|
||||
return tempnam( $temporaryDirectory, 'Zip' );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user