Files
doitinpoland.com/wp-content/plugins/sitepress-multilingual-cms/classes/taxonomy-term-translation/class-wpml-term-language-synchronization.php
2023-09-12 21:41:04 +02:00

278 lines
9.0 KiB
PHP

<?php
/**
* @since 3.1.8.4
*
* Class WPML_Term_Language_Synchronization
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Term_Language_Synchronization extends WPML_WPDB_And_SP_User {
/** @var string $taxonomy */
private $taxonomy;
/** @var array $data */
private $data;
/** @var array $missing_terms */
private $missing_terms = array();
/** @var WPML_Terms_Translations $term_utils */
private $term_utils;
/**
* @param SitePress $sitepress
* @param WPML_Terms_Translations $term_utils
* @param string $taxonomy
*/
public function __construct( &$sitepress, &$term_utils, $taxonomy ) {
$wpdb = $sitepress->wpdb();
parent::__construct( $wpdb, $sitepress );
$this->term_utils = $term_utils;
$this->taxonomy = $taxonomy;
$this->data = $this->set_affected_ids();
$this->prepare_missing_terms_data();
}
/**
* Wrapper for the two database actions performed by this object.
* First those terms are created that lack translations and then following that,
* the assignment of posts and languages is corrected, taking advantage of the newly created terms
* and resulting in a state of no conflicts in the form of a post language being different from
* an assigned terms language, remaining.
*/
public function set_translated() {
$this->prepare_missing_originals();
$this->reassign_terms();
$this->set_initial_term_language();
}
/**
* Helper function for the installation process,
* finds all terms missing an entry in icl_translations and then
* assigns them the default language.
*/
public function set_initial_term_language() {
$element_ids = $this->wpdb->get_col(
$this->wpdb->prepare(
"
SELECT tt.term_taxonomy_id
FROM {$this->wpdb->term_taxonomy} AS tt
LEFT JOIN {$this->wpdb->prefix}icl_translations AS i
ON tt.term_taxonomy_id = i.element_id
AND CONCAT('tax_', tt.taxonomy) = i.element_type
WHERE taxonomy = %s
AND i.element_id IS NULL",
$this->taxonomy
)
);
$default_language = $this->sitepress->get_default_language();
foreach ( $element_ids as $id ) {
$this->sitepress->set_element_language_details( $id, 'tax_' . $this->taxonomy, false, $default_language );
}
}
/**
* Performs an SQL query assigning all terms to their correct language equivalent if it exists.
* This should only be run after the previous functionality in here has finished.
* Afterwards the term counts are recalculated globally, since term assignments bypassing the WordPress Core,
* will not trigger any sort of update on those.
*/
private function reassign_terms() {
$update_query = $this->wpdb->prepare(
"UPDATE {$this->wpdb->term_relationships} AS o,
{$this->wpdb->prefix}icl_translations AS ic,
{$this->wpdb->prefix}icl_translations AS iw,
{$this->wpdb->prefix}icl_translations AS ip,
{$this->wpdb->posts} AS p
SET o.term_taxonomy_id = ic.element_id
WHERE ic.trid = iw.trid
AND ic.element_type = iw.element_type
AND iw.element_id = o.term_taxonomy_id
AND ic.language_code = ip.language_code
AND ip.element_type = CONCAT('post_', p.post_type)
AND ip.element_id = p.ID
AND o.object_id = p.ID
AND o.term_taxonomy_id != ic.element_id
AND iw.element_type = %s",
'tax_' . $this->taxonomy
);
$rows_affected = $this->wpdb->query( $update_query );
if ( $rows_affected ) {
$term_ids = $this->wpdb->get_col(
$this->wpdb->prepare(
"SELECT term_taxonomy_id FROM {$this->wpdb->term_taxonomy} WHERE taxonomy = %s",
$this->taxonomy
)
);
// Do not run the count update on taxonomies that are not actually registered as proper taxonomy objects, e.g. WooCommerce Product Attributes.
$taxonomy_object = $this->sitepress->get_wp_api()->get_taxonomy( $this->taxonomy );
if ( $taxonomy_object && isset( $taxonomy_object->object_type ) ) {
$this->sitepress->get_wp_api()->wp_update_term_count( $term_ids, $this->taxonomy );
}
}
}
/**
* @param object[] $sql_result holding the information retrieved in \self::set_affected_ids
*
* @return array The associative array to be returned by \self::set_affected_ids
*/
private function format_data( $sql_result ) {
$res = array();
foreach ( $sql_result as $pair ) {
$res[ $pair->ttid ] = isset( $res[ $pair->ttid ] )
? $res[ $pair->ttid ]
: array(
'tlang' => array(),
'plangs' => array(),
);
if ( $pair->term_lang && $pair->trid ) {
$res[ $pair->ttid ]['tlang'] = array(
'lang' => $pair->term_lang,
'trid' => $pair->trid,
);
}
if ( $pair->post_lang ) {
$res[ $pair->ttid ]['plangs'][ $pair->post_id ] = $pair->post_lang;
}
}
return $res;
}
/**
* Uses the API provided in \WPML_Terms_Translations to create missing term translations.
* These arise when a term, previously having been untranslated, is set to be translated
* and assigned to posts in more than one language.
*
* @param int $trid The trid value for which term translations are missing.
* @param string $source_lang The source language of this trid.
* @param array $langs The languages' codes for which term translations are missing.
*/
private function prepare_missing_translations(
$trid,
$source_lang,
$langs
) {
$existing_translations = $this->sitepress->term_translations()->get_element_translations(
false,
$trid
);
foreach ( $langs as $lang ) {
if ( ! isset( $existing_translations[ $lang ] ) ) {
$this->term_utils->create_automatic_translation(
array(
'lang_code' => $lang,
'source_language' => $source_lang,
'trid' => $trid,
'taxonomy' => $this->taxonomy,
)
);
}
}
}
/**
* Retrieves all term_ids, and if applicable, their language and assigned to posts,
* in an associative array,
* which are in the situation of not being assigned to any language or in which a term
* is assigned to a post in a language different from its own.
*
* @return array
*/
private function set_affected_ids() {
$query_for_post_ids = $this->wpdb->prepare(
"
SELECT tl.trid AS trid, tl.ttid AS ttid, tl.tlang AS term_lang, tl.pid AS post_id, pl.plang AS post_lang
FROM (
SELECT
o.object_id AS pid,
tt.term_taxonomy_id AS ttid,
i.language_code AS tlang,
i.trid AS trid
FROM {$this->wpdb->term_relationships} AS o
JOIN {$this->wpdb->term_taxonomy} AS tt
ON o.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN {$this->wpdb->prefix}icl_translations AS i
ON i.element_id = tt.term_taxonomy_id
AND i.element_type = CONCAT('tax_', tt.taxonomy)
WHERE tt.taxonomy = %s) AS tl
LEFT JOIN
( SELECT p.ID AS pid, i.language_code AS plang
FROM {$this->wpdb->posts} AS p
JOIN {$this->wpdb->prefix}icl_translations AS i
ON i.element_id = p.ID
AND i.element_type = CONCAT('post_', p.post_type)
) AS pl
ON tl.pid = pl.pid
",
$this->taxonomy
);
$ttid_pid_pairs = $this->wpdb->get_results( $query_for_post_ids );
return $this->format_data( $ttid_pid_pairs );
}
/**
* Assigns language information to terms that are to be treated as originals at the time of
* their taxonomy being set to translated instead of 'do nothing'.
*/
private function prepare_missing_originals() {
foreach ( $this->missing_terms as $ttid => $missing_lang_data ) {
if ( ! isset( $this->data[ $ttid ]['tlang']['trid'] ) ) {
foreach ( $missing_lang_data as $lang => $post_ids ) {
$this->sitepress->set_element_language_details(
$ttid,
'tax_' . $this->taxonomy,
null,
$lang
);
$trid = $this->sitepress->term_translations()->get_element_trid( $ttid );
if ( $trid ) {
$this->data[ $ttid ]['tlang']['trid'] = $trid;
$this->data[ $ttid ]['tlang']['lang'] = $lang;
unset( $this->missing_terms[ $ttid ][ $lang ] );
break;
}
}
}
if ( isset( $this->data[ $ttid ]['tlang']['trid'] ) ) {
$this->prepare_missing_translations(
$this->data[ $ttid ]['tlang']['trid'],
$this->data[ $ttid ]['tlang']['lang'],
array_keys( $this->missing_terms[ $ttid ] )
);
}
}
}
/**
* Uses the data retrieved from the database and saves information about,
* in need of fixing terms to this object.
*/
private function prepare_missing_terms_data() {
$default_lang = $this->sitepress->get_default_language();
$data = $this->data;
$missing = array();
foreach ( $data as $ttid => $data_item ) {
if ( empty( $data_item['plangs'] ) && empty( $data_item['tlang'] ) ) {
$missing[ $ttid ][ $default_lang ] = - 1;
} else {
$affected_languages = array_diff( $data_item['plangs'], $data_item['tlang'] );
if ( ! empty( $affected_languages ) ) {
foreach ( $data_item['plangs'] as $post_id => $lang ) {
if ( ! isset( $missing[ $ttid ][ $lang ] ) ) {
$missing[ $ttid ][ $lang ] = array( $post_id );
} else {
$missing[ $ttid ][ $lang ][] = $post_id;
}
}
}
}
}
$this->missing_terms = $missing;
}
}