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,67 @@
<?php
namespace WPML\UrlHandling;
use WPML\LIB\WP\Option;
use function WPML\Container\make;
class WPLoginUrlConverterRules implements \IWPML_Action {
const MARKED_FOR_UPDATE_AND_VALIDATE_OR_ROLLBACK = 3;
const MARKED_FOR_UPDATE = true;
const UNMARKED = false;
const UPDATE_RULES_KEY = 'wpml_login_page_translation_update_rules';
public function add_hooks() {
if ( Option::getOr( self::UPDATE_RULES_KEY, self::UNMARKED ) ) {
add_filter( 'init', [ self::class, 'update' ] );
}
}
public static function markRulesForUpdating( $verify = false ) {
Option::update(
self::UPDATE_RULES_KEY,
$verify ? self::MARKED_FOR_UPDATE_AND_VALIDATE_OR_ROLLBACK : self::MARKED_FOR_UPDATE
);
}
public static function update() {
global $wp_rewrite;
if ( ! function_exists( 'save_mod_rewrite_rules' ) ) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
$wp_rewrite->rewrite_rules();
save_mod_rewrite_rules();
$wp_rewrite->flush_rules( false );
$needsValidation = self::MARKED_FOR_UPDATE_AND_VALIDATE_OR_ROLLBACK === (int) Option::get( self::UPDATE_RULES_KEY );
Option::update( self::UPDATE_RULES_KEY, self::UNMARKED );
if ( $needsValidation ) {
static::validateOrDisable();
}
}
/**
* Validates that the Translated Login URL is accessible.
* Used to validate the setting when enabled by default.
*/
public static function validateOrDisable() {
$translationLangs = \WPML\Setup\Option::getTranslationLangs();
if ( empty( $translationLangs ) ) {
return;
}
/** @var \WPML_URL_Converter $urlConverter */
$urlConverter = make( \WPML_URL_Converter::class );
$newUrl = $urlConverter->convert_url( wp_login_url(), $translationLangs[0] );
$loginResponseCode = wp_remote_retrieve_response_code( wp_remote_get( $newUrl ) );
if ( 200 !== $loginResponseCode ) {
WPLoginUrlConverter::disable();
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
use WPML\FP\Str;
use WPML\FP\Lst;
use WPML\FP\Fns;
use WPML\FP\Wrapper;
use function WPML\FP\pipe;
class WPML_Absolute_Links_Blacklist {
private $blacklist_requests;
public function __construct( $blacklist_requests ) {
$this->blacklist_requests = $blacklist_requests;
if ( ! is_array( $this->blacklist_requests ) ) {
$this->blacklist_requests = array();
}
}
public function is_blacklisted( $request ) {
$isBlacklisted = function ( $request ) {
return Lst::includes( $request, $this->blacklist_requests )
|| $this->is_blacklisted_with_regex( $request );
};
return Wrapper::of( $request )
->map( Str::split( '/' ) )
->map( Fns::map( Fns::unary( pipe( 'urlencode', 'strtolower' ) ) ) )
->map( Lst::join( '/' ) )
->map( $isBlacklisted )
->get();
}
private function is_blacklisted_with_regex( $request ) {
foreach ( $this->blacklist_requests as $blacklist_request ) {
if ( $this->is_regex( $blacklist_request ) && preg_match( $blacklist_request, $request ) ) {
return true;
}
}
return false;
}
private function is_regex( $blacklist_request ) {
return strpos( $blacklist_request, '/' ) === 0 && strrpos( $blacklist_request, '/' ) === strlen( $blacklist_request ) - 1;
}
}

View File

@@ -0,0 +1,146 @@
<?php
use WPML\Utils\AutoAdjustIds;
class WPML_Absolute_To_Permalinks {
private $taxonomies_query;
private $lang;
/** @var SitePress $sitepress */
private $sitepress;
/** @var AutoAdjustIds $auto_adjust_ids */
private $auto_adjust_ids;
public function __construct( SitePress $sitepress, AutoAdjustIds $auto_adjust_ids = null ) {
$this->sitepress = $sitepress;
$this->auto_adjust_ids = $auto_adjust_ids ?: new AutoAdjustIds( $sitepress );
}
public function convert_text( $text ) {
$this->lang = $this->sitepress->get_current_language();
$active_langs_reg_ex = implode( '|', array_keys( $this->sitepress->get_active_languages() ) );
if ( ! $this->taxonomies_query ) {
$this->taxonomies_query = new WPML_WP_Taxonomy_Query( $this->sitepress->get_wp_api() );
}
$home = rtrim( $this->sitepress->get_wp_api()->get_option( 'home' ), '/' );
$parts = parse_url( $home );
$abshome = $parts['scheme'] . '://' . $parts['host'];
$path = isset( $parts['path'] ) ? ltrim( $parts['path'], '/' ) : '';
$tx_qvs = join( '|', $this->taxonomies_query->get_query_vars() );
$reg_ex = '@<a([^>]+)?href="((' . $abshome . ')?/' . $path . '/?(' . $active_langs_reg_ex . ')?\?(p|page_id|cat_ID|' . $tx_qvs . ')=([0-9a-z-]+))(#?[^"]*)"([^>]+)?>@i';
$text = preg_replace_callback( $reg_ex, [ $this, 'show_permalinks_cb' ], $text );
return $text;
}
function show_permalinks_cb( $matches ) {
$parts = $this->get_found_parts( $matches );
$url = $this->auto_adjust_ids->runWith(
function() use ( $parts ) {
return $this->get_url( $parts );
}
);
if ( $this->sitepress->get_wp_api()->is_wp_error( $url ) || empty( $url ) ) {
return $parts->whole;
}
$fragment = $this->get_fragment( $url, $parts );
if ( 'widget_text' == $this->sitepress->get_wp_api()->current_filter() ) {
$url = $this->sitepress->convert_url( $url );
}
return '<a' . $parts->pre_href . 'href="' . $url . $fragment . '"' . $parts->trail . '>';
}
private function get_found_parts( $matches ) {
return (object) array(
'whole' => $matches[0],
'pre_href' => $matches[1],
'content_type' => $matches[5],
'id' => $matches[6],
'fragment' => $matches[7],
'trail' => isset( $matches[8] ) ? $matches[8] : '',
);
}
private function get_url( $parts ) {
$tax = $this->taxonomies_query->find( $parts->content_type );
if ( $parts->content_type == 'cat_ID' ) {
$url = $this->sitepress->get_wp_api()->get_category_link( $parts->id );
} elseif ( $tax ) {
$url = $this->sitepress->get_wp_api()->get_term_link( $parts->id, $tax );
} else {
$url = $this->sitepress->get_wp_api()->get_permalink( $parts->id );
}
return $url;
}
private function get_fragment( $url, $parts ) {
$fragment = $parts->fragment;
$fragment = $this->remove_query_in_wrong_lang( $fragment );
if ( $fragment != '' ) {
$fragment = str_replace( '&#038;', '&', $fragment );
$fragment = str_replace( '&amp;', '&', $fragment );
if ( $fragment[0] == '&' ) {
if ( strpos( $fragment, '?' ) === false && strpos( $url, '?' ) === false ) {
$fragment[0] = '?';
}
}
if ( strpos( $url, '?' ) ) {
$fragment = $this->check_for_duplicate_lang_query( $fragment, $url );
}
}
return $fragment;
}
private function remove_query_in_wrong_lang( $fragment ) {
if ( $fragment != '' ) {
$fragment = str_replace( '&#038;', '&', $fragment );
$fragment = str_replace( '&amp;', '&', $fragment );
$start = $fragment[0];
parse_str( substr( $fragment, 1 ), $fragment_query );
if ( isset( $fragment_query['lang'] ) ) {
if ( $fragment_query['lang'] != $this->lang ) {
unset( $fragment_query['lang'] );
$fragment = build_query( $fragment_query );
if ( strlen( $fragment ) ) {
$fragment = $start . $fragment;
}
}
}
}
return $fragment;
}
private function check_for_duplicate_lang_query( $fragment, $url ) {
$url_parts = explode( '?', $url );
parse_str( $url_parts[1], $url_query );
if ( isset( $url_query['lang'] ) ) {
parse_str( substr( $fragment, 1 ), $fragment_query );
if ( isset( $fragment_query['lang'] ) ) {
unset( $fragment_query['lang'] );
$fragment = build_query( $fragment_query );
if ( strlen( $fragment ) ) {
$fragment = '&' . $fragment;
}
}
}
return $fragment;
}
}

View File

@@ -0,0 +1,33 @@
<?php
class WPML_Allowed_Redirect_Hosts extends WPML_SP_User {
public function __construct( &$sitepress ) {
parent::__construct( $sitepress );
}
public function get_hosts( $hosts ) {
$domains = $this->sitepress->get_setting( 'language_domains' );
$default_language = $this->sitepress->get_default_language();
$default_home = $this->sitepress->convert_url( $this->sitepress->get_wp_api()->get_home_url(), $default_language );
$home_schema = wpml_parse_url( $default_home, PHP_URL_SCHEME ) . '://';
if ( ! isset( $domains[ $default_language ] ) ) {
$domains[ $default_language ] = wpml_parse_url( $default_home, PHP_URL_HOST );
}
$active_languages = $this->sitepress->get_active_languages();
foreach ( $domains as $code => $url ) {
if ( ! empty( $active_languages[ $code ] ) ) {
$url = $home_schema . $url;
$parts = wpml_parse_url( $url );
if ( isset( $parts['host'] ) && ! in_array( $parts['host'], $hosts ) ) {
$hosts[] = $parts['host'];
}
}
}
return $hosts;
}
}

View File

@@ -0,0 +1,28 @@
<?php
class WPML_Endpoints_Support_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader, IWPML_Deferred_Action_Loader {
public function get_load_action() {
return 'plugins_loaded';
}
/**
* @return WPML_Endpoints_Support
*/
public function create() {
global $sitepress, $wpml_post_translations;
if ( $this->are_st_functions_loaded() ) {
return new WPML_Endpoints_Support( $wpml_post_translations, $sitepress->get_current_language(), $sitepress->get_default_language() );
}
return null;
}
/**
* @return bool
*/
private function are_st_functions_loaded() {
return function_exists( 'icl_get_string_id' );
}
}

View File

@@ -0,0 +1,281 @@
<?php
class WPML_Endpoints_Support {
const REGISTERED_ENDPOINTS_OPTION_KEY = 'wpml_registered_endpoints';
const STRING_CONTEXT = 'WP Endpoints';
/**
* @var WPML_Post_Translation
*/
private $post_translations;
/**
* @var string
*/
private $current_language;
/**
* @var string
*/
private $default_language;
public function __construct( WPML_Post_Translation $post_translations, $current_language, $default_language ) {
$this->post_translations = $post_translations;
$this->current_language = $current_language;
$this->default_language = $default_language;
}
public function add_hooks() {
add_action( 'init', array( $this, 'add_endpoints_translations' ) );
add_action( 'generate_rewrite_rules', array( $this, 'register_rewrite_rules_strings' ) );
add_filter( 'option_rewrite_rules', array(
$this,
'translate_endpoints_in_rewrite_rules'
), 0, 1 ); // high priority
add_filter( 'page_link', array( $this, 'endpoint_permalink_filter' ), 10, 2 );
add_filter( 'wpml_ls_language_url', array( $this, 'add_endpoint_to_current_ls_language_url' ), 10, 2 );
add_filter( 'wpml_get_endpoint_translation', array( $this, 'get_endpoint_translation' ), 10, 3 );
add_filter( 'wpml_register_endpoint_string', array( $this, 'register_endpoint_string' ), 10, 2 );
add_filter( 'wpml_get_endpoint_url', array( $this, 'get_endpoint_url' ), 10, 4 );
add_filter( 'wpml_get_current_endpoint', array( $this, 'get_current_endpoint' ) );
add_filter( 'wpml_get_registered_endpoints', array( $this, 'get_registered_endpoints' ) );
}
public function add_endpoints_translations() {
if ( $this->default_language !== $this->current_language ) {
$registered_endpoints = $this->get_registered_endpoints();
if ( $registered_endpoints ) {
foreach ( $registered_endpoints as $endpoint_key => $endpoint_value ) {
$endpoint_translation = $this->get_endpoint_translation( $endpoint_key, $endpoint_value );
if ( $endpoint_value !== urldecode( $endpoint_translation ) ) {
add_rewrite_endpoint( $endpoint_translation, EP_ROOT | EP_PAGES );
}
}
}
}
do_action( 'wpml_after_add_endpoints_translations', $this->current_language );
}
/**
* @param WP_Rewrite $wp_rewrite
*/
public function register_rewrite_rules_strings( $wp_rewrite ) {
$registered_endpoints = array();
foreach ( $wp_rewrite->endpoints as $endpoint ) {
$this->register_endpoint_string( $endpoint[1], $endpoint[2] );
$registered_endpoints[ $endpoint[1] ] = $endpoint[2];
}
update_option( self::REGISTERED_ENDPOINTS_OPTION_KEY, $registered_endpoints );
}
/**
* @param string $key
* @param string $endpoint
* @param null|string $language
*
* @return string
*/
public function get_endpoint_translation( $key, $endpoint, $language = null ) {
$this->register_endpoint_string( $key, $endpoint );
$endpoint_translation = apply_filters( 'wpml_translate_single_string', $endpoint, self::STRING_CONTEXT, $key, $language ? $language : $this->current_language );
if ( ! empty( $endpoint_translation ) ) {
return implode( '/', array_map( 'rawurlencode', explode( '/', $endpoint_translation ) ) );
} else {
return $endpoint;
}
}
/**
* @param string $key
* @param string $endpoint
*/
public function register_endpoint_string( $key, $endpoint ) {
$string = icl_get_string_id( $endpoint, self::STRING_CONTEXT, $key );
if ( ! $string ) {
icl_register_string( self::STRING_CONTEXT, $key, $endpoint );
}
}
/**
* @param array $value
*
* @return array
*/
public function translate_endpoints_in_rewrite_rules( $value ) {
if ( ! empty( $value ) ) {
$registered_endpoints = $this->get_registered_endpoints();
if ( $registered_endpoints ) {
foreach ( $registered_endpoints as $endpoint_key => $endpoint_value ) {
$endpoint_translation = $this->get_endpoint_translation( $endpoint_key, $endpoint_value );
if ( $endpoint_value === urldecode( $endpoint_translation ) ) {
continue;
}
$buff_value = array();
foreach ( $value as $k => $v ) {
$k = preg_replace( '/(\/|^)' . $endpoint_value . '(\/)?(\(\/\(\.\*\)\)\?\/\?\$)/', '$1' . $endpoint_translation . '$2$3', $k );
$buff_value[ $k ] = $v;
}
$value = $buff_value;
}
}
}
return $value;
}
/**
* @param string $link
* @param int $pid
*
* @return string
*/
public function endpoint_permalink_filter( $link, $pid ) {
global $post, $wp;
if ( isset( $post->ID ) && $post->ID && ! is_admin() ) {
$page_lang = $this->post_translations->get_element_lang_code( $post->ID );
if ( ! $page_lang ) {
return $link;
}
if ( $pid === $post->ID ) {
$pid_in_page_lang = $pid;
} else {
$translations = $this->post_translations->get_element_translations( $pid );
$pid_in_page_lang = isset( $translations[ $page_lang ] ) ? $translations[ $page_lang ] : $pid;
}
$current_lang = apply_filters( 'wpml_current_language', $this->current_language );
if (
(
$current_lang != $page_lang &&
$pid_in_page_lang == $post->ID
) ||
apply_filters( 'wpml_endpoint_force_permalink_filter', false, $current_lang, $page_lang )
) {
$endpoints = $this->get_registered_endpoints();
foreach ( $endpoints as $key => $endpoint ) {
if ( isset( $wp->query_vars[ $key ] ) ) {
list( $link, $endpoint ) = apply_filters( 'wpml_endpoint_permalink_filter', array( $link, $endpoint ), $key );
$link = $this->get_endpoint_url( $this->get_endpoint_translation( $key, $endpoint, $current_lang ), $wp->query_vars[ $key ], $link, $page_lang );
}
}
}
}
return $link;
}
/**
* @param string $endpoint
* @param string $value
* @param string $permalink
* @param bool|string $page_lang
*
* @return string
*/
public function get_endpoint_url( $endpoint, $value = '', $permalink = '', $page_lang = false ) {
$value = apply_filters( 'wpml_endpoint_url_value', $value, $page_lang );
if ( get_option( 'permalink_structure' ) ) {
$query_string = '';
if ( strstr( $permalink, '?' ) ) {
$query_string = '?' . parse_url( $permalink, PHP_URL_QUERY );
$permalink = current( explode( '?', $permalink ) );
}
$url = trailingslashit( $permalink ) . $endpoint . '/' . $value . $query_string;
} else {
$url = add_query_arg( $endpoint, $value, $permalink );
}
return $url;
}
/**
* @param string $url
* @param array $data
*
* @return string
*/
public function add_endpoint_to_current_ls_language_url( $url, $data ) {
global $post;
$post_lang = '';
$current_endpoint = array();
if ( isset( $post->ID ) && $post->ID ) {
$post_lang = $this->post_translations->get_element_lang_code( $post->ID );
$current_endpoint = $this->get_current_endpoint( $data['code'] );
if ( $post_lang === $data['code'] && $current_endpoint ) {
$url = $this->get_endpoint_url( $current_endpoint['key'], $current_endpoint['value'], $url );
}
}
return apply_filters( 'wpml_current_ls_language_url_endpoint', $url, $post_lang, $data, $current_endpoint );
}
/**
* @return array
*/
public function get_registered_endpoints() {
return get_option( self::REGISTERED_ENDPOINTS_OPTION_KEY, array() );
}
/**
* @param string $language
*
* @return array
*/
public function get_current_endpoint( $language ) {
global $wp;
$current_endpoint = array();
foreach ( $this->get_registered_endpoints() as $key => $value ) {
if ( isset( $wp->query_vars[ $key ] ) ) {
$current_endpoint['key'] = $this->get_endpoint_translation( $key, $value, $language );
$current_endpoint['value'] = $wp->query_vars[ $key ];
break;
}
}
return $current_endpoint;
}
}

View File

@@ -0,0 +1,60 @@
<?php
class WPML_Home_Url_Filter_Context {
const REST_REQUEST = 'rest-request';
const REWRITE_RULES = 'rewrite-rules';
const PAGINATION = 'pagination';
/**
* @var int
*/
private $language_negotiation_type;
/**
* @var string
*/
private $orig_scheme;
/**
* @var WPML_Debug_BackTrace
*/
private $debug_backtrace;
public function __construct( $language_negotiation_type, $orig_scheme, WPML_Debug_BackTrace $debug_backtrace ) {
$this->language_negotiation_type = $language_negotiation_type;
$this->orig_scheme = $orig_scheme;
$this->debug_backtrace = $debug_backtrace;
}
/**
* @return bool
*/
public function should_not_filter() {
return $this->is_rest_request()
|| $this->rewriting_rules()
|| $this->pagination_link();
}
/**
* @return bool
*/
private function is_rest_request() {
return in_array( $this->orig_scheme, array( 'json', 'rest' ), true );
}
/**
* @return bool
*/
private function rewriting_rules() {
return $this->debug_backtrace->is_class_function_in_call_stack( 'WP_Rewrite', 'rewrite_rules' );
}
/**
* @return bool
*/
private function pagination_link() {
return WPML_LANGUAGE_NEGOTIATION_TYPE_PARAMETER === $this->language_negotiation_type
&& $this->debug_backtrace->is_function_in_call_stack( 'get_pagenum_link' );
}
}

View File

@@ -0,0 +1,63 @@
<?php
class WPML_Include_Url extends WPML_WPDB_User {
private $unfiltered_home_url;
private $requested_host;
public function __construct( &$wpdb, $requested_host ) {
parent::__construct( $wpdb );
add_filter( 'style_loader_src', array( $this, 'filter_include_url' ) );
add_filter( 'script_loader_src', array( $this, 'filter_include_url' ) );
add_filter( 'the_password_form', array( $this, 'wpml_password_form_filter' ) );
$this->requested_host = $requested_host;
}
public function filter_include_url( $result ) {
$domains = wpml_get_setting_filter( array(), 'language_domains' );
$domains = preg_replace( '#^(http(?:s?))://#', '', array_map( 'untrailingslashit', $domains ) );
if ( (bool) $domains === true ) {
$php_host_in_domain = wpml_parse_url( $result, PHP_URL_HOST );
$domains[] = wpml_parse_url( $this->get_unfiltered_home(), PHP_URL_HOST );
foreach ( $domains as $dom ) {
if ( strpos( trailingslashit( $php_host_in_domain ), trailingslashit( $dom ) ) === 0 ) {
$http_host_parts = explode( ':', $this->requested_host );
unset( $http_host_parts[1] );
$http_host_without_port = implode( $http_host_parts );
$result = str_replace( $php_host_in_domain, $http_host_without_port, $result );
break;
}
}
}
return $result;
}
public function wpml_password_form_filter( $form ) {
if ( preg_match( '/action="(.*?)"/', $form, $matches ) ) {
$new_url = $this->filter_include_url( $matches[1] );
$form_action = str_replace( $matches[1], $new_url, $matches[0] );
$form = str_replace( $matches[0], $form_action, $form );
}
return $form;
}
/**
* Returns the value of the unfiltered home option directly from the wp_options table.
*
* @return string
*/
public function get_unfiltered_home() {
$this->unfiltered_home_url = $this->unfiltered_home_url
? $this->unfiltered_home_url
: $this->wpdb->get_var(
" SELECT option_value
FROM {$this->wpdb->options}
WHERE option_name = 'home'
LIMIT 1"
);
return $this->unfiltered_home_url;
}
}

View File

@@ -0,0 +1,20 @@
<?php
class WPML_Language_Domains {
private $domains;
public function __construct(
SitePress $sitepress,
WPML_URL_Converter_Url_Helper $converter_url_helper
) {
$this->domains = $sitepress->get_setting( 'language_domains' );
$this->domains[ $sitepress->get_default_language() ] = wpml_parse_url( $converter_url_helper->get_abs_home(), PHP_URL_HOST );
}
public function get( $lang ) {
return $this->domains[ $lang ];
}
}

View File

@@ -0,0 +1,298 @@
<?php
class WPML_Root_Page {
public static function init() {
if ( self::uses_html_root()
|| self::get_root_id() > 0
|| strpos( (string) filter_var( $_SERVER['REQUEST_URI'] ), 'wpml_root_page=1' ) !== false
|| (bool) filter_input( INPUT_POST, '_wpml_root_page' ) === true
) {
global $wpml_root_page_actions;
$wpml_root_page_actions = wpml_get_root_page_actions_obj();
add_action( 'init', array( $wpml_root_page_actions, 'wpml_home_url_init' ), 0 );
add_filter( 'wp_page_menu_args', array( $wpml_root_page_actions, 'wpml_home_url_exclude_root_page_from_menus' ) );
add_filter( 'wp_get_nav_menu_items', array( $wpml_root_page_actions, 'exclude_root_page_menu_item' ), 10, 1 );
add_filter( 'wp_list_pages_excludes', array( $wpml_root_page_actions, 'wpml_home_url_exclude_root_page' ) );
add_filter( 'page_attributes_dropdown_pages_args', array( $wpml_root_page_actions, 'wpml_home_url_exclude_root_page2' ) );
add_filter( 'get_pages', array( $wpml_root_page_actions, 'wpml_home_url_get_pages' ) );
add_action( 'save_post', array( $wpml_root_page_actions, 'wpml_home_url_save_post_actions' ), 0, 2 );
if ( self::get_root_id() > 0 ) {
add_filter( 'template_include', array( 'WPML_Root_Page', 'wpml_home_url_template_include' ) );
add_filter( 'the_preview', array( 'WPML_Root_Page', 'front_page_id_filter' ) );
}
$root_page_actions = wpml_get_root_page_actions_obj();
add_action( 'icl_set_element_language', array( $root_page_actions, 'delete_root_page_lang' ), 10, 0 );
}
}
/**
* Checks if the value in $_SERVER['REQUEST_URI] points towards the root page.
* Therefore this can be used to check if the current request points towards the root page.
*
* @return bool
*/
public static function is_current_request_root() {
return self::is_root_page( $_SERVER['REQUEST_URI'] );
}
/**
* @param string $requested_url
* Checks if a requested url points towards the root page.
*
* @return bool
*/
public static function is_root_page( $requested_url ) {
$cached_val = wp_cache_get( md5( $requested_url ) );
if ( $cached_val !== false ) {
return (bool) $cached_val;
}
$request_parts = self::get_slugs_and_get_query( $requested_url );
$slugs = $request_parts['slugs'];
$gets = $request_parts['querystring'];
$target_of_gets = self::get_query_target_from_query_string( $gets );
if ( $target_of_gets == WPML_QUERY_IS_ROOT ) {
$result = true;
} elseif ( $target_of_gets == WPML_QUERY_IS_OTHER_THAN_ROOT || $target_of_gets == WPML_QUERY_IS_NOT_FOR_POST && self::query_points_to_archive( $gets ) ) {
$result = false;
} else {
$result = self::slugs_point_to_root( $slugs );
}
wp_cache_add( md5( $requested_url ), ( $result === true ? 1 : 0 ) );
return $result;
}
public static function uses_html_root() {
$urls = icl_get_setting( 'urls' );
return isset( $urls['root_page'] ) && isset( $urls['show_on_root'] ) && $urls['directory_for_default_language'] && 'html_file' === $urls['show_on_root'];
}
/**
* Returns the id of the root page or false if it isn't set.
*
* @return bool|int
*/
public static function get_root_id() {
$root_actions = wpml_get_root_page_actions_obj();
return $root_actions->get_root_page_id();
}
/**
* Returns the slug of the root page or false if non exists.
*
* @return bool|string
*/
private static function get_root_slug() {
$root_id = self::get_root_id();
$root_slug = false;
if ( $root_id ) {
$root_page_object = get_post( $root_id );
if ( $root_page_object && isset( $root_page_object->post_name ) ) {
$root_slug = $root_page_object->post_name;
}
}
return $root_slug;
}
/**
* @param string $requested_url
* Takes a request_url in the format of $_SERVER['REQUEST_URI']
* and returns an associative array containing its slugs ans query string.
*
* @return array
*/
private static function get_slugs_and_get_query( $requested_url ) {
$result = array();
$request_path = wpml_parse_url( $requested_url, PHP_URL_PATH );
$request_path = wpml_strip_subdir_from_url( $request_path );
$slugs = self::get_slugs_array( $request_path );
$result['slugs'] = $slugs;
$query_string = wpml_parse_url( $requested_url, PHP_URL_QUERY );
$result['querystring'] = ! $query_string ? '' : $query_string;
return $result;
}
/**
* @param string $path
* Turns a query string into an array of its slugs.
* The array is filtered so to not contain empty values and
* consecutively and numerically indexed starting at 0.
*
* @return array
*/
private static function get_slugs_array( $path ) {
$slugs = explode( '/', $path );
$slugs = array_filter( $slugs );
$slugs = array_values( $slugs );
return $slugs;
}
/**
* @param array $slugs
* Checks if a given set of slugs points towards the root page or not.
* The result of this can always be overridden by GET parameters and is not a certain
* check as to being on the root page or not.
*
* @return bool
*/
private static function slugs_point_to_root( $slugs ) {
$result = true;
if ( ! empty( $slugs ) ) {
$root_slug = self::get_root_slug();
$last_slug = array_pop( $slugs );
$second_slug = array_pop( $slugs );
$third_slug = array_pop( $slugs );
if ( ( $root_slug != $last_slug && ! is_numeric( $last_slug ) )
|| ( is_numeric( $last_slug )
&& $second_slug != null
&& $root_slug !== $second_slug
&& ( ( 'page' !== $second_slug )
|| ( 'page' === $second_slug && ( $third_slug && $third_slug != $root_slug ) ) ) )
) {
$result = false;
}
}
return $result;
}
/**
* Turns a given query string into an associative array of its parameters.
*
* @param string $query_string
* @return array<string,string>
*/
private static function get_query_array_from_string( $query_string ) {
$all_query_params = array();
parse_str( $query_string, $all_query_params );
return $all_query_params;
}
/**
* @param string $query_string
* Checks if the WP_Query functionality can decisively recognize if a querystring points
* towards an archive.
*
* @return bool
*/
private static function query_points_to_archive( $query_string ) {
$root_page_actions = wpml_get_root_page_actions_obj();
remove_action( 'parse_query', array( $root_page_actions, 'wpml_home_url_parse_query' ) );
$query_string = str_replace( '?', '', $query_string );
$query = new WP_Query( $query_string );
$is_archive = $query->is_archive();
add_action( 'parse_query', array( $root_page_actions, 'wpml_home_url_parse_query' ) );
return $is_archive;
}
/**
* @param string $query_string
* Checks if a given query string decisively points towards or away from the root page.
*
* @return int
*/
private static function get_query_target_from_query_string( $query_string ) {
$params_array = self::get_query_array_from_string( $query_string );
return self::get_query_target_from_params_array( $params_array );
}
/**
* @param array $query_params
* Checks if a set of query parameters decisively points towards or away from the root page.
*
* @return int
*/
private static function get_query_target_from_params_array( $query_params ) {
if ( ! isset( $query_params['p'] )
&& ! isset( $query_params['page_id'] )
&& ! isset( $query_params['name'] )
&& ! isset( $query_params['pagename'] )
&& ! isset( $query_params['page_name'] )
&& ! isset( $query_params['attachment_id'] )
) {
$result = WPML_QUERY_IS_NOT_FOR_POST;
} else {
$root_id = self::get_root_id();
$root_slug = self::get_root_slug();
if ( ( isset( $query_params['p'] ) && $query_params['p'] != $root_id )
|| ( isset( $query_params['page_id'] ) && $query_params['page_id'] != $root_id )
|| ( isset( $query_params['name'] ) && $query_params['name'] != $root_slug )
|| ( isset( $query_params['pagename'] ) && $query_params['pagename'] != $root_slug )
|| ( isset( $query_params['preview_id'] ) && $query_params['preview_id'] != $root_id )
|| ( isset( $query_params['attachment_id'] ) && $query_params['attachment_id'] != $root_id )
) {
$result = WPML_QUERY_IS_OTHER_THAN_ROOT;
} else {
$result = WPML_QUERY_IS_ROOT;
}
}
return $result;
}
/**
* @param false|WP_Post $post
* Filters the postID used by the preview for the case of the root page preview.
*
* @return null|WP_Post
*/
public static function front_page_id_filter( $post ) {
$preview_id = isset( $_GET['preview_id'] ) ? $_GET['preview_id'] : - 1;
if ( $preview_id == self::get_root_id() ) {
$post = get_post( $preview_id );
}
return $post;
}
/**
* Filters the template that is used for the root page
*
* @param string $template
*
* @return string
*/
public static function wpml_home_url_template_include( $template ) {
return self::is_current_request_root() ? self::get_root_page_template() : $template;
}
/**
* @return string
*/
public static function get_root_page_template() {
$page_template = get_page_template();
if ( $page_template ) {
return $page_template;
}
$singular_template = get_singular_template();
return $singular_template ?: get_index_template();
}
}

View File

@@ -0,0 +1,139 @@
<?php
class WPML_Score_Hierarchy {
private $data = array();
private $slugs = array();
/**
* WPML_Score_Hierarchy constructor.
*
* @param object[] $data_set
* @param string[] $slugs
*/
public function __construct( $data_set, $slugs ) {
$this->data = $data_set;
$this->slugs = $slugs;
}
/**
* Array of best matched post_ids. Better matches have a lower index!
*
* @return int[]
*/
public function get_possible_ids_ordered() {
$pages_with_name = $this->data;
$slugs = $this->slugs;
$parent_slugs = array_slice( $slugs, 0, - 1 );
foreach ( $this->data as $key => $page ) {
if ( $page->parent_name ) {
$slug_pos = array_keys( $slugs, $page->post_name, true );
$par_slug_pos = array_keys( $slugs, $page->parent_name, true );
if ( (bool) $par_slug_pos !== false
&& (bool) $slug_pos !== false
) {
$remove = true;
foreach ( $slug_pos as $child_slug_pos ) {
if ( in_array( $child_slug_pos - 1, $par_slug_pos ) ) {
$remove = false;
break;
}
}
if ( $remove === true ) {
unset( $pages_with_name[ $key ] );
}
}
}
}
$related_ids = array();
$matching_ids = array();
foreach ( $pages_with_name as $key => $page ) {
$correct_slug = end( $slugs );
if ( $page->post_name === $correct_slug ) {
if ( $this->is_exactly_matching_all_slugs_in_order( $page ) ) {
$matching_ids[] = (int) $page->ID;
} else {
$related_ids[ $page->ID ] = $this->calculate_score( $parent_slugs, $pages_with_name, $page );
}
}
}
arsort( $related_ids );
$related_ids = array_keys( $related_ids );
return array(
'matching_ids' => $matching_ids,
'related_ids' => $related_ids,
);
}
/**
* Get page object by its id.
*
* @param int $id
*
* @return false|object
*/
private function get_page_by_id( $id ) {
foreach ( $this->data as $page ) {
if ( (int) $page->ID === (int) $id ) {
return $page;
}
}
return false;
}
/**
* @param string[] $parent_slugs
* @param object[] $pages_with_name
* @param object $page
*
* @return int
*/
private function calculate_score( $parent_slugs, $pages_with_name, $page ) {
$parent_positions = array_keys( $parent_slugs, $page->parent_name, true );
$new_score = (bool) $parent_positions === true ? max( $parent_positions ) + 1 : ( $page->post_parent ? - 1 : 0 );
if ( $page->post_parent ) {
foreach ( $pages_with_name as $parent ) {
if ( $parent->ID == $page->post_parent ) {
$new_score += $this->calculate_score( array_slice( $parent_slugs, 0, count( $parent_slugs ) - 1 ), $pages_with_name, $parent );
break;
}
}
}
return $new_score;
}
/**
* @param stdClass $page
*
* @return bool
*/
private function is_exactly_matching_all_slugs_in_order( $page ) {
return $this->slugs === $this->get_slugs_for_page( $page );
}
/**
* @param stdClass $current_page
*
* @return array
*/
private function get_slugs_for_page( $current_page ) {
$slugs = array();
while ( $current_page && $current_page->post_name ) {
$slugs[] = $current_page->post_name;
$parent_name = $current_page->parent_name;
$current_page = $this->get_page_by_id( $current_page->post_parent );
if ( ! $current_page && $parent_name ) {
$slugs[] = $parent_name;
}
}
return array_reverse( $slugs );
}
}

View File

@@ -0,0 +1,11 @@
<?php
class WPML_URL_HTTP_Referer_Factory {
/**
* @return WPML_URL_HTTP_Referer
*/
public function create() {
return new WPML_URL_HTTP_Referer( new WPML_Rest( new WP_Http() ) );
}
}

View File

@@ -0,0 +1,80 @@
<?php
class WPML_URL_HTTP_Referer {
private $rest;
public function __construct( WPML_Rest $rest ) {
$this->rest = $rest;
}
/**
* @param string $backup_url
*
* @return string
*/
public function get_url( $backup_url ) {
if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {
return $backup_url;
}
if ( WPML_Ajax::is_admin_ajax_request_called_from_frontend( $backup_url )
|| $this->is_rest_request_called_from_post_edit_page()
) {
return $_SERVER['HTTP_REFERER'];
}
return $backup_url;
}
/**
* @return bool|int
*/
public function get_trid() {
$referer_data = $request_uri_data = array();
if ( array_key_exists( 'HTTP_REFERER', $_SERVER ) ) {
$query = (string) wpml_parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_QUERY );
parse_str( $query, $referer_data );
}
if ( array_key_exists( 'REQUEST_URI', $_SERVER ) ) {
$request_uri = (string) wpml_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
parse_str( $request_uri, $request_uri_data );
}
/**
* trid from `HTTP_REFERER` should be return only if `REQUEST_URI` also has trid set.
*
* @link https://onthegosystems.myjetbrains.com/youtrack/issue/wpmltm-1351
*
* Or when it is a rest request called in the post edit page (Gutenberg)
* @link https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-5265
*/
return array_key_exists( 'trid', $referer_data ) && array_key_exists( 'trid', $request_uri_data )
|| ( array_key_exists( 'trid', $referer_data ) && $this->is_rest_request_called_from_post_edit_page() )
? (int) $referer_data['trid']
: false;
}
/**
* We need this in order to detect the language when adding
* translation from inside of a Gutenberg page while
* they don't provide a JS API which allows us to do it
*
* @link https://github.com/WordPress/gutenberg/issues/5958
* @link https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-5265
*
* @return bool
*/
public function is_rest_request_called_from_post_edit_page() {
return $this->rest->is_rest_request() && self::is_post_edit_page();
}
public static function is_post_edit_page() {
return isset( $_SERVER['HTTP_REFERER'] )
&& ( strpos( $_SERVER['HTTP_REFERER'], 'wp-admin/post.php' )
|| strpos( $_SERVER['HTTP_REFERER'], 'wp-admin/post-new.php' )
|| strpos( $_SERVER['HTTP_REFERER'], 'wp-admin/edit.php' ) );
}
}

View File

@@ -0,0 +1,28 @@
<?php
use WPML\API\Sanitize;
class WPML_WP_In_Subdir_URL_Filters_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader {
public function create() {
/**
* @var WPML_URL_Converter $wpml_url_converter
* @var SitePress $sitepress
*/
global $wpml_url_converter, $sitepress;
$lang_negotiation_type = $sitepress->get_setting( 'language_negotiation_type', false );
if ( WPML_LANGUAGE_NEGOTIATION_TYPE_DIRECTORY === (int) $lang_negotiation_type ) {
$request_uri = Sanitize::stringProp( 'REQUEST_URI', $_SERVER );
$uri_without_subdir = wpml_strip_subdir_from_url( $request_uri );
if ( trim( $request_uri, '/' ) !== trim( $uri_without_subdir, '/' ) ) {
$backtrace = new WPML_Debug_BackTrace( null, 5 );
return new WPML_WP_In_Subdir_URL_Filters( $backtrace, $sitepress, $wpml_url_converter, $uri_without_subdir );
}
}
return null;
}
}

View File

@@ -0,0 +1,79 @@
<?php
class WPML_WP_In_Subdir_URL_Filters implements IWPML_Action {
/** @var WPML_Debug_BackTrace $backtrace */
private $backtrace;
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_URL_Converter $url_converter */
private $url_converter;
/** @var string $uri_without_subdir */
private $uri_without_subdir;
/**
* @param WPML_Debug_BackTrace $backtrace
* @param SitePress $sitepress
* @param WPML_URL_Converter $url_converter
* @param string $uri_without_subdir
*/
public function __construct(
WPML_Debug_BackTrace $backtrace,
SitePress $sitepress,
WPML_URL_Converter $url_converter,
$uri_without_subdir
) {
$this->url_converter = $url_converter;
$this->backtrace = $backtrace;
$this->sitepress = $sitepress;
$this->url_converter = $url_converter;
$this->uri_without_subdir = $uri_without_subdir;
}
public function add_hooks() {
add_filter( 'home_url', array( $this, 'home_url_filter_on_parse_request' ), PHP_INT_MAX );
}
/**
* This filter is only applied in `WP::parse_request` in order to get
* the proper URI cleanup base in `$home_path_regex`
*
* @param string $home_url
*
* @return string
*/
public function home_url_filter_on_parse_request( $home_url ) {
if ( $this->backtrace->is_class_function_in_call_stack( 'WP', 'parse_request' ) ) {
if ( $this->request_uri_begins_with_lang() ) {
/**
* The URL is already filtered with the lang directory.
* e.g. http://example.org/subdir/en/
*/
return $home_url;
}
/**
* We return the root URL of the WP install.
* e.g. http://example.org/subdir/
*/
return $this->url_converter->get_abs_home();
}
return $home_url;
}
/**
* @return bool
*/
private function request_uri_begins_with_lang() {
$active_lang_codes = array_keys( $this->sitepress->get_active_languages() );
$uri_parts = explode( '/', trim( wpml_parse_url( $this->uri_without_subdir, PHP_URL_PATH ), '/' ) );
return isset( $uri_parts[0] ) && in_array( $uri_parts[0], $active_lang_codes, true );
}
}

View File

@@ -0,0 +1,107 @@
<?php
use WPML\API\Sanitize;
class WPML_XDomain_Data_Parser {
const SCRIPT_HANDLER = 'wpml-xdomain-data';
/**
* @var array $settings
*/
private $settings;
private $encryptor;
/**
* WPML_XDomain_Data_Parser constructor.
*
* @param array<string,mixed> $settings
* @param \WPML_Data_Encryptor $encryptor
*/
public function __construct( &$settings, $encryptor ) {
$this->settings = &$settings;
$this->encryptor = $encryptor;
}
public function init_hooks() {
if ( ! isset( $this->settings['xdomain_data'] ) || $this->settings['xdomain_data'] != WPML_XDOMAIN_DATA_OFF ) {
add_action( 'init', array( $this, 'init' ) );
add_filter( 'wpml_get_cross_domain_language_data', array( $this, 'get_xdomain_data' ) );
add_filter( 'wpml_get_cross_domain_language_data', array( $this, 'get_xdomain_data' ) );
}
}
public function init() {
add_action( 'wp_ajax_switching_language', array( $this, 'send_xdomain_language_data' ) );
add_action( 'wp_ajax_nopriv_switching_language', array( $this, 'send_xdomain_language_data' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts_action' ), 100 );
}
public function register_scripts_action() {
if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
$ls_parameters = WPML_Language_Switcher::parameters();
$js_xdomain_data = array(
'css_selector' => $ls_parameters['css_prefix'] . 'item',
'ajax_url' => admin_url( 'admin-ajax.php' ),
'current_lang' => apply_filters( 'wpml_current_language', '' ),
);
wp_enqueue_script( self::SCRIPT_HANDLER, ICL_PLUGIN_URL . '/res/js/xdomain-data.js', array( 'jquery' ), ICL_SITEPRESS_VERSION );
wp_localize_script( self::SCRIPT_HANDLER, 'wpml_xdomain_data', $js_xdomain_data );
}
}
public function set_up_xdomain_language_data() {
$ret = array();
$data = apply_filters( 'WPML_cross_domain_language_data', array() );
$data = apply_filters( 'wpml_cross_domain_language_data', $data );
if ( ! empty( $data ) ) {
$encoded_data = json_encode( $data );
$encoded_data = $this->encryptor->encrypt( $encoded_data );
$base64_encoded_data = base64_encode( $encoded_data );
$ret['xdomain_data'] = urlencode( $base64_encoded_data );
$ret['method'] = WPML_XDOMAIN_DATA_POST == $this->settings['xdomain_data'] ? 'post' : 'get';
}
return $ret;
}
public function send_xdomain_language_data() {
$data = $this->set_up_xdomain_language_data();
wp_send_json_success( $data );
}
public function get_xdomain_data() {
$xdomain_data = array();
if ( isset( $_GET['xdomain_data'] ) || isset( $_POST['xdomain_data'] ) ) {
$xdomain_data_request = false;
if ( WPML_XDOMAIN_DATA_GET == $this->settings['xdomain_data'] ) {
$xdomain_data_request = Sanitize::stringProp( 'xdomain_data', $_GET );
} elseif ( WPML_XDOMAIN_DATA_POST == $this->settings['xdomain_data'] ) {
$xdomain_data_request = urldecode( Sanitize::stringProp( 'xdomain_data', $_POST ) );
}
if ( $xdomain_data_request ) {
$data = base64_decode( $xdomain_data_request );
$data = $this->encryptor->decrypt( $data );
$xdomain_data = (array) json_decode( $data, true );
}
}
return $xdomain_data;
}
}

View File

@@ -0,0 +1,77 @@
<?php
class WPML_Rewrite_Rules_Filter {
/**
* @var array
*/
private $active_languages;
/**
* @var WPML_URL_Filters
*/
private $wpml_url_filters;
/**
* @param array $active_languages
* @param WPML_URL_Filters $wpml_url_filters
*/
public function __construct( $active_languages, $wpml_url_filters = null ) {
$this->active_languages = $active_languages;
if ( ! $wpml_url_filters ) {
global $wpml_url_filters;
}
$this->wpml_url_filters = $wpml_url_filters;
}
/**
* @param string $htaccess_string Content of the .htaccess file
*
* @return string .htaccess file contents with adjusted RewriteBase
*/
public function rid_of_language_param( $htaccess_string ) {
if ( $this->wpml_url_filters->frontend_uses_root() || $this->is_permalink_page() || $this->is_shop_page() ) {
foreach ( $this->active_languages as $lang_code ) {
foreach ( array( '', 'index.php' ) as $base ) {
$htaccess_string = str_replace(
'/' . $lang_code . '/' . $base,
'/' . $base,
$htaccess_string
);
}
}
}
return $htaccess_string;
}
/**
* Check if it is permalink page in admin.
*
* @return bool
*/
private function is_permalink_page() {
return $this->is_admin_screen( 'options-permalink' );
}
/**
* Check if it is WooCommerce shop page in admin.
*
* @return bool
*/
private function is_shop_page() {
return $this->is_admin_screen( 'page' ) && get_option( 'woocommerce_shop_page_id' ) === intval( $_GET['post'] );
}
/**
* Check if it as a certain screen on admin page.
*
* @param string $screen_id
*
* @return bool
*/
private function is_admin_screen( $screen_id ) {
return is_admin() && function_exists( 'get_current_screen' ) && get_current_screen() && get_current_screen()->id === $screen_id;
}
}

View File

@@ -0,0 +1,56 @@
<?php
class WPML_URL_Cached_Converter extends WPML_URL_Converter {
/** @var string[] $cache */
private $cache;
/**
* @param string $url
* @param string|bool $lang_code
*
* @return string
*/
public function convert_url( $url, $lang_code = false ) {
global $sitepress;
if ( ! $lang_code ) {
$lang_code = $sitepress->get_current_language();
}
$negotiation_type = $sitepress->get_setting( 'language_negotiation_type' );
$skip_convert_url_string = $this->get_strategy()->skip_convert_url_string( $url, $lang_code );
$cache_key_args = array( $url, $lang_code, $negotiation_type, $skip_convert_url_string );
$cache_key = md5( wp_json_encode( $cache_key_args ) );
$cache_group = 'convert_url';
$cache_found = false;
$cache = new WPML_WP_Cache( $cache_group );
$new_url = $cache->get( $cache_key, $cache_found );
if ( ! $cache_found ) {
$new_url = parent::convert_url( $url, $lang_code );
$cache->set( $cache_key, $new_url );
}
return $new_url;
}
/**
* @param string $url
*
* @return string
*/
public function get_language_from_url( $url ) {
if ( isset( $this->cache[ $url ] ) ) {
return $this->cache[ $url ];
}
$lang = parent::get_language_from_url( $url );
$this->cache[ $url ] = $lang;
return $lang;
}
}

View File

@@ -0,0 +1,51 @@
<?php
class WPML_URL_Converter_CPT {
/**
* @var WPML_Slash_Management
*/
private $slash_helper;
/**
* @param WPML_Slash_Management $slash_helper
*/
public function __construct( WPML_Slash_Management $slash_helper = null ) {
if ( ! $slash_helper ) {
$slash_helper = new WPML_Slash_Management();
}
$this->slash_helper = $slash_helper;
}
/**
* Adjusts the CPT archive slug for possible slug translations from ST.
*
* @param string $link
* @param string $post_type
* @param null|string $language_code
*
* @return string
*/
public function adjust_cpt_slug_in_url( $link, $post_type, $language_code = null ) {
$post_type_object = get_post_type_object( $post_type );
if ( isset( $post_type_object->rewrite ) ) {
$slug = trim( $post_type_object->rewrite['slug'], '/' );
} else {
$slug = $post_type_object->name;
}
$translated_slug = apply_filters( 'wpml_get_translated_slug', $slug, $post_type, $language_code );
if ( is_string( $translated_slug ) ) {
$link_parts = explode( '?', $link, 2 );
$pattern = '#\/' . preg_quote( $slug, '#' ) . '\/#';
$link_new = trailingslashit( preg_replace( $pattern, '/' . $translated_slug . '/', trailingslashit( $link_parts[0] ), 1 ) );
$link = $this->slash_helper->match_trailing_slash_to_reference( $link_new, $link_parts[0] );
$link = isset( $link_parts[1] ) ? $link . '?' . $link_parts[1] : $link;
}
return $link;
}
}

View File

@@ -0,0 +1,158 @@
<?php
class WPML_URL_Converter_Factory {
/**
* @var array
*/
private $settings;
/**
* @var string
*/
private $default_lang_code;
/**
* @var array
*/
private $active_language_codes;
/**
* @var WPML_Resolve_Object_Url_Helper_Factory
*/
private $object_url_helper_factory;
/** @var WPML_URL_Converter */
private static $previous_url_converter;
const SUBDIR = 1;
const DOMAIN = 2;
/**
* @param array $settings
* @param string $default_lang_code
* @param array $active_language_codes
*/
public function __construct( $settings, $default_lang_code, $active_language_codes ) {
$this->settings = $settings;
$this->default_lang_code = $default_lang_code;
$this->active_language_codes = $active_language_codes;
}
public static function remove_previous_hooks() {
if ( self::$previous_url_converter ) {
self::$previous_url_converter->get_strategy()->remove_hooks();
}
}
/**
* @return WPML_Resolve_Object_Url_Helper_Factory
*/
public function get_object_url_helper_factory() {
if ( ! $this->object_url_helper_factory ) {
$this->object_url_helper_factory = new WPML_Resolve_Object_Url_Helper_Factory();
}
return $this->object_url_helper_factory;
}
/**
* @param WPML_Resolve_Object_Url_Helper_Factory $factory
*/
public function set_object_url_helper_factory( WPML_Resolve_Object_Url_Helper_Factory $factory ) {
$this->object_url_helper_factory = $factory;
}
/**
* @param int $url_type
*
* @return WPML_URL_Converter
*/
public function create( $url_type ) {
switch ( $url_type ) {
case self::SUBDIR:
$wpml_url_converter = $this->create_subdir_converter();
break;
case self::DOMAIN:
$wpml_url_converter = $this->create_domain_converter();
break;
default:
$wpml_url_converter = $this->create_parameter_converter();
}
$home_url = new WPML_URL_Converter_Url_Helper();
$wpml_url_converter->set_url_helper( $home_url );
$wpml_url_converter->get_strategy()->add_hooks();
self::$previous_url_converter = $wpml_url_converter;
return $wpml_url_converter;
}
/**
* @return WPML_URL_Cached_Converter
*/
private function create_subdir_converter() {
$dir_default = false;
if ( ! isset( $this->settings['urls'] ) ) {
$this->settings['urls'] = array();
} else {
if ( isset( $this->settings['urls']['directory_for_default_language'] ) ) {
$dir_default = $this->settings['urls']['directory_for_default_language'];
}
}
$strategy = new WPML_URL_Converter_Subdir_Strategy( $dir_default, $this->default_lang_code, $this->active_language_codes, $this->settings['urls'] );
return new WPML_URL_Cached_Converter(
$strategy,
$this->get_object_url_helper_factory()->create(),
$this->default_lang_code,
$this->active_language_codes
);
}
/**
* @return WPML_URL_Cached_Converter
*/
private function create_domain_converter() {
$domains = isset( $this->settings['language_domains'] ) ? $this->settings['language_domains'] : array();
$wpml_wp_api = new WPML_WP_API();
$strategy = new WPML_URL_Converter_Domain_Strategy( $domains, $this->default_lang_code, $this->active_language_codes );
$wpml_url_converter = new WPML_URL_Cached_Converter(
$strategy,
$this->get_object_url_helper_factory()->create(),
$this->default_lang_code,
$this->active_language_codes
);
$wpml_fix_url_domain = new WPML_Lang_Domain_Filters(
$wpml_url_converter,
$wpml_wp_api,
new WPML_Debug_BackTrace( null, 10 )
);
$wpml_fix_url_domain->add_hooks();
$xdomain_data_parser = new WPML_XDomain_Data_Parser( $this->settings, new WPML_Data_Encryptor() );
$xdomain_data_parser->init_hooks();
return $wpml_url_converter;
}
/**
* @return WPML_URL_Cached_Converter
*/
private function create_parameter_converter() {
$strategy = new WPML_URL_Converter_Parameter_Strategy( $this->default_lang_code, $this->active_language_codes );
$wpml_url_converter = new WPML_URL_Cached_Converter(
$strategy,
$this->get_object_url_helper_factory()->create(),
$this->default_lang_code,
$this->active_language_codes
);
$filters = new WPML_Lang_Parameter_Filters();
$filters->add_hooks();
return $wpml_url_converter;
}
}

View File

@@ -0,0 +1,235 @@
<?php
/**
* Class WPML_URL_Converter
*
* @package wpml-core
* @subpackage url-handling
*/
use WPML\SuperGlobals\Server;
use WPML\UrlHandling\WPLoginUrlConverter;
class WPML_URL_Converter {
/**
* @var IWPML_URL_Converter_Strategy
*/
private $strategy;
/**
* @var string
*/
protected $default_language;
/**
* @var string[]
*/
protected $active_languages;
/**
* @var WPML_URL_Converter_Url_Helper
*/
protected $home_url_helper;
/**
* @var WPML_URL_Converter_Lang_Param_Helper
*/
protected $lang_param;
/**
* @var WPML_Slash_Management
*/
protected $slash_helper;
/**
* @var WPML_Resolve_Object_Url_Helper
*/
protected $object_url_helper;
/**
* @param IWPML_URL_Converter_Strategy $strategy
* @param WPML_Resolve_Object_Url_Helper $object_url_helper
* @param string $default_language
* @param array<string> $active_languages
*/
public function __construct(
IWPML_URL_Converter_Strategy $strategy,
WPML_Resolve_Object_Url_Helper $object_url_helper,
$default_language,
$active_languages
) {
$this->strategy = $strategy;
$this->object_url_helper = $object_url_helper;
$this->default_language = $default_language;
$this->active_languages = $active_languages;
$this->lang_param = new WPML_URL_Converter_Lang_Param_Helper( $active_languages );
$this->slash_helper = new WPML_Slash_Management();
}
/**
* @return IWPML_URL_Converter_Strategy
*/
public function get_strategy() {
return $this->strategy;
}
/**
* @param WPML_URL_Converter_Url_Helper $url_helper
*/
public function set_url_helper( WPML_URL_Converter_Url_Helper $url_helper ) {
$this->home_url_helper = $url_helper;
if ( $this->strategy instanceof WPML_URL_Converter_Abstract_Strategy ) {
$this->strategy->set_url_helper( $url_helper );
}
}
/**
* @return WPML_URL_Converter_Url_Helper
*/
public function get_url_helper() {
if ( ! $this->home_url_helper ) {
$this->home_url_helper = new WPML_URL_Converter_Url_Helper();
}
return $this->home_url_helper;
}
public function get_abs_home() {
return $this->get_url_helper()->get_abs_home();
}
/**
* @param WPML_URL_Converter_Lang_Param_Helper $lang_param_helper
*/
public function set_lang_param_helper( WPML_URL_Converter_Lang_Param_Helper $lang_param_helper ) {
$this->lang_param = $lang_param_helper;
}
/**
* @param WPML_Slash_Management $slash_helper
*/
public function set_slash_helper( WPML_Slash_Management $slash_helper ) {
$this->slash_helper = $slash_helper;
}
public function get_default_site_url() {
return $this->get_url_helper()->get_unfiltered_home_option();
}
/**
* Scope of this function:
* 1. Convert the home URL in the specified language depending on language negotiation:
* 1. Add a language directory
* 2. Change the domain
* 3. Add a language parameter
* 2. If the requested URL is equal to the current URL, the URI will be adapted
* with potential slug translations for:
* - single post slugs
* - taxonomy term slug
*
* WARNING: The URI slugs won't be translated for arbitrary URL (not the current one)
*
* @param string $url
* @param bool $lang_code
*
* @return bool|mixed|string
*/
public function convert_url( $url, $lang_code = false ) {
if ( ! $url ) {
return $url;
}
global $sitepress;
$new_url = false;
if ( ! $lang_code ) {
$lang_code = $sitepress->get_current_language();
}
$language_from_url = $this->get_language_from_url( $url );
if ( $language_from_url === $lang_code || 'all' === $lang_code ) {
$new_url = $url;
} else {
if ( $this->can_resolve_object_url( $url ) ) {
$new_url = $this->object_url_helper->resolve_object_url( $url, $lang_code );
}
if ( false === $new_url ) {
$new_url = $this->strategy->convert_url_string( $url, $lang_code );
}
}
return $new_url;
}
/**
* Takes a URL and returns the language of the document it points at
*
* @param string $url
* @return string
*/
public function get_language_from_url( $url ) {
$http_referer_factory = new WPML_URL_HTTP_Referer_Factory();
$http_referer = $http_referer_factory->create();
$url = $http_referer->get_url( $url );
$language = $this->lang_param->lang_by_param( $url ) ?: $this->get_strategy()->get_lang_from_url_string( $url );
/**
* Filters language code fetched from the current URL and allows to rewrite
* the language to set on front-end
*
* @param string $language language fetched from the current URL
* @param string $url current URL.
*/
$language = apply_filters( 'wpml_get_language_from_url', $language, $url );
return $this->get_strategy()->validate_language( $language, $url );
}
/**
* @param string $url
* @param string $language
*
* @return string
*/
public function get_home_url_relative( $url, $language ) {
return $this->get_strategy()->get_home_url_relative( $url, $language );
}
/**
* @param SitePress $sitepress
*
* @return WPLoginUrlConverter|null
*/
public function get_wp_login_url_converter( $sitepress ) {
return $this->strategy->use_wp_login_url_converter()
? new WPLoginUrlConverter( $sitepress, $this )
: null;
}
/**
* @param string $url
*
* @return bool
*/
private function can_resolve_object_url( $url ) {
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';
$server_name = strpos( $request_uri, '/' ) === 0
? untrailingslashit( Server::getServerName() ) : trailingslashit( Server::getServerName() );
$request_url = stripos( get_option( 'siteurl' ), 'https://' ) === 0
? 'https://' . $server_name . $request_uri : 'http://' . $server_name . $request_uri;
$is_request_url = trailingslashit( $request_url ) === trailingslashit( $url );
$is_home_url = trailingslashit( $this->get_url_helper()->get_abs_home() ) === trailingslashit( $url );
$is_home_url_filter = current_filter() === 'home_url';
return $is_request_url && ! $is_home_url && ! $is_home_url_filter;
}
/** @return WPML_URL_Converter */
public static function getGlobalInstance() {
global $wpml_url_converter;
return $wpml_url_converter;
}
}

View File

@@ -0,0 +1,167 @@
<?php
class WPML_Lang_Domain_Filters {
private $wpml_url_converter;
private $wpml_wp_api;
private $debug_backtrace;
private $domains = array();
/**
* WPML_Lang_Domain_Filters constructor.
*
* @param \WPML_URL_Converter $wpml_url_converter
* @param \WPML_WP_API $wpml_wp_api
*/
public function __construct(
WPML_URL_Converter $wpml_url_converter,
WPML_WP_API $wpml_wp_api,
WPML_Debug_BackTrace $debug_backtrace
) {
$this->wpml_url_converter = $wpml_url_converter;
$this->wpml_wp_api = $wpml_wp_api;
$this->debug_backtrace = $debug_backtrace;
}
public function add_hooks() {
add_filter( 'upload_dir', array( $this, 'upload_dir_filter_callback' ) );
add_filter( 'stylesheet_uri', array( $this, 'convert_url' ) );
add_filter( 'option_siteurl', array( $this, 'siteurl_callback' ) );
add_filter( 'content_url', array( $this, 'siteurl_callback' ) );
add_filter( 'plugins_url', array( $this, 'siteurl_callback' ) );
add_filter( 'login_url', array( $this, 'convert_url' ) );
add_filter( 'logout_url', array( $this, 'convert_logout_url' ) );
add_filter( 'admin_url', array( $this, 'admin_url_filter' ), 10, 2 );
add_filter( 'login_redirect', array( $this, 'convert_url' ), 1, 3 );
}
/**
* @param string $url
*
* @return string
*/
public function convert_url( $url ) {
return $this->wpml_url_converter->convert_url( $url );
}
/**
* @param array $upload_dir
*
* @return array
*/
public function upload_dir_filter_callback( $upload_dir ) {
$convertWithMatchingTrailingSlash = function ( $url ) {
$hasTrailingSlash = '/' === substr( $url, -1 );
$newUrl = $this->wpml_url_converter->convert_url( $url );
return $hasTrailingSlash ? trailingslashit( $newUrl ) : untrailingslashit( $newUrl );
};
$upload_dir['url'] = $convertWithMatchingTrailingSlash( $upload_dir['url'] );
$upload_dir['baseurl'] = $convertWithMatchingTrailingSlash( $upload_dir['baseurl'] );
return $upload_dir;
}
/**
* @param string $url
*
* @return string
*/
public function siteurl_callback( $url ) {
$getting_network_site_url = $this->debug_backtrace->is_function_in_call_stack( 'get_admin_url' ) && is_multisite();
if ( ! $this->debug_backtrace->is_function_in_call_stack( 'get_home_path', false ) && ! $getting_network_site_url ) {
$parsed_url = wpml_parse_url( $url );
$host = is_array( $parsed_url ) && isset( $parsed_url['host'] );
if ( $host && isset( $_SERVER['HTTP_HOST'] ) && $_SERVER['HTTP_HOST'] ) {
$domain_from_global = $this->get_host_from_HTTP_HOST();
if ( $domain_from_global ) {
$url = str_replace( $parsed_url['host'], $domain_from_global, $url );
}
}
}
return $url;
}
/**
* @return string
*/
private function get_host_from_HTTP_HOST() {
$host = $_SERVER['HTTP_HOST'];
if ( false !== strpos( $_SERVER['HTTP_HOST'], ':' ) ) {
$host = explode( ':', $_SERVER['HTTP_HOST'] );
$host = $host[0];
}
return $this->is_host_valid( $host ) ? $host : null;
}
/**
* @param string $host
*
* @return bool
*/
private function is_host_valid( $host ) {
$valid = false;
foreach ( $this->get_domains() as $domain ) {
if ( $domain === $host ) {
$valid = true;
break;
}
}
return $valid;
}
/**
* @return array
*/
private function get_domains() {
if ( ! $this->domains ) {
$this->domains = wpml_get_setting( 'language_domains' );
$home_parsed = wp_parse_url( $this->wpml_url_converter->get_abs_home() );
$this->domains[] = $home_parsed['host'];
}
return $this->domains;
}
/**
* @param string $url
* @param string $path
*
* @return string
*/
public function admin_url_filter( $url, $path ) {
if ( ( strpos( $url, 'http://' ) === 0
|| strpos( $url, 'https://' ) === 0 )
&& 'admin-ajax.php' === $path && $this->wpml_wp_api->is_front_end()
) {
global $sitepress;
$url = $this->wpml_url_converter->convert_url( $url, $sitepress->get_current_language() );
}
return $url;
}
/**
* Convert logout url only for front-end.
*
* @param string $logout_url
*
* @return string
*/
public function convert_logout_url( $logout_url ) {
if ( $this->wpml_wp_api->is_front_end() ) {
$logout_url = $this->wpml_url_converter->convert_url( $logout_url );
}
return $logout_url;
}
}

View File

@@ -0,0 +1,63 @@
<?php
class WPML_Lang_Parameter_Filters {
public function add_hooks() {
add_filter( 'request', array( $this, 'request_filter' ) );
add_filter( 'get_pagenum_link', array( $this, 'paginated_url_filter' ) );
add_filter( 'wp_link_pages_link', array( $this, 'paginated_link_filter' ) );
}
public function request_filter( $request ) {
// This is required so that home page detection works for other languages.
if ( ! defined( 'WP_ADMIN' ) && isset( $request['lang'] ) ) {
unset( $request['lang'] );
}
return $request;
}
/**
* Filters the pagination links on taxonomy archives to properly have the language parameter after the URI.
*
* @param string $url
*
* @return string
*/
public function paginated_url_filter( $url ) {
$url = urldecode( $url );
$parts = explode( '?', $url );
$last_part = count( $parts ) > 2 ? array_pop( $parts ) : '';
$url = join( '?', $parts );
$url = preg_replace( '#(.+?)(/\?|\?)(.*?)(/.+?$)$#', '$1$4$5?$3', $url );
$url = preg_replace( '#(\?.+)(%2F|\/)$#', '$1', $url );
if ( '' !== $last_part && strpos( $url, '?' . $last_part ) === false ) {
$url .= '&' . $last_part;
}
$parts = explode( '?', $url );
if ( isset( $parts[1] ) ) {
// Maybe remove duplicated lang param
$params = array();
parse_str( $parts[1], $params );
$url = $parts[0] . '?' . build_query( $params );
}
return $url;
}
/**
* Filters the pagination links on paginated posts and pages, acting on the links html
* output containing the anchor tag the link is a property of.
*
* @param string $link_html
*
* @return string
*
* @hook wp_link_pages_link
*/
public function paginated_link_filter( $link_html ) {
return preg_replace( '#"([^"].+?)(/\?|\?)([^/]+)(/[^"]+)"#', '"$1$4?$3"', $link_html );
}
}

View File

@@ -0,0 +1,15 @@
<?php
class WPML_Tax_Permalink_Filters_Factory implements IWPML_Frontend_Action_Loader, IWPML_Backend_Action_Loader {
public function create() {
global $wpml_url_converter, $sitepress;
return new WPML_Tax_Permalink_Filters(
$wpml_url_converter,
new WPML_WP_Cache_Factory(),
new WPML_Translation_Element_Factory( $sitepress ),
WPML_Get_LS_Languages_Status::get_instance()
);
}
}

View File

@@ -0,0 +1,96 @@
<?php
class WPML_Tax_Permalink_Filters implements IWPML_Action {
/** @var WPML_Translation_Element_Factory */
private $term_element_factory;
/** @var WPML_URL_Converter */
private $url_converter;
/** @var WPML_WP_Cache_Factory $cache_factory */
private $cache_factory;
/** @var WPML_Get_LS_Languages_Status */
private $ls_languages_status;
public function __construct(
WPML_URL_Converter $url_converter,
WPML_WP_Cache_Factory $cache_factory,
WPML_Translation_Element_Factory $term_element_factory,
WPML_Get_LS_Languages_Status $ls_language_status
) {
$this->term_element_factory = $term_element_factory;
$this->url_converter = $url_converter;
$this->cache_factory = $cache_factory;
$this->ls_languages_status = $ls_language_status;
}
public function add_hooks() {
add_filter( 'term_link', array( $this, 'cached_filter_tax_permalink' ), 1, 3 );
}
/**
* @param string $permalink
* @param int|\WP_Term|\stdClass $tag
* @param string $taxonomy
*
* @return string
*/
public function cached_filter_tax_permalink( $permalink, $tag, $taxonomy ) {
$tag = is_object( $tag ) ? $tag : get_term( $tag, $taxonomy );
$tag_id = $tag ? $tag->term_id : 0;
$cache = $this->cache_factory->create_cache_item( 'icl_tax_permalink_filter', array(
$tag_id,
$taxonomy,
$this->is_link_for_language_switcher()
) );
if ( $cache->exists() ) {
return $cache->get();
}
$permalink = $this->filter_tax_permalink( $permalink, $tag_id );
$cache->set( $permalink );
return $permalink;
}
/**
* Filters the permalink pointing at a taxonomy archive to correctly reflect the language of its underlying term
*
* @param string $permalink url pointing at a term's archive
* @param int $tag_id
*
* @return string
*/
private function filter_tax_permalink( $permalink, $tag_id ) {
if ( $tag_id ) {
$term_element = $this->term_element_factory->create( $tag_id, 'term' );
if ( ! $this->is_display_as_translated_and_in_default_lang( $term_element )
|| $this->is_link_for_language_switcher()
) {
$term_language = $term_element->get_language_code();
if ( (bool) $term_language ) {
$permalink = $this->url_converter->convert_url( $permalink, $term_language );
}
}
}
return $permalink;
}
private function is_display_as_translated_and_in_default_lang( WPML_Translation_Element $element ) {
return $element->is_display_as_translated()
&& $element->is_in_default_language();
}
private function is_link_for_language_switcher() {
return $this->ls_languages_status->is_getting_ls_languages();
}
}

View File

@@ -0,0 +1,109 @@
<?php
class WPML_URL_Converter_Lang_Param_Helper {
/**
* @var array
*/
private static $cache = array();
/**
* @var array
*/
private $active_languages;
/**
* @param array $active_languages
*/
public function __construct( array $active_languages ) {
$this->active_languages = $active_languages;
}
/**
*
* @param string $url
* @param bool $only_admin If set to true only language parameters on Admin Screen URLs will be recognized. The
* function will return null for non-Admin Screens.
*
* @return null|string Language code
*/
public function lang_by_param( $url, $only_admin = true ) {
if ( isset( self::$cache[ $url ] ) ) {
return self::$cache[ $url ];
}
$lang = $this->extract_lang_param_from_url( $url, $only_admin );
self::$cache[ $url ] = $lang;
return $lang;
}
/**
* @param string $url
* @param bool $only_admin
*
* @return string|null
*/
private function extract_lang_param_from_url( $url, $only_admin ) {
$url = wpml_strip_subdir_from_url( $url );
$url_query_parts = wpml_parse_url( $url );
$url_query = $this->has_query_part( $only_admin, $url_query_parts ) ? untrailingslashit( $url_query_parts['query'] ) : null;
if ( null !== $url_query ) {
parse_str( $url_query, $vars );
if ( $this->can_retrieve_lang_from_query( $only_admin, $vars ) ) {
return $vars['lang'];
}
}
return null;
}
/**
* @param bool $only_admin
* @param array $url_query_parts
*
* @return bool
*/
private function has_query_part( $only_admin, $url_query_parts ) {
if ( ! isset( $url_query_parts['query'] ) ) {
return false;
}
if ( false === $only_admin ) {
return true;
}
if ( ! isset( $url_query_parts['path'] ) ) {
return false;
}
if ( false === strpos( $url_query_parts['path'], '/wp-admin' ) ) {
return false;
}
return true;
}
/**
* @param bool $only_admin
* @param array $vars
*
* @return bool
*/
private function can_retrieve_lang_from_query( $only_admin, $vars ) {
if ( ! isset( $vars['lang'] ) ) {
return false;
}
if ( $only_admin && 'all' === $vars['lang'] ) {
return true;
}
if ( in_array( $vars['lang'], $this->active_languages, true ) ) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,99 @@
<?php
class WPML_URL_Converter_Url_Helper {
/**
* @var wpdb
*/
private $wpdb;
/**
* @var WPML_Include_Url
*/
private $wpml_include_url_filter;
/**
* @var string
*/
private $absolute_home;
/**
*
* @param wpdb $wpdb
* @param WPML_Include_Url $wpml_include_url_filter
*/
public function __construct( wpdb $wpdb = null, WPML_Include_Url $wpml_include_url_filter = null ) {
if ( ! $wpdb ) {
global $wpdb;
}
if ( ! $wpml_include_url_filter ) {
global $wpml_include_url_filter;
}
$this->wpdb = $wpdb;
$this->wpml_include_url_filter = $wpml_include_url_filter;
}
/**
* Returns the unfiltered home_url by directly retrieving it from wp_options.
*
* @return string
*/
public function get_abs_home() {
if ( ! $this->absolute_home ) {
$is_multisite = is_multisite();
if ( ! $is_multisite && defined( 'WP_HOME' ) ) {
$this->absolute_home = WP_HOME;
} elseif ( $is_multisite && ! is_main_site() ) {
$protocol = preg_match( '/^(https)/', get_option( 'home' ) ) === 1 ? 'https://' : 'http://';
$sql = "
SELECT CONCAT(b.domain, b.path)
FROM {$this->wpdb->blogs} b
WHERE blog_id = {$this->wpdb->blogid}
LIMIT 1
";
$this->absolute_home = $protocol . $this->wpdb->get_var( $sql );
} else {
$this->absolute_home = $this->get_unfiltered_home_option();
}
}
return apply_filters( 'wpml_url_converter_get_abs_home', $this->absolute_home );
}
/**
* Checks if a $url points to a WP Admin screen.
*
* @param string $url
* @return bool True if the input $url points to an admin screen.
*/
public function is_url_admin( $url ) {
$url_query_parts = wpml_parse_url( strpos( $url, 'http' ) === false ? 'http://' . $url : $url );
return isset( $url_query_parts['path'] )
&& strpos( wpml_strip_subdir_from_url( $url_query_parts['path'] ), '/wp-admin' ) === 0;
}
/**
* Returns the unfiltered home option from the database.
*
* @uses \WPML_Include_Url::get_unfiltered_home in case the $wpml_include_url_filter global is loaded
*
* @return string
*/
public function get_unfiltered_home_option() {
if ( $this->wpml_include_url_filter ) {
return $this->wpml_include_url_filter->get_unfiltered_home();
} else {
$sql = "
SELECT option_value
FROM {$this->wpdb->options}
WHERE option_name = 'home'
LIMIT 1
";
return $this->wpdb->get_var( $sql );
}
}
}

View File

@@ -0,0 +1,116 @@
<?php
abstract class WPML_URL_Converter_Abstract_Strategy implements IWPML_URL_Converter_Strategy {
protected $absolute_home;
protected $default_language;
protected $active_languages;
protected $cache;
/**
* @var WPML_URL_Converter_Url_Helper
*/
protected $url_helper;
/**
* @var WPML_URL_Converter_Lang_Param_Helper
*/
protected $lang_param;
/**
* @var WPML_Slash_Management
*/
protected $slash_helper;
/**
* @var WP_Rewrite
*/
protected $wp_rewrite;
/**
* @param string $default_language
* @param array<string> $active_languages
* @param WP_Rewrite|null $wp_rewrite
* @param WPML_Slash_Management|null $splash_helper
*/
public function __construct( $default_language, $active_languages, $wp_rewrite = null, $splash_helper = null ) {
$this->default_language = $default_language;
$this->active_languages = $active_languages;
$this->lang_param = new WPML_URL_Converter_Lang_Param_Helper( $active_languages );
$this->slash_helper = $splash_helper ?: new WPML_Slash_Management();
if ( ! $wp_rewrite ) {
global $wp_rewrite;
}
$this->wp_rewrite = $wp_rewrite;
}
public function validate_language( $language, $url ) {
return in_array( $language, $this->active_languages, true )
|| 'all' === $language && $this->get_url_helper()->is_url_admin( $url ) ? $language : $this->get_default_language();
}
/**
* @param WPML_URL_Converter_Url_Helper $url_helper
*/
public function set_url_helper( WPML_URL_Converter_Url_Helper $url_helper ) {
$this->url_helper = $url_helper;
}
/**
* @return WPML_URL_Converter_Url_Helper
*/
public function get_url_helper() {
if ( ! $this->url_helper ) {
$this->url_helper = new WPML_URL_Converter_Url_Helper();
}
return $this->url_helper;
}
/**
* @param WPML_URL_Converter_Lang_Param_Helper $lang_param
*/
public function set_lang_param( WPML_URL_Converter_Lang_Param_Helper $lang_param ) {
$this->lang_param = $lang_param;
}
/**
* @param WPML_Slash_Management $slash_helper
*/
public function set_slash_helper( WPML_Slash_Management $slash_helper ) {
$this->slash_helper = $slash_helper;
}
private function get_default_language() {
if ( $this->default_language ) {
return $this->default_language;
} else {
return icl_get_setting( 'default_language' );
}
}
public function fix_trailingslashit( $source_url ) {
return $source_url;
}
public function skip_convert_url_string( $source_url, $lang_code ) {
/**
* Allows plugins to skip url conversion.
*
* @since 4.3
*
* @param bool
* @param string $source_url
* @param string $lang_code
* @return bool
*/
return apply_filters( 'wpml_skip_convert_url_string', false, $source_url, $lang_code );
}
public function use_wp_login_url_converter() {
return false;
}
}

View File

@@ -0,0 +1,146 @@
<?php
use \WPML\SuperGlobals\Server;
class WPML_URL_Converter_Domain_Strategy extends WPML_URL_Converter_Abstract_Strategy {
/** @var string[] $domains */
private $domains = array();
/**
* @param array $domains
* @param string $default_language
* @param array $active_languages
*/
public function __construct(
$domains,
$default_language,
$active_languages
) {
parent::__construct( $default_language, $active_languages );
$domains = array_map( 'untrailingslashit', $domains );
$this->domains = array_map( array( $this, 'strip_protocol' ), $domains );
if ( isset( $this->domains[ $default_language ] ) ) {
unset( $this->domains[ $default_language ] );
}
}
public function add_hooks() {
add_filter( 'rest_url', [ $this, 'convertRestUrlToCurrentDomain' ], 10, 4 );
}
public function remove_hooks() {
remove_filter( 'rest_url', [ $this, 'convertRestUrlToCurrentDomain' ] );
}
/**
* Filter REST url to avoid CORS error in Gutenberg.
* https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlcore-7022
*
* @param string $url REST URL.
* @param string $path REST route.
* @param int $blog_id Blog ID.
* @param string $scheme Sanitization scheme.
*
* @return string
*/
public function convertRestUrlToCurrentDomain( $url, $path, $blog_id, $scheme ) {
$url_parts = $this->parse_domain_and_subdir( $url );
$url_parts['host'] = Server::getServerName();
$url = http_build_url( $url_parts );
return $url;
}
public function get_lang_from_url_string( $url ) {
$url = $this->strip_protocol( $url );
if ( strpos( $url, '?' ) ) {
$parts = explode( '?', $url );
$url = $parts[0];
}
foreach ( $this->domains as $code => $domain ) {
if ( $domain && strpos( trailingslashit( $url ), trailingslashit( $domain ) ) === 0 ) {
return $code;
}
}
return null;
}
public function convert_url_string( $source_url, $lang ) {
$original_source_url = untrailingslashit( $source_url );
if ( is_admin() && $this->get_url_helper()->is_url_admin( $original_source_url ) ) {
return $original_source_url;
}
return $this->convert_url( $source_url, $lang );
}
public function convert_admin_url_string( $source_url, $lang ) {
return $this->convert_url( $source_url, $lang );
}
private function convert_url( $source_url, $lang ) {
if ( $this->skip_convert_url_string( $source_url, $lang ) ) {
return $source_url;
}
$base_url = isset( $this->domains[ $lang ] ) ? $this->domains[ $lang ] : $this->get_url_helper()->get_abs_home();
$base_url_parts = $this->parse_domain_and_subdir( $base_url );
$url_parts = $this->parse_domain_and_subdir( $source_url );
if ( isset( $base_url_parts['host'] ) ) {
$url_parts['host'] = $base_url_parts['host'];
}
$converted_url = http_build_url( $url_parts );
return $this->slash_helper->maybe_user_trailingslashit( $converted_url );
}
/**
* @param string $base_url
*
* @return array
*/
private function parse_domain_and_subdir( $base_url ) {
$url_parts = wpml_parse_url( $base_url );
return is_array( $url_parts ) ?
$this->slash_helper->parse_missing_host_from_path( $url_parts ) :
[];
}
/**
* @param string $url
* @param string $language
*
* @return string
*/
public function get_home_url_relative( $url, $language ) {
return $url;
}
/**
* @param string $url
*
* @return array|string
*/
private function strip_protocol( $url ) {
$url_parts = wpml_parse_url( $url );
if ( is_array( $url_parts ) ) {
$url_parts = $this->slash_helper->parse_missing_host_from_path( $url_parts );
unset( $url_parts['scheme'] );
return http_build_url( $url_parts );
} else {
return preg_replace( '/^https?:\/\//', '', $url );
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
class WPML_URL_Converter_Parameter_Strategy extends WPML_URL_Converter_Abstract_Strategy {
public function add_hooks() {
}
public function remove_hooks() {
}
public function get_lang_from_url_string( $url ) {
return $this->lang_param->lang_by_param( $url, false );
}
public function convert_url_string( $source_url, $lang_code ) {
if ( $this->skip_convert_url_string( $source_url, $lang_code ) ) {
return $source_url;
}
if ( ! $lang_code || $lang_code === $this->default_language ) {
$lang_code = '';
}
$url_parts = wpml_parse_url( $source_url );
if ( ! is_array( $url_parts ) ) {
$url_parts = [];
}
// This is required for logout url, the parameter comes urlencoded in the URI generated by WordPress.
// If we don't decode it first, it will be misinterpreted by browsers when combined with regular parameters.
if ( isset( $url_parts['query'] ) ) {
$url_parts['query'] = str_replace( '&amp;_wpnonce', '&_wpnonce', $url_parts['query'] );
}
$query_args = $this->get_query_args( $url_parts );
if ( $lang_code ) {
$query_args['lang'] = $lang_code;
} else {
unset( $query_args['lang'] );
}
$url_parts['query'] = http_build_query( $query_args );
$converted_url = http_build_url( $url_parts );
return $this->slash_helper->maybe_user_trailingslashit( $converted_url );
}
public function convert_admin_url_string( $source_url, $lang ) {
return $this->convert_url_string( $source_url, $lang );
}
/**
* @param array $url_parts
*
* @return array
*/
private function get_query_args( array $url_parts ) {
$query = isset( $url_parts['query'] ) ? $url_parts['query'] : '';
$query = str_replace( '?', '&', $query );
parse_str( $query, $query_args );
return $query_args;
}
/**
* @param string $url
* @param string $language
*
* @return string
*/
public function get_home_url_relative( $url, $language ) {
if ( $language === $this->default_language ) {
$language = '';
}
if ( $language ) {
return add_query_arg( 'lang', $language, $url );
} else {
return $url;
}
}
/**
* @param string $source_url
*
* @return string
*/
public function fix_trailingslashit( $source_url ) {
$query = wpml_parse_url( $source_url, PHP_URL_QUERY );
if ( ! empty( $query ) ) {
$source_url = str_replace( '?' . $query, '', $source_url );
}
$source_url = $this->slash_helper->maybe_user_trailingslashit( $source_url );
if ( ! empty( $query ) ) {
$source_url .= '?' . untrailingslashit( $query );
}
return $source_url;
}
public function use_wp_login_url_converter() {
return true;
}
}

View File

@@ -0,0 +1,252 @@
<?php
class WPML_URL_Converter_Subdir_Strategy extends WPML_URL_Converter_Abstract_Strategy {
/** @var bool */
private $use_directory_for_default_lang;
/** @var array copy of $sitepress->get_settings( 'urls' ) */
private $urls_settings;
/** @var string|bool */
private $root_url;
/** @var array map of wpml codes to custom codes*/
private $language_codes_map;
private $language_codes_reverse_map;
/** @var bool */
private $is_rest_request;
/**
* @param bool $use_directory_for_default_lang
* @param string $default_language
* @param array $active_languages
* @param array $urls_settings
*/
public function __construct(
$use_directory_for_default_lang,
$default_language,
$active_languages,
$urls_settings
) {
parent::__construct( $default_language, $active_languages );
$this->use_directory_for_default_lang = (bool) $use_directory_for_default_lang;
$this->urls_settings = $urls_settings;
$this->language_codes_map = array_combine( $active_languages, $active_languages );
$this->language_codes_map = apply_filters( 'wpml_language_codes_map', $this->language_codes_map );
$this->language_codes_reverse_map = array_flip( $this->language_codes_map );
}
public function add_hooks() {
add_filter( 'rest_url', [ $this, 'convertRestUrl' ] );
}
public function remove_hooks() {
remove_filter( 'rest_url', [ $this, 'convertRestUrl' ] );
}
/**
* @param string $url
*
* @return string
*/
public function convertRestUrl( $url ) {
/** @var SitePress */
global $sitepress;
$matchTrailingSlash = $url[ strlen( $url ) - 1 ] === '/' ? 'trailingslashit' : 'untrailingslashit';
return $matchTrailingSlash( $this->convert_url_string( $url, $sitepress->get_current_language() ) );
}
public function get_lang_from_url_string( $url ) {
$url_path = $this->get_url_path( wpml_strip_subdir_from_url( $url ) );
$lang = $this->extract_lang_from_url_path( $url_path );
if ( $lang && in_array( $lang, $this->active_languages, true ) ) {
return $lang;
}
return $this->use_directory_for_default_lang ? null : $this->default_language;
}
public function validate_language( $language, $url ) {
if ( ! ( null === $language && $this->use_directory_for_default_lang && ! $this->get_url_helper()->is_url_admin( $url ) ) ) {
$language = parent::validate_language( $language, $url );
}
return $language;
}
public function convert_url_string( $source_url, $code ) {
if ( $this->is_root_url( $source_url ) || $this->skip_convert_url_string( $source_url, $code ) ) {
return $source_url;
}
$source_url = $this->filter_source_url( $source_url );
$absolute_home_url = trailingslashit( preg_replace( '#^(http|https)://#', '', $this->get_url_helper()->get_abs_home() ) );
$absolute_home_url = strpos( $source_url, $absolute_home_url ) === false ? trailingslashit( get_option( 'home' ) ) : $absolute_home_url;
$current_language = $this->get_lang_from_url_string( $source_url );
$code = $this->get_language_of_current_dir( $code, '' );
$current_language = $this->get_language_of_current_dir( $current_language, '' );
$code = isset( $this->language_codes_map[ $code ] ) ? $this->language_codes_map[ $code ] : $code;
$current_language = isset( $this->language_codes_map[ $current_language ] ) ? $this->language_codes_map[ $current_language ] : $current_language;
$source_url = str_replace(
[
trailingslashit( $absolute_home_url . $current_language ),
'/' . $code . '//',
],
[
$code ? ( $absolute_home_url . $code . '/' ) : trailingslashit( $absolute_home_url ),
'/' . $code . '/',
],
$source_url
);
return $this->slash_helper->maybe_user_trailingslashit( $source_url );
}
public function convert_admin_url_string( $source_url, $lang ) {
return $source_url; // Admin strings should not be converted with language in directories
}
/**
* @param string $url
* @param string $language
*
* @return string
*/
public function get_home_url_relative( $url, $language ) {
$language = $this->get_language_of_current_dir( $language, '' );
$language = isset( $this->language_codes_map[ $language ] ) ? $this->language_codes_map[ $language ] : $language;
if ( $language ) {
$parts = parse_url( get_option( 'home' ) );
$path = isset( $parts['path'] ) ? $parts['path'] : '';
$url = preg_replace( '@^' . $path . '@', '', $url );
return rtrim( $path, '/' ) . '/' . $language . $url;
} else {
return $url;
}
}
public function use_wp_login_url_converter() {
return true;
}
/**
* Will return true if root URL or child of root URL
*
* @param string $url
*
* @return bool
*/
private function is_root_url( $url ) {
$result = false;
if ( isset( $this->urls_settings['root_page'], $this->urls_settings['show_on_root'] ) &&
'page' === $this->urls_settings['show_on_root'] &&
! empty( $this->urls_settings['directory_for_default_language'] )
) {
$root_url = $this->get_root_url();
if ( $root_url ) {
$result = strpos( trailingslashit( $url ), $root_url ) === 0;
}
}
return $result;
}
/**
* @return string|bool
*/
private function get_root_url() {
if ( null === $this->root_url ) {
$root_post = get_post( $this->urls_settings['root_page'] );
if ( $root_post ) {
$this->root_url = trailingslashit( $this->get_url_helper()->get_abs_home() ) . $root_post->post_name;
$this->root_url = trailingslashit( $this->root_url );
} else {
$this->root_url = false;
}
}
return $this->root_url;
}
/**
* @param string $source_url
*
* @return string
*/
private function filter_source_url( $source_url ) {
if ( false === strpos( $source_url, '?' ) ) {
$source_url = trailingslashit( $source_url );
} elseif ( false !== strpos( $source_url, '?' ) && false === strpos( $source_url, '/?' ) ) {
$source_url = str_replace( '?', '/?', $source_url );
}
return $source_url;
}
/**
* @param string $url
*
* @return string
*/
private function get_url_path( $url ) {
if ( strpos( $url, 'http://' ) === 0 || strpos( $url, 'https://' ) === 0 ) {
$url_path = wpml_parse_url( $url, PHP_URL_PATH );
} else {
$pathparts = array_filter( explode( '/', $url ) );
if ( count( $pathparts ) > 1 ) {
unset( $pathparts[0] );
$url_path = implode( '/', $pathparts );
} else {
$url_path = $url;
}
}
return $url_path;
}
/**
* @param string $url_path
*
* @return string
*/
private function extract_lang_from_url_path( $url_path ) {
$fragments = ! empty( $url_path ) ? array_filter( explode( '/', $url_path ) ) : [''];
$lang = array_shift( $fragments );
$lang_get_parts = ! empty( $lang ) ? explode( '?', $lang ) : [''];
$lang = $lang_get_parts[0];
return isset( $this->language_codes_reverse_map[ $lang ] ) ? $this->language_codes_reverse_map[ $lang ] : $lang;
}
/**
* @param string $language_code
* @param null|string $value_if_default_language
*
* @return string|null
*/
private function get_language_of_current_dir( $language_code, $value_if_default_language = null ) {
if ( ! $this->use_directory_for_default_lang && $language_code === $this->default_language ) {
return $value_if_default_language;
}
return $language_code;
}
}

View File

@@ -0,0 +1,24 @@
<?php
interface IWPML_URL_Converter_Strategy {
public function add_hooks();
public function remove_hooks();
public function convert_url_string( $source_url, $lang );
public function convert_admin_url_string( $source_url, $lang );
public function validate_language( $language, $url );
public function get_lang_from_url_string( $url );
public function get_home_url_relative( $url, $lang );
public function fix_trailingslashit( $source_url );
public function skip_convert_url_string( $url, $lang_code );
public function use_wp_login_url_converter();
}

View File

@@ -0,0 +1,32 @@
<?php
class WPML_Resolve_Object_Url_Helper_Factory {
const CURRENT_URL_RESOLVER = 'current';
const ABSOLUTE_URL_RESOLVER = 'absolute';
/**
* @return IWPML_Resolve_Object_Url
*/
public function create( $type = self::CURRENT_URL_RESOLVER ) {
global $sitepress, $wp_query, $wpml_term_translations, $wpml_post_translations;
if ( self::CURRENT_URL_RESOLVER === $type ) {
return new WPML_Resolve_Object_Url_Helper( $sitepress, $wp_query, $wpml_term_translations, $wpml_post_translations );
}
if ( self::ABSOLUTE_URL_RESOLVER === $type ) {
$absolute_links = new AbsoluteLinks();
$absolute_to_permalinks = new WPML_Absolute_To_Permalinks( $sitepress );
$translate_links_target = new WPML_Translate_Link_Targets( $absolute_links, $absolute_to_permalinks );
$resolve_url = new WPML_Resolve_Absolute_Url( $sitepress, $translate_links_target );
$url_persisted = WPML_Absolute_Url_Persisted::get_instance();
return new WPML_Resolve_Absolute_Url_Cached( $url_persisted, $resolve_url );
}
throw new InvalidArgumentException( 'Unknown Resolve_Object_Url type: ' . $type );
}
}

View File

@@ -0,0 +1,125 @@
<?php
class WPML_Resolve_Object_Url_Helper implements IWPML_Resolve_Object_Url {
const CACHE_GROUP = 'resolve_object_url';
/**
* @var bool
*/
protected $lock = false;
/**
* @var SitePress
*/
private $sitepress;
/**
* @var WP_Query
*/
private $wp_query;
/**
* @var WPML_Term_Translation
*/
private $wpml_term_translations;
/**
* @var WPML_Post_Translation
*/
private $wpml_post_translations;
/**
* @param SitePress $sitepress
* @param WP_Query $wp_query
* @param WPML_Term_Translation $wpml_term_translations
* @param WPML_Post_Translation $wpml_post_translations
*/
public function __construct(
SitePress &$sitepress = null,
WP_Query &$wp_query = null,
WPML_Term_Translation $wpml_term_translations = null,
WPML_Post_Translation $wpml_post_translations = null
) {
$this->sitepress = &$sitepress;
$this->wp_query = &$wp_query;
$this->wpml_term_translations = $wpml_term_translations;
$this->wpml_post_translations = $wpml_post_translations;
}
/**
* Try to parse the URL to find a related post or term
*
* @param string $url
* @param string $lang_code
*
* @return string|bool
*/
public function resolve_object_url( $url, $lang_code ) {
if ( $this->lock ) {
return false;
}
$this->lock = true;
$new_url = false;
$translations = $this->cached_retrieve_translations( $url );
if ( $translations && isset( $translations[ $lang_code ]->element_type ) ) {
$current_lang = $this->sitepress->get_current_language();
$this->sitepress->switch_lang( $lang_code );
$element = explode( '_', $translations[ $lang_code ]->element_type );
$type = array_shift( $element );
$subtype = implode( '_', $element );
switch ( $type ) {
case 'post':
$new_url = get_permalink( $translations[ $lang_code ]->element_id );
break;
case 'tax':
$term = get_term_by( 'term_taxonomy_id', $translations[ $lang_code ]->element_id, $subtype );
$new_url = get_term_link( $term );
break;
}
$this->sitepress->switch_lang( $current_lang );
}
$this->lock = false;
return $new_url;
}
/**
* @param string $url
*
* @return array<string,\stdClass>
*/
private function cached_retrieve_translations( $url ) {
$cache_key = md5( $url );
$cache_found = false;
$cache = new WPML_WP_Cache( self::CACHE_GROUP );
$translations = $cache->get( $cache_key, $cache_found );
if ( ! $cache_found && is_object( $this->wp_query ) ) {
$translations = $this->retrieve_translations();
$cache->set( $cache_key, $translations );
}
return $translations;
}
/**
* @return array<string,\stdClass>
*/
private function retrieve_translations() {
$this->sitepress->set_wp_query(); // Make sure $sitepress->wp_query is set
$_wp_query_back = clone $this->wp_query;
$wp_query = $this->sitepress->get_wp_query();
$wp_query = is_object( $wp_query ) ? clone $wp_query : clone $_wp_query_back;
$languages_helper = new WPML_Languages( $this->wpml_term_translations, $this->sitepress, $this->wpml_post_translations );
list( $translations) = $languages_helper->get_ls_translations( $wp_query, $_wp_query_back, $this->sitepress->get_wp_query() );
return $translations;
}
}

View File

@@ -0,0 +1,14 @@
<?php
class WPML_Absolute_Url_Persisted_Filters_Factory implements IWPML_Backend_Action_Loader, IWPML_AJAX_Action_Loader {
public function create() {
$url_persisted = WPML_Absolute_Url_Persisted::get_instance();
if ( $url_persisted->has_urls() ) {
return new WPML_Absolute_Url_Persisted_Filters( $url_persisted );
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
<?php
class WPML_Absolute_Url_Persisted_Filters implements IWPML_Action {
/** @var WPML_Absolute_Url_Persisted $url_persisted */
private $url_persisted;
public function __construct( WPML_Absolute_Url_Persisted $url_persisted ) {
$this->url_persisted = $url_persisted;
}
public function add_hooks() {
add_filter( 'wp_insert_post_data', [ $this, 'reset' ] );
add_action( 'delete_post', [ $this, 'reset' ] );
add_filter( 'wp_update_term_data', [ $this, 'reset' ] );
add_action( 'pre_delete_term', [ $this, 'reset' ] );
add_filter( 'rewrite_rules_array', [ $this, 'reset' ] );
}
/**
* @param mixed $data
*
* @return array
*/
public function reset( $data = null ) {
$this->url_persisted->reset();
return $data;
}
}

View File

@@ -0,0 +1,13 @@
<?php
interface IWPML_Resolve_Object_Url {
/**
* @param string $url
* @param string $lang
*
* @return string|false Will return the resolved URL or `false` if it could not be resolved.
*/
public function resolve_object_url( $url, $lang );
}

View File

@@ -0,0 +1,100 @@
<?php
class WPML_Absolute_Url_Persisted {
const OPTION_KEY = 'wpml_resolved_url_persist';
private static $instance;
/**
* @var array
*/
private $urls;
/**
* @return WPML_Absolute_Url_Persisted
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
protected function __construct() {}
private function __clone() {}
/**
* @throws Exception
*/
public function __wakeup() {
throw new Exception( 'Cannot unserialize singleton' );
}
/**
* Returns urls array.
*
* @return array Array with urls.
*/
private function get_urls() {
if ( null === $this->urls ) {
$this->urls = get_option( self::OPTION_KEY, array() );
if ( ! is_array( $this->urls ) ) {
$this->urls = array();
}
}
return $this->urls;
}
/** @return bool */
public function has_urls() {
return (bool) $this->get_urls();
}
/**
* @param string $original_url
* @param string $lang
* @param string|false $converted_url A `false` value means that the URL could not be resolved
*/
public function set( $original_url, $lang, $converted_url ) {
$this->get_urls();
$this->urls[ $original_url ][ $lang ] = $converted_url;
$this->persist_in_shutdown();
}
/**
* @param string $original_url
* @param string $lang
*
* @return string|false|null If the URL has already been processed but could not be resolved, it will return `false`
*/
public function get( $original_url, $lang ) {
$this->get_urls();
if ( isset( $this->urls[ $original_url ][ $lang ] ) ) {
return $this->urls[ $original_url ][ $lang ];
}
return null;
}
public function reset() {
$this->urls = [];
$this->persist();
$this->urls = null;
}
public function persist() {
update_option( self::OPTION_KEY, $this->urls );
}
private function persist_in_shutdown() {
if ( ! has_action( 'shutdown', array( $this, 'persist' ) ) ) {
add_action( 'shutdown', array( $this, 'persist' ) );
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
class WPML_Resolve_Absolute_Url_Cached implements IWPML_Resolve_Object_Url {
/** @var WPML_Absolute_Url_Persisted $url_persisted */
private $url_persisted;
/** @var WPML_Resolve_Absolute_Url $resolve_url */
private $resolve_url;
public function __construct( WPML_Absolute_Url_Persisted $url_persisted, WPML_Resolve_Absolute_Url $resolve_url ) {
$this->url_persisted = $url_persisted;
$this->resolve_url = $resolve_url;
}
/**
* @param string $url
* @param string $lang
*
* @return false|string Will return `false` if the URL could not be resolved
*/
public function resolve_object_url( $url, $lang ) {
$resolved_url = $this->url_persisted->get( $url, $lang );
if ( null === $resolved_url ) {
$resolved_url = $this->resolve_url->resolve_object_url( $url, $lang );
$this->url_persisted->set( $url, $lang, $resolved_url );
}
return $resolved_url;
}
}

View File

@@ -0,0 +1,50 @@
<?php
class WPML_Resolve_Absolute_Url implements IWPML_Resolve_Object_Url {
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_Translate_Link_Targets */
private $translate_link_targets;
/** @var bool */
private $lock;
public function __construct( SitePress $sitepress, WPML_Translate_Link_Targets $translate_link_targets ) {
$this->sitepress = $sitepress;
$this->translate_link_targets = $translate_link_targets;
}
/**
* @param string $url
* @param string $lang
*
* @return string|false
*/
public function resolve_object_url( $url, $lang ) {
if ( $this->lock ) {
return false;
}
$this->lock = true;
$current_lang = $this->sitepress->get_current_language();
$this->sitepress->switch_lang( $lang );
try {
$new_url = $this->translate_link_targets->convert_url( $url );
if ( trailingslashit( $new_url ) === trailingslashit( $url ) ) {
$new_url = false;
}
} catch ( Exception $e ) {
$new_url = false;
}
$this->sitepress->switch_lang( $current_lang );
$this->lock = false;
return $new_url;
}
}

View File

@@ -0,0 +1,454 @@
<?php
use \WPML\FP\Str;
use \WPML\SuperGlobals\Server;
/**
* Class WPML_URL_Filters
*/
class WPML_URL_Filters {
/** @var \SitePress */
private $sitepress;
/** @var \WPML_Post_Translation $post_translation */
private $post_translation;
/** @var \WPML_Canonicals */
private $canonicals;
/** @var \WPML_URL_Converter $url_converter */
private $url_converter;
/** @var \WPML_Debug_BackTrace */
private $debug_backtrace;
/**
* WPML_URL_Filters constructor.
*
* @param \WPML_Post_Translation $post_translation
* @param string $url_converter
* @param \WPML_Canonicals $canonicals
* @param \SitePress $sitepress
* @param \WPML_Debug_BackTrace $debug_backtrace
*/
public function __construct(
&$post_translation,
&$url_converter,
WPML_Canonicals $canonicals,
&$sitepress,
WPML_Debug_BackTrace $debug_backtrace
) {
$this->sitepress = &$sitepress;
$this->post_translation = &$post_translation;
$this->url_converter = &$url_converter;
$this->canonicals = $canonicals;
$this->debug_backtrace = $debug_backtrace;
if ( $this->frontend_uses_root() === true ) {
WPML_Root_Page::init();
}
$this->add_hooks();
}
private function add_hooks() {
if ( $this->frontend_uses_root() === true ) {
add_filter( 'page_link', array( $this, 'page_link_filter_root' ), 1, 2 );
} else {
add_filter( 'page_link', array( $this, 'page_link_filter' ), 1, 2 );
}
$this->add_global_hooks();
if ( $this->has_wp_get_canonical_url() ) {
add_filter( 'get_canonical_url', array( $this, 'get_canonical_url_filter' ), 1, 2 );
}
}
public function add_global_hooks() {
add_filter( 'home_url', [ $this, 'home_url_filter' ], - 10, 4 );
// posts, pages & attachments links filters
add_filter( 'post_link', [ $this, 'permalink_filter' ], 1, 2 );
add_filter( 'attachment_link', [ $this, 'permalink_filter' ], 1, 2 );
add_filter( 'post_type_link', [ $this, 'permalink_filter' ], 1, 2 );
add_filter( 'wpml_filter_link', [ $this, 'permalink_filter' ], 1, 2 );
add_filter( 'get_edit_post_link', [ $this, 'get_edit_post_link' ], 1, 3 );
add_filter( 'oembed_request_post_id', [ $this, 'embedded_front_page_id_filter' ], 1, 2 );
add_filter( 'post_embed_url', [ $this, 'fix_post_embedded_url' ], 1, 1 );
}
public function remove_global_hooks() {
// posts and pages links filters
remove_filter( 'oembed_request_post_id', [ $this, 'embedded_front_page_id_filter' ], 1 );
remove_filter( 'post_embed_url', [ $this, 'fix_post_embedded_url' ], 1 );
remove_filter( 'get_edit_post_link', [ $this, 'get_edit_post_link' ], 1 );
remove_filter( 'wpml_filter_link', [ $this, 'permalink_filter' ], 1 );
remove_filter( 'post_type_link', [ $this, 'permalink_filter' ], 1 );
remove_filter( 'attachment_link', [ $this, 'permalink_filter' ], 1 );
remove_filter( 'post_link', [ $this, 'permalink_filter' ], 1 );
remove_filter( 'home_url', [ $this, 'home_url_filter' ], - 10 );
}
/**
* @param int $post_id
* @param string $url
*
* @return int
*
* @hook oembed_request_post_id
*/
public function embedded_front_page_id_filter( $post_id, $url ) {
if ( ! $post_id && $this->is_front_page( $url ) ) {
$page_on_front = get_option( 'page_on_front' );
if ( $page_on_front && get_post( $page_on_front ) instanceof WP_Post ) {
$post_id = (int) $page_on_front;
}
}
return $post_id;
}
/**
* @param string $embedded_url
*
* @return string
*/
public function fix_post_embedded_url( $embedded_url ) {
$query = wpml_parse_url( $embedded_url, PHP_URL_QUERY );
$embed = user_trailingslashit( 'embed' );
if ( Str::includes( $embed, $query ) ) {
$embedded_url = Str::split( '?', $embedded_url )[0] . $embed . '?' .
untrailingslashit( Str::replace( $embed, '', $query ) );
}
return $embedded_url;
}
/**
* Filters the link to a post's edit screen by appending the language query argument
*
* @param string $link
* @param int $id
* @param string $context
*
* @return string
*
* @hook get_edit_post_link
*/
public function get_edit_post_link( $link, $id, $context = 'display' ) {
if ( $id && (bool) ( $lang = $this->post_translation->get_element_lang_code( $id ) ) === true ) {
$link .= ( 'display' === $context ? '&amp;' : '&' ) . 'lang=' . $lang;
if ( ! did_action( 'wpml_pre_status_icon_display' ) ) {
do_action( 'wpml_pre_status_icon_display' );
}
$link = apply_filters( 'wpml_link_to_translation', $link, $id, $lang, $this->post_translation->get_element_trid( $id ) );
}
return $link;
}
/**
* Permalink filter that is used when the site uses a root page
*
* @param string $link
* @param int|WP_Post $pid
*
* @return string
*/
public function permalink_filter_root( $link, $pid ) {
$pid = is_object( $pid ) ? $pid->ID : $pid;
$link = $this->sitepress->get_root_page_utils()->get_root_page_id() != $pid
? $this->permalink_filter( $link, $pid ) : $this->filter_root_permalink( $link );
return $link;
}
/**
* @param string $link
* @param int $pid
*
* @return string|WPML_Notice|WPML_Notice_Render
*/
public function page_link_filter_root( $link, $pid ) {
$pid = is_object( $pid ) ? $pid->ID : $pid;
$link = $this->sitepress->get_root_page_utils()->get_root_page_id() != $pid
? $this->page_link_filter( $link, $pid ) : $this->filter_root_permalink( $link );
return $link;
}
/**
* Filters links to the root page, so that they are displayed properly in the front-end.
*
* @param string $url
*
* @return string
*/
public function filter_root_permalink( $url ) {
$root_page_utils = $this->sitepress->get_root_page_utils();
if ( $root_page_utils->get_root_page_id() > 0 && $root_page_utils->is_url_root_page( $url ) ) {
$url_parts = wpml_parse_url( $url );
$query = isset( $url_parts['query'] ) ? $url_parts['query'] : '';
$path = isset( $url_parts['path'] ) ? $url_parts['path'] : '';
$slugs = array_filter( explode( '/', $path ) );
$last_slug = array_pop( $slugs );
$new_url = $this->url_converter->get_abs_home();
$new_url = is_numeric( $last_slug ) ? trailingslashit( trailingslashit( $new_url ) . $last_slug ) : $new_url;
$query = $this->unset_page_query_vars( $query );
$new_url = trailingslashit( $new_url );
$url = (bool) $query === true ? trailingslashit( $new_url ) . '?' . $query : $new_url;
}
return $url;
}
/**
* @param string $link
* @param int|WP_Post $post
*
* @return string
*/
public function permalink_filter( $link, $post ) {
if ( ! $post ) {
return $link;
}
$post_id = $post;
if ( is_object( $post ) ) {
$post_id = $post->ID;
}
/** @var int $post_id */
if ( $post_id < 1 ) {
return $link;
}
$canonical_url = $this->canonicals->permalink_filter( $link, $post_id );
if ( $canonical_url ) {
return $canonical_url;
}
$post_element = new WPML_Post_Element( $post_id, $this->sitepress );
if ( ! $this->is_display_as_translated_mode( $post_element ) && $post_element->is_translatable() ) {
$link = $this->get_translated_permalink( $link, $post_id, $post_element );
}
return $this->url_converter->get_strategy()->fix_trailingslashit( $link );
}
/**
* @param string $link
* @param int|WP_Post $post
*
* @return string
*/
public function page_link_filter( $link, $post ) {
return $this->permalink_filter( $link, $post );
}
private function has_wp_get_canonical_url() {
return $this->sitepress->get_wp_api()->function_exists( 'wp_get_canonical_url' );
}
/**
* @param string|bool $canonical_url
* @param WP_Post $post
*
* @return mixed
* @throws \InvalidArgumentException
*/
public function get_canonical_url_filter( $canonical_url, $post ) {
return $this->canonicals->get_canonical_url( $canonical_url, $post, $this->get_request_language() );
}
/**
* @param string $url
* @param string $path
* @param string $orig_scheme
* @param int $blog_id
*
* @return string
*/
public function home_url_filter( $url, $path, $orig_scheme, $blog_id ) {
$language_negotiation_type = (int) $this->sitepress->get_setting( 'language_negotiation_type' );
$context = new WPML_Home_Url_Filter_Context(
$language_negotiation_type,
$orig_scheme,
$this->debug_backtrace
);
if ( $context->should_not_filter() ) {
return $url;
}
$url_language = $this->get_request_language();
if ( 'relative' === $orig_scheme ) {
$home_url = $this->url_converter->get_home_url_relative( $url, $url_language );
} else {
$home_url = $this->url_converter->convert_url( $url, $url_language );
}
$home_url = apply_filters( 'wpml_get_home_url', $home_url, $url, $path, $orig_scheme, $blog_id );
return $home_url;
}
public function frontend_uses_root() {
/** @var array $urls */
$urls = $this->sitepress->get_setting( 'urls' );
if ( is_admin() ) {
$uses_root = isset( $urls['root_page'], $urls['show_on_root'] )
&& ! empty( $urls['directory_for_default_language'] )
&& ( in_array( $urls['show_on_root'], array( 'page', 'html_file' ) ) );
} else {
$uses_root = isset( $urls['root_page'], $urls['show_on_root'] )
&& ! empty( $urls['directory_for_default_language'] )
&& ( ( $urls['root_page'] > 0 && 'page' === $urls['show_on_root'] )
|| ( $urls['root_html_file_path'] && 'html_file' === $urls['show_on_root'] ) );
}
return $uses_root;
}
/**
* Finds the correct language a post belongs to by handling the special case of the post edit screen.
*
* @param int $post_id
*
* @return bool|mixed|null|String
*/
private function get_permalink_filter_lang( $post_id ) {
if ( isset( $_POST['action'] ) && $_POST['action'] === 'sample-permalink' ) {
$code = $this->get_language_from_url();
$code = $code
? $code
: ( ! isset( $_SERVER['HTTP_REFERER'] )
? $this->sitepress->get_default_language()
: $this->url_converter->get_language_from_url( $_SERVER['HTTP_REFERER'] ) );
} else {
$code = $this->post_translation->get_element_lang_code( $post_id );
}
return $code;
}
private function unset_page_query_vars( $query ) {
parse_str( (string) $query, $query_parts );
foreach ( array( 'p', 'page_id', 'page', 'pagename', 'page_name', 'attachement_id' ) as $part ) {
if ( isset( $query_parts[ $part ] ) && ! ( $part === 'page_id' && ! empty( $query_parts['preview'] ) ) ) {
unset( $query_parts[ $part ] );
}
}
return http_build_query( $query_parts );
}
private function get_language_from_url( $return_default_language_if_missing = false ) {
$query_string_argument = '';
$language = '';
if ( $return_default_language_if_missing ) {
$language = $this->sitepress->get_current_language();
}
if ( array_key_exists( 'wpml_lang', $_GET ) ) {
$query_string_argument = 'wpml_lang';
} elseif ( array_key_exists( 'lang', $_GET ) ) {
$query_string_argument = 'lang';
}
if ( '' !== $query_string_argument ) {
$language = filter_var( $_GET[ $query_string_argument ], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
return $language;
}
/**
* @param string $link
* @param int $post
* @param WPML_Post_Element $post_element
*
* @return bool|false|mixed|string
*/
public function get_translated_permalink( $link, $post, $post_element ) {
$code = $this->get_permalink_filter_lang( $post );
$force_translate_permalink = apply_filters( 'wpml_force_translated_permalink', false );
if ( ( ! is_admin() || wp_doing_ajax() || wpml_is_ajax() || $force_translate_permalink )
&& ( ! $this->sitepress->get_wp_api()->is_a_REST_request() || $force_translate_permalink )
&& $this->should_use_permalink_of_post_translation( $post_element )
) {
$link = get_permalink( $this->get_translated_post_id_for_current_language( $post_element ) );
} else {
$link = $this->url_converter->get_strategy()->convert_url_string( $link, $code );
}
if ( $this->sitepress->get_wp_api()->is_feed() ) {
$link = str_replace( '&lang=', '&#038;lang=', $link );
}
return $link;
}
/**
* @param string $link
* @param int $post_id
* @param \WPML_Post_Element $post_element
*
* @return bool|mixed|string
*/
public function get_translated_page_link( $link, $post_id, $post_element ) {
$code = $this->get_permalink_filter_lang( $post_id );
if ( ! is_admin() && $this->should_use_permalink_of_post_translation( $post_element ) ) {
$link = get_page_link( $this->get_translated_post_id_for_current_language( $post_element ) );
} else {
$link = $this->url_converter->get_strategy()->convert_url_string( $link, $code );
}
if ( $this->sitepress->get_wp_api()->is_feed() ) {
$link = str_replace( '&lang=', '&#038;lang=', $link );
}
return $link;
}
public function get_request_language() {
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';
$server_name = strpos( $request_uri, '/' ) === 0
? untrailingslashit( Server::getServerName() ) : trailingslashit( Server::getServerName() );
$url_snippet = $server_name . $request_uri;
return $this->url_converter->get_language_from_url( $url_snippet );
}
private function should_use_permalink_of_post_translation( WPML_Post_Element $post_element ) {
return $this->sitepress->get_setting( 'auto_adjust_ids' )
&& $post_element->get_language_code() !== $this->sitepress->get_current_language()
&& $this->get_translated_post_id_for_current_language( $post_element );
}
private function is_display_as_translated_mode( WPML_Post_Element $post_element ) {
return $post_element->is_display_as_translated() &&
$post_element->get_language_code() == $this->sitepress->get_default_language() &&
! $this->get_translated_post_id_for_current_language( $post_element ) &&
! $this->debug_backtrace->is_class_function_in_call_stack( 'SitePress', 'get_ls_languages' );
}
private function get_translated_post_id_for_current_language( WPML_Post_Element $post_element ) {
$post_id = $post_element->get_element_id();
$current_language = $this->sitepress->get_current_language();
return $this->post_translation->element_id_in( $post_id, $current_language );
}
/**
* @param string $url
*
* @return bool
*/
private function is_front_page( $url ) {
return $this->canonicals->get_general_canonical_url( $url ) === home_url() && 'page' === get_option( 'show_on_front' );
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace WPML\UrlHandling;
class WPLoginUrlConverterFactory implements \IWPML_Frontend_Action_Loader, \IWPML_Backend_Action_Loader {
/**
* @return array
*/
public function create() {
/** @var \WPML_URL_Converter $wpml_url_converter */
global $wpml_url_converter, $sitepress;
$rules = new WPLoginUrlConverterRules();
return WPLoginUrlConverter::isEnabled()
? array_filter( [ $wpml_url_converter->get_wp_login_url_converter( $sitepress ), $rules ] )
: [ $rules ];
}
}

View File

@@ -0,0 +1,279 @@
<?php
namespace WPML\UrlHandling;
use WPML\API\Sanitize;
use WPML\Element\API\Languages;
use WPML\FP\Obj;
use WPML\FP\Relation;
use WPML\FP\Str;
use WPML\LIB\WP\Option;
use WPML\LIB\WP\User;
class WPLoginUrlConverter implements \IWPML_Action {
const PRIORITY_AFTER_URL_FILTERS = 100;
const SETTINGS_KEY = 'wpml_login_page_translation';
private $rewrite_rule_not_found;
/** @var \WPML_URL_Converter $url_converter */
private $url_converter;
/** @var \SitePress $sitepress */
private $sitepress;
/**
* @param \WPML_URL_Converter $url_converter
* @param \SitePress $sitepress
*/
public function __construct( $sitepress, $url_converter ) {
$this->rewrite_rule_not_found = false;
$this->url_converter = $url_converter;
$this->sitepress = $sitepress;
}
public function add_hooks() {
add_filter( 'site_url', [ $this, 'site_url' ], self::PRIORITY_AFTER_URL_FILTERS, 3 );
add_filter( 'login_url', [ $this, 'convert_url' ] );
add_filter( 'register_url', [ $this, 'convert_url' ] );
add_filter( 'lostpassword_url', [ $this, 'convert_url' ] );
add_filter( 'request', [ $this, 'on_request' ] );
add_filter(
'wpml_get_language_from_url',
[
$this,
'wpml_login_page_language_from_url',
],
self::PRIORITY_AFTER_URL_FILTERS,
2
);
add_filter( 'registration_redirect', [ $this, 'filter_redirect_with_lang' ] );
add_filter( 'lostpassword_redirect', [ $this, 'filter_redirect_with_lang' ] );
if ( self::isEnabled() ) {
add_filter( 'logout_url', [ $this, 'convert_user_logout_url' ] );
add_filter( 'logout_redirect', [ $this, 'convert_default_redirect_url' ], 2, 10 );
}
add_action( 'generate_rewrite_rules', [ $this, 'generate_rewrite_rules' ] );
add_action( 'login_init', [ $this, 'redirect_to_login_url_with_lang' ] );
if ( is_multisite() ) {
add_filter( 'network_site_url', [ $this, 'site_url' ], self::PRIORITY_AFTER_URL_FILTERS, 3 );
add_filter( 'wp_signup_location', [ $this, 'convert_url' ] );
add_action( 'signup_hidden_fields', [ $this, 'add_signup_language_field' ] );
}
}
/**
* Converts the logout URL to be translated.
*
* @param string $url
* @return string
*/
public function convert_user_logout_url( $url ) {
$current_user_id = User::getCurrentId();
if ( $current_user_id ) {
$user_locale = User::getMetaSingle( $current_user_id, 'locale' );
if ( ! empty( $user_locale ) ) {
$language_code = $this->sitepress->get_language_code_from_locale( $user_locale );
return $this->url_converter->convert_url( $url, $language_code );
}
}
return $url;
}
public function add_signup_language_field() {
echo "<input type='hidden' name='lang' value='" . $this->sitepress->get_current_language() . "' />";
}
public function redirect_to_login_url_with_lang() {
$sitePath = Obj::propOr( '', 'path', parse_url( site_url() ) );
$requestUriWithoutPath = Str::trimPrefix( $sitePath, $_SERVER['REQUEST_URI'] );
$converted_url = site_url( $requestUriWithoutPath, 'login' );
if (
! is_multisite()
&& $converted_url != site_url( $requestUriWithoutPath )
&& wp_redirect( $converted_url, 302, 'WPML' )
) {
exit;
}
}
public function generate_rewrite_rules() {
global $wp_rewrite;
$language_rules = wpml_collect( Languages::getActive() )->mapWithKeys(
function ( $lang ) {
return [ $lang['code'] . '/wp-login.php' => 'wp-login.php' ];
}
);
$wp_rewrite->non_wp_rules = array_merge( $language_rules->toArray(), $wp_rewrite->non_wp_rules );
}
/**
* Converts the redirected string if it's the default one.
*
* @param string $redirect_to
* @param string $requested_redirect_to
* @return string
*/
public function convert_default_redirect_url( $redirect_to, $requested_redirect_to ) {
if ( '' === $requested_redirect_to ) {
return $this->convert_url( $redirect_to );
}
return $redirect_to;
}
public function filter_redirect_with_lang( $redirect_to ) {
if ( ! $redirect_to && Obj::prop( 'lang', $_GET ) ) {
$redirect_to = site_url( 'wp-login.php?checkemail=confirm', 'login' );
}
return $redirect_to;
}
public function wpml_login_page_language_from_url( $language, $url ) {
if ( $this->is_wp_login_url( $url ) ) {
if ( isset( $_GET['lang'] ) && $_GET['lang'] != $language ) {
return Sanitize::stringProp( 'lang', $_GET );
}
if ( is_multisite() && isset( $_POST['lang'] ) && $_POST['lang'] != $language ) {
return Sanitize::stringProp( 'lang', $_POST );
}
}
return $language;
}
public function site_url( $url, $path, $scheme ) {
if ( $scheme === 'login_post' || $scheme === 'login' || $this->should_convert_url_for_multisite( $url ) ) {
$url = $this->convert_url( $url );
}
return $url;
}
public function convert_url( $url ) {
$lang_param = $this->get_language_param_for_convert_url();
if ( $lang_param ) {
return add_query_arg( 'lang', $lang_param, $url );
}
return $this->url_converter->convert_url( $url );
}
public function on_request( $query_vars ) {
if ( Relation::propEq( 'name', 'wp-login.php', $query_vars ) ) {
$this->rewrite_rule_not_found = true;
} else {
$this->rewrite_rule_not_found = false;
}
if ( $this->rewrite_rule_not_found && $this->is_wp_login_action() ) {
$current_url = site_url( $_SERVER['REQUEST_URI'], 'login' );
$redirect_to = $this->remove_language_directory_from_url( $current_url );
if (
$redirect_to !== $current_url
&& wp_redirect( $redirect_to, 302, 'WPML' )
) {
exit;
}
}
return $query_vars;
}
/**
* @param bool $validateOrRollback - If true, it will be validated that the translated Login URL is accessible or rollback.
* @return void
*/
public static function enable( $validateOrRollback = false ) {
self::saveState( true, $validateOrRollback );
}
public static function disable() {
self::saveState( false );
}
/**
* @return bool
*/
public static function isEnabled() {
return Option::getOr( self::SETTINGS_KEY, false );
}
/**
* @param bool $state
* @param bool $validate - if true, will validate the change or undo it.
*
*/
public static function saveState( $state, $validate = false ) {
Option::update( self::SETTINGS_KEY, $state );
WPLoginUrlConverterRules::markRulesForUpdating( $validate );
}
private function should_convert_url_for_multisite( $url ) {
return is_multisite() && Str::includes( 'wp-activate.php', $url );
}
private function is_wp_login_url( $url ) {
return Str::includes( 'wp-login.php', $url )
|| Str::includes( 'wp-signup.php', $url )
|| Str::includes( 'wp-activate.php', $url );
}
private function is_wp_login_action() {
$actions = wpml_collect(
[
'confirm_admin_email',
'postpass',
'logout',
'lostpassword',
'retrievepassword',
'resetpass',
'rp',
'register',
'login',
'confirmaction',
]
);
return $actions->contains( Obj::prop( 'action', $_GET ) );
}
private function get_language_param_for_convert_url() {
if ( isset( $_GET['lang'] ) ) {
return Sanitize::stringProp( 'lang', $_GET );
}
if ( is_multisite() && isset( $_POST['lang'] ) ) {
return Sanitize::stringProp( 'lang', $_POST );
}
if ( $this->rewrite_rule_not_found || $this->is_subdomain_multisite() ) {
return $this->sitepress->get_current_language();
}
return null;
}
private function is_subdomain_multisite() {
return is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL;
}
private function remove_language_directory_from_url( $url ) {
$lang = $this->get_language_param_for_convert_url();
$url_parts = wpml_parse_url( $url );
if ( $lang && Str::includes( '/' . $lang . '/', $url_parts['path'] ) ) {
$url = Str::replace( '/' . $lang . '/', '/', $url );
}
return $url;
}
}