first commit
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* WPML_Element_Translation Class
|
||||
*
|
||||
* @package wpml-core
|
||||
* @abstract
|
||||
*/
|
||||
abstract class WPML_Element_Translation extends WPML_WPDB_User {
|
||||
/** @var array[] $element_data */
|
||||
protected $element_data = [];
|
||||
|
||||
/** @var array[] $translations */
|
||||
protected $translations = [];
|
||||
|
||||
/** @var array[] $trid_groups */
|
||||
protected $trid_groups = [];
|
||||
|
||||
/** @var array[] $trid_groups */
|
||||
protected $translation_ids_element = [];
|
||||
|
||||
/** @var int $type_prefix_length */
|
||||
private $type_prefix_length;
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( &$wpdb ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->type_prefix_length = strlen( $this->get_type_prefix() );
|
||||
}
|
||||
|
||||
abstract protected function get_element_join();
|
||||
abstract protected function get_type_prefix();
|
||||
|
||||
/**
|
||||
* Clears the cached translations.
|
||||
*/
|
||||
public function reload() {
|
||||
$this->element_data = [];
|
||||
$this->translations = [];
|
||||
$this->trid_groups = [];
|
||||
$this->translation_ids_element = [];
|
||||
|
||||
}
|
||||
|
||||
public function get_element_trid( $element_id ) {
|
||||
return $this->maybe_populate_cache( $element_id )
|
||||
? $this->element_data[ $element_id ]['trid'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
* @param string $lang
|
||||
* @param bool|false $original_fallback if true will return input $element_id if no translation is found
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
public function element_id_in( $element_id, $lang, $original_fallback = false ) {
|
||||
$result = ( $original_fallback ? (int) $element_id : null );
|
||||
if ( $this->maybe_populate_cache( $element_id ) && isset( $this->translations[ $element_id ][ $lang ] ) ) {
|
||||
$result = (int) $this->translations[ $element_id ][ $lang ];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
* @param bool $root if true gets the root element of the trid which itself
|
||||
* has no original. Otherwise returns the direct original of the given
|
||||
* element_id.
|
||||
*
|
||||
* @return int|null null if the element has no original
|
||||
*/
|
||||
public function get_original_element( $element_id, $root = false ) {
|
||||
$element_id = (int) $element_id;
|
||||
$source_lang = $this->maybe_populate_cache( $element_id )
|
||||
? $this->element_data[ $element_id ]['source_lang'] : null;
|
||||
|
||||
if ( null === $source_lang ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! $root && isset( $this->translations[ $element_id ][ $source_lang ] ) ) {
|
||||
return (int) $this->translations[ $element_id ][ $source_lang ];
|
||||
}
|
||||
|
||||
if ( $root ) {
|
||||
foreach ( $this->translations[ $element_id ] as $trans_id ) {
|
||||
if ( ! $this->element_data[ $trans_id ]['source_lang'] ) {
|
||||
return (int) $trans_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function get_element_id( $lang, $trid ) {
|
||||
$this->maybe_populate_cache( false, $trid );
|
||||
|
||||
return isset( $this->trid_groups [ $trid ][ $lang ] ) ? $this->trid_groups [ $trid ][ $lang ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_element_lang_code( $element_id ) {
|
||||
$result = null;
|
||||
|
||||
if ( $this->maybe_populate_cache( $element_id ) ) {
|
||||
$result = $this->element_data[ $element_id ]['lang'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
* @param string $output
|
||||
*
|
||||
* @return array|null|stdClass
|
||||
*/
|
||||
public function get_element_language_details( $element_id, $output = OBJECT ) {
|
||||
$result = null;
|
||||
if ( $element_id && $this->maybe_populate_cache( $element_id ) ) {
|
||||
$result = new stdClass();
|
||||
$result->element_id = $element_id;
|
||||
$result->trid = $this->element_data[ $element_id ]['trid'];
|
||||
$result->language_code = $this->element_data[ $element_id ]['lang'];
|
||||
$result->source_language_code = $this->element_data[ $element_id ]['source_lang'];
|
||||
}
|
||||
|
||||
if ( $output == ARRAY_A ) {
|
||||
return $result ? get_object_vars( $result ) : null;
|
||||
} elseif ( $output == ARRAY_N ) {
|
||||
return $result ? array_values( get_object_vars( $result ) ) : null;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_source_lang_code( $element_id ) {
|
||||
|
||||
return $this->maybe_populate_cache( $element_id )
|
||||
? $this->element_data[ $element_id ]['source_lang'] : null;
|
||||
}
|
||||
|
||||
public function get_type( $element_id ) {
|
||||
return $this->maybe_populate_cache( $element_id ) ? $this->element_data[ $element_id ]['type'] : null;
|
||||
}
|
||||
|
||||
public function get_source_lang_from_translation_id( $translation_id ) {
|
||||
$lang = array(
|
||||
'code' => null,
|
||||
'found' => false,
|
||||
);
|
||||
$element_id = $this->get_element_from_translation_id( $translation_id );
|
||||
if ( $element_id ) {
|
||||
$lang['code'] = $this->get_source_lang_code( $element_id );
|
||||
$lang['found'] = true;
|
||||
}
|
||||
|
||||
return $lang;
|
||||
|
||||
}
|
||||
|
||||
public function get_translation_id( $element_id ) {
|
||||
return $this->maybe_populate_cache( $element_id )
|
||||
? $this->element_data[ $element_id ]['translation_id'] : null;
|
||||
}
|
||||
|
||||
public function get_translations_ids() {
|
||||
$translation_ids = array();
|
||||
foreach ( $this->element_data as $data ) {
|
||||
$translation_ids[] = $data['translation_id'];
|
||||
}
|
||||
|
||||
return $translation_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $element_id
|
||||
* @param int|false $trid
|
||||
* @param bool $actual_translations_only
|
||||
*
|
||||
* @return array<int,int>
|
||||
*/
|
||||
public function get_element_translations( $element_id, $trid = false, $actual_translations_only = false ) {
|
||||
$valid_element = $this->maybe_populate_cache( $element_id, $trid );
|
||||
|
||||
if ( $element_id ) {
|
||||
$res = $valid_element
|
||||
? ( $actual_translations_only
|
||||
? $this->filter_for_actual_trans( $element_id ) : $this->translations[ $element_id ] ) : array();
|
||||
} elseif ( $trid ) {
|
||||
$res = isset( $this->trid_groups[ $trid ] ) ? $this->trid_groups[ $trid ] : array();
|
||||
}
|
||||
|
||||
return isset( $res ) ? $res : array();
|
||||
}
|
||||
|
||||
public function get_element_from_translation_id( $translation_id ) {
|
||||
return isset( $this->translation_ids_element[ $translation_id ] ) ? $this->translation_ids_element[ $translation_id ] : null;
|
||||
}
|
||||
|
||||
public function get_trid_from_translation_id( $translation_id ) {
|
||||
$trid = null;
|
||||
$element_id = $this->get_element_from_translation_id( $translation_id );
|
||||
if ( $element_id ) {
|
||||
$trid = $this->get_element_trid( $element_id );
|
||||
}
|
||||
|
||||
return $trid;
|
||||
}
|
||||
|
||||
public function get_trids() {
|
||||
return array_keys( $this->trid_groups );
|
||||
}
|
||||
|
||||
public function prefetch_ids( $element_ids ) {
|
||||
$element_ids = (array) $element_ids;
|
||||
$element_ids = array_diff( $element_ids, array_keys( $this->element_data ) );
|
||||
if ( (bool) $element_ids === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trid_snippet = ' tridt.element_id IN (' . wpml_prepare_in( $element_ids, '%d' ) . ')';
|
||||
$sql = $this->build_sql( $trid_snippet );
|
||||
$elements = $this->wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
$this->group_and_populate_cache( $elements );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $trid_snippet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function build_sql( $trid_snippet ) {
|
||||
|
||||
return 'SELECT wpml_translations.translation_id, wpml_translations.element_id, wpml_translations.language_code, wpml_translations.source_language_code, wpml_translations.trid, wpml_translations.element_type
|
||||
' . $this->get_element_join() . "
|
||||
JOIN {$this->wpdb->prefix}icl_translations tridt
|
||||
ON tridt.element_type = wpml_translations.element_type
|
||||
AND tridt.trid = wpml_translations.trid
|
||||
WHERE {$trid_snippet}";
|
||||
}
|
||||
|
||||
private function maybe_populate_cache( $element_id, $trid = false ) {
|
||||
if ( ! $element_id && ! $trid ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! $element_id && isset( $this->trid_groups [ $trid ] ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( ! $element_id || ! isset( $this->translations[ $element_id ] ) ) {
|
||||
if ( ! $element_id ) {
|
||||
$trid_snippet = $this->wpdb->prepare( ' tridt.trid = %d ', $trid );
|
||||
} else {
|
||||
$trid_snippet = $this->wpdb->prepare(
|
||||
' tridt.trid = (SELECT trid ' . $this->get_element_join() . ' WHERE element_id = %d LIMIT 1)',
|
||||
$element_id
|
||||
);
|
||||
}
|
||||
$sql = $this->build_sql( $trid_snippet );
|
||||
$elements = $this->wpdb->get_results( $sql, ARRAY_A );
|
||||
$this->populate_cache( $elements );
|
||||
if ( $element_id && ! isset( $this->translations[ $element_id ] ) ) {
|
||||
$this->translations[ $element_id ] = array();
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $this->translations[ $element_id ] );
|
||||
}
|
||||
|
||||
private function group_and_populate_cache( $elements ) {
|
||||
$trids = array();
|
||||
foreach ( $elements as $element ) {
|
||||
$trid = $element['trid'];
|
||||
if ( ! isset( $trids[ $trid ] ) ) {
|
||||
$trids[ $trid ] = array();
|
||||
}
|
||||
$trids[ $trid ][] = $element;
|
||||
}
|
||||
foreach ( $trids as $trid_group ) {
|
||||
$this->populate_cache( $trid_group );
|
||||
}
|
||||
}
|
||||
|
||||
private function populate_cache( $elements ) {
|
||||
if ( ! $elements ) {
|
||||
return;
|
||||
}
|
||||
$element_ids = array();
|
||||
foreach ( $elements as $element ) {
|
||||
$element_id = $element['element_id'];
|
||||
$language_code = $element['language_code'];
|
||||
$element_ids[ $language_code ] = $element_id;
|
||||
|
||||
$this->element_data[ $element_id ] = array(
|
||||
'translation_id' => $element['translation_id'],
|
||||
'trid' => $element['trid'],
|
||||
'lang' => $language_code,
|
||||
'source_lang' => $element['source_language_code'],
|
||||
'type' => substr( $element['element_type'], $this->type_prefix_length ),
|
||||
);
|
||||
|
||||
$this->translation_ids_element[ $element['translation_id'] ] = $element_id;
|
||||
}
|
||||
foreach ( $element_ids as $element_id ) {
|
||||
$trid = $this->element_data[ $element_id ]['trid'];
|
||||
$this->trid_groups[ $trid ] = $element_ids;
|
||||
$this->translations[ $element_id ] = &$this->trid_groups[ $trid ];
|
||||
}
|
||||
}
|
||||
|
||||
private function filter_for_actual_trans( $element_id ) {
|
||||
$res = $this->translations[ $element_id ];
|
||||
foreach ( $res as $lang => $element ) {
|
||||
if ( $this->element_data[ $element ]['source_lang'] !== $this->element_data[ $element_id ]['lang'] ) {
|
||||
unset( $res[ $lang ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_a_duplicate( $post_id ) {
|
||||
return (bool) get_post_meta( $post_id, '_icl_lang_duplicate_of', true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_WPDB_And_SP_User
|
||||
*/
|
||||
abstract class WPML_Full_PT_API extends WPML_WPDB_And_SP_User {
|
||||
|
||||
/** @var WPML_Post_Translation $post_translations */
|
||||
protected $post_translations;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
* @param SitePress $sitepress
|
||||
* @param WPML_Post_Translation $post_translations
|
||||
*/
|
||||
public function __construct( &$wpdb, &$sitepress, &$post_translations ) {
|
||||
parent::__construct( $wpdb, $sitepress );
|
||||
$this->post_translations = &$post_translations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
class WPML_Full_Translation_API extends WPML_Full_PT_API {
|
||||
|
||||
/** @var WPML_Term_Translation $term_translations */
|
||||
protected $term_translations;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
* @param wpdb $wpdb
|
||||
* @param WPML_Post_Translation $post_translations
|
||||
* @param WPML_Term_Translation $term_translations
|
||||
*/
|
||||
function __construct( &$sitepress, &$wpdb, &$post_translations, &$term_translations ) {
|
||||
parent::__construct( $wpdb, $sitepress, $post_translations );
|
||||
$this->term_translations = &$term_translations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_Hierarchy_Sync extends WPML_WPDB_User {
|
||||
|
||||
const CACHE_GROUP = __CLASS__;
|
||||
|
||||
protected $original_elements_table_alias = 'org';
|
||||
protected $translated_elements_table_alias = 'tra';
|
||||
protected $original_elements_language_table_alias = 'iclo';
|
||||
protected $translated_elements_language_table_alias = 'iclt';
|
||||
protected $correct_parent_table_alias = 'corr';
|
||||
protected $correct_parent_language_table_alias = 'iclc';
|
||||
protected $original_parent_table_alias = 'parents';
|
||||
protected $original_parent_language_table_alias = 'parent_lang';
|
||||
protected $element_id_column;
|
||||
protected $parent_element_id_column;
|
||||
protected $parent_id_column;
|
||||
protected $element_type_column;
|
||||
protected $element_type_prefix;
|
||||
protected $elements_table;
|
||||
protected $lang_info_table;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( &$wpdb ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->lang_info_table = $wpdb->prefix . 'icl_translations';
|
||||
add_action( 'clean_post_cache', [ $this, 'clean_cache' ] );
|
||||
add_action( 'set_object_terms', [ $this, 'clean_cache' ] );
|
||||
add_action( 'wpml_sync_term_hierarchy_done', [ $this, 'clean_cache' ] );
|
||||
}
|
||||
|
||||
public function clean_cache() {
|
||||
WPML_Non_Persistent_Cache::flush_group( self::CACHE_GROUP );
|
||||
}
|
||||
|
||||
public function get_unsynced_elements( $element_types, $ref_lang_code = false ) {
|
||||
$element_types = (array) $element_types;
|
||||
$results = array();
|
||||
if ( $element_types ) {
|
||||
$key = md5( wp_json_encode( array( $element_types, $ref_lang_code ) ) );
|
||||
$found = false;
|
||||
$results = WPML_Non_Persistent_Cache::get( $key, self::CACHE_GROUP, $found );
|
||||
if ( ! $found ) {
|
||||
$results_sql_parts = array();
|
||||
|
||||
$results_sql_parts['source_element_table'] = $this->get_source_element_table();
|
||||
$results_sql_parts['source_element_join'] = $this->get_source_element_join();
|
||||
$results_sql_parts['join_translation_language_data'] = $this->get_join_translation_language_data( $ref_lang_code );
|
||||
$results_sql_parts['translated_element_join'] = $this->get_translated_element_join();
|
||||
$results_sql_parts['original_parent_join'] = $this->get_original_parent_join();
|
||||
$results_sql_parts['original_parent_language_join'] = $this->get_original_parent_language_join();
|
||||
$results_sql_parts['correct_parent_language_join'] = $this->get_correct_parent_language_join();
|
||||
$results_sql_parts['correct_parent_element_join'] = $this->get_correct_parent_element_join();
|
||||
$results_sql_parts['where_statement'] = $this->get_where_statement(
|
||||
$element_types,
|
||||
$ref_lang_code
|
||||
);
|
||||
|
||||
$results_sql = $this->get_select_statement();
|
||||
|
||||
$results_sql .= ' FROM ';
|
||||
$results_sql .= implode( ' ', $results_sql_parts );
|
||||
|
||||
$results = $this->wpdb->get_results( $results_sql );
|
||||
|
||||
WPML_Non_Persistent_Cache::set( $key, $results, self::CACHE_GROUP );
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $element_types
|
||||
* @param bool $ref_lang_code
|
||||
*/
|
||||
public function sync_element_hierarchy( $element_types, $ref_lang_code = false ) {
|
||||
$hierarchical_element_types = wpml_collect( $element_types )->filter( [ $this, 'is_hierarchical' ] );
|
||||
|
||||
if ( $hierarchical_element_types->isEmpty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$unsynced = $this->get_unsynced_elements( $hierarchical_element_types->toArray(), $ref_lang_code );
|
||||
|
||||
foreach ( $unsynced as $row ) {
|
||||
$this->update_hierarchy_for_element( $row );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $element_type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function is_hierarchical( $element_type );
|
||||
|
||||
private function update_hierarchy_for_element( $row ) {
|
||||
$update = $this->validate_parent_synchronization( $row );
|
||||
|
||||
if ( $update ) {
|
||||
$target_element_id = $row->translated_id;
|
||||
$new_parent = (int) $row->correct_parent;
|
||||
$this->wpdb->update( $this->elements_table, array( $this->parent_id_column => $new_parent ), array( $this->element_id_column => $target_element_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
private function validate_parent_synchronization( $row ) {
|
||||
$is_valid = false;
|
||||
$is_for_posts = ( $this->elements_table === $this->wpdb->posts );
|
||||
if ( ! $is_for_posts ) {
|
||||
$is_valid = true;
|
||||
}
|
||||
|
||||
if ( $row && $is_for_posts ) {
|
||||
global $sitepress;
|
||||
|
||||
$target_element_id = $row->translated_id;
|
||||
$target_post = get_post( $target_element_id );
|
||||
if ( $target_post ) {
|
||||
$parent_must_empty = false;
|
||||
$post_type = $target_post->post_type;
|
||||
$element_type = 'post_' . $post_type;
|
||||
$target_element_language = $sitepress->get_element_language_details( $target_element_id, $element_type );
|
||||
$original_element_id = $sitepress->get_original_element_id( $target_element_id, $element_type );
|
||||
if ( $original_element_id ) {
|
||||
$parent_has_translation_in_target_language = false;
|
||||
|
||||
$original_element = get_post( $original_element_id );
|
||||
$original_post_parent_id = $original_element->post_parent;
|
||||
if ( $original_post_parent_id ) {
|
||||
$original_post_parent_trid = $sitepress->get_element_trid( $original_post_parent_id, $element_type );
|
||||
$original_post_parent_translations = $sitepress->get_element_translations( $original_post_parent_trid, $element_type );
|
||||
foreach ( $original_post_parent_translations as $original_post_parent_translation ) {
|
||||
if ( $original_post_parent_translation->language_code == $target_element_language->language_code ) {
|
||||
$parent_has_translation_in_target_language = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$parent_must_empty = true;
|
||||
}
|
||||
/**
|
||||
* Check if the parent of the original post has a translation in the language of the target post or if the parent must be set to 0
|
||||
*/
|
||||
$is_valid = $parent_has_translation_in_target_language || $parent_must_empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $is_valid;
|
||||
}
|
||||
|
||||
private function get_source_element_join() {
|
||||
|
||||
return "JOIN {$this->lang_info_table} {$this->original_elements_language_table_alias}
|
||||
ON {$this->original_elements_table_alias}.{$this->element_id_column}
|
||||
= {$this->original_elements_language_table_alias}.element_id
|
||||
AND {$this->original_elements_language_table_alias}.element_type
|
||||
= CONCAT('{$this->element_type_prefix}', {$this->original_elements_table_alias}.{$this->element_type_column})";
|
||||
}
|
||||
|
||||
private function get_translated_element_join() {
|
||||
|
||||
return "JOIN {$this->elements_table} {$this->translated_elements_table_alias}
|
||||
ON {$this->translated_elements_table_alias}.{$this->element_id_column}
|
||||
= {$this->translated_elements_language_table_alias}.element_id ";
|
||||
}
|
||||
|
||||
private function get_source_element_table() {
|
||||
|
||||
return " {$this->elements_table} {$this->original_elements_table_alias} ";
|
||||
}
|
||||
|
||||
private function get_join_translation_language_data( $ref_language_code ) {
|
||||
|
||||
$res = " JOIN {$this->lang_info_table} {$this->translated_elements_language_table_alias}
|
||||
ON {$this->translated_elements_language_table_alias}.trid
|
||||
= {$this->original_elements_language_table_alias}.trid ";
|
||||
if ( (bool) $ref_language_code === true ) {
|
||||
$res .= "AND {$this->translated_elements_language_table_alias}.language_code
|
||||
!= {$this->original_elements_language_table_alias}.language_code ";
|
||||
} else {
|
||||
$res .= " AND {$this->translated_elements_language_table_alias}.source_language_code
|
||||
= {$this->original_elements_language_table_alias}.language_code ";
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function get_select_statement() {
|
||||
|
||||
return " SELECT {$this->translated_elements_table_alias}.{$this->element_id_column} AS translated_id
|
||||
, IFNULL({$this->correct_parent_table_alias}.{$this->parent_element_id_column}, 0) AS correct_parent ";
|
||||
}
|
||||
|
||||
private function get_original_parent_join() {
|
||||
|
||||
return " LEFT JOIN {$this->elements_table} {$this->original_parent_table_alias}
|
||||
ON {$this->original_parent_table_alias}.{$this->parent_element_id_column}
|
||||
= {$this->original_elements_table_alias}.{$this->parent_id_column} ";
|
||||
}
|
||||
|
||||
private function get_original_parent_language_join() {
|
||||
|
||||
return " LEFT JOIN {$this->lang_info_table} {$this->original_parent_language_table_alias}
|
||||
ON {$this->original_parent_table_alias}.{$this->element_id_column}
|
||||
= {$this->original_parent_language_table_alias}.element_id
|
||||
AND {$this->original_parent_language_table_alias}.element_type
|
||||
= CONCAT('{$this->element_type_prefix}', {$this->original_parent_table_alias}.{$this->element_type_column}) ";
|
||||
}
|
||||
|
||||
private function get_correct_parent_language_join() {
|
||||
|
||||
return " LEFT JOIN {$this->lang_info_table} {$this->correct_parent_language_table_alias}
|
||||
ON {$this->correct_parent_language_table_alias}.language_code
|
||||
= {$this->translated_elements_language_table_alias}.language_code
|
||||
AND {$this->original_parent_language_table_alias}.trid
|
||||
= {$this->correct_parent_language_table_alias}.trid ";
|
||||
}
|
||||
|
||||
private function get_correct_parent_element_join() {
|
||||
|
||||
return " LEFT JOIN {$this->elements_table} {$this->correct_parent_table_alias}
|
||||
ON {$this->correct_parent_table_alias}.{$this->element_id_column}
|
||||
= {$this->correct_parent_language_table_alias}.element_id ";
|
||||
}
|
||||
|
||||
private function get_where_statement( $element_types, $ref_lang_code ) {
|
||||
|
||||
$filter_originals_snippet = $ref_lang_code
|
||||
? $this->wpdb->prepare( " AND {$this->original_elements_language_table_alias}.language_code = %s ", $ref_lang_code )
|
||||
: " AND {$this->translated_elements_language_table_alias}.source_language_code IS NOT NULL ";
|
||||
|
||||
return " WHERE {$this->original_elements_table_alias}.{$this->element_type_column}
|
||||
IN (" . wpml_prepare_in( $element_types ) . ")
|
||||
AND IFNULL({$this->correct_parent_table_alias}.{$this->parent_element_id_column}, 0)
|
||||
!= {$this->translated_elements_table_alias}.{$this->parent_id_column} " . $filter_originals_snippet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
|
||||
class WPML_Set_Language extends WPML_Full_Translation_API {
|
||||
|
||||
/**
|
||||
* @param int $el_id the element's ID (for terms we use the `term_taxonomy_id`)
|
||||
* @param string $el_type
|
||||
* @param int|bool|null $trid Trid the element is to be assigned to. Input that is == false will cause the term to
|
||||
* be assigned a new trid and potential translation relations to/from it to disappear.
|
||||
* @param string $language_code
|
||||
* @param null|string $src_language_code
|
||||
* @param bool $check_duplicates
|
||||
*
|
||||
* @return bool|int|null|string
|
||||
*/
|
||||
public function set(
|
||||
$el_id,
|
||||
$el_type,
|
||||
$trid,
|
||||
$language_code,
|
||||
$src_language_code = null,
|
||||
$check_duplicates = true
|
||||
) {
|
||||
if ( strlen( $el_type ) > 60 ) {
|
||||
throw new InvalidArgumentException( 'The element type "' . $el_type . '" is too long' );
|
||||
}
|
||||
|
||||
$this->clear_cache();
|
||||
if ( $check_duplicates && $el_id && (bool) ( $el_type_db = $this->check_duplicate(
|
||||
$el_type,
|
||||
$el_id
|
||||
) ) === true
|
||||
) {
|
||||
throw new InvalidArgumentException( 'element_id and type do not match for element_id:' . $el_id . ' the database contains ' . $el_type_db . ' while this function was called with ' . $el_type );
|
||||
}
|
||||
|
||||
$context = explode( '_', $el_type );
|
||||
$context = $context[0];
|
||||
|
||||
$src_language_code = $src_language_code === $language_code ? null : $src_language_code;
|
||||
|
||||
if ( $trid ) { // it's a translation of an existing element
|
||||
/** @var int $trid is an integer if not falsy */
|
||||
$this->maybe_delete_orphan( $trid, $language_code, $el_id );
|
||||
if ( $el_id && (bool) ( $translation_id = $this->is_language_change( $el_id, $el_type, $trid ) ) === true
|
||||
&& (bool) $this->trid_lang_trans_id( $trid, $language_code ) === false
|
||||
) {
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_translations',
|
||||
array( 'language_code' => $language_code ),
|
||||
array( 'translation_id' => $translation_id )
|
||||
);
|
||||
|
||||
do_action(
|
||||
'wpml_translation_update',
|
||||
array(
|
||||
'type' => 'update',
|
||||
'trid' => $trid,
|
||||
'element_id' => $el_id,
|
||||
'element_type' => $el_type,
|
||||
'translation_id' => $translation_id,
|
||||
'context' => $context,
|
||||
)
|
||||
);
|
||||
|
||||
} elseif ( $el_id && (bool) ( $translation_id = $this->existing_element( $el_id, $el_type ) ) === true ) {
|
||||
$this->change_translation_of( $trid, $el_id, $el_type, $language_code, $src_language_code );
|
||||
} elseif ( (bool) ( $translation_id = $this->is_placeholder_update( $trid, $language_code ) ) === true ) {
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_translations',
|
||||
array( 'element_id' => $el_id ),
|
||||
array( 'translation_id' => $translation_id )
|
||||
);
|
||||
|
||||
do_action(
|
||||
'wpml_translation_update',
|
||||
array(
|
||||
'type' => 'update',
|
||||
'trid' => $trid,
|
||||
'element_id' => $el_id,
|
||||
'element_type' => $el_type,
|
||||
'translation_id' => $translation_id,
|
||||
'context' => $context,
|
||||
)
|
||||
);
|
||||
|
||||
} elseif ( (bool) ( $translation_id = $this->trid_lang_trans_id( $trid, $language_code ) ) === false ) {
|
||||
$translation_id = $this->insert_new_row( $el_id, $trid, $el_type, $language_code, $src_language_code );
|
||||
}
|
||||
} else { // it's a new element or we are removing it from a trid
|
||||
$this->delete_existing_row( $el_type, $el_id );
|
||||
$translation_id = $this->insert_new_row( $el_id, false, $el_type, $language_code, $src_language_code );
|
||||
}
|
||||
|
||||
$this->clear_cache();
|
||||
if ( $translation_id && substr( $el_type, 0, 4 ) === 'tax_' ) {
|
||||
$taxonomy = substr( $el_type, 4 );
|
||||
do_action( 'created_term_translation', $taxonomy, $el_id, $language_code );
|
||||
}
|
||||
do_action( 'icl_set_element_language', $translation_id, $el_id, $language_code, $trid );
|
||||
|
||||
return $translation_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation id belonging to a specific trid, language_code combination
|
||||
*
|
||||
* @param int $trid
|
||||
* @param string $lang
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
private function trid_lang_trans_id( $trid, $lang ) {
|
||||
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT translation_id
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE trid = %d
|
||||
AND language_code = %s
|
||||
LIMIT 1",
|
||||
$trid,
|
||||
$lang
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the source_language_code of an element
|
||||
*
|
||||
* @param int $trid
|
||||
* @param int $el_id
|
||||
* @param string $el_type
|
||||
* @param string $language_code
|
||||
* @param string $src_language_code
|
||||
*/
|
||||
private function change_translation_of( $trid, $el_id, $el_type, $language_code, $src_language_code ) {
|
||||
$src_language_code = empty( $src_language_code )
|
||||
? $this->sitepress->get_source_language_by_trid( $trid ) : $src_language_code;
|
||||
if ( $src_language_code !== $language_code ) {
|
||||
$this->wpdb->update(
|
||||
$this->wpdb->prefix . 'icl_translations',
|
||||
array(
|
||||
'trid' => $trid,
|
||||
'language_code' => $language_code,
|
||||
'source_language_code' => $src_language_code,
|
||||
),
|
||||
array(
|
||||
'element_type' => $el_type,
|
||||
'element_id' => $el_id,
|
||||
)
|
||||
);
|
||||
|
||||
$context = explode( '_', $el_type );
|
||||
|
||||
do_action(
|
||||
'wpml_translation_update',
|
||||
array(
|
||||
'type' => 'update',
|
||||
'trid' => $trid,
|
||||
'element_id' => $el_id,
|
||||
'element_type' => $el_type,
|
||||
'context' => $context[0],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $el_type
|
||||
* @param int $el_id
|
||||
*/
|
||||
private function delete_existing_row( $el_type, $el_id ) {
|
||||
|
||||
$context = explode( '_', $el_type );
|
||||
$update_args = array(
|
||||
'element_id' => $el_id,
|
||||
'element_type' => $el_type,
|
||||
'context' => $context[0],
|
||||
);
|
||||
|
||||
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
|
||||
|
||||
$this->wpdb->query(
|
||||
$this->wpdb->prepare(
|
||||
"DELETE FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE element_type = %s
|
||||
AND element_id = %d",
|
||||
$el_type,
|
||||
$el_id
|
||||
)
|
||||
);
|
||||
|
||||
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new row into icl_translations
|
||||
*
|
||||
* @param int $el_id
|
||||
* @param int $trid
|
||||
* @param string $el_type
|
||||
* @param string $language_code
|
||||
* @param string $src_language_code
|
||||
*
|
||||
* @return int Translation ID of the new row
|
||||
*/
|
||||
private function insert_new_row( $el_id, $trid, $el_type, $language_code, $src_language_code ) {
|
||||
$new = array(
|
||||
'element_type' => $el_type,
|
||||
'language_code' => $language_code,
|
||||
);
|
||||
|
||||
if ( $trid === false ) {
|
||||
$trid = 1 + (int) $this->wpdb->get_var( "SELECT MAX(trid) FROM {$this->wpdb->prefix}icl_translations" );
|
||||
} else {
|
||||
$src_language_code = empty( $src_language_code )
|
||||
? $this->sitepress->get_source_language_by_trid( $trid ) : $src_language_code;
|
||||
$new['source_language_code'] = $src_language_code;
|
||||
}
|
||||
|
||||
$new['trid'] = $trid;
|
||||
if ( $el_id ) {
|
||||
$new['element_id'] = $el_id;
|
||||
}
|
||||
$this->wpdb->insert( $this->wpdb->prefix . 'icl_translations', $new );
|
||||
$translation_id = $this->wpdb->insert_id;
|
||||
|
||||
$context = explode( '_', $el_type );
|
||||
|
||||
do_action(
|
||||
'wpml_translation_update',
|
||||
array(
|
||||
'type' => 'insert',
|
||||
'trid' => $trid,
|
||||
'element_id' => $el_id,
|
||||
'element_type' => $el_type,
|
||||
'translation_id' => $translation_id,
|
||||
'context' => $context[0],
|
||||
)
|
||||
);
|
||||
|
||||
return $translation_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a row exists for a concrete id, type and trid combination
|
||||
* in icl_translations.
|
||||
*
|
||||
* @param int $el_id
|
||||
* @param string $el_type
|
||||
* @param int $trid
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
private function is_language_change( $el_id, $el_type, $trid ) {
|
||||
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT translation_id
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE element_type = %s
|
||||
AND element_id = %d
|
||||
AND trid = %d",
|
||||
$el_type,
|
||||
$el_id,
|
||||
$trid
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given trid, language_code combination contains a placeholder with NULL element_id
|
||||
* and if so returns the translation id of this row.
|
||||
*
|
||||
* @param int $trid
|
||||
* @param string $language_code
|
||||
*
|
||||
* @return null|string translation id
|
||||
*/
|
||||
private function is_placeholder_update( $trid, $language_code ) {
|
||||
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
" SELECT translation_id
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE trid=%d
|
||||
AND language_code = %s
|
||||
AND element_id IS NULL",
|
||||
$trid,
|
||||
$language_code
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a row in icl_translations exists for a concrete element type and id combination
|
||||
*
|
||||
* @param int $el_id
|
||||
* @param string $el_type
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
private function existing_element( $el_id, $el_type ) {
|
||||
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT translation_id
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE element_type= %s
|
||||
AND element_id= %d
|
||||
LIMIT 1",
|
||||
$el_type,
|
||||
$el_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a trid contains an existing translation other than a specific element id and deletes that row if it
|
||||
* exists.
|
||||
*
|
||||
* @param int $trid
|
||||
* @param string $language_code
|
||||
* @param int $correct_element_id
|
||||
*/
|
||||
private function maybe_delete_orphan( $trid, $language_code, $correct_element_id ) {
|
||||
/** @var \stdClass $result */
|
||||
$result = $this->wpdb->get_row(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT translation_id, element_type, element_id
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE trid = %d
|
||||
AND language_code = %s
|
||||
AND element_id <> %d
|
||||
AND source_language_code IS NOT NULL
|
||||
",
|
||||
$trid,
|
||||
$language_code,
|
||||
$correct_element_id
|
||||
)
|
||||
);
|
||||
|
||||
$translation_id = ( null != $result ? $result->translation_id : null );
|
||||
|
||||
if ( $translation_id ) {
|
||||
|
||||
$context = explode( '_', $result->element_type );
|
||||
$update_args = array(
|
||||
'trid' => $trid,
|
||||
'element_id' => $result->element_id,
|
||||
'element_type' => $result->element_type,
|
||||
'translation_id' => $translation_id,
|
||||
'context' => $context[0],
|
||||
);
|
||||
|
||||
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
|
||||
|
||||
$this->wpdb->query(
|
||||
$this->wpdb->prepare(
|
||||
"DELETE FROM {$this->wpdb->prefix}icl_translations WHERE translation_id=%d",
|
||||
$translation_id
|
||||
)
|
||||
);
|
||||
|
||||
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a duplicate element_id already exists with a different than the input type.
|
||||
* This only applies to posts and taxonomy terms.
|
||||
*
|
||||
* @param string $el_type
|
||||
* @param int $el_id
|
||||
*
|
||||
* @return null|string null if no duplicate icl translations entry is found
|
||||
* having a different than the input element type, the element type if a
|
||||
* duplicate row is found.
|
||||
*/
|
||||
private function check_duplicate( $el_type, $el_id ) {
|
||||
$res = false;
|
||||
$exp = explode( '_', $el_type );
|
||||
$_type = $exp[0];
|
||||
if ( in_array( $_type, array( 'post', 'tax' ) ) ) {
|
||||
$res = $this->duplicate_from_db( $el_id, $el_type, $_type );
|
||||
if ( $res ) {
|
||||
$fix_assignments = new WPML_Fix_Type_Assignments( $this->sitepress );
|
||||
$fix_assignments->run();
|
||||
$res = $this->duplicate_from_db( $el_id, $el_type, $_type );
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function duplicate_from_db( $el_id, $el_type, $_type ) {
|
||||
|
||||
return $this->wpdb->get_var(
|
||||
$this->wpdb->prepare(
|
||||
"SELECT element_type
|
||||
FROM {$this->wpdb->prefix}icl_translations
|
||||
WHERE element_id = %d
|
||||
AND element_type <> %s
|
||||
AND element_type LIKE %s",
|
||||
$el_id,
|
||||
$el_type,
|
||||
$_type . '%'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function clear_cache() {
|
||||
$this->term_translations->reload();
|
||||
$this->post_translations->reload();
|
||||
$this->sitepress->get_translations_cache()->clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_SP_And_PT_User extends WPML_SP_User {
|
||||
|
||||
/** @var WPML_Post_Translation $post_translation */
|
||||
protected $post_translation;
|
||||
|
||||
/**
|
||||
* @param WPML_Post_Translation $post_translation
|
||||
* @param SitePress $sitepress
|
||||
*/
|
||||
public function __construct( &$post_translation, &$sitepress ) {
|
||||
parent::__construct( $sitepress );
|
||||
$this->post_translation = &$post_translation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_SP_User
|
||||
*
|
||||
* Superclass for all WPML classes using the @global SitePress $sitepress directly
|
||||
*
|
||||
* @since 3.2.3
|
||||
*/
|
||||
abstract class WPML_SP_User {
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
protected $sitepress;
|
||||
|
||||
/**
|
||||
* @param SitePress $sitepress
|
||||
*/
|
||||
public function __construct( &$sitepress ) {
|
||||
$this->sitepress = &$sitepress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_User {
|
||||
|
||||
/** @var TranslationManagement $tm_instance */
|
||||
protected $tm_instance;
|
||||
|
||||
/**
|
||||
* WPML_Custom_Field_Setting_Factory constructor.
|
||||
*
|
||||
* @param TranslationManagement $tm_instance
|
||||
*/
|
||||
public function __construct( TranslationManagement $tm_instance ) {
|
||||
$this->tm_instance = $tm_instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_URL_Converter_User
|
||||
*
|
||||
* @since 3.2.3
|
||||
*/
|
||||
abstract class WPML_URL_Converter_User {
|
||||
|
||||
/** @var WPML_URL_Converter */
|
||||
protected $url_converter;
|
||||
|
||||
/**
|
||||
* @param \WPML_URL_Converter $url_converter
|
||||
*/
|
||||
public function __construct( &$url_converter ) {
|
||||
$this->url_converter = &$url_converter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_WPDB_And_SP_User
|
||||
*/
|
||||
abstract class WPML_WPDB_And_SP_User extends WPML_WPDB_User {
|
||||
|
||||
/** @var SitePress $sitepress */
|
||||
protected $sitepress;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
* @param SitePress $sitepress
|
||||
*/
|
||||
public function __construct( &$wpdb, &$sitepress ) {
|
||||
parent::__construct( $wpdb );
|
||||
$this->sitepress = &$sitepress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_WPDB_User
|
||||
*
|
||||
* Superclass for all WPML classes using the @global wpdb $wpdb
|
||||
*
|
||||
* @since 3.2.3
|
||||
*/
|
||||
abstract class WPML_WPDB_User {
|
||||
|
||||
/** @var wpdb $wpdb */
|
||||
public $wpdb;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( &$wpdb ) {
|
||||
$this->wpdb = &$wpdb;
|
||||
}
|
||||
|
||||
public function get_wpdb() {
|
||||
return $this->wpdb;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user