first commit

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

View File

@@ -0,0 +1,54 @@
<?php
/** NOTE:
* Use the $wpml_post_translations or $wpml_term_translations globals for posts and taxonomy
* They are more efficient
*/
class WPML_Element_Type_Translation {
/** @var wpdb $wpdb */
private $wpdb;
/** @var WPML_Cache_Factory $cache_factory */
private $cache_factory;
/** @var string $element_type */
private $element_type;
public function __construct( wpdb $wpdb, WPML_Cache_Factory $cache_factory, $element_type ) {
$this->wpdb = $wpdb;
$this->cache_factory = $cache_factory;
$this->element_type = $element_type;
}
function get_element_lang_code( $element_id ) {
$cache_key_array = array( $element_id, $this->element_type );
$cache_key = md5( serialize( $cache_key_array ) );
$cache_group = 'WPML_Element_Type_Translation::get_language_for_element';
$cache_found = false;
$cache = $this->cache_factory->get( $cache_group );
$result = $cache->get( $cache_key, $cache_found );
if ( ! $cache_found ) {
$language_for_element_prepared = $this->wpdb->prepare(
"SELECT language_code
FROM {$this->wpdb->prefix}icl_translations
WHERE element_id=%d
AND element_type=%s
LIMIT 1",
array( $element_id, $this->element_type )
);
$result = $this->wpdb->get_var( $language_for_element_prepared );
if ( $result ) {
$cache->set( $cache_key, $result );
}
}
return $result;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* @author OnTheGo Systems
*/
class WPML_Menu_Element extends WPML_Term_Element {
/**
* WPML_Menu_Element constructor.
*
* @param int $id
* @param SitePress $sitepress
* @param WPML_WP_Cache $wpml_cache
*/
public function __construct( $id, SitePress $sitepress, WPML_WP_Cache $wpml_cache = null ) {
$this->taxonomy = 'nav_menu';
parent::__construct( $id, $sitepress, $this->taxonomy, $wpml_cache );
}
/**
* @param stdClass $element_data standard object containing at least the `term_id` property.
*
* @return WPML_Menu_Element
* @throws \InvalidArgumentException Exception.
*/
public function get_new_instance( $element_data ) {
return new WPML_Menu_Element( $element_data->term_id, $this->sitepress, $this->wpml_cache );
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* @author OnTheGo Systems
*/
class WPML_Post_Element extends WPML_Translation_Element implements WPML_Duplicable_Element {
/**
* @return WP_Post
*/
function get_wp_object() {
return get_post( $this->id );
}
/**
* @param WP_Post $post
*
* @return string
*/
function get_type( $post = null ) {
if ( $post ) {
return $post->post_type;
}
return $this->get_wp_object()->post_type;
}
public function get_wpml_element_type() {
$element_type = '';
if ( ! is_wp_error( $this->get_wp_element_type() ) ) {
$element_type = 'post_' . $this->get_wp_element_type();
}
return $element_type;
}
function get_element_id() {
return $this->id;
}
/**
* @param null|stdClass $element_data null, or a standard object containing at least the `translation_id`, `language_code`, `element_id`, `source_language_code`, `element_type`, and `original` properties.
*
* @return WPML_Post_Element
* @throws \InvalidArgumentException
*/
function get_new_instance( $element_data ) {
return new WPML_Post_Element( $element_data->element_id, $this->sitepress, $this->wpml_cache );
}
function is_translatable() {
return $this->sitepress->is_translated_post_type( $this->get_wp_element_type() );
}
function is_display_as_translated() {
return $this->sitepress->is_display_as_translated_post_type( $this->get_wp_element_type() );
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* @author OnTheGo Systems
*/
class WPML_Term_Element extends WPML_Translation_Element {
/** @var string Taxonomy name */
protected $taxonomy;
/**
* WPML_Term_Element constructor.
*
* @param int $id term_id of Term Element.
* @param SitePress $sitepress
* @param string $taxonomy
* @param WPML_WP_Cache $wpml_cache
*/
public function __construct( $id, SitePress $sitepress, $taxonomy = '', WPML_WP_Cache $wpml_cache = null ) {
$this->taxonomy = $taxonomy;
parent::__construct( $id, $sitepress, $wpml_cache );
}
/**
* @return array|null|WP_Error|WP_Term
*/
public function get_wp_object() {
$has_filter = remove_filter( 'get_term', array( $this->sitepress, 'get_term_adjust_id' ), 1 );
$term = get_term( $this->id, $this->taxonomy );
if ( ! $term || is_wp_error( $term ) ) {
$term = get_term_by( 'term_taxonomy_id', $this->id, $this->taxonomy );
$term = $term ?: null;
}
if ( $has_filter ) {
add_filter( 'get_term', array( $this->sitepress, 'get_term_adjust_id' ), 1, 1 );
}
return $term;
}
/**
* @param WP_Term $term
*
* @return string
*/
public function get_type( $term = null ) {
if ( ! $this->taxonomy && $term && ! is_wp_error( $term ) ) {
$this->taxonomy = $term->taxonomy;
}
return $this->taxonomy;
}
public function get_wpml_element_type() {
$element_type = '';
if ( ! is_wp_error( $this->get_wp_element_type() ) ) {
$element_type = 'tax_' . $this->get_wp_element_type();
}
return $element_type;
}
public function get_element_id() {
$element_id = null;
$term = $this->get_wp_object();
if ( $term && ! is_wp_error( $term ) ) {
$element_id = $term->term_taxonomy_id;
}
return $element_id;
}
/**
* @param null|stdClass $element_data null, or a standard object containing at least the `translation_id`, `language_code`, `element_id`, `source_language_code`, `element_type`, and `original` properties.
*
* @return WPML_Term_Element
* @throws \InvalidArgumentException Exception.
*/
public function get_new_instance( $element_data ) {
return new WPML_Term_Element( $element_data->element_id, $this->sitepress, $this->taxonomy, $this->wpml_cache );
}
public function is_translatable() {
return $this->sitepress->is_translated_taxonomy( $this->get_wp_element_type() );
}
public function is_display_as_translated() {
return $this->sitepress->is_display_as_translated_taxonomy( $this->get_wp_element_type() );
}
}

View File

@@ -0,0 +1,51 @@
<?php
class WPML_Translation_Element_Factory {
const ELEMENT_TYPE_POST = 'Post';
const ELEMENT_TYPE_TERM = 'Term';
const ELEMENT_TYPE_MENU = 'Menu';
/** @var SitePress */
private $sitepress;
/** @var WPML_WP_Cache */
private $wpml_cache;
/**
* @param SitePress $sitepress
* @param WPML_WP_Cache $wpml_cache
*/
public function __construct( SitePress $sitepress, WPML_WP_Cache $wpml_cache = null ) {
$this->sitepress = $sitepress;
$this->wpml_cache = $wpml_cache;
}
/**
* @param int $id
* @param string $type any of `WPML_Translation_Element_Factory::ELEMENT_TYPE_POST`, `WPML_Translation_Element_Factory::ELEMENT_TYPE_TERM`, `WPML_Translation_Element_Factory::ELEMENT_TYPE_MENU`.
*
* @return WPML_Translation_Element
* @throws InvalidArgumentException InvalidArgumentException.
*/
public function create( $id, $type ) {
$fn = 'create_' . $type;
if ( method_exists( $this, $fn ) ) {
return $this->$fn( $id );
}
throw new InvalidArgumentException( 'Element type: ' . $type . ' does not exist.' );
}
public function create_post( $id ) {
return new WPML_Post_Element( $id, $this->sitepress, $this->wpml_cache );
}
public function create_term( $id ) {
return new WPML_Term_Element( $id, $this->sitepress, '', $this->wpml_cache );
}
public function create_menu( $id ) {
return new WPML_Menu_Element( $id, $this->sitepress, $this->wpml_cache );
}
}

View File

@@ -0,0 +1,225 @@
<?php
/**
* Use this class as parent class for translatable elements in WPML,
* to have a common approach for retrieving and setting translation information.
*
* @author OnTheGo Systems
*/
abstract class WPML_Translation_Element extends WPML_SP_User {
/** @var int */
protected $id;
/** @var stdClass */
private $languages_details;
/** @var array */
private $element_translations;
/** @var WPML_WP_Cache */
protected $wpml_cache;
/**
* WPML_Translation_Element constructor.
*
* @param int $id
* @param SitePress $sitepress
* @param WPML_WP_Cache $wpml_cache
*/
public function __construct( $id, SitePress $sitepress, WPML_WP_Cache $wpml_cache = null ) {
if ( ! is_numeric( $id ) || $id <= 0 ) {
throw new InvalidArgumentException( 'Argument ID must be numeric and greater than 0.' );
}
$this->id = (int) $id;
$this->wpml_cache = $wpml_cache ? $wpml_cache : new WPML_WP_Cache( WPML_ELEMENT_TRANSLATIONS_CACHE_GROUP );
parent::__construct( $sitepress );
}
public function get_id() {
return $this->id;
}
/**
* @return string|null
*/
public function get_source_language_code() {
$source_language_code = null;
if ( $this->get_language_details() ) {
$source_language_code = $this->get_language_details()->source_language_code;
}
return $source_language_code;
}
/**
* @return stdClass
* @throws \UnexpectedValueException
*/
protected function get_language_details() {
$this->init_language_details();
return $this->languages_details;
}
abstract function get_element_id();
abstract function get_wpml_element_type();
/**
* @return array
*/
private function get_element_translations() {
return $this->sitepress->get_element_translations( $this->get_trid(), $this->get_wpml_element_type() );
}
/**
* @param string $language_code
*
* @return WPML_Translation_Element|null
* @throws \InvalidArgumentException
*/
public function get_translation( $language_code ) {
if ( ! $language_code ) {
throw new InvalidArgumentException( 'Argument $language_code must be a non empty string.' );
}
$this->maybe_init_translations();
$translation = null;
if ( $this->element_translations && array_key_exists( $language_code, $this->element_translations ) ) {
$translation = $this->element_translations[ $language_code ];
}
return $translation;
}
/**
* @return WPML_Translation_Element[]
*/
public function get_translations() {
return $this->maybe_init_translations();
}
/**
* @return WPML_Translation_Element[]
*/
public function maybe_init_translations() {
if ( ! $this->element_translations ) {
$this->element_translations = array();
$translations = $this->get_element_translations();
foreach ( $translations as $language_code => $element_data ) {
if ( ! isset( $element_data->element_id ) ) {
continue;
}
try {
$this->element_translations[ $language_code ] = $this->get_new_instance( $element_data );
} catch ( Exception $e ) {
}
}
}
return $this->element_translations;
}
/**
* @return false|int
*/
public function get_trid() {
$trid = false;
if ( $this->get_language_details() ) {
$trid = $this->get_language_details()->trid;
}
return $trid;
}
/**
* @return string|WP_Error
*/
function get_wp_element_type() {
$element = $this->get_wp_object();
if ( is_wp_error( $element ) ) {
return $element;
}
if ( false === (bool) $element ) {
return new WP_Error( 1, 'Element does not exists.' );
}
return $this->get_type( $element );
}
/**
* @return mixed|WP_Error
*/
abstract function get_wp_object();
/**
* @param mixed $element
*
* @return string
*/
abstract function get_type( $element = null );
/**
* @param null|stdClass $element_data null, or a standard object containing at least the `translation_id`, `language_code`, `element_id`, `source_language_code`, `element_type`, and `original` properties.
*
* @return WPML_Translation_Element
*/
abstract function get_new_instance( $element_data );
/**
* @return null|WPML_Translation_Element
*/
public function get_source_element() {
$this->maybe_init_translations();
$source_element = null;
$source_language_code = $this->get_source_language_code();
if ( $this->element_translations && $source_language_code && array_key_exists( $source_language_code, $this->element_translations ) ) {
$source_element = $this->element_translations[ $source_language_code ];
}
return $source_element;
}
/**
* Determines whether the current element language is the root source element.
*
* @return bool
*/
public function is_root_source() {
return null !== $this->get_source_language_code() && $this->get_source_language_code() === $this->get_language_code();
}
/**
* @return string|null
*/
public function get_language_code() {
$language_code = null;
if ( $this->get_language_details() ) {
$language_code = $this->get_language_details()->language_code;
}
return $language_code;
}
protected function init_language_details() {
if ( ! $this->languages_details ) {
$this->languages_details = $this->sitepress->get_element_language_details( $this->get_element_id(), $this->get_wpml_element_type() );
}
}
public function flush_cache() {
$this->languages_details = null;
$this->element_translations = null;
$this->wpml_cache->flush_group_cache();
}
/** @return bool */
public function is_in_default_language() {
return $this->get_language_code() === $this->sitepress->get_default_language();
}
abstract function is_translatable();
abstract function is_display_as_translated();
}

View File

@@ -0,0 +1,325 @@
<?php
/**
* @author OnTheGo Systems
*/
class WPML_Translations extends WPML_SP_User {
/** @var bool */
public $skip_empty = false;
/** @var bool */
public $all_statuses = false;
/** @var bool */
public $skip_cache = false;
/** @var bool */
public $skip_recursions = false;
private $duplicated_by = array();
private $mark_as_duplicate_meta_key = '_icl_lang_duplicate_of';
private $wpml_cache;
/**
* WPML_Translations constructor.
*
* @param SitePress $sitepress
* @param WPML_WP_Cache $wpml_cache
*/
public function __construct( SitePress $sitepress, WPML_WP_Cache $wpml_cache = null ) {
parent::__construct( $sitepress );
$this->wpml_cache = $wpml_cache ? $wpml_cache : new WPML_WP_Cache( WPML_ELEMENT_TRANSLATIONS_CACHE_GROUP );
}
/**
* @param int $trid
* @param string $wpml_element_type
* @param bool $skipPrivilegeChecking
*
* @return array<string,\stdClass>
*/
public function get_translations( $trid, $wpml_element_type, $skipPrivilegeChecking = false ) {
$cache_key_args = array_filter( array( $trid, $wpml_element_type, $this->skip_empty, $this->all_statuses, $this->skip_recursions ) );
$cache_key = md5( wp_json_encode( $cache_key_args ) );
$cache_found = false;
$temp_elements = $this->wpml_cache->get( $cache_key, $cache_found );
if ( ! $this->skip_cache && $cache_found ) {
return $temp_elements;
}
$translations = array();
$sql_parts = array(
'select' => array(),
'join' => array(),
'where' => array(),
'group_by' => array(),
);
if ( $trid ) {
if ( $this->wpml_element_type_is_post( $wpml_element_type ) ) {
$sql_parts = $this->get_sql_parts_for_post( $wpml_element_type, $sql_parts, $skipPrivilegeChecking );
} elseif ( $this->wpml_element_type_is_taxonomy( $wpml_element_type ) ) {
$sql_parts = $this->get_sql_parts_for_taxonomy( $sql_parts );
}
$sql_parts['where'][] = $this->sitepress->get_wpdb()->prepare( ' AND wpml_translations.trid=%d ', $trid );
$select = implode( ' ', $sql_parts['select'] );
$join = implode( ' ', $sql_parts['join'] );
$where = implode( ' ', $sql_parts['where'] );
$group_by = implode( ' ', $sql_parts['group_by'] );
$query = "
SELECT wpml_translations.translation_id, wpml_translations.language_code, wpml_translations.element_id, wpml_translations.source_language_code, wpml_translations.element_type, NULLIF(wpml_translations.source_language_code, '') IS NULL AS original
{$select}
FROM {$this->sitepress->get_wpdb()->prefix}icl_translations wpml_translations
{$join}
WHERE 1 {$where}
{$group_by}
";
$results = $this->sitepress->get_wpdb()->get_results( $query );
foreach ( $results as $translation ) {
if ( $this->must_ignore_translation( $translation ) ) {
continue;
}
$translations[ $translation->language_code ] = $translation;
}
}
if ( $translations ) {
$this->wpml_cache->set( $cache_key, $translations );
}
return $translations;
}
public function link_elements( WPML_Translation_Element $source_translation_element, WPML_Translation_Element $target_translation_element, $target_language = null ) {
if ( null !== $target_language ) {
$this->set_language_code( $target_translation_element, $target_language );
}
$this->set_source_element( $target_translation_element, $source_translation_element );
}
public function set_source_element( WPML_Translation_Element $element, WPML_Translation_Element $source_element ) {
$this->elements_type_matches( $element, $source_element );
$this->sitepress->set_element_language_details( $element->get_element_id(), $element->get_wpml_element_type(), $source_element->get_trid(), $element->get_language_code(), $source_element->get_language_code() );
$element->flush_cache();
}
private function elements_type_matches( $element1, $element2 ) {
if ( get_class( $element1 ) !== get_class( $element2 ) ) {
throw new UnexpectedValueException( '$source_element is not an instance of ' . get_class( $element1 ) . ': instance of ' . get_class( $element2 ) . ' received instead.' );
}
}
/**
* @param WPML_Translation_Element $element
* @param string $language_code
*/
public function set_language_code( WPML_Translation_Element $element, $language_code ) {
$element_id = $element->get_element_id();
$wpml_element_type = $element->get_wpml_element_type();
$trid = $element->get_trid();
$this->sitepress->set_element_language_details( $element_id, $wpml_element_type, $trid, $language_code );
$element->flush_cache();
}
/**
* @param WPML_Translation_Element $element
* @param int $trid
*
* @throws \UnexpectedValueException
*/
public function set_trid( WPML_Translation_Element $element, $trid ) {
if ( ! $element->get_language_code() ) {
throw new UnexpectedValueException( 'Element has no language information.' );
}
$this->sitepress->set_element_language_details( $element->get_element_id(), $element->get_wpml_element_type(), $trid, $element->get_language_code() );
$element->flush_cache();
}
/**
* @param \WPML_Translation_Element $duplicate
* @param \WPML_Translation_Element $original
*
* @throws \UnexpectedValueException
*/
public function make_duplicate_of( WPML_Translation_Element $duplicate, WPML_Translation_Element $original ) {
$this->validate_duplicable_element( $duplicate );
$this->validate_duplicable_element( $original, 'source' );
$this->set_source_element( $duplicate, $original );
update_post_meta( $duplicate->get_id(), $this->mark_as_duplicate_meta_key, $original->get_id() );
$duplicate->flush_cache();
$this->duplicated_by[ $duplicate->get_id() ] = array();
}
/**
* @param \WPML_Translation_Element $element
*
* @return WPML_Post_Element
* @throws \InvalidArgumentException
*/
public function is_a_duplicate_of( WPML_Translation_Element $element ) {
$this->validate_duplicable_element( $element );
$duplicate_of = get_post_meta( $element->get_id(), $this->mark_as_duplicate_meta_key, true );
if ( $duplicate_of ) {
return new WPML_Post_Element( $duplicate_of, $this->sitepress );
}
return null;
}
/**
* @param \WPML_Translation_Element $element
*
* @return array
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
*/
public function is_duplicated_by( WPML_Translation_Element $element ) {
$this->validate_duplicable_element( $element );
$this->init_cache_for_element( $element );
if ( ! $this->duplicated_by[ $element->get_id() ] ) {
$this->duplicated_by[ $element->get_id() ] = array();
$args = array(
'post_type' => $element->get_wp_element_type(),
'meta_query' => array(
array(
'key' => $this->mark_as_duplicate_meta_key,
'value' => $element->get_id(),
'compare' => '=',
),
),
);
$query = new WP_Query( $args );
$results = $query->get_posts();
foreach ( $results as $post ) {
$this->duplicated_by[ $element->get_id() ][] = new WPML_Post_Element( $post->ID, $this->sitepress );
}
}
return $this->duplicated_by[ $element->get_id() ];
}
/**
* @param \WPML_Translation_Element $element
* @param string $argument_name
*
* @throws \UnexpectedValueException
*/
private function validate_duplicable_element( WPML_Translation_Element $element, $argument_name = 'element' ) {
if ( ! ( $element instanceof WPML_Duplicable_Element ) ) {
throw new UnexpectedValueException( sprintf( 'Argument %s does not implement `WPML_Duplicable_Element`.', $argument_name ) );
}
}
/**
* @param \WPML_Translation_Element $element
*/
private function init_cache_for_element( WPML_Translation_Element $element ) {
if ( ! array_key_exists( $element->get_id(), $this->duplicated_by ) ) {
$this->duplicated_by[ $element->get_id() ] = array();
}
}
/**
* @param string $element_type
* @param array<string,array<string>> $sql_parts
* @param bool $skipPrivilegeChecking
*
* @return array<string,array<string>>
*/
private function get_sql_parts_for_post( $element_type, $sql_parts, $skipPrivilegeChecking = false ) {
$sql_parts['select'][] = ', p.post_title, p.post_status';
$sql_parts['join'][] = " LEFT JOIN {$this->sitepress->get_wpdb()->posts} p ON wpml_translations.element_id=p.ID";
if ( ! $this->all_statuses && 'post_attachment' !== $element_type && ! is_admin() ) {
$public_statuses_where = $this->get_public_statuses();
// the current user may not be the admin but may have read private post/page caps!
if ( current_user_can( 'read_private_pages' ) || current_user_can( 'read_private_posts' ) || $skipPrivilegeChecking ) {
$sql_parts['where'][] = ' AND (p.post_status IN (' . $public_statuses_where . ", 'draft', 'private', 'pending' ))";
} else {
$sql_parts['where'][] = ' AND (';
$sql_parts['where'][] = 'p.post_status IN (' . $public_statuses_where . ') ';
if ( $uid = $this->sitepress->get_current_user()->ID ) {
$sql_parts['where'][] = $this->sitepress->get_wpdb()->prepare( " OR (post_status in ('draft', 'private', 'pending') AND post_author = %d)", $uid );
}
$sql_parts['where'][] = ') ';
}
}
return $sql_parts;
}
/**
* @return string
*/
private function get_public_statuses() {
return wpml_prepare_in( get_post_stati( [ 'public' => true ] ) );
}
/**
* @param array<string,array<string>> $sql_parts
*
* @return array<string,array<string>>
*/
private function get_sql_parts_for_taxonomy( $sql_parts ) {
$sql_parts['select'][] = ', tm.name, tm.term_id, COUNT(tr.object_id) AS instances';
$sql_parts['join'][] = " LEFT JOIN {$this->sitepress->get_wpdb()->term_taxonomy} tt ON wpml_translations.element_id=tt.term_taxonomy_id
LEFT JOIN {$this->sitepress->get_wpdb()->terms} tm ON tt.term_id = tm.term_id
LEFT JOIN {$this->sitepress->get_wpdb()->term_relationships} tr ON tr.term_taxonomy_id=tt.term_taxonomy_id
";
$sql_parts['group_by'][] = 'GROUP BY tm.term_id';
return $sql_parts;
}
/**
* @param stdClass $translation
*
* @return bool
*/
private function must_ignore_translation( stdClass $translation ) {
return $this->skip_empty
&& (
! $translation->element_id
|| $this->must_ignore_translation_for_taxonomy( $translation )
);
}
/**
* @param stdClass $translation
*
* @return bool
*/
private function must_ignore_translation_for_taxonomy( stdClass $translation ) {
return $this->wpml_element_type_is_taxonomy( $translation->element_type )
&& $translation->instances === 0
&& ( ! $this->skip_recursions && ! _icl_tax_has_objects_recursive( $translation->element_id ) );
}
/**
* @param string $wpml_element_type
*
* @return int
*/
private function wpml_element_type_is_taxonomy( $wpml_element_type ) {
return preg_match( '#^tax_(.+)$#', $wpml_element_type );
}
/**
* @param string $wpml_element_type
*
* @return bool
*/
private function wpml_element_type_is_post( $wpml_element_type ) {
return 0 === strpos( $wpml_element_type, 'post_' );
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* @author OnTheGo Systems
*/
interface WPML_Duplicable_Element {
}