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,42 @@
<?php
namespace WPML\AbsoluteLinks;
class BlockProtector {
private $protectedBlocks = [];
public function protect( $text ) {
if ( ! function_exists( 'has_blocks' ) || ! has_blocks( $text ) ) {
return $text;
}
$integrationClass = \WPML_Gutenberg_Integration::class;
$decodeForwardSlashes = function ( $str ) {
return str_replace( '\\/', '/', $str );
};
$replaceBlockWithPlaceholder = function ( $text, $block ) {
$key = md5( $block );
$this->protectedBlocks[ $key ] = $block;
return str_replace( $block, $key, $text );
};
return wpml_collect( \WPML_Gutenberg_Integration::parse_blocks( $text ) )
->map( [ $integrationClass, 'sanitize_block' ] )
->filter( [ $integrationClass, 'has_non_empty_attributes' ] )
->map( [ $integrationClass, 'render_block' ] )
->map( $decodeForwardSlashes )
->reduce( $replaceBlockWithPlaceholder, $text );
}
public function unProtect( $text ) {
foreach ( $this->protectedBlocks as $key => $value ) {
$text = str_replace( $key, $value, $text );
}
return $text;
}
}

View File

@@ -0,0 +1,698 @@
<?php
use WPML\FP\Lst;
use WPML\FP\Str;
class AbsoluteLinks {
/** @var array */
public $custom_post_query_vars = [];
/** @var array */
public $taxonomies_query_vars = [];
/** @var array */
private $active_languages;
public function __construct() {
if ( did_action( 'init' ) ) {
$this->init_query_vars();
} else {
// init_query_vars is using $wp_taxonomies.
// We have to change priority of our action
// to make sure that all custom taxonomies are already registered.
add_action( 'init', [ $this, 'init_query_vars' ], 1000 );
}
}
public function init_query_vars() {
global $wp_post_types, $wp_taxonomies;
// Custom posts query vars.
foreach ( $wp_post_types as $k => $v ) {
if ( 'post' === $k || 'page' === $k ) {
continue;
}
if ( $v->query_var ) {
$this->custom_post_query_vars[ $k ] = $v->query_var;
}
}
// Taxonomies query vars.
foreach ( $wp_taxonomies as $k => $v ) {
if ( 'category' === $k ) {
continue;
}
if ( 'post_tag' === $k && ! $v->query_var ) {
$tag_base = get_option( 'tag_base', 'tag' );
$v->query_var = $tag_base;
}
if ( $v->query_var ) {
$this->taxonomies_query_vars[ $k ] = $v->query_var;
}
}
}
public function _process_generic_text( $source_text, &$alp_broken_links ) {
global $wpdb, $wp_rewrite, $sitepress, $sitepress_settings;
$sitepress_settings = $sitepress->get_settings();
$default_language = $sitepress->get_default_language();
$current_language = $sitepress->get_current_language();
$cache_key_args = [
$default_language,
$current_language,
md5( $source_text ),
md5( implode( '', $alp_broken_links ) ),
];
$cache_key = md5( wp_json_encode( $cache_key_args ) );
$cache_group = '_process_generic_text';
$found = false;
$text = WPML_Non_Persistent_Cache::get( $cache_key, $cache_group, $found );
if ( $found ) {
return $text;
}
$filtered_icl_post_language = filter_input( INPUT_POST, 'icl_post_language', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$block_protector = new \WPML\AbsoluteLinks\BlockProtector();
$text = $block_protector->protect( $source_text );
// We need to loop over each language so we create sticky links for all languages.
$this->active_languages = array_keys( $sitepress->get_active_languages() );
$current_language = empty( $filtered_icl_post_language ) ? $current_language : $filtered_icl_post_language;
if ( ! empty( $current_language ) ) {
$key = array_search( $current_language, $this->active_languages, true );
if ( false !== $key ) {
unset( $this->active_languages[ $key ] );
}
array_unshift( $this->active_languages, $current_language );
}
$blacklist_requests = new WPML_Absolute_Links_Blacklist(
apply_filters( 'wpml_sl_blacklist_requests', [], $sitepress )
);
foreach ( $this->active_languages as $test_language ) {
$rewrite = $this->initialize_rewrite( $current_language, $default_language, $sitepress );
$home_url = $sitepress->language_url( $test_language );
if ( 3 === $sitepress_settings['language_negotiation_type'] ) {
$home_url = preg_replace( '#\?lang=([a-z-]+)#i', '', $home_url );
}
$home_url = str_replace( '?', '\?', $home_url );
if ( $sitepress_settings['urls']['directory_for_default_language'] && $test_language === $default_language ) {
$home_url = str_replace( $default_language . '/', '', $home_url );
}
$int1 = preg_match_all( '@<a([^>]*)href="((' . rtrim( $home_url, '/' ) . ')?/([^"^>^\[^\]]+))"([^>]*)>@i', $text, $alp_matches1 );
$int2 = preg_match_all( '@<a([^>]*)href=\'((' . rtrim( $home_url, '/' ) . ')?/([^\'^>^\[^\]]+))\'([^>]*)>@i', $text, $alp_matches2 );
$alp_matches = [];
for ( $i = 0; $i < 6; $i ++ ) {
$alp_matches[ $i ] = array_merge( (array) $alp_matches1[ $i ], (array) $alp_matches2[ $i ] );
}
if ( $int1 || $int2 ) {
$def_url = [];
$url_parts = wp_parse_url( $this->get_home_url_with_no_lang_directory() );
$url_parts['path'] = isset( $url_parts['path'] ) ? $url_parts['path'] : '';
foreach ( $alp_matches[4] as $k => $dir_path ) {
if ( 0 === strpos( $dir_path, WP_CONTENT_DIR ) ) {
continue;
}
list( $lang, $dir_path ) = $this->extract_lang_from_path( $sitepress_settings, $default_language, $dir_path );
$req_uri = '/' . $dir_path;
$req_uri_array = explode( '?', $req_uri );
$req_uri = $req_uri_array[0];
$req_uri_params = '';
if ( isset( $req_uri_array[1] ) ) {
$req_uri_params = $req_uri_array[1];
}
// Separate anchor.
$req_uri_array = explode( '#', $req_uri );
$req_uri = $req_uri_array[0];
$anchor_output = isset( $req_uri_array[1] ) ? '#' . $req_uri_array[1] : '';
$home_path = wp_parse_url( get_home_url() );
if ( isset( $home_path['path'] ) ) {
$home_path = $home_path['path'];
} else {
$home_path = '';
}
$home_path = trim( $home_path, '/' );
$pathinfo = '';
$req_uri = str_replace( $pathinfo, '', rawurldecode( $req_uri ) );
$req_uri = trim( $req_uri, '/' );
$req_uri = preg_replace( "|^$home_path|", '', $req_uri );
$req_uri = trim( $req_uri, '/' );
$pathinfo = trim( $pathinfo, '/' );
$pathinfo = preg_replace( "|^$home_path|", '', $pathinfo );
$pathinfo = trim( $pathinfo, '/' );
if ( ! empty( $pathinfo ) && ! preg_match( '|^.*' . $wp_rewrite->index . '$|', $pathinfo ) ) {
$request = $pathinfo;
} else {
// If the request uri is the index, blank it out so that we don't try to match it against a rule.
if ( $req_uri === $wp_rewrite->index ) {
$req_uri = '';
}
$request = $req_uri;
}
if ( ! $request || $blacklist_requests->is_blacklisted( $request ) ) {
continue;
}
$request_match = $request;
$permalink_query_vars = [];
foreach ( (array) $rewrite as $match => $query ) {
// If the requesting file is the anchor of the match, prepend it to the path info.
if ( ( ! empty( $req_uri ) ) && ( strpos( $match, $req_uri ) === 0 ) && ( $req_uri !== $request ) ) {
$request_match = $req_uri . '/' . $request;
}
if ( preg_match( "!^$match!", $request_match, $matches ) || preg_match( "!^$match!", urldecode( $request_match ), $matches ) ) {
// Got a match.
// Trim the query of everything up to the '?'.
$query = preg_replace( '!^.+\?!', '', $query );
// Substitute the substring matches into the query.
$query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
// Parse the query.
parse_str( $query, $permalink_query_vars );
break;
}
}
$post_name = false;
$category_name = false;
$tax_name = false;
if ( isset( $permalink_query_vars['pagename'] ) ) {
$get_page_by_path = new WPML_Get_Page_By_Path( $wpdb, $sitepress, new WPML_Debug_BackTrace( null, 7 ) );
$page_by_path = $get_page_by_path->get( $permalink_query_vars['pagename'], $test_language );
$post_name = $permalink_query_vars['pagename'];
if ( ! empty( $page_by_path->post_type ) ) {
$post_type = 'page';
} else {
$post_type = 'post';
}
} elseif ( isset( $permalink_query_vars['name'] ) ) {
$post_name = $permalink_query_vars['name'];
$post_type = 'post';
} elseif ( isset( $permalink_query_vars['category_name'] ) ) {
$category_name = $permalink_query_vars['category_name'];
} elseif ( isset( $permalink_query_vars['p'] ) ) { // Case or /archives/%post_id.
list( $post_type, $post_name ) = $wpdb->get_row(
$wpdb->prepare( "SELECT post_type, post_name FROM {$wpdb->posts} WHERE id=%d", $permalink_query_vars['p'] ),
ARRAY_N
);
} else {
if ( empty( $this->custom_post_query_vars ) || empty( $this->taxonomies_query_vars ) ) {
$this->init_query_vars();
}
foreach ( $this->custom_post_query_vars as $query_vars_key => $query_vars_value ) {
if ( isset( $permalink_query_vars[ $query_vars_value ] ) ) {
$post_name = $permalink_query_vars[ $query_vars_value ];
$post_type = $query_vars_key;
break;
}
}
foreach ( $this->taxonomies_query_vars as $query_vars_value ) {
if ( isset( $permalink_query_vars[ $query_vars_value ] ) ) {
$tax_name = $permalink_query_vars[ $query_vars_value ];
$tax_type = $query_vars_value;
break;
}
}
}
if ( $post_name && isset( $post_type ) ) {
$get_page_by_path = new WPML_Get_Page_By_Path( $wpdb, $sitepress, new WPML_Debug_BackTrace( null, 7 ) );
$p = $get_page_by_path->get( $post_name, $test_language, OBJECT, $post_type );
if ( empty( $p ) ) { // Fail safe.
$switchLang = new WPML_Temporary_Switch_Language( $sitepress, $test_language );
remove_filter( 'url_to_postid', array( $sitepress, 'url_to_postid' ) );
$post_id = url_to_postid( $home_path . '/' . $post_name );
add_filter( 'url_to_postid', array( $sitepress, 'url_to_postid' ) );
$switchLang->restore_lang();
if ( $post_id ) {
$p = get_post( $post_id );
}
}
if ( $p ) {
$offsite_url = get_post_meta( $p->ID, '_cms_nav_offsite_url', true );
if ( 'page' === $p->post_type && $offsite_url ) {
$def_url = $this->get_regex_replacement_offline(
$def_url,
$offsite_url,
$sitepress_settings['language_negotiation_type'],
$lang,
$dir_path,
$home_url,
$anchor_output
);
} elseif ( ! $this->is_pagination_in_post( $dir_path, $post_name ) ) {
$def_url = $this->get_regex_replacement(
$def_url,
'page' === $p->post_type ? 'page_id' : 'p',
$p->ID,
$sitepress_settings['language_negotiation_type'],
$lang,
$dir_path,
$home_url,
$url_parts,
$req_uri_params,
$anchor_output
);
}
} else {
$alp_broken_links[ $alp_matches[2][ $k ] ] = [];
$name = wpml_like_escape( $post_name );
$p = $this->_get_ids_and_post_types( $name );
if ( $p ) {
foreach ( $p as $post_suggestion ) {
if ( 'page' === $post_suggestion->post_type ) {
$qvid = 'page_id';
} else {
$qvid = 'p';
}
$alp_broken_links[ $alp_matches[2][ $k ] ]['suggestions'][] = [
'absolute' => '/' . ltrim( $url_parts['path'], '/' ) . '?' . $qvid . '=' . $post_suggestion->ID,
'perma' => '/' . ltrim( str_replace( site_url(), '', get_permalink( $post_suggestion->ID ) ), '/' ),
];
}
}
}
} elseif ( $category_name ) {
if ( false !== strpos( $category_name, '/' ) ) {
$splits = explode( '/', $category_name );
$category_name = array_pop( $splits );
$category_parent = array_pop( $splits );
$category_parent_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM {$wpdb->terms} WHERE slug=%s", $category_parent ) );
$c = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id FROM {$wpdb->terms} t JOIN {$wpdb->term_taxonomy} x ON x.term_id=t.term_id AND x.taxonomy='category' AND x.parent=%d AND t.slug=%s", $category_parent_id, $category_name ) );
} else {
$c = $wpdb->get_row( $wpdb->prepare( "SELECT term_id FROM {$wpdb->terms} WHERE slug=%s", $category_name ) );
}
if ( $c ) {
$def_url = $this->get_regex_replacement(
$def_url,
'cat_ID',
$c->term_id,
$sitepress_settings['language_negotiation_type'],
$lang,
$dir_path,
$home_url,
$url_parts,
$req_uri_params,
$anchor_output
);
} elseif ( isset( $name ) ) {
$alp_broken_links[ $alp_matches[2][ $k ] ] = [];
$c = $wpdb->get_results(
$wpdb->prepare( "SELECT term_id FROM {$wpdb->terms} WHERE slug LIKE %s", [ $name . '%' ] )
);
if ( $c ) {
foreach ( $c as $cat_suggestion ) {
$perma = '/' . ltrim( str_replace( get_home_url(), '', get_category_link( $cat_suggestion->term_id ) ), '/' );
$alp_broken_links[ $alp_matches[2][ $k ] ]['suggestions'][] = [
'absolute' => '?cat_ID=' . $cat_suggestion->term_id,
'perma' => $perma,
];
}
}
}
} elseif ( $tax_name && isset( $tax_type ) ) {
$def_url = $this->get_regex_replacement(
$def_url,
$tax_type,
$tax_name,
$sitepress_settings['language_negotiation_type'],
$lang,
$dir_path,
$home_url,
$url_parts,
$req_uri_params,
$anchor_output
);
}
}
if ( ! empty( $def_url ) ) {
$text = preg_replace( array_keys( $def_url ), array_values( $def_url ), $text );
}
$tx_qvs = ! empty( $this->taxonomies_query_vars ) && is_array( $this->taxonomies_query_vars ) ? '|' . join( '|', $this->taxonomies_query_vars ) : '';
$post_qvs = ! empty( $this->custom_posts_query_vars ) && is_array( $this->custom_posts_query_vars ) ? '|' . join( '|', $this->custom_posts_query_vars ) : '';
$int = preg_match_all( '@href=[\'"](' . rtrim( get_home_url(), '/' ) . '/?\?(p|page_id' . $tx_qvs . $post_qvs . ')=([0-9a-z-]+)(#.+)?)[\'"]@i', $text, $matches2 );
if ( $int ) {
$url_parts = wp_parse_url( rtrim( get_home_url(), '/' ) . '/' );
$text = preg_replace( '@href=[\'"](' . rtrim( get_home_url(), '/' ) . '/?\?(p|page_id' . $tx_qvs . $post_qvs . ')=([0-9a-z-]+)(#.+)?)[\'"]@i', 'href="/' . ltrim( $url_parts['path'], '/' ) . '?$2=$3$4"', $text );
}
}
}
$text = $block_protector->unProtect( $text );
WPML_Non_Persistent_Cache::set( $cache_key, $text, $cache_group );
return $text;
}
private function get_home_url_with_no_lang_directory() {
global $sitepress, $sitepress_settings;
$sitepress_settings = $sitepress->get_settings();
$home_url = rtrim( get_home_url(), '/' );
if ( 1 === $sitepress_settings['language_negotiation_type'] ) {
// Strip lang directory from end if it's there.
$exp = explode( '/', $home_url );
$lang = end( $exp );
if ( $this->does_lang_exist( $lang ) ) {
$home_url = substr( $home_url, 0, strlen( $home_url ) - strlen( $lang ) );
}
}
return $home_url;
}
private function does_lang_exist( $lang ) {
return in_array( $lang, $this->active_languages, true );
}
public function _get_ids_and_post_types( $name ) {
global $wpdb;
static $cache = [];
$name = rawurlencode( $name );
if ( ! isset( $cache[ $name ] ) ) {
$cache[ $name ] = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_type FROM {$wpdb->posts} WHERE post_name LIKE %s AND post_type IN('post','page')", $name . '%' ) );
}
return $cache[ $name ];
}
private function initialize_rewrite( $current_language, $default_language, $sitepress ) {
global $wp_rewrite;
$key = $current_language . $default_language;
$found = false;
$rewrite = WPML_Non_Persistent_Cache::get( $key, __CLASS__, $found );
if ( $found ) {
return $rewrite;
}
if ( ! isset( $wp_rewrite ) ) {
require_once ABSPATH . WPINC . '/rewrite.php';
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_rewrite = new WP_Rewrite();
}
if ( $current_language === $default_language ) {
$rewrite = $wp_rewrite->wp_rewrite_rules();
} else {
remove_filter( 'option_rewrite_rules', [ $sitepress, 'rewrite_rules_filter' ] );
add_filter( 'wpml_st_disable_rewrite_rules', '__return_true' );
$rewrite = $wp_rewrite->wp_rewrite_rules();
remove_filter( 'wpml_st_disable_rewrite_rules', '__return_true' );
}
$rewrite = $this->all_rewrite_rules( $rewrite );
WPML_Non_Persistent_Cache::set( $key, $rewrite, __CLASS__ );
return $rewrite;
}
public function all_rewrite_rules( $rewrite ) {
global $sitepress;
if ( ! class_exists( 'WPML\ST\SlugTranslation\Hooks\Hooks' ) ) {
return $rewrite;
}
$active_languages = $sitepress->get_active_languages();
$current_language = $sitepress->get_current_language();
$default_language = $sitepress->get_default_language();
$cache_keys = [ $current_language, $default_language ];
$cache_keys[] = md5( wp_json_encode( $active_languages ) );
$cache_keys[] = md5( wp_json_encode( $rewrite ) );
$cache_key = implode( ':', $cache_keys );
$cache_group = 'all_rewrite_rules';
$cache_found = false;
$final_rules = WPML_Non_Persistent_Cache::get( $cache_key, $cache_group, $cache_found );
if ( $cache_found ) {
return $final_rules;
}
$final_rules = $rewrite;
foreach ( $active_languages as $next_language ) {
if ( $next_language['code'] === $default_language ) {
continue;
}
$sitepress->switch_lang( $next_language['code'] );
$translated_rules = ( new \WPML\ST\SlugTranslation\Hooks\HooksFactory() )->create()->filter( $final_rules );
if ( is_array( $translated_rules ) && is_array( $final_rules ) ) {
$new_rules = array_diff_assoc( $translated_rules, $final_rules );
$final_rules = array_merge( $new_rules, $final_rules );
}
}
$sitepress->switch_lang( $current_language );
WPML_Non_Persistent_Cache::set( $cache_key, $final_rules, $cache_group );
return $final_rules;
}
private function get_regex_replacement(
$def_url,
$type,
$type_id,
$lang_negotiation,
$lang,
$dir_path,
$home_url,
$url_parts,
$req_uri_params,
$anchor_output
) {
$type_id = $this->maybeStripParentTerm( $type_id );
if ( 1 === $lang_negotiation && $lang ) {
$langprefix = '/' . $lang;
} else {
$langprefix = '';
}
$perm_url = '(' . rtrim( $home_url, '/' ) . ')?' . $langprefix . '/' . str_replace( '?', '\?', $dir_path );
$regk = '@href=[\'"](' . self::escapePlusSign( $perm_url ) . ')[\'"]@i';
$regv = 'href="/' . ltrim( $url_parts['path'], '/' ) . '?' . $type . '=' . $type_id;
if ( '' !== $req_uri_params ) {
$regv .= '&' . $req_uri_params;
}
$regv .= $anchor_output . '"';
$def_url[ $regk ] = $regv;
return $def_url;
}
/**
* Split parent/child term slug and get only the last part.
*
* @see https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-7036
*
* If $typeId is a child term of some taxonomy, then it comes here as `parent/child'
* in next stages WordPress will use it in url like `?category=parent/child` and will try to resolve
* what category has slug `parent/child'. WordPress must actually try to find just `child` so the code
* below gets only last part of slash containing $typeId
*
* @param string $typeId Type slug.
*
* @return string
*/
private function maybeStripParentTerm( $typeId ) {
return Lst::nth( -1, Str::split( '/', $typeId ) );
}
private function get_regex_replacement_offline(
$def_url,
$offsite_url,
$lang_negotiation,
$lang,
$dir_path,
$home_url,
$anchor_output
) {
if ( 1 === $lang_negotiation && $lang ) {
$langprefix = '/' . $lang;
} else {
$langprefix = '';
}
$perm_url = '(' . rtrim( $home_url, '/' ) . ')?' . $langprefix . '/' . str_replace( '?', '\?', $dir_path );
$regk = '@href=["\'](' . self::escapePlusSign( $perm_url ) . ')["\']@i';
$regv = 'href="' . $offsite_url . $anchor_output . '"';
$def_url[ $regk ] = $regv;
return $def_url;
}
/**
* @param string $url
*
* @return string
*/
private static function escapePlusSign( $url ) {
return str_replace( '+', '\+', $url );
}
private function extract_lang_from_path( $sitepress_settings, $default_language, $dir_path ) {
$lang = false;
if ( 1 === $sitepress_settings['language_negotiation_type'] ) {
$exp = explode( '/', $dir_path, 2 );
$lang = $exp[0];
if ( $this->does_lang_exist( $lang ) ) {
$dir_path = isset( $exp[1] ) ? $exp[1] : '';
} else {
$lang = false;
}
}
return [ $lang, $dir_path ];
}
public function process_string( $st_id ) {
global $wpdb;
if ( $st_id ) {
$table = $wpdb->prefix . 'icl_string_translations';
$data = $wpdb->get_row( $wpdb->prepare( "SELECT value, string_id, language FROM {$table} WHERE id=%d", $st_id ) );
$string_value = $data->value;
$string_type = $wpdb->get_var( $wpdb->prepare( "SELECT type FROM {$wpdb->prefix}icl_strings WHERE id=%d", $data->string_id ) );
if ( 'LINK' === $string_type ) {
$string_value_up = $this->convert_url( $string_value, $data->language );
} else {
$string_value_up = $this->convert_text( $string_value );
}
if ( $string_value_up !== $string_value ) {
$wpdb->update(
$table,
[
'value' => $string_value_up,
'status' => ICL_STRING_TRANSLATION_COMPLETE,
],
[ 'id' => $st_id ]
);
}
}
}
public function process_post( $post_id ) {
global $wpdb, $sitepress;
delete_post_meta( $post_id, '_alp_broken_links' );
$post = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %s", $post_id ) );
$this_post_language = $sitepress->get_language_for_element( $post_id, 'post_' . $post->post_type );
$current_language = $sitepress->get_current_language();
$sitepress->switch_lang( $this_post_language );
$post_content = $this->convert_text( $post->post_content );
$sitepress->switch_lang( $current_language );
if ( $post_content !== $post->post_content ) {
$wpdb->update( $wpdb->posts, [ 'post_content' => $post_content ], [ 'ID' => $post_id ] );
}
update_post_meta( $post_id, '_alp_processed', time() );
}
public function convert_text( $text ) {
$alp_broken_links = [];
return $this->_process_generic_text( $text, $alp_broken_links );
}
public function convert_url( $url, $lang = null ) {
global $sitepress;
if ( $this->is_home( $url ) ) {
$absolute_url = $sitepress->convert_url( $url, $lang );
} else {
$html = '<a href="' . $url . '">removeit</a>';
$html = $this->convert_text( $html );
$absolute_url = str_replace( [ '<a href="', '">removeit</a>' ], [ '', '' ], $html );
}
return $absolute_url;
}
public function is_home( $url ) {
return untrailingslashit( get_home_url() ) === untrailingslashit( $url );
}
/**
* Check if the link is the pagination inside the post.
*
* @param string $url
* @param string $post_name
*
* @return bool
*/
private function is_pagination_in_post( $url, $post_name ) {
$is_pagination_url_in_post = false !== mb_strpos( $url, $post_name . '/page/' );
/**
* Check if the given URL is the pagination inside the post.
*
* @param bool $is_pagination_url_in_post
* @param string $url
* @param string $post_name
*/
return apply_filters( 'wpml_is_pagination_url_in_post', $is_pagination_url_in_post, $url, $post_name );
}
}

View File

@@ -0,0 +1,158 @@
<?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
$hasFilter = 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 );
}
if ( $hasFilter ) {
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();
}
}

View File

@@ -0,0 +1,255 @@
<?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(
WPML_TM_Action_Helper $helper,
WPML_TM_Blog_Translators $blog_translators,
WPML_TM_Records $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 = \WPML\TM\API\Job\Map::fromRid( $rid );
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 ) {
$statusesUpdater = $this->get_translation_statuses_updater( $post_id, $translations );
/**
* The filter allows to delegate the status update for translations.
*
* @param bool false (default) if we should update immediately or true if done at a different stage.
* @param int $post_id The original post ID.
* @param callable The updater function to execute.
*
* @since 2.11.0
*
*/
if ( ! apply_filters( 'wpml_tm_delegate_translation_statuses_update', false, $post_id, $statusesUpdater ) ) {
call_user_func( $statusesUpdater );
}
}
}
}
/**
* @param int $post_id
* @param stdClass[] $translations
*
* @return Closure
*/
public function get_translation_statuses_updater( $post_id, $translations ) {
return function () use ( $post_id, $translations ) {
$needsUpdate = false;
$md5 = $this->action_helper->post_md5( $post_id );
foreach ( $translations as $translation ) {
if ( ! $translation->original ) {
$statusRecord = $this->tm_records->icl_translation_status_by_translation_id( $translation->translation_id );
if ( $md5 !== $statusRecord->md5() ) {
$needsUpdate = true;
$status = $statusRecord->status();
$translation_package = $this->action_helper->create_translation_package( $post_id );
$data = [
'translation_id' => $translation->translation_id,
'needs_update' => 1,
'md5' => $md5,
'translation_package' => serialize( $translation_package ),
'status' => $status === ICL_TM_ATE_CANCELLED ? ICL_TM_NOT_TRANSLATED : $status,
];
$this->action_helper->get_tm_instance()->update_translation_status( $data );
}
}
}
return $needsUpdate;
};
}
/**
* 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];
}
}

View File

@@ -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' ) );

View File

@@ -0,0 +1,189 @@
<?php
if ( ! defined( 'ICL_DISABLE_CACHE' ) ) {
define( 'ICL_DISABLE_CACHE', false );
}
// phpcs:disable PEAR.NamingConventions.ValidClassName.Invalid
// phpcs:disable PEAR.NamingConventions.ValidClassName.StartWithCapital
/**
* Class icl_cache
*/
class icl_cache {
/** @var array */
protected $data;
/** @var string */
protected $name;
/** @var bool */
protected $cache_to_option;
/** @var bool */
protected $cache_needs_saving;
public function __construct( $name = '', $cache_to_option = false ) {
$this->data = [];
$this->name = $name;
$this->cache_to_option = $cache_to_option;
$this->cache_needs_saving = false;
$this->init();
}
public function init() {
if ( $this->cache_to_option ) {
$this->data = icl_cache_get( $this->name . '_cache_class' );
if ( false === $this->data ) {
$this->data = [];
}
add_action( 'shutdown', [ $this, 'save_cache_if_required' ] );
}
}
public function save_cache_if_required() {
if ( $this->cache_needs_saving ) {
icl_cache_set( $this->name . '_cache_class', $this->data );
$this->cache_needs_saving = false;
}
}
public function get( $key ) {
if ( ICL_DISABLE_CACHE ) {
return null;
}
return isset( $this->data[ $key ] ) ? $this->data[ $key ] : false;
}
public function has_key( $key ) {
if ( ICL_DISABLE_CACHE ) {
return false;
}
return array_key_exists( $key, (array) $this->data );
}
public function set( $key, $value ) {
if ( ICL_DISABLE_CACHE ) {
return;
}
if ( $this->cache_to_option ) {
$old_value = null;
if ( isset( $this->data[ $key ] ) ) {
$old_value = $this->data[ $key ];
}
if ( $old_value !== $value ) {
$this->data[ $key ] = $value;
$this->cache_needs_saving = true;
}
} else {
$this->data[ $key ] = $value;
}
}
public function clear() {
$this->data = array();
if ( $this->cache_to_option ) {
icl_cache_clear( $this->name . '_cache_class' );
}
}
}
if ( ! function_exists( 'icl_disable_cache' ) ) {
function icl_disable_cache() {
return defined( 'ICL_DISABLE_CACHE' ) && ICL_DISABLE_CACHE;
}
}
if ( ! function_exists( 'icl_cache_get' ) ) {
function icl_cache_get( $key ) {
$result = false;
if ( ! icl_disable_cache() ) {
$icl_cache = get_option( '_icl_cache' );
$result = isset( $icl_cache[ $key ] ) ? $icl_cache[ $key ] : false;
}
return $result;
}
}
if ( ! function_exists( 'icl_cache_set' ) ) {
function icl_cache_set( $key, $value = null ) {
global $switched;
if ( empty( $switched ) && ! icl_disable_cache() ) {
$icl_cache = get_option( '_icl_cache' );
if ( false === $icl_cache ) {
$icl_cache = [];
delete_option( '_icl_cache' );
}
if ( ! isset( $icl_cache[ $key ] ) || $icl_cache[ $key ] !== $value ) {
if ( ! is_null( $value ) ) {
$icl_cache[ $key ] = $value;
} elseif ( isset( $icl_cache[ $key ] ) ) {
unset( $icl_cache[ $key ] );
}
update_option( '_icl_cache', $icl_cache, 'no' );
}
}
}
}
if ( ! function_exists( 'icl_cache_clear' ) ) {
function icl_cache_clear( $key = false, $key_as_prefix = false ) {
if ( ! icl_disable_cache() ) {
/**
* @var WPML_Term_Translation $wpml_term_translations
* @var WPML_Post_Translation $wpml_post_translations
*/
global $wpml_term_translations, $wpml_post_translations;
$wpml_term_translations->reload();
$wpml_post_translations->reload();
if ( false === $key ) {
delete_option( '_icl_cache' );
} else {
/** @var array $icl_cache */
$icl_cache = get_option( '_icl_cache' );
if ( is_array( $icl_cache ) ) {
if ( isset( $icl_cache[ $key ] ) ) {
unset( $icl_cache[ $key ] );
}
if ( $key_as_prefix ) {
$cache_keys = array_keys( $icl_cache );
foreach ( $cache_keys as $cache_key ) {
if ( strpos( $cache_key, $key ) === 0 ) {
unset( $icl_cache[ $cache_key ] );
}
}
}
// Special cache of 'per language' - clear different statuses.
if ( false !== strpos( $key, '_per_language' ) ) {
foreach ( $icl_cache as $k => $v ) {
if ( false !== strpos( $k, $key . '#' ) ) {
unset( $icl_cache[ $k ] );
}
}
}
update_option( '_icl_cache', $icl_cache, 'no' );
}
}
}
do_action( 'wpml_cache_clear' );
}
}
function w3tc_translate_cache_key_filter( $key ) {
global $sitepress;
return $sitepress->get_current_language() . $key;
}

View File

@@ -0,0 +1,47 @@
<?php
define( 'WPML_TM_FOLDER', 'tm' );
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' );

View File

@@ -0,0 +1,163 @@
<?php
if ( file_exists( WPML_PLUGIN_PATH . '/inc/sandbox.inc' ) ) {
require WPML_PLUGIN_PATH . '/inc/sandbox.inc';
define( 'OTG_SANDBOX', true );
} else {
define( 'OTG_SANDBOX', false );
}
if ( ! defined( 'ICL_API_ENDPOINT' ) ) {
define( 'ICL_API_ENDPOINT', 'https://www.icanlocalize.com' );
}
if ( ! defined( 'OTG_TRANSLATION_PROXY_URL' ) ) {
define( 'OTG_TRANSLATION_PROXY_URL', 'https://tp.wpml.org' );
}
if ( ! defined( 'ICL_PLUGIN_INACTIVE' ) ) {
define( 'ICL_PLUGIN_INACTIVE', false );
}
if ( defined( 'PHP_INT_MIN' ) ) {
// phpcs:disable PHPCompatibility.Constants.NewConstants.php_int_minFound -- A check for the presence of the constant is made
define( 'WPML_PRIORITY_BEFORE_EVERYTHING', PHP_INT_MIN );
// phpcs:enable PHPCompatibility.Constants.NewConstants.php_int_minFound
} else {
define( 'WPML_PRIORITY_BEFORE_EVERYTHING', ~PHP_INT_MAX );
}
define( 'ICL_TM_NOT_TRANSLATED', 0 );
define( 'ICL_TM_WAITING_FOR_TRANSLATOR', 1 );
define( 'ICL_TM_IN_PROGRESS', 2 );
define( 'ICL_TM_NEEDS_UPDATE', 3 ); // virt. status code (based on needs_update)
define( 'ICL_TM_TRANSLATION_READY_TO_DOWNLOAD', 4 ); // when translation is ready in TP
define( 'ICL_TM_DUPLICATE', 9 );
define( 'ICL_TM_COMPLETE', 10 );
define( 'ICL_TM_IN_BASKET', 20 );
define( 'ICL_TM_NEEDS_REVIEW', 30 ); // Virtual status - NOT STORE IN DB.
define( 'ICL_TM_ATE_NEEDS_RETRY', 40 );
// @since 3.2
define( 'ICL_TM_PENDING_TP', 102 );
define( 'ICL_TM_ATE_CANCELLED', 42 );
/** @deprecated Use constants in WPML_TM_Emails_Settings instead */
define( 'ICL_TM_NOTIFICATION_NONE', 0 );
/** @deprecated Use WPML_TM_Emails_Settings::NOTIFY_IMMEDIATELY instead */
define( 'ICL_TM_NOTIFICATION_IMMEDIATELY', 1 );
/** @deprecated Use WPML_TM_Emails_Settings::NOTIFY_DAILY instead */
define( 'ICL_TM_NOTIFICATION_DAILY', 2 );
define( 'ICL_TM_TMETHOD_MANUAL', 0 );
define( 'ICL_TM_TMETHOD_EDITOR', 1 );
define( 'ICL_TM_TMETHOD_PRO', 2 );
define( 'ICL_TM_TMETHOD_ATE', 'ATE' );
if ( ! defined( 'ICL_TM_DOCS_PER_PAGE' ) ) {
define( 'ICL_TM_DOCS_PER_PAGE', 20 );
}
define( 'ICL_ASIAN_LANGUAGE_CHAR_SIZE', 6 );
/* legacy? */
define( 'CMS_REQUEST_WAITING_FOR_PROJECT_CREATION', 1 );
define( 'ICL_FINANCE_LINK', '/finance' );
define( 'MESSAGE_TRANSLATION_IN_PROGRESS', 3 );
define( 'MESSAGE_TRANSLATION_COMPLETE', 4 );
define( 'ICL_LANG_SEL_BLUE_FONT_CURRENT_NORMAL', '#ffffff' );
define( 'ICL_LANG_SEL_BLUE_FONT_CURRENT_HOVER', '#000000' );
define( 'ICL_LANG_SEL_BLUE_BACKGROUND_CURRENT_NORMAL', '#0099cc' );
define( 'ICL_LANG_SEL_BLUE_BACKGROUND_CURRENT_HOVER', '#0099cc' );
define( 'ICL_LANG_SEL_BLUE_FONT_OTHER_NORMAL', '#000000' );
define( 'ICL_LANG_SEL_BLUE_FONT_OTHER_HOVER', '#000000' );
define( 'ICL_LANG_SEL_BLUE_BACKGROUND_OTHER_NORMAL', '#eeeeee' );
define( 'ICL_LANG_SEL_BLUE_BACKGROUND_OTHER_HOVER', '#cccccc' );
define( 'ICL_LANG_SEL_BLUE_BORDER', '#000000' );
define( 'ICL_LANG_SEL_WHITE_FONT_CURRENT_NORMAL', '#444444' );
define( 'ICL_LANG_SEL_WHITE_FONT_CURRENT_HOVER', '#000000' );
define( 'ICL_LANG_SEL_WHITE_BACKGROUND_CURRENT_NORMAL', '#ffffff' );
define( 'ICL_LANG_SEL_WHITE_BACKGROUND_CURRENT_HOVER', '#eeeeee' );
define( 'ICL_LANG_SEL_WHITE_FONT_OTHER_NORMAL', '#444444' );
define( 'ICL_LANG_SEL_WHITE_FONT_OTHER_HOVER', '#000000' );
define( 'ICL_LANG_SEL_WHITE_BACKGROUND_OTHER_NORMAL', '#ffffff' );
define( 'ICL_LANG_SEL_WHITE_BACKGROUND_OTHER_HOVER', '#eeeeee' );
define( 'ICL_LANG_SEL_WHITE_BORDER', '#aaaaaa' );
define( 'ICL_LANG_SEL_GRAY_FONT_CURRENT_NORMAL', '#222222' );
define( 'ICL_LANG_SEL_GRAY_FONT_CURRENT_HOVER', '#000000' );
define( 'ICL_LANG_SEL_GRAY_BACKGROUND_CURRENT_NORMAL', '#eeeeee' );
define( 'ICL_LANG_SEL_GRAY_BACKGROUND_CURRENT_HOVER', '#dddddd' );
define( 'ICL_LANG_SEL_GRAY_FONT_OTHER_NORMAL', '#222222' );
define( 'ICL_LANG_SEL_GRAY_FONT_OTHER_HOVER', '#000000' );
define( 'ICL_LANG_SEL_GRAY_BACKGROUND_OTHER_NORMAL', '#eeeeee' );
define( 'ICL_LANG_SEL_GRAY_BACKGROUND_OTHER_HOVER', '#dddddd' );
define( 'ICL_LANG_SEL_GRAY_BORDER', '#555555' );
define( 'ICL_PRO_TRANSLATION_COST_PER_WORD', 0.09 );
define( 'ICL_PRO_TRANSLATION_PICKUP_XMLRPC', 0 );
define( 'ICL_PRO_TRANSLATION_PICKUP_POLLING', 1 );
if ( ! defined( 'ICL_REMOTE_WPML_CONFIG_FILES_INDEX' ) ) {
define( 'ICL_REMOTE_WPML_CONFIG_FILES_INDEX', 'http://cdn.wpml.org/' );
}
define( 'ICL_ICONS_URL', ICL_PLUGIN_URL . '/res/img/' );
define( 'WPML_ELEMENT_IS_NOT_TRANSLATED', 0 );
define( 'WPML_ELEMENT_IS_TRANSLATED', 1 );
define( 'WPML_ELEMENT_IS_DUPLICATED', 2 );
define( 'WPML_ELEMENT_IS_A_DUPLICATE', 3 );
define( 'WPML_STRING_TABLE_NAME_CONTEXT_LENGTH', 160 );
define( 'WPML_QUERY_IS_ROOT', 1 );
define( 'WPML_QUERY_IS_OTHER_THAN_ROOT', 2 );
define( 'WPML_QUERY_IS_NOT_FOR_POST', 3 );
define( 'WPML_XDOMAIN_DATA_OFF', 0 );
define( 'WPML_XDOMAIN_DATA_GET', 1 );
define( 'WPML_XDOMAIN_DATA_POST', 2 );
define( 'WPML_TT_TAXONOMIES_NOT_TRANSLATED', 1 );
define( 'WPML_TT_TAXONOMIES_ALL', 0 );
// This sets the number of rows in the table to be displayed by this class, not the actual number of terms.
define( 'WPML_TT_TERMS_PER_PAGE', 10 );
define( 'WPML_TRANSLATE_CUSTOM_FIELD', 2 );
define( 'WPML_COPY_CUSTOM_FIELD', 1 );
define( 'WPML_IGNORE_CUSTOM_FIELD', 0 );
define( 'WPML_COPY_ONCE_CUSTOM_FIELD', 3 );
define( 'WPML_POST_META_CONFIG_INDEX_SINGULAR', 'custom-field' );
define( 'WPML_POST_META_SETTING_INDEX_SINGULAR', 'custom_field' );
define( 'WPML_POST_META_CONFIG_INDEX_PLURAL', 'custom-fields' );
define( 'WPML_POST_META_SETTING_INDEX_PLURAL', 'custom_fields_translation' );
define( 'WPML_TERM_META_CONFIG_INDEX_SINGULAR', 'custom-term-field' );
define( 'WPML_TERM_META_CONFIG_INDEX_PLURAL', 'custom-term-fields' );
define( 'WPML_TERM_META_SETTING_INDEX_SINGULAR', 'custom_term_field' );
define( 'WPML_TERM_META_SETTING_INDEX_PLURAL', 'custom_term_fields_translation' );
define( 'WPML_POST_META_READONLY_SETTING_INDEX', 'custom_fields_readonly_config' );
define( 'WPML_TERM_META_READONLY_SETTING_INDEX', 'custom_term_fields_readonly_config' );
define( 'WPML_POST_META_UNLOCKED_SETTING_INDEX', 'custom_fields_unlocked_config' );
define( 'WPML_TERM_META_UNLOCKED_SETTING_INDEX', 'custom_term_fields_unlocked_config' );
define( 'WPML_POST_TYPE_READONLY_SETTING_INDEX', 'custom_types_readonly_config' );
define( 'WPML_LANGUAGE_NEGOTIATION_TYPE_DIRECTORY', 1 );
define( 'WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN', 2 );
define( 'WPML_LANGUAGE_NEGOTIATION_TYPE_PARAMETER', 3 );
define( 'WPML_ELEMENT_TRANSLATIONS_CACHE_GROUP', 'element_translations' );
define( 'WEBSITE_DETAILS_TRANSIENT_KEY', 'wpml_icl_query_website_details' );
define( 'WPML_CONTENT_TYPE_DONT_TRANSLATE', 0 );
define( 'WPML_CONTENT_TYPE_TRANSLATE', 1 );
define( 'WPML_CONTENT_TYPE_DISPLAY_AS_IF_TRANSLATED', 2 );

View File

@@ -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();
}
}
}

View File

@@ -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 );

View File

@@ -0,0 +1,7 @@
<?php
function get_debug_info() {
global $sitepress, $wpdb;
return new WPML_Debug_Information( $wpdb, $sitepress );
}

View File

@@ -0,0 +1,49 @@
<?php
if ( ! function_exists( 'object_to_array' ) ) {
function object_to_array( $obj ) {
return json_decode( json_encode( $obj ), true );
}
}
if ( ! function_exists( 'wpml_get_admin_url' ) ) {
/**
* A more helpful version of `admin_url`
* Is not called it `wpml_admin_url` because there is already a class with the same name
*
* @param array $args
*
* @return string
*/
function wpml_get_admin_url( array $args = array() ) {
if ( ! $args ) {
return admin_url();
}
$default_args = array(
'path' => '',
'scheme' => 'admin',
'query' => array(),
);
$args = array_merge( $default_args, $args );
$admin_url = '';
if ( $args['path'] || $args['scheme'] ) {
$admin_url = admin_url( $args['path'], $args['scheme'] );
}
if ( $args['query'] ) {
$admin_url_parsed = wp_parse_url( $admin_url );
if ( is_string( $args['query'] ) ) {
$admin_url_parsed['query'] = $args['query'];
} elseif ( is_array( $args['query'] ) ) {
$admin_url_parsed['query'] = http_build_query( $args['query'] );
}
$admin_url = http_build_url( $admin_url_parsed );
}
return $admin_url;
}
}

View File

@@ -0,0 +1,905 @@
<?php
use WPML\TM\Editor\ClassicEditorActions;
use WPML\TM\Jobs\Query\CompositeQuery;
use WPML\TM\Jobs\Query\LimitQueryHelper;
use WPML\TM\Jobs\Query\OrderQueryHelper;
use WPML\TM\Jobs\Query\PackageQuery;
use WPML\TM\Jobs\Query\PostQuery;
use WPML\TM\Jobs\Query\QueryBuilder;
use WPML\TM\Jobs\Query\StringQuery;
use WPML\TM\Jobs\Query\StringsBatchQuery;
use function WPML\Container\make;
use \WPML\Setup\Option as SetupOptions;
if ( ! \WPML\Plugins::isTMActive() && ( ! wpml_is_setup_complete() || false !== SetupOptions::isTMAllowed() ) ) {
/**
* @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();
}
/**
* @depecated since WPML 4.5.0
*
* @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_Custom_XML_Factory
*/
function wpml_tm_custom_xml_factory() {
static $tm_custom_xml_factory;
if ( ! $tm_custom_xml_factory ) {
$tm_custom_xml_factory = new WPML_Custom_XML_Factory();
}
return $tm_custom_xml_factory;
}
/**
* @return \WPML_Custom_XML_UI_Hooks
*/
function wpml_tm_custom_xml_ui_hooks() {
static $tm_custom_xml_ui_hooks;
if ( ! $tm_custom_xml_ui_hooks ) {
global $sitepress;
$factory = wpml_tm_custom_xml_factory();
if ( $factory ) {
$tm_custom_xml_ui_hooks = new WPML_Custom_XML_UI_Hooks( $factory->create_resources( $sitepress->get_wp_api() ) );
}
}
return $tm_custom_xml_ui_hooks;
}
/**
* @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, $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_tm_load_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_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 (
defined( 'WPML_TM_FOLDER' ) && 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-tm.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.
*
* @return WPML_TM_ATE_Models_Job_Create
*/
function wpml_tm_create_ATE_job_creation_model( $job_id ) {
$job_factory = wpml_tm_load_job_factory();
$translation_job = $job_factory->get_translation_job( $job_id, false, 0, true );
$rid = \WPML\TM\API\Job\Map::fromJobId( $job_id );
$job = new WPML_TM_ATE_Models_Job_Create();
$job->id = $job_id;
$job->source_id = $rid;
$previousStatus = \WPML_TM_ICL_Translation_Status::makeByRid( $rid )->previous();
if ( $previousStatus->map( \WPML\FP\Obj::prop( 'status' ) )->getOrElse( null ) === (string) ICL_TM_ATE_CANCELLED ) {
wpml_tm_load_job_factory()->update_job_data( $job_id, array( 'editor' => WPML_TM_Editors::ATE ) );
$job->existing_ate_id = make( \WPML\TM\ATE\JobRecords::class )->get_ate_job_id( $job_id );
} else {
$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\ATE\REST\PublicReceive::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.
*
* @param bool $forceReload
* @param bool $loadObsoleteStringQuery
* @param bool $dontCache
*
* @return \WPML_TM_Jobs_Repository
*/
function wpml_tm_get_jobs_repository( $forceReload = false, $loadObsoleteStringQuery = true, $dontCache = false ) {
static $repository;
if ( ! $repository || $forceReload ) {
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 )
);
if ( $loadObsoleteStringQuery ) {
$subqueries[] = new StringQuery(
$wpdb,
new QueryBuilder( $limit_helper, $order_helper )
);
}
$subqueries[] = new StringsBatchQuery(
$wpdb,
new QueryBuilder( $limit_helper, $order_helper )
);
}
$result = new WPML_TM_Jobs_Repository(
$wpdb,
new CompositeQuery(
$subqueries,
$limit_helper,
$order_helper
),
new WPML_TM_Job_Elements_Repository( $wpdb )
);
if ( $dontCache ) {
return $result;
} else {
$repository = $result;
}
}
return $repository;
}
function wpml_tm_reload_jobs_repository() {
wpml_tm_get_jobs_repository( true );
}
/**
* Reloading Jobs Repository when WPML String Translation finishes setup.
*/
add_action( 'wpml_register_string_packages', 'wpml_tm_reload_jobs_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 );
}
/**
* @param \WPML\TM\ATE\Log\Entry $entry
* @param bool $avoidDuplication
*
* @throws \WPML\Auryn\InjectionException
*/
function wpml_tm_ate_ams_log( WPML\TM\ATE\Log\Entry $entry, $avoidDuplication = false ) {
make( WPML\TM\ATE\Log\Storage::class )->add( $entry, $avoidDuplication );
}
/**
* @param \WPML\TM\ATE\Log\Entry $entry
*
* @throws \WPML\Auryn\InjectionException
*/
function wpml_tm_ate_ams_log_remove( WPML\TM\ATE\Log\Entry $entry ) {
make( WPML\TM\ATE\Log\Storage::class )->remove( $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 );
}

View File

@@ -0,0 +1,437 @@
<?php
/**
* @global \WPML_Term_Translation $wpml_language_resolution
* @global \WPML_Slug_Filter $wpml_slug_filter
* @global \WPML_Term_Translation $wpml_term_translations
*/
use WPML\FP\Obj;
use WPML\LIB\WP\WPDB as WpWPDB;
use function WPML\Container\make;
require_once __DIR__ . '/wpml_load_request_handler.php';
function wpml_is_setup_complete() {
return (bool) Obj::prop( 'setup_complete', get_option( 'icl_sitepress_settings', [] ) );
}
/**
* Loads global variables providing functionality that is used throughout the plugin.
*
* @param null|bool $is_admin If set to `null` it will read from `is_admin()`
*/
function load_essential_globals( $is_admin = null ) {
global $wpml_language_resolution, $wpml_term_translations, $wpdb;
$settings = get_option( 'icl_sitepress_settings' );
if ( (bool) $settings === false ) {
icl_sitepress_activate();
} else {
if ( wpml_is_setup_complete() ) {
$active_plugins = get_option( 'active_plugins' );
$wpmu_sitewide_plugins = (array) maybe_unserialize( get_site_option( 'active_sitewide_plugins' ) );
if ( in_array(
WPML_PLUGIN_BASENAME,
$active_plugins,
true
) === false &&
in_array(
WPML_PLUGIN_BASENAME,
array_keys( $wpmu_sitewide_plugins ),
true
) === false
) {
// The plugin has just be reactivated.
// reset ajx_health_flag
// set the just_reactivated flag so any posts created while
// WPML was not activate will get the default language
// https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-1924
$settings['ajx_health_checked'] = 0;
$settings['just_reactivated'] = 1;
update_option( 'icl_sitepress_settings', $settings );
}
}
}
$active_language_codes = isset( $settings['active_languages'] ) ? $settings['active_languages']
: array();
$active_language_codes = (bool) $active_language_codes === true
? $active_language_codes : wpml_reload_active_languages_setting();
$default_lang_code = isset( $settings['default_language'] ) ? $settings['default_language']
: false;
$wpml_language_resolution = new WPML_Language_Resolution( $active_language_codes, $default_lang_code );
$admin = $is_admin === null ? is_admin() : $is_admin;
wpml_load_post_translation( $admin, $settings );
$wpml_term_translations = new WPML_Term_Translation( $wpdb );
$domain_validation = filter_input( INPUT_GET, '____icl_validate_domain' ) ? 1 : false;
$domain_validation = filter_input( INPUT_GET, '____icl_validate_directory' ) ? 2 : $domain_validation;
$url_converter = load_wpml_url_converter( $settings, $domain_validation, $default_lang_code );
$directory = $domain_validation === 2 || ( is_multisite() && ! is_subdomain_install() );
if ( $domain_validation ) {
echo wpml_validate_host( $_SERVER['REQUEST_URI'], $url_converter, $directory );
exit;
}
if ( $admin ) {
wpml_load_admin_files();
}
}
function wpml_load_post_translation( $is_admin, $settings ) {
global $wpml_post_translations, $wpdb;
$http_referer = make( WPML_URL_HTTP_Referer::class );
if (
$is_admin === true
|| $http_referer->is_rest_request_called_from_post_edit_page()
|| ( defined( 'WP_CLI' ) && WP_CLI )
) {
$wpml_post_translations = new WPML_Admin_Post_Actions( $settings, $wpdb );
} else {
$wpml_post_translations = new WPML_Frontend_Post_Actions( $settings, $wpdb );
wpml_load_frontend_tax_filters();
}
$wpml_post_translations->init();
}
function wpml_load_query_filter( $installed ) {
global $wpml_query_filter, $sitepress, $wpdb, $wpml_post_translations, $wpml_term_translations;
$wpml_query_filter = $wpml_query_filter ? $wpml_query_filter : new WPML_Query_Filter( $sitepress, $wpdb, $wpml_post_translations, $wpml_term_translations );
if ( $installed ) {
if ( ! has_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ) ) ) {
add_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ), 10, 2 );
add_filter( 'posts_where', array( $wpml_query_filter, 'posts_where_filter' ), 10, 2 );
}
}
}
function load_wpml_url_converter( $settings, $domain_validation, $default_lang_code ) {
/**
* @var WPML_URL_Converter $wpml_url_converter
* @var WPML_Language_Resolution $wpml_language_resolution
*/
global $wpml_url_converter, $wpml_language_resolution;
$url_type = isset( $settings['language_negotiation_type'] ) ? $settings['language_negotiation_type'] : false;
$url_type = $domain_validation ? $domain_validation : $url_type;
$active_language_codes = $wpml_language_resolution->get_active_language_codes();
WPML_URL_Converter_Factory::remove_previous_hooks();
$factory = new WPML_URL_Converter_Factory( $settings, $default_lang_code, $active_language_codes );
$wpml_url_converter = $factory->create( (int) $url_type );
return $wpml_url_converter;
}
/**
* @param string $req_uri
* @param WPML_URL_Converter $wpml_url_converter
* @param bool $directory
*
* @return string
*/
function wpml_validate_host( $req_uri, $wpml_url_converter, $directory = true ) {
if ( $directory === true ) {
$req_uri_parts = array_filter( explode( '/', $req_uri ) );
$lang_slug = array_pop( $req_uri_parts );
if ( strpos( $lang_slug, '?' ) === 0 ) {
$lang_slug = array_pop( $req_uri_parts );
} elseif ( strpos( $lang_slug, '?' ) !== false ) {
$parts = explode( '?', $lang_slug );
$lang_slug = array_shift( $parts );
}
} else {
$lang_slug = '';
}
return '<!--' . esc_url( untrailingslashit( trailingslashit( $wpml_url_converter->get_abs_home() ) . $lang_slug ) ) . '-->';
}
/**
* Checks if a given taxonomy is currently translated
*
* @param string $taxonomy name/slug of a taxonomy
* @return bool true if the taxonomy is currently set to being translatable in WPML
*/
function is_taxonomy_translated( $taxonomy ) {
return in_array( $taxonomy, array( 'category', 'post_tag', 'nav_menu' ), true )
|| in_array(
$taxonomy,
array_keys( array_filter( icl_get_setting( 'taxonomies_sync_option', array() ) ) )
);
}
/**
* Checks if a given post_type is currently translated
*
* @param string $post_type name/slug of a post_type
* @return bool true if the post_type is currently set to being translatable in WPML
*/
function is_post_type_translated( $post_type ) {
return 'nav_menu_item' === $post_type ||
in_array(
$post_type,
array_keys( array_filter( icl_get_setting( 'custom_posts_sync_option', array() ) ) )
);
}
function setup_admin_menus() {
global $pagenow, $sitepress;
if ( $pagenow === 'edit-tags.php' || $pagenow === 'term.php' ) {
maybe_load_translated_tax_screen();
$wpml_term_translation_help_notice = new WPML_Taxonomy_Translation_Help_Notice( wpml_get_admin_notices(), $sitepress );
$wpml_term_translation_help_notice->add_hooks();
}
}
function maybe_load_translated_tax_screen() {
$taxonomy_get = (string) filter_input( INPUT_GET, 'taxonomy' );
$taxonomy_get = $taxonomy_get ? $taxonomy_get : 'post_tag';
if ( is_taxonomy_translated( $taxonomy_get ) ) {
global $wpdb, $sitepress;
require WPML_PLUGIN_PATH . '/menu/term-taxonomy-menus/wpml-tax-menu-loader.class.php';
new WPML_Tax_Menu_Loader( $wpdb, $sitepress, $taxonomy_get );
}
}
/**
* @param bool $override
*
* @return array
*/
function wpml_reload_active_languages_setting( $override = false ) {
global $wpdb, $sitepress_settings;
if ( true === (bool) $sitepress_settings
&& ( $override || wpml_is_setup_complete() )
) {
// This won't output any MySQL error if `icl_languages` is missing on the
// current site, and it will return an empty array in that case.
$active_languages = WpWPDB::withoutError( function() use ( $wpdb ) {
return $wpdb->get_col( "
SELECT code
FROM {$wpdb->prefix}icl_languages
WHERE active = 1
" );
} );
$sitepress_settings['active_languages'] = $active_languages;
icl_set_setting( 'active_languages', $active_languages, true );
} else {
$active_languages = [];
}
return (array) $active_languages;
}
/**
* Returns and if necessary instantiates an instance of the WPML_Installation Class
*
* @return \WPML_Installation
*/
function wpml_get_setup_instance() {
global $wpml_installation, $wpdb, $sitepress;
if ( ! isset( $wpml_installation ) ) {
$wpml_installation = new WPML_Installation( $wpdb, $sitepress );
}
return $wpml_installation;
}
function wpml_load_admin_files() {
require_once WPML_PLUGIN_PATH . '/menu/wpml-troubleshooting-terms-menu.class.php';
require_once WPML_PLUGIN_PATH . '/inc/wpml-post-edit-ajax.class.php';
require_once WPML_PLUGIN_PATH . '/menu/wpml-post-status-display.class.php';
require_once WPML_PLUGIN_PATH . '/inc/utilities/wpml-color-picker.class.php';
}
function wpml_get_post_status_helper() {
global $wpml_post_status, $wpdb, $sitepress;
if ( ! isset( $wpml_post_status ) ) {
$wpml_post_status = new WPML_Post_Status( $wpdb, $sitepress->get_wp_api() );
}
return $wpml_post_status;
}
function wpml_get_create_post_helper() {
global $sitepress;
return new WPML_Create_Post_Helper( $sitepress );
}
/**
* @return \TranslationManagement
*/
function wpml_load_core_tm() {
global $iclTranslationManagement;
if ( ! isset( $iclTranslationManagement ) ) {
require_once WPML_PLUGIN_PATH . '/inc/translation-management/translation-management.class.php';
$iclTranslationManagement = new TranslationManagement();
}
return $iclTranslationManagement;
}
function wpml_get_langs_in_dirs_val( $wpml_url_converter ) {
global $sitepress;
return new WPML_Lang_URL_Validator( $wpml_url_converter, $sitepress );
}
function wpml_get_root_page_actions_obj() {
global $wpml_root_page_actions, $sitepress_settings;
if ( ! isset( $wpml_root_page_actions ) ) {
require_once WPML_PLUGIN_PATH . '/inc/post-translation/wpml-root-page-actions.class.php';
$wpml_root_page_actions = new WPML_Root_Page_Actions( $sitepress_settings );
}
return $wpml_root_page_actions;
}
function wpml_get_hierarchy_sync_helper( $type = 'post' ) {
global $wpdb;
if ( $type === 'post' ) {
require_once WPML_PLUGIN_PATH . '/inc/post-translation/wpml-post-hierarchy-sync.class.php';
$hierarchy_helper = new WPML_Post_Hierarchy_Sync( $wpdb );
} elseif ( $type === 'term' ) {
require_once WPML_PLUGIN_PATH . '/inc/taxonomy-term-translation/wpml-term-hierarchy-sync.class.php';
$hierarchy_helper = new WPML_Term_Hierarchy_Sync( $wpdb );
} else {
$hierarchy_helper = false;
}
return $hierarchy_helper;
}
function wpml_maybe_setup_post_edit() {
global $pagenow, $sitepress, $post_edit_screen;
if (
in_array( $pagenow, [ 'post.php', 'post-new.php', 'edit.php' ], true ) ||
defined( 'DOING_AJAX' ) ||
apply_filters( 'wpml_enable_language_meta_box', false )
) {
$post_edit_screen = new WPML_Post_Edit_Screen( $sitepress );
add_action( 'admin_head', [ $sitepress, 'post_edit_language_options' ] );
}
}
/**
* @return \WPML_Frontend_Tax_Filters
*/
function wpml_load_frontend_tax_filters() {
global $wpml_term_filters;
if ( ! isset( $wpml_term_filters ) ) {
require WPML_PLUGIN_PATH . '/inc/taxonomy-term-translation/wpml-frontend-tax-filters.class.php';
$wpml_term_filters = new WPML_Frontend_Tax_Filters();
}
return $wpml_term_filters;
}
/**
* @return \WPML_Settings_Helper
*/
function wpml_load_settings_helper() {
global $wpml_settings_helper, $sitepress, $wpml_post_translations;
if ( ! isset( $wpml_settings_helper ) ) {
$wpml_settings_helper = new WPML_Settings_Helper( $wpml_post_translations, $sitepress );
}
return $wpml_settings_helper;
}
function wpml_get_term_translation_util() {
global $sitepress;
require_once WPML_PLUGIN_PATH . '/inc/taxonomy-term-translation/wpml-term-translation-utils.class.php';
return new WPML_Term_Translation_Utils( $sitepress );
}
/**
* @return \WPML_Term_Filters
*/
function wpml_load_term_filters() {
global $wpml_term_filters_general, $sitepress, $wpdb;
if ( ! isset( $wpml_term_filters_general ) ) {
require WPML_PLUGIN_PATH . '/inc/taxonomy-term-translation/wpml-term-filters.class.php';
$wpml_term_filters_general = new WPML_Term_Filters( $wpdb, $sitepress );
$wpml_term_filters_general->init();
}
return $wpml_term_filters_general;
}
function wpml_show_user_options() {
global $sitepress, $current_user, $user_id, $pagenow;
if ( ! isset( $user_id ) && 'profile.php' === $pagenow ) {
$user_id = $current_user->ID;
}
$user = new WP_User( $user_id );
$user_options_menu = new WPML_User_Options_Menu( $sitepress, $user );
echo $user_options_menu->render();
}
/**
* @return \WPML_Upgrade_Command_Factory
*/
function wpml_get_upgrade_command_factory() {
static $factory;
if ( ! $factory ) {
$factory = new WPML_Upgrade_Command_Factory();
}
return $factory;
}
function wpml_get_upgrade_schema() {
global $wpdb;
static $instance;
if ( ! $instance ) {
$instance = new WPML_Upgrade_Schema( $wpdb );
}
return $instance;
}
/**
* @param string $class_name A class implementing \IWPML_Upgrade_Command.
* @param array $dependencies An array of dependencies passed to the `$class_name`'s constructor.
* @param array $scopes An array of scope values. Accepted values are: `\WPML_Upgrade::SCOPE_ADMIN`, `\WPML_Upgrade::SCOPE_AJAX`, and `\WPML_Upgrade::SCOPE_FRONT_END`.
* @param string|null $method The method to call to run the upgrade (otherwise, it calls the "run" method),
*
* @return \WPML_Upgrade_Command_Definition
*/
function wpml_create_upgrade_command_definition( $class_name, array $dependencies, array $scopes, $method = null ) {
return wpml_get_upgrade_command_factory()->create_command_definition( $class_name, $dependencies, $scopes, $method );
}
if ( is_admin() ) {
add_action( 'personal_options', 'wpml_show_user_options' );
}
// TM
require_once 'functions-load-tm.php';

View File

@@ -0,0 +1,112 @@
<?php
wp_cache_add_global_groups( array( 'sitepress_ms' ) );
$filtered_action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_action = $filtered_action ? $filtered_action : filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
if ( 0 === strcmp( $filtered_action, 'resetwpml' ) ) {
include_once WPML_PLUGIN_PATH . '/inc/functions-troubleshooting.php';
}
add_action( 'network_admin_menu', 'icl_network_administration_menu' );
add_action( 'wpmuadminedit', 'icl_wpmuadminedit' );
function icl_wpmuadminedit() {
if ( ! isset( $_REQUEST['action'] ) ) {
return;
}
$filtered_action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_action = $filtered_action ? $filtered_action : filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
switch ( $filtered_action ) {
case 'resetwpml':
icl_network_reset_wpml();
break;
case 'deactivatewpml':
icl_network_deactivate_wpml();
break;
case 'activatewpml':
icl_network_activate_wpml();
break;
}
}
function icl_network_administration_menu() {
add_menu_page(
__( 'WPML', 'sitepress' ),
__( 'WPML', 'sitepress' ),
'manage_sitess',
WPML_PLUGIN_FOLDER . '/menu/network.php',
null,
ICL_PLUGIN_URL . '/res/img/icon16.png'
);
add_submenu_page(
WPML_PLUGIN_FOLDER . '/menu/network.php',
__( 'Network settings', 'sitepress' ),
__( 'Network settings', 'sitepress' ),
'manage_sites',
WPML_PLUGIN_FOLDER . '/menu/network.php'
);
}
function icl_network_reset_wpml() {
icl_reset_wpml();
wp_redirect( network_admin_url( 'admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/network.php&updated=true&action=resetwpml' ) );
}
function icl_network_deactivate_wpml( $blog_id = false ) {
global $wpdb;
$filtered_action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_action = $filtered_action ? $filtered_action : filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
if ( 0 === strcmp( $filtered_action, 'deactivatewpml' ) ) {
if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'deactivatewpml' ) ) {
return;
}
}
if ( empty( $blog_id ) ) {
$filtered_id = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_id = $filtered_id ? $filtered_id : filter_input( INPUT_GET, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$blog_id = $filtered_id !== false ? $filtered_id : $wpdb->blogid;
}
if ( $blog_id ) {
switch_to_blog( $blog_id );
update_option( '_wpml_inactive', true );
restore_current_blog();
}
wp_redirect( network_admin_url( 'admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/network.php&updated=true&action=deactivatewpml' ) );
}
function icl_network_activate_wpml( $blog_id = false ) {
global $wpdb;
$filtered_action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_action = $filtered_action ? $filtered_action : filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
if ( 0 === strcmp( $filtered_action, 'activatewpml' ) ) {
if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'activatewpml' ) ) {
return;
}
}
if ( empty( $blog_id ) ) {
$filtered_id = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_id = $filtered_id ? $filtered_id : filter_input( INPUT_GET, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$blog_id = $filtered_id !== false ? $filtered_id : $wpdb->blogid;
}
if ( $blog_id ) {
switch_to_blog( $blog_id );
delete_option( '_wpml_inactive' );
restore_current_blog();
}
wp_redirect( network_admin_url( 'admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/network.php&updated=true&action=activatewpml' ) );
exit();
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* @param string $input
* @param string $default_if_invalid
*
* @return string
*/
function wpml_sanitize_hex_color( $input, $default_if_invalid = '' ) {
$input = sanitize_text_field( $input );
$result = $input;
if ( ! is_string( $input ) || ! wpml_is_valid_hex_color( $input ) ) {
$result = $default_if_invalid;
}
return $result;
}
function wpml_sanitize_hex_color_array( $input, $default_if_invalid = '', $bypass_non_strings = true, $recursive = false ) {
$result = $input;
if ( is_array( $input ) ) {
$result = array();
foreach ( $input as $key => $value ) {
if ( is_array( $value ) && $recursive ) {
$result[ $key ] = wpml_sanitize_hex_color_array( $value, $default_if_invalid, $recursive );
} elseif ( is_string( $value ) ) {
$result[ $key ] = wpml_sanitize_hex_color( $value, $default_if_invalid );
} elseif ( $bypass_non_strings ) {
$result[ $key ] = $value;
}
}
}
return $result;
}
/**
* @param string $input
*
* @return bool
*/
function wpml_is_valid_hex_color( $input ) {
if ( 'transparent' === $input || preg_match( '/' . wpml_get_valid_hex_color_pattern() . '/i', $input ) ) {
$is_valid = true;
} else {
$try_rgb2hex = wpml_rgb_to_hex( $input );
$is_valid = $try_rgb2hex ? preg_match( '/' . wpml_get_valid_hex_color_pattern() . '/i', $try_rgb2hex ) : false;
}
return $is_valid;
}
function wpml_get_valid_hex_color_pattern() {
return '(^#[a-fA-F0-9]{6}$)|(^#[a-fA-F0-9]{3}$)';
}
/**
* Convert RGB color code to HEX code.
*
* @param array $rgb
*
* @return bool|string
*/
function wpml_rgb_to_hex( $rgb ) {
if ( ! is_array( $rgb ) || count( $rgb ) < 3 ) {
return false;
}
$hex = '#';
$hex .= str_pad( dechex( $rgb[0] ), 2, '0', STR_PAD_LEFT );
$hex .= str_pad( dechex( $rgb[1] ), 2, '0', STR_PAD_LEFT );
$hex .= str_pad( dechex( $rgb[2] ), 2, '0', STR_PAD_LEFT );
return $hex;
}

View File

@@ -0,0 +1,75 @@
<?php
function wpml_get_authenticated_action() {
$action = filter_input( INPUT_POST, 'icl_ajx_action' );
$action = $action ? $action : filter_input( INPUT_POST, 'action' );
$nonce = $action ? filter_input( INPUT_POST, '_icl_nonce' ) : null;
if ( $nonce === null || $action === null ) {
$action = filter_input( INPUT_GET, 'icl_ajx_action' );
$nonce = $action ? filter_input( INPUT_GET, '_icl_nonce' ) : null;
}
$authenticated_action = $action && wp_verify_nonce( (string) $nonce, $action . '_nonce' ) ? $action : null;
return $authenticated_action;
}
/**
* Validates a nonce according to the schema also used by \wpml_nonce_field
*
* @param string $action
*
* @return false|int
*/
function wpml_is_action_authenticated( $action ) {
$nonce = isset( $_POST['_icl_nonce'] ) ? $_POST['_icl_nonce'] : '';
if ( '' !== $nonce ) {
$action = $action . '_nonce';
} else {
$nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : '';
}
return wp_verify_nonce( $nonce, $action );
}
/**
* Generates HTML for the hidden nonce input field following the schema
* used by \wpml_is_action_authenticated
*
* @param string $action
*
* @return string
*/
function wpml_nonce_field( $action ) {
return '<input name="_icl_nonce" type="hidden" value="'
. wp_create_nonce( $action . '_nonce' ) . '"/>';
}
/**
* RFC 4122 compliant UUID version 5.
*
* @param string $name The name to generate the UUID from.
* @param string $ns_uuid Namespace UUID. Default is for the NS when name string is a URL.
*
* @return string The UUID string.
*/
if ( ! function_exists( 'uuid_v5' ) ) {
function uuid_v5( $name, $ns_uuid = '6ba7b811-9dad-11d1-80b4-00c04fd430c8' ) {
$wpml_uuid = new WPML_UUID();
return $wpml_uuid->get_uuid_v5( $name, $ns_uuid );
}
}
/**
* @param string $object_id
* @param string $object_type
* @param int|null $timestamp If this parameter is `null`, it will be assigned the current time
* Set this parameter to 0 if the uuid should not have a time footprint
*
* @return string
*/
function wpml_uuid( $object_id, $object_type, $timestamp = null ) {
$wpml_uuid = new WPML_UUID();
return $wpml_uuid->get( $object_id, $object_type, $timestamp );
}

View File

@@ -0,0 +1,211 @@
<?php
function icl_reset_wpml( $blog_id = false ) {
global $wpdb, $sitepress_settings;
if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'resetwpml' ) {
check_admin_referer( 'resetwpml' );
}
if ( empty( $blog_id ) ) {
$filtered_id = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_id = $filtered_id ? $filtered_id : filter_input( INPUT_GET, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$blog_id = $filtered_id !== false ? $filtered_id : $wpdb->blogid;
}
if ( $blog_id || ! function_exists( 'is_multisite' ) || ! is_multisite() ) {
if ( function_exists( 'is_multisite' ) && is_multisite() ) {
switch_to_blog( $blog_id );
}
do_action( 'wpml_reset_plugins_before' );
wp_clear_scheduled_hook( 'update_wpml_config_index' );
$icl_tables = array(
$wpdb->prefix . 'icl_languages',
$wpdb->prefix . 'icl_languages_translations',
$wpdb->prefix . 'icl_translations',
$wpdb->prefix . 'icl_translation_status',
$wpdb->prefix . 'icl_translate_job',
$wpdb->prefix . 'icl_translate',
$wpdb->prefix . 'icl_locale_map',
$wpdb->prefix . 'icl_flags',
$wpdb->prefix . 'icl_content_status',
$wpdb->prefix . 'icl_core_status',
$wpdb->prefix . 'icl_node',
$wpdb->prefix . 'icl_strings',
$wpdb->prefix . 'icl_string_packages',
$wpdb->prefix . 'icl_translation_batches',
$wpdb->prefix . 'icl_string_translations',
$wpdb->prefix . 'icl_string_status',
$wpdb->prefix . 'icl_string_positions',
$wpdb->prefix . 'icl_message_status',
$wpdb->prefix . 'icl_reminders',
$wpdb->prefix . 'icl_mo_files_domains',
$wpdb->prefix . 'icl_string_pages',
$wpdb->prefix . 'icl_string_urls',
$wpdb->prefix . 'icl_cms_nav_cache',
$wpdb->prefix . 'icl_string_batches',
$wpdb->prefix . 'icl_translation_downloads',
);
$icl_tables = apply_filters( 'wpml_reset_tables', $icl_tables, $blog_id );
foreach ( $icl_tables as $icl_table ) {
$wpdb->query( 'DROP TABLE IF EXISTS ' . $icl_table );
}
$wpml_options = array(
'icl_sitepress_settings',
'icl_sitepress_version',
'_icl_cache',
'_icl_admin_option_names',
'wp_icl_translators_cached',
'wpml32_icl_non_translators_cached',
'wpml-package-translation-db-updates-run',
'wpml-package-translation-refresh-required',
'wpml-package-translation-string-packages-table-updated',
'wpml-package-translation-string-table-updated',
'icl_translation_jobs_basket',
'widget_icl_lang_sel_widget',
'icl_admin_messages',
'icl_adl_settings',
'wpml_tp_com_log',
'wpml_config_index',
'wpml_config_index_updated',
'wpml_config_files_arr',
'wpml_language_switcher',
'wpml_notices',
'wpml_start_version',
'wpml_dependencies:installed_plugins',
'wpml_translation_services',
'wpml_update_statuses',
'_wpml_dismissed_notices',
'wpml_translation_services_timestamp',
'wpml_string_table_ok_for_mo_import',
'wpml-charset-validation',
'_wpml_media',
'wpml_st_display_strings_scan_notices',
'wpml-st-all-strings-are-in-english',
'wpml_strings_need_links_fixed',
'_wpml_batch_report',
'wpml_cms_nav_settings',
'WPML_CMS_NAV_VERSION',
'icl_st_settings',
'wpml-tm-custom-xml',
'wpml-st-persist-errors',
'wpml_base_slug_translation',
);
$wpml_options = apply_filters( 'wpml_reset_options', $wpml_options, $blog_id );
foreach ( $wpml_options as $wpml_option ) {
delete_option( $wpml_option );
}
$wpml_user_options = array(
'language_pairs',
);
$wpml_user_options = apply_filters( 'wpml_reset_user_options', $wpml_user_options, $blog_id );
if ( $wpml_user_options ) {
foreach ( $wpml_user_options as $wpml_user_option ) {
$meta_key = $wpdb->get_blog_prefix( $blog_id ) . $wpml_user_option;
$users = get_users(
array(
'blog_id' => $blog_id,
'meta_key' => $meta_key,
'fields' => array( 'ID' ),
)
);
/** @var WP_User $user */
foreach ( $users as $user ) {
delete_user_option( $user->ID, $wpml_user_option );
}
}
}
$capabilities = array(
'wpml_manage_translation_management',
'wpml_manage_languages',
'wpml_manage_theme_and_plugin_localization',
'wpml_manage_support',
'wpml_manage_woocommerce_multilingual',
'wpml_operate_woocommerce_multilingual',
'wpml_manage_media_translation',
'wpml_manage_navigation',
'wpml_manage_sticky_links',
'wpml_manage_string_translation',
'wpml_manage_translation_analytics',
'wpml_manage_wp_menus_sync',
'wpml_manage_taxonomy_translation',
'wpml_manage_troubleshooting',
'wpml_manage_translation_options',
'manage_translations',
'translate',
);
$capabilities = apply_filters( 'wpml_reset_user_capabilities', $capabilities, $blog_id );
if ( $capabilities ) {
$users = get_users(
array(
'blog_id' => $blog_id,
)
);
/** @var WP_User $user */
foreach ( $users as $user ) {
foreach ( $capabilities as $capability ) {
$user->remove_cap( $capability );
}
}
}
$sitepress_settings = null;
wp_cache_init();
$wpml_cache_directory = new WPML_Cache_Directory( new WPML_WP_API() );
$wpml_cache_directory->remove();
do_action( 'wpml_reset_plugins_after' );
$wpmu_sitewide_plugins = (array) maybe_unserialize( get_site_option( 'active_sitewide_plugins' ) );
if ( ! isset( $wpmu_sitewide_plugins[ WPML_PLUGIN_BASENAME ] ) ) {
remove_action( 'deactivate_' . WPML_PLUGIN_BASENAME, 'icl_sitepress_deactivate' );
deactivate_plugins( WPML_PLUGIN_BASENAME );
$ra = get_option( 'recently_activated' );
$ra[ WPML_PLUGIN_BASENAME ] = time();
update_option( 'recently_activated', $ra );
} else {
update_option( '_wpml_inactive', true );
}
$options_to_delete_after_deactivation = array(
'wpml_dependencies:needs_validation',
'wpml_dependencies:valid_plugins',
'wpml_dependencies:invalid_plugins',
);
$options_to_delete_after_deactivation = apply_filters( 'wpml_reset_options_after_deactivation', $options_to_delete_after_deactivation, $blog_id );
foreach ( $options_to_delete_after_deactivation as $option ) {
delete_option( $option );
}
if ( function_exists( 'is_multisite' ) && is_multisite() ) {
restore_current_blog();
}
}
}
/**
* Ajax handler for type assignment fix troubleshoot action
*/
function icl_repair_broken_type_and_language_assignments() {
global $sitepress;
$lang_setter = new WPML_Fix_Type_Assignments( $sitepress );
$rows_fixed = $lang_setter->run();
wp_send_json_success( $rows_fixed );
}

View File

@@ -0,0 +1,885 @@
<?php
/**
* SitePress Template functions
*
* @package wpml-core
*/
use function WPML\Container\make;
/**
* Returns true if the site uses ICanLocalize.
*
* @return bool
*/
function wpml_site_uses_icl() {
global $wpdb;
$setting = 'site_does_not_use_icl';
if ( icl_get_setting( $setting, false ) ) {
return false;
}
$cache = new WPML_WP_Cache( 'wpml-checks' );
$found = false;
$site_uses_icl = $cache->get( 'site_uses_icl', $found );
if ( ! $found ) {
$site_uses_icl = false;
$table_exists = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}icl_translation_status'" );
if ( $table_exists ) {
$icl_job_count_query = $wpdb->prepare(
"SELECT rid
FROM {$wpdb->prefix}icl_translation_status
WHERE translation_service = %s
LIMIT 1;",
'icanlocalize'
);
$site_uses_icl = (bool) $wpdb->get_var( $icl_job_count_query );
}
$cache->set( 'site_uses_icl', $site_uses_icl );
}
if ( icl_get_setting( 'setup_complete', false ) && ! $site_uses_icl ) {
icl_set_setting( $setting, true, true );
}
return $site_uses_icl;
}
/**
* Returns the value of a given key setting.
*
* @param string $key The setting's key.
* @param mixed|false $default The value to use if the setting does not exist.
*
* @return bool|mixed
* @since 3.1
* @deprecated 3.2 use `\wpml_setting` or 'wpml_get_setting_filter' filter instead
*/
function icl_get_setting( $key, $default = false ) {
return wpml_get_setting( $key, $default );
}
/**
* Get a WPML setting value.
* If the Main SitePress Class cannot be accessed by the function it will read the setting from the database.
* It will return `$default` if the requested key is not set.
*
* @param string $key The setting's key.
* @param mixed|null $default Required. The value to return if the settings key does not exist
* (typically it's false, but you may want to use something else).
*
* @return mixed The value of the requested setting, or `$default`
* @since 4.1
*/
function wpml_get_setting( $key, $default = null ) {
global $sitepress_settings;
$sitepress_settings = isset( $sitepress_settings ) ? $sitepress_settings : get_option( 'icl_sitepress_settings' );
return isset( $sitepress_settings[ $key ] ) ? $sitepress_settings[ $key ] : $default;
}
/**
* Get a WPML setting value.
* If the Main SitePress Class cannot be access to the function will read the setting from the database.
* Will return false if the requested key is not set or.
* the default value passed in the function's second parameter.
*
* @param mixed|false $default Required. The value to return if the settings key does not exist
* (typically it's false, but you may want to use something else).
* @param string $key The setting's key.
*
* @return mixed The value of the requested setting, or $default
* @since 3.2
* @uses \SitePress::api_hooks
*/
function wpml_get_setting_filter( $default, $key ) {
$args = func_get_args();
if ( count( $args ) > 2 && $args[2] !== null ) {
$default = $args[2];
}
return wpml_get_setting( $key, $default );
}
/**
* Returns the value of a given key sub-setting.
*
* @param string $key The setting's key.
* @param string $sub_key The settings name key to return the value of.
* @param mixed|false $default Required. The value to return if the settings key does not exist
* (typically it's false, but you may want to use something else).
*
* @return bool|mixed
* @since 3.1
* @deprecated 3.2 use 'wpml_sub_setting' filter instead
*/
function icl_get_sub_setting( $key, $sub_key, $default = false ) {
$parent = icl_get_setting( $key, array() );
return isset( $parent[ $sub_key ] ) ? $parent[ $sub_key ] : $default;
}
/**
* Gets a WPML sub setting value.
*
* @param mixed|false $default Required. The value to return if the settings key does not exist
* (typically it's false, but you may want to use something else).
* @param string $key The settings name key the sub key belongs to.
* @param string $sub_key The sub key to return the value of.
* @param mixed $deprecated Deprecated param.
*
* @return mixed The value of the requested setting, or $default
* @todo [WPML 3.3] Remove deprecated argument
*
* @uses \wpml_get_setting_filter
*
* @since 3.2
* @uses \SitePress::api_hooks
*/
function wpml_get_sub_setting_filter( $default, $key, $sub_key, $deprecated = null ) {
$default = null !== $deprecated && ! $default ? $deprecated : $default;
$parent = wpml_get_setting_filter( array(), $key );
return isset( $parent[ $sub_key ] ) ? $parent[ $sub_key ] : $default;
}
/**
* Saves the value of a given key.
*
* @param string $key The settings name key the sub key belongs to.
* @param mixed $value The value to assign to the given key.
* @param bool $save_now Must call icl_save_settings() to permanently store the value.
*
* @return bool Always True. If `$save_now === true`, it returns the result of `update_option`
*/
function icl_set_setting( $key, $value, $save_now = false ) {
global $sitepress_settings;
$result = true;
$sitepress_settings[ $key ] = $value;
if ( true === $save_now ) {
/* We need to save settings anyway, in this case. */
$result = update_option( 'icl_sitepress_settings', $sitepress_settings );
do_action( 'icl_save_settings', $sitepress_settings );
}
return $result;
}
/**
* Save the settings in the db.
*/
function icl_save_settings() {
global $sitepress;
$sitepress->save_settings();
}
/**
* Gets all the settings.
*
* @return array|false
*/
function icl_get_settings() {
global $sitepress;
return isset( $sitepress ) ? $sitepress->get_settings() : false;
}
/**
* Add settings link to plugin page.
*
* @param SitePress $sitepress
* @param array<string> $links
* @param string $file
*
* @return array
*/
function icl_plugin_action_links( SitePress $sitepress, $links, $file ) {
if ( $file == WPML_PLUGIN_BASENAME ) {
$endpoint = $sitepress->is_setup_complete() ? 'languages.php' : 'setup.php';
$links[] = '<a href="admin.php?page=' . WPML_PLUGIN_FOLDER . '/menu/' . $endpoint . '">' . __( 'Configure', 'sitepress' ) . '</a>';
}
return $links;
}
if ( defined( 'ICL_DEBUG_MODE' ) && ICL_DEBUG_MODE ) {
add_action( 'admin_notices', '_icl_deprecated_icl_debug_mode' );
}
function _icl_deprecated_icl_debug_mode() {
echo '<div class="updated"><p><strong>ICL_DEBUG_MODE</strong> no longer supported. Please use <strong>WP_DEBUG</strong> instead.</p></div>';
}
if ( ! function_exists( 'icl_js_escape' ) ) {
function icl_js_escape( $str ) {
$str = esc_js( $str );
$str = htmlspecialchars_decode( $str );
return $str;
}
}
/**
* Read and, if needed, generate the site ID based on the scope.
*
* @param string $scope Defaults to "global".
* Use a different value when the ID is used for specific scopes.
*
* @param bool $create_new Forces the creation of a new ID.
*
* @return string|null The generated/stored ID or null if it wasn't possible to generate/store the value.
*/
function wpml_get_site_id( $scope = WPML_Site_ID::SITE_SCOPES_GLOBAL, $create_new = false ) {
static $site_id;
if ( ! $site_id ) {
$site_id = new WPML_Site_ID();
}
return $site_id->get_site_id( $scope, $create_new );
}
function _icl_tax_has_objects_recursive( $id, $term_id = -1, $rec = 0 ) {
// based on the case where two categories were one the parent of another
// eliminating the chance of infinite loops by letting this function calling itself too many times
// 100 is the default limit in most of teh php configuration
//
// this limit this function to work only with categories nested up to 60 levels
// should enough for most cases
if ( $rec > 60 ) {
return false;
}
global $wpdb;
if ( $term_id === -1 ) {
$term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id=%d", $id ) );
}
$children = $wpdb->get_results(
$wpdb->prepare(
"
SELECT term_taxonomy_id, term_id, count FROM {$wpdb->term_taxonomy} WHERE parent = %d
",
$term_id
)
);
$count = 0;
foreach ( $children as $ch ) {
$count += $ch->count;
}
if ( $count ) {
return true;
} else {
foreach ( $children as $ch ) {
if ( _icl_tax_has_objects_recursive( $ch->term_taxonomy_id, $ch->term_id, $rec + 1 ) ) {
return true;
}
}
}
return false;
}
function _icl_trash_restore_prompt() {
if ( isset( $_GET['lang'] ) ) {
$post = get_post( intval( $_GET['post'] ) );
if ( isset( $post->post_status ) && $post->post_status == 'trash' ) {
$post_type_object = get_post_type_object( $post->post_type );
$delete_post_link = '<a href="' . esc_url( get_delete_post_link( $post->ID, '', true ) ) . '">' . esc_html__( 'delete it permanently', 'sitepress' ) . '</a>';
$restore_post_link = '<a href="' . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . '">' . esc_html__( 'restore', 'sitepress' ) . '</a>';
$ret = '<p>' . sprintf( esc_html__( 'This translation is currently in the trash. You need to either %1$s or %2$s it in order to continue.' ), $delete_post_link, $restore_post_link );
wp_die( $ret );
}
}
}
/**
* Build or update duplicated posts from a master post.
*
* @param string $master_post_id The ID of the post to duplicate from. Master post doesn't need to be in the default language.
*
* @uses SitePress
* @uses TranslationManagement
* @since unknown
* @deprecated 3.2 use 'wpml_admin_make_duplicates' action instead
*/
function icl_makes_duplicates( $master_post_id ) {
wpml_admin_make_post_duplicates_action( $master_post_id );
}
/**
* Build or update duplicated posts from a master post.
* To be used only for admin backend actions
*
* @param int $master_post_id The ID of the post to duplicate from.
* The ID can be that of a post, page or custom post
* Master post doesn't need to be in the default language.
*
* @see $iclTranslationManagement in \SitePress:: __construct
*
* @uses SitePress
* @uses TranslationManagement
* @since 3.2
* @uses \SitePress::api_hooks
*/
function wpml_admin_make_post_duplicates_action( $master_post_id ) {
$post = get_post( $master_post_id );
$post_type = $post->post_type;
if ( $post->post_status == 'auto-draft' || $post->post_type == 'revision' ) {
return;
}
global $sitepress;
$iclTranslationManagement = wpml_load_core_tm();
if ( $sitepress->is_translated_post_type( $post_type ) ) {
$iclTranslationManagement->make_duplicates_all( $master_post_id );
}
}
/**
* Build duplicated posts from a master post only in case of the duplicate not being present at the time.
*
* @param string $master_post_id The ID of the post to duplicate from. Master post doesn't need to be in the default language.
*
* @uses SitePress
* @since unknown
* @deprecated 3.2 use 'wpml_make_post_duplicates' action instead
*/
function icl_makes_duplicates_public( $master_post_id ) {
wpml_make_post_duplicates_action( $master_post_id );
}
/**
* Build duplicated posts from a master post only in case of the duplicate not being present at the time.
*
* @param int $master_post_id The ID of the post to duplicate from.
* Master post doesn't need to be in the default language.
*
* @uses SitePress
* @since 3.2
* @uses \SitePress::api_hooks
*/
function wpml_make_post_duplicates_action( $master_post_id ) {
global $sitepress;
$master_post = get_post( $master_post_id );
if ( 'auto-draft' === $master_post->post_status || 'revision' === $master_post->post_type ) {
return;
}
$active_langs = $sitepress->get_active_languages();
foreach ( $active_langs as $lang_to => $one ) {
$trid = $sitepress->get_element_trid( $master_post->ID, 'post_' . $master_post->post_type );
$lang_from = $sitepress->get_source_language_by_trid( $trid );
if ( $lang_from == $lang_to ) {
continue;
}
$sitepress->make_duplicate( $master_post_id, $lang_to );
}
}
/**
* Wrapper function for deprecated like_escape() and recommended wpdb::esc_like()
*
* @global wpdb $wpdb
*
* @param string $text
*
* @return string
*/
function wpml_like_escape( $text ) {
global $wpdb;
if ( method_exists( $wpdb, 'esc_like' ) ) {
return $wpdb->esc_like( $text );
}
/** @noinspection PhpDeprecationInspection */
return like_escape( $text );
}
function icl_do_not_promote() {
return defined( 'ICL_DONT_PROMOTE' ) && ICL_DONT_PROMOTE;
}
/**
* @param string $time
*
* @return string
*/
function icl_convert_to_user_time( $time ) {
// offset between server time and user time in seconds
$time_offset = get_option( 'gmt_offset' ) * 3600;
$local_time = __( 'Last Update Time could not be determined', 'sitepress' );
try {
// unix time stamp in server time
$creation_time = strtotime( $time );
// creating dates before 2014 are impossible
if ( $creation_time !== false ) {
$local_time = date( 'Y-m-d H:i:s', $creation_time + $time_offset );
}
} catch ( Exception $e ) {
// Ignoring the exception, as we already set the default value in $local_time
}
return $local_time;
}
/**
* Check if given language is activated
*
* @global sitepress $sitepress
*
* @param string $language 2 letters language code
*
* @return boolean
* @since unknown
* @deprecated 3.2 use 'wpml_language_is_active' filter instead
*/
function icl_is_language_active( $language ) {
global $sitepress;
$active_languages = $sitepress->get_active_languages();
return isset( $active_languages[ $language ] );
}
/**
* Checks if given language is enabled
*
* @param mixed $empty_value This is normally the value the filter will be modifying.
* We are not filtering anything here therefore the NULL value
* This for the filter function to actually receive the full argument list:
* apply_filters('wpml_language_is_active', '', $language_code);
* @param string $language_code The language code to check Accepts a 2-letter language code
*
* @return boolean
* @global sitepress $sitepress
*
* @since 3.2
* @uses \SitePress::api_hooks
*/
function wpml_language_is_active_filter( $empty_value, $language_code ) {
global $sitepress;
return $sitepress->is_active_language( $language_code );
}
/**
* @param string $url url either with or without schema
* Removes the subdirectory in which WordPress is installed from a url.
* If WordPress is not installed in a subdirectory, then the input is returned unaltered.
*
* @return string the url input without the blog's subdirectory. Potentially existing schemata on the input are kept intact.
*/
function wpml_strip_subdir_from_url( $url ) {
/** @var WPML_URL_Converter $wpml_url_converter */
global $wpml_url_converter;
$subdir = wpml_parse_url( $wpml_url_converter->get_abs_home(), PHP_URL_PATH );
$subdir_slugs = ! empty( $subdir ) ? array_values( array_filter( explode( '/', $subdir ) ) ) : [''];
$url_path_expl = explode( '/', preg_replace( '#^(http|https)://#', '', $url ) );
array_shift( $url_path_expl );
$url_slugs = array_values( array_filter( $url_path_expl ) );
$url_slugs_before = $url_slugs;
$url_slugs = array_diff_assoc( $url_slugs, $subdir_slugs );
$url = str_replace( '/' . join( '/', $url_slugs_before ), '/' . join( '/', $url_slugs ), $url );
return untrailingslashit( $url );
}
/**
* Changes array of items into string of items, separated by comma and sql-escaped
*
* @see https://coderwall.com/p/zepnaw
* @global wpdb $wpdb
*
* @param mixed|array $items item(s) to be joined into string
* @param string $format %s or %d
*
* @return string Items separated by comma and sql-escaped
*/
function wpml_prepare_in( $items, $format = '%s' ) {
global $wpdb;
$items = (array) $items;
$how_many = count( $items );
if ( $how_many > 0 ) {
$placeholders = array_fill( 0, $how_many, $format );
$prepared_format = implode( ',', $placeholders );
$prepared_in = $wpdb->prepare( $prepared_format, $items );
} else {
$prepared_in = '';
}
return $prepared_in;
}
function is_not_installing_plugins() {
global $sitepress;
$checked = isset( $_REQUEST['checked'] ) ? (array) $_REQUEST['checked'] : array();
if ( ! isset( $_REQUEST['action'] ) ) {
return true;
} elseif ( $_REQUEST['action'] != 'activate' && $_REQUEST['action'] != 'activate-selected' ) {
return true;
} elseif ( ( ! isset( $_REQUEST['plugin'] ) || $_REQUEST['plugin'] != WPML_PLUGIN_FOLDER . '/' . basename( __FILE__ ) ) && ! in_array( WPML_PLUGIN_FOLDER . '/' . basename( __FILE__ ), $checked ) ) {
return true;
} elseif ( in_array( WPML_PLUGIN_FOLDER . '/' . basename( __FILE__ ), $checked ) && ! isset( $sitepress ) ) {
return true;
}
return false;
}
function wpml_mb_strtolower( $string ) {
if ( function_exists( 'mb_strtolower' ) ) {
return mb_strtolower( $string );
}
return strtolower( $string );
}
function wpml_mb_strpos( $haystack, $needle, $offset = 0 ) {
if ( function_exists( 'mb_strpos' ) ) {
return mb_strpos( $haystack, $needle, $offset );
}
return strpos( $haystack, $needle, $offset );
}
function wpml_set_plugin_as_inactive() {
global $icl_plugin_inactive;
if ( ! defined( 'ICL_PLUGIN_INACTIVE' ) ) {
define( 'ICL_PLUGIN_INACTIVE', true );
}
$icl_plugin_inactive = true;
}
function wpml_version_is( $version_to_check, $comparison = '==' ) {
return version_compare( ICL_SITEPRESS_VERSION, $version_to_check, $comparison ) && function_exists( 'wpml_site_uses_icl' );
}
/**
* Interrupts the plugin activation process if the WPML Core Plugin could not be activated
*/
function icl_suppress_activation() {
$active_plugins = get_option( 'active_plugins' );
$icl_sitepress_idx = array_search( WPML_PLUGIN_BASENAME, $active_plugins );
if ( false !== $icl_sitepress_idx ) {
unset( $active_plugins[ $icl_sitepress_idx ] );
update_option( 'active_plugins', $active_plugins );
unset( $_GET['activate'] );
$recently_activated = get_option( 'recently_activated' );
if ( ! isset( $recently_activated[ WPML_PLUGIN_BASENAME ] ) ) {
$recently_activated[ WPML_PLUGIN_BASENAME ] = time();
update_option( 'recently_activated', $recently_activated );
}
}
}
/**
* @param SitePress $sitepress
*/
function activate_installer( $sitepress = null ) {
// installer hook - start
include_once WPML_PLUGIN_PATH . '/vendor/otgs/installer/loader.php'; // produces global variable $wp_installer_instance
$args = array(
'plugins_install_tab' => 1,
);
if ( $sitepress ) {
$args['site_key_nags'] = array(
array(
'repository_id' => 'wpml',
'product_name' => 'WPML',
'condition_cb' => array( $sitepress, 'setup' ),
),
);
}
/**
* @var WP_Installer $wp_installer_instance
*/
/** @phpstan-ignore-next-line */
WP_Installer_Setup( $wp_installer_instance, $args );
// installer hook - end
}
function wpml_missing_filter_input_notice() {
?>
<div class="message error">
<h3><?php esc_html_e( "WPML can't be functional because it requires a disabled PHP extension!", 'sitepress' ); ?></h3>
<p><?php esc_html_e( 'To ensure and improve the security of your website, WPML makes use of the ', 'sitepress' ); ?><a href="http://php.net/manual/en/book.filter.php">PHP Data Filtering</a> extension.<br><br>
<?php
esc_html_e(
'The filter extension is enabled by default as of PHP 5.2.0. Before this time an experimental PECL extension was
used, however, the PECL version is no longer recommended to be used or updated. (source: ',
'sitepress'
);
?>
<a href="http://php.net/manual/en/filter.installation.php">PHP Manual Function Reference Variable and
Type Related Extensions Filter
Installing/Configuring</a>)<br>
<br>
<?php esc_html_e( 'The filter extension is enabled by default as of PHP 5.2, therefore it must have been disabled by either you or your host.', 'sitepress' ); ?>
<br><?php esc_html_e( "To enable it, either you or your host will need to open your website's php.ini file and either:", 'sitepress' ); ?><br>
<ol>
<li><?php esc_html_e( "Remove the 'filter_var' string from the 'disable_functions' directive or...", 'sitepress' ); ?>
</li>
<li><?php esc_html_e( 'Add the following line:', 'sitepress' ); ?> <code class="inline-code">extension=filter.so</code></li>
</ol>
<?php
$ini_location = php_ini_loaded_file();
if ( $ini_location !== false ) {
?>
<strong><?php esc_html_e( 'Your php.ini file is located at', 'sitepress' ) . ' ' . esc_html( $ini_location ); ?>.</strong>
<?php
}
?>
</div>
<?php
}
function repair_el_type_collate() {
global $wpdb;
$correct_collate = $wpdb->get_var(
$wpdb->prepare(
"SELECT collation_name
FROM information_schema.COLUMNS
WHERE TABLE_NAME = '%s'
AND COLUMN_NAME = 'post_type'
AND table_schema = (SELECT DATABASE())
LIMIT 1",
$wpdb->posts
)
);
// translations
$table_name = $wpdb->prefix . 'icl_translations';
$sql = $wpdb->prepare(
"ALTER TABLE `$table_name` CHANGE `element_type` `element_type` VARCHAR( 36 ) NOT NULL DEFAULT 'post_post' COLLATE %s",
$correct_collate
);
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
/**
* Wrapper for `parse_url` using `wp_parse_url`
*
* @param string $url
* @param int $component
*
* @return array|string|int|null
*/
function wpml_parse_url( $url, $component = -1 ) {
$ret = null;
$component_map = array(
PHP_URL_SCHEME => 'scheme',
PHP_URL_HOST => 'host',
PHP_URL_PORT => 'port',
PHP_URL_USER => 'user',
PHP_URL_PASS => 'pass',
PHP_URL_PATH => 'path',
PHP_URL_QUERY => 'query',
PHP_URL_FRAGMENT => 'fragment',
);
if ( $component === -1 ) {
$ret = wp_parse_url( $url );
} elseif ( isset( $component_map[ $component ] ) ) {
$key = $component_map[ $component ];
$parsed = wp_parse_url( $url );
$ret = isset( $parsed[ $key ] ) ? $parsed[ $key ] : null;
}
return $ret;
}
/**
* Wrapper function to prevent ampersand to be encoded (depending on some PHP versions)
*
* @link http://php.net/manual/en/function.http-build-query.php#102324
*
* @param array|object $query_data
*
* @return string
*/
function wpml_http_build_query( $query_data ) {
return http_build_query( $query_data, '', '&' );
}
/**
* @param array $array
* @param int $sort_flags
*
* @uses \wpml_array_unique_fallback
*
* @return array
*/
function wpml_array_unique( $array, $sort_flags = SORT_REGULAR ) {
if ( version_compare( phpversion(), '5.2.9', '>=' ) ) {
// phpcs:disable PHPCompatibility.FunctionUse.NewFunctionParameters.array_unique_sort_flagsFound -- This statement is preceded by a version check
return array_unique( $array, $sort_flags );
// phpcs:enable PHPCompatibility.FunctionUse.NewFunctionParameters.array_unique_sort_flagsFound
}
return wpml_array_unique_fallback( $array, true );
}
/**
* @param array<mixed> $array
* @param bool $keep_key_assoc
*
* @return array
* @see \wpml_array_unique
*/
function wpml_array_unique_fallback( $array, $keep_key_assoc ) {
$duplicate_keys = array();
$tmp = array();
foreach ( $array as $key => $val ) {
// convert objects to arrays, in_array() does not support objects
if ( is_object( $val ) ) {
$val = (array) $val;
}
if ( ! in_array( $val, $tmp ) ) {
$tmp[] = $val;
} else {
$duplicate_keys[] = $key;
}
}
foreach ( $duplicate_keys as $key ) {
unset( $array[ $key ] );
}
return $keep_key_assoc ? $array : array_values( $array );
}
/**
* @return bool
*/
function wpml_is_rest_request() {
return make( WPML_REST_Request_Analyze::class )->is_rest_request();
}
/**
* @return bool
*/
function wpml_is_rest_enabled() {
return make( \WPML\Core\REST\Status::class )->isEnabled();
}
function wpml_is_cli() {
return defined( 'WP_CLI' ) && WP_CLI;
}
function wpml_sticky_post_sync( SitePress $sitepress = null ) {
static $instance;
if ( ! $instance ) {
global $wpml_post_translations;
if ( ! $sitepress ) {
global $sitepress;
}
$instance = new WPML_Sticky_Posts_Sync(
$sitepress,
$wpml_post_translations,
new WPML_Sticky_Posts_Lang_Filter(
$sitepress,
$wpml_post_translations
)
);
}
return $instance;
}
/**
* @return WP_Filesystem_Direct
*/
function wpml_get_filesystem_direct() {
static $instance;
if ( ! $instance ) {
$wp_api = new WPML_WP_API();
$instance = $wp_api->get_wp_filesystem_direct();
}
return $instance;
}
/**
* @param array $postarray It will be escaped inside the function
* @param string|null $lang
* @param bool $wp_error
*
* @return int|\WP_Error
*/
function wpml_update_escaped_post( array $postarray, $lang = null, $wp_error = false ) {
return wpml_get_create_post_helper()->insert_post( $postarray, $lang, $wp_error );
}
/**
* @param string $group
*
* @return WPML_WP_Cache
*/
function wpml_get_cache( $group = '' ) {
return new WPML_WP_Cache( $group );
}
if ( ! function_exists( 'wpml_is_ajax' ) ) {
/**
* wpml_is_ajax - Returns true when the page is loaded via ajax.
*
* @since 3.1.5
*
* @return bool
*/
function wpml_is_ajax() {
if ( defined( 'DOING_AJAX' ) ) {
return true;
}
return ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && wpml_mb_strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) ? true : false;
}
}

View File

@@ -0,0 +1,30 @@
<?php
// using this file to handle particular situations that would involve more ellaborate solutions
add_action( 'init', 'icl_load_hacks' );
function icl_dev_mode_warning() {
?>
<div class="error message">
<p>This is a development version of WPML, provided for evaluation purposes only. The code you are using did not
go through any testing or QA. Do not use it in production sites.</strong></p>
<p>To obtain production versions of WPML, visit: <a href="https://wpml.org">wpml.org</a>.</p>
</div>
<?php
}
function icl_load_hacks() {
if ( file_exists( WPML_PLUGIN_PATH . '/inc/hacks/misc-constants.php' ) ) {
include WPML_PLUGIN_PATH . '/inc/hacks/misc-constants.php';
}
include WPML_PLUGIN_PATH . '/inc/hacks/language-canonical-redirects.php';
if ( is_admin() && ! defined( 'ICL_PRODUCTION_MODE' ) ) {
add_action( 'admin_notices', 'icl_dev_mode_warning' );
icl_dev_mode_warning();
}
}
require WPML_PLUGIN_PATH . '/inc/hacks/missing-php-functions.php';

View File

@@ -0,0 +1,18 @@
<?php
if ( defined( 'WP_ADMIN' ) ) {
return;
}
add_action( 'template_redirect', 'icl_language_canonical_redirects', 1 );
function icl_language_canonical_redirects() {
global $wp_query, $sitepress_settings;
if ( 3 == $sitepress_settings['language_negotiation_type'] && is_singular() && empty( $wp_query->posts ) ) {
$pid = get_query_var( 'p' );
$permalink = html_entity_decode( get_permalink( $pid ) );
if ( $permalink ) {
wp_redirect( $permalink, 301 );
exit;
}
}
}

View File

@@ -0,0 +1,2 @@
<?php
define( 'ICL_PRODUCTION_MODE', '2.1.1' );

View File

@@ -0,0 +1,166 @@
<?php
if ( ! function_exists( '_cleanup_header_comment' ) ) {
function _cleanup_header_comment( $str ) {
return trim( preg_replace( '/\s*(?:\*\/|\?>).*/', '', $str ) );
}
}
if ( ! defined( 'E_DEPRECATED' ) ) {
define( 'E_DEPRECATED', 8192 ); }
if ( ! function_exists( 'esc_textarea' ) ) :
function esc_textarea( $text ) {
$safe_text = esc_html( $text );
return apply_filters( 'esc_textarea', $safe_text, $text );
}
endif;
/**
* This file is part of the array_column library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) 2013 Ben Ramsey <http://benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/
if ( ! function_exists( 'array_column' ) ) {
/**
* Returns the values from a single column of the input array, identified by
* the $columnKey.
*
* Optionally, you may provide an $indexKey to index the values in the returned
* array by the values from the $indexKey column in the input array.
*
* @param array $input A multi-dimensional array (record set) from which to pull
* a column of values.
* @param mixed $columnKey The column of values to return. This value may be the
* integer key of the column you wish to retrieve, or it
* may be the string key name for an associative array.
* @param mixed $indexKey (Optional.) The column to use as the index/keys for
* the returned array. This value may be the integer key
* of the column, or it may be the string key name.
* @return array|false
*/
function array_column( $input = null, $columnKey = null, $indexKey = null ) {
// Using func_get_args() in order to check for proper number of
// parameters and trigger errors exactly as the built-in array_column()
// does in PHP 5.5.
$argc = func_num_args();
$params = func_get_args();
if ( $argc < 2 ) {
trigger_error( "array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING );
return null;
}
if ( ! is_array( $params[0] ) ) {
trigger_error( 'array_column() expects parameter 1 to be array, ' . gettype( $params[0] ) . ' given', E_USER_WARNING );
return null;
}
if ( ! is_int( $params[1] )
&& ! is_float( $params[1] )
&& ! is_string( $params[1] )
&& $params[1] !== null
&& ! ( is_object( $params[1] ) && method_exists( $params[1], '__toString' ) )
) {
trigger_error( 'array_column(): The column key should be either a string or an integer', E_USER_WARNING );
return false;
}
if ( isset( $params[2] )
&& ! is_int( $params[2] )
&& ! is_float( $params[2] )
&& ! is_string( $params[2] )
&& ! ( is_object( $params[2] ) && method_exists( $params[2], '__toString' ) )
) {
trigger_error( 'array_column(): The index key should be either a string or an integer', E_USER_WARNING );
return false;
}
$paramsInput = $params[0];
$paramsColumnKey = ( $params[1] !== null ) ? (string) $params[1] : null;
$paramsIndexKey = null;
if ( isset( $params[2] ) ) {
if ( is_float( $params[2] ) || is_int( $params[2] ) ) {
$paramsIndexKey = (int) $params[2];
} else {
$paramsIndexKey = (string) $params[2];
}
}
$resultArray = array();
foreach ( $paramsInput as $row ) {
$key = $value = null;
$keySet = $valueSet = false;
if ( $paramsIndexKey !== null && array_key_exists( $paramsIndexKey, $row ) ) {
$keySet = true;
$key = (string) $row[ $paramsIndexKey ];
}
if ( $paramsColumnKey === null ) {
$valueSet = true;
$value = $row;
} elseif ( is_array( $row ) && array_key_exists( $paramsColumnKey, $row ) ) {
$valueSet = true;
$value = $row[ $paramsColumnKey ];
}
if ( $valueSet ) {
if ( $keySet ) {
$resultArray[ $key ] = $value;
} else {
$resultArray[] = $value;
}
}
}
return $resultArray;
}
}
if ( ! function_exists( 'array_replace_recursive' ) ) {
function array_replace_recursive( $array, $array1 ) {
// handle the arguments, merge one by one
$args = func_get_args();
$array = $args[0];
if ( ! is_array( $array ) ) {
return $array;
}
$args_count = count( $args );
for ( $i = 1; $i < $args_count; $i ++ ) {
if ( is_array( $args[ $i ] ) ) {
$array = array_replace_recursive_recurse( $array, $args[ $i ] );
}
}
return $array;
}
function array_replace_recursive_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 = array_replace_recursive_recurse( $array[ $key ], $value );
}
$array[ $key ] = $value;
}
return $array;
}
}

View File

@@ -0,0 +1,11 @@
<?php
/*
$_pingback_url_parts = parse_url(get_bloginfo('pingback_url'));
if($_SERVER['REQUEST_URI'] == $_pingback_url_parts['path']){
function __icl_void_error_handler($errno, $errstr, $errfile, $errline){
throw new Exception ($errstr . ' [' . $errno . '] in '. $errfile . ':' . $errline);
}
set_error_handler('__icl_void_error_handler',E_ALL);
}
*/

View File

@@ -0,0 +1,32 @@
<?php
global $PHP_SELF;
if ( is_admin() ) {
// wp-admin pages are checked more carefully
if ( is_network_admin() ) {
preg_match( '#/wp-admin/network/?(.*?)$#i', $PHP_SELF, $self_matches );
} elseif ( is_user_admin() ) {
preg_match( '#/wp-admin/user/?(.*?)$#i', $PHP_SELF, $self_matches );
} else {
preg_match( '#/wp-admin/?(.*?)$#i', $PHP_SELF, $self_matches );
}
$pagenow = $self_matches[1];
$pagenow = trim( $pagenow, '/' );
$pagenow = preg_replace( '#\?.*?$#', '', $pagenow );
if ( '' === $pagenow || 'index' === $pagenow || 'index.php' === $pagenow ) {
$pagenow = 'index.php';
} else {
preg_match( '#(.*?)(/|$)#', $pagenow, $self_matches );
$pagenow = strtolower( $self_matches[1] );
if ( '.php' !== substr( $pagenow, -4, 4 ) ) {
$pagenow .= '.php'; // for Options +Multiviews: /wp-admin/themes/index.php (themes.php is queried)
}
}
} else {
if ( preg_match( '#([^/]+\.php)([?/].*?)?$#i', $PHP_SELF, $self_matches ) ) {
$pagenow = strtolower( $self_matches[1] );
} else {
$pagenow = 'index.php';
}
}
unset( $self_matches );

View File

@@ -0,0 +1,814 @@
<?php
/**
* @package wpml-core
*/
/**
* Admin Notifier Class
*
* Manages Admin Notices
*/
add_action( 'init', array( 'ICL_AdminNotifier', 'init' ) );
if ( ! class_exists( 'ICL_AdminNotifier' ) ) {
class ICL_AdminNotifier {
public static function init() {
if ( is_admin() ) {
add_action( 'wp_ajax_icl-hide-admin-message', array( __CLASS__, 'hide_message' ) );
add_action( 'wp_ajax_icl-show-admin-message', array( __CLASS__, 'show_message' ) );
if ( ! defined( 'DOING_AJAX' ) ) {
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_script' ) );
add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) );
}
add_filter( 'troubleshooting_js_data', array( __CLASS__, 'troubleshooting_js_data' ) );
add_action( 'wpml_troubleshooting_cleanup', array( __CLASS__, 'troubleshooting' ) );
add_action( 'wp_ajax_icl_restore_notifications', array( __CLASS__, 'restore_notifications' ) );
add_action( 'wp_ajax_icl_remove_notifications', array( __CLASS__, 'remove_notifications' ) );
}
}
public static function add_script() {
wp_enqueue_script( 'icl-admin-notifier', ICL_PLUGIN_URL . '/res/js/icl-admin-notifier.js', array( 'jquery' ), ICL_SITEPRESS_VERSION );
}
/**
* @param string $message
* @param string $type
*/
public static function add_instant_message( $message, $type = '' ) {
$messages = self::get_messages();
$messages['instant_messages'][] = array(
'text' => $message,
'type' => $type,
);
self::save_messages( $messages );
}
/**
* @param int $message_id
*
* @return bool|array
*/
public static function get_message( $message_id ) {
$messages = self::get_messages();
return isset( $messages['messages'][ $message_id ] ) ? $messages['messages'][ $message_id ] : false;
}
public static function message_id_exists( $message_id ) {
$message = self::get_message( $message_id );
return $message !== false;
}
private static function get_messages() {
$messages = get_option( 'icl_admin_messages' );
if ( ! ( isset( $messages ) && $messages != false ) ) {
return array(
'messages' => array(),
'instant_messages' => array(),
);
}
if ( ! isset( $messages['messages'] ) || ! isset( $messages['instant_messages'] ) ) {
$messages = array(
'messages' => array(),
'instant_messages' => array(),
);
}
return (array) $messages;
}
private static function save_messages( $messages ) {
if ( isset( $messages ) ) {
update_option( 'icl_admin_messages', (array) $messages );
}
self::get_messages();
}
/**
* @param array<mixed> $args
* Args attributes:
* string id - An unique identifier for the message
* string msg - The actual message
* string type (optional) - Any string: it will be used as css class fro the message container. A typical value is 'error', but the following strings can be also used: icl-admin-message-information, icl-admin-message-warning
* array classes (optional) - Display the notice only on specific url(s)
* bool hide (optional) - Enable the toggle link to permanently hide the notice
* bool hide_per_user (optional) - Enable the toggle link per user basis (overrides hide option)
* bool dismiss (optional) - Enable the dismiss option
* bool dismiss_per_user (optional) - Enable the dismiss option per user basis (overrides dismiss option)
* bool|string fallback_text (optional) - A message to show when the notice gets hidden
* bool|string fallback_type (optional) - The message type to use in the fallback message (@see $type)
* array fallback_classes (optional) - The message type to use in the fallback message (@see $type)
* bool|string group (optional) - A way to group messages: when displaying messages stored with this method, it's possible to filter them by group (@see ICL_AdminNotifier::displayMessages)
* bool admin_notice (optional) - Hook the rendering to the 'admin_notice' action
* string|array limit_to_page (optional) - Display the notice only on specific page(s)
*/
public static function add_message( $args ) {
$defaults = array(
'type' => '',
'classes' => array(),
'hide' => false,
'hide_per_user' => false,
'dismiss' => false,
'dismiss_per_user' => false,
'fallback_text' => false,
'fallback_type' => false,
'fallback_classes' => array(),
'group' => false,
'admin_notice' => false,
'hidden' => false,
'dismissed' => false,
'limit_to_page' => false,
'show_once' => false,
'capability' => '',
);
$args = self::sanitize_message_args( $args );
$args = array_merge( $defaults, $args );
$id = $args['id'];
// Check if existing message has been set as dismissed or hidden
if ( self::message_id_exists( $id ) ) {
$temp_msg = self::get_message( $id );
if ( $temp_msg ) {
$current_user_id = get_current_user_id();
$message_user_data = isset( $temp_msg['users'][ $current_user_id ] ) ? $temp_msg['users'][ $current_user_id ] : false;
if ( self::is_user_dismissed( $temp_msg ) || self::is_globally_dismissed( $temp_msg ) || self::is_globally_hidden( $temp_msg ) ) {
return;
}
if ( isset( $message_user_data['hidden'] ) ) {
$args['hidden'] = $message_user_data['hidden'] ? false : $args['hidden'];
}
}
}
$id = $id ? $id : md5( wp_json_encode( $args ) );
$messages = self::get_messages();
$message = array(
'id' => $id,
'text' => $args['text'],
'type' => $args['type'],
'classes' => $args['classes'],
'hide' => $args['hide'],
'hide_per_user' => $args['hide_per_user'],
'dismiss' => $args['dismiss'],
'dismiss_per_user' => $args['dismiss_per_user'],
'fallback_text' => $args['fallback_text'],
'fallback_type' => $args['fallback_type'],
'fallback_classes' => $args['classes'],
'group' => $args['group'],
'admin_notice' => $args['admin_notice'],
'hidden' => false,
'dismissed' => false,
'limit_to_page' => $args['limit_to_page'],
'show_once' => $args['show_once'],
'capability' => $args['capability'],
);
$message_md5 = md5( wp_json_encode( $message ) );
if ( isset( $messages['messages'][ $id ] ) ) {
$existing_message_md5 = md5( wp_json_encode( $messages['messages'][ $id ] ) );
if ( $message_md5 != $existing_message_md5 ) {
unset( $messages['messages'][ $id ] );
}
}
if ( ! isset( $messages['messages'][ $id ] ) ) {
$messages['messages'][ $id ] = $message;
self::save_messages( $messages );
}
}
public static function is_user_dismissed( $message_data ) {
$current_user_id = get_current_user_id();
$message_user_data = isset( $message_data['users'][ $current_user_id ] ) ? $message_data['users'][ $current_user_id ] : false;
return ! empty( $message_data['dismiss_per_user'] ) && ! empty( $message_user_data['dismissed'] );
}
public static function is_globally_dismissed( $message_data ) {
return ! empty( $message_data['dismiss'] ) && $message_data['dismissed'];
}
public static function is_globally_hidden( $message_data ) {
return ! empty( $message_data['hide'] ) && $message_data['hidden'];
}
public static function hide_message() {
$message_id = self::get_message_id();
$dismiss = isset( $_POST['dismiss'] ) ? $_POST['dismiss'] : false;
if ( ! self::message_id_exists( $message_id ) ) {
exit;
}
self::set_message_display( $message_id, false, 'hide', 'hidden', 'hide_per_user' );
if ( $dismiss ) {
self::set_message_display( $message_id, false, 'dismiss', 'dismissed', 'dismiss_per_user' );
} else {
$messages = self::get_messages();
$message = $messages['messages'][ $message_id ];
if ( $message && isset( $message['fallback_text'] ) && $message['fallback_text'] ) {
echo self::display_message( $message_id, $message['fallback_text'], $message['fallback_type'], $message['fallback_classes'], false, false, true, true );
}
}
exit;
}
public static function get_message_id() {
$message_id = '';
if ( isset( $_POST['icl-admin-message-id'] ) ) {
$message_id = filter_var( $_POST['icl-admin-message-id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
$message_id = $message_id ? $message_id : '';
$message_id = preg_replace( '/^icl-id-/', '', $message_id );
return $message_id;
}
public static function show_message() {
$message_id = self::get_message_id();
if ( ! self::message_id_exists( $message_id ) ) {
exit;
}
self::set_message_display( $message_id, true, 'hide', 'hidden', 'hide_per_user' );
$messages = self::get_messages();
$message = $messages['messages'][ $message_id ];
if ( $message ) {
echo self::display_message( $message_id, $message['text'], $message['type'], $message['classes'], $message['hide'] || $message['hide_per_user'], $message['dismiss'] || $message['dismiss_per_user'], true, true );
}
exit;
}
public static function engage_message() {
$message_id = self::get_message_id();
if ( ! self::message_id_exists( $message_id ) ) {
exit;
}
self::set_message_display( $message_id, true, 'dismiss', 'dismissed', 'dismiss_per_user' );
}
private static function set_message_display( $message_id, $show, $action, $action_past, $action_user ) {
if ( $message_id === null ) {
return;
}
$messages = self::get_messages();
if ( ! isset( $messages['messages'][ $message_id ] ) ) {
return;
}
$message = $messages['messages'][ $message_id ];
$current_user_id = get_current_user_id();
if ( $message[ $action_user ] && $current_user_id ) {
$message['users'][ $current_user_id ][ $action_past ] = ! $show;
} elseif ( $message[ $action ] ) {
$message[ $action_past ] = ! $show;
}
$messages['messages'][ $message_id ] = $message;
self::save_messages( $messages );
}
public static function remove_message( $message_id ) {
if ( ! $message_id ) {
return false;
}
$messages = self::get_messages();
if ( ! isset( $messages['messages'][ $message_id ] ) ) {
return false;
}
unset( $messages['messages'][ $message_id ] );
self::save_messages( $messages );
return false;
}
public static function remove_message_group( $message_group ) {
if ( ! $message_group ) {
return;
}
$all_messages = self::get_messages();
if ( ! isset( $all_messages['messages'] ) ) {
return;
}
$messages = $all_messages['messages'];
$ids_to_remove = array();
foreach ( $messages as $id => $message_data ) {
if ( isset( $message_data['group'] ) && $message_data['group'] == $message_group ) {
$ids_to_remove[] = $id;
}
}
foreach ( $ids_to_remove as $id_to_remove ) {
self::remove_message( $id_to_remove );
}
}
public static function display_messages( $group = false ) {
if ( is_admin() ) {
$messages = self::get_messages();
foreach ( $messages['messages'] as $id => $msg ) {
if ( ! $group || ( isset( $msg['group'] ) && $msg['group'] == $group ) ) {
if ( isset( $msg['admin_notice'] ) && ! $msg['admin_notice'] ) {
if ( ! isset( $msg['capability'] ) || ( $msg['capability'] == '' ) || current_user_can( $msg['capability'] ) ) {
if ( array_key_exists( 'limit_to_page', $msg ) ) {
foreach ( $msg['limit_to_page'] as $page ) {
if ( array_key_exists( 'page', $_GET ) && $_GET['page'] === $page ) {
self::display_message( $id, $msg['text'], $msg['type'], $msg['classes'], $msg['hide'] || $msg['hide_per_user'], $msg['dismiss'] || $msg['dismiss_per_user'], true );
}
}
} else {
self::display_message( $id, $msg['text'], $msg['type'], $msg['classes'], $msg['hide'] || $msg['hide_per_user'], $msg['dismiss'] || $msg['dismiss_per_user'], true );
}
}
}
}
}
foreach ( $messages['instant_messages'] as $msg ) {
self::display_instant_message( $msg['text'], $msg['type'] );
}
// delete instant messages
$messages['instant_messages'] = array();
self::save_messages( $messages );
}
}
/**
* @deprecated deprecated @since version 3.2. Use ICL_AdminNotifier::display_message()
*
* @param bool $group
*/
public static function displayMessages( $group = false ) {
self::display_messages( $group );
}
public static function admin_notices() {
$messages = self::get_messages();
if ( isset( $messages['messages'] ) ) {
foreach ( $messages['messages'] as $id => $msg ) {
if ( isset( $msg['limit_to_page'] ) && $msg['limit_to_page'] ) {
if ( ! is_array( $msg['limit_to_page'] ) ) {
$msg['limit_to_page'] = (array) $msg['limit_to_page'];
}
if ( ! isset( $_REQUEST['page'] ) || ! in_array( $_REQUEST['page'], $msg['limit_to_page'] ) ) {
continue;
}
}
if ( $msg['admin_notice'] ) {
$current_user_id = get_current_user_id();
$display = true;
$display_fallback = false;
$message_user_data = isset( $msg['users'][ $current_user_id ] ) ? $msg['users'][ $current_user_id ] : false;
if ( $msg['dismiss_per_user'] && isset( $message_user_data['dismissed'] ) && $message_user_data['dismissed'] ) {
$display = false;
} elseif ( $msg['dismiss'] && isset( $msg['dismissed'] ) && $msg['dismissed'] ) {
$display = false;
}
if ( $display ) {
if ( $msg['hide_per_user'] && isset( $message_user_data['hidden'] ) && $message_user_data['hidden'] ) {
$display = false;
$display_fallback = $msg['fallback_text'];
} elseif ( $msg['hide'] && isset( $msg['hidden'] ) && $msg['hidden'] ) {
$display = false;
$display_fallback = $msg['fallback_text'];
}
}
$msg['classes'] = isset( $msg['classes'] ) ? $msg['classes'] : array();
$msg['fallback_classes'] = isset( $msg['fallback_classes'] ) ? $msg['fallback_classes'] : array();
if ( $display ) {
self::display_message( $id, $msg['text'], $msg['type'], $msg['classes'], $msg['hide'] || $msg['hide_per_user'], $msg['dismiss'] || $msg['dismiss_per_user'], true );
if ( $msg['show_once'] && ! $display_fallback ) {
self::remove_message( $msg['id'] );
}
} elseif ( $display_fallback ) {
self::display_message( $id, $msg['fallback_text'], $msg['fallback_type'], $msg['fallback_classes'], false, false, true );
if ( $msg['show_once'] ) {
self::remove_message( $msg['id'] );
}
}
}
}
}
}
/**
* @param string $id
* @param string $message
* @param string $type
* @param string|array $classes
* @param bool $hide
* @param bool $dismiss
* @param bool $admin_notice
* @param bool $echo
*
* @return string
*/
private static function display_message( $id, $message, $type = '', $classes = array(), $hide = true, $dismiss = false, $admin_notice = false, $echo = false ) {
$result = '';
$temp_classes = array();
if ( strpos( $type, 'icl-admin-message' ) ) {
$type = str_replace( 'icl-admin-message-', '', $type );
}
if ( $admin_notice ) {
$temp_classes[] = 'icl-admin-message';
}
$temp_types = explode( ' ', $type );
$temp_types = array_unique( $temp_types );
foreach ( $temp_types as $temp_type ) {
if ( $admin_notice ) {
$temp_classes[] = 'icl-admin-message-' . $temp_type;
}
$temp_classes[] = $temp_type;
}
if ( $classes ) {
if ( ! is_array( $classes ) ) {
$classes = explode( ' ', $classes );
}
foreach ( $classes as $class ) {
$temp_classes[] = $class;
}
}
if ( $hide or $dismiss ) {
$temp_classes[] = 'otgs-is-dismissible';
}
$temp_classes = array_unique( $temp_classes );
$class = implode( ' ', $temp_classes );
$result .= '<div class="' . $class . '" id="icl-id-' . $id . '"';
if ( $hide ) {
$result .= ' data-hide-text="' . __( 'Hide', 'sitepress' ) . '" ';
}
$result .= '>';
$result .= '<p>' . self::sanitize_and_format_message( $message ) . '</p>';
if ( $hide ) {
$result .= ' <span class="icl-admin-message-hide notice-dismiss"><span class="screen-reader-text">' . __( 'Hide this notice.', 'sitepress' ) . '</span></span>';
}
if ( $dismiss ) {
$result .= ' <span class="icl-admin-message-dismiss notice-dismiss">';
$result .= '<span class="screen-reader-text"><input class="icl-admin-message-dismiss-check" type="checkbox" value="1" />';
$result .= __( 'Dismiss this notice.', 'sitepress' );
$result .= '</span></span>';
}
$result .= '</div>';
if ( ! $echo ) {
echo $result;
}
return $result;
}
public static function display_instant_message( $message, $type = 'information', $class = false, $return = false, $fadeout = false ) {
$classes = array();
if ( ! $class && $type ) {
$classes[] = $type;
}
$classes[] = 'instant-message';
$classes[] = 'message';
$classes[] = 'message-' . $type;
foreach ( $classes as $class ) {
$classes[] = 'icl-admin-' . $class;
}
if ( $fadeout ) {
$classes[] = 'js-icl-fadeout';
}
$classes = array_unique( $classes );
if ( in_array( 'error', $classes ) ) {
$key = array_search( 'error', $classes );
if ( $key !== false ) {
unset( $classes[ $key ] );
}
}
$result = '<div class="' . implode( ' ', $classes ) . '">';
$result .= self::sanitize_and_format_message( $message );
$result .= '</div>';
if ( ! $return ) {
echo $result;
}
return $result;
}
/**
* @param array<mixed> $args
*
* @return mixed
*/
private static function sanitize_message_args( $args ) {
if ( isset( $args['msg'] ) ) {
$args['text'] = $args['msg'];
unset( $args['msg'] );
}
if ( isset( $args['fallback'] ) ) {
$args['fallback_message'] = $args['fallback'];
unset( $args['fallback'] );
}
if ( isset( $args['message_fallback'] ) ) {
$args['fallback_message'] = $args['message_fallback'];
unset( $args['message_fallback'] );
}
if ( isset( $args['type_fallback'] ) ) {
$args['fallback_type'] = $args['type_fallback'];
unset( $args['type_fallback'] );
return $args;
}
if ( ! isset( $args['classes'] ) ) {
$args['classes'] = array();
} elseif ( ! is_array( $args['classes'] ) ) {
$args['classes'] = (array) $args['classes'];
}
if ( ! isset( $args['limit_to_page'] ) ) {
$args['limit_to_page'] = array();
} elseif ( ! is_array( $args['limit_to_page'] ) ) {
$args['limit_to_page'] = (array) $args['limit_to_page'];
}
return $args;
}
static function troubleshooting_js_data( $data ) {
$data['nonce']['icl_restore_notifications'] = wp_create_nonce( 'icl_restore_notifications' );
$data['nonce']['icl_remove_notifications'] = wp_create_nonce( 'icl_remove_notifications' );
return $data;
}
static function has_hidden_messages() {
$messages = self::get_messages();
$no_hidden_messages = true;
foreach ( $messages as $group => $message_group ) {
foreach ( $message_group as $id => $msg ) {
if ( ( isset( $msg['hidden'] ) && $msg['hidden'] ) || ( isset( $msg['dismissed'] ) && $msg['dismissed'] ) ) {
$no_hidden_messages = false;
} else {
$current_user_id = get_current_user_id();
$message_user_data = isset( $msg['users'][ $current_user_id ] ) ? $msg['users'][ $current_user_id ] : false;
if ( $message_user_data && $msg['dismiss_per_user'] && isset( $message_user_data['dismissed'] ) && $message_user_data['dismissed'] ) {
$no_hidden_messages = false;
} elseif ( $message_user_data && $msg['dismiss'] && isset( $msg['dismissed'] ) && $msg['dismissed'] ) {
$no_hidden_messages = false;
}
if ( $no_hidden_messages ) {
if ( isset( $msg['hide_per_user'] ) && $msg['hide_per_user'] && isset( $message_user_data['hidden'] ) && $message_user_data['hidden'] ) {
$no_hidden_messages = ! $msg['fallback_text'];
} elseif ( $msg['hide'] && isset( $msg['hidden'] ) && $msg['hidden'] ) {
$no_hidden_messages = ! $msg['fallback_text'];
}
}
}
if ( ! $no_hidden_messages ) {
return true;
}
}
}
return false;
}
static function troubleshooting() {
?>
<h4><?php _e( 'Messages and notifications', 'sitepress' ); ?></h4>
<?php
if ( self::has_hidden_messages() ) {
?>
<p>
<input id="icl_restore_notifications" type="button" class="button-secondary" value="<?php echo __( 'Restore messages and notification', 'sitepress' ); ?>"/>
<br/>
<br/>
<input id="icl_restore_notifications_all_users" name="icl_restore_notifications_all_users" type="checkbox" value="1"/><label for="icl_restore_notifications_all_users"><?php echo __( 'Apply to all users', 'sitepress' ); ?></label>
<br/>
<br/>
<small style="margin-left:10px;"><?php echo __( 'Restore dismissed and hidden messages and notifications.', 'sitepress' ); ?></small>
</p>
<?php
}
?>
<p>
<input id="icl_remove_notifications" type="button" class="button-secondary" value="<?php echo __( 'Remove all messages and notifications', 'sitepress' ); ?>"/>
<br/>
<small style="margin-left:10px;"><?php echo __( 'Remove all messages and notifications, for all users.', 'sitepress' ); ?></small>
</p>
<?php
}
static function remove_notifications() {
self::save_messages( array() );
echo wp_json_encode(
array(
'errors' => 0,
'message' => __( 'Done', 'sitepress' ),
'cont' => 0,
'reload' => 1,
)
);
die();
}
static function restore_notifications() {
$all_users = $_POST['all_users'];
$messages = self::get_messages();
$dirty = 0;
foreach ( $messages as $group => $message_group ) {
foreach ( $message_group as $id => $msg ) {
if ( $msg['hidden'] ) {
$msg['hidden'] = false;
$dirty ++;
}
if ( $msg['dismissed'] ) {
$msg['dismissed'] = false;
$dirty ++;
}
$current_user_id = get_current_user_id();
foreach ( $msg['users'] as $user_id => $message_user_data ) {
if ( $current_user_id == $user_id || $all_users ) {
if ( $message_user_data['hidden'] ) {
$message_user_data['hidden'] = false;
$dirty ++;
}
if ( $message_user_data['dismissed'] ) {
$message_user_data['dismissed'] = false;
$dirty ++;
}
}
$msg['users'][ $user_id ] = $message_user_data;
}
$message_group[ $id ] = $msg;
}
$messages[ $group ] = $message_group;
}
if ( $dirty ) {
self::save_messages( $messages );
}
echo wp_json_encode(
array(
'errors' => 0,
'message' => __( 'Done', 'sitepress' ),
'cont' => $dirty,
'reload' => 1,
)
);
die();
}
/** Deprecated methods */
/**
* @param int $message_id
*
* @return bool
* @deprecated deprecated @since version 3.2. Use ICL_AdminNotifier::remove_message()
*
*/
public static function removeMessage( $message_id ) {
return self::remove_message( $message_id );
}
/**
* @deprecated deprecated @since version 3.2
*/
public static function hideMessage() {
self::hide_message();
}
/**
* @deprecated deprecated @since version 3.2
*
* @param string $message
* @param string $type
*/
public static function addInstantMessage( $message, $type = '' ) {
self::add_instant_message( $message, $type );
}
/**
* @deprecated deprecated @since version 3.2
*/
public static function addScript() {
self::add_script();
}
/**
* @deprecated deprecated @since version 3.2
*
* @param string $id An unique identifier for the message
* @param string $msg The actual message
* @param string $type (optional) Any string: it will be used as css class fro the message container. A typical value is 'error', but the following strings can be also used: icl-admin-message-information, icl-admin-message-warning
* @param bool $hide (optional) Enable the toggle link to permanently hide the notice
* @param bool $fallback_message (optional) A message to show when the notice gets hidden
* @param bool $fallback_type (optional) The message type to use in the fallback message (@see $type)
* @param bool $group (optional) A way to group messages: when displaying messages stored with this method, it's possible to filter them by group (@see ICL_AdminNotifier::displayMessages)
* @param bool $admin_notice (optional) Hook the rendering to the 'admin_notice' action
*/
public static function addMessage( $id, $msg, $type = '', $hide = true, $fallback_message = false, $fallback_type = false, $group = false, $admin_notice = false ) {
$args = array(
'id' => $id,
'msg' => $msg,
'type' => $type,
'hide' => $hide,
'message_fallback' => $fallback_message,
'fallback_type' => $fallback_type,
'group' => $group,
'admin_notice' => $admin_notice,
);
self::add_message( $args );
}
/**
* @param string $message
* @param string $type
* @param bool $class
* @param bool $return
*
* @return string
* @deprecated deprecated @since version 3.2. Use ICL_AdminNotifier::display_instant_message()
*
*/
public static function displayInstantMessage( $message, $type = 'information', $class = false, $return = false ) {
return self::display_instant_message( $message, $type, $class, $return );
}
/**
* @param string $message
*
* @return string
*/
public static function sanitize_and_format_message( $message ) {
// return preg_replace( '/`(.*?)`/s', '<pre>$1</pre>', stripslashes( $message ) );
$backticks_pattern = '|`(.*)`|U';
preg_match_all( $backticks_pattern, $message, $matches );
$sanitized_message = $message;
if ( 2 === count( $matches ) ) {
$matches_to_sanitize = $matches[1];
foreach ( $matches_to_sanitize as &$match_to_sanitize ) {
$match_to_sanitize = '<pre>' . esc_html( $match_to_sanitize ) . '</pre>';
}
unset( $match_to_sanitize );
$sanitized_message = str_replace( $matches[0], $matches_to_sanitize, $sanitized_message );
}
return stripslashes( $sanitized_message );
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
global $pagenow;
$filtered_import = filter_input( INPUT_GET, 'import',FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
$filtered_step = filter_input( INPUT_GET, 'step',FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
if ($pagenow == 'admin.php' && 0 === strcmp( $filtered_import, 'wordpress' ) && $filtered_step == 1 ) {
add_action('admin_head', 'icl_import_xml');
}
function icl_import_xml() {
global $sitepress;
$langs = $sitepress->get_active_languages();
if (empty($langs)) {
return;
}
$default = $sitepress->get_default_language();
$out = '<h3>' . esc_html__('Select Language', 'sitepress') . '</h3><p><select name="icl_post_language">';
foreach ($langs as $lang) {
$out .= '<option value="' . esc_attr( $lang['code'] ) . '"';
if ($default == $lang['code']) {
$out .= ' selected="selected"';
}
$out .= '>' . esc_html( $lang['native_name'] ) . '<\/option>';
}
$out .= '<\/select><\/p>';
echo '
<script type="text/javascript">
jQuery(document).ready(function(){
jQuery("#wpbody-content").find("form .submit").before(\'' . $out . '\');
});
</script>
';
}
add_action('import_start', 'icl_import_xml_start', 0);
function icl_import_xml_start() {
set_time_limit( 0 );
$post_languages = isset( $_POST['icl_post_language'] ) ? $_POST['icl_post_language'] : array();
$_POST['icl_tax_post_tag_language'] = $_POST['icl_tax_category_language'] = $_POST['icl_tax_language'] = $post_languages;
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* Registers scripts so that they can be reused throughout WPML plugins
* Hooked to `admin_enqueue_scripts`
*/
function wpml_register_js_scripts() {
wp_register_script( 'wpml-underscore-template-compiler',
ICL_PLUGIN_URL . '/res/js/shared/wpml-template-compiler.js',
array( "underscore" ) );
wp_register_script( 'wpml-domain-validation',
ICL_PLUGIN_URL . '/res/js/settings/wpml-domain-validation.js',
array( "jquery" ) );
}
if ( is_admin() ) {
add_action( 'admin_enqueue_scripts', 'wpml_register_js_scripts', -PHP_INT_MAX );
} else {
add_action( 'wp_enqueue_scripts', 'wpml_register_js_scripts', -PHP_INT_MAX );
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Registers TM scripts that they are being used from WPML plugin
* Hooked to `admin_enqueue_scripts` and 'wp_enqueue_scripts'
*/
if ( ! \WPML\Plugins::isTMActive() && defined( 'WPML_TM_URL' ) ) {
/**
* Registers scripts so that they can be reused throughout WPML plugins
*/
function wpml_tm_register_js_scripts() {
if ( \WPML\Setup\Option::isTMAllowed() ) {
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' );
}
}

View File

@@ -0,0 +1,301 @@
<?php
/**
* @return mixed
*/
function icl_get_languages_names() {
static $__icl_lang_names_cached = null;
if ( $__icl_lang_names_cached === null ) {
$serialized_languages = file_get_contents( WPML_PLUGIN_PATH . '/res/languages.json' );
$__icl_lang_names = json_decode( $serialized_languages, true );
$__icl_lang_names_cached = $__icl_lang_names;
}
return $__icl_lang_names_cached;
}
/**
* @return array
*/
function icl_get_languages_codes() {
static $result = null;
if ( null === $result ) {
$result = array(
'Abkhazian' => 'ab',
'Afar' => 'aa',
'Afrikaans' => 'af',
'Akan' => 'ak',
'Albanian' => 'sq',
'Amharic' => 'am',
'Arabic' => 'ar',
'Armenian' => 'hy',
'Assamese' => 'as',
'Avar' => 'av',
'Avestan' => 'ae',
'Aymara' => 'ay',
'Azerbaijani' => 'az',
'Bambara' => 'bm',
'Bashkir' => 'ba',
'Basque' => 'eu',
'Belarusian' => 'be',
'Bengali' => 'bn',
'Bhutani' => 'dz',
'Bihari' => 'bh',
'Bislama' => 'bi',
'Bosnian' => 'bs',
'Breton' => 'br',
'Bulgarian' => 'bg',
'Burmese' => 'my',
'Cambodian' => 'km',
'Catalan' => 'ca',
'Chamorro' => 'ch',
'Chechen' => 'ce',
'Chichewa' => 'ny',
'Chinese (Simplified)' => 'zh-hans',
'Chinese (Traditional)' => 'zh-hant',
'Chuvash' => 'cv',
'Cornish' => 'kw',
'Corsican' => 'co',
'Cree' => 'cr',
'Croatian' => 'hr',
'Czech' => 'cs',
'Danish' => 'da',
'English' => 'en',
'Esperanto' => 'eo',
'Estonian' => 'et',
'Ewe' => 'ee',
'Faeroese' => 'fo',
'Fiji' => 'fj',
'Finnish' => 'fi',
'French' => 'fr',
'Frisian' => 'fy',
'Fulah' => 'ff',
'Galician' => 'gl',
'Georgian' => 'ka',
'German' => 'de',
'Greek' => 'el',
'Greenlandic' => 'kl',
'Guarani' => 'gn',
'Gujarati' => 'gu',
'Hausa' => 'ha',
'Hebrew' => 'he',
'Herero' => 'hz',
'Hindi' => 'hi',
'Hiri Motu' => 'ho',
'Hungarian' => 'hu',
'Icelandic' => 'is',
'Igbo' => 'ig',
'Indonesian' => 'id',
'Interlingua' => 'ia',
'Interlingue' => 'ie',
'Inuktitut' => 'iu',
'Inupiak' => 'ik',
'Irish' => 'ga',
'Italian' => 'it',
'Japanese' => 'ja',
'Javanese' => 'jv',
'Kannada' => 'kn',
'Kanuri' => 'kr',
'Kashmiri' => 'ks',
'Kazakh' => 'kk',
'Kikuyu' => 'ki',
'Kinyarwanda' => 'rw',
'Kirghiz' => 'ky',
'Kirundi' => 'rn',
'Komi' => 'kv',
'Kongo' => 'kg',
'Korean' => 'ko',
'Kurdish' => 'ku',
'Kwanyama' => 'kj',
'Laothian' => 'lo',
'Latvian' => 'lv',
'Lingala' => 'ln',
'Lithuanian' => 'lt',
'Luganda' => 'lg',
'Luxembourgish' => 'lb',
'Macedonian' => 'mk',
'Malagasy' => 'mg',
'Malay' => 'ms',
'Malayalam' => 'ml',
'Maldivian' => 'dv',
'Maltese' => 'mt',
'Manx' => 'gv',
'Maori' => 'mi',
'Marathi' => 'mr',
'Marshallese' => 'mh',
'Moldavian' => 'mo',
'Mongolian' => 'mn',
'Nauru' => 'na',
'Navajo' => 'nv',
'Ndonga' => 'ng',
'Nepali' => 'ne',
'North Ndebele' => 'nd',
'Northern Sami' => 'se',
'Dutch' => 'nl',
'Norwegian Bokmål' => 'no',
'Norwegian Nynorsk' => 'nn',
'Occitan' => 'oc',
'Old Slavonic' => 'cu',
'Oriya' => 'or',
'Oromo' => 'om',
'Ossetian' => 'os',
'Pali' => 'pi',
'Pashto' => 'ps',
'Persian' => 'fa',
'Polish' => 'pl',
'Portuguese, Brazil' => 'pt-br',
'Portuguese, Portugal' => 'pt-pt',
'Punjabi' => 'pa',
'Quechua' => 'qu',
'Rhaeto-Romance' => 'rm',
'Romanian' => 'ro',
'Russian' => 'ru',
'Samoan' => 'sm',
'Sango' => 'sg',
'Sanskrit' => 'sa',
'Sardinian' => 'sc',
'Scots Gaelic' => 'gd',
'Serbian' => 'sr',
'Serbo-Croatian' => 'sh',
'Sesotho' => 'st',
'Setswana' => 'tn',
'Shona' => 'sn',
'Sindhi' => 'sd',
'Singhalese' => 'si',
'Siswati' => 'ss',
'Slavic' => 'sla',
'Slovak' => 'sk',
'Slovenian' => 'sl',
'Somali' => 'so',
'South Ndebele' => 'nr',
'Spanish' => 'es',
'Sudanese' => 'su',
'Swahili' => 'sw',
'Swedish' => 'sv',
'Tagalog' => 'tl',
'Tahitian' => 'ty',
'Tajik' => 'tg',
'Tamil' => 'ta',
'Tatar' => 'tt',
'Telugu' => 'te',
'Thai' => 'th',
'Tibetan' => 'bo',
'Tigrinya' => 'ti',
'Tonga' => 'to',
'Tsonga' => 'ts',
'Turkish' => 'tr',
'Turkmen' => 'tk',
'Twi' => 'tw',
'Uighur' => 'ug',
'Ukrainian' => 'uk',
'Urdu' => 'ur',
'Uzbek' => 'uz',
'Venda' => 've',
'Vietnamese' => 'vi',
'Welsh' => 'cy',
'Wolof' => 'wo',
'Xhosa' => 'xh',
'Yiddish' => 'yi',
'Yoruba' => 'yo',
'Zhuang' => 'za',
'Zulu' => 'zu',
);
}
return $result;
}
/**
* @return array
*/
function icl_get_languages_locales() {
static $result = null;
if ( ! $result ) {
$result = array(
'af' => 'af_ZA',
'ar' => 'ar',
'az' => 'az',
'be' => 'be_BY',
'bg' => 'bg_BG',
'bn' => 'bn_BD',
'bs' => 'bs_BA',
'ca' => 'ca',
'cs' => 'cs_CZ',
'cy' => 'cy_GB',
'da' => 'da_DK',
'de' => 'de_DE',
'el' => 'el',
'en' => 'en_US',
'eo' => 'eo_UY',
'es' => 'es_ES',
'et' => 'et',
'eu' => 'eu_ES',
'fa' => 'fa_IR',
'fi' => 'fi',
'fo' => 'fo_FO',
'fr' => 'fr_FR',
'ga' => 'ga_IE',
'gl' => 'gl_ES',
'he' => 'he_IL',
'hi' => 'hi_IN',
'hr' => 'hr',
'hu' => 'hu_HU',
'hy' => 'hy_AM',
'id' => 'id_ID',
'is' => 'is_IS',
'it' => 'it_IT',
'ja' => 'ja',
'ka' => 'ge_GE',
'km' => 'km_KH',
'ko' => 'ko_KR',
'ku' => 'ckb',
'lt' => 'lt_LT',
'lv' => 'lv_LV',
'mg' => 'mg_MG',
'mk' => 'mk_MK',
'mn' => 'mn_MN',
'ms' => 'ms_MY',
'mt' => 'mt_MT',
'nb' => 'nb_NO',
'ne' => 'ne',
'no' => 'nb_NO',
'nn' => 'nn_NO',
'ni' => 'ni_ID',
'nl' => 'nl_NL',
'pa' => 'pa_IN',
'pl' => 'pl_PL',
'pt-br' => 'pt_BR',
'pt-pt' => 'pt_PT',
'qu' => 'quz_PE',
'ro' => 'ro_RO',
'ru' => 'ru_RU',
'si' => 'si_LK',
'sk' => 'sk_SK',
'sl' => 'sl_SI',
'so' => 'so_SO',
'sq' => 'sq_AL',
'sr' => 'sr_RS',
'su' => 'su_ID',
'sv' => 'sv_SE',
'ta' => 'ta_IN',
'tg' => 'tg_TJ',
'th' => 'th',
'tr' => 'tr_TR',
'ug' => 'ug_CN',
'uk' => 'uk',
'ur' => 'ur',
'uz' => 'uz_UZ',
'vi' => 'vi_VN',
'zh-hans' => 'zh_CN',
'zh-hant' => 'zh_TW',
);
}
return $result;
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* Class SitePressLanguageSwitcher
*
* @deprecated since 3.6.0
*/
class SitePressLanguageSwitcher {
/**
* @deprecated since 3.6.0
*
* @return string
*/
static function get_language_selector_footer() {
ob_start();
do_action( 'wpml_footer_language_selector' );
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* @deprecated since 3.6.0
*/
function language_selector_footer() {
do_action( 'wpml_footer_language_selector' );
}
/**
* @param string $native_name
* @param bool $translated_name
* @param bool $show_native_name
* @param bool $show_translate_name
* @param bool $include_html
*
* @return string
* @deprecated since 3.6.0
*
*/
public function language_display( $native_name, $translated_name = false, $show_native_name = false, $show_translate_name = false, $include_html = true ) {
$result = '';
if ( ! $show_native_name ) {
$native_name = '';
}
if ( ! $show_translate_name ) {
$translated_name = '';
}
if ( $native_name && $translated_name ) {
if ( $native_name != $translated_name ) {
if ( $show_native_name ) {
if ( $include_html ) {
$result .= '<span class="icl_lang_sel_native">';
}
$result .= '%1$s';
if ( $include_html ) {
$result .= '</span>';
}
if ( $show_translate_name ) {
$result .= ' ';
if ( $include_html ) {
$result .= '<span class="icl_lang_sel_translated">';
}
$result .= $show_native_name
? '<span class="icl_lang_sel_bracket">(</span>%2$s<span class="icl_lang_sel_bracket">)</span>'
: '%2$s';
if ( $include_html ) {
$result .= '</span>';
}
}
} elseif ( $show_translate_name ) {
if ( $include_html ) {
$result .= '<span class="icl_lang_sel_translated">';
}
$result .= '%2$s';
if ( $include_html ) {
$result .= '</span>';
}
}
} else {
if ( $include_html ) {
$result .= '<span class="icl_lang_sel_current icl_lang_sel_native">';
}
$result .= '%1$s';
if ( $include_html ) {
$result .= '</span>';
}
}
} elseif ( $native_name ) {
$result = '%1$s';
} elseif ( $translated_name ) {
$result = '%2$s';
}
return sprintf( $result, $native_name, $translated_name );
}
} // end class
global $icl_language_switcher;
$icl_language_switcher = new SitePressLanguageSwitcher();

View File

@@ -0,0 +1,183 @@
<?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,
'post_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
);
}
}

View File

@@ -0,0 +1,55 @@
<?php
$icl_ncp_plugins = array(
'absolute-links/absolute-links-plugin.php',
'cms-navigation/CMS-Navigation.php',
);
$active_plugins = get_option( 'active_plugins' );
$icl_ncp_plugins = $active_plugins ? array_intersect( $icl_ncp_plugins, $active_plugins ) : [];
if ( ! empty( $icl_ncp_plugins ) ) {
$icl_sitepress_disabled = true;
icl_suppress_activation();
add_action( 'admin_notices', 'icl_incomp_plugins_warn' );
function icl_incomp_plugins_warn() {
global $icl_ncp_plugins;
echo '<div class="error"><ul><li><strong>';
esc_html_e( 'WPML cannot be activated together with these older plugins:', 'sitepress' );
echo '<ul style="list-style:disc;margin:20px;">';
foreach ( $icl_ncp_plugins as $incp ) {
echo '<li>' . esc_html( $incp ) . '</li>';
}
echo '</ul>';
esc_html_e( 'WPML will be deactivated', 'sitepress' );
echo '</strong></li></ul></div>';
}
} else {
$icl_sitepress_disabled = false;
}
$filtered_page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
if ( 0 === strcmp( $filtered_page, WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' ) || isset( $pagenow ) && $pagenow == 'index.php' ) {
$icl_ncp_plugins2 = array(
'wp-no-category-base/no-category-base.php',
);
$active_plugins = get_option( 'active_plugins' );
$icl_ncp_plugins2 = array_intersect( $icl_ncp_plugins2, $active_plugins );
if ( ! empty( $icl_ncp_plugins2 ) ) {
if ( 0 === strcmp( $filtered_page, WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' ) ) {
add_action( 'admin_notices', 'icl_incomp_plugins_warn2' );
function icl_incomp_plugins_warn2() {
global $icl_ncp_plugins2;
echo '<a name="icl_inc_plugins_notice"></a><div class="error" style="padding:10px;">';
esc_html_e( 'These plugins are known to have compatibiliy issues with WPML:', 'sitepress' );
echo '<ul style="list-style:disc;margin-left:20px;">';
foreach ( $icl_ncp_plugins2 as $incp ) {
echo '<li>' . esc_html( $incp ) . '</li>';
}
echo '</ul>';
echo '</div>';
}
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* NextGen Gallery plugin integration.
*
* - Filters the_content
* -- Adjusts gallery preview image URL from default to current language (2.0.66 <=)
*/
class WPML_Plugin_Integration_Nexgen_Gallery {
function __construct() {
if ( defined( 'NEXTGEN_GALLERY_PLUGIN_VERSION' ) && version_compare( NEXTGEN_GALLERY_PLUGIN_VERSION, '2.0.66', '<=' ) ) {
add_filter( 'the_content', array( 'WPML_Plugin_Integration_Nexgen_Gallery', 'the_content_gallery_preview_images' ), 1 );
}
}
/**
* Filters post content and fixes gallery preview images URL.
*
* Adjust gallery preview image URL from default to current language.
* Allows NextGen to match and replace preview images with gallery.
* NextGen inserts image previews with default language URL.
*
* @global SitePress $sitepress
* @param string $content
* @return string
*/
public static function the_content_gallery_preview_images( $content ) {
global $sitepress;
if ( $sitepress->get_current_language() != $sitepress->get_default_language() ) {
$default_url = preg_replace(
'/(^http[s]?:\/\/)/',
'',
$sitepress->language_url( $sitepress->get_default_language() )
);
$current_url = preg_replace( '/(^http[s]?:\/\/)/', '', home_url() );
$preview_url = $default_url . 'nextgen-attach_to_post/preview';
$alt_preview_url = $default_url . 'index.php/nextgen-attach_to_post/preview';
if ( preg_match_all( "#<img(.*)http(s)?://({$preview_url}|{$alt_preview_url})/id--(\\d+)(.*)\\/>#mi", $content, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $match ) {
$content = str_replace( $match[0], "<img{$match[1]}http{$match[2]}://{$current_url}nextgen-attach_to_post/preview/id--{$match[4]}\"{$match[5]}/>", $content );
}
}
}
return $content;
}
}
$wpml_ngg = new WPML_Plugin_Integration_Nexgen_Gallery();

View File

@@ -0,0 +1,74 @@
<?php
$action_filter_loader = new WPML_Action_Filter_Loader();
$action_filter_loader->load(
array(
'WPML_Compatibility_Factory',
)
);
add_action( 'plugins_loaded', 'wpml_plugins_integration_setup', 10 );
/**
* Loads compatibility classes for active plugins.
*/
function wpml_plugins_integration_setup() {
global $sitepress, $wpdb;
$factories_to_load = [];
// bbPress integration.
if ( class_exists( 'bbPress' ) ) {
$wpml_bbpress_api = new WPML_BBPress_API();
$wpml_bbpress_filters = new WPML_BBPress_Filters( $wpml_bbpress_api );
$wpml_bbpress_filters->add_hooks();
}
// NextGen Gallery.
if ( defined( 'NEXTGEN_GALLERY_PLUGIN_VERSION' ) ) {
// Todo: do not include files: move to autoloaded classes.
require_once WPML_PLUGIN_PATH . '/inc/plugin-integration-nextgen.php';
}
if ( class_exists( 'GoogleSitemapGeneratorLoader' ) ) {
$wpml_google_sitemap_generator = new WPML_Google_Sitemap_Generator( $wpdb, $sitepress );
$wpml_google_sitemap_generator->init_hooks();
}
if ( class_exists( 'Tiny_Plugin' ) ) {
$factories_to_load[] = 'WPML_Compatibility_Tiny_Compress_Images_Factory';
}
// phpcs:disable WordPress.NamingConventions.ValidVariableName
global $DISQUSVERSION;
if ( $DISQUSVERSION ) {
$factories_to_load[] = 'WPML_Compatibility_Disqus_Factory';
}
// phpcs:enable
if ( defined( 'GOOGLESITEKIT_VERSION' ) ) {
$factories_to_load[] = \WPML\Compatibility\GoogleSiteKit\Hooks::class;
}
$action_filter_loader = new WPML_Action_Filter_Loader();
$action_filter_loader->load( $factories_to_load );
}
add_action( 'after_setup_theme', 'wpml_themes_integration_setup' );
/**
* Loads compatibility classes for active themes.
*/
function wpml_themes_integration_setup() {
$actions = [];
if ( function_exists( 'twentyseventeen_panel_count' ) && ! function_exists( 'twentyseventeen_translate_panel_id' ) ) {
$wpml_twentyseventeen = new WPML_Compatibility_2017();
$wpml_twentyseventeen->init_hooks();
}
$action_filter_loader = new WPML_Action_Filter_Loader();
$action_filter_loader->load( $actions );
}

View File

@@ -0,0 +1,290 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_Admin_Post_Actions
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Admin_Post_Actions extends WPML_Post_Translation {
const DUPLICATE_MEDIA_META_KEY = '_wpml_media_duplicate';
const DUPLICATE_FEATURED_META_KEY = '_wpml_media_featured';
const DUPLICATE_MEDIA_GLOBAL_KEY = 'duplicate_media';
const DUPLICATE_FEATURED_GLOBAL_KEY = 'duplicate_media';
private $http_referer;
public function init() {
parent::init ();
if ( $this->is_setup_complete() ) {
add_action ( 'delete_post', array( $this, 'delete_post_actions' ) );
add_action ( 'wp_trash_post', array( $this, 'trashed_post_actions' ) );
add_action ( 'untrashed_post', array( $this, 'untrashed_post_actions' ) );
}
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
function get_save_post_trid( $post_id, $post_status ) {
$trid = $this->get_element_trid( $post_id );
if ( ! ( $this->is_inner_post_insertion() && $this->is_editing_different_post( $post_id ) ) ) {
$trid = $trid ? $trid : filter_var( isset( $_POST['icl_trid'] ) ? $_POST['icl_trid'] : '', FILTER_SANITIZE_NUMBER_INT );
$trid = $trid ? $trid : filter_var( isset( $_GET['trid'] ) ? $_GET['trid'] : '', FILTER_SANITIZE_NUMBER_INT );
$trid = $trid ? $trid : $this->get_trid_from_referer();
}
$trid = apply_filters( 'wpml_save_post_trid_value', $trid, $post_status );
return $trid;
}
/**
* @param int $post_id
* @param WP_Post $post
*/
public function save_post_actions( $post_id, $post ) {
global $sitepress;
$this->defer_term_counting();
if ( ! $post ) {
$post = get_post( $post_id );
}
// exceptions
$http_referer = $this->get_http_referer();
if ( ! $this->has_save_post_action( $post ) && ! $http_referer->is_rest_request_called_from_post_edit_page() ) {
return;
}
if ( WPML_WordPress_Actions::is_bulk_trash( $post_id ) ||
WPML_WordPress_Actions::is_bulk_untrash( $post_id ) ||
$this->has_invalid_language_details_on_heartbeat()
) {
return;
}
$default_language = $sitepress->get_default_language();
$post_vars = $this->get_post_vars( $post );
if ( isset( $post_vars['action'] ) && $post_vars['action'] === 'post-quickpress-publish' ) {
$language_code = $default_language;
} else {
if( isset( $post_vars['post_ID'] ) ){
$post_id = $post_vars['post_ID'];
}
$language_code = $this->get_save_post_lang( $post_id, $sitepress );
}
if ( $this->is_inline_action( $post_vars ) && ! ( $language_code = $this->get_element_lang_code(
$post_id
) )
) {
return;
}
if ( isset( $post_vars['icl_translation_of'] ) && is_numeric( $post_vars['icl_translation_of'] ) ) {
$translation_of_data_prepared = $this->wpdb->prepare(
"SELECT trid, language_code
FROM {$this->wpdb->prefix}icl_translations
WHERE element_id=%d
AND element_type=%s
LIMIT 1",
$post_vars['icl_translation_of'],
'post_' . $post->post_type
);
list( $trid, $source_language ) = $this->wpdb->get_row( $translation_of_data_prepared, 'ARRAY_N' );
}
if ( isset( $post_vars['icl_translation_of'] ) && $post_vars['icl_translation_of'] == 'none' ) {
$trid = null;
$source_language = $language_code;
} else {
$trid = isset( $trid ) && $trid ? $trid : $this->get_save_post_trid( $post_id, $post->post_status );
// after getting the right trid set the source language from it by referring to the root translation
// of this trid, in case no proper source language has been set yet
$source_language = isset( $source_language )
? $source_language : $this->get_save_post_source_lang( $trid, $language_code, $default_language );
}
if ( isset( $post_vars['icl_tn_note'] ) ) {
update_post_meta( $post_id, '_icl_translator_note', $post_vars['icl_tn_note'] );
}
$this->after_save_post( $trid, $post_vars, $language_code, $source_language );
if ( 'attachment' !== $post->post_type ) {
$this->save_media_options( $post_id, $source_language );
}
}
/**
* @param int $post_id
* @param string|null $source_language
*/
private function save_media_options( $post_id, $source_language ) {
if ( $this->has_post_media_options_metabox() ) {
$original_post_id = isset( $_POST['icl_translation_of'] )
? filter_var( $_POST['icl_translation_of'], FILTER_SANITIZE_NUMBER_INT ) : $post_id;
$duplicate_media = isset( $_POST['wpml_duplicate_media'] )
? filter_var( $_POST['wpml_duplicate_media'], FILTER_SANITIZE_NUMBER_INT ) : false;
$duplicate_featured = isset( $_POST['wpml_duplicate_featured'] )
? filter_var( $_POST['wpml_duplicate_featured'], FILTER_SANITIZE_NUMBER_INT ) : false;
update_post_meta( $original_post_id, self::DUPLICATE_MEDIA_META_KEY, (int) $duplicate_media );
update_post_meta( $original_post_id, self::DUPLICATE_FEATURED_META_KEY, (int) $duplicate_featured );
} else {
$this->sync_media_options_with_original_or_global_settings( $post_id, $source_language );
}
}
private function has_post_media_options_metabox() {
return array_key_exists( WPML_Meta_Boxes_Post_Edit_HTML::FLAG_HAS_MEDIA_OPTIONS, $_POST );
}
/**
* @param int $post_id
* @param string|null $source_language
*/
private function sync_media_options_with_original_or_global_settings( $post_id, $source_language ) {
global $sitepress;
$source_post_id = $sitepress->get_object_id( $post_id, get_post_type( $post_id ), false, $source_language );
$is_translation = $source_post_id && $source_post_id !== $post_id;
foreach (
array(
self::DUPLICATE_FEATURED_META_KEY => self::DUPLICATE_FEATURED_GLOBAL_KEY,
self::DUPLICATE_MEDIA_META_KEY => self::DUPLICATE_MEDIA_GLOBAL_KEY,
) as $meta_key => $global_key
) {
$source_value = get_post_meta( $source_post_id, $meta_key, true );
if ( '' === $source_value ) {
// fallback to global setting
$media_options = get_option( '_wpml_media', array() );
if ( isset( $media_options['new_content_settings'][ $global_key ] ) ) {
$source_value = (int) $media_options['new_content_settings'][ $global_key ];
if ( $source_post_id ) {
update_post_meta( $source_post_id, $meta_key, $source_value );
}
}
}
if ( '' !== $source_value && $is_translation ) {
update_post_meta( $post_id, $meta_key, $source_value );
}
}
}
private function has_invalid_language_details_on_heartbeat() {
if ( ! WPML_WordPress_Actions::is_heartbeat() ) {
return false;
}
if ( isset( $_POST['data']['icl_post_language'], $_POST['data']['icl_trid'] ) ) {
$_POST['icl_post_language'] = Sanitize::string( $_POST['data']['icl_post_language'] );
$_POST['icl_trid'] = filter_var( $_POST['data']['icl_trid'], FILTER_SANITIZE_NUMBER_INT );
return false;
}
return true;
}
/**
* @param integer $post_id
* @param SitePress $sitepress
*
* @return null|string
*/
public function get_save_post_lang( $post_id, $sitepress ) {
$language_code = null;
if ( isset( $_POST['post_ID'] ) && (int) $_POST['post_ID'] === (int) $post_id ) {
$language_code = filter_var(
( isset( $_POST['icl_post_language'] ) ? $_POST['icl_post_language'] : '' ),
FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
$language_code = $language_code
? $language_code
: filter_input(
INPUT_GET,
'lang',
FILTER_SANITIZE_FULL_SPECIAL_CHARS
);
return $language_code ? $language_code : parent::get_save_post_lang( $post_id, $sitepress );
}
/**
* @param array $post_vars
* @return bool
*/
private function is_inline_action( $post_vars ) {
return isset( $post_vars[ 'action' ] )
&& $post_vars[ 'action' ] == 'inline-save'
|| isset( $_GET[ 'bulk_edit' ] )
|| isset( $_GET[ 'doing_wp_cron' ] )
|| ( isset( $_GET[ 'action' ] )
&& $_GET[ 'action' ] == 'untrash' );
}
/**
* @param int $trid
* @param string $language_code
* @param string $default_language
*
* @return null|string
*/
protected function get_save_post_source_lang( $trid, $language_code, $default_language ) {
$source_language = filter_input ( INPUT_GET, 'source_lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$source_language = $source_language ? $source_language : $this->get_source_language_from_referer();
$source_language = $source_language ? $source_language : SitePress::get_source_language_by_trid ( $trid );
$source_language = $source_language === 'all' ? $default_language : $source_language;
$source_language = $source_language !== $language_code ? $source_language : null;
return $source_language;
}
/**
* Gets the source_language $_GET parameter from the HTTP_REFERER
*
* @return string|bool
*/
private function get_source_language_from_referer() {
if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {
return false;
}
$referer = $_SERVER['HTTP_REFERER'];
$query = (string) wpml_parse_url( $referer, PHP_URL_QUERY );
parse_str( $query, $query_parts );
return isset( $query_parts['source_lang'] ) ? $query_parts['source_lang'] : false;
}
public function get_trid_from_referer() {
$http_referer = $this->get_http_referer();
return $http_referer->get_trid();
}
protected function get_http_referer() {
if ( ! $this->http_referer ) {
$factory = new WPML_URL_HTTP_Referer_Factory();
$this->http_referer = $factory->create();
}
return $this->http_referer;
}
}

View File

@@ -0,0 +1,103 @@
<?php
class WPML_Comment_Duplication{
public function move_to_original($duplicate_of, $post_duplicates, $comment){
global $wpml_post_translations, $wpdb;
$_orig_lang = $wpml_post_translations->get_element_lang_code ( $duplicate_of );
$post_duplicates[ $_orig_lang ] = $duplicate_of;
$original_parent = get_comment_meta ( $comment[ 'comment_parent' ], '_icl_duplicate_of', true );
$wpdb->update (
$wpdb->comments,
array(
'comment_post_ID' => $duplicate_of,
'comment_parent' => $original_parent
), array( 'comment_ID' => $comment['comment_ID'] ), array( '%d', '%d' ), array( '%d' )
);
wp_update_comment_count_now($duplicate_of);
}
public function get_correct_parent($comment, $dup_id){
global $wpdb;
$translated_parent = $wpdb->get_var (
$wpdb->prepare (
" SELECT cmb.comment_id
FROM {$wpdb->commentmeta} cm
JOIN {$wpdb->commentmeta} cmb
ON ( cmb.meta_value = cm.meta_value
AND cmb.meta_key = cm.meta_key)
OR cm.comment_id = cmb.meta_value
JOIN {$wpdb->comments} c
ON c.comment_ID = cmb.comment_id
WHERE cm.meta_key = '_icl_duplicate_of'
AND ( cm.comment_id = %d OR cm.meta_value = %d )
AND c.comment_post_ID = %d",
$comment[ 'comment_parent' ],
$comment[ 'comment_parent' ],
$dup_id
)
);
return $translated_parent;
}
public function insert_duplicated_comment( $comment, $dup_id, $original_cid ) {
global $wpdb, $iclTranslationManagement;
$dup_comment_id = $this->duplicate_exists ( $dup_id, $original_cid );
remove_action ( 'wp_insert_comment', array( $iclTranslationManagement, 'duplication_insert_comment' ), 100 );
if ( $dup_comment_id ) {
$comment[ 'comment_ID' ] = $dup_comment_id;
wp_update_comment ( $comment );
} else {
$wpdb->insert ( $wpdb->comments, $comment );
$dup_comment_id = $wpdb->insert_id;
add_action ( 'wp_insert_comment', array( $iclTranslationManagement, 'duplication_insert_comment' ), 100 );
update_comment_meta ( $dup_comment_id, '_icl_duplicate_of', $original_cid );
// comment meta
$meta = $wpdb->get_results (
$wpdb->prepare (
"SELECT meta_key, meta_value FROM {$wpdb->commentmeta} WHERE comment_id=%d",
$original_cid
)
);
foreach ( $meta as $meta_row ) {
$wpdb->insert (
$wpdb->commentmeta,
array(
'comment_id' => $dup_comment_id,
'meta_key' => $meta_row->meta_key,
'meta_value' => $meta_row->meta_value
), array( '%d', '%s', '%s' )
);
}
}
wp_update_comment_count_now ( $dup_id );
}
private function duplicate_exists( $dup_id, $original_cid ) {
global $wpdb;
$duplicate = $wpdb->get_var (
$wpdb->prepare (
" SELECT comm.comment_ID
FROM {$wpdb->comments} comm
JOIN {$wpdb->commentmeta} cm
ON comm.comment_ID = cm.comment_id
WHERE comm.comment_post_ID = %d
AND cm.meta_key = '_icl_duplicate_of'
AND cm.meta_value = %d
LIMIT 1",
$dup_id,
$original_cid
)
);
return $duplicate;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Class WPML_Create_Post_Helper
*
* @since 3.2
*/
class WPML_Create_Post_Helper {
/** @var SitePress $sitepress */
private $sitepress;
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
/**
* @param array $postarr will be escaped inside the method
* @param string|null $lang
* @param bool $wp_error
*
* @return int|WP_Error
*/
public function insert_post( array $postarr, $lang = null, $wp_error = false ) {
$current_language = null;
$postarr = $this->slash_and_preserve_tag_ids( $postarr );
if ( $lang ) {
$current_language = $this->sitepress->get_current_language();
$this->sitepress->switch_lang( $lang, false );
}
if ( isset( $postarr['ID'] ) ) {
$new_post_id = wp_update_post( $postarr, $wp_error );
} else {
add_filter( 'wp_insert_post_empty_content', array( $this, 'allow_empty_post' ), 10, 0 );
$new_post_id = wp_insert_post( $postarr, $wp_error );
remove_filter( 'wp_insert_post_empty_content', array( $this, 'allow_empty_post' ) );
}
if ( $current_language ) {
$this->sitepress->switch_lang( $current_language, false );
}
return $new_post_id;
}
public function allow_empty_post() {
return false; // We need to return false to indicate that the post is not empty
}
/**
* We need to make sure that tag IDs are not casted into strings.
* This is a side effect of https://core.trac.wordpress.org/ticket/45121
* (wp_update_post() can modify post tag) for which we have
* a temporary fix in `\WPML_Page_Builders_Media_Shortcodes_Update::translate`.
*
* @param array $postarr
*
* @return array
*/
private function slash_and_preserve_tag_ids( array $postarr ) {
if ( array_key_exists( 'tags_input', $postarr ) ) {
$tagIds = array_filter( $postarr['tags_input'], 'is_int' );
$postarr = wp_slash( $postarr );
$postarr['tags_input'] = array_merge( $tagIds, $this->parse_tag( $postarr['tags_input'] ) );
} else {
$postarr = wp_slash( $postarr );
}
return $postarr;
}
private function parse_tag( $tags ) {
if ( empty( $tags ) ) {
$tags = array();
}
if ( ! is_array( $tags ) ) {
$comma = _x( ',', 'tag delimiter' );
if ( ',' !== $comma ) {
$tags = str_replace( $comma, ',', $tags );
}
$tags = explode( ',', trim( $tags, " \n\t\r\0\x0B," ) );
}
return $tags;
}
}

View File

@@ -0,0 +1,69 @@
<?php
use WPML\LIB\WP\Hooks;
class WPML_Frontend_Post_Actions extends WPML_Post_Translation {
public function init() {
parent::init ();
if ( $this->is_setup_complete() ) {
/**
* In theory, we could add the 'delete_post` action for all frontend requests
* We'll limit it to REST to avoid any unexpected problems
*/
Hooks::onAction( 'rest_api_init' )
->then( function () {
add_action( 'delete_post', [ $this, 'delete_post_actions' ] );
} );
}
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
function get_save_post_trid( $post_id, $post_status ) {
return $this->get_element_trid( $post_id );
}
/**
* @param int $pidd
* @param WP_Post $post
*
* @return void
*/
public function save_post_actions( $pidd, $post ) {
global $sitepress;
$this->defer_term_counting();
if ( ! $post ) {
$post = get_post( $pidd );
}
$http_referer = new WPML_URL_HTTP_Referer( new WPML_Rest( new WP_Http() ) );
// exceptions
if ( ! $this->has_save_post_action( $post ) || $http_referer->is_rest_request_called_from_post_edit_page() ) {
return;
}
$default_language = $sitepress->get_default_language();
$post_vars = $this->get_post_vars( $post );
$post_id = isset( $post_vars['post_ID'] ) ? $post_vars['post_ID']
: $pidd; //latter case for XML-RPC publishing
$language_code = $this->get_save_post_lang( $post_id, $sitepress );
$trid = $this->get_save_post_trid( $post_id, $post->post_status );
// after getting the right trid set the source language from it by referring to the root translation
// of this trid, in case no proper source language has been set yet
$source_language = $this->get_save_post_source_lang( $trid, $language_code, $default_language );
$this->after_save_post( $trid, $post_vars, $language_code, $source_language );
}
protected function get_save_post_source_lang( $trid, $language_code, $default_language ) {
$post_id = $this->get_element_id ( $trid, $language_code );
return $post_id ? $this->get_source_lang_code ( $post_id ) : null;
}
}

View File

@@ -0,0 +1,383 @@
<?php
require_once dirname( __FILE__ ) . '/wpml-wordpress-actions.class.php';
/**
* Class WPML_Post_Duplication
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Post_Duplication extends WPML_WPDB_And_SP_User {
function get_duplicates( $master_post_id ) {
global $wpml_post_translations;
$duplicates = array();
$post_ids_query = " SELECT post_id
FROM {$this->wpdb->postmeta}
WHERE meta_key='_icl_lang_duplicate_of'
AND meta_value = %d
AND post_id <> %d";
$post_ids_prepare = $this->wpdb->prepare( $post_ids_query, array( $master_post_id, $master_post_id ) );
$post_ids = $this->wpdb->get_col( $post_ids_prepare );
foreach ( $post_ids as $post_id ) {
$language_code = $wpml_post_translations->get_element_lang_code( $post_id );
if ( ! $language_code ) {
continue;
}
$duplicates[ $language_code ] = $post_id;
}
return $duplicates;
}
function make_duplicate( $master_post_id, $lang ) {
global $wpml_post_translations;
/**
* @deprecated Use 'wpml_before_make_duplicate' instead
* @since 3.4
*/
do_action( 'icl_before_make_duplicate', $master_post_id, $lang );
do_action( 'wpml_before_make_duplicate', $master_post_id, $lang );
$master_post = get_post( $master_post_id );
if ( ! $master_post || $this->is_external( $master_post_id ) ) {
return true;
}
$is_duplicated = false;
$translations = $wpml_post_translations->get_element_translations( $master_post_id, false, false );
if ( isset( $translations[ $lang ] ) ) {
$post_array[ 'ID' ] = $translations[ $lang ];
if ( WPML_WordPress_Actions::is_bulk_trash( $post_array[ 'ID' ] ) || WPML_WordPress_Actions::is_bulk_untrash( $post_array[ 'ID' ] ) ) {
return true;
}
$is_duplicated = get_post_meta( $translations[ $lang ], '_icl_lang_duplicate_of', true );
}
$post_array['post_author'] = $master_post->post_author;
$post_array['post_date'] = $master_post->post_date;
$post_array['post_date_gmt'] = $master_post->post_date_gmt;
$duplicated_post_content = $this->duplicate_post_content( $lang, $master_post );
$post_array['post_content'] = $duplicated_post_content;
$duplicated_post_title = $this->duplicate_post_title( $lang, $master_post );
$post_array['post_title'] = $duplicated_post_title;
$duplicated_post_excerpt = $this->duplicate_post_excerpt( $lang, $master_post );
$post_array['post_excerpt'] = $duplicated_post_excerpt;
if ( $this->sitepress->get_setting('sync_post_status' ) ) {
$sync_post_status = true;
} else {
$sync_post_status = ( ! isset( $post_array[ 'ID' ] )
|| ( $this->sitepress->get_setting( 'sync_delete' ) && $master_post->post_status === 'trash' ) || $is_duplicated );
}
if ( $sync_post_status || ( isset( $post_array[ 'ID' ] ) && get_post_status( $post_array[ 'ID' ] ) === 'auto-draft' ) ) {
$post_array[ 'post_status' ] = $master_post->post_status;
}
$post_array[ 'comment_status' ] = $master_post->comment_status;
$post_array[ 'ping_status' ] = $master_post->ping_status;
$post_array[ 'post_name' ] = $master_post->post_name;
if ( $master_post->post_parent ) {
$parent = $this->sitepress->get_object_id( $master_post->post_parent, $master_post->post_type, false, $lang );
$post_array[ 'post_parent' ] = $parent;
}
$post_array['menu_order'] = $master_post->menu_order;
$post_array['post_type'] = $master_post->post_type;
$post_array['post_mime_type'] = $master_post->post_mime_type;
$trid = $this->sitepress->get_element_trid( $master_post->ID,
'post_' . $master_post->post_type );
$id = $this->save_duplicate( $post_array, $lang );
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear();
global $ICL_Pro_Translation;
/** @var WPML_Pro_Translation $ICL_Pro_Translation */
if ( $ICL_Pro_Translation ) {
$ICL_Pro_Translation->fix_links_to_translated_content( $id, $lang );
}
if ( ! is_wp_error( $id ) ) {
$ret = $this->run_wpml_actions( $master_post, $trid, $lang, $id, $post_array );
} else {
throw new Exception( $id->get_error_message() );
}
return $ret;
}
/**
* @param int $element_id
*
* @return null|string
*/
private function is_external( $element_id ) {
$query = "SELECT element_type FROM {$this->wpdb->prefix}icl_translations WHERE element_id=%d AND element_type LIKE %s LIMIT 1";
return ! $this->wpdb->get_var( $this->wpdb->prepare( $query, $element_id, 'post_%' ) );
}
private function run_wpml_actions( $master_post, $trid, $lang, $id, $post_array ) {
$master_post_id = $master_post->ID;
$this->sitepress->set_element_language_details( $id, 'post_' . $master_post->post_type, $trid, $lang );
$this->sync_duplicate_password( $master_post_id, $id );
$this->sync_page_template( $master_post_id, $id );
$this->duplicate_fix_children( $master_post_id, $lang );
// make sure post name is copied
$this->wpdb->update( $this->wpdb->posts, array( 'post_name' => $master_post->post_name ), array( 'ID' => $id ) );
if ( $this->sitepress->get_setting( 'sync_post_taxonomies', false ) ) {
$this->duplicate_taxonomies( $master_post_id, $lang );
}
$this->duplicate_custom_fields( $master_post_id, $lang );
update_post_meta( $id, '_icl_lang_duplicate_of', $master_post->ID );
// Duplicate post format after the taxonomies because post format is stored
// as a taxonomy by WP.
if ( $this->sitepress->get_setting( 'sync_post_format' ) ) {
$_wp_post_format = get_post_format( $master_post_id );
set_post_format( $id, $_wp_post_format );
}
if ( $this->sitepress->get_setting( 'sync_comments_on_duplicates' ) ) {
$this->duplicate_comments( $master_post_id, $id );
}
$status_helper = wpml_get_post_status_helper();
$status_helper->set_status( $id, ICL_TM_DUPLICATE );
$status_helper->set_update_status( $id, false );
do_action( 'icl_make_duplicate', $master_post_id, $lang, $post_array, $id );
clean_post_cache( $id );
return $id;
}
private function sync_page_template( $master_post_id, $duplicate_post_id ) {
$_wp_page_template = get_post_meta( $master_post_id, '_wp_page_template', true );
if ( ! empty( $_wp_page_template ) ) {
update_post_meta( $duplicate_post_id, '_wp_page_template', $_wp_page_template );
}
}
private function duplicate_comments( $master_post_id, $translated_id ) {
global $sitepress;
remove_filter( 'comments_clauses', array( $sitepress, 'comments_clauses' ), 10 );
$comments_on_master = get_comments( array( 'post_id' => $master_post_id ) );
$comments_on_translation = get_comments( array( 'post_id' => $translated_id, 'status' => 'any' ) );
add_filter( 'comments_clauses', array( $sitepress, 'comments_clauses' ), 10, 2 );
foreach ( $comments_on_translation as $comment ) {
wp_delete_comment( $comment->comment_ID, true );
clean_comment_cache( $comment->comment_ID );
}
$iclTranslationManagement = wpml_load_core_tm();
foreach ( $comments_on_master as $comment ) {
$iclTranslationManagement->duplication_insert_comment( $comment->comment_ID );
clean_comment_cache( $comment->comment_ID );
}
wp_update_comment_count_now( $master_post_id );
wp_update_comment_count_now( $translated_id );
}
/**
* @param array $post_array
* @param string $lang
*
* @return int|WP_Error
*/
private function save_duplicate( array $post_array, $lang ) {
return wpml_get_create_post_helper()->insert_post( $post_array, $lang, true );
}
private function duplicate_fix_children( $master_post_id, $lang ) {
$post_type = $this->wpdb->get_var(
$this->wpdb->prepare( "SELECT post_type FROM {$this->wpdb->posts} WHERE ID=%d", $master_post_id )
);
$master_children = $this->wpdb->get_col(
$this->wpdb->prepare(
"SELECT ID FROM {$this->wpdb->posts} WHERE post_parent=%d AND post_type != 'revision'",
$master_post_id
)
);
$dup_parent = icl_object_id( $master_post_id, $post_type, false, $lang );
if ( $master_children ) {
foreach ( $master_children as $master_child ) {
$dup_child = icl_object_id( $master_child, $post_type, false, $lang );
if ( $dup_child ) {
$this->wpdb->update( $this->wpdb->posts, array( 'post_parent' => $dup_parent ), array( 'ID' => $dup_child ) );
}
$this->duplicate_fix_children( $master_child, $lang );
}
}
}
private function duplicate_taxonomies( $master_post_id, $lang ) {
$post_type = get_post_field( 'post_type', $master_post_id );
$taxonomies = get_object_taxonomies( $post_type );
$trid = $this->sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
if ( $trid ) {
$translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post_type, false, false, true );
if ( isset( $translations[ $lang ] ) ) {
$duplicate_post_id = $translations[ $lang ]->element_id;
/* If we have an existing post, we first of all remove all terms currently attached to it.
* The main reason behind is the removal of the potentially present default category on the post.
*/
wp_delete_object_term_relationships( $duplicate_post_id, $taxonomies );
} else {
return false; // translation not found!
}
}
$term_helper = wpml_get_term_translation_util();
$term_helper->duplicate_terms( $master_post_id, $lang );
return true;
}
private function sync_duplicate_password( $master_post_id, $duplicate_post_id ) {
if ( post_password_required( $master_post_id ) ) {
$sql = $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} AS dupl,
(SELECT org.post_password FROM {$this->wpdb->posts} AS org WHERE ID = %d ) AS pwd
SET dupl.post_password = pwd.post_password
WHERE dupl.ID = %d",
array( $master_post_id, $duplicate_post_id ) );
$this->wpdb->query( $sql );
}
}
private function duplicate_custom_fields( $master_post_id, $lang ) {
$duplicate_post_id = false;
$post_type = get_post_field( 'post_type', $master_post_id );
$trid = $this->sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
if ( $trid ) {
$translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post_type );
if ( isset( $translations[ $lang ] ) ) {
$duplicate_post_id = $translations[ $lang ]->element_id;
} else {
return false; // translation not found!
}
}
$default_exceptions = WPML_Config::get_custom_fields_translation_settings();
$exceptions = apply_filters( 'wpml_duplicate_custom_fields_exceptions', array() );
$exceptions = array_merge( $exceptions, $default_exceptions );
$exceptions = array_unique( $exceptions );
$exceptions_in = ! empty( $exceptions )
? 'AND meta_key NOT IN ( ' . wpml_prepare_in( $exceptions ) . ') ' : '';
$from_where_string = "FROM {$this->wpdb->postmeta} WHERE post_id = %d " . $exceptions_in;
$post_meta_master = $this->wpdb->get_results( "SELECT meta_key, meta_value " . $this->wpdb->prepare( $from_where_string,
$master_post_id ) );
$this->wpdb->query( "DELETE " . $this->wpdb->prepare( $from_where_string, $duplicate_post_id ) );
$values = [];
foreach ( $post_meta_master as $post_meta ) {
$is_serialized = is_serialized( $post_meta->meta_value );
$meta_data = array(
'context' => 'custom_field',
'attribute' => 'value',
'key' => $post_meta->meta_key,
'is_serialized' => $is_serialized,
'post_id' => $duplicate_post_id,
'master_post_id' => $master_post_id,
);
/**
* @deprecated use 'wpml_duplicate_generic_string' instead, with the same arguments
*/
$icl_duplicate_generic_string = apply_filters( 'icl_duplicate_generic_string',
$post_meta->meta_value,
$lang,
$meta_data );
$post_meta->meta_value = $icl_duplicate_generic_string;
$wpml_duplicate_generic_string = apply_filters( 'wpml_duplicate_generic_string',
$post_meta->meta_value,
$lang,
$meta_data );
$post_meta->meta_value = $wpml_duplicate_generic_string;
if ( ! is_serialized( $post_meta->meta_value ) ) {
$post_meta->meta_value = maybe_serialize( $post_meta->meta_value );
}
$values[] = $this->wpdb->prepare(
'( %d, %s, %s )',
$duplicate_post_id,
$post_meta->meta_key,
$post_meta->meta_value
);
}
if ( ! empty( $values ) ) {
$values = implode( ', ', $values );
$this->wpdb->query(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"INSERT INTO `{$this->wpdb->postmeta}` (`post_id`, `meta_key`, `meta_value`) VALUES {$values}"
);
}
return true;
}
/**
* @param string $lang
* @param \WP_Post $master_post
*
* @return array<string,mixed>
*/
private function duplicate_post_content( $lang, $master_post ) {
$duplicated_post_content_meta = array(
'context' => 'post',
'attribute' => 'content',
'key' => $master_post->ID
);
$duplicated_post_content = $master_post->post_content;
$duplicated_post_content = apply_filters( 'icl_duplicate_generic_string', $duplicated_post_content, $lang, $duplicated_post_content_meta );
$duplicated_post_content = apply_filters( 'wpml_duplicate_generic_string', $duplicated_post_content, $lang, $duplicated_post_content_meta );
return $duplicated_post_content;
}
/**
* @param string $lang
* @param \WP_Post $master_post
*
* @return mixed
*/
private function duplicate_post_title( $lang, $master_post ) {
$duplicated_post_title_meta = array(
'context' => 'post',
'attribute' => 'title',
'key' => $master_post->ID
);
$duplicated_post_title = $master_post->post_title;
$duplicated_post_title = apply_filters( 'icl_duplicate_generic_string', $duplicated_post_title, $lang, $duplicated_post_title_meta );
$duplicated_post_title = apply_filters( 'wpml_duplicate_generic_string', $duplicated_post_title, $lang, $duplicated_post_title_meta );
return $duplicated_post_title;
}
/**
* @param string $lang
* @param WP_Post $master_post
*
* @return mixed
*/
private function duplicate_post_excerpt( $lang, $master_post ) {
$duplicated_post_excerpt_meta = array(
'context' => 'post',
'attribute' => 'excerpt',
'key' => $master_post->ID
);
$duplicated_post_excerpt = $master_post->post_excerpt;
$duplicated_post_excerpt = apply_filters( 'icl_duplicate_generic_string',
$duplicated_post_excerpt,
$lang,
$duplicated_post_excerpt_meta );
$duplicated_post_excerpt = apply_filters( 'wpml_duplicate_generic_string',
$duplicated_post_excerpt,
$lang,
$duplicated_post_excerpt_meta );
return $duplicated_post_excerpt;
}
}

View File

@@ -0,0 +1,27 @@
<?php
class WPML_Post_Hierarchy_Sync extends WPML_Hierarchy_Sync {
protected $element_id_column = 'ID';
protected $parent_element_id_column = 'ID';
protected $parent_id_column = 'post_parent';
protected $element_type_column = 'post_type';
protected $element_type_prefix = 'post_';
/**
* @param wpdb $wpdb
*/
public function __construct( &$wpdb ) {
parent::__construct( $wpdb );
$this->elements_table = $wpdb->posts;
}
/**
* @param string $element_type
*
* @return bool
*/
public function is_hierarchical( $element_type ) {
return is_post_type_hierarchical( $element_type );
}
}

View File

@@ -0,0 +1,364 @@
<?php
use WPML\FP\Lst;
use WPML\FP\Maybe;
use WPML\FP\Obj;
use function WPML\FP\partial;
/**
* Class WPML_Post_Synchronization
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Post_Synchronization extends WPML_SP_And_PT_User {
/** @var bool[] */
private $sync_parent_cpt = array();
/** @var bool $sync_parent */
private $sync_parent;
/** @var bool $sync_delete */
private $sync_delete;
/** @var bool $sync_ping_status */
private $sync_ping_status;
/** @var bool $sync_post_date */
private $sync_post_date;
/** @var bool $sync_post_format */
private $sync_post_format;
/** @var bool $sync_comment_status */
private $sync_comment_status;
/** @var bool $sync_page_template */
private $sync_page_template;
/** @var bool $sync_menu_order */
private $sync_menu_order;
/** @var bool $sync_password */
private $sync_password;
/** @var bool $sync_private_flag */
private $sync_private_flag;
/** @var bool $is_deleting_all_translations */
private $is_deleting_all_translations = false;
/** @var array $deleted_post_types */
private $deleted_post_types = array();
/**
* @var int
*/
private $sync_document_status;
/**
* @param array $settings
* @param WPML_Post_Translation $post_translations
* @param SitePress $sitepress
*/
public function __construct( &$settings, &$post_translations, &$sitepress ) {
parent::__construct( $post_translations, $sitepress );
$this->sync_delete = isset( $settings[ 'sync_delete' ] ) ? $settings[ 'sync_delete' ] : false;
$this->sync_parent = isset( $settings[ 'sync_page_parent' ] ) ? $settings[ 'sync_page_parent' ] : false;
$this->sync_ping_status = isset( $settings[ 'sync_ping_status' ] ) ? $settings[ 'sync_ping_status' ] : false;
$this->sync_post_date = isset( $settings[ 'sync_post_date' ] ) ? $settings[ 'sync_post_date' ] : false;
$this->sync_post_format = isset( $settings[ 'sync_post_format' ] ) ? $settings[ 'sync_post_format' ] : false;
$this->sync_comment_status = isset( $settings[ 'sync_comment_status' ] ) ? $settings[ 'sync_comment_status' ] : false;
$this->sync_page_template = isset( $settings[ 'sync_page_template' ] ) ? $settings[ 'sync_page_template' ] : false;
$this->sync_password = isset( $settings[ 'sync_password' ] ) ? $settings[ 'sync_password' ] : false;
$this->sync_private_flag = isset( $settings[ 'sync_private_flag' ] ) ? $settings[ 'sync_private_flag' ] : false;
$this->sync_document_status = isset( $settings[ 'translated_document_status' ] ) ? $settings[ 'translated_document_status' ] : 1;
$this->sync_menu_order = isset( $settings[ 'sync_page_ordering' ] ) ? $settings[ 'sync_page_ordering' ] : array();
}
private function must_sync_parents( $post_type ) {
if ( ! array_key_exists( $post_type, $this->sync_parent_cpt ) ) {
$this->sync_parent_cpt[ $post_type ] = apply_filters( 'wpml_sync_parent_for_post_type', $this->sync_parent, $post_type );
}
return $this->sync_parent_cpt[ $post_type ];
}
/**
* Fixes parents of translations for hierarchical post types
*
* User changed parent for a post in $post_type and we are setting proper parent for $translation_id in
* $language_code_translated language
*
* @param string $post_type - post_type that should have the translated parents fixed
*/
private function maybe_fix_translated_parent( $post_type ) {
if ( $this->must_sync_parents( $post_type ) ) {
$sync_helper = wpml_get_hierarchy_sync_helper();
$sync_helper->sync_element_hierarchy( $post_type );
}
}
public function sync_with_duplicates( $post_id ) {
$duplicates = $this->sitepress->get_duplicates( $post_id );
foreach ( array_keys( $duplicates ) as $lang_code ) {
$this->sitepress->make_duplicate( $post_id, $lang_code );
}
}
/**
* @param int $post_id
* @param bool $keep_db_entries
*/
public function delete_post_actions( $post_id, $keep_db_entries = false ) {
$post_type = get_post_type( $post_id );
$post_type_exceptions = array( 'nav_menu_item' );
if ( in_array( $post_type, $post_type_exceptions ) ) {
return;
}
$trid = null;
if ( ! $this->is_deleting_all_translations ) {
$this->is_deleting_all_translations = ! $this->post_translation->get_original_element( $post_id, true );
$trid = $this->post_translation->get_element_trid( $post_id );
$translated_ids = $this->get_translations_without_source( $post_id, $trid );
if ( $this->sync_delete || Lst::includes( $post_type, [ 'wp_template', 'wp_template_part' ] ) ) {
$this->delete_translations( $translated_ids, $keep_db_entries );
}
$this->is_deleting_all_translations = false;
}
if ( ! $keep_db_entries ) {
$this->post_translation->delete_post_translation_entry( $post_id );
if ( $trid && ! $this->is_deleting_all_translations ) {
$lang_code = $this->post_translation->get_element_lang_code( $post_id );
$this->set_new_original( $trid, $lang_code );
}
}
if ( ! $this->is_deleting_all_translations ) {
$this->run_final_actions_for_delete_post( $post_type );
}
}
/**
* @param int $post_id
* @param int $trid
*
* @return array
*/
private function get_translations_without_source( $post_id, $trid ) {
$actual_translations_only = ! $this->is_deleting_all_translations;
$translated_ids = $this->post_translation->get_element_translations( $post_id, $trid, $actual_translations_only );
unset( $translated_ids[ array_search( $post_id, $translated_ids ) ] );
return $translated_ids;
}
private function is_bulk_delete() {
return ( isset( $_REQUEST['action'] ) && 'delete' === $_REQUEST['action']
|| isset( $_REQUEST['action2'] ) && 'delete' === $_REQUEST['action2']
) && ( isset( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] )
|| isset( $_REQUEST['media'] ) && is_array( $_REQUEST['media'] )
);
}
/** @param string $post_type */
private function reset_cache( $post_type ) {
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear( $post_type . 's_per_language', true );
}
/** @param string $post_type */
private function defer_delete_actions( $post_type ) {
if ( ! in_array( $post_type, $this->deleted_post_types, true ) ) {
$this->deleted_post_types[] = $post_type;
if ( ! has_action( 'shutdown', array( $this, 'shutdown_action' ) ) ) {
add_action( 'shutdown', array( $this, 'shutdown_action' ) );
}
}
}
public function shutdown_action() {
$this->post_translation->reload();
foreach ( $this->deleted_post_types as $post_type ) {
$this->reset_cache( $post_type );
$this->maybe_fix_translated_parent( $post_type );
}
}
/**
* @param array $translated_ids
* @param bool $keep_db_entries
*/
private function delete_translations( array $translated_ids, $keep_db_entries ) {
if ( ! empty( $translated_ids ) ) {
foreach ( $translated_ids as $trans_id ) {
if ( ! $this->is_bulk_prevented( $trans_id ) ) {
if ( $keep_db_entries ) {
$this->post_translation->trash_translation( $trans_id );
} else {
wp_delete_post( $trans_id, true );
}
}
}
}
}
/** @param string $post_type */
private function run_final_actions_for_delete_post( $post_type ) {
if ( $this->is_bulk_delete() ) {
$this->defer_delete_actions( $post_type );
} else {
$this->post_translation->reload();
$this->reset_cache( $post_type );
$this->maybe_fix_translated_parent( $post_type );
}
}
private function is_bulk_prevented( $post_id ) {
return ( isset( $_GET[ 'delete_all' ] ) && $_GET[ 'delete_all' ] === 'Empty Trash' )
|| in_array( $post_id, ( isset( $_GET[ 'ids' ] ) ? $_GET[ 'ids' ] : array() ) );
}
function untrashed_post_actions( $post_id ) {
if ( $this->sync_delete ) {
$translations = $this->post_translation->get_element_translations( $post_id, false, true );
foreach ( $translations as $t_id ) {
$this->post_translation->untrash_translation( $t_id );
}
}
$post_type = get_post_type( $post_id );
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear( $post_type . 's_per_language', true );
}
public function sync_with_translations( $post_id, $post_vars = false ) {
global $wpdb;
$wp_api = $this->sitepress->get_wp_api();
$term_count_update = new WPML_Update_Term_Count( $wp_api );
$post = get_post ( $post_id );
$source_post_status = $this->get_post_status( $post_id );
$translated_ids = $this->post_translation->get_element_translations( $post_id, false, true );
$post_format = $this->sync_post_format ? get_post_format( $post_id ) : null;
$ping_status = $this->sync_ping_status ? ( pings_open( $post_id ) ? 'open' : 'closed' ) : null;
$comment_status = $this->sync_comment_status ? ( comments_open( $post_id ) ? 'open' : 'closed' ) : null;
$post_password = $this->sync_password ? $post->post_password : null;
$menu_order = $this->sync_menu_order && ! empty( $post->menu_order ) ? $post->menu_order : null;
$page_template = $this->sync_page_template && get_post_type( $post_id ) === 'page' ? get_post_meta( $post_id, '_wp_page_template', true ) : null;
$post_date = $this->sync_post_date ? $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM {$wpdb->posts} WHERE ID=%d LIMIT 1", $post_id ) ) : null;
foreach ( $translated_ids as $lang_code => $translated_pid ) {
$post_status = $this->get_post_status( $translated_pid );
$post_status_differs = ( 'private' === $source_post_status && 'publish' === $post_status )
|| ( 'publish' === $source_post_status && 'private' === $post_status );
if ( $this->sync_private_flag && $post_status_differs ) {
$post_status = $source_post_status;
}
$this->sync_custom_fields ( $post_id, $translated_pid );
if ( $post_format ) {
set_post_format ( $translated_pid, $post_format );
}
if ( $post_date !== null ) {
$post_date_gmt = get_gmt_from_date ( $post_date );
$data = array( 'post_date' => $post_date, 'post_date_gmt' => $post_date_gmt );
$now = gmdate('Y-m-d H:i:59');
$allow_post_statuses = array( 'private', 'pending', 'draft' );
if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) {
if ( ! in_array( $post_status, $allow_post_statuses, true ) ) {
$post_status = 'future';
}
}
$data[ 'post_status' ] = $post_status;
$wpdb->update ( $wpdb->posts, $data, array( 'ID' => $translated_pid ) );
wp_schedule_single_event( strtotime( $post_date_gmt . '+1 second' ), 'publish_future_post', array( $translated_pid ) );
}
if ( $post_password !== null ) {
$wpdb->update ( $wpdb->posts, array( 'post_password' => $post_password ), array( 'ID' => $translated_pid ) );
}
if ( $post_status !== null && ! in_array( $this->get_post_status( $translated_pid ), array( 'auto-draft', 'draft', 'inherit', 'trash' ) ) ) {
$wpdb->update ( $wpdb->posts, array( 'post_status' => $post_status ), array( 'ID' => $translated_pid ) );
$term_count_update->update_for_post( $translated_pid );
} elseif ( $post_status == null && $this->sync_private_flag && $this->get_post_status( $translated_pid ) === 'private' ) {
$wpdb->update ( $wpdb->posts, array( 'post_status' => $this->get_post_status( $post_id ) ), array( 'ID' => $translated_pid ) );
$term_count_update->update_for_post( $translated_pid );
}
if ( $ping_status !== null ) {
$wpdb->update ( $wpdb->posts, array( 'ping_status' => $ping_status ), array( 'ID' => $translated_pid ) );
}
if ( $comment_status !== null ) {
$wpdb->update ( $wpdb->posts, array( 'comment_status' => $comment_status ), array( 'ID' => $translated_pid ) );
}
if ( $page_template !== null ) {
update_post_meta ( $translated_pid, '_wp_page_template', $page_template );
}
$this->sync_with_translations ( $translated_pid );
}
$this->maybe_fix_translated_parent( get_post_type( $post_id ) );
if ( $menu_order !== null && (bool) $translated_ids !== false ) {
$query = $wpdb->prepare(
"UPDATE {$wpdb->posts}
SET menu_order=%s
WHERE ID IN (" . wpml_prepare_in( $translated_ids, '%d' ) . ')',
$menu_order
);
$wpdb->query( $query );
}
}
/**
* The function `get_post_status` does not return the raw status for attachments.
* As we are running direct DB updates here, we need the actual DB value.
*
* @param int $post_id
*
* @return string|false
*/
private function get_post_status( $post_id ) {
$isAttachment = function( $post_id ) { return 'attachment' === get_post_type( $post_id ); };
return Maybe::of( $post_id )
->filter( $isAttachment )
->map( 'get_post' )
->map( Obj::prop( 'post_status' ) )
->getOrElse( partial( 'get_post_status', $post_id ) );
}
private function sync_custom_fields( $original_id, $post_id ) {
if ( $original_id && $original_id != $post_id ) {
$this->sitepress->copy_custom_fields ( $original_id, $post_id );
} else {
$translations = $this->post_translation->get_element_translations ( $post_id, false, true );
foreach ( $translations as $t_id ) {
$this->sitepress->copy_custom_fields ( $post_id, $t_id );
}
}
}
private function set_new_original( $trid, $removed_lang_code ) {
if ( $trid && $removed_lang_code ) {
$priorities = $this->sitepress->get_setting( 'languages_order' );
$this->post_translation->reload();
$translations = $this->post_translation->get_element_translations( false, $trid );
$new_source_lang_code = false;
foreach ( $priorities as $lang_code ) {
if ( isset( $translations[ $lang_code ] ) ) {
$new_source_lang_code = $lang_code;
break;
}
}
if ( $new_source_lang_code ) {
global $wpdb;
$rows_updated = $wpdb->update( $wpdb->prefix . 'icl_translations',
array( 'source_language_code' => $new_source_lang_code ),
array( 'trid' => $trid, 'source_language_code' => $removed_lang_code )
);
if( 0 < $rows_updated ) {
do_action( 'wpml_translation_update', array( 'trid' => $trid ) );
}
$wpdb->query( " UPDATE {$wpdb->prefix}icl_translations
SET source_language_code = NULL
WHERE language_code = source_language_code" );
}
}
}
}

View File

@@ -0,0 +1,442 @@
<?php
/**
* Class WPML_Post_Translation
*
* @package wpml-core
* @subpackage post-translation
*/
abstract class WPML_Post_Translation extends WPML_Element_Translation {
protected $settings;
protected $post_translation_sync;
public static $defer_term_counting = false;
/**
* @var WPML_Debug_BackTrace
*/
private $debug_backtrace;
/**
* @param array $settings
* @param wpdb $wpdb
*/
public function __construct( &$settings, &$wpdb ) {
parent::__construct( $wpdb );
$this->settings = $settings;
}
protected function is_setup_complete( ) {
return isset( $this->settings[ 'setup_complete' ]) && $this->settings[ 'setup_complete' ];
}
public function init() {
if ( $this->is_setup_complete() ) {
add_action( 'save_post', [ $this, 'save_post_actions' ], 100, 2 );
add_action( 'shutdown', [ $this, 'shutdown_action' ], PHP_INT_MAX );
add_action( 'edit_attachment', [ $this, 'attachment_actions' ], 100 );
add_action( 'add_attachment', [ $this, 'attachment_actions' ], 100 );
}
}
public function get_original_post_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'post_status', $source_lang_code );
}
public function get_original_post_ID( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'ID', $source_lang_code );
}
public function get_original_menu_order
( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'menu_order', $source_lang_code );
}
public function get_original_comment_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'comment_status', $source_lang_code );
}
public function get_original_ping_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'ping_status', $source_lang_code );
}
public function get_original_post_format( $trid, $source_lang_code = null ) {
return get_post_format ( $this->get_original_post_ID ( $trid, $source_lang_code ) );
}
/**
* @param int $pidd
* @param WP_Post $post
*
* @return void
*/
public abstract function save_post_actions( $pidd, $post );
/** @param int $post_id */
public function attachment_actions( $post_id ) {
/**
* This filter hooks determines whether we should apply the
* "save_post" actions on an attachment.
*
* @since 4.4.0
*
* @param bool True if we should apply save post actions on the attachment, false otherwise (default false).
* @param int $post_id The attachment post ID.
*/
if ( apply_filters( 'wpml_apply_save_attachment_actions', false, $post_id ) ) {
$post = get_post( $post_id );
if ( $post ) {
$this->save_post_actions( $post_id, $post );
}
}
}
public function shutdown_action() {
if ( self::$defer_term_counting ) {
self::$defer_term_counting = false;
wp_defer_term_counting( false );
}
}
public function trash_translation ( $trans_id ) {
if ( !WPML_WordPress_Actions::is_bulk_trash( $trans_id ) ) {
wp_trash_post( $trans_id );
}
}
public function untrash_translation( $trans_id ) {
if ( ! WPML_WordPress_Actions::is_bulk_untrash( $trans_id ) ) {
wp_untrash_post( $trans_id );
}
}
function untrashed_post_actions( $post_id ) {
$translation_sync = $this->get_sync_helper ();
$translation_sync->untrashed_post_actions ( $post_id );
}
public function delete_post_translation_entry( $post_id ) {
$update_args = array( 'context' => 'post', 'element_id' => $post_id );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$sql = $this->wpdb->prepare( "DELETE FROM {$this->wpdb->prefix}icl_translations
WHERE element_id = %d
AND element_type LIKE 'post%%'
LIMIT 1",
$post_id );
$res = $this->wpdb->query( $sql );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
return $res;
}
public function trashed_post_actions( $post_id ) {
$this->delete_post_actions( $post_id, true );
}
/**
* This function holds all actions to be run after deleting a post.
* 1. Delete the posts entry in icl_translations.
* 2. Set one of the posts translations or delete all translations of the post, depending on sitepress settings.
*
* @param Integer $post_id
* @param bool $keep_db_entries Sets whether icl_translations entries are to be deleted or kept, when hooking this to
* post trashing we want them to be kept.
*/
public function delete_post_actions( $post_id, $keep_db_entries = false ) {
$translation_sync = $this->get_sync_helper ();
$translation_sync->delete_post_actions ( $post_id, $keep_db_entries );
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
abstract function get_save_post_trid( $post_id, $post_status );
/**
* @param integer $post_id
* @param SitePress $sitepress
* @return bool|mixed|null|string|void
*/
public function get_save_post_lang( $post_id, $sitepress ) {
$language_code = $this->get_element_lang_code ( $post_id );
$language_code = $language_code ? $language_code : $sitepress->get_current_language ();
$language_code = $sitepress->is_active_language ( $language_code ) ? $language_code
: $sitepress->get_default_language ();
return apply_filters ( 'wpml_save_post_lang', $language_code );
}
/**
* @param int $trid
* @param string $language_code
* @param string $default_language
*
* @return string|null
*/
protected abstract function get_save_post_source_lang( $trid, $language_code, $default_language );
/**
* Sets a posts language details, invalidates caches relating to the post and triggers
* synchronisation actions across translations of the just saved post.
*
* @param int $trid
* @param array $post_vars
* @param string $language_code
* @param string $source_language
*
* @used-by \WPML_Post_Translation::save_post_actions as final step of the WPML Core save_post actions
*/
protected function after_save_post( $trid, $post_vars, $language_code, $source_language ) {
$this->maybe_set_elid( $trid, $post_vars['post_type'], $language_code, $post_vars['ID'], $source_language );
$translation_sync = $this->get_sync_helper();
$original_id = $this->get_original_element( $post_vars['ID'] );
$translation_sync->sync_with_translations( $original_id ? $original_id : $post_vars['ID'], $post_vars );
$translation_sync->sync_with_duplicates( $post_vars['ID'] );
if ( ! function_exists( 'icl_cache_clear' ) ) {
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
}
icl_cache_clear( $post_vars['post_type'] . 's_per_language', true );
if ( ! in_array( $post_vars['post_type'], array( 'nav_menu_item', 'attachment' ), true ) ) {
do_action( 'wpml_tm_save_post', $post_vars['ID'], get_post( $post_vars['ID'] ), false );
}
// Flush object cache.
$this->flush_object_cache_for_groups( array( 'ls_languages', 'element_translations' ) );
do_action( 'wpml_after_save_post', $post_vars['ID'], $trid, $language_code, $source_language );
}
/**
* Create new instance of WPML_WP_Cache for each group and flush cache for group.
* @param array $groups
*/
private function flush_object_cache_for_groups( $groups = array() ) {
if ( ! empty( $groups ) ) {
foreach ( $groups as $group ) {
$cache = new WPML_WP_Cache( $group );
$cache->flush_group_cache();
}
}
}
private function get_original_post_attr( $trid, $attribute, $source_lang_code ) {
$legal_attributes = array(
'post_status',
'post_date',
'menu_order',
'comment_status',
'ping_status',
'ID'
);
$res = false;
if ( in_array ( $attribute, $legal_attributes, true ) ) {
$attribute = 'p.' . $attribute;
$source_snippet = $source_lang_code === null
? " AND wpml_translations.source_language_code IS NULL "
: $this->wpdb->prepare ( " AND wpml_translations.language_code = %s ", $source_lang_code );
$res = $this->wpdb->get_var (
$this->wpdb->prepare (
"SELECT {$attribute}
" . $this->get_element_join() . "
WHERE wpml_translations.trid=%d
{$source_snippet}
LIMIT 1",
$trid
)
);
}
return $res;
}
public function has_save_post_action( $post ) {
if ( ! $post ) {
return false;
}
$is_auto_draft = isset( $post->post_status ) && $post->post_status === 'auto-draft';
$is_editing_different_post = $this->is_editing_different_post( $post->ID );
$is_saving_a_revision = array_key_exists( 'post_type', $_POST ) && 'revision' === $_POST['post_type'];
$is_untrashing = array_key_exists( 'action', $_GET ) && 'untrash' === $_GET['action'];
$is_auto_save = array_key_exists( 'autosave', $_POST );
$skip_sitepress_actions = array_key_exists( 'skip_sitepress_actions', $_POST );
$is_post_a_revision = 'revision' === $post->post_type;
$is_scheduled_to_be_trashed = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
$is_add_meta_action = isset( $_POST['action'] ) && 'add-meta' === $_POST['action'];
$is_inner_post_insertion = $this->is_inner_post_insertion();
return $this->is_translated_type( $post->post_type )
&& ! ( $is_auto_draft
|| $is_auto_save
|| $skip_sitepress_actions
|| ( $is_editing_different_post && ! $is_inner_post_insertion )
|| $is_saving_a_revision
|| $is_post_a_revision
|| $is_scheduled_to_be_trashed
|| $is_add_meta_action
|| $is_untrashing );
}
/**
* @param int $post_id
*
* @return bool
*/
protected function is_editing_different_post( $post_id ) {
return array_key_exists( 'post_ID', $_POST ) && (int) $_POST['post_ID'] && $post_id != $_POST['post_ID'];
}
protected function get_element_join() {
return "FROM {$this->wpdb->prefix}icl_translations wpml_translations
JOIN {$this->wpdb->posts} p
ON wpml_translations.element_id = p.ID
AND wpml_translations.element_type = CONCAT('post_', p.post_type)";
}
protected function get_type_prefix() {
return 'post_';
}
public function is_translated_type( $post_type ) {
global $sitepress;
return $sitepress->is_translated_post_type ( $post_type );
}
/**
* @param WP_Post $post
*
* @return string[] all language codes the post can be translated into
*/
public function get_allowed_target_langs( $post ) {
global $sitepress;
$active_languages = $sitepress->get_active_languages ();
$can_translate = array_keys ( $active_languages );
$can_translate = array_diff (
$can_translate,
array( $this->get_element_lang_code ( $post->ID ) )
);
return apply_filters ( 'wpml_allowed_target_langs', $can_translate, $post->ID, 'post' );
}
/**
* Before setting the language of the post to be saved, check if a translation in this language already exists
* This check is necessary, so that synchronization actions like thrashing or un-trashing of posts, do not lead to
* database corruption, due to erroneously changing a posts language into a state,
* where it collides with an existing translation. While the UI prevents this sort of action for the most part,
* this is not necessarily the case for other plugins like TM.
* The logic here first of all checks if an existing translation id is present in the desired language_code.
* If so but this translation is actually not the currently to be saved post,
* then this post will be saved to its current language. If the translation already exists,
* the existing translation id will be used. In all other cases a new entry in icl_translations will be created.
*
* @param Integer $trid
* @param String $post_type
* @param String $language_code
* @param Integer $post_id
* @param String $source_language
*/
private function maybe_set_elid( $trid, $post_type, $language_code, $post_id, $source_language ) {
global $sitepress;
$element_type = 'post_' . $post_type;
$sitepress->set_element_language_details (
$post_id,
$element_type,
$trid,
$language_code,
$source_language
);
}
/**
* @return WPML_Post_Synchronization
*/
private function get_sync_helper() {
global $sitepress;
$this->post_translation_sync = $this->post_translation_sync
? $this->post_translation_sync : new WPML_Post_Synchronization( $this->settings, $this, $sitepress );
return $this->post_translation_sync;
}
/**
* @return WPML_Debug_BackTrace
*/
private function get_debug_backtrace() {
if ( ! $this->debug_backtrace ) {
$this->debug_backtrace = new WPML\Utils\DebugBackTrace( 20 );
}
return $this->debug_backtrace;
}
public function set_debug_backtrace( WPML_Debug_BackTrace $debug_backtrace ) {
$this->debug_backtrace = $debug_backtrace;
}
/**
* @return bool
*/
protected function is_inner_post_insertion() {
$debug_backtrace = $this->get_debug_backtrace();
return 1 < $debug_backtrace->count_function_in_call_stack( 'wp_insert_post' );
}
/**
* @param WP_Post $post
*
* @return array
*/
protected function get_post_vars( $post ) {
$post_vars = array();
if ( ! $this->is_inner_post_insertion() ) {
$post_vars = (array) $_POST;
}
foreach ( (array) $post as $k => $v ) {
$post_vars[ $k ] = $v;
}
$post_vars['post_type'] = isset( $post_vars['post_type'] ) ? $post_vars['post_type'] : $post->post_type;
return $post_vars;
}
protected function defer_term_counting() {
if ( ! self::$defer_term_counting ) {
self::$defer_term_counting = true;
wp_defer_term_counting( true );
}
}
/**
* @return self|WPML_Frontend_Post_Actions|WPML_Admin_Post_Actions
*/
public static function getGlobalInstance() {
global $wpml_post_translations, $sitepress;
if ( ! isset( $wpml_post_translations ) ) {
wpml_load_post_translation( is_admin(), $sitepress->get_settings() );
}
return $wpml_post_translations;
}
}

View File

@@ -0,0 +1,310 @@
<?php
class WPML_Root_Page_Actions {
/** @var array $sp_settings */
private $sp_settings;
public function __construct( &$sitepress_settings ) {
$this->sp_settings = &$sitepress_settings;
}
public function delete_root_page_lang() {
global $wpdb;
$root_id = $this->get_root_page_id ();
if ( $root_id ) {
$update_args = array(
'element_id' => $root_id,
'element_type' => 'post_page',
'context' => 'post'
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$wpdb->delete (
$wpdb->prefix . 'icl_translations', array( 'element_id' => $root_id, 'element_type' => 'post_page' ), array( '%d', '%s' )
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
}
/**
* Checks if a given $url points at the root page
*
* @param string $url
*
* @return bool
*
* @uses \WPML_Root_Page::is_root_page
*/
public function is_url_root_page( $url ) {
$ret = false;
if ( $this->get_root_page_id() ) {
$ret = WPML_Root_Page::is_root_page( $url );
}
return $ret;
}
/**
* If a page is used as the root page, returns the id of that page, otherwise false.
*
* @return bool|false|int
*/
public function get_root_page_id() {
$urls_in_dirs = isset($this->sp_settings['language_negotiation_type']) && (int)$this->sp_settings['language_negotiation_type'] === 1;
$urls = isset( $this->sp_settings['urls'] ) ? $this->sp_settings['urls'] : array();
return $urls_in_dirs && isset( $urls['root_page'] )
&& ! empty( $urls['directory_for_default_language'] )
&& isset( $urls['show_on_root'] )
&& $urls['show_on_root'] === 'page'
&& $urls['root_page']
? $urls['root_page'] : false;
}
function wpml_home_url_init() {
global $pagenow, $sitepress;
if ( $pagenow == 'post.php' || $pagenow == 'post-new.php' ) {
$root_id = $this->get_root_page_id();
if ( ! empty( $_GET['wpml_root_page'] ) && ! empty( $root_id ) ) {
$rp = get_post( $root_id );
if ( $rp && $rp->post_status != 'trash' ) {
wp_redirect( get_edit_post_link( $root_id, 'no-display' ) );
exit;
}
}
if ( isset( $_GET['wpml_root_page'] ) && $_GET['wpml_root_page'] || ( isset( $_GET['post'] ) && $_GET['post'] == $root_id ) ) {
remove_action( 'admin_head', array( $sitepress, 'post_edit_language_options' ) );
add_action( 'admin_head', array( $this, 'wpml_home_url_language_box_setup' ) );
remove_action( 'page_link', array( $sitepress, 'permalink_filter' ), 1 );
}
}
}
function wpml_home_url_exclude_root_page_from_menus( $args ) {
if ( !empty( $args[ 'exclude' ] ) ) {
$args[ 'exclude' ] .= ',';
} else {
$args[ 'exclude' ] = '';
}
$args[ 'exclude' ] .= $this->get_root_page_id();
return $args;
}
/**
* Filters out all page menu items that point to the root page.
*
* @param object[] $items
*
* @return array
*
* @hook wp_get_nav_menu_items
*/
function exclude_root_page_menu_item( $items ) {
$root_id = $this->get_root_page_id();
foreach ( $items as $key => $item ) {
if ( isset( $item->object_id )
&& isset( $item->type )
&& $item->object_id == $root_id
&& $item->type === 'post_type'
) {
unset( $items[ $key ] );
}
}
return $items;
}
function wpml_home_url_exclude_root_page( $excludes ) {
$excludes[ ] = $this->get_root_page_id();
return $excludes;
}
function wpml_home_url_exclude_root_page2( $args ) {
$args[ 'exclude' ][ ] = $this->get_root_page_id();
return $args;
}
function wpml_home_url_get_pages( $pages ) {
$root_id = $this->get_root_page_id();
foreach ( $pages as $k => $page ) {
if ( $page->ID == $root_id ) {
unset( $pages[ $k ] );
$pages = array_values ( $pages );
break;
}
}
return $pages;
}
function wpml_home_url_language_box_setup() {
add_meta_box(
WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID,
__( 'Language', 'sitepress' ),
array( $this, 'wpml_home_url_language_box' ),
'page',
/**
* Filter meta box position.
*
* The context within the screen where the boxes should display. Available contexts vary from screen to screen.
* Post edit screen contexts include 'normal', 'side', and 'advanced'.
*
* @param String WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID Meta box ID.
*
* @since 4.2.8
*
*/
apply_filters( 'wpml_post_edit_meta_box_context', 'side', WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID ),
apply_filters( 'wpml_post_edit_meta_box_priority', 'high' )
);
}
function wpml_home_url_language_box( $post ) {
$root_id = $this->get_root_page_id();
if ( isset( $_GET[ 'wpml_root_page' ] )
|| ( !empty( $root_id )
&& $post->ID == $root_id ) ) {
_e ( "This page does not have a language since it's the site's root page." );
echo '<input type="hidden" name="_wpml_root_page" value="1" />';
}
}
function wpml_home_url_save_post_actions( $pidd, $post ) {
global $sitepress, $wpdb, $iclTranslationManagement;
if ( (bool) filter_input ( INPUT_POST, '_wpml_root_page' ) === true ) {
if ( isset( $_POST[ 'autosave' ] ) || ( isset( $post->post_type ) && $post->post_type == 'revision' ) ) {
return;
}
$iclsettings[ 'urls' ][ 'root_page' ] = $post->ID;
$sitepress->save_settings ( $iclsettings );
remove_action( 'save_post', array( $sitepress, 'save_post_actions' ), 10 );
if ( !is_null ( $iclTranslationManagement ) ) {
remove_action( 'save_post', array( $iclTranslationManagement, 'save_post_actions' ), 11 );
}
$update_args = array(
'element_id' => $post->ID,
'element_type' => 'post_page',
'context' => 'post'
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$wpdb->query (
$wpdb->prepare (
"DELETE FROM {$wpdb->prefix}icl_translations WHERE element_type='post_page' AND element_id=%d",
$post->ID
)
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
}
function wpml_home_url_setup_root_page() {
global $sitepress, $wpml_query_filter;
remove_action( 'template_redirect', 'redirect_canonical' );
add_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
remove_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ), 10 );
remove_filter( 'posts_where', array( $wpml_query_filter, 'posts_where_filter' ), 10 );
$root_id = $this->get_root_page_id();
$rp = get_post( $root_id );
if ( $rp && $rp->post_status != 'trash' ) {
$sitepress->ROOT_URL_PAGE_ID = $root_id;
}
}
/**
* @param WP_Query $q
*
* @return mixed
*/
function wpml_home_url_parse_query( $q ) {
if ( ! $q->is_main_query() ) {
return $q;
}
if ( ! WPML_Root_Page::is_current_request_root() ) {
return $q;
} else {
remove_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
$uri_path = trim( wpml_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
$uri_parts = explode( '/', $uri_path );
$potential_pagination_parameter = array_pop( $uri_parts );
$query_args = array();
wp_parse_str( wpml_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ), $query_args );
if ( is_numeric( $potential_pagination_parameter ) ) {
$query_args['page'] = $potential_pagination_parameter;
}
$q->parse_query( $query_args );
$root_id = $this->get_root_page_id();
add_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
if ( false !== $root_id ) {
$q = $this->set_page_query_parameters( $q, $root_id );
} else {
$front_page = get_option( 'page_on_front' );
if ( $front_page ) {
$q = $this->set_page_query_parameters( $q, $front_page );
}
}
}
return $q;
}
private function set_page_query_parameters( $q, $page_id ) {
$q->query_vars['page_id'] = $page_id;
$q->query['page_id'] = $page_id;
$q->is_page = 1;
$q->queried_object = new WP_Post( get_post( $page_id ) );
$q->queried_object_id = $page_id;
$q->query_vars['error'] = '';
$q->is_404 = false;
$q->query['error'] = null;
$q->is_home = false;
$q->is_singular = true;
return $q;
}
}
/**
* Checks if the language switcher is to be displayed.
* Used to check if the displayed page is a root page and the switcher is to be hidden because of it.
*
* @return bool true if the switcher is to be hidden
*/
function wpml_home_url_ls_hide_check() {
global $sitepress;
return $sitepress->get_setting( 'language_negotiation_type' ) == 1
&& (bool) ( $urls = $sitepress->get_setting( 'urls' ) ) === true
&& ! empty( $urls['directory_for_default_language'] )
&& isset( $urls['show_on_root'] ) && $urls['show_on_root'] === 'page'
&& ! empty( $urls['hide_language_switchers'] )
&& WPML_Root_Page::is_current_request_root();
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Class WPML_WordPress_Actions
* @package wpml-core
* @subpackage post-translation
*/
class WPML_WordPress_Actions {
/**
* @param int $post_id
*
* @return bool
*/
public static function is_bulk_trash( $post_id ) {
if ( self::is_trash_action() && self::post_id_in_bulk( $post_id ) ) {
return true;
} else {
return false;
}
}
/**
* @param int $post_id
*
* @return bool
*/
public static function is_bulk_untrash( $post_id ) {
if ( self::is_untrash_action() && self::post_id_in_bulk( $post_id, true ) ) {
return true;
} else {
return false;
}
}
public static function is_heartbeat( ) {
return self::is_action( 'heartbeat', 'post' );
}
protected static function is_trash_action() {
return self::is_action( 'trash' );
}
protected static function is_untrash_action() {
return self::is_action( 'untrash' );
}
/**
* @param string $action
*
* @return bool
*/
protected static function is_action( $action, $type = 'get' ) {
if ( $type == 'get' ) {
return ( isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == $action ) || ( isset( $_GET[ 'action2' ] ) && $_GET[ 'action2' ] == $action );
} elseif ( $type == 'post' ) {
return ( isset( $_POST[ 'action' ] ) && $_POST[ 'action' ] == $action ) || ( isset( $_POST[ 'action2' ] ) && $_POST[ 'action2' ] == $action );
} else {
return false;
}
}
/**
* @param int $post_id
* @param bool $check_ids
*
* @return bool
*/
protected static function post_id_in_bulk( $post_id, $check_ids = false ) {
if ( isset( $_GET[ 'post' ] ) && is_array( $_GET[ 'post' ] ) && in_array( $post_id, $_GET[ 'post' ] ) ) {
return true;
} elseif ( $check_ids ) {
// We need to check the ids parameter when user clicks on 'undo' after trashing.
return isset( $_GET[ 'ids' ] ) && is_string( $_GET[ 'ids' ] ) && in_array( $post_id, explode( ',', $_GET[ 'ids' ] ) );
} else {
return false;
}
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Class WPML_Name_Query_Filter_Translated
*
* @package wpml-core
* @subpackage post-translation
*
* @since 3.2.3
*/
class WPML_Name_Query_Filter_Translated extends WPML_Name_Query_Filter {
private $pages_to_langs = array();
/**
* @param array $pages_with_name
*
* @return int|null|string
*/
protected function select_best_match( $pages_with_name ) {
if ( ! empty( $pages_with_name['matching_ids'] ) ) {
$matching_page = $this->get_matching_page_in_requested_lang( $pages_with_name['matching_ids'] );
if ( $matching_page ) {
return $matching_page;
}
$display_as_translated_page = $this->get_matching_page_displayed_as_translated();
if ( $display_as_translated_page ) {
return $display_as_translated_page;
}
}
if ( ! empty( $pages_with_name['related_ids'] ) ) {
return $this->get_the_best_related_page_to_redirect( $pages_with_name['related_ids'] );
}
return null;
}
/**
* @param array $matching_ids
*
* @return int|null
*/
private function get_matching_page_in_requested_lang( array $matching_ids ) {
foreach ( $matching_ids as $matching_id ) {
$page_lang = $this->post_translation->get_element_lang_code( (int) $matching_id );
if ( $this->sitepress->get_current_language() === $page_lang ) {
return (int) $matching_id;
}
$this->pages_to_langs[ $matching_id ] = $page_lang;
}
return null;
}
/**
* @return int|null
*/
private function get_matching_page_displayed_as_translated() {
foreach ( $this->pages_to_langs as $page_id => $lang ) {
if ( $lang === $this->sitepress->get_default_language()
&& $this->sitepress->is_display_as_translated_post_type( get_post_type( $page_id ) )
) {
return $this->post_translation->element_id_in( $page_id, $this->sitepress->get_current_language(), true );
}
}
return null;
}
/**
* Note: `$this->active_languages` is already ordered in `get_ordered_langs`
*
* @param array $related_page_ids
*
* @return int|null
*/
private function get_the_best_related_page_to_redirect( array $related_page_ids ) {
foreach ( $this->active_languages as $lang_code ) {
foreach ( $related_page_ids as $related_page_id ) {
$page_lang = $this->post_translation->get_element_lang_code( (int) $related_page_id );
if ( $page_lang === $lang_code ) {
return $related_page_id;
}
}
}
return null;
}
/**
* Returns a SQL snippet for joining the posts table with icl translations filtered for the post_type
* of this class.
*
* @return string
*/
protected function get_from_join_snippet() {
return " FROM {$this->wpdb->posts} p
JOIN {$this->wpdb->prefix}icl_translations wpml_translations
ON p.ID = wpml_translations.element_id
AND wpml_translations.element_type = CONCAT('post_', p.post_type ) ";
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Class WPML_Name_Query_Filter_Untranslated
*
* @package wpml-core
* @subpackage post-translation
*
* @since 3.2.3
*/
class WPML_Name_Query_Filter_Untranslated extends WPML_Name_Query_Filter {
protected function select_best_match( $pages_with_name ) {
if ( ! empty( $pages_with_name['matching_ids'] ) ) {
return reset( $pages_with_name['matching_ids'] );
} elseif ( ! empty( $pages_with_name['related_ids'] ) ) {
return reset( $pages_with_name['related_ids'] );
}
return null;
}
/**
* Returns a SQL snippet for joining the posts table with icl translations filtered for the post_type
* of this class.
*
* @return string
*/
protected function get_from_join_snippet() {
return " FROM {$this->wpdb->posts} p ";
}
}

View File

@@ -0,0 +1,283 @@
<?php
/**
* Class WPML_Name_Query_Filter
*
* @package wpml-core
* @subpackage post-translation
*
* @since 3.2.3
*/
abstract class WPML_Name_Query_Filter extends WPML_Slug_Resolution {
/** @var string $post_type */
protected $post_type;
/** @var string[] $indexes */
protected $indexes = array( 'name' );
/** @var string $id_index */
protected $id_index = 'p';
/** @var string[] $active_languages */
protected $active_languages = array();
/** @var string $al_regexp */
protected $al_regexp;
/** @var WPML_Post_Translation $post_translation */
protected $post_translation;
protected $is_translated;
/**
* @param string $post_type
* @param SitePress $sitepress
* @param WPML_Post_Translation $post_translations
* @param wpdb $wpdb
*/
public function __construct( $post_type, &$sitepress, &$post_translations, &$wpdb ) {
parent::__construct( $wpdb, $sitepress );
$this->post_type = $post_type;
$this->indexes[] = $post_type;
$this->is_translated = $this->sitepress->is_translated_post_type( $post_type );
$this->post_translation = &$post_translations;
}
/**
* Looks through the "name" and "pagename" query vars in a given query and identifies the correct page_id
* corresponding to either of these two and then adjusts the query page_id to point at this correct page_id.
*
* @param WP_Query $page_query
*
* @return array
* - WP_Query that uses the id index stored in \WPML_Name_Query_Filter::$id_index
* instead of "name" or "pagename" in case a match was found, otherwise
* returns the input query unaltered.
* - int|false the page ID
*/
public function filter_page_name( WP_Query $page_query ) {
$this->active_languages = $this->get_ordered_langs();
$this->al_regexp = $this->generate_al_regexp( $this->active_languages );
foreach ( $this->indexes as $index ) {
list( $pages_with_name, $page_name_for_query ) = $this->query_needs_adjustment( $page_query, $index );
if ( ! empty( $pages_with_name['matching_ids'] ) || ! empty( $pages_with_name['related_ids'] ) ) {
$pid = $this->select_best_match( $pages_with_name );
if ( isset( $pid ) ) {
if ( ! isset( $page_query->queried_object_id ) || $pid != $page_query->queried_object_id ) {
$page_query = $this->maybe_adjust_query_by_pid( $page_query, $pid, $index );
break;
} else {
unset( $pid );
}
} elseif ( (bool) $page_name_for_query === true ) {
$page_query->query_vars[ $index ] = $page_name_for_query;
$page_query->query_vars['post_type'] = $this->post_type;
}
}
}
return array( $page_query, isset( $pid ) ? $pid : false );
}
abstract protected function select_best_match( $pages_with_name );
/**
* @param WP_Query $page_query
* @param int $pid
* @param string $index
*
* @return WP_Query
*/
protected function maybe_adjust_query_by_pid( $page_query, $pid, $index ) {
if ( ! ( isset( $page_query->queried_object )
&& isset( $page_query->queried_object->ID )
&& (int) $page_query->queried_object->ID === (int) $pid )
) {
if ( isset( $page_query->query['page_id'] ) ) {
$page_query->query['page_id'] = (int) $pid;
}
$page_query->query_vars['page_id'] = null;
$page_query->query_vars[ $this->id_index ] = (int) $pid;
if ( isset( $page_query->query[ $this->id_index ] ) ) {
$page_query->query[ $this->id_index ] = (int) $pid;
}
$page_query->is_page = false;
if ( isset( $page_query->query_vars[ $this->post_type ] )
&& $this->post_type !== 'page'
) {
$this->set_query_var_to_restore( $this->post_type, $page_query );
unset( $page_query->query_vars[ $this->post_type ] );
}
unset( $page_query->query_vars[ $index ] );
if ( ! empty( $page_query->queried_object ) ) {
$page_query->queried_object = get_post( $pid );
$page_query->queried_object_id = (int) $pid;
}
$page_query = $this->adjusting_id( $page_query );
}
return $page_query;
}
/**
* Called when the post id is being adjusted. Can be overridden.
*
* @param WP_Query $page_query
*
* @return WP_Query
*/
protected function adjusting_id( $page_query ) {
$page_query->is_single = true;
return $page_query;
}
/**
* Returns a SQL snippet for joining the posts table with icl translations filtered for the post_type
* of this class.
*
* @return string
*/
abstract protected function get_from_join_snippet();
/**
* Generates a regular expression matcher for matching language slugs in a URI
*
* @param string[] $active_language_codes
*
* @return string
*/
private function generate_al_regexp( $active_language_codes ) {
return '/^(' . implode( '|', $active_language_codes ) . ')\//';
}
/**
* @param WP_Query $page_query
* @param string $index
*
* @return array
*/
private function query_needs_adjustment( WP_Query $page_query, $index ) {
if ( empty( $page_query->query_vars[ $index ] ) ) {
$pages_with_name = false;
$page_name_for_query = false;
} else {
$page_name_for_query = preg_replace( $this->al_regexp, '', $page_query->query_vars[ $index ] );
if ( $this->page_name_has_parent( $page_name_for_query ) ) {
$pages_with_name = $this->get_multiple_slug_adjusted_IDs( explode( '/', $page_name_for_query ) );
} else {
$pages_with_name = $this->get_single_slug_adjusted_IDs(
$page_name_for_query,
$this->get_post_parent_query_var( $page_query )
);
}
}
return array( $pages_with_name, $page_name_for_query );
}
/**
* @param string $page_name_for_query
*
* @return bool
*/
private function page_name_has_parent( $page_name_for_query ) {
return false !== strpos( $page_name_for_query, '/' );
}
/**
* @param WP_Query $page_query
*
* @return int|string
*/
private function get_post_parent_query_var( WP_Query $page_query ) {
$post_parent = 0;
if ( isset( $page_query->query_vars['post_parent'] ) ) {
$post_parent = $page_query->query_vars['post_parent'];
}
return $post_parent;
}
/**
* @param string $page_name_for_query
* @param string|int $post_parent
*
* @return array
*/
private function get_single_slug_adjusted_IDs( $page_name_for_query, $post_parent ) {
$cache = new WPML_WP_Cache( get_class( $this ) );
$cache_key = 'get_single_slug_adjusted_IDs' . $this->post_type . $page_name_for_query . $post_parent;
$found = false;
$pages_with_name = $cache->get( $cache_key, $found );
if ( ! $found ) {
$pages_with_name = $this->get_single_slug_adjusted_IDs_from_DB( $page_name_for_query, $post_parent );
$cache->set( $cache_key, $pages_with_name );
}
return array( 'matching_ids' => $pages_with_name );
}
/**
* @param string $page_name_for_query
* @param string|int $post_parent
*
* @return array
*/
private function get_single_slug_adjusted_IDs_from_DB( $page_name_for_query, $post_parent ) {
$pages_with_name = $this->wpdb->get_col(
$this->wpdb->prepare(
'
SELECT ID
' . $this->get_from_join_snippet()
. $this->get_where_snippet() . ' p.post_name = %s
ORDER BY p.post_parent = %d DESC
',
$page_name_for_query,
$post_parent
)
);
return $pages_with_name;
}
/**
* @param string[] $slugs slugs that were queried for
*
* @return int[] page_ids ordered by their likelihood of correctly matching the query target,
* derived from checking all slugs against the sits pages slugs as well as their parent slugs.
* Elements at the beginning of the array are more correct than later elements, but the results
* are not yet filtered for the correct language.
*
* @used-by \WPML_Page_Name_Query_Filter::filter_page_name to find the correct page_id corresponding to a set of slugs,
* by filtering the results of this function by language of the
* returned page_ids.
*/
private function get_multiple_slug_adjusted_IDs( $slugs ) {
$parent_slugs = array_slice( $slugs, 0, - 1 );
$pages_with_name = $this->wpdb->get_results(
' SELECT p.ID, p.post_name, p.post_parent, par.post_name as parent_name
' . $this->get_from_join_snippet() . "
LEFT JOIN {$this->wpdb->posts} par
ON par.ID = p.post_parent
" . $this->get_where_snippet() . ' p.post_name IN (' . wpml_prepare_in( $slugs ) . ')
ORDER BY par.post_name IN (' . wpml_prepare_in( $parent_slugs ) . ') DESC'
);
$query_scorer = new WPML_Score_Hierarchy( $pages_with_name, $slugs );
return $query_scorer->get_possible_ids_ordered();
}
private function get_where_snippet() {
return $this->wpdb->prepare( ' WHERE p.post_type = %s AND ', $this->post_type );
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Class WPML_Page_Name_Query_Filter
*
* @package wpml-core
* @subpackage post-translation
*
* @since 3.2
*/
class WPML_Page_Name_Query_Filter extends WPML_Name_Query_Filter_Translated {
protected $id_index = 'page_id';
/**
* @param SitePress $sitepress
* @param WPML_Post_Translation $post_translations
* @param wpdb $wpdb
*/
public function __construct( &$sitepress, &$post_translations, &$wpdb ) {
parent::__construct( 'page', $sitepress, $post_translations, $wpdb );
$this->indexes = array( 'name', 'pagename' );
}
/**
* @param WP_Query $page_query
* @param int $pid
* @param string $index
*
* @return WP_Query
*/
protected function maybe_adjust_query_by_pid( $page_query, $pid, $index ) {
$page_query = parent::maybe_adjust_query_by_pid( $page_query, $pid, $index );
$is_page_for_posts = 'page' == get_option( 'show_on_front' ) && (int) $pid === (int) get_option( 'page_for_posts' );
if ( $is_page_for_posts ) {
$page_query->query_vars['post_status'] = 'publish';
$page_query->query_vars['name'] = '';
$page_query->query_vars['page_id'] = 0;
$page_query->post_status = 'publish';
$page_query->is_page = false;
$page_query->is_singular = false;
$page_query->is_home = true;
$page_query->is_posts_page = true;
}
return $page_query;
}
/**
* Called when the post id is being adjusted. Can be overridden.
*
* @param WP_Query $page_query
*
* @return WP_Query
*/
protected function adjusting_id( $page_query ) {
$page_query->is_page = true;
return $page_query;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Class WPML_Slug_Resolution
*
* @package wpml-core
* @subpackage post-translation
*/
abstract class WPML_Slug_Resolution extends WPML_WPDB_And_SP_User {
const WPML_BACKUP_KEY = '_wpml_backup';
/**
* Returns all active language codes ordered by the language order, but having the current language
* at the beginning.
*
* @return string[]
*
* @uses \SitePress::get_setting to get the languages order from the sitepress settings
*/
protected function get_ordered_langs() {
$lang_order = $this->sitepress->get_setting( 'languages_order' );
$lang_order = $lang_order ? $lang_order : array_keys( $this->sitepress->get_active_languages() );
array_unshift( $lang_order, $this->sitepress->get_current_language() );
return array_unique( $lang_order );
}
/**
* @param string $key
* @param WP_Query $wp_query
*/
protected function set_query_var_to_restore( $key, WP_Query $wp_query ) {
$wp_query->query_vars[ self::WPML_BACKUP_KEY ][ $key ] = $wp_query->query_vars[ $key ];
add_filter( 'the_posts', array( $this, 'restore_query_vars' ), 10, 2 );
}
/**
* @param WP_Post[] $posts
* @param WP_Query $wp_query
*
* @return mixed
*/
public function restore_query_vars( $posts, $wp_query ) {
if ( isset( $wp_query->query_vars[ self::WPML_BACKUP_KEY ] ) ) {
foreach ( $wp_query->query_vars[ self::WPML_BACKUP_KEY ] as $key => $value ) {
$wp_query->query_vars[ $key ] = $value;
}
unset( $wp_query->query_vars[ self::WPML_BACKUP_KEY ] );
remove_filter( 'the_posts', array( $this, 'restore_query_vars' ), 10 );
}
return $posts;
}
}

View File

@@ -0,0 +1,501 @@
<?php
/**
* @package wpml-core
*/
function icl_reset_language_data() {
global $wpdb, $sitepress;
$active = $wpdb->get_col( "SELECT code FROM {$wpdb->prefix}icl_languages WHERE active = 1" );
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}icl_languages`" );
SitePress_Setup::fill_languages();
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}icl_languages_translations`" );
SitePress_Setup::fill_languages_translations();
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}icl_flags`" );
SitePress_Setup::fill_flags();
// restore active
$wpdb->query( "UPDATE {$wpdb->prefix}icl_languages SET active=1 WHERE code IN(" . wpml_prepare_in( $active ) . ')' );
$wpdb->update( $wpdb->prefix . 'icl_flags', array( 'from_template' => 0 ), null );
$codes = $wpdb->get_col( "SELECT code FROM {$wpdb->prefix}icl_languages" );
foreach ( $codes as $code ) {
if ( ! $code || $wpdb->get_var(
$wpdb->prepare( "SELECT lang_code FROM {$wpdb->prefix}icl_flags WHERE lang_code = %s", $code )
)
) {
continue;
}
if ( ! file_exists( WPML_PLUGIN_PATH . '/res/flags/' . $code . '.png' ) ) {
$file = 'nil.png';
} else {
$file = $code . '.png';
}
$wpdb->insert(
$wpdb->prefix . 'icl_flags',
array(
'lang_code' => $code,
'flag' => $file,
'from_template' => 0,
)
);
}
$last_default_language = $sitepress !== null ? $sitepress->get_default_language() : 'en';
if ( ! in_array( $last_default_language, $codes ) ) {
$allowed_langs = array_intersect( array_keys( $sitepress->get_active_languages() ), $codes );
$sitepress->set_default_language( array_pop( $allowed_langs ) );
}
icl_cache_clear();
do_action( 'wpml_translation_update', array( 'type' => 'reset' ) );
}
function icl_sitepress_activate() {
global $wpdb;
$charset_collate = SitePress_Setup::get_charset_collate();
try {
SitePress_Setup::fill_languages();
SitePress_Setup::fill_languages_translations();
SitePress_Setup::fill_flags();
// translations
$table_name = $wpdb->prefix . 'icl_translations';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`translation_id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`element_type` VARCHAR( 60 ) NOT NULL DEFAULT 'post_post',
`element_id` BIGINT NULL DEFAULT NULL ,
`trid` BIGINT NOT NULL ,
`language_code` VARCHAR( 7 ) NOT NULL,
`source_language_code` VARCHAR( 7 ),
UNIQUE KEY `el_type_id` (`element_type`,`element_id`),
UNIQUE KEY `trid_lang` (`trid`,`language_code`),
KEY `trid` (`trid`),
KEY `id_type_language` (`element_id`, `element_type`, `language_code`)
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// translation_status table
$table_name = $wpdb->prefix . 'icl_translation_status';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`rid` bigint(20) NOT NULL AUTO_INCREMENT,
`translation_id` bigint(20) NOT NULL,
`status` tinyint(4) NOT NULL,
`translator_id` bigint(20) NOT NULL,
`needs_update` tinyint(4) NOT NULL,
`md5` varchar(32) NOT NULL,
`translation_service` varchar(16) NOT NULL,
`batch_id` int DEFAULT 0 NOT NULL,
`translation_package` longtext NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`links_fixed` tinyint(4) NOT NULL DEFAULT 0,
`_prevstate` longtext,
`uuid` varchar(36) NULL,
`tp_id` INT NULL DEFAULT NULL,
`tp_revision` INT NOT NULL DEFAULT 1,
`ts_status` TEXT NULL DEFAULT NULL,
`review_status` ENUM('NEEDS_REVIEW', 'EDITING', 'ACCEPTED'),
`ate_comm_retry_count` INT(11) UNSIGNED DEFAULT 0,
PRIMARY KEY (`rid`),
UNIQUE KEY `translation_id` (`translation_id`)
) {$charset_collate}
";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// translation jobs
$table_name = $wpdb->prefix . 'icl_translate_job';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`job_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`rid` BIGINT UNSIGNED NOT NULL ,
`translator_id` INT UNSIGNED NOT NULL ,
`translated` TINYINT UNSIGNED NOT NULL DEFAULT 0,
`manager_id` INT UNSIGNED NOT NULL ,
`revision` INT UNSIGNED NULL,
`title` VARCHAR(160) NULL,
`deadline_date` DATETIME NULL,
`completed_date` DATETIME NULL,
`editor` VARCHAR(16) NULL,
`editor_job_id` BIGINT(20) UNSIGNED NULL,
`edit_timestamp` INT(11) UNSIGNED NULL,
`automatic` TINYINT UNSIGNED NOT NULL DEFAULT 0,
INDEX ( `rid` , `translator_id` )
) {$charset_collate}
";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// translate table
$table_name = $wpdb->prefix . 'icl_translate';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`tid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`job_id` BIGINT UNSIGNED NOT NULL ,
`content_id` BIGINT UNSIGNED NOT NULL ,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`field_type` VARCHAR( 160 ) NOT NULL ,
`field_wrap_tag` VARCHAR( 16 ) NOT NULL ,
`field_format` VARCHAR( 16 ) NOT NULL ,
`field_translate` TINYINT NOT NULL ,
`field_data` longtext NOT NULL ,
`field_data_translated` longtext NOT NULL ,
`field_finished` TINYINT NOT NULL DEFAULT 0,
INDEX ( `job_id` )
) {$charset_collate}
";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// batches table
$table_name = $wpdb->prefix . 'icl_translation_batches';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS {$wpdb->prefix}icl_translation_batches (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batch_name` text NOT NULL,
`tp_id` int NULL,
`ts_url` text NULL,
`last_update` DATETIME NULL,
PRIMARY KEY (`id`)
) {$charset_collate}
";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// languages locale file names
$table_name = $wpdb->prefix . 'icl_locale_map';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`code` VARCHAR( 7 ) NOT NULL ,
`locale` VARCHAR( 35 ) NOT NULL ,
PRIMARY KEY (`code` ,`locale`)
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
/* general string translation */
$translation_priority_default = __( 'Optional', 'sitepress' );
$table_name = $wpdb->prefix . 'icl_strings';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`language` varchar(7) NOT NULL,
`context` varchar(" . WPML_STRING_TABLE_NAME_CONTEXT_LENGTH . ') NOT NULL,
`name` varchar(' . WPML_STRING_TABLE_NAME_CONTEXT_LENGTH . ") NOT NULL,
`value` longtext NOT NULL,
`string_package_id` BIGINT unsigned NULL,
`location` BIGINT unsigned NULL,
`wrap_tag` VARCHAR( 16 ) NOT NULL,
`type` VARCHAR(40) NOT NULL DEFAULT 'LINE',
`title` VARCHAR(160) NULL,
`status` TINYINT NOT NULL,
`gettext_context` TEXT NOT NULL,
`domain_name_context_md5` VARCHAR(32) NOT NULL,
`translation_priority` varchar(160) NOT NULL,
`word_count` int unsigned NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uc_domain_name_context_md5` (`domain_name_context_md5`),
KEY `language_context` (`language`, `context`),
KEY `icl_strings_name` (`name` ASC),
KEY `icl_strings_translation_priority` ( `translation_priority` ASC ),
KEY `context` ( `context` )
) {$charset_collate}
";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
$table_name = $wpdb->prefix . 'icl_string_translations';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`string_id` bigint(20) unsigned NOT NULL,
`language` varchar(10) NOT NULL,
`status` tinyint(4) NOT NULL,
`value` longtext NULL DEFAULT NULL,
`mo_string` longtext NULL DEFAULT NULL,
`translator_id` bigint(20) unsigned DEFAULT NULL,
`translation_service` varchar(16) DEFAULT '' NOT NULL,
`batch_id` int DEFAULT 0 NOT NULL,
`translation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `string_language` (`string_id`,`language`)
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
$table_name = $wpdb->prefix . 'icl_string_status';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`rid` BIGINT NOT NULL ,
`string_translation_id` BIGINT NOT NULL ,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`md5` VARCHAR( 32 ) NOT NULL,
INDEX ( `string_translation_id` )
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
$table_name = $wpdb->prefix . 'icl_string_positions';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`string_id` BIGINT NOT NULL ,
`kind` TINYINT,
`position_in_page` VARCHAR( 255 ) NOT NULL,
INDEX ( `string_id` )
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
// message status table
$table_name = $wpdb->prefix . 'icl_message_status';
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
if ( 0 !== strcasecmp( $found_table, $table_name ) ) {
$sql = "
CREATE TABLE IF NOT EXISTS `{$table_name}` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`rid` bigint(20) unsigned NOT NULL,
`object_id` bigint(20) unsigned NOT NULL,
`from_language` varchar(10) NOT NULL,
`to_language` varchar(10) NOT NULL,
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
`md5` varchar(32) NOT NULL,
`object_type` varchar(64) NOT NULL,
`status` smallint(6) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `rid` (`rid`),
KEY `object_id` (`object_id`)
) {$charset_collate}";
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
/* string translation - start */
$icl_translation_sql = "
CREATE TABLE IF NOT EXISTS {$wpdb->prefix}icl_core_status (
`id` BIGINT NOT NULL auto_increment,
`rid` BIGINT NOT NULL,
`module` VARCHAR( 16 ) NOT NULL ,
`origin` VARCHAR( 64 ) NOT NULL ,
`target` VARCHAR( 64 ) NOT NULL ,
`status` SMALLINT NOT NULL,
`tp_revision` INT NOT NULL DEFAULT 1,
`ts_status` TEXT NULL DEFAULT NULL,
PRIMARY KEY ( `id` ) ,
INDEX ( `rid` )
) {$charset_collate}
";
if ( $wpdb->query( $icl_translation_sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
$icl_translation_sql = "
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}icl_content_status` (
`rid` BIGINT NOT NULL ,
`nid` BIGINT NOT NULL ,
`timestamp` DATETIME NOT NULL ,
`md5` VARCHAR( 32 ) NOT NULL ,
PRIMARY KEY ( `rid` ) ,
INDEX ( `nid` )
) {$charset_collate}
";
if ( $wpdb->query( $icl_translation_sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
$icl_translation_sql = "
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}icl_node` (
`nid` BIGINT NOT NULL ,
`md5` VARCHAR( 32 ) NOT NULL ,
`links_fixed` TINYINT NOT NULL DEFAULT 0,
PRIMARY KEY ( `nid` )
) {$charset_collate}
";
if ( $wpdb->query( $icl_translation_sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
$icl_translation_sql = "
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}icl_reminders` (
`id` BIGINT NOT NULL ,
`message` TEXT NOT NULL ,
`url` TEXT NOT NULL ,
`can_delete` TINYINT NOT NULL ,
`show` TINYINT NOT NULL ,
PRIMARY KEY ( `id` )
) {$charset_collate}
";
if ( $wpdb->query( $icl_translation_sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
} catch ( Exception $e ) {
trigger_error( $e->getMessage(), E_USER_ERROR );
exit;
}
// don't set the new version if a multi-step upgrade is in progress
if ( ! defined( 'ICL_MULTI_STEP_UPGRADE' ) ) {
delete_option( 'icl_sitepress_version' );
add_option( 'icl_sitepress_version', ICL_SITEPRESS_VERSION, '', true );
}
$iclsettings = get_option( 'icl_sitepress_settings' );
if ( $iclsettings === false ) {
$short_v = implode( '.', array_slice( explode( '.', ICL_SITEPRESS_VERSION ), 0, 3 ) );
$settings = array(
'hide_upgrade_notice' => $short_v,
);
add_option( 'icl_sitepress_settings', $settings, '', true );
} else {
// reset ajx_health_flag
$iclsettings['ajx_health_checked'] = 0;
$iclsettings['just_reactivated'] = 1;
update_option( 'icl_sitepress_settings', $iclsettings );
}
// Set new caps for all administrator role
wpml_enable_capabilities();
repair_el_type_collate();
WPML_Media_Duplication_Setup::initialize_settings();
do_action( 'wpml_activated' );
}
function icl_sitepress_deactivate() {
wp_clear_scheduled_hook( 'update_wpml_config_index' );
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear();
do_action( 'wpml_deactivated' );
}
function icl_enable_capabilities() {
global $wp_roles;
if ( ! isset( $wp_roles ) || ! is_object( $wp_roles ) ) {
$wp_roles = new WP_Roles();
}
$iclsettings = get_option( 'icl_sitepress_settings' );
$icl_capabilities = icl_sitepress_get_capabilities();
// Set WPML capabilities to all roles with cap:"".
$roles = $wp_roles->get_names();
foreach ( $roles as $current_role => $role_name ) {
if ( isset( $wp_roles->roles[ $current_role ]['capabilities']['manage_options'] ) ) {
$role = get_role( $current_role );
if ( isset( $role ) && is_object( $role ) ) {
for ( $i = 0, $caps_limit = count( $icl_capabilities ); $i < $caps_limit; $i ++ ) {
if ( ! isset( $wp_roles->roles[ $current_role ]['capabilities'][ $icl_capabilities[ $i ] ] ) ) {
$role->add_cap( $icl_capabilities[ $i ] );
}
}
}
}
}
$user_admins = get_users(
array(
'role' => 'administrator',
)
);
if ( is_multisite() ) {
$super_admins = get_super_admins();
foreach ( $super_admins as $admin ) {
$super_admin = new WP_User( $admin );
if ( ! in_array( $super_admin, $user_admins, true ) ) {
$user_admins[] = $super_admin;
}
}
}
foreach ( $user_admins as $user ) {
if ( $user->exists() ) {
for ( $i = 0, $caps_limit = count( $icl_capabilities ); $i < $caps_limit; $i ++ ) {
$user->add_cap( $icl_capabilities[ $i ] );
}
}
}
$iclsettings['icl_capabilities_verified'] = true;
update_option( 'icl_sitepress_settings', $iclsettings );
}
/**
* Fires at plugins_loaded action, to call icl_enable_capabilities().
* https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-5695
*/
function wpml_enable_capabilities() {
global $sitepress_settings;
/**
* In case of multisite, in network activation,
* including of pluggable.php before muplugins_loaded event trigger errors -
* we postpone executing of icl_enable_capabilities to after plugins_loaded event.
*
* In other cases we include pluggable.php earlier than in wp-settings.php
*/
if ( ! did_action( 'muplugins_loaded' ) ) {
add_action( 'plugins_loaded', 'wpml_enable_capabilities' );
} else {
if ( ! function_exists( 'get_user_by' ) ) {
require ABSPATH . WPINC . '/pluggable.php';
}
icl_enable_capabilities();
$sitepress_settings = get_option( 'icl_sitepress_settings' );
}
}

View File

@@ -0,0 +1,386 @@
<?php
class SitePress_Setup {
static function setup_complete() {
global $sitepress;
return $sitepress->get_setting( 'setup_complete' );
}
static function languages_complete() {
return self::active_languages_complete() && self::languages_table_is_complete();
}
private static function active_languages_complete() {
static $result = null;
if ( $result === null ) {
global $sitepress;
$result = $sitepress && 1 < count( $sitepress->get_active_languages() );
}
return $result;
}
/**
* @return array
*/
private static function get_languages_codes() {
static $languages_codes = array();
if ( ! $languages_codes ) {
$languages_codes = icl_get_languages_codes();
}
return $languages_codes;
}
/**
* @return array
*/
private static function get_languages_names() {
static $languages_names = array();
if ( ! $languages_names ) {
$languages_names = icl_get_languages_names();
}
return $languages_names;
}
private static function get_languages_names_count() {
return count( self::get_languages_names() );
}
static function get_charset_collate() {
static $charset_collate = null;
if ( $charset_collate == null ) {
$charset_collate = '';
global $wpdb;
if ( method_exists( $wpdb, 'has_cap' ) && $wpdb->has_cap( 'collation' ) ) {
$schema = wpml_get_upgrade_schema();
$charset = $schema->get_default_charset();
$collate = $schema->get_default_collate();
if ( $charset ) {
$charset_collate = "DEFAULT CHARACTER SET $charset";
}
if ( $collate ) {
$charset_collate .= " COLLATE $collate";
}
}
}
return $charset_collate;
}
private static function create_languages() {
$sql = "( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`code` VARCHAR( 7 ) NOT NULL ,
`english_name` VARCHAR( 128 ) NOT NULL ,
`major` TINYINT NOT NULL DEFAULT '0',
`active` TINYINT NOT NULL ,
`default_locale` VARCHAR( 35 ),
`tag` VARCHAR( 35 ),
`encode_url` TINYINT( 1 ) NOT NULL DEFAULT 0,
`country` VARCHAR(10) NULL DEFAULT NULL,
UNIQUE KEY `code` (`code`),
UNIQUE KEY `english_name` (`english_name`)
) ";
return self::create_table( 'icl_languages', $sql );
}
static function languages_table_is_complete() {
global $wpdb;
$table_name = $wpdb->prefix . 'icl_languages';
$sql = "SELECT count(id) FROM {$table_name}";
$records_count = $wpdb->get_var( $sql );
$languages_names_count = self::get_languages_names_count();
if ( $records_count < $languages_names_count ) {
return false;
}
$languages_codes = self::get_languages_codes();
$language_pairs = self::get_language_translations();
foreach ( self::get_languages_names() as $lang => $val ) {
foreach ( $val['tr'] as $k => $display ) {
$k = self::fix_language_name( $k );
$code = $languages_codes[ $lang ];
if ( ! array_key_exists( $code, $language_pairs ) || ! in_array( $languages_codes[ $k ], $language_pairs[ $code ], true ) ) {
return false;
}
}
}
return true;
}
/**
* @param string $language_name
*
* @return string
*/
protected static function fix_language_name( $language_name ) {
if ( strpos( $language_name, 'Norwegian Bokm' ) === 0 ) {
$language_name = 'Norwegian Bokmål';
}
return $language_name;
}
private static function get_language_translations() {
$result = array();
global $wpdb;
$table_name = $wpdb->prefix . 'icl_languages_translations';
$sql = "SELECT language_code, display_language_code FROM {$table_name}";
$rowset = $wpdb->get_results( $sql );
if ( is_array( $rowset ) ) {
foreach ( $rowset as $row ) {
$result[ $row->language_code ][] = $row->display_language_code;
}
}
return $result;
}
static function fill_languages() {
global $wpdb, $sitepress;
$languages_codes = icl_get_languages_codes();
$lang_locales = icl_get_languages_locales();
$table_name = $wpdb->prefix . 'icl_languages';
if ( ! self::create_languages() ) {
return false;
}
if ( ! self::languages_table_is_complete() ) {
// First truncate the table
$active_languages = ( $sitepress !== null
&& $sitepress->is_setup_complete() ) ? $sitepress->get_active_languages() : array();
$wpdb->hide_errors();
$sql = 'TRUNCATE ' . $table_name;
$truncate_result = $wpdb->query( $sql );
$wpdb->show_errors();
if ( false !== $truncate_result ) {
foreach ( self::get_languages_names() as $key => $val ) {
$language_code = $languages_codes[ $key ];
$default_locale = isset( $lang_locales[ $language_code ] ) ? $lang_locales[ $language_code ] : '';
$language_tag = strtolower( str_replace( '_', '-', $language_code ) );
$args = array(
'english_name' => $key,
'code' => $language_code,
'major' => $val['major'],
'active' => isset( $active_languages[ $language_code ] ) ? 1 : 0,
'default_locale' => $default_locale,
'tag' => $language_tag,
);
if ( $wpdb->insert( $table_name, $args ) === false ) {
return false;
}
}
}
}
return true;
}
private static function create_languages_translations() {
$sql = '(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`language_code` VARCHAR( 7 ) NOT NULL ,
`display_language_code` VARCHAR( 7 ) NOT NULL ,
`name` VARCHAR( 255 ) NOT NULL,
UNIQUE(`language_code`, `display_language_code`)
)';
return self::create_table( 'icl_languages_translations', $sql );
}
static function fill_languages_translations() {
global $wpdb;
$languages_codes = icl_get_languages_codes();
$table_name = $wpdb->prefix . 'icl_languages_translations';
if ( ! self::create_languages_translations() ) {
return false;
}
if ( ! self::languages_table_is_complete() ) {
// First truncate the table
$wpdb->hide_errors();
$sql = 'TRUNCATE ' . $table_name;
$truncate_result = $wpdb->query( $sql );
$wpdb->show_errors();
if ( false !== $truncate_result ) {
$index = 1;
$insert_sql_parts = array();
$languages = self::get_languages_names();
if ( $languages ) {
foreach ( $languages as $lang => $val ) {
foreach ( $val['tr'] as $k => $display ) {
$k = self::fix_language_name( $k );
if ( ! trim( $display ) ) {
$display = $lang;
}
$inserts_language_data = array(
'id' => $index,
'language_code' => $languages_codes[ $lang ],
'display_language_code' => $languages_codes[ $k ],
'name' => $display,
);
$insert_sql_parts[] = $wpdb->prepare( '(%d, %s, %s, %s)', $inserts_language_data );
$index ++;
}
}
$insert_sql = implode( ",\n", $insert_sql_parts );
$insert_sql = "INSERT INTO {$table_name} (id, language_code, display_language_code, name) VALUES "
. $insert_sql;
if ( $wpdb->query( $insert_sql ) === false ) {
return false;
}
}
}
}
return true;
}
private static function create_table( $name, $table_sql ) {
global $wpdb;
$table_name = $wpdb->prefix . $name;
$found_table = (string) $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
return 0 === strcasecmp( $found_table, $table_name )
|| ( $wpdb->query(
sprintf( 'CREATE TABLE IF NOT EXISTS `%s` ', $table_name )
. $table_sql . ' '
. self::get_charset_collate()
) !== false );
}
private static function create_flags() {
$sql = "(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`lang_code` VARCHAR( 10 ) NOT NULL ,
`flag` VARCHAR( 32 ) NOT NULL ,
`from_template` TINYINT NOT NULL DEFAULT '0',
UNIQUE (`lang_code`)
)";
return self::create_table( 'icl_flags', $sql );
}
public static function fill_flags() {
global $wpdb;
if ( self::create_flags() === false ) {
return;
}
$codes = $wpdb->get_col( "SELECT code FROM {$wpdb->prefix}icl_languages" );
foreach ( $codes as $code ) {
if ( ! $code || $wpdb->get_var(
$wpdb->prepare(
"SELECT lang_code
FROM {$wpdb->prefix}icl_flags
WHERE lang_code = %s ",
$code
)
)
) {
continue;
}
$code_parts = explode( '-', $code );
$file = 'nil.png';
if ( file_exists( WPML_PLUGIN_PATH . '/res/flags/' . $code . '.png' ) ) {
$file = $code . '.png';
} elseif ( file_exists( WPML_PLUGIN_PATH . '/res/flags/' . $code_parts[0] . '.png' ) ) {
$file = $code_parts[0] . '.png';
}
$wpdb->insert(
$wpdb->prefix . 'icl_flags',
array(
'lang_code' => $code,
'flag' => $file,
'from_template' => 0,
)
);
}
}
public static function insert_default_category( $lang_code ) {
global $sitepress;
$default_language = $sitepress->get_default_language();
if ( $lang_code === $default_language ) {
return;
}
// Get default categories.
$default_categories = $sitepress->get_setting( 'default_categories', array() );
if ( isset( $default_categories[ $lang_code ] ) ) {
return;
}
$sitepress->switch_locale( $lang_code );
$tr_cat = __( 'Uncategorized', 'sitepress' );
$tr_cat = $tr_cat === 'Uncategorized' && $lang_code !== 'en' ? 'Uncategorized @' . $lang_code : $tr_cat;
$tr_term = term_exists( $tr_cat, 'category' );
$sitepress->switch_locale();
// check if the term already exists
if ( $tr_term !== 0 && $tr_term !== null ) {
$tmp = get_term( $tr_term['term_taxonomy_id'], 'category', ARRAY_A );
} else {
$tmp = wp_insert_term( $tr_cat, 'category' );
}
// add it to settings['default_categories']
$default_categories[ $lang_code ] = $tmp['term_taxonomy_id'];
$sitepress->set_default_categories( $default_categories );
// update translations table
$default_category_trid = $sitepress->get_element_trid(
get_option( 'default_category' ),
'tax_category'
);
$sitepress->set_element_language_details(
$tmp['term_taxonomy_id'],
'tax_category',
$default_category_trid,
$lang_code,
$default_language
);
}
}

View File

@@ -0,0 +1,462 @@
<?php
class WPML_Installation extends WPML_WPDB_And_SP_User {
const WPML_START_VERSION_KEY = 'wpml_start_version';
public static function getStartVersion() {
return get_option( self::WPML_START_VERSION_KEY, '0.0.0' );
}
function go_to_setup1() {
// Reverse $this->prepopulate_translations()
$this->wpdb->query( "TRUNCATE TABLE {$this->wpdb->prefix}icl_translations" );
// Unset or reset sitepress settings
$settings = $this->sitepress->get_settings();
unset(
$settings['default_categories'],
$settings['default_language'],
$settings['setup_wizard_step']
);
$settings['existing_content_language_verified'] = 0;
$settings['active_languages'] = array();
$GLOBALS['sitepress_settings']['existing_content_language_verified'] = $settings['existing_content_language_verified'];
update_option( 'icl_sitepress_settings', $settings );
// Reverse $this->maybe_set_locale()
$this->wpdb->query( "TRUNCATE TABLE {$this->wpdb->prefix}icl_locale_map" );
// Make sure no language is active
$this->wpdb->update( $this->wpdb->prefix . 'icl_languages', array( 'active' => 0 ), array( 'active' => 1 ) );
}
/**
* Sets the locale in the icl_locale_map if it has not yet been set
*
* @param string $initial_language_code
*/
private function maybe_set_locale( $initial_language_code ) {
$q = "SELECT code FROM {$this->wpdb->prefix}icl_locale_map WHERE code=%s";
$q_prepared = $this->wpdb->prepare( $q, $initial_language_code );
if ( ! $this->wpdb->get_var( $q_prepared ) ) {
$q = "SELECT default_locale FROM {$this->wpdb->prefix}icl_languages WHERE code=%s";
$q_prepared = $this->wpdb->prepare( $q, $initial_language_code );
$default_locale = $this->wpdb->get_var( $q_prepared );
if ( $default_locale ) {
$this->wpdb->insert(
$this->wpdb->prefix . 'icl_locale_map',
array( 'code' => $initial_language_code, 'locale' => $default_locale )
);
}
}
}
public function finish_step2( $active_languages ) {
return $this->set_active_languages( $active_languages );
}
public function set_active_languages( $arr ) {
$tmp = $this->sanitize_language_input( $arr );
if ( (bool) $tmp === false ) {
return false;
}
foreach ( $tmp as $code ) {
$default_locale_prepared = $this->wpdb->prepare(
"SELECT default_locale FROM {$this->wpdb->prefix}icl_languages WHERE code= %s LIMIT 1",
$code
);
$default_locale = $this->wpdb->get_var( $default_locale_prepared );
if ( $default_locale ) {
$code_exists_prepared = $this->wpdb->prepare(
"SELECT code FROM {$this->wpdb->prefix}icl_locale_map WHERE code = %s LIMIT 1",
$code
);
$code_exists = $this->wpdb->get_var( $code_exists_prepared );
if ( $code_exists ) {
$this->wpdb->update(
$this->wpdb->prefix . 'icl_locale_map',
array( 'locale' => $default_locale ),
array( 'code' => $code )
);
} else {
$this->wpdb->insert(
$this->wpdb->prefix . 'icl_locale_map',
array( 'code' => $code, 'locale' => $default_locale )
);
}
}
SitePress_Setup::insert_default_category( $code );
}
$this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_languages SET active = 1 WHERE code IN (" . wpml_prepare_in( $tmp ) . " ) "
);
$this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_languages SET active = 0 WHERE code NOT IN (" . wpml_prepare_in( $tmp ) . " ) "
);
$this->updated_active_languages();
return true;
}
private function sanitize_language_input( $lang_codes ) {
$languages = $this->sitepress->get_languages( false, false, true );
$sanitized_codes = array();
$lang_codes = array_filter( array_unique( $lang_codes ) );
foreach ( $lang_codes as $code ) {
$code = esc_sql( trim( $code ) );
if ( isset( $languages[ $code ] ) ) {
$sanitized_codes[] = $code;
}
}
return $sanitized_codes;
}
public function finish_installation( ) {
icl_set_setting( 'store_frontend_cookie', 1 );
icl_set_setting( 'setup_complete', 1, true );
update_option( self::WPML_START_VERSION_KEY, ICL_SITEPRESS_VERSION );
do_action( 'wpml_setup_completed' );
}
public function store_site_key( $site_key = false ) {
if ( $site_key ) {
icl_set_setting( 'site_key', $site_key, true );
}
}
public function finish_step3() {
$this->maybe_move_setup( 4 );
}
private function maybe_move_setup( $step ) {
$setup_complete = icl_get_setting( 'setup_complete' );
if ( empty( $setup_complete ) ) {
icl_set_setting( 'setup_wizard_step', $step, true );
}
}
private function updated_active_languages() {
wp_cache_init();
icl_cache_clear();
$this->refresh_active_lang_cache( wpml_get_setting_filter( false, 'default_language' ), true );
$this->update_languages_order();
wpml_reload_active_languages_setting( true );
$active_langs = $this->sitepress->get_active_languages( true );
$this->maybe_move_setup( 3 );
if ( count( $active_langs ) > 1 ) {
icl_set_setting( 'dont_show_help_admin_notice', true );
}
}
public function finish_step1( $initial_language_code ) {
$this->set_initial_default_category( $initial_language_code );
$this->prepopulate_translations( $initial_language_code );
$this->update_active_language( $initial_language_code );
$admin_language = $this->get_admin_language( $initial_language_code );
$this->maybe_set_locale( $admin_language );
icl_set_setting( 'existing_content_language_verified', 1 );
icl_set_setting( 'default_language', $initial_language_code );
icl_set_setting( 'setup_wizard_step', 2 );
icl_save_settings();
wp_cache_flush();
$this->refresh_active_lang_cache( $initial_language_code );
add_filter( 'locale', array( $this->sitepress, 'locale_filter' ), 10, 1 );
if ( ! array_key_exists( 'wp_styles', $GLOBALS ) || ! $GLOBALS['wp_styles'] ) {
wp_styles();
}
if ( $this->sitepress->is_rtl( $admin_language ) ) {
$GLOBALS['text_direction'] = 'rtl';
$GLOBALS['wp_styles']->text_direction = 'rtl';
} else {
$GLOBALS['text_direction'] = 'ltr';
$GLOBALS['wp_styles']->text_direction = 'ltr';
}
$GLOBALS['wp_locale'] = new WP_Locale();
$GLOBALS['locale'] = $this->sitepress->get_locale( $admin_language );
do_action( 'icl_initial_language_set' );
}
/**
* @param string $initial_language_code
*
* @return string
*/
private function get_admin_language( $initial_language_code ) {
$user_locale = get_user_meta( get_current_user_id(), 'locale', true );
if ( $user_locale ) {
$lang = $this->sitepress->get_language_code_from_locale( $user_locale );
if ( $lang ) {
return $lang;
}
}
return $initial_language_code;
}
private function set_initial_default_category( $initial_lang ) {
$blog_default_cat = get_option( 'default_category' );
$blog_default_cat_tax_id = $this->wpdb->get_var(
$this->wpdb->prepare(
" SELECT term_taxonomy_id
FROM {$this->wpdb->term_taxonomy}
WHERE term_id=%d
AND taxonomy='category'",
$blog_default_cat
)
);
if ($initial_lang !== 'en') {
$this->rename_default_category_of_initial_language( $initial_lang, $blog_default_cat );
}
icl_set_setting( 'default_categories', array( $initial_lang => $blog_default_cat_tax_id ), true );
}
private function rename_default_category_of_initial_language( $initial_lang, $category_id ) {
global $sitepress;
$sitepress->switch_locale( $initial_lang );
$tr_cat = __( 'Uncategorized', 'sitepress' );
$tr_cat = $tr_cat === 'Uncategorized' ? 'Uncategorized @' . $initial_lang : $tr_cat;
$sitepress->switch_locale();
wp_update_term( $category_id, 'category', array(
'name' => $tr_cat,
'slug' => sanitize_title( $tr_cat ),
) );
}
/**
* @param string $display_language
* @param bool $active_only
* @param bool $major_first
* @param string $order_by
*
* @return array<string,\stdClass>
*/
public function refresh_active_lang_cache( $display_language, $active_only = false, $major_first = false, $order_by = 'english_name' ) {
$active_snippet = $active_only ? " l.active = 1 AND " : "";
$res_query
= "
SELECT
l.code,
l.id,
english_name,
nt.name AS native_name,
major,
active,
default_locale,
encode_url,
tag,
lt.name AS display_name
FROM {$this->wpdb->prefix}icl_languages l
JOIN {$this->wpdb->prefix}icl_languages_translations nt
ON ( nt.language_code = l.code AND nt.display_language_code = l.code )
LEFT OUTER JOIN {$this->wpdb->prefix}icl_languages_translations lt ON l.code=lt.language_code
WHERE {$active_snippet}
( lt.display_language_code = %s
OR (lt.display_language_code = 'en'
AND NOT EXISTS ( SELECT *
FROM {$this->wpdb->prefix}icl_languages_translations ls
WHERE ls.language_code = l.code
AND ls.display_language_code = %s ) ) )
GROUP BY l.code";
$order_by_fields = array();
if ( $major_first ) {
$order_by_fields[] = 'major DESC';
}
$order_by_fields[] = ( $order_by ? $order_by : 'english_name' ) . ' ASC';
$res_query .= PHP_EOL . 'ORDER BY ' . implode( ', ', $order_by_fields );
$res_query_prepared = $this->wpdb->prepare( $res_query, $display_language, $display_language );
$res = $this->wpdb->get_results( $res_query_prepared, ARRAY_A );
$languages = array();
$icl_cache = $this->sitepress->get_language_name_cache();
foreach ( (array) $res as $r ) {
$languages[ $r[ 'code' ] ] = $r;
$icl_cache->set( 'language_details_' . $r['code'] . $display_language, $r );
}
if ( $active_only ) {
$icl_cache->set( 'in_language_' . $display_language . '_' . $major_first . '_' . $order_by, $languages );
} else {
$icl_cache->set( 'all_language_' . $display_language . '_' . $major_first . '_' . $order_by, $languages );
}
$icl_cache->save_cache_if_required();
return $languages;
}
private function update_languages_order() {
$needs_update = false;
$current_order = $this->sitepress->get_setting( 'languages_order', array() );
if ( ! is_array( $current_order ) ) {
$current_order = array();
}
$languages = $this->sitepress->get_languages( false, false, true );
$new_order = $current_order;
foreach ( $languages as $language_code => $language ) {
if ( ! in_array( $language_code, $new_order ) && '1' === $language['active'] ) {
$new_order[] = $language_code;
$needs_update = true;
}
if ( in_array( $language_code, $new_order ) && '1' !== $language['active'] ) {
$new_order = array_diff( $new_order, array( $language_code ) );
$needs_update = true;
}
}
if ( $needs_update ) {
$new_order = array_values( $new_order );
$this->sitepress->set_setting( 'languages_order', $new_order, true );
}
}
private function prepopulate_translations( $lang ) {
$existing_lang_verified = icl_get_setting( 'existing_content_language_verified' );
if ( ! empty( $existing_lang_verified ) ) {
return;
}
icl_cache_clear();
// case of icl_sitepress_settings accidentally lost
// if there's at least one translation do not initialize the languages for elements
$one_translation = $this->wpdb->get_var(
$this->wpdb->prepare(
"SELECT translation_id FROM {$this->wpdb->prefix}icl_translations WHERE language_code<>%s",
$lang
)
);
if ( $one_translation ) {
return;
}
$this->wpdb->query( "TRUNCATE TABLE {$this->wpdb->prefix}icl_translations" );
$this->wpdb->query(
$this->wpdb->prepare(
"
INSERT INTO {$this->wpdb->prefix}icl_translations(element_type, element_id, trid, language_code, source_language_code)
SELECT CONCAT('post_',post_type), ID, ID, %s, NULL FROM {$this->wpdb->posts} WHERE post_status IN ('draft', 'publish','schedule','future','private', 'pending')
",
$lang
)
);
$maxtrid = 1 + (int) $this->wpdb->get_var( "SELECT MAX(trid) FROM {$this->wpdb->prefix}icl_translations" );
global $wp_taxonomies;
$taxonomies = array_keys( (array) $wp_taxonomies );
foreach ( $taxonomies as $tax ) {
$element_type = 'tax_' . $tax;
$insert_query
= "
INSERT INTO {$this->wpdb->prefix}icl_translations(element_type, element_id, trid, language_code, source_language_code)
SELECT %s, term_taxonomy_id, %d+term_taxonomy_id, %s, NULL FROM {$this->wpdb->term_taxonomy} WHERE taxonomy = %s
";
$insert_prepare = $this->wpdb->prepare( $insert_query, array( $element_type, $maxtrid, $lang, $tax ) );
$this->wpdb->query( $insert_prepare );
$maxtrid = 1 + (int) $this->wpdb->get_var( "SELECT MAX(trid) FROM {$this->wpdb->prefix}icl_translations" );
}
$this->wpdb->query(
$this->wpdb->prepare(
"
INSERT INTO {$this->wpdb->prefix}icl_translations(element_type, element_id, trid, language_code, source_language_code)
SELECT 'comment', comment_ID, {$maxtrid}+comment_ID, %s, NULL FROM {$this->wpdb->comments}
",
$lang
)
);
}
public function update_active_language( $lang ) {
$this->wpdb->update( $this->wpdb->prefix . 'icl_languages', array( 'active' => '1' ), array( 'code' => $lang ) );
}
function reset_language_data() {
global $sitepress, $wpdb;
$active = $this->wpdb->get_col( "SELECT code FROM {$this->wpdb->prefix}icl_languages WHERE active = 1" );
$this->wpdb->query( "TRUNCATE TABLE `{$this->wpdb->prefix}icl_languages`" );
SitePress_Setup::fill_languages();
$this->wpdb->query( "TRUNCATE TABLE `{$this->wpdb->prefix}icl_languages_translations`" );
SitePress_Setup::fill_languages_translations();
$this->wpdb->query( "TRUNCATE TABLE `{$this->wpdb->prefix}icl_flags`" );
SitePress_Setup::fill_flags();
//restore active
$this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_languages SET active=1 WHERE code IN(" . wpml_prepare_in( $active ) . ")"
);
$this->wpdb->update( $this->wpdb->prefix . 'icl_flags', array( 'from_template' => 0 ), null );
$codes = $this->wpdb->get_col( "SELECT code FROM {$this->wpdb->prefix}icl_languages" );
foreach ( $codes as $code ) {
if ( ! $code || $this->wpdb->get_var(
$this->wpdb->prepare( "SELECT lang_code FROM {$this->wpdb->prefix}icl_flags WHERE lang_code = %s", $code )
)
) {
continue;
}
if ( ! file_exists( WPML_PLUGIN_PATH . '/res/flags/' . $code . '.png' ) ) {
$file = 'nil.png';
} else {
$file = $code . '.png';
}
$this->wpdb->insert(
$this->wpdb->prefix . 'icl_flags',
array( 'lang_code' => $code, 'flag' => $file, 'from_template' => 0 )
);
}
$last_default_language = $this->sitepress !== null ? $this->sitepress->get_default_language() : 'en';
if ( ! in_array( $last_default_language, $codes ) ) {
$last_active_languages = $this->sitepress->get_active_languages();
foreach ( $last_active_languages as $code => $last_active_language ) {
if ( in_array( $code, $codes ) ) {
$this->sitepress->set_default_language( $code );
break;
}
}
}
$language_pair_records = new WPML_Language_Pair_Records( $wpdb, new WPML_Language_Records( $wpdb ) );
$users = get_users( [ 'fields' => [ 'ID' ] ] );
foreach ( $users as $user ) {
$language_pair_records->remove_invalid_language_pairs( $user->ID );
}
icl_cache_clear();
$sitepress->get_translations_cache()->clear();
$sitepress->clear_flags_cache();
$sitepress->get_language_name_cache()->clear();
}
}

View File

@@ -0,0 +1,25 @@
<?php
class WPML_Media_Duplication_Setup {
const MEDIA_SETTINGS_OPTION_KEY = '_wpml_media';
public static function initialize_settings() {
if ( ! get_option( self::MEDIA_SETTINGS_OPTION_KEY, [] ) ) {
$settings = [
'new_content_settings' => [
'always_translate_media' => true,
'duplicate_media' => true,
'duplicate_featured' => true,
],
'translate_media_library_texts' => false,
];
update_option( self::MEDIA_SETTINGS_OPTION_KEY, $settings );
}
}
public static function isTranslateMediaLibraryTextsEnabled() {
$settings = get_option( self::MEDIA_SETTINGS_OPTION_KEY, [] );
return \WPML\FP\Obj::propOr(false, 'translate_media_library_texts', $settings);
}
}

View File

@@ -0,0 +1,315 @@
<?php
class WPML_Settings_Helper {
const KEY_CPT_UNLOCK_OPTION = 'custom_posts_unlocked_option';
const KEY_TAXONOMY_UNLOCK_OPTION = 'taxonomies_unlocked_option';
/** @var SitePress */
protected $sitepress;
/** @var WPML_Post_Translation */
protected $post_translation;
/**
* @var WPML_Settings_Filters
*/
private $filters;
/**
* @param WPML_Post_Translation $post_translation
* @param SitePress $sitepress
*/
public function __construct( WPML_Post_Translation $post_translation, SitePress $sitepress ) {
$this->sitepress = $sitepress;
$this->post_translation = $post_translation;
}
/**
* @return WPML_Settings_Filters
*/
private function get_filters() {
if ( ! $this->filters ) {
$this->filters = new WPML_Settings_Filters();
}
return $this->filters;
}
function set_post_type_translatable( $post_type ) {
$this->set_post_type_translate_mode( $post_type, WPML_CONTENT_TYPE_TRANSLATE );
}
function set_post_type_display_as_translated( $post_type ) {
$this->set_post_type_translate_mode( $post_type, WPML_CONTENT_TYPE_DISPLAY_AS_IF_TRANSLATED );
}
function set_post_type_not_translatable( $post_type ) {
$sync_settings = $this->sitepress->get_setting( 'custom_posts_sync_option', array() );
if ( isset( $sync_settings[ $post_type ] ) ) {
unset( $sync_settings[ $post_type ] );
}
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'custom_posts_sync_option', $sync_settings, true );
}
private function set_post_type_translate_mode( $post_type, $mode ) {
$sync_settings = $this->sitepress->get_setting( 'custom_posts_sync_option', array() );
$sync_settings[ $post_type ] = $mode;
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'custom_posts_sync_option', $sync_settings, true );
$this->sitepress->verify_post_translations( $post_type );
$this->post_translation->reload();
}
function set_taxonomy_translatable( $taxonomy ) {
$this->set_taxonomy_translatable_mode( $taxonomy, WPML_CONTENT_TYPE_TRANSLATE );
}
function set_taxonomy_display_as_translated( $taxonomy ) {
$this->set_taxonomy_translatable_mode( $taxonomy, WPML_CONTENT_TYPE_DISPLAY_AS_IF_TRANSLATED );
}
function set_taxonomy_translatable_mode( $taxonomy, $mode ) {
$sync_settings = $this->sitepress->get_setting( 'taxonomies_sync_option', array() );
$sync_settings[ $taxonomy ] = $mode;
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'taxonomies_sync_option', $sync_settings, true );
$this->sitepress->verify_taxonomy_translations( $taxonomy );
}
function set_taxonomy_not_translatable( $taxonomy ) {
$sync_settings = $this->sitepress->get_setting( 'taxonomies_sync_option', array() );
if ( isset( $sync_settings[ $taxonomy ] ) ) {
unset( $sync_settings[ $taxonomy ] );
}
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'taxonomies_sync_option', $sync_settings, true );
}
function set_post_type_translation_unlocked_option( $post_type, $unlocked = true ) {
$unlocked_settings = $this->sitepress->get_setting( 'custom_posts_unlocked_option', array() );
$unlocked_settings[ $post_type ] = $unlocked ? 1 : 0;
$this->sitepress->set_setting( 'custom_posts_unlocked_option', $unlocked_settings, true );
}
function set_taxonomy_translation_unlocked_option( $taxonomy, $unlocked = true ) {
$unlocked_settings = $this->sitepress->get_setting( 'taxonomies_unlocked_option', array() );
$unlocked_settings[ $taxonomy ] = $unlocked ? 1 : 0;
$this->sitepress->set_setting( 'taxonomies_unlocked_option', $unlocked_settings, true );
}
/**
* @deprecated use the action `wpml_activate_slug_translation` instead
* or `WPML_ST_Post_Slug_Translation_Settings` instead (on ST side)
*
* @param string $post_type
*/
function activate_slug_translation( $post_type ) {
$slug_settings = $this->sitepress->get_setting( 'posts_slug_translation', array() );
$slug_settings['types'] = isset( $slug_settings['types'] )
? $slug_settings['types'] : array();
$slug_settings['types'][ $post_type ] = 1;
/** @deprected key `on`, use option `wpml_base_slug_translation` instead */
$slug_settings['on'] = 1;
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'posts_slug_translation', $slug_settings, true );
update_option( 'wpml_base_slug_translation', 1 );
}
/**
* @deprecated use `WPML_ST_Post_Slug_Translation_Settings` instead (on ST side)
*
* @param string $post_type
*/
function deactivate_slug_translation( $post_type ) {
$slug_settings = $this->sitepress->get_setting( 'posts_slug_translation', array() );
if ( isset( $slug_settings['types'][ $post_type ] ) ) {
unset( $slug_settings['types'][ $post_type ] );
}
$this->clear_ls_languages_cache();
$this->sitepress->set_setting( 'posts_slug_translation', $slug_settings, true );
}
/**
* @param array[] $taxs_obj_type
*
* @see \WPML_Config::maybe_add_filter
*
* @return array
*/
function _override_get_translatable_taxonomies( $taxs_obj_type ) {
global $wp_taxonomies;
$taxs = $taxs_obj_type['taxs'];
$object_type = $taxs_obj_type['object_type'];
foreach ( $taxs as $k => $tax ) {
if ( ! $this->sitepress->is_translated_taxonomy( $tax ) ) {
unset( $taxs[ $k ] );
}
}
$tm_settings = $this->sitepress->get_setting( 'translation-management', array() );
foreach ( $tm_settings['taxonomies_readonly_config'] as $tx => $translate ) {
if ( $translate
&& ! in_array( $tx, $taxs )
&& isset( $wp_taxonomies[ $tx ] )
&& in_array( $object_type, $wp_taxonomies[ $tx ]->object_type )
) {
$taxs[] = $tx;
}
}
$ret = array(
'taxs' => $taxs,
'object_type' => $taxs_obj_type['object_type'],
);
return $ret;
}
/**
* @param array[] $types
*
* @see \WPML_Config::maybe_add_filter
*
* @return array
*/
function _override_get_translatable_documents( $types ) {
$tm_settings = $this->sitepress->get_setting( 'translation-management', array() );
$cpt_unlocked_options = $this->sitepress->get_setting( 'custom_posts_unlocked_option', array() );
foreach ( $types as $k => $type ) {
if ( isset( $tm_settings['custom-types_readonly_config'][ $k ] )
&& ! $tm_settings['custom-types_readonly_config'][ $k ]
) {
unset( $types[ $k ] );
}
}
$types = $this->get_filters()->get_translatable_documents( $types, $tm_settings['custom-types_readonly_config'], $cpt_unlocked_options );
return $types;
}
/**
* Updates the custom post type translation settings with new settings.
*
* @param array $new_options
*
* @uses \SitePress::get_setting
* @uses \SitePress::save_settings
*
* @return array new custom post type settings after the update
*/
function update_cpt_sync_settings( array $new_options ) {
$cpt_sync_options = $this->sitepress->get_setting( WPML_Element_Sync_Settings_Factory::KEY_POST_SYNC_OPTION, array() );
$cpt_sync_options = array_merge( $cpt_sync_options, $new_options );
$new_options = array_filter( $new_options );
$this->clear_ls_languages_cache();
do_action( 'wpml_verify_post_translations', $new_options );
do_action( 'wpml_save_cpt_sync_settings' );
$this->sitepress->set_setting( WPML_Element_Sync_Settings_Factory::KEY_POST_SYNC_OPTION, $cpt_sync_options, true );
return $cpt_sync_options;
}
/**
* Updates the taxonomy type translation settings with new settings.
*
* @param array $new_options
*
* @uses \SitePress::get_setting
* @uses \SitePress::save_settings
*
* @return array new taxonomy type settings after the update
*/
function update_taxonomy_sync_settings( array $new_options ) {
$taxonomy_sync_options = $this->sitepress->get_setting( WPML_Element_Sync_Settings_Factory::KEY_TAX_SYNC_OPTION, [] );
$changed_sync_options = array_diff_assoc( $new_options, $taxonomy_sync_options );
$taxonomy_sync_options = array_merge( $taxonomy_sync_options, $new_options );
foreach ( $changed_sync_options as $taxonomy_name => $taxonomy_sync_option ) {
$this->sitepress->verify_taxonomy_translations( $taxonomy_name );
}
$this->clear_ls_languages_cache();
do_action( 'wpml_save_taxonomy_sync_settings' );
$this->sitepress->set_setting( WPML_Element_Sync_Settings_Factory::KEY_TAX_SYNC_OPTION, $taxonomy_sync_options, true );
return $taxonomy_sync_options;
}
/**
* Updates the custom post type unlocked settings with new settings.
*
* @param array $unlock_options
*
* @uses \SitePress::get_setting
* @uses \SitePress::save_settings
*
* @return array new custom post type unlocked settings after the update
*/
function update_cpt_unlocked_settings( array $unlock_options ) {
return $this->update_unlocked_settings( $unlock_options, self::KEY_CPT_UNLOCK_OPTION );
}
/**
* Updates the taxonomy type unlocked settings with new settings.
*
* @param array $unlock_options
*
* @uses \SitePress::get_setting
* @uses \SitePress::save_settings
*
* @return array new taxonomy type unlocked settings after the update
*/
function update_taxonomy_unlocked_settings( array $unlock_options ) {
return $this->update_unlocked_settings( $unlock_options, self::KEY_TAXONOMY_UNLOCK_OPTION );
}
/**
* @param array $unlock_options
* @param string $setting_key
*
* @return array
*/
private function update_unlocked_settings( array $unlock_options, $setting_key ) {
$cpt_unlock_options = $this->sitepress->get_setting( $setting_key, array() );
$cpt_unlock_options = array_merge( $cpt_unlock_options, $unlock_options );
$this->sitepress->set_setting( $setting_key, $cpt_unlock_options, true );
return $cpt_unlock_options;
}
/**
* @param string $config_type
*/
function maybe_add_filter( $config_type ) {
if ( $config_type === 'taxonomies' ) {
add_filter(
'get_translatable_taxonomies',
array( $this, '_override_get_translatable_taxonomies' )
);
} elseif ( $config_type === 'custom-types' ) {
add_filter(
'get_translatable_documents',
array( $this, '_override_get_translatable_documents' )
);
}
}
private function clear_ls_languages_cache() {
$cache = new WPML_WP_Cache( 'ls_languages' );
$cache->flush_group_cache();
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* Class WPML_Nav_Menu_Actions
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Nav_Menu_Actions extends WPML_Full_Translation_API {
/**
* @param SitePress $sitepress
* @param wpdb $wpdb
* @param WPML_Post_Translation $post_translations
* @param WPML_Term_Translation $term_translations
*/
public function __construct( &$sitepress, &$wpdb, &$post_translations, &$term_translations ) {
parent::__construct( $sitepress, $wpdb, $post_translations, $term_translations );
add_action( 'wp_delete_nav_menu', array( $this, 'wp_delete_nav_menu' ) );
add_action( 'wp_create_nav_menu', array( $this, 'wp_update_nav_menu' ), 10, 2 );
add_action( 'wp_update_nav_menu', array( $this, 'wp_update_nav_menu' ), 10, 2 );
add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item' ), 10, 3 );
add_action( 'delete_post', array( $this, 'wp_delete_nav_menu_item' ) );
add_filter( 'pre_update_option_theme_mods_' . get_option( 'stylesheet' ), array( $this, 'pre_update_theme_mods_theme' ) );
if ( is_admin() ) {
add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ) );
}
}
public function wp_delete_nav_menu( $id ) {
$menu_id_tt = $this->wpdb->get_var(
$this->wpdb->prepare(
"SELECT term_taxonomy_id FROM {$this->wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu'",
$id
)
);
$update_args = array(
'element_id' => $menu_id_tt,
'element_type' => 'tax_nav_menu',
'context' => 'tax',
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$q = "DELETE FROM {$this->wpdb->prefix}icl_translations WHERE element_id=%d AND element_type='tax_nav_menu' LIMIT 1";
$q_prepared = $this->wpdb->prepare( $q, $menu_id_tt );
$this->wpdb->query( $q_prepared );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
function wp_update_nav_menu( $menu_id, $menu_data = null ) {
if ( $menu_data ) {
$trid = $this->get_trid_from_post_data();
$language_code = $this->get_save_lang( $menu_id );
$menu_id_tt = $this->wpdb->get_var(
$this->wpdb->prepare(
"SELECT term_taxonomy_id FROM {$this->wpdb->term_taxonomy} WHERE term_id=%d AND taxonomy='nav_menu' LIMIT 1",
$menu_id
)
);
$this->term_translations->reload();
$this->sitepress->set_element_language_details( $menu_id_tt, 'tax_nav_menu', $trid, $language_code );
}
}
function wp_update_nav_menu_item( $menu_id, $menu_item_db_id, $args ) {
$menu_lang = $this->term_translations->lang_code_by_termid( $menu_id );
$trid = $this->post_translations->get_element_trid( $menu_item_db_id );
if ( array_key_exists( 'menu-item-type', $args )
&& ( $args['menu-item-type'] === 'post_type' || $args['menu-item-type'] === 'taxonomy' )
&& array_key_exists( 'menu-item-object-id', $args )
&& $menu_id > 0
) {
$language_code_item = $args['menu-item-type'] === 'post_type'
? $this->post_translations->get_element_lang_code( $args['menu-item-object-id'] )
: $this->term_translations->lang_code_by_termid( $args['menu-item-object-id'] );
$language_code_item = $language_code_item ? $language_code_item : $this->sitepress->get_current_language();
if ( $language_code_item !== $menu_lang ) {
wp_remove_object_terms( (int) $menu_item_db_id, (int) $menu_id, 'nav_menu' );
}
}
$language_code = isset( $language_code_item ) && $language_code_item
? $language_code_item : ( $menu_lang ? $menu_lang : $this->sitepress->get_current_language() );
$this->sitepress->set_element_language_details( $menu_item_db_id, 'post_nav_menu_item', $trid, $language_code );
}
public function wp_delete_nav_menu_item( $menu_item_id ) {
$post = get_post( $menu_item_id );
if ( ! empty( $post->post_type ) && $post->post_type == 'nav_menu_item' ) {
$update_args = array(
'element_id' => $menu_item_id,
'element_type' => 'post_nav_menu_item',
'context' => 'post',
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$q = "DELETE FROM {$this->wpdb->prefix}icl_translations WHERE element_id=%d AND element_type='post_nav_menu_item' LIMIT 1";
$q_prepared = $this->wpdb->prepare( $q, $menu_item_id );
$this->wpdb->query( $q_prepared );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
}
public function pre_update_theme_mods_theme( $val ) {
$default_language = $this->sitepress->get_default_language();
$current_language = $this->sitepress->get_current_language();
if ( isset( $val['nav_menu_locations'] )
&& filter_input( INPUT_GET, 'action' ) === 'delete'
&& $current_language !== $default_language
) {
$val['nav_menu_locations'] = get_theme_mod( 'nav_menu_locations' );
}
if ( isset( $val['nav_menu_locations'] ) ) {
foreach ( (array) $val['nav_menu_locations'] as $k => $v ) {
if ( ! $v && $current_language !== $default_language ) {
$tl = get_theme_mod( 'nav_menu_locations' );
if ( isset( $tl[ $k ] ) ) {
$val['nav_menu_locations'][ $k ] = $tl[ $k ];
}
} else {
$val['nav_menu_locations'][ $k ] = icl_object_id(
$val['nav_menu_locations'][ $k ],
'nav_menu',
true,
$default_language
);
}
}
}
return $val;
}
public function theme_mod_nav_menu_locations( $theme_locations ) {
if ( is_admin() && (bool) $theme_locations === true ) {
$current_lang = $this->sitepress->get_current_language();
foreach ( (array) $theme_locations as $location => $menu_id ) {
$translated_menu_id = $this->term_translations->term_id_in( $menu_id, $current_lang );
if ( $translated_menu_id ) {
$theme_locations[ $location ] = $translated_menu_id;
}
}
}
return $theme_locations;
}
private function get_save_lang( $menu_id ) {
$language_code = isset( $_POST['icl_nav_menu_language'] )
? $_POST['icl_nav_menu_language'] : $this->term_translations->lang_code_by_termid( $menu_id );
$language_code = $language_code ? $language_code : $this->sitepress->get_current_language();
return $language_code;
}
/**
* @return bool|int|mixed|null|string
*/
private function get_trid_from_post_data() {
$trid = null;
if ( ! empty( $_POST['icl_translation_of'] ) && $_POST['icl_translation_of'] !== 'none' ) {
$trid = $this->sitepress->get_element_trid( $_POST['icl_translation_of'], 'tax_nav_menu' );
return $trid;
} elseif ( isset( $_POST['icl_nav_menu_trid'] ) ) {
$trid = ( (int) $_POST['icl_nav_menu_trid'] );
return $trid;
}
return $trid;
}
}

View File

@@ -0,0 +1,103 @@
<?php
class WPML_Frontend_Tax_Filters {
public function __construct() {
add_filter( 'taxonomy_template', array( $this, 'slug_template' ) );
add_filter( 'category_template', array( $this, 'slug_template' ) );
add_filter( 'tag_template', array( $this, 'slug_template' ) );
}
/**
* Adjust template (taxonomy-)$taxonomy-$term.php for translated term slugs and IDs
*
* @since 3.1
*
* @param string $template
*
* @return string The template filename if found.
*/
function slug_template( $template ) {
global $sitepress;
$term = $this->get_queried_tax_term();
if ( $term === false || ! is_taxonomy_translated( $term->taxonomy ) ) {
return $template;
}
$templates = array();
$has_filter = remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1 );
$current_language = $sitepress->get_current_language();
$default_language = $sitepress->get_default_language();
$templates = $this->add_term_templates( $term, $current_language, $templates );
$templates = $this->add_original_term_templates( $term, $default_language, $current_language, $templates );
if ( ! in_array( $term->taxonomy, array( 'category', 'post_tag' ), true ) ) {
$templates[] = 'taxonomy-' . $current_language . '.php';
$templates[] = 'taxonomy.php';
}
if ( $has_filter ) {
add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
}
$new_template = locate_template( array_unique( $templates ) );
$template = $new_template ? $new_template : $template;
return $template;
}
private function get_template_prefix( $taxonomy ) {
$prefix = in_array( $taxonomy, array( 'category', 'post_tag' ), true ) ? '' : 'taxonomy-';
$prefix .= $taxonomy == 'post_tag' ? 'tag' : $taxonomy;
return $prefix;
}
private function add_term_templates( $term, $current_language, $templates ) {
$prefix = $this->get_template_prefix( $term->taxonomy );
$templates[] = "$prefix-{$current_language}-{$term->slug}.php";
$templates[] = "$prefix-{$current_language}-{$term->term_id}.php";
$templates[] = "$prefix-{$current_language}.php";
$templates[] = "$prefix-{$term->slug}.php";
$templates[] = "$prefix-{$term->term_id}.php";
return $templates;
}
private function add_original_term_templates( $term, $default_language, $current_language, $templates ) {
$taxonomy = $term->taxonomy;
$prefix = $this->get_template_prefix( $taxonomy );
$original_term_id = icl_object_id( $term->term_id, $taxonomy, true, $default_language );
$original_term = get_term_by( 'id', $original_term_id, $taxonomy );
if ( $original_term ) {
$templates[] = "$prefix-{$current_language}-{$original_term->slug}.php";
$templates[] = "$prefix-{$current_language}-{$original_term_id}.php";
$templates[] = "$prefix-{$original_term->slug}.php";
$templates[] = "$prefix-{$original_term_id}.php";
$templates[] = "$prefix-{$current_language}.php";
$templates[] = "$prefix.php";
}
return $templates;
}
private function get_queried_tax_term() {
global $wp_query;
/** @var WP_Query $wp_query */
$term = $wp_query->get_queried_object();
$res = false;
if ( (bool) $term === true && isset( $term->taxonomy ) && $term->taxonomy ) {
$res = $term;
}
return $res;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* WPML_Term_Filters class file.
*
* @package WPML\Core
* @subpackage taxonomy-term-translation
*/
/**
* Class WPML_Term_Filters
*/
class WPML_Term_Filters extends WPML_WPDB_And_SP_User {
/**
* Init class.
*/
public function init() {
$taxonomies = get_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
$this->add_hooks_to_translated_taxonomy( $taxonomy );
}
add_action( 'registered_taxonomy', [ $this, 'registered_taxonomy' ], 10, 3 );
}
/**
* @param string $taxonomy Taxonomy slug.
* @param array|string $object_type Object type or array of object types.
* @param array $taxonomy_object Array of taxonomy registration arguments.
*/
public function registered_taxonomy( $taxonomy, $object_type, $taxonomy_object ) {
$this->add_hooks_to_translated_taxonomy( $taxonomy );
}
/**
* @param string $taxonomy Taxonomy slug.
*/
private function add_hooks_to_translated_taxonomy( $taxonomy ) {
if ( is_taxonomy_translated( $taxonomy ) ) {
add_filter( "pre_option_{$taxonomy}_children", [ $this, 'pre_option_tax_children' ], 10, 0 );
add_action( "create_{$taxonomy}", [ $this, 'update_tax_children_option' ], 10, 0 );
add_action( "edit_{$taxonomy}", [ $this, 'update_tax_children_option' ], 10, 0 );
}
}
public function update_tax_children_option( $taxonomy_input = false ) {
global $wpml_language_resolution, $wp_taxonomies;
$language_codes = $wpml_language_resolution->get_active_language_codes();
$language_codes[] = 'all';
$taxonomy = str_replace( array( 'create_', 'edit_' ), '', current_action() );
$taxonomy = isset( $wp_taxonomies[ $taxonomy ] ) ? $taxonomy : $taxonomy_input;
foreach ( $language_codes as $lang ) {
$tax_children = $this->get_tax_hier_array( $taxonomy, $lang );
$option_key = "{$taxonomy}_children_{$lang}";
update_option( $option_key, $tax_children );
}
}
public function pre_option_tax_children() {
$taxonomy = str_replace( array( 'pre_option_', '_children' ), '', current_filter() );
$lang = $this->sitepress->get_current_language();
$option_key = "{$taxonomy}_children_{$lang}";
$tax_children = get_option( $option_key, false );
if ( $tax_children === false ) {
$tax_children = $this->get_tax_hier_array( $taxonomy, $lang );
update_option( $option_key, $tax_children );
}
return ! empty( $tax_children ) ? $tax_children : false;
}
/**
* @param string $taxonomy
* @param string $lang_code
*
* @return array
*/
public function get_tax_hier_array( $taxonomy, $lang_code ) {
$hierarchy = array();
if ( $lang_code != 'all' ) {
$terms = $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT term_id, parent
FROM {$this->wpdb->term_taxonomy} tt
JOIN {$this->wpdb->prefix}icl_translations iclt
ON tt.term_taxonomy_id = iclt.element_id
WHERE tt.parent > 0
AND tt.taxonomy = %s
AND iclt.language_code = %s
AND iclt.element_type = %s
ORDER BY term_id",
$taxonomy,
$lang_code,
'tax_' . $taxonomy
)
);
} else {
$terms = $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT term_id, parent
FROM {$this->wpdb->term_taxonomy} tt
WHERE tt.parent > 0
AND tt.taxonomy = %s
ORDER BY term_id",
$taxonomy
)
);
}
foreach ( $terms as $term ) {
if ( $term->parent > 0 ) {
$hierarchy[ $term->parent ] = isset( $hierarchy[ $term->parent ] )
? $hierarchy[ $term->parent ] : array();
$hierarchy[ $term->parent ][] = $term->term_id;
}
}
return $hierarchy;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* Class WPML_Term_Hierarchy_Duplication
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Term_Hierarchy_Duplication extends WPML_WPDB_And_SP_User {
public function duplicates_require_sync( $post_ids, $duplicates_only = true ) {
$taxonomies = $this->sitepress->get_translatable_taxonomies( true );
foreach ( $taxonomies as $key => $tax ) {
if ( ! is_taxonomy_hierarchical( $tax ) ) {
unset( $taxonomies[ $key ] );
}
}
if ( (bool) $post_ids === true ) {
$need_sync_taxonomies = $duplicates_only === true
? $this->get_need_sync_new_dupl( $post_ids, $taxonomies )
: $this->get_need_sync_all_terms( $taxonomies, $post_ids );
} else {
$need_sync_taxonomies = array();
}
return array_values( array_unique( $need_sync_taxonomies ) );
}
private function get_need_sync_new_dupl( $duplicated_ids, $taxonomies ) {
$new_terms = $this->get_new_terms_just_duplicated( $duplicated_ids, $taxonomies );
$affected_taxonomies = array();
foreach ( $new_terms as $term ) {
$affected_taxonomies[] = $term->taxonomy;
}
$affected_taxonomies = array_unique( $affected_taxonomies );
$hierarchy_sync_helper = wpml_get_hierarchy_sync_helper( 'term' );
$unsynced_terms = $hierarchy_sync_helper->get_unsynced_elements(
$affected_taxonomies,
$this->sitepress->get_default_language()
);
foreach ( $new_terms as $key => $new_term ) {
$sync = true;
foreach ( $unsynced_terms as $term_unsynced ) {
if ( $term_unsynced->translated_id == $new_term->term_taxonomy_id ) {
$sync = false;
break;
}
}
if ( $sync === true ) {
unset( $new_terms[ $key ] );
}
}
$need_sync_taxonomies = array();
foreach ( $new_terms as $term ) {
$need_sync_taxonomies[] = $term->taxonomy;
}
return $need_sync_taxonomies;
}
private function get_need_sync_all_terms( $translated_taxonomies, $post_ids ) {
$hierarchy_sync_helper = wpml_get_hierarchy_sync_helper( 'term' );
$post_ids_in = wpml_prepare_in( (array) $post_ids, '%d' );
$taxonomies_in = wpml_prepare_in( $translated_taxonomies );
$this->wpdb->get_col(
"SELECT DISTINCT tt.taxonomy
FROM {$this->wpdb->term_taxonomy} tt
JOIN {$this->wpdb->term_relationships} tr
ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tr.object_id IN ({$post_ids_in}) AND tt.taxonomy IN ({$taxonomies_in})"
);
foreach ( $translated_taxonomies as $key => $tax ) {
$unsynced_terms = $hierarchy_sync_helper->get_unsynced_elements(
$tax,
$this->sitepress->get_default_language()
);
if ( (bool) $unsynced_terms === false ) {
unset( $translated_taxonomies[ $key ] );
}
}
return $translated_taxonomies;
}
private function get_new_terms_just_duplicated( $duplicate_ids, $taxonomies ) {
if ( (bool) $duplicate_ids === false || (bool) $taxonomies === false ) {
return array();
}
$duplicate_ids_in = wpml_prepare_in( $duplicate_ids, '%d' );
$taxonomies_in = wpml_prepare_in( $taxonomies );
$terms = $this->wpdb->get_results(
"SELECT tt.term_taxonomy_id, tt.taxonomy
FROM {$this->wpdb->term_taxonomy} tt
JOIN {$this->wpdb->term_relationships} tr
ON tt.term_taxonomy_id = tr.term_taxonomy_id
JOIN {$this->wpdb->postmeta} pm
ON pm.post_id = tr.object_id
JOIN {$this->wpdb->terms} t_duplicate
ON t_duplicate.term_id = tt.term_id
JOIN {$this->wpdb->terms} t_original
ON t_original.name = t_duplicate.name
JOIN {$this->wpdb->term_taxonomy} tt_master
ON tt_master.term_id = t_original.term_id
JOIN {$this->wpdb->term_relationships} tr_master
ON tt_master.term_taxonomy_id = tr_master.term_taxonomy_id
LEFT JOIN {$this->wpdb->term_relationships} tr_other
ON tt.term_taxonomy_id = tr_other.term_taxonomy_id
AND tr_other.object_id != tr.object_id
AND tr_other.object_id NOT IN ({$duplicate_ids_in})
LEFT JOIN {$this->wpdb->postmeta} pm_other
ON pm_other.post_id = tr_other.object_id
AND NOT (pm_other.meta_key = '_icl_lang_duplicate_of'
AND pm_other.meta_value IN ({$duplicate_ids_in}))
WHERE pm.meta_key = '_icl_lang_duplicate_of'
AND tr_other.object_id IS NULL
AND pm_other.post_id IS NULL
AND pm.meta_value IN ({$duplicate_ids_in})
AND tr_master.object_id IN ({$duplicate_ids_in})
AND tt.taxonomy IN ({$taxonomies_in})"
);
return $terms;
}
}

View File

@@ -0,0 +1,47 @@
<?php
class WPML_Term_Hierarchy_Sync extends WPML_Hierarchy_Sync {
protected $element_id_column = 'term_taxonomy_id';
protected $parent_id_column = 'parent';
protected $parent_element_id_column = 'term_id';
protected $element_type_column = 'taxonomy';
protected $element_type_prefix = 'tax_';
/**
* @param wpdb $wpdb
*/
public function __construct( &$wpdb ) {
parent::__construct( $wpdb );
$this->elements_table = $wpdb->term_taxonomy;
}
public function is_need_sync( $taxonomy, $ref_lang = false ) {
return (bool) $this->get_unsynced_elements( $taxonomy, $ref_lang );
}
public function sync_element_hierarchy( $element_types, $ref_lang = false ) {
/** @var WPML_Term_Filters $wpml_term_filters_general */
global $wpml_term_filters_general;
parent::sync_element_hierarchy( $element_types, $ref_lang );
do_action( 'wpml_sync_term_hierarchy_done' );
$element_types = (array) $element_types;
foreach ( $element_types as $taxonomy ) {
$wpml_term_filters_general->update_tax_children_option( $taxonomy );
}
}
/**
* @param string $element_type
*
* @return bool
*/
public function is_hierarchical( $element_type ) {
return is_taxonomy_hierarchical( $element_type );
}
}

View File

@@ -0,0 +1,106 @@
<?php
use WPML\FP\Fns;
class WPML_Term_Translation_Utils extends WPML_SP_User {
/**
* Duplicates all terms, that exist in the given target language,
* from the original post to the translation in that language.
*
* @param int $original_post_id
* @param string $lang
*/
function sync_terms( $original_post_id, $lang ) {
$this->synchronize_terms( $original_post_id, $lang, false );
}
/**
* Duplicates all terms on the original post to its translation in the given target language.
* Missing terms are created with the same name as their originals.
*
* @param int $original_post_id
* @param string $lang
*/
function duplicate_terms( $original_post_id, $lang ) {
$this->synchronize_terms( $original_post_id, $lang, true );
}
/**
* @param int $original_post_id
* @param string $lang
* @param bool $duplicate sets whether missing terms should be created by duplicating the original term
*/
private function synchronize_terms( $original_post_id, $lang, $duplicate ) {
global $wpml_post_translations;
$returnTrue = Fns::always( true );
add_filter( 'wpml_disable_term_adjust_id', $returnTrue );
$wpml_post_translations->reload();
$translated_post_id = $wpml_post_translations->element_id_in( $original_post_id, $lang );
if ( (bool) $translated_post_id === true ) {
$taxonomies = get_post_taxonomies( $original_post_id );
foreach ( $taxonomies as $tax ) {
$terms_on_original = wp_get_object_terms( $original_post_id, $tax );
if ( ! $this->sitepress->is_translated_taxonomy( $tax ) ) {
if ( $this->sitepress->get_setting( 'sync_post_taxonomies' ) ) {
// Taxonomy is not translated so we can just copy from the original
foreach ( $terms_on_original as $key => $term ) {
$terms_on_original[ $key ] = $term->term_id;
}
wp_set_object_terms( $translated_post_id, $terms_on_original, $tax );
}
} else {
/** @var int[] $translated_terms translated term_ids */
$translated_terms = $this->get_translated_term_ids( $terms_on_original, $lang, $tax, $duplicate );
wp_set_object_terms( $translated_post_id, $translated_terms, $tax );
}
}
}
remove_filter( 'wpml_disable_term_adjust_id', $returnTrue );
clean_object_term_cache( $original_post_id, get_post_type( $original_post_id ) );
}
/**
* @param object[] $terms
* @param string $lang
* @param string $taxonomy
* @param bool $duplicate sets whether missing terms should be created by duplicating the original term
*
* @return array
*/
private function get_translated_term_ids( $terms, $lang, $taxonomy, $duplicate ) {
/** @var WPML_Term_Translation $wpml_term_translations */
global $wpml_term_translations;
$term_utils = new WPML_Terms_Translations();
$wpml_term_translations->reload();
$translated_terms = array();
foreach ( $terms as $orig_term ) {
$translated_id = (int) $wpml_term_translations->term_id_in( $orig_term->term_id, $lang );
if ( ! $translated_id && $duplicate ) {
$translation = $term_utils->create_automatic_translation(
array(
'lang_code' => $lang,
'taxonomy' => $taxonomy,
'trid' => $wpml_term_translations->get_element_trid( $orig_term->term_taxonomy_id ),
'source_language' => $wpml_term_translations->get_element_lang_code(
$orig_term->term_taxonomy_id
),
)
);
$translated_id = isset( $translation['term_id'] ) ? $translation['term_id'] : false;
}
if ( $translated_id ) {
$translated_terms[] = $translated_id;
}
}
return $translated_terms;
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* @since 3.2
*
* Class WPML_Term_Translation
*
* Provides APIs for translating taxonomy terms
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Term_Translation extends WPML_Element_Translation {
/** @var array|null */
protected $ttids;
/** @var array|null */
protected $term_ids;
public function reload() {
parent::reload();
$this->term_ids = null;
$this->ttids = null;
}
/**
* @param int $term_id
*
* @return null|string
*/
public function lang_code_by_termid( $term_id ) {
return $this->get_element_lang_code( $this->adjust_ttid_for_term_id( $term_id ) );
}
/**
* Converts term_id into term_taxonomy_id
*
* @param int $term_id
*
* @return int
*/
public function adjust_ttid_for_term_id( $term_id ) {
$this->maybe_warm_term_id_cache();
return $term_id && isset( $this->ttids[ $term_id ] ) ? end( $this->ttids[ $term_id ] ) : $term_id;
}
/**
* Converts term_taxonomy_id into term_id
*
* @param int $ttid term_taxonomy_id
*
* @return int
*/
public function adjust_term_id_for_ttid( $ttid ) {
$this->maybe_warm_term_id_cache();
return $ttid && isset( $this->term_ids[ $ttid ] ) ? $this->term_ids[ $ttid ] : $ttid;
}
/**
* @param int $term_id
* @param string $lang_code
* @param bool|false $original_fallback if true will return the the input term_id in case no translation is found
*
* @return null|int
*/
public function term_id_in( $term_id, $lang_code, $original_fallback = false ) {
return $this->adjust_term_id_for_ttid(
$this->element_id_in( $this->adjust_ttid_for_term_id( $term_id ), $lang_code, $original_fallback )
);
}
/**
* Returns the trid for a given term_id and taxonomy or null on failure
*
* @param int $term_id term_id of a term
* @param string $taxonomy taxonomy of the term
*
* @return null|int
*/
public function trid_from_tax_and_id( $term_id, $taxonomy ) {
$this->maybe_warm_term_id_cache();
$ttid = $term_id && isset( $this->ttids[ $term_id ][ $taxonomy ] )
? $this->ttids[ $term_id ][ $taxonomy ] : $term_id;
return $this->get_element_trid( $ttid );
}
/**
* Returns all post types to which a taxonomy is linked.
*
* @param string $taxonomy
*
* @return array
*
* @since 3.2.3
*/
public function get_taxonomy_post_types( $taxonomy ) {
return WPML_WP_Taxonomy::get_linked_post_types( $taxonomy );
}
protected function get_element_join() {
return "FROM {$this->wpdb->prefix}icl_translations wpml_translations
JOIN {$this->wpdb->term_taxonomy} tax
ON wpml_translations.element_id = tax.term_taxonomy_id
AND wpml_translations.element_type = CONCAT('tax_', tax.taxonomy)";
}
protected function get_type_prefix() {
return 'tax_';
}
private function maybe_warm_term_id_cache() {
if ( ! isset( $this->ttids ) || ! isset( $this->term_ids ) ) {
$data = $this->wpdb->get_results(
' SELECT wpml_translations.element_id, tax.term_id, tax.taxonomy
' . $this->get_element_join() . "
JOIN {$this->wpdb->terms} terms
ON terms.term_id = tax.term_id
WHERE tax.term_id != tax.term_taxonomy_id",
ARRAY_A
);
$this->term_ids = array();
$this->ttids = array();
foreach ( $data as $row ) {
$this->ttids[ $row['term_id'] ] = isset( $this->ttids[ $row['term_id'] ] )
? $this->ttids[ $row['term_id'] ] : array();
$this->ttids[ $row['term_id'] ][ $row['taxonomy'] ] = $row['element_id'];
$this->term_ids[ $row['element_id'] ] = $row['term_id'];
}
}
}
/**
* @param string $term
* @param string $slug
* @param string $taxonomy
* @param string $lang_code
*
* @return string
*/
public function generate_unique_term_slug( $term, $slug, $taxonomy, $lang_code ) {
if ( '' === trim( $slug ) ) {
$slug = sanitize_title( $term );
}
return WPML_Terms_Translations::term_unique_slug( $slug, $taxonomy, $lang_code );
}
/**
* @return self
*/
public static function getGlobalInstance() {
global $wpml_term_translations, $wpdb;
if ( ! isset( $wpml_term_translations ) ) {
$wpml_term_translations = new WPML_Term_Translation( $wpdb );
}
return $wpml_term_translations;
}
}

View File

@@ -0,0 +1,719 @@
<?php
require_once dirname( __FILE__ ) . '/wpml-update-term-action.class.php';
use WPML\FP\Lst;
use WPML\FP\Fns;
use WPML\FP\Obj;
use WPML\FP\Logic;
use function WPML\FP\pipe;
/**
* @since 3.1.8
*
* Class WPML_Terms_Translations
*
* This class holds some basic functionality for translating taxonomy terms.
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Terms_Translations {
/**
* @param array<string|\WP_Term> $terms
* @param string[]|string $taxonomies This is only used by the WP core AJAX call that fetches the preview
* auto-complete for flat taxonomy term adding
*
* @return array<\WP_Term>
* @deprecated since Version 3.1.8.3
*/
public static function get_terms_filter( $terms, $taxonomies ) {
global $wpdb, $sitepress;
$lang = $sitepress->get_current_language();
foreach ( $taxonomies as $taxonomy ) {
if ( $sitepress->is_translated_taxonomy( $taxonomy ) ) {
$element_type = 'tax_' . $taxonomy;
$query = $wpdb->prepare(
"SELECT wptt.term_id
FROM {$wpdb->prefix}icl_translations AS iclt
JOIN {$wpdb->prefix}term_taxonomy AS wptt
ON iclt.element_id = wptt.term_taxonomy_id
WHERE language_code=%s AND element_type = %s",
$lang,
$element_type
);
$element_ids_array = $wpdb->get_col( $query );
foreach ( $terms as $key => $term ) {
if ( ! is_object( $term ) ) {
$term = get_term_by( 'name', $term, $taxonomy );
}
if ( $term && isset( $term->taxonomy )
&& $term->taxonomy === $taxonomy
&& ! in_array( $term->term_id, $element_ids_array ) ) {
unset( $terms[ $key ] );
}
}
}
}
return $terms;
}
/**
* @param string $slug
* @param string $taxonomy
* @param string $lang
* Creates a unique slug for a given term, using a scheme
* encoding the language code in the slug.
*
* @return string
*/
public static function term_unique_slug( $slug, $taxonomy, $lang ) {
global $sitepress;
$default_language = $sitepress->get_default_language();
if ( $lang !== $default_language && self::term_slug_exists( $slug, $taxonomy ) ) {
$slug .= '-' . $lang;
}
$i = 2;
$suffix = '-' . $i;
if ( self::term_slug_exists( $slug, $taxonomy ) ) {
while ( self::term_slug_exists( $slug . $suffix, $taxonomy ) ) {
$i ++;
$suffix = '-' . $i;
}
$slug .= $suffix;
}
return $slug;
}
/**
* @param string $slug
* @param bool $taxonomy
* If $taxonomy is given, then slug existence is checked only for the specific taxonomy.
*
* @return bool
*/
private static function term_slug_exists( $slug, $taxonomy = false ) {
global $wpdb;
$existing_term_prepared_query = $wpdb->prepare(
"SELECT t.term_id
FROM {$wpdb->terms} t
JOIN {$wpdb->term_taxonomy} tt
ON t.term_id = tt.term_id
WHERE t.slug = %s
AND tt.taxonomy = %s
LIMIT 1",
$slug,
$taxonomy
);
$term_id = $wpdb->get_var( $existing_term_prepared_query );
return (bool) $term_id;
}
/**
* This function provides an action hook only used by WCML.
* It will be removed in the future and should not be implemented in new spots.
*
* @deprecated deprecated since version 3.1.8.3
*
* @param string $taxonomy The identifier of the taxonomy the translation was just saved to.
* @param array $translated_term The associative array holding term taxonomy id and term id,
* as returned by wp_insert_term or wp_update_term.
*/
public static function icl_save_term_translation_action( $taxonomy, $translated_term ) {
global $wpdb, $sitepress;
if ( is_taxonomy_hierarchical( $taxonomy ) ) {
$term_taxonomy_id = $translated_term['term_taxonomy_id'];
$original_ttid = $sitepress->get_original_element_id( $term_taxonomy_id, 'tax_' . $taxonomy );
$original_tax_sql = "SELECT * FROM {$wpdb->term_taxonomy} WHERE taxonomy=%s AND term_taxonomy_id = %d";
$original_tax_prepared = $wpdb->prepare( $original_tax_sql, array( $taxonomy, $original_ttid ) );
$original_tax = $wpdb->get_row( $original_tax_prepared );
do_action( 'icl_save_term_translation', $original_tax, $translated_term );
}
}
/**
* Prints a hidden div, containing the list of allowed terms for a post type in each language.
* This is used to only display the correct categories and tags in the quick-edit fields of the post table.
*
* @param string $column_name
* @param string|string[]|\WP_Post $post_type
*/
public static function quick_edit_terms_removal( $column_name, $post_type ) {
global $sitepress, $wpdb;
if ( $column_name == 'icl_translations' ) {
$taxonomies = array_filter(
get_object_taxonomies( $post_type ),
array(
$sitepress,
'is_translated_taxonomy',
)
);
$terms_by_language_and_taxonomy = array();
if ( ! empty( $taxonomies ) ) {
$res = $wpdb->get_results(
" SELECT language_code, taxonomy, term_id FROM {$wpdb->term_taxonomy} tt
JOIN {$wpdb->prefix}icl_translations wpml_translations
ON wpml_translations.element_id = tt.term_taxonomy_id
AND wpml_translations.element_type = CONCAT('tax_', tt.taxonomy)
WHERE tt.taxonomy IN (" . wpml_prepare_in( $taxonomies ) . ' )'
);
} else {
$res = array();
}
foreach ( $res as $term ) {
$lang = $term->language_code;
$tax = $term->taxonomy;
$terms_by_language_and_taxonomy[ $lang ] = isset( $terms_by_language_and_taxonomy[ $lang ] ) ? $terms_by_language_and_taxonomy[ $lang ] : array();
$terms_by_language_and_taxonomy[ $lang ][ $tax ] = isset( $terms_by_language_and_taxonomy[ $lang ][ $tax ] ) ? $terms_by_language_and_taxonomy[ $lang ][ $tax ] : array();
$terms_by_language_and_taxonomy[ $lang ][ $tax ][] = $term->term_id;
}
$terms_json = wp_json_encode( $terms_by_language_and_taxonomy );
$output = '<div id="icl-terms-by-lang" style="display: none;">' . wp_kses_post( $terms_json ) . '</div>';
echo $output;
}
}
/**
* Creates a new term from an argument array.
*
* @param array $args
* @return array|bool
* Returns either an array containing the term_id and term_taxonomy_id of the term resulting from this database
* write or false on error.
*/
public static function create_new_term( $args ) {
global $wpdb, $sitepress;
/** @var string $taxonomy */
$taxonomy = false;
/** @var string $lang_code */
$lang_code = false;
/**
* Sets whether translations of posts are to be updated by the newly created term,
* should they be missing a translation still.
* During debug actions designed to synchronise post and term languages this should not be set to true,
* doing so introduces the possibility of removing terms from posts before switching
* them with their translation in the correct language.
*
* @var bool
*/
$sync = false;
extract( $args, EXTR_OVERWRITE );
require_once dirname( __FILE__ ) . '/wpml-update-term-action.class.php';
$new_term_action = new WPML_Update_Term_Action( $wpdb, $sitepress, $args );
$new_term = $new_term_action->execute();
if ( $sync && $new_term && $taxonomy && $lang_code ) {
self::sync_taxonomy_terms_language( $taxonomy );
}
return $new_term;
}
/**
* @param array<mixed> $args
* Creates an automatic translation of a term, the name of which is set as "original" . @ "lang_code" and the slug of which is set as "original_slug" . - . "lang_code".
*
* @return array|bool
*/
public function create_automatic_translation( $args ) {
global $sitepress;
$term = false;
$lang_code = false;
$taxonomy = false;
$original_id = false;
$original_tax_id = false;
$trid = false;
$original_term = false;
$update_translations = false;
$source_language = null;
extract( $args, EXTR_OVERWRITE );
if ( $trid && ! $original_id ) {
$original_tax_id = $sitepress->get_original_element_id_by_trid( $trid );
$original_term = get_term_by( 'term_taxonomy_id', $original_tax_id, $taxonomy, OBJECT, 'no' );
}
if ( $original_id && ! $original_tax_id ) {
$original_term = get_term( $original_id, $taxonomy, OBJECT, 'no' );
if ( isset( $original_term['term_taxonomy_id'] ) ) {
$original_tax_id = $original_term['term_taxonomy_id'];
}
}
if ( ! $trid ) {
$trid = $sitepress->get_element_trid( $original_tax_id, 'tax_' . $taxonomy );
}
if ( ! $source_language ) {
$source_language = $sitepress->get_source_language_by_trid( $trid );
}
$existing_translations = $sitepress->get_element_translations( $trid, 'tax_' . $taxonomy );
if ( $lang_code && isset( $existing_translations[ $lang_code ] ) ) {
$new_translated_term = false;
} else {
if ( ! $original_term ) {
if ( $original_id ) {
$original_term = get_term( $original_id, $taxonomy, OBJECT, 'no' );
} elseif ( $original_tax_id ) {
$original_term = get_term_by( 'term_taxonomy_id', $original_tax_id, $taxonomy, OBJECT, 'no' );
}
}
$translated_slug = false;
if ( ! $term && isset( $original_term->name ) ) {
$term = $original_term->name;
/**
* @deprecated use 'wpml_duplicate_generic_string' instead, with the same arguments
*/
$term = apply_filters(
'icl_duplicate_generic_string',
$term,
$lang_code,
array(
'context' => 'taxonomy',
'attribute' => $taxonomy,
'key' => $original_term->term_id,
)
);
$term = apply_filters(
'wpml_duplicate_generic_string',
$term,
$lang_code,
array(
'context' => 'taxonomy',
'attribute' => $taxonomy,
'key' => $original_term->term_id,
)
);
}
if ( isset( $original_term->slug ) ) {
$translated_slug = $original_term->slug;
/**
* @deprecated use 'wpml_duplicate_generic_string' instead, with the same arguments
*/
$translated_slug = apply_filters(
'icl_duplicate_generic_string',
$translated_slug,
$lang_code,
array(
'context' => 'taxonomy_slug',
'attribute' => $taxonomy,
'key' => $original_term->term_id,
)
);
$translated_slug = apply_filters(
'wpml_duplicate_generic_string',
$translated_slug,
$lang_code,
array(
'context' => 'taxonomy_slug',
'attribute' => $taxonomy,
'key' => $original_term->term_id,
)
);
$translated_slug = self::term_unique_slug( $translated_slug, $taxonomy, $lang_code );
}
$new_translated_term = false;
if ( $term ) {
$new_term_args = array(
'term' => $term,
'slug' => $translated_slug,
'taxonomy' => $taxonomy,
'lang_code' => $lang_code,
'original_tax_id' => $original_tax_id,
'update_translations' => $update_translations,
'trid' => $trid,
'source_language' => $source_language,
);
$new_translated_term = self::create_new_term( $new_term_args );
}
}
return $new_translated_term;
}
/**
* @param string $taxonomy
*
* Sets all taxonomy terms to the correct language on each post, having at least one term from the taxonomy.
*/
public static function sync_taxonomy_terms_language( $taxonomy ) {
$all_posts_in_taxonomy = get_posts( array( 'tax_query' => array( 'taxonomy' => $taxonomy ) ) );
foreach ( $all_posts_in_taxonomy as $post_in_taxonomy ) {
self::sync_post_and_taxonomy_terms_language( $post_in_taxonomy->ID, $taxonomy );
}
}
/**
* @param int $post_id
*
* Sets all taxonomy terms ot the correct language for a given post.
*/
public static function sync_post_terms_language( $post_id ) {
$taxonomies = get_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
self::sync_post_and_taxonomy_terms_language( $post_id, $taxonomy );
}
}
/**
* @param int $post_id
* @param string $taxonomy
* Synchronizes a posts taxonomy term's languages with the posts language for all translations of the post.
*/
public static function sync_post_and_taxonomy_terms_language( $post_id, $taxonomy ) {
global $sitepress;
$post = get_post( $post_id );
$post_type = $post->post_type;
$post_trid = $sitepress->get_element_trid( $post_id, 'post_' . $post_type );
$post_translations = $sitepress->get_element_translations( $post_trid, 'post_' . $post_type );
$terms_from_original_post = wp_get_post_terms( $post_id, $taxonomy );
$is_original = true;
if ( $sitepress->get_original_element_id( $post_id, 'post_' . $post_type ) != $post_id ) {
$is_original = false;
}
foreach ( $post_translations as $post_language => $translated_post ) {
$translated_post_id = $translated_post->element_id;
if ( ! $translated_post_id ) {
continue;
}
$terms_from_translated_post = wp_get_post_terms( $translated_post_id, $taxonomy );
if ( $is_original ) {
$duplicates = $sitepress->get_duplicates( $post_id );
if ( in_array( $translated_post_id, $duplicates ) ) {
$terms = array_merge( $terms_from_original_post, $terms_from_translated_post );
} else {
$terms = $terms_from_translated_post;
}
} else {
$terms = $terms_from_translated_post;
}
foreach ( (array) $terms as $term ) {
$term_original_tax_id = $term->term_taxonomy_id;
$original_term_language_object = $sitepress->get_element_language_details( $term_original_tax_id, 'tax_' . $term->taxonomy );
if ( $original_term_language_object && isset( $original_term_language_object->language_code ) ) {
$original_term_language = $original_term_language_object->language_code;
} else {
$original_term_language = $post_language;
}
if ( $original_term_language != $post_language ) {
$term_trid = $sitepress->get_element_trid( $term_original_tax_id, 'tax_' . $term->taxonomy );
$translated_terms = $sitepress->get_element_translations( $term_trid, 'tax_' . $term->taxonomy, false, false, true );
$term_id = $term->term_id;
wp_remove_object_terms( $translated_post_id, (int) $term_id, $taxonomy );
if ( isset( $translated_terms[ $post_language ] ) ) {
$term_in_correct_language = $translated_terms[ $post_language ];
wp_set_post_terms( $translated_post_id, array( (int) $term_in_correct_language->term_id ), $taxonomy, true );
}
if ( isset( $term->term_taxonomy_id ) ) {
wp_update_term_count( $term->term_taxonomy_id, $taxonomy );
}
}
wp_update_term_count( $term_original_tax_id, $taxonomy );
}
}
}
/**
* @param int $post_id Object ID.
* @param array $terms An array of object terms.
* @param array $tt_ids An array of term taxonomy IDs.
* @param string $taxonomy Taxonomy slug.
* @param bool $append Whether to append new terms to the old terms.
* @param array $old_tt_ids Old array of term taxonomy IDs.
*/
public static function set_object_terms_action( $post_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
global $sitepress;
// TODO: [WPML 3.2] We have a better way to check if the post is an external type (e.g. Package).
if ( get_post( $post_id ) ) {
self::set_tags_in_proper_language( $post_id, $tt_ids, $taxonomy, $old_tt_ids );
if ( $sitepress->get_setting( 'sync_post_taxonomies' ) ) {
$term_actions_helper = $sitepress->get_term_actions_helper();
$term_actions_helper->added_term_relationships( $post_id );
}
}
}
/**
* @param int $post_id Object ID.
* @param array $tt_ids An array of term taxonomy IDs.
* @param string $taxonomy Taxonomy slug.
* @param array $old_tt_ids Old array of term taxonomy IDs.
* @param bool $isBulkEdit
*/
private static function set_tags_in_proper_language( $post_id, $tt_ids, $taxonomy, $old_tt_ids ) {
$isEditAction = isset( $_POST['action'] ) && ( 'editpost' === $_POST['action'] || 'inline-save' === $_POST['action'] );
$isBulkEdit = isset( $_REQUEST['bulk_edit'] );
if ( $isEditAction || $isBulkEdit ) {
$tt_ids = array_map( 'intval', $tt_ids );
$tt_ids = array_diff( $tt_ids, $old_tt_ids );
self::quick_edited_post_terms( $post_id, $taxonomy, $tt_ids );
}
}
/**
* @param int $post_id
* @param string $taxonomy
* @param array $changed_ttids
*
* Running this function will remove certain issues arising out of bulk adding of terms to posts of various languages.
* This case can result in situations in which the WP Core functionality adds a term to a post, before the language assignment
* operations of WPML are triggered. This leads to states in which terms can be assigned to a post even though their language
* differs from that of the post.
* This function behaves between hierarchical and flat taxonomies. Hierarchical terms from the wrong taxonomy are simply removed
* from the post. Flat terms are added with the same name but in the correct language.
* For flat terms this implies either the use of the existing term or the creation of a new one.
* This function uses wpdb queries instead of the WordPress API, it is therefore save to be run out of
* any language setting.
*/
public static function quick_edited_post_terms( $post_id, $taxonomy, $newlyAddedTermIds ) {
global $wpdb, $sitepress, $wpml_post_translations;
$postLang = $wpml_post_translations->get_element_lang_code( $post_id ) ?: Obj::prop( 'icl_post_language', $_POST );
if ( ! $sitepress->is_translated_taxonomy( $taxonomy ) || ! ( $postLang ) ) {
return;
}
$sql = "SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE language_code = %s AND element_type = %s";
$termIdsInPostLang = $wpdb->get_col( $wpdb->prepare( $sql, $postLang, 'tax_' . $taxonomy ) );
$termIdsInPostLang = Fns::map( \WPML\FP\Cast::toInt(), $termIdsInPostLang );
$newlyCreatedTermIds = [];
$isInPostLang = Lst::includes( Fns::__, $termIdsInPostLang );
if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
$getTermInPostLang = Obj::prop( 'idInPostLang' );
$createTermIfNotExists = Logic::ifElse( $getTermInPostLang, Fns::identity(), self::createTerm( $taxonomy, $postLang ) );
$updateOrDeleteTermInPost = Logic::ifElse(
$getTermInPostLang,
self::updatePostTaxonomy( $post_id ),
pipe( Obj::prop( 'id' ), self::deletePostTaxonomy( $post_id ) )
);
$newlyCreatedTermIds = \wpml_collect( $newlyAddedTermIds )
->reject( $isInPostLang )
->map( self::appendTermName() )
->map( self::appendTermIdCounterpartInPostLang( $termIdsInPostLang, $taxonomy ) )
->map( $createTermIfNotExists )
->map( $updateOrDeleteTermInPost )
->all();
} else {
\wpml_collect( $newlyAddedTermIds )->reject( $isInPostLang )->each( self::deletePostTaxonomy( $post_id ) );
}
// Update term counts manually here, since using sql, will not trigger the updating of term counts automatically.
wp_update_term_count( array_merge( $newlyAddedTermIds, $newlyCreatedTermIds ), $taxonomy );
}
/**
* @param int $postId
*
* @return Closure
*/
private static function deletePostTaxonomy( $postId ) {
return function ( $termId ) use ( $postId ) {
global $wpdb;
$wpdb->delete(
$wpdb->term_relationships,
[
'object_id' => $postId,
'term_taxonomy_id' => $termId,
]
);
};
}
/**
* @return Closure
*/
private static function appendTermName() {
return function ( $termId ) {
global $wpdb;
$sql = "SELECT t.name FROM {$wpdb->terms} AS t JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.term_taxonomy_id=%d";
$termName = $wpdb->get_var( $wpdb->prepare( $sql, $termId ) );
return [ 'id' => $termId, 'name' => $termName ];
};
}
/**
* @param array $termIdsInPostLang
* @param string $taxonomy
*
* @return Closure
*/
private static function appendTermIdCounterpartInPostLang( $termIdsInPostLang, $taxonomy ) {
return function ( $termData ) use ( $termIdsInPostLang, $taxonomy ) {
global $wpdb;
$idInPostLang = false;
if ( count( $termIdsInPostLang ) ) {
$in = wpml_prepare_in( $termIdsInPostLang, '%d' );
$sql = "
SELECT tt.term_taxonomy_id FROM {$wpdb->terms} AS t
JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
WHERE t.name=%s AND tt.taxonomy=%s AND tt.term_taxonomy_id IN ({$in})
";
$idInPostLang = $wpdb->get_var( $wpdb->prepare( $sql, $termData['name'], $taxonomy ) );
}
return Obj::assoc( 'idInPostLang', $idInPostLang, $termData );
};
}
/**
* @param string $taxonomy
* @param string $postLang
*
* @return Closure
*/
private static function createTerm( $taxonomy, $postLang ) {
return function ( $termData ) use ( $taxonomy, $postLang ) {
global $sitepress;
$idInCorrectId = false;
$newTerm = wp_insert_term( $termData['name'], $taxonomy, [ 'slug' => self::term_unique_slug( sanitize_title( $termData['name'] ), $taxonomy, $postLang ) ] );
if ( isset( $newTerm['term_taxonomy_id'] ) ) {
$idInCorrectId = $newTerm['term_taxonomy_id'];
$trid = $sitepress->get_element_trid( $termData['id'], 'tax_' . $taxonomy );
$sitepress->set_element_language_details( $idInCorrectId, 'tax_' . $taxonomy, $trid, $postLang );
}
return Obj::assoc( 'idInPostLang', $idInCorrectId, $termData );
};
}
/**
* @param int $postId
*
* @return Closure
*/
private static function updatePostTaxonomy( $postId ) {
return function ( $termData ) use ( $postId ) {
global $wpdb;
$wpdb->update(
$wpdb->term_relationships,
[ 'term_taxonomy_id' => $termData['idInPostLang'] ],
[
'object_id' => $postId,
'term_taxonomy_id' => $termData['id'],
]
);
};
}
/**
* Returns an array of all terms, that have a language suffix on them.
* This is used by troubleshooting functionality.
*
* @return array
*/
public static function get_all_terms_with_language_suffix() {
global $wpdb;
$lang_codes = $wpdb->get_col( "SELECT code FROM {$wpdb->prefix}icl_languages" );
/*
Build the expression to find all potential candidates for renaming.
* These must have the part "<space>@lang_code<space>" in them.
*/
$where_parts = array();
foreach ( $lang_codes as $key => $code ) {
$where_parts[ $key ] = "t.name LIKE '" . '% @' . esc_sql( $code ) . "%'";
}
$where = '(' . join( ' OR ', $where_parts ) . ')';
$terms_with_suffix = $wpdb->get_results( "SELECT t.name, t.term_id, tt.taxonomy FROM {$wpdb->terms} AS t JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE {$where}" );
$terms = array();
foreach ( $terms_with_suffix as $term ) {
if ( $term->name == WPML_Troubleshooting_Terms_Menu::strip_language_suffix( $term->name ) ) {
continue;
}
$term_id = $term->term_id;
$term_taxonomy_label = $term->taxonomy;
$taxonomy = get_taxonomy( $term->taxonomy );
if ( $taxonomy && isset( $taxonomy->labels ) && isset( $taxonomy->labels->name ) ) {
$term_taxonomy_label = $taxonomy->labels->name;
}
if ( isset( $terms[ $term_id ] ) && isset( $terms[ $term_id ]['taxonomies'] ) ) {
if ( ! in_array( $term_taxonomy_label, $terms[ $term_id ]['taxonomies'] ) ) {
$terms[ $term_id ]['taxonomies'][] = $term_taxonomy_label;
}
} else {
$terms[ $term_id ] = array(
'name' => $term->name,
'taxonomies' => array( $term_taxonomy_label ),
);
}
}
return $terms;
}
}

View File

@@ -0,0 +1,266 @@
<?php
/**
* Class WPML_Update_Term_Action
*
* This class holds the functionality for creating or editing a taxonomy term.
*
* @package wpml-core
* @subpackage taxonomy-term-translation
*/
class WPML_Update_Term_Action extends WPML_WPDB_And_SP_User {
/**
* TRUE if this object represents valid data for the update or creation of a term, false otherwise.
*
* @var bool
*/
private $is_valid = true;
/**
* TRUE if this object represents term update action, false if it represents a term creation action.
*
* @var bool
*/
private $is_update;
/**
* Argument array containing arguments in a format that can and is used as input to \wp_update_term or
* \wp_insert_term
*
* @var array
*/
private $wp_new_term_args = array();
/**
* The taxonomy in which this action takes place.
*
* @var string
*/
private $taxonomy;
/**
* Trid value in the icl_translations table to which this action is to be written.
*
* @var int
*/
private $trid;
/**
* Language of the term that is to result from this action.
*
* @var string
*/
private $lang_code;
/**
* Source language of the term that is to result from this action.
*
* @var string|null
*/
private $source_lang_code = null;
/**
* Array holding translations of the term created by this object prior to it's creation.
*
* @var array
*/
private $existing_translations = array();
/**
* The term id of the term to be updated or resulting from this action.
*
* @var int
*/
private $term_id;
/**
* This only gets set for update actions. In this case the new slug has to be compared with the old slug,
* to decide whether any slug name sanitation has to happen.
*
* @var string
*/
private $old_slug;
/**
* @param wpdb $wpdb
* @param SitePress $sitepress
* @param array $args
*/
public function __construct( &$wpdb, &$sitepress, $args ) {
parent::__construct( $wpdb, $sitepress );
/**
* Actual name of the term. Same as the name input argument to \wp_update_term or \wp_insert_term
*
* @var string|bool
*/
$term = false;
$slug = '';
$taxonomy = '';
/** @var string $lang_code */
$lang_code = '';
$trid = null;
/** @var int|bool $original_tax_id */
$original_tax_id = false;
/**
* Taxonomy_term_id of the parent element
*
* @var int
*/
$parent = 0;
$description = false;
$term_group = false;
$source_language = null;
extract( $args, EXTR_OVERWRITE );
// We cannot create a term unless we at least know its name
if ( (string) $term !== '' && $taxonomy ) {
$this->wp_new_term_args['name'] = $term;
$this->taxonomy = $taxonomy;
} else {
$this->is_valid = false;
return;
}
if ( $parent ) {
$this->wp_new_term_args['parent'] = $parent;
}
if ( $description ) {
$this->wp_new_term_args['description'] = $description;
}
if ( $term_group ) {
$this->wp_new_term_args['term_group'] = $term_group;
}
$this->wp_new_term_args['term_group'] = $term_group;
$this->is_valid = $this->set_language_information( $trid, $original_tax_id, $lang_code, $source_language );
$this->set_action_type();
if ( ! $this->is_update || ( $this->is_update && $slug != $this->old_slug && ! empty( $slug ) ) ) {
if ( trim( $slug ) == '' ) {
$slug = sanitize_title( $term );
}
$slug = WPML_Terms_Translations::term_unique_slug( $slug, $taxonomy, $lang_code );
$this->wp_new_term_args['slug'] = $slug;
}
}
/**
* Writes the term update or creation action saved in this object to the database.
*
* @return array|false
* Returns either an array containing the term_id and term_taxonomy_id of the term resulting from this database
* write or false on error.
*/
public function execute() {
global $sitepress;
$switch_lang = new WPML_Temporary_Switch_Language( $sitepress, $this->lang_code );
remove_action( 'create_term', array( $sitepress, 'create_term' ), 1 );
remove_action( 'edit_term', array( $sitepress, 'create_term' ), 1 );
add_action( 'create_term', array( $this, 'add_term_language_action' ), 1, 3 );
$new_term = false;
if ( $this->is_valid ) {
if ( $this->is_update && $this->term_id ) {
$new_term = wp_update_term( $this->term_id, $this->taxonomy, $this->wp_new_term_args );
} else {
$new_term = wp_insert_term( $this->wp_new_term_args['name'], $this->taxonomy, $this->wp_new_term_args );
}
}
add_action( 'create_term', array( $sitepress, 'create_term' ), 1, 3 );
add_action( 'edit_term', array( $sitepress, 'create_term' ), 1, 3 );
remove_action( 'create_term', array( $this, 'add_term_language_action' ), 1 );
if ( ! is_array( $new_term ) ) {
$new_term = false;
}
unset( $switch_lang );
return $new_term;
}
/**
* This action is to be hooked to the WP create_term and edit_term hooks.
* It sets the correct language information after a term is saved.
*
* @param int|string $term_id
* @param int|string $term_taxonomy_id
* @param string $taxonomy
*/
public function add_term_language_action( $term_id, $term_taxonomy_id, $taxonomy ) {
if ( $this->is_valid && ! $this->is_update && $this->taxonomy == $taxonomy ) {
$this->sitepress->set_element_language_details(
$term_taxonomy_id,
'tax_' . $taxonomy,
$this->trid,
$this->lang_code,
$this->source_lang_code
);
}
}
/**
* Sets the language variables for this object.
*
* @param bool|int $trid
* @param bool|int $original_tax_id
* @param string $lang_code
* @param bool|string $source_language
* @return bool True if the given language parameters allowed for determining valid language information, false
* otherwise.
*/
private function set_language_information( $trid, $original_tax_id, $lang_code, $source_language ) {
if ( ! $lang_code || ! $this->sitepress->is_active_language( $lang_code ) ) {
return false;
} else {
$this->lang_code = $lang_code;
}
if ( ! $trid && $original_tax_id ) {
$trid = $this->sitepress->get_element_trid( $original_tax_id, 'tax_' . $this->taxonomy );
}
if ( $trid ) {
$this->trid = $trid;
$this->existing_translations = $this->sitepress->get_element_translations( $trid, 'tax_' . $this->taxonomy );
foreach ( $this->existing_translations as $lang => $translation ) {
if ( $original_tax_id && isset( $translation->element_id ) && $translation->element_id == $original_tax_id && isset( $translation->language_code ) && $translation->language_code ) {
$this->source_lang_code = $translation->language_code;
break;
} elseif ( isset( $translation->language_code ) && $translation->language_code && ! $translation->source_language_code ) {
$this->source_lang_code = $translation->language_code;
}
}
}
return true;
}
/**
* Sets the action type of this object.
* In case of this action being an update the is_update flag is set true.
* Also the term_id of the existing term is saved in $this->term_id.
*/
private function set_action_type() {
if ( ! $this->trid ) {
$this->is_update = false;
} elseif ( isset( $this->existing_translations[ $this->lang_code ] ) ) {
$existing_db_entry = $this->existing_translations[ $this->lang_code ];
if ( isset( $existing_db_entry->element_id ) && $existing_db_entry->element_id ) {
// Term update actions need information about the term_id, not the term_taxonomy_id saved in the element_id column of icl_translations.
/** @var \stdClass $term */
$term = $this->wpdb->get_row(
$this->wpdb->prepare(
"SELECT t.term_id, t.slug FROM {$this->wpdb->terms} AS t
JOIN {$this->wpdb->term_taxonomy} AS tt ON t.term_id=tt.term_id
WHERE term_taxonomy_id=%d",
$existing_db_entry->element_id
)
);
if ( $term->term_id && $term->slug ) {
$this->is_update = true;
$this->term_id = $term->term_id;
$this->old_slug = $term->slug;
} else {
$this->is_update = false;
}
} else {
$this->sitepress->delete_element_translation( $this->trid, 'tax_' . $this->taxonomy, $this->lang_code );
$this->is_update = false;
}
} else {
$this->is_update = false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
<?php
/**
* WPML Sunrise Script - START
*
* @author OnTheGoSystems
* @version 3.7.0
*
* Place this script in the wp-content folder and add "define('SUNRISE', 'on');" in wp-config.php
* in order to enable using different domains for different languages in multisite mode
*
* Experimental feature
*/
/**
* Class WPML_Sunrise_Lang_In_Domains
*
* @author OnTheGoSystems
*/
class WPML_Sunrise_Lang_In_Domains {
/** @var wpdb $wpdb */
private $wpdb;
/** @var string $table_prefix */
private $table_prefix;
/** @var string $current_blog */
private $current_blog;
/** @var bool $no_recursion */
private $no_recursion;
/**
* Method init
*/
public function init() {
if ( ! defined( 'WPML_SUNRISE_MULTISITE_DOMAINS' ) ) {
define( 'WPML_SUNRISE_MULTISITE_DOMAINS', true );
}
add_filter( 'query', array( $this, 'query_filter' ) );
}
/**
* @param string $q
*
* @return string
*/
public function query_filter( $q ) {
$this->set_private_properties();
if ( ! $this->current_blog && ! $this->no_recursion ) {
$this->no_recursion = true;
$domains = $this->extract_variables_from_query( $q, 'domain' );
if ( $domains && $this->query_has_no_result( $q ) ) {
$q = $this->transpose_query_if_one_domain_is_matching( $q, $domains );
}
$this->no_recursion = false;
}
return $q;
}
/**
* method set_private_properties
*/
private function set_private_properties() {
global $wpdb, $table_prefix, $current_blog;
$this->wpdb = $wpdb;
$this->table_prefix = $table_prefix;
$this->current_blog = $current_blog;
}
/**
* @param string $query
*
* @return array
*/
private function extract_variables_from_query( $query, $field ) {
$variables = array();
$patterns = array(
'#WHERE\s+' . $field . '\s+IN\s*\(([^\)]+)\)#',
'#WHERE\s+' . $field . '\s*=\s*([^\s]+)#',
'#AND\s+' . $field . '\s+IN\s*\(([^\)]+)\)#',
'#AND\s+' . $field . '\s*=\s*([^\s]+)#',
);
foreach ( $patterns as $pattern ) {
$found = preg_match( $pattern, $query, $matches );
if ( $found && array_key_exists( 1, $matches ) ) {
$variables = $matches[1];
$variables = preg_replace( '/\s+/', '', $variables );
$variables = preg_replace( '/[\'"]/', '', $variables );
$variables = explode( ',', $variables );
break;
}
}
return $variables;
}
/**
* @param string $q
*
* @return bool
*/
private function query_has_no_result( $q ) {
return ! (bool) $this->wpdb->get_row( $q );
}
/**
* @param string $q
* @param array $domains
*
* @return string
*/
private function transpose_query_if_one_domain_is_matching( $q, $domains ) {
$paths = $this->extract_variables_from_query( $q, 'path' );
// Create as many placeholders as $paths we have.
$placeholders = implode( ',', array_fill( 0, sizeof( $paths ), '%s' ) );
// Array with all the parameters for preparing the SQL.
$parameters = $paths;
$parameters[] = BLOG_ID_CURRENT_SITE;
// The ORDER is there to get the default site at the end of the results.
$blogs = $this->wpdb->get_col(
$this->wpdb->prepare(
"SELECT blog_id FROM {$this->wpdb->blogs} WHERE path IN ($placeholders) ORDER BY blog_id = %d",
$parameters
)
);
$found_blog_id = null;
foreach ( (array) $blogs as $blog_id ) {
$prefix = $this->table_prefix;
if ( $blog_id > 1 ) {
$prefix .= $blog_id . '_';
}
$icl_settings = $this->wpdb->get_var( "SELECT option_value FROM {$prefix}options WHERE option_name = 'icl_sitepress_settings'" );
if ( $icl_settings ) {
$icl_settings = unserialize( $icl_settings );
if ( $icl_settings && 2 === (int) $icl_settings['language_negotiation_type'] ) {
$found_blog_id = $this->get_blog_id_from_domain( $domains, $icl_settings, $blog_id );
if ( $found_blog_id ) {
$q = $this->wpdb->prepare( "SELECT blog_id FROM {$this->wpdb->blogs} WHERE blog_id = %d", $found_blog_id );
break;
}
}
}
}
return $q;
}
/**
* @param array $domains
* @param array $wpml_settings
* @param int $blog_id
*
* @return mixed
*/
private function get_blog_id_from_domain( array $domains, array $wpml_settings, $blog_id ) {
foreach ( $domains as $domain ) {
if ( in_array( 'http://' . $domain, $wpml_settings['language_domains'], true ) ) {
return $blog_id;
} elseif ( in_array( $domain, $wpml_settings['language_domains'], true ) ) {
return $blog_id;
}
}
return null;
}
}
$wpml_sunrise_lang_in_domains = new WPML_Sunrise_Lang_In_Domains();
$wpml_sunrise_lang_in_domains->init();
/**
* WPML Sunrise Script - END
*/

View File

@@ -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 );
}
}

View File

@@ -0,0 +1,294 @@
<?php
use WPML\TM\ATE\Review\ReviewStatus;
/**
* 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 = (int) $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 = sprintf( ' s.status NOT IN ( %d, %d )', ICL_TM_NOT_TRANSLATED, ICL_TM_ATE_CANCELLED );
if ( $status ) {
if ( $status === ICL_TM_NEEDS_REVIEW ) {
$where .= $this->wpdb->prepare( ' AND (s.review_status = %s OR s.review_status = %s) ', ReviewStatus::EDITING, ReviewStatus::NEEDS_REVIEW );
} else {
$where .= $this->wpdb->prepare( ' AND s.status = %d AND (s.review_status IS NULL OR s.review_status = %s)', (int) $status, ReviewStatus::ACCEPTED );
}
}
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';
}
$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 ) {
$review_status = 's.review_status IS NOT NULL';
$translator_id_query_parts[] = ' j.translator_id = 0 OR j.translator_id IS NULL OR ' . $review_status;
}
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;
}
}

View File

@@ -0,0 +1,451 @@
<?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;
} else {
$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( [
'status' => apply_filters( 'wpml_tm_applied_job_status', ICL_TM_COMPLETE, $job, $new_post_id ),
'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 );
}
}

View File

@@ -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
)
);
}
}

View File

@@ -0,0 +1,40 @@
<?php
class WPML_Translation_Job_Helper {
public function encode_field_data( $data ) {
return null === $data ? '' : 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;
}
}

View File

@@ -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 ) );
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,143 @@
<?php
use WPML\FP\Obj;
use WPML\TM\API\Job\Map;
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;
$previousStatus = \WPML_TM_ICL_Translation_Status::makeByRid( $rid )->previous();
if (
$previousStatus->map( Obj::prop( 'status' ) )->getOrElse( null ) === (string) ICL_TM_ATE_CANCELLED
) {
$job_id = Map::fromRid( $rid );
} else {
$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 ) {
$job = wpml_tm_load_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 );
}
}
}

View File

@@ -0,0 +1,393 @@
<?php
require_once WPML_TM_PATH . '/inc/translation-jobs/jobs/wpml-translation-job.class.php';
use WPML\FP\Obj;
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' );
$this->job_factory = $job_factory ?: wpml_tm_load_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'] = Obj::prop('job_id', $this->basic_data);
$data_array['translation_id'] = Obj::prop('translation_id', $this->basic_data);
$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();
$status = ! empty( $this->basic_data->translated ) ? ICL_TM_COMPLETE : Obj::prop('status', $this->basic_data);
return TranslationManagement::get_job_status_string( $status, Obj::prop( 'needs_update', $this->basic_data ) );
}
/**
* @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' );
}
}

View File

@@ -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 ) ) : '';
}
}

View File

@@ -0,0 +1,294 @@
<?php
use WPML\FP\Obj;
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;
$job_id = $this->get_id();
$post_id = $this->get_resultant_element_id();
$data['complete'] = 1;
$data['job_id'] = $job_id;
$job = wpml_tm_load_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
{$wpdb->terms}.name,
{$wpdb->term_taxonomy}.description,
SUBSTR(translate.field_type, 3) original_term_id,
{$wpdb->terms}.term_id translated_term_id
FROM {$translate_table} translate
INNER JOIN {$translations_table} original_translation ON original_translation.element_id = SUBSTR(translate.field_type, 3)
INNER JOIN {$translations_table} translation ON translation.trid = original_translation.trid AND translation.language_code = %s
INNER JOIN {$wpdb->term_taxonomy} ON {$wpdb->term_taxonomy}.term_taxonomy_id = translation.element_id AND CONCAT('tax_', {$wpdb->term_taxonomy}.taxonomy) = translation.element_type
INNER JOIN {$wpdb->terms} ON {$wpdb->terms}.term_id = {$wpdb->term_taxonomy}.term_id
WHERE translate.job_id = %d AND translate.field_type LIKE 't\_%'
", $this->get_language_code(), $job_id );
$term_values = $wpdb->get_results( $get_target_terms_for_job_query );
foreach ( $term_values as $term ) {
if ( $delete ) {
$conditions = [
"field_type LIKE 'tfield-%-{$term->original_term_id}'", // Term fields
"field_type LIKE 'tfield-%-{$term->original_term_id}\_%'", // Term fields as array
"field_type = 't_{$term->original_term_id}'",
"field_type = 'tdesc_{$term->original_term_id}'",
];
$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->original_term_id, 'job_id' => $job_id ]
);
$wpdb->update(
$translate_table,
[ 'field_data_translated' => base64_encode( $term->description ), 'field_finished' => 1 ],
[ 'field_type' => 'tdesc_' . $term->original_term_id, 'job_id' => $job_id ]
);
$meta_values = $wpdb->get_results( "SELECT meta_key, meta_value FROM {$wpdb->termmeta} WHERE term_id = {$term->translated_term_id}" );
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->original_term_id ]
);
}
}
}
}
/**
* @param array $args
*
* @return array
*/
protected function filter_is_translator_args( array $args ) {
return Obj::assoc( 'post_id', $this->get_original_element_id(), $args );
}
}

View File

@@ -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
)
);
}
}

View File

@@ -0,0 +1,431 @@
<?php
use WPML\FP\Obj;
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_review_status() {
$this->maybe_load_basic_data();
return $this->basic_data->review_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,
$this->filter_is_translator_args( [
'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 array $args
*
* @return array
*/
protected function filter_is_translator_args( array $args ) {
return $args;
}
/**
* @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 ( Obj::prop( 'translation_service', $this->basic_data ) == TranslationProxy::get_current_service_id() ) {
$this->basic_data->translator_name = TranslationProxy_Translator::get_translator_name( Obj::prop('translator_id', $this->basic_data) );
} 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'] = Obj::prop('batch_id', $job_data);
$data_array['source_language_code'] = Obj::prop('source_language_code', $this->basic_data);
$data_array['language_code'] = Obj::prop('language_code', $this->basic_data);
$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 ( Obj::prop( 'translation_service', $job ) !== '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'
)
. '" />&nbsp;';
$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( ' &raquo; ' )
. $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
*/
public function get_basic_data_property( $name ) {
$this->maybe_load_basic_data();
return Obj::prop( $name, $this->basic_data );
}
/**
* @param string $name
* @param mixed $value
*/
public function set_basic_data_property( $name, $value ) {
$this->basic_data = Obj::assoc( $name, $value, $this->basic_data );
}
}

View File

@@ -0,0 +1,200 @@
<?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 ? TranslationProxy_Batch::get_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( TranslationProxy_Batch::get_generic_batch_name() );
}
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 );
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,44 @@
<?php
class WPML_Translator {
/** @var int */
public $ID;
/** @var string */
public $display_name;
/** @var string */
public $user_login;
/**
* Array where key represents a source language code and values are codes of target languages.
*
* @var array<string, string[]>
*/
public $language_pairs;
/**
* @param string $property
*
* @return int
*/
public function __get( $property ) {
if ( $property == 'translator_id' ) {
return $this->ID;
}
return null;
}
/**
* @param string $property
* @param int $value
*
* @return null
*/
public function __set( $property, $value ) {
if ( $property == 'translator_id' ) {
$this->ID = $value;
}
return null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
jQuery(function () {
jQuery('#icl-migrate-start').one('click', function () {
iclStepper(jQuery(this).attr('href'), '&init=1', true);
return false;
});
});
function iclStepper(href, addUrl, init) {
init = typeof(init) != 'undefined' ? true : false;
jQuery.ajax({
type: "POST",
url: href+addUrl,
cache: false,
dataType: 'json',
success: function(data) {
if (data.stop == true) {
jQuery('#icl-migrate-progress .message').html('stopped');
} else if (data.error == false) {
if (init == true) {
jQuery('#icl-migrate-start').fadeOut(function(){
jQuery('#icl-migrate-progress').fadeIn().html(data.output).children('.message').html(data.message);
iclStepper(href, '&step='+data.step);
});
// jQuery('#icl-migrate-progress').html(data.output).children('.message').html(data.message);
} else {
jQuery('#icl-migrate-progress .message').html(data.message);
jQuery('#icl-migrate-progress .progress').animate({
width : data.barWidth+'%'
}, 100);
if (data.completed == true) {
jQuery('#icl-migrate').delay(3000).fadeOut();
} else {
iclStepper(href, '&step='+data.step);
}
}
} else {
alert('error');
jQuery('#icl-migrate-progress .message').html(data.error);
}
}
});
}

View File

@@ -0,0 +1,116 @@
<?php
class Icl_Stepper {
/**
* Added dummy element to match number of elements and to cover init action
*
* @var array
*/
protected $_steps = array( null );
/**
* Current step
*
* @var int
*/
protected $_step;
/**
* Next step can be forced with Icl_Stepper::setNextStep()
*
* @var int
*/
protected $_nextStep = null;
/**
* Provide current step here
*
* @param int $step
*/
function __construct( $step = 0 ) {
if ( empty( $step ) ) {
$step = 0;
}
$this->_step = intval( $step );
}
/**
* Register steps (function names)
*/
public function registerSteps() {
$args = func_get_args();
$this->_steps = array_merge( $this->_steps, $args );
}
/**
* Returns current step
*
* @return int
*/
public function getStep() {
return $this->_step;
}
/**
* Returns next step
*
* @return int
*/
public function getNextStep() {
return ! is_null( $this->_nextStep ) ? $this->_nextStep : $this->_step += 1;
}
/**
* Sets current step
*
* @param int $num
*/
public function setStep( $num ) {
$this->_step = intval( $num );
}
/**
* Forcing next step
*
* @param int $num
*/
public function setNextStep( $num ) {
$this->_nextStep = intval( $num );
}
/**
* Calculates bar width
*
* @return int Should be used as percentage width (%)
*/
public function barWidth() {
return round( ( $this->_step * 100 ) / count( $this->_steps ) );
}
/**
* Calls current step's function
*
* @return mixed
*/
public function init() {
if ( $this->_step !== 0 && isset( $this->_steps[ $this->_step ] )
&& is_callable( $this->_steps[ $this->_step ] ) ) {
return call_user_func_array( $this->_steps[ $this->_step ], array( $this->_step, $this ) );
}
}
/**
* Returns initial HTML formatted screen
*
* @param string $message Message to be displayed
* @return string
*/
public function render( $message = '' ) {
$output = '<div class="progress-bar" style="width: 100%; height: 15px; background-color: #FFFFFFF; border: 1px solid #e6db55;">
<div class="progress" style="height: 100%; background-color: Yellow; width: ' . $this->barWidth() . '%;"></div>
</div>
<div class="message" style="clear:both;">' . $message . '</div>';
return $output;
}
}

View File

@@ -0,0 +1,92 @@
<?php
function update_string_statuses() {
global $wpdb;
$wpdb->update(
$wpdb->prefix . 'icl_string_translations',
array(
'status' => ICL_TM_COMPLETE,
'batch_id' => 0,
),
array(
'batch_id' => -1,
'status' => 1,
)
);
$wpdb->update(
$wpdb->prefix . 'icl_string_translations',
array(
'status' => ICL_TM_NEEDS_UPDATE,
'batch_id' => 0,
),
array(
'batch_id' => -1,
'status' => 2,
)
);
$wpdb->update(
$wpdb->prefix . 'icl_string_translations',
array(
'status' => ICL_TM_WAITING_FOR_TRANSLATOR,
'batch_id' => 0,
),
array(
'batch_id' => -1,
'status' => 11,
)
);
$sql = "ALTER TABLE `{$wpdb->prefix}icl_string_translations` CHANGE batch_id batch_id int DEFAULT 0 NOT NULL;";
$wpdb->query( $sql );
}
function fix_icl_string_status() {
global $wpdb;
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->prefix}icl_strings s
SET s.status = %d
WHERE EXISTS ( SELECT 1
FROM {$wpdb->prefix}icl_string_translations st
WHERE string_id = s.id AND st.status = %d )",
ICL_TM_NEEDS_UPDATE,
ICL_TM_NEEDS_UPDATE
)
);
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->prefix}icl_strings s
SET s.status = %d
WHERE ( SELECT COUNT(string_id)
FROM {$wpdb->prefix}icl_string_translations st
WHERE st.string_id = s.id AND st.status = %d ) = (( SELECT COUNT(*)
FROM {$wpdb->prefix}icl_languages
WHERE active = 1) - 1)",
ICL_TM_COMPLETE,
ICL_TM_COMPLETE
)
);
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->prefix}icl_strings s
SET s.status = %d
WHERE ( SELECT COUNT(string_id)
FROM {$wpdb->prefix}icl_string_translations st
WHERE st.string_id = s.id AND st.status = %d ) < (( SELECT COUNT(*)
FROM {$wpdb->prefix}icl_languages
WHERE active = 1) - 1)
AND ( SELECT COUNT(string_id)
FROM {$wpdb->prefix}icl_string_translations st2
WHERE st2.string_id = s.id AND st2.status = %d ) > 0 ",
2,
ICL_TM_COMPLETE,
ICL_TM_COMPLETE
)
);
}

View File

@@ -0,0 +1,319 @@
<?php
function icl_upgrade_2_0_0_steps( $step, $stepper ) {
global $wpdb, $sitepress, $wp_post_types, $sitepress_settings;
if ( ! isset( $sitepress ) ) {
$sitepress = new SitePress();
}
$TranslationManagement = new TranslationManagement();
$default_language = $sitepress->get_default_language();
define( 'ICL_TM_DISABLE_ALL_NOTIFICATIONS', true ); // make sure no notifications are being sent
ini_set( 'max_execution_time', 300 );
$post_types = array_keys( $wp_post_types );
foreach ( $post_types as $pt ) {
$types[] = 'post_' . $pt;
}
$temp_upgrade_data = get_option(
'icl_temp_upgrade_data',
array(
'step' => 0,
'offset' => 0,
)
);
switch ( $step ) {
case 1:
// if the tables are missing, call the plugin activation routine
$table_name = $wpdb->prefix . 'icl_translation_status';
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" ) != $table_name ) {
icl_sitepress_activate();
}
$wpdb->query( "ALTER TABLE `{$wpdb->prefix}icl_translations` CHANGE `element_type` `element_type` VARCHAR( 32 ) NOT NULL DEFAULT 'post_post'" );
$wpdb->query( "ALTER TABLE `{$wpdb->prefix}icl_translations` CHANGE `element_id` `element_id` BIGINT( 20 ) NULL DEFAULT NULL " );
// fix source_language_code
// all source documents must have null
if ( isset( $types ) ) {
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->prefix}icl_translations SET source_language_code = NULL
WHERE element_type IN('" . join( "','", $types ) . "') AND source_language_code = '' AND language_code=%s",
$default_language
)
);
// get translated documents with missing source language
$res = $wpdb->get_results(
$wpdb->prepare(
"
SELECT translation_id, trid, language_code
FROM {$wpdb->prefix}icl_translations
WHERE (source_language_code = '' OR source_language_code IS NULL)
AND element_type IN('" . join( "','", $types ) . "')
AND language_code <> %s
",
$default_language
)
);
foreach ( $res as $row ) {
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}icl_translations SET source_language_code = %s WHERE translation_id=%d", $default_language, $row->translation_id ) );
}
}
$temp_upgrade_data['step'] = 2;
update_option( 'icl_temp_upgrade_data', $temp_upgrade_data );
return array( 'message' => __( 'Processing translations...', 'sitepress' ) );
break;
case 2:
$limit = 100;
$offset = $temp_upgrade_data['offset'];
$processing = false;
// loop existing translations
if ( isset( $types ) ) {
$res = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}icl_translations
WHERE element_type IN(" . wpml_prepare_in( $types ) . ' )
AND source_language_code IS NULL LIMIT %d OFFSET %d',
array( $limit, $offset )
)
);
foreach ( $res as $row ) {
$processing = true;
// grab translations
$translations = $sitepress->get_element_translations( $row->trid, $row->element_type );
$md5 = 0;
$table_name = $wpdb->prefix . 'icl_node';
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" ) == $table_name ) {
list($md5, $links_fixed) = $wpdb->get_row(
$wpdb->prepare(
"
SELECT md5, links_fixed FROM {$wpdb->prefix}icl_node
WHERE nid = %d
",
$row->element_id
),
ARRAY_N
);
}
if ( ! $md5 ) {
$md5 = $TranslationManagement->post_md5( $row->element_id );
}
$translation_package = $TranslationManagement->create_translation_package( $row->element_id );
foreach ( $translations as $lang => $t ) {
if ( ! $t->original ) {
// determine service and status
$service = 'local';
$needs_update = 0;
list($rid, $status, $current_md5) = $wpdb->get_row(
$wpdb->prepare(
"
SELECT c.rid, n.status , c.md5
FROM {$wpdb->prefix}icl_content_status c
JOIN {$wpdb->prefix}icl_core_status n ON c.rid = n.rid
WHERE c.nid = %d AND target = %s
ORDER BY rid DESC
LIMIT 1
",
$row->element_id,
$lang
),
ARRAY_N
);
$translator_id = false;
if ( $rid ) {
if ( $current_md5 != $md5 ) {
$needs_update = 1;
}
if ( $status == 3 ) {
$status = 10;
} else {
$status = 2;
}
$service = 'icanlocalize';
foreach ( $sitepress_settings['icl_lang_status'] as $lpair ) {
if ( $lpair['from'] == $row->language_code && $lpair['to'] == $lang && isset( $lpair['translators'][0]['id'] ) ) {
$translator_id = $lpair['translators'][0]['id'];
break;
}
}
} else {
$status = 10;
$translator_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_author FROM {$wpdb->posts} WHERE ID=%d", $t->element_id ) );
$tlp = get_user_meta( $translator_id, $wpdb->prefix . 'language_pairs', true );
$tlp[ $row->language_code ][ $lang ] = 1;
if ( method_exists( $TranslationManagement, 'edit_translator' ) ) {
$TranslationManagement->edit_translator( $translator_id, $tlp );
}
}
// add translation_status record
list($newrid) = $TranslationManagement->update_translation_status(
array(
'translation_id' => $t->translation_id,
'status' => $status,
'translator_id' => $translator_id,
'needs_update' => $needs_update,
'md5' => $md5,
'translation_service' => $service,
'translation_package' => serialize( $translation_package ),
'links_fixed' => intval( isset( $links_fixed ) ? $links_fixed : 0 ),
)
);
$job_id = $TranslationManagement->add_translation_job( $newrid, $translator_id, $translation_package );
if ( $job_id && $status == 10 ) {
do_action( 'wpml_save_job_fields_from_post', $job_id );
}
}
}
}
}
if ( $processing ) {
update_option(
'icl_temp_upgrade_data',
array(
'step' => 2,
'offset' => intval( $offset + 100 ),
)
);
$stepper->setNextStep( 2 );
} else {
update_option(
'icl_temp_upgrade_data',
array(
'step' => 3,
'offset' => 99999999999999999999,
)
);
}
$message = $processing ? __( 'Processing translations...', 'sitepress' ) : __( 'Finalizing upgrade...', 'sitepress' );
return array( 'message' => $message );
break;
case 3:
// removing the plugins text table; importing data into a SitePress setting
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}icl_plugins_texts" );
if ( ! empty( $results ) ) {
foreach ( $results as $row ) {
$cft[ $row->attribute_name ] = $row->translate + 1;
}
if ( isset( $cft ) ) {
$iclsettings['translation-management']['custom_fields_translation'] = $cft;
$sitepress->save_settings( $iclsettings );
}
$wpdb->query( "DROP TABLE {$wpdb->prefix}icl_plugins_texts" );
}
$iclsettings['language_selector_initialized'] = 1;
if ( get_option( '_force_mp_post_http' ) ) {
$iclsettings['troubleshooting_options']['http_communication'] = intval( get_option( '_force_mp_post_http' ) );
delete_option( '_force_mp_post_http' );
}
$sitepress->save_settings( $iclsettings );
$iclsettings['migrated_2_0_0'] = 1;
$sitepress->save_settings( $iclsettings );
delete_option( 'icl_temp_upgrade_data' );
return array(
'message' => __( 'Done', 'sitepress' ),
'completed' => 1,
);
break;
default:
return array(
'error' => __( 'Missing step', 'sitepress' ),
'stop' => 1,
);
}
}
// $iclsettings defined in upgrade.php
if ( empty( $iclsettings['migrated_2_0_0'] ) ) {
wp_enqueue_script( 'icl-stepper', ICL_PLUGIN_URL . '/inc/upgrade-functions/2.0.0/stepper.js', array( 'jquery' ) );
add_filter( 'admin_notices', 'icl_migrate_2_0_0' );
add_action( 'icl_ajx_custom_call', 'icl_ajx_upgrade_2_0_0', 1, 2 );
}
function icl_migrate_2_0_0() {
$ajax_action = 'wpml_upgrade_2_0_0';
$ajax_action_none = wp_create_nonce( $ajax_action );
$link = 'index.php?icl_ajx_action=' . $ajax_action . '&nonce=' . $ajax_action_none;
$txt = get_option( 'icl_temp_upgrade_data', false ) ? __( 'Resume Upgrade Process', 'sitepress' ) : __( 'Run Upgrade Process', 'sitepress' );
echo '<div class="message error" id="icl-migrate"><p><strong>' . __( 'WPML requires database upgrade', 'sitepress' ) . '</strong></p>'
. '<p>' . __( 'This normally takes a few seconds, but may last up to several minutes of very large databases.', 'sitepress' ) . '</p>'
. '<p><a href="' . $link . '" style="" id="icl-migrate-start">' . $txt . '</a></p>'
. '<div id="icl-migrate-progress" style="display:none; margin: 10px 0 20px 0;">'
. '</div></div>';
}
function icl_ajx_upgrade_2_0_0( $call, $request ) {
if ( $call == 'wpml_upgrade_2_0_0' ) {
$error = 0;
$completed = 0;
$stop = 0;
$message = __( 'Starting the upgrade process...', 'sitepress' );
include_once WPML_PLUGIN_PATH . '/inc/upgrade-functions/2.0.0/stepper.php';
include_once WPML_PLUGIN_PATH . '/inc/upgrade-functions/upgrade-2.0.0.php';
$temp_upgrade_data = get_option(
'icl_temp_upgrade_data',
array(
'step' => 0,
'offset' => 0,
)
);
$step = isset( $request['step'] ) ? $request['step'] : $temp_upgrade_data['step'];
$migration = new Icl_Stepper( $step );
$migration->registerSteps(
'icl_upgrade_2_0_0_steps',
'icl_upgrade_2_0_0_steps',
'icl_upgrade_2_0_0_steps'
);
if ( isset( $request['init'] ) ) {
echo json_encode(
array(
'error' => $error,
'output' => $migration->render(),
'step' => $migration->getNextStep(),
'message' => __( 'Creating new tables...', 'sitepress' ),
'stop' => $stop,
)
);
exit;
}
$data = $migration->init();
@extract( $data, EXTR_OVERWRITE );
echo json_encode(
array(
'error' => $error,
'completed' => $completed,
'message' => $message,
'step' => $migration->getNextStep(),
'barWidth' => $migration->barWidth(),
'stop' => $stop,
)
);
}
}

Some files were not shown because too many files have changed in this diff Show More