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,65 @@
<?php
namespace WPML\Ajax\ST\AdminText;
use WPML\Ajax\IHandler;
use WPML\Collect\Support\Collection;
use WPML\FP\Either;
use WPML\FP\Fns;
use function WPML\FP\partial;
use function WPML\FP\pipe;
class Register implements IHandler {
/** @var \WPML_Admin_Texts */
private $adminTexts;
public function __construct( \WPML_Admin_Texts $adminTexts ) {
$this->adminTexts = $adminTexts;
}
/**
* Registers or Unregisters an option for translation depending
* on the `state` data.
*
* @param Collection $data
*
* @return Either
*/
public function run( Collection $data ) {
$state = $data->get( 'state' ) ? 'on' : '';
$applyState = partial( [ self::class, 'flatToHierarchical' ], $state );
$register = pipe(
'wpml_collect',
Fns::map( $applyState ),
Fns::reduce( 'array_replace_recursive', [] ),
[ $this->adminTexts, 'icl_register_admin_options' ]
);
$register( $data->get( 'selected', [] ) );
return Either::right( true );
}
/**
* string $state -> string [key1][key2][name] -> array [ key1 => [ key2 => [ name => $state ] ] ]
*
* @param string $state
* @param string $option
*
* @return array
*/
public static function flatToHierarchical( $state, $option ) {
// string $value -> mixed $key -> array [ $key => $value ]
$makeArrayWithStringKey = function ( $value, $key ) {
return [ (string) $key => $value ];
};
return \WPML_Admin_Texts::getKeysParts( $option )
->reverse()
->reduce( $makeArrayWithStringKey, $state );
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace WPML\ST\AdminTexts;
use WPML\Ajax\ST\AdminText\Register;
use WPML\Collect\Support\Collection;
use WPML\FP\Obj;
use WPML\FP\Fns;
use WPML\ST\WP\App\Resources;
use function WPML\Container\make;
use WPML\LIB\WP\Hooks as WPHooks;
use function WPML\FP\pipe;
class UI implements \IWPML_Backend_Action_Loader {
// shouldShow :: Collection -> bool
public static function shouldShow( Collection $data ) {
return $data->get( 'page' ) === WPML_ST_FOLDER . '/menu/string-translation.php' &&
(int) $data->get( 'trop' ) === 1;
}
public static function localize( Collection $model ) {
$esc_value = Obj::over( Obj::lensProp( 'value' ), 'esc_js' );
return [
'name' => 'wpml_admin_strings',
'data' => [
'model' => $model->map( $esc_value )->toArray(),
'endpoint' => Register::class,
],
];
}
/**
* @return callable|null
*/
public function create() {
if ( self::shouldShow( wpml_collect( $_GET ) ) ) {
return function() {
WPHooks::onAction( 'admin_enqueue_scripts' )
->then( [ make( \WPML_Admin_Texts::class ), 'getModelForRender' ] )
->then( [ self::class, 'localize' ] )
->then( Resources::enqueueApp( 'admin-strings' ) );
};
}
return null;
}
}

View File

@@ -0,0 +1,167 @@
<?php
require_once dirname( __FILE__ ) . '/wpml-admin-text-functionality.class.php';
class WPML_Admin_Text_Configuration extends WPML_Admin_Text_Functionality {
/** @var array $config */
private $config;
/**
* @param string|stdClass $file_or_object
*/
function __construct( $file_or_object = '' ) {
if ( is_object( $file_or_object ) ) {
$config = $file_or_object->config;
$type = $file_or_object->type;
$admin_text_context = $file_or_object->admin_text_context;
} elseif ( $this->can_handle_custom_xml( $file_or_object ) ) {
$validate = new WPML_XML_Config_Validate( WPML_PLUGIN_PATH . '/res/xsd/wpml-config.xsd' );
$transform = new WPML_XML2Array();
$xml_config_file = new WPML_XML_Config_Read_File( $file_or_object, $validate, $transform );
$config = $xml_config_file->get();
$type = ( dirname( $file_or_object ) === get_template_directory() || dirname( $file_or_object ) === get_stylesheet_directory() ) ? 'theme' : 'plugin';
$admin_text_context = basename( dirname( $file_or_object ) );
}
$admin_text_config = isset( $config['wpml-config']['admin-texts'] ) ? $config['wpml-config']['admin-texts'] : array();
$wpml_config_all = array();
if ( isset( $type, $admin_text_context, $admin_text_config['key'] ) ) {
if ( isset( $admin_text_config['key']['attr'] ) ) { // single
$admin_text_config['key']['type'] = $type;
$admin_text_config['key']['context'] = $admin_text_context;
$wpml_config_all[] = $admin_text_config['key'];
} else {
foreach ( (array) $admin_text_config['key'] as $cf ) {
$cf['type'] = $type;
$cf['context'] = $admin_text_context;
$wpml_config_all[] = $cf;
}
}
}
$this->config = $this->fill_wildcards( $wpml_config_all );
}
function get_config_array() {
return $this->config;
}
private function fill_wildcards( array $config_array ) {
return ( ! isset( $config_array['attr']['name'] ) || $config_array['attr']['name'] !== '*' )
&& ( ! isset( $config_array[0]['attr']['name'] ) || $config_array[0]['attr']['name'] !== '*' )
? $this->remove_unmatched(
$config_array,
$this->all_strings_array( $this->get_top_level_filters( $config_array ) )
)
: array();
}
private function get_top_level_filters( array $config_array ) {
$ret = array();
$config_array = isset( $config_array['attr']['name'] ) ? array( $config_array ) : $config_array;
foreach ( $config_array as $option ) {
if ( isset( $option['attr']['name'] ) ) {
$ret[] = $option['attr']['name'];
}
}
return $ret;
}
private function remove_unmatched( array $input, array $all_possibilities ) {
$ret = array();
$input = isset( $input['attr'] ) ? array( $input ) : $input;
foreach ( $input as $val ) {
$name_matcher = $this->wildcard_to_matcher( $val['attr']['name'] );
foreach ( $all_possibilities as $a_val ) {
if ( preg_match( $name_matcher, $a_val['attr']['name'] ) === 1 ) {
$match = $val;
$match['attr']['name'] = $a_val['attr']['name'];
$has_sub_filter = ! empty( $val['key'] );
$match['key'] = $has_sub_filter
? $this->remove_unmatched( $val['key'], $a_val['key'] )
: $a_val['key'];
if ( $has_sub_filter === false || ! empty( $match['key'] ) ) {
$ret[] = $match;
}
}
}
}
return $ret;
}
/**
* Creates a regex matcher from a wildcard string name definition
*
* @param string $wildcard
*
* @return string
*/
private function wildcard_to_matcher( $wildcard ) {
return '#^' . str_replace( '\*', '.+', preg_quote( $wildcard, '#' ) ) . '$#';
}
private function all_strings_array( array $top_level_filters ) {
global $wpdb;
if ( (bool) $top_level_filters === false ) {
return array();
}
foreach ( $top_level_filters as $key => $filter ) {
$like = strpos( $filter, '*' ) !== false;
$comparator = $like ? ' LIKE ' : '=';
$top_level_filters[ $key ] = $wpdb->prepare(
' option_name ' . $comparator . ' %s ',
$like
? str_replace( '*', '%', $wpdb->esc_like( $filter ) )
: $filter
);
}
$where = ' AND ( ' . join( ' OR ', $top_level_filters ) . ' )';
$strings = $wpdb->get_results(
"SELECT option_name, option_value
FROM {$wpdb->options}
WHERE option_name NOT LIKE '_transient%'
AND option_name NOT LIKE '_site_transient%' {$where}
AND LENGTH(option_value) < 1000000"
);
$all_options = array();
foreach ( $strings as $data_pair ) {
if ( $this->is_blacklisted( $data_pair->option_name ) === false ) {
$all_options[ $data_pair->option_name ] = maybe_unserialize( $data_pair->option_value );
}
}
return $this->reformat_array( $all_options );
}
private function reformat_array( $option_value ) {
$ret = array();
if ( is_array( $option_value ) ) {
foreach ( $option_value as $key => $value ) {
$ret[] = array(
'attr' => array( 'name' => $key ),
'key' => $this->reformat_array( $value ),
);
}
}
return $ret;
}
/**
* @param string $file_path
*
* @return bool
*/
private function can_handle_custom_xml( $file_path ) {
return is_string( $file_path ) && '' !== $file_path && file_exists( $file_path ) && class_exists( 'WPML_XML_Config_Validate' );
}
}

View File

@@ -0,0 +1,149 @@
<?php
abstract class WPML_Admin_Text_Functionality {
final public function is_blacklisted( $option_name ) {
global $wp_taxonomies;
$black_list = array_fill_keys(
array(
'active_plugins',
'wp_user_roles',
'_wpml_media',
'users_can_register',
'admin_email',
'start_of_week',
'use_balanceTags',
'use_smilies',
'require_name_email',
'comments_notify',
'posts_per_rss',
'sticky_posts',
'page_for_post',
'_wcml_settings',
'_wcml_version',
'page_on_front',
'default_post_format',
'link_manager_enabled',
'icl_sitepress_settings',
'wpml_config_index',
'wpml_config_index_updated',
'wpml_config_files_arr',
'icl_admin_messages',
'wpml-package-translation-db-updates-run',
'wpml_media',
'wpml_ta_settings',
'_icl_admin_option_names',
'_icl_cache',
'icl_sitepress_version',
'rewrite_rules',
'recently_activated',
'wpml_tm_version',
'wp_installer_settings',
'icl_adl_settings',
'rss_use_excerpt',
'template',
'stylesheet',
'comment_whitelist',
'comment_registration',
'html_type',
'use_trackback',
'default_role',
'db_version',
'siteurl',
'home',
'blogname',
'blogdescription',
'mailserver_url',
'mailserver_login',
'mailserver_pass',
'mailserver_port',
'default_category',
'default_comment_status',
'default_ping_status',
'default_pingback_flag',
'comment_moderation',
'moderation_notify',
'permalink_structure',
'gzipcompression',
'hack_file',
'blog_charset',
'ping_sites',
'advanced_edit',
'comment_max_links',
'gmt_offset',
'default_email_category',
'uploads_use_yearmonth_folders',
'upload_path',
'blog_public',
'default_link_category',
'tag_base',
'show_avatars',
'avatar_rating',
'WPLANG',
'wp_icl_translators_cached',
'cron',
'_transient_WPML_ST_MO_Downloader_lang_map',
'icl_translation_jobs_basket',
),
1
);
$tax_prefixes = array_keys( $wp_taxonomies );
foreach ( $tax_prefixes as &$tax_name ) {
$tax_name .= '_children';
}
$blacklist_prefixes = array_merge(
$tax_prefixes,
array( '_transient_', '_site_transient_' )
);
$matcher = '#^' . join( '|^', $blacklist_prefixes ) . '#';
return array_key_exists( $option_name, $black_list )
|| preg_match( $matcher, $option_name ) === 1;
}
protected function read_admin_texts_recursive( $keys, $admin_text_context, $type, &$arr_context, &$arr_type ) {
$keys = ! empty( $keys ) && isset( $keys ['attr']['name'] ) ? array( $keys ) : $keys;
foreach ( $keys as $key ) {
$key_name = $key['attr']['name'];
if ( ! empty( $key['key'] ) ) {
$arr[ $key_name ] = $this->read_admin_texts_recursive(
$key['key'],
$admin_text_context,
$type,
$arr_context,
$arr_type
);
} else {
$arr[ $key_name ] = 1;
$arr_context[ $key_name ] = $admin_text_context;
$arr_type[ $key_name ] = $type;
}
}
return isset( $arr ) ? $arr : false;
}
/**
* @param string $key Name of option to retrieve. Expected to not be SQL-escaped.
* @param mixed $default Value to return in case the string does not exists
*
* @return mixed Value set for the option.
*/
public function get_option_without_filtering( $key, $default = false ) {
global $wpdb;
$value = $wpdb->get_var(
$wpdb->prepare(
"SELECT option_value
FROM {$wpdb->options}
WHERE option_name = %s
LIMIT 1",
$key
)
);
return isset( $value ) ? $value : $default;
}
}

View File

@@ -0,0 +1,137 @@
<?php
require_once dirname( __FILE__ ) . '/wpml-admin-text-configuration.php';
require_once dirname( __FILE__ ) . '/wpml-admin-text-functionality.class.php';
class WPML_Admin_Text_Import extends WPML_Admin_Text_Functionality {
/** @var WPML_ST_Records $st_records */
private $st_records;
/** @var WPML_WP_API $wp_api */
private $wp_api;
function __construct( WPML_ST_Records $st_records, WPML_WP_API $wp_api ) {
$this->st_records = $st_records;
$this->wp_api = $wp_api;
}
/**
* @param array $admin_texts
* @param string $config_handler_hash
*/
function parse_config( array $admin_texts, $config_handler_hash ) {
$admin_texts_hash = md5( serialize( $admin_texts ) );
$transient_name = 'wpml_admin_text_import:parse_config:' . $config_handler_hash;
if ( $this->wp_api->is_string_translation_page() || get_transient( $transient_name ) !== $admin_texts_hash ) {
global $iclTranslationManagement, $sitepress;
foreach ( $admin_texts as $a ) {
$type = isset( $a['type'] ) ? $a['type'] : 'plugin';
$admin_text_context = isset( $a['context'] ) ? $a['context'] : '';
$admin_string_name = $a['attr']['name'];
if ( $this->is_blacklisted( $admin_string_name ) ) {
continue;
}
if ( ! empty( $a['key'] ) ) {
foreach ( $a['key'] as $key ) {
$arr[ $admin_string_name ][ $key['attr']['name'] ] = isset( $key['key'] )
? $this->read_admin_texts_recursive( $key['key'],
$admin_text_context,
$type,
$arr_context,
$arr_type )
: 1;
$arr_context[ $admin_string_name ] = $admin_text_context;
$arr_type[ $admin_string_name ] = $type;
}
} else {
$arr[ $admin_string_name ] = 1;
$arr_context[ $admin_string_name ] = $admin_text_context;
$arr_type[ $admin_string_name ] = $type;
}
}
if ( isset( $arr ) ) {
$iclTranslationManagement->admin_texts_to_translate = array_merge( $iclTranslationManagement->admin_texts_to_translate,
$arr );
}
$_icl_admin_option_names = get_option( '_icl_admin_option_names' );
$arr_options = array();
if ( isset( $arr ) && is_array( $arr ) ) {
foreach ( $arr as $key => $v ) {
$value = maybe_unserialize( $this->get_option_without_filtering( $key ) );
$value = is_array( $value ) && is_array( $v ) ? array_intersect_key( $value, $v ) : $value;
$admin_text_context = isset( $arr_context[ $key ] ) ? $arr_context[ $key ] : '';
$type = isset( $arr_type[ $key ] ) ? $arr_type[ $key ] : '';
$req_upgrade = ! $sitepress->get_setting( 'admin_text_3_2_migration_complete_' . $admin_texts_hash, false );
if ( (bool) $value === true ) {
$this->register_string_recursive( $key,
$value,
$arr[ $key ],
'',
$key,
$req_upgrade,
$type,
$admin_text_context );
}
$arr_options[ $key ] = $v;
}
$_icl_admin_option_names = is_array( $_icl_admin_option_names )
? array_replace_recursive( $_icl_admin_option_names, $arr_options ) : $arr_options;
}
update_option( '_icl_admin_option_names', $_icl_admin_option_names, 'no' );
set_transient( $transient_name, $admin_texts_hash );
$sitepress->set_setting( 'admin_text_3_2_migration_complete_' . $admin_texts_hash, true, true );
}
}
private function register_string_recursive( $key, $value, $arr, $prefix, $suffix, $requires_upgrade, $type, $admin_text_context_old ) {
if ( is_scalar( $value ) ) {
icl_register_string( WPML_Admin_Texts::DOMAIN_NAME_PREFIX . $suffix, $prefix . $key, $value, true );
if ( $requires_upgrade ) {
$this->migrate_3_2( $type, $admin_text_context_old, $suffix, $prefix . $key );
}
} elseif ( ! is_null( $value ) ) {
foreach ( $value as $sub_key => $sub_value ) {
if ( isset( $arr[ $sub_key ] ) ) {
$this->register_string_recursive( $sub_key,
$sub_value,
$arr[ $sub_key ],
$prefix . '[' . $key . ']',
$suffix,
$requires_upgrade,
$type,
$admin_text_context_old );
}
}
}
}
private function migrate_3_2( $type, $old_admin_text_context, $new_admin_text_context, $key ) {
global $wpdb;
$old_string_id = icl_st_is_registered_string( WPML_Admin_Texts::DOMAIN_NAME_PREFIX . $type . '_' . $old_admin_text_context, $key );
if ( $old_string_id ) {
$new_string_id = icl_st_is_registered_string( WPML_Admin_Texts::DOMAIN_NAME_PREFIX . $new_admin_text_context, $key );
if ( $new_string_id ) {
$wpdb->update( $wpdb->prefix . 'icl_string_translations', array( 'string_id' => $new_string_id ), array( 'string_id' => $old_string_id ) );
$this->st_records->icl_strings_by_string_id( $new_string_id )
->update(
array(
'status' => $this->st_records
->icl_strings_by_string_id( $old_string_id )
->status()
)
);
}
}
}
}

View File

@@ -0,0 +1,474 @@
<?php
use WPML\Collect\Support\Collection;
use WPML\FP\Either;
use \WPML\FP\Obj;
use function WPML\Container\make;
use function \WPML\FP\partial;
use function \WPML\FP\invoke;
use function \WPML\FP\flip;
class WPML_Admin_Texts extends WPML_Admin_Text_Functionality {
const DOMAIN_NAME_PREFIX = 'admin_texts_';
/** @var array $cache - A cache for each option translation */
private $cache = [];
/** @var array $option_names - The option names from Admin texts settings */
private $option_names = [];
/** @var TranslationManagement $tm_instance */
private $tm_instance;
/** @var WPML_String_Translation $st_instance */
private $st_instance;
/** @var bool $lock */
private $lock = false;
/**
* @param TranslationManagement $tm_instance
* @param WPML_String_Translation $st_instance
*/
function __construct( &$tm_instance, &$st_instance ) {
add_action( 'plugins_loaded', [ $this, 'icl_st_set_admin_options_filters' ], 10 );
add_filter( 'wpml_unfiltered_admin_string', flip( [ $this, 'get_option_without_filtering' ] ), 10, 2 );
add_action( 'wpml_st_force_translate_admin_options', [ $this, 'force_translate_admin_options' ] );
$this->tm_instance = &$tm_instance;
$this->st_instance = &$st_instance;
}
/**
* @param mixed $value
*
* @return array|mixed|object
*/
private static function object_to_array( $value ) {
return is_object( $value ) ? object_to_array( $value ) : $value;
}
function icl_register_admin_options( $array, $key = '', $option = array() ) {
$option = self::object_to_array( $option );
foreach ( $array as $k => $v ) {
$option = $key === '' ? array( $k => maybe_unserialize( $this->get_option_without_filtering( $k ) ) ) : $option;
if ( is_array( $v ) ) {
$this->icl_register_admin_options( $v, $key . '[' . $k . ']', $option[ $k ] );
} else {
$context = $this->get_context( $key, $k );
if ( $v === '' ) {
icl_unregister_string( $context, $key . $k );
} elseif ( isset( $option[ $k ] ) && ( $key === '' || $opt_keys = self::findKeys( (string) $key ) ) ) {
icl_register_string( $context, $key . $k, $option[ $k ], true );
$vals = array( $k => 1 );
$opt_keys = isset( $opt_keys ) ? array_reverse( $opt_keys ) : [];
foreach ( $opt_keys as $opt ) {
$vals = array( $opt => $vals );
}
update_option(
'_icl_admin_option_names',
array_merge_recursive( (array) get_option( '_icl_admin_option_names' ), $vals ),
'no'
);
$this->option_names = [];
}
}
}
}
public function getModelForRender() {
return $this->getModel( $this->getOptions() );
}
/**
* @param Collection $options
*
* @return Collection
*/
public function getModel( Collection $options ) {
$stringNamesPerContext = $this->getStringNamesPerContext();
$isRegistered = function ( $context, $name ) use ( $stringNamesPerContext ) {
return $stringNamesPerContext->has( $context ) &&
$stringNamesPerContext->get( $context )->contains( $name );
};
$getItems = partial( [ $this, 'getItemModel' ], $isRegistered );
return $options->map( $getItems )
->filter()
->values()
->reduce( [ $this, 'flattenModelItems' ], wpml_collect() );
}
/**
* @param Collection $flattened
* @param array $item
*
* @return Collection
*/
public function flattenModelItems( Collection $flattened, array $item ) {
if ( empty( $item ) ) {
return $flattened;
}
if ( isset( $item['items'] ) ) {
return $flattened->merge(
$item['items']->reduce( [ $this, 'flattenModelItems' ], wpml_collect() )
);
}
return $flattened->push( $item );
}
/**
* @param callable $isRegistered - string -> string -> bool
* @param mixed $value
* @param string $name
* @param string $key
* @param array $stack
*
* @return array
*/
public function getItemModel( callable $isRegistered, $value, $name, $key = '', $stack = [] ) {
$sub_key = $this->getSubKey( $key, $name );
$result = [];
if ( $this->isMultiValue( $value ) ) {
$stack[] = $value;
$getSubItem = function ( $v, $key ) use ( $isRegistered, $sub_key, $stack ) {
return $this->getItemModel( $isRegistered, $v, $key, $sub_key, $stack );
};
$result['items'] = wpml_collect( $value )
->reject( $this->isOnStack( $stack ) )
->map( $getSubItem );
} elseif ( is_string( $value ) || is_numeric( $value ) ) {
$context = $this->get_context( $key, $name );
$stringName = $this->getDBStringName( $key, $name );
$result['value'] = $value;
$result['fixed'] = $this->is_sub_key_fixed( $sub_key );
$result['name'] = $sub_key;
$result['registered'] = $isRegistered( $context, $stringName );
$result['hasTranslations'] = ! $result['fixed'] && $result['registered']
&& icl_st_string_has_translations( $context, $stringName );
}
return $result;
}
private function isOnStack( array $stack ) {
return function ( $item ) use ( $stack ) {
return \wpml_collect( $stack )->first(
function ( $currentItem ) use ( $item ) {
return $currentItem === $item;
}
) !== null;
};
}
private function is_sub_key_fixed( $sub_key ) {
$fixed = false;
if ( $keys = self::findKeys( $sub_key ) ) {
$fixed = true;
$fixed_settings = $this->tm_instance->admin_texts_to_translate;
foreach ( $keys as $m ) {
if ( $fixed = isset( $fixed_settings[ $m ] ) ) {
$fixed_settings = $fixed_settings[ $m ];
} else {
break;
}
}
}
return $fixed;
}
private function get_context( $option_key, $option_name ) {
$keys = self::findKeys( (string) $option_key );
return self::DOMAIN_NAME_PREFIX . ( $keys ? reset( $keys ) : $option_name );
}
public function getOptions() {
return wpml_collect( wp_load_alloptions() )
->reject( flip( [ $this, 'is_blacklisted' ] ) )
->map( 'maybe_unserialize' );
}
function icl_st_set_admin_options_filters() {
$option_names = $this->getOptionNames();
$isAdmin = is_admin() && ! wpml_is_ajax();
foreach ( $option_names as $option_key => $option ) {
if ( $this->is_blacklisted( $option_key ) ) {
unset( $option_names[ $option_key ] );
update_option( '_icl_admin_option_names', $option_names, 'no' );
} elseif ( $option_key != 'theme' && $option_key != 'plugin' ) { // theme and plugin are an obsolete format before 3.2
/**
* We don't want to translate admin strings in admin panel because it causes a lot of confusion
* when a value is displayed inside the form input.
*/
if ( ! $isAdmin ) {
$this->add_filter_for( $option_key );
}
add_action( 'update_option_' . $option_key, array( $this, 'on_update_original_value' ), 10, 3 );
}
}
}
/**
* @param array $options
*/
public function force_translate_admin_options( $options ) {
wpml_collect( $options )->each( [ $this, 'add_filter_for' ] );
}
/**
* @param string $option
*/
public function add_filter_for( $option ) {
add_filter( 'option_' . $option, [ $this, 'icl_st_translate_admin_string' ] );
}
function icl_st_translate_admin_string( $option_value, $key = '', $name = '', $root_level = true ) {
if ( $root_level && $this->lock ) {
return $option_value;
}
$this->lock = true;
$lang = $this->st_instance->get_current_string_language( $name );
$option_name = substr( current_filter(), 7 );
$name = $name === '' ? $option_name : $name;
$blog_id = get_current_blog_id();
if ( isset( $this->cache[ $blog_id ][ $lang ][ $name ] ) ) {
$this->lock = false;
return $this->cache[ $blog_id ][ $lang ][ $name ];
}
$is_serialized = is_serialized( $option_value );
$option_value = $is_serialized ? unserialize( $option_value ) : $option_value;
if ( is_array( $option_value ) || is_object( $option_value ) ) {
$option_value = $this->translate_multiple( $option_value, $key, $name );
} else {
$option_value = $this->translate_single( $option_value, $key, $name, $option_name );
}
$option_value = $is_serialized ? serialize( $option_value ) : $option_value;
if ( $root_level ) {
$this->lock = false;
$this->cache[ $blog_id ][ $lang ][ $name ] = $option_value;
}
return $option_value;
}
/**
* @param string $key - string like '[key1][key2]'
* @param string $name
*
* @return bool
*/
private function isAdminText( $key, $name ) {
return null !== Either::of( $this->getSubKey( $key, $name ) )
->map( [ self::class, 'getKeysParts' ] )
->tryCatch( invoke( 'reduce' )->with( flip( Obj::prop() ), $this->getOptionNames() ) )
->getOrElse( null );
}
/**
* getKeys :: string [key1][key2][name] => Collection [key1, key2, name]
*
* @param string $option
*
* @return Collection
*/
public static function getKeysParts( $option ) {
return wpml_collect( self::findKeys( $option ) );
}
/**
* @param string $string
*
* @return array
*/
private static function findKeys( $string ) {
return array_filter( explode( '][', preg_replace( '/^\[(.*)\]$/', '$1', $string ) ), 'strlen' );
}
function clear_cache_for_option( $option_name ) {
$blog_id = get_current_blog_id();
if ( isset( $this->cache[ $blog_id ] ) ) {
foreach ( $this->cache[ $blog_id ] as $lang_code => &$cache_data ) {
if ( array_key_exists( $option_name, $cache_data ) ) {
unset( $cache_data[ $option_name ] );
}
}
}
}
/**
* @param string|array $old_value
* @param string|array $value
* @param string $option_name
* @param string $name
* @param string $sub_key
*/
public function on_update_original_value( $old_value, $value, $option_name, $name = '', $sub_key = '' ) {
$name = $name ? $name : $option_name;
$value = self::object_to_array( $value );
$old_value = self::object_to_array( $old_value );
if ( is_array( $value ) ) {
foreach ( array_keys( $value ) as $key ) {
$this->on_update_original_value(
isset( $old_value[ $key ] ) ? $old_value[ $key ] : '',
$value[ $key ],
$option_name,
$key,
$this->getSubKey( $sub_key, $name )
);
}
} else {
if ( $this->isAdminText( $sub_key, $name ) ) {
icl_st_update_string_actions( self::DOMAIN_NAME_PREFIX . $option_name, $this->getDBStringName( $sub_key, $name ), $old_value, $value );
}
}
if ( $sub_key === '' ) {
$this->clear_cache_for_option( $option_name );
}
}
public function migrate_original_values() {
$migrate = function ( $option_name ) {
$option_value = maybe_unserialize( $this->get_option_without_filtering( $option_name ) );
$this->on_update_original_value( '', $option_value, $option_name );
};
wpml_collect( $this->getOptionNames() )
->keys()
->filter()
->each( $migrate );
}
/**
* Returns a function to lazy load the migration
*
* @return Closure
*/
public static function get_migrator() {
return function () {
wpml_st_load_admin_texts()->migrate_original_values();
};
}
/**
* @param mixed $option_value
* @param string $key
* @param string $name
*
* @return array|mixed
*/
private function translate_multiple( $option_value, $key, $name ) {
$subKey = $this->getSubKey( $key, $name );
foreach ( $option_value as $k => &$value ) {
$value = $this->icl_st_translate_admin_string(
$value,
$subKey,
$k,
false
);
}
return $option_value;
}
/**
* @param string $option_value
* @param string $key
* @param string $name
* @param string $option_name
*
* @return string
*/
private function translate_single( $option_value, $key, $name, $option_name ) {
if ( $option_value !== '' && $this->isAdminText( $key, $name ) ) {
$option_value = icl_translate( self::DOMAIN_NAME_PREFIX . $option_name, $key . $name, $option_value );
}
return $option_value;
}
/**
* @return array
*/
private function getOptionNames() {
if ( empty( $this->option_names ) ) {
$this->option_names = get_option( '_icl_admin_option_names' );
if ( ! is_array( $this->option_names ) ) {
$this->option_names = [];
}
}
return $this->option_names;
}
/**
* getSubKeys :: string [key1][key2] -> string name => string [key1][key2][name]
*
* @param string $key - [key1][key2]
* @param string $name
*
* @return string
*/
private function getSubKey( $key, $name ) {
return $key . '[' . $name . ']';
}
/**
* getSubKeys :: string [key1][key2] -> string name => string [key1][key2]name
*
* @param string $key
* @param string $name
*
* @return string
*/
private function getDBStringName( $key, $name ) {
return $key . $name;
}
/**
* @return Collection
* @throws \WPML\Auryn\InjectionException
*/
private function getStringNamesPerContext() {
$strings = make( WPML_ST_DB_Mappers_Strings::class )
->get_all_by_context( self::DOMAIN_NAME_PREFIX . '%' );
return wpml_collect( $strings )
->groupBy( 'context' )
->map( invoke( 'pluck' )->with( 'name' ) );
}
/**
* @param mixed $value
*
* @return bool
*/
private function isMultiValue( $value ) {
return is_array( $value ) ||
( is_object( $value ) && '__PHP_Incomplete_Class' !== get_class( $value ) );
}
}

View File

@@ -0,0 +1,438 @@
<?php
class WPML_ST_MO_Downloader {
const LOCALES_XML_FILE = 'http://d2pf4b3z51hfy8.cloudfront.net/wp-locales.xml.gz';
const CONTEXT = 'WordPress';
private $settings;
private $xml;
private $translation_files = array();
/**
* @var string[string]
*/
private $lang_map;
/**
* @var string[string]
*/
private $lang_map_rev;
function __construct() {
global $wp_version;
// requires Sitepress
if ( ! defined( 'ICL_SITEPRESS_VERSION' ) || ICL_PLUGIN_INACTIVE ) {
return;
}
$wpversion = preg_replace( '#-(.+)$#', '', $wp_version );
$this->settings = get_option( 'icl_adl_settings' );
if ( empty( $this->settings['wp_version'] ) || version_compare( $wpversion, $this->settings['wp_version'], '>' ) ) {
try {
$this->updates_check( array( 'trigger' => 'wp-update' ) );
} catch ( Exception $e ) {
// do nothing - this is automated request for updates
}
}
if ( get_transient( 'WPML_ST_MO_Downloader_lang_map' ) === false ) {
$this->set_lang_map_from_csv();
}
$this->lang_map = get_transient( 'WPML_ST_MO_Downloader_lang_map' );
$this->lang_map_rev = array_flip( $this->lang_map );
add_action( 'wp_ajax_icl_adm_updates_check', array( $this, 'show_updates' ) );
add_action( 'wp_ajax_icl_adm_save_preferences', array( $this, 'save_preferences' ) );
}
function set_lang_map_from_csv() {
$fh = fopen( WPML_ST_PATH . '/inc/lang-map.csv', 'r' );
while ( list($locale, $code) = fgetcsv( $fh ) ) {
$this->lang_map[ $locale ] = $code;
}
if ( isset( $this->lang_map ) && is_array( $this->lang_map ) ) {
set_transient( 'WPML_ST_MO_Downloader_lang_map', $this->lang_map );
}
}
function updates_check( $args = array() ) {
global $wp_version, $sitepress;
$wpversion = preg_replace( '#-(.+)$#', '', $wp_version );
$trigger = 'manual';
extract( $args, EXTR_OVERWRITE );
$active_languages = $sitepress->get_active_languages();
$default_language = $sitepress->get_default_language();
$this->load_xml();
$this->get_translation_files();
$updates = array();
foreach ( $active_languages as $language ) {
if ( $language != $default_language ) {
if ( isset( $this->translation_files[ $language['code'] ] ) ) {
foreach ( $this->translation_files[ $language['code'] ] as $project => $info ) {
$this->settings['translations'][ $language['code'] ][ $project ]['available'] = $info['signature'];
if ( empty( $this->settings['translations'][ $language['code'] ][ $project ]['installed'] ) ||
( isset( $info['available'] ) && $this->settings['translations'][ $language['code'] ][ $project ]['installed'] != $info['available'] )
) {
$updates['languages'][ $language['code'] ][ $project ] = $this->settings['translations'][ $language['code'] ][ $project ]['available'];
}
}
}
}
}
$this->settings['wp_version'] = $wpversion;
$this->settings['last_time_xml_check'] = time();
$this->settings['last_time_xml_check_trigger'] = $trigger;
$this->save_settings();
return $updates;
}
function show_updates() {
global $sitepress;
$html = '';
try {
$updates = $this->updates_check();
// filter only core( & admin)
$updates_core = array();
if ( array_key_exists( 'languages', $updates ) && ! empty( $updates['languages'] ) ) {
foreach ( $updates['languages'] as $k => $v ) {
if ( ! empty( $v['core'] ) ) {
$updates_core['languages'][ $k ]['core'] = $v['core'];
}
if ( ! empty( $v['admin'] ) ) {
$updates_core['languages'][ $k ]['admin'] = $v['admin'];
}
}
}
$updates = $updates_core;
if ( ! empty( $updates ) ) {
$html .= '<table>';
foreach ( $updates['languages'] as $language => $projects ) {
$l = $sitepress->get_language_details( $language );
if ( ! empty( $projects['core'] ) || ! empty( $projects['admin'] ) ) {
$vkeys = array();
foreach ( $projects as $key => $value ) {
$vkeys[] = $key . '|' . $value;
}
$version_key = join( ';', $vkeys );
$html .= '<tr>';
$html .= '<td>' . sprintf(
__( 'Updated %s translation is available', 'wpml-string-translation' ),
'<strong>' . $l['display_name'] . '</strong>'
) . '</td>';
$html .= '<td align="right">';
$html .= '<a href="' . admin_url( 'admin.php?page=' . WPML_ST_FOLDER . '/menu/string-translation.php&amp;download_mo=' . $language . '&amp;version=' . $version_key ) . '" class="button-secondary">' . __( 'Review changes and update', 'wpml-string-translation' ) . '</a>';
$html .= '</td>';
$html .= '<tr>';
$html .= '</tr>';
}
}
$html .= '</table>';
} else {
$html .= __( 'No updates found.', 'wpml-string-translation' );
}
} catch ( Exception $error ) {
$html .= '<span style="color:#f00" >' . $error->getMessage() . '</span>';
}
echo json_encode( array( 'html' => $html ) );
exit;
}
function save_preferences() {
global $sitepress;
$iclsettings['st']['auto_download_mo'] = @intval( $_POST['auto_download_mo'] );
$iclsettings['hide_upgrade_notice'] = implode( '.', array_slice( explode( '.', ICL_SITEPRESS_VERSION ), 0, 3 ) );
$sitepress->save_settings( $iclsettings );
if ( $iclsettings['st']['auto_download_mo'] ) {
try {
$this->updates_check( array( 'trigger' => 'setting-changed' ) );
} catch ( Exception $e ) {
}
}
wp_send_json_success( array( 'enabled' => $iclsettings['st']['auto_download_mo'] ) );
}
function save_settings() {
update_option( 'icl_adl_settings', $this->settings );
}
function get_option( $name ) {
return isset( $this->settings[ $name ] ) ? $this->settings[ $name ] : null;
}
function load_xml() {
if ( ! class_exists( 'WP_Http' ) ) {
include_once ABSPATH . WPINC . '/class-http.php';
}
$client = new WP_Http();
$response = $client->request(
self::LOCALES_XML_FILE,
array(
'timeout' => 15,
'decompress' => false,
)
);
if ( is_wp_error( $response ) || ! in_array( $response['response']['code'], array( 200, 301, 300 ) ) ) {
$load_xml_error_message = '';
if ( isset( $response->errors ) ) {
$errors = '';
foreach ( $response->errors as $error => $error_messages ) {
$errors .= $error . '<br/>';
foreach ( $error_messages as $error_message ) {
$errors .= '- ' . $error_message . '<br/>';
}
}
$load_xml_error_message .= sprintf( __( 'Failed downloading the language information file.', 'wpml-string-translation' ), $errors );
$load_xml_error_message .= '<br/>' . sprintf( __( 'Errors: %s', 'wpml-string-translation' ), $errors );
} else {
$load_xml_error_message .= __( 'Failed downloading the language information file. Please go back and try a little later.', 'wpml-string-translation' );
}
if ( ! is_wp_error( $response ) && isset( $response['response'] ) ) {
$load_xml_error_message .= '<br/>Response: ' . $response['response']['code'] . ' (' . $response['response']['message'] . ').';
}
$this->xml = false;
throw new Exception( $load_xml_error_message );
} elseif ( $response['response']['code'] == 200 ) {
require_once ICL_PLUGIN_PATH . '/lib/icl_api.php';
$this->xml = new SimpleXMLElement( icl_gzdecode( $response['body'] ) );
}
}
function get_mo_file_urls( $wplocale ) {
if ( ! $this->xml ) {
return false;
}
global $wp_version;
$wpversion = preg_replace( '#-(.+)$#', '', $wp_version );
$wpversion = join( '.', array_slice( explode( '.', $wpversion ), 0, 2 ) ) . '.x';
$exp = explode( '-', $wplocale );
$language = $exp[0];
$locale = isset( $exp[1] ) ? $wplocale : $language;
$mo_files = array();
$projects = $this->xml->xpath( $language . '/' . $locale );
if ( ! empty( $projects ) ) {
$project_names = array();
foreach ( $projects[0] as $project_name => $data ) {
// subprojects
if ( empty( $data->versions ) ) {
$subprojects = $this->xml->xpath( $language . '/' . $locale . '/' . $project_name );
if ( ! empty( $subprojects ) ) {
foreach ( $subprojects[0] as $sub_project_name => $sdata ) {
$project_names[] = $project_name . '/' . $sub_project_name;
}
}
} else {
$project_names[] = $project_name;
}
}
if ( ! empty( $project_names ) ) {
foreach ( $project_names as $project_name ) {
// try to get the corresponding version
$locv_path = $this->xml->xpath( "{$language}/{$locale}/{$project_name}/versions/version[@number=\"" . $wpversion . '"]' );
// try to get the dev recent version
if ( empty( $locv_path ) ) {
$locv_path = $this->xml->xpath( "{$language}/{$locale}/{$project_name}/versions/version[@number=\"dev\"]" );
}
if ( ! empty( $locv_path ) ) {
$mo_files[ $project_name ]['url'] = (string) $locv_path[0]->url;
$mo_files[ $project_name ]['signature'] = (string) $locv_path[0]['signature'];
$mo_files[ $project_name ]['translated'] = (string) $locv_path[0]['translated'];
$mo_files[ $project_name ]['untranslated'] = (string) $locv_path[0]['untranslated'];
}
}
}
}
return $mo_files;
}
function get_translation_files() {
global $sitepress;
$active_languages = $sitepress->get_active_languages();
foreach ( $active_languages as $language ) {
$locale = $sitepress->get_locale( $language['code'] );
if ( ! isset( $this->lang_map[ $locale ] ) ) {
continue;
}
$wplocale = $this->lang_map[ $locale ];
$urls = $this->get_mo_file_urls( $wplocale );
if ( ! empty( $urls ) ) {
$this->translation_files[ $language['code'] ] = $urls;
}
}
return $this->translation_files;
}
function get_translations( $language, $args = array() ) {
global $wpdb;
$translations = array();
$types = array( 'core' );
extract( $args, EXTR_OVERWRITE );
if ( ! class_exists( 'WP_Http' ) ) {
include_once ABSPATH . WPINC . '/class-http.php';
}
$client = new WP_Http();
foreach ( $types as $type ) {
if ( isset( $this->translation_files[ $language ][ $type ]['url'] ) ) {
$response = $client->request( $this->translation_files[ $language ][ $type ]['url'], array( 'timeout' => 15 ) );
if ( is_wp_error( $response ) ) {
$err = __( 'Error getting the translation file. Please go back and try again.', 'wordpress-language' );
if ( isset( $response->errors['http_request_failed'][0] ) ) {
$err .= '<br />' . $response->errors['http_request_failed'][0];
}
echo '<div class="error"><p>' . $err . '</p></div>';
return false;
}
$mo = new MO();
$pomo_reader = new POMO_StringReader( $response['body'] );
$mo->import_from_reader( $pomo_reader );
$data = $wpdb->get_results(
$wpdb->prepare(
"
SELECT st.value, s.name, s.gettext_context
FROM {$wpdb->prefix}icl_string_translations st
JOIN {$wpdb->prefix}icl_strings s ON st.string_id = s.id
WHERE s.context = %s AND st.language = %s
",
self::CONTEXT,
$language
)
);
$string_existing = array();
foreach ( $data as $row ) {
$string_existing[ md5( $row->name . $row->gettext_context ) ] = $row->value;
}
foreach ( $mo->entries as $key => $v ) {
$tpairs = array();
$v->singular = str_replace( "\n", '\n', $v->singular );
$tpairs[] = array(
'string' => $v->singular,
'translation' => $v->translations[0],
'name' => md5( $v->singular ),
'gettext_context' => $v->context,
);
if ( $v->is_plural ) {
$v->plural = str_replace( "\n", '\n', $v->plural );
$tpairs[] = array(
'string' => $v->plural,
'translation' => ! empty( $v->translations[1] ) ? $v->translations[1] : $v->translations[0],
'name' => md5( $v->plural ),
'gettext_context' => $v->context,
);
}
foreach ( $tpairs as $pair ) {
$key = md5( $pair['name'] . $pair['gettext_context'] );
$existing_translation = isset( $string_existing[ $key ] ) ? $string_existing[ $key ] : null;
if ( empty( $existing_translation ) ) {
$translations['new'][] = array(
'string' => $pair['string'],
'translation' => '',
'new' => $pair['translation'],
'name' => $pair['name'],
'gettext_context' => $pair['gettext_context'],
);
} else {
if ( strcmp( $existing_translation, $pair['translation'] ) !== 0 ) {
$translations['updated'][] = array(
'string' => $pair['string'],
'translation' => $existing_translation,
'new' => $pair['translation'],
'name' => $pair['name'],
'gettext_context' => $pair['gettext_context'],
);
}
}
}
}
}
}
return $translations;
}
function save_translations( $data, $language, $version = false ) {
set_time_limit( 0 );
if ( false === $version ) {
global $wp_version;
$version = preg_replace( '#-(.+)$#', '', $wp_version );
}
foreach ( $data as $key => $string ) {
if ( $string['gettext_context'] ) {
$string_context = array(
'domain' => self::CONTEXT,
'context' => $string['gettext_context'],
);
} else {
$string_context = self::CONTEXT;
}
$string_id = icl_register_string( $string_context, $string['name'], $string['string'] );
if ( $string_id ) {
icl_add_string_translation( $string_id, $language, $string['translation'], ICL_TM_COMPLETE );
}
}
$version_projects = explode( ';', $version );
foreach ( $version_projects as $project ) {
$exp = explode( '|', $project );
$this->settings['translations'][ $language ][ $exp[0] ]['time'] = time();
$this->settings['translations'][ $language ][ $exp[0] ]['installed'] = $exp[1];
}
$this->save_settings();
}
}

View File

@@ -0,0 +1,28 @@
<?php
define( 'WPML_ST_FOLDER', basename( WPML_ST_PATH ) );
define( 'WPML_ST_URL', plugins_url( '', dirname( __FILE__ ) ) );
// Old ST status constants, kept for backward compatibility with plugins that use them, like WCML
define( 'ICL_STRING_TRANSLATION_PARTIAL', 2 );
define( 'ICL_STRING_TRANSLATION_COMPLETE', 10 );
define( 'ICL_STRING_TRANSLATION_NEEDS_UPDATE', 3 );
define( 'ICL_STRING_TRANSLATION_NOT_TRANSLATED', 0 );
define( 'ICL_STRING_TRANSLATION_WAITING_FOR_TRANSLATOR', 1 );
define( 'ICL_STRING_TRANSLATION_TEMPLATE_DIRECTORY', get_template_directory() );
define( 'ICL_STRING_TRANSLATION_STYLESHEET_DIRECTORY', get_stylesheet_directory() );
define( 'ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_SOURCE', 0 );
define( 'ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_PAGE', 1 );
define( 'ICL_STRING_TRANSLATION_STRING_TRACKING_THRESHOLD', 5 );
define( 'ICL_STRING_TRANSLATION_AUTO_REGISTER_THRESHOLD', 500 );
if ( ! defined( 'ICL_STRING_TRANSLATION_DYNAMIC_CONTEXT' ) ) {
define( 'ICL_STRING_TRANSLATION_DYNAMIC_CONTEXT', 'wpml_string' );
}
define( 'WPML_ST_DEFAULT_STRINGS_PER_PAGE', 10 );
define( 'WPML_ST_WIDGET_STRING_DOMAIN', 'Widgets' );

View File

@@ -0,0 +1,19 @@
<?php
require WPML_ST_PATH . '/inc/functions.php';
require WPML_ST_PATH . '/inc/private-actions.php';
require WPML_ST_PATH . '/inc/private-filters.php';
/**
* @return WPML_Admin_Texts
*/
function wpml_st_load_admin_texts() {
global $wpml_st_admin_texts;
if ( ! $wpml_st_admin_texts ) {
global $iclTranslationManagement, $WPML_String_Translation;
$wpml_st_admin_texts = new WPML_Admin_Texts( $iclTranslationManagement, $WPML_String_Translation );
}
return $wpml_st_admin_texts;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
<?php
use WPML\API\Sanitize;
class WPML_Plugin_String_Scanner extends WPML_String_Scanner implements IWPML_ST_String_Scanner {
private $current_plugin_file;
public function scan() {
$plugin_file = $_POST['plugin'];
$this->current_plugin_file = WPML_PLUGINS_DIR . '/' . $plugin_file;
$this->current_type = 'plugin';
$this->current_path = dirname( $this->current_plugin_file );
$this->text_domain = $this->get_plugin_text_domain();
$this->scan_starting( $this->current_type );
$text_domain = $this->get_plugin_text_domain();
$this->init_text_domain( $text_domain );
$this->scan_plugin_files();
$this->current_type = 'plugin';
$this->set_stats( 'plugin_localization_domains', $plugin_file );
$this->scan_response();
}
private function scan_plugin_files( $dir_or_file = false, $recursion = 0 ) {
require_once WPML_ST_PATH . '/inc/potx.php';
foreach ( $_POST['files'] as $file ) {
$file = Sanitize::string( $file );
if ( $this->file_hashing->hash_changed( $file ) ) {
_potx_process_file( $file, 0, array( $this, 'store_results' ), '_potx_save_version', $this->get_default_domain() );
$this->add_scanned_file( $file );
}
}
}
private function get_plugin_text_domain() {
$text_domain = '';
if ( ! function_exists( 'get_plugin_data' ) ) {
include_once ABSPATH . '/wp-admin/includes/plugin.php';
}
if ( function_exists( 'get_plugin_data' ) ) {
$plugin_data = get_plugin_data( $this->current_plugin_file );
if ( isset( $plugin_data['TextDomain'] ) && $plugin_data['TextDomain'] != '' ) {
$text_domain = $plugin_data['TextDomain'];
return $text_domain;
}
return $text_domain;
}
return $text_domain;
}
}

View File

@@ -0,0 +1,132 @@
<?php
class WPML_PO_Import {
private $lines;
private $strings;
private $error_str;
public function __construct( $file_name ) {
global $wpdb;
$this->strings = array( );
$this->error_str = '';
$this->lines = file( $file_name );
$fuzzy = 0;
$name = false;
$context = '';
for ( $k = 0; $k < count( $this->lines ); $k ++ ) {
$date_time_flag = false;
if ( 0 === strpos( $this->lines[ $k ], '#, fuzzy' ) ) {
$fuzzy = 1;
$k ++;
}
if ( 0 === strpos( $this->lines[ $k ], '# wpml-name: ' ) ) {
$name = preg_replace( "/^# wpml-name: /i", '', trim( $this->lines[ $k ] ) );
$k ++;
}
if ( preg_match( '#msgctxt "(.*)"#im', trim( $this->lines[ $k ] ), $matches ) ) { //we look for the line that poedit needs for unique identification of the string
$context = $matches[ 1 ];
//if ( preg_match( '/wpmldatei18/', $this->lines[ $k ] ) ) { //if it contains the date_time setting we add the flag to escape the control structures in the date time placeholder string
// $date_time_flag = true;
//}
$k ++;
}
$int = preg_match( '#msgid "(.*)"#im', trim( $this->lines[ $k ] ), $matches );
if ( $int ) {
list( $string, $k ) = $this->get_string( $matches[1], $k );
$int = preg_match( '#msgstr "(.*)"#im', trim( $this->lines[ $k + 1 ] ), $matches );
if ( $int ) {
list( $translation, $k ) = $this->get_string( $matches[ 1 ], $k + 1 );
} else {
$translation = "";
}
if ( $name === false ) {
$name = md5( $string );
}
if ( $string ) {
$string_exists = $wpdb->get_var( $wpdb->prepare( "
SELECT id FROM {$wpdb->prefix}icl_strings
WHERE context=%s AND name=%s AND gettext_context=%s",
esc_sql( $_POST[ 'icl_st_i_context_new' ] ? $_POST[ 'icl_st_i_context_new' ] : $_POST[ 'icl_st_i_context' ] ),
$name,
$context
)
);
if ( $date_time_flag ) {
$string = str_replace( "\\\\", "\\", $string );
$translation = str_replace( "\\\\", "\\", $translation );
$name = str_replace( "\\\\", "\\", $name );
}
$this->strings[ ] = array(
'string' => $string,
'translation' => $translation,
'name' => $name,
'fuzzy' => $fuzzy,
'exists' => $string_exists,
'context' => $context
);
}
$k ++;
$name = false;
$context = '';
}
if ( $k < count( $this->lines ) && ! trim( $this->lines[ $k ] ) ) {
$fuzzy = 0;
}
}
if ( empty( $this->strings ) ) {
$this->error_str = __( 'No string found', 'wpml-string-translation' );
}
}
private function get_string( $string, $k ) {
$string = $this->strip_slashes( $string );
// check for multiline strings
if ( $k + 1 < count( $this->lines ) ) {
$int = preg_match( '#^"(.*)"$#', trim( $this->lines[ $k + 1 ] ), $matches );
while ( $int ) {
$string .= $this->strip_slashes( $matches[ 1 ] );
$k++;
if ( $k + 1 < count( $this->lines ) ) {
$int = preg_match( '#^"(.*)"$#', trim( $this->lines[ $k + 1 ] ), $matches );
} else {
$int = false;
}
}
}
return array( $string, $k );
}
private function strip_slashes( $string ) {
$string = str_replace( '\"', '"', $string );
$string = str_replace( '\\\\', '\\', $string );
return $string;
}
public function has_strings( ) {
return ! empty( $this->strings );
}
public function get_strings( ) {
return $this->strings;
}
public function get_errors( ) {
return $this->error_str;
}
}

View File

@@ -0,0 +1,126 @@
<?php
class WPML_PO_Parser {
public static function create_po( $strings, $pot_only = false ) {
global $wpdb;
$po = self::get_po_file_header();
foreach ( $strings as $s ) {
$ids[ ] = $s[ 'string_id' ];
}
if ( ! empty( $ids ) ) {
$sql_prepared = $wpdb->prepare( "SELECT string_id, position_in_page
FROM {$wpdb->prefix}icl_string_positions
WHERE kind=%d AND string_id IN(%s)", ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_SOURCE, implode( ',', $ids ));
$res = $wpdb->get_results( $sql_prepared );
foreach ( $res as $row ) {
$positions[ $row->string_id ] = $row->position_in_page;
}
}
foreach ( $strings as $s ) {
$po .= PHP_EOL;
if ( ! $pot_only && isset( $s[ 'translations' ] ) && isset( $s[ 'translations' ][ key( $s[ 'translations' ] ) ][ 'value' ] ) ) {
$translation = $s[ 'translations' ][ key( $s[ 'translations' ] ) ][ 'value' ];
if ( $translation != '' && $s[ 'translations' ][ key( $s[ 'translations' ] ) ][ 'status' ] != ICL_TM_COMPLETE ) {
$po .= '#, fuzzy' . PHP_EOL;
}
} else {
$translation = '';
}
if ( isset( $positions[ $s[ 'string_id' ] ] ) ) {
$exp = @explode( '::', $positions[ $s[ 'string_id' ] ] );
$file = @file( $exp[ 0 ] );
} else {
unset( $file );
unset( $exp );
}
$po_single = '';
if ( isset( $file ) && isset( $exp ) ) {
$line_number = (int) $exp[ 1 ];
$line_number--; // Make it 0 base
$line_number -= 2; // Go back 2 lines
if ( $line_number < 0 ) {
$line_number = 0;
}
for ( $line = 0; $line < 3; $line++ ) {
$po_single .= '#: ' . @trim( $file[ $line_number + $line ] ) . PHP_EOL;
}
}
$po_single .= '# wpml-name: ' . $s[ 'name' ] . PHP_EOL;
if ( $s[ 'gettext_context' ] ) {
$po_single .=
'msgctxt "'
. $s[ 'gettext_context' ]
. '"'
. PHP_EOL;
}
$po_single .= 'msgid ' . self::output_string( $s[ 'value' ] ) . PHP_EOL;
$po_single .= 'msgstr ' . self::output_string( $translation ) . PHP_EOL;
$po .= $po_single;
}
return $po;
}
public static function get_po_file_header() {
$po_title = 'WPML_EXPORT';
$translation_language = 'en';
if ( isset( $_GET['context'] ) ) {
$po_title .= '_' . filter_var( $_GET['context'], FILTER_SANITIZE_STRING );
}
if ( isset( $_GET['translation_language'] ) ) {
$translation_language = filter_var( $_GET['translation_language'], FILTER_SANITIZE_STRING );
}
$po = "";
$po .= '# This file was generated by WPML' . PHP_EOL;
$po .= '# WPML is a WordPress plugin that can turn any WordPress or WordPressMU site into a full featured multilingual content management system.' . PHP_EOL;
$po .= '# https://wpml.org' . PHP_EOL;
$po .= 'msgid ""' . PHP_EOL;
$po .= 'msgstr ""' . PHP_EOL;
$po .= '"Content-Type: text/plain; charset=utf-8\n"' . PHP_EOL;
$po .= '"Content-Transfer-Encoding: 8bit\n"' . PHP_EOL;
$po .= '"Project-Id-Version:' . $po_title . '\n"' . PHP_EOL;
$po .= '"POT-Creation-Date: \n"' . PHP_EOL;
$po .= '"PO-Revision-Date: \n"' . PHP_EOL;
$po .= '"Last-Translator: \n"' . PHP_EOL;
$po .= '"Language-Team: \n"' . PHP_EOL;
$po .= '"Language:' . $translation_language . '\n"' . PHP_EOL;
$po .= '"MIME-Version: 1.0\n"' . PHP_EOL;
return $po;
}
private static function output_string( $str ) {
if ( strstr( $str, "\n" ) ) {
$str = str_replace( "\r", "", $str );
$lines = explode( "\n", $str );
$str = '""';
foreach ( $lines as $line ) {
$str .= PHP_EOL . '"' . self::addslashes( $line ) . '\n"' ;
}
} else {
$str = '"' . self::addslashes( $str ) . '"';
}
return $str;
}
private static function addslashes ( $str ) {
$str = str_replace( '\\', '\\\\', $str );
$str = str_replace( '"', '\"', $str );
return $str;
}
}

View File

@@ -0,0 +1,704 @@
<?php
class WPML_String_Scanner {
const DEFAULT_DOMAIN = 'default';
/**
* @param string|NULL $type 'plugin' or 'theme'
*/
protected $current_type;
protected $current_path;
protected $text_domain;
private $domains;
private $registered_strings;
private $lang_codes;
private $currently_scanning;
private $domains_found;
private $default_domain;
/** @var WP_Filesystem_Base */
private $wp_filesystem;
/** @var WPML_File $wpml_file */
private $wpml_file;
/**
* @var array
*/
private $scan_stats;
private $scanned_files;
/**
* @var WPML_File_Name_Converter
*/
private $file_name_converter;
/**
* @var WPML_ST_DB_Mappers_String_Positions
*/
private $string_positions_mapper;
/**
* @var WPML_ST_DB_Mappers_Strings
*/
private $strings_mapper;
/** @var WPML_ST_File_Hashing */
protected $file_hashing;
/**
* WPML_String_Scanner constructor.
*
* @param WP_Filesystem_Base $wp_filesystem
* @param WPML_ST_File_Hashing $file_hashing
*/
public function __construct( WP_Filesystem_Base $wp_filesystem, WPML_ST_File_Hashing $file_hashing ) {
global $wpdb;
$this->domains = array();
$this->registered_strings = array();
$this->lang_codes = array();
$this->domains_found = array();
$this->scan_stats = array();
$this->scanned_files = array();
$this->default_domain = 'default';
$this->wp_filesystem = $wp_filesystem;
$this->file_hashing = $file_hashing;
}
protected function scan_starting( $scanning ) {
$this->currently_scanning = $scanning;
$this->domains_found[ $this->currently_scanning ] = array();
$this->default_domain = 'default';
}
protected function scan_response() {
global $__icl_registered_strings, $sitepress;
$result = array(
'scan_successful_message' => esc_html__( 'Scan successful! WPML found %s strings.', 'wpml-string-translation' ),
'files_processed_message' => esc_html__( 'The following files were processed:', 'wpml-string-translation' ),
'files_processed' => $this->get_scanned_files(),
'strings_found' => is_array( $__icl_registered_strings ) ? count( $__icl_registered_strings ) : 0,
);
if ( $result['strings_found'] ) {
$result['scan_successful_message'] .= __( ' They were added to the string translation table.', 'wpml-string-translation' );
}
$sitepress->get_wp_api()->wp_send_json_success( $result );
}
final protected function init_text_domain( $text_domain ) {
$string_settings = apply_filters( 'wpml_get_setting', false, 'st' );
$use_header_text_domain = isset( $string_settings['use_header_text_domains_when_missing'] ) && $string_settings['use_header_text_domains_when_missing'];
$this->default_domain = 'default';
if ( $use_header_text_domain && $text_domain ) {
$this->default_domain = $text_domain;
}
}
protected function get_domains_found() {
return $this->domains_found[ $this->currently_scanning ];
}
protected function get_default_domain() {
return $this->default_domain;
}
protected function maybe_register_string( $value, $gettext_context ) {
if ( ! $this->get_string_id( $value, $gettext_context, $gettext_context ) ) {
$this->store_results( $value, $gettext_context, $gettext_context, '', 0 );
}
}
/**
* Get list of files under directory.
*
* @param string $path Directory to parse.
* @param object $filesystem WP_Filesystem object
* @return array
*/
private function extract_files( $path, $filesystem ) {
$path = $this->add_dir_separator( $path );
$files = array();
$list = $filesystem->dirlist( $path );
foreach ( $list as $single_file ) {
if ( 'f' === $single_file['type'] ) {
$files[] = $path . $single_file['name'];
} else {
$files = array_merge( $files, $this->extract_files( $path . $single_file['name'], $filesystem ) );
}
}
return $files;
}
/**
* Make sure that the last character is second argument.
*
* @param string $path
* @param string $separator
* @return string
*/
private function add_dir_separator( $path, $separator = DIRECTORY_SEPARATOR ) {
if ( strlen( $path ) > 0 ) {
if ( substr( $path, -1 ) !== $separator ) {
return $path . $separator;
} else {
return $path;
}
} else {
return $path;
}
}
private function fix_existing_string_with_wrong_context( $original_value, $new_string_context, $gettext_context ) {
if ( ! isset( $this->current_type ) || ! isset( $this->current_path ) ) {
return;
}
$old_context = $this->get_old_context();
$new_context_string_id = $this->get_string_id( $original_value, $new_string_context, $gettext_context );
if ( ! $new_context_string_id ) {
$old_context_string_id = $this->get_string_id( $original_value, $old_context, $gettext_context );
if ( $old_context_string_id ) {
$this->fix_string_context( $old_context_string_id, $new_string_context );
unset( $this->registered_strings[ $old_context ] );
unset( $this->registered_strings[ $new_string_context ] );
}
}
}
private function get_old_context() {
$plugin_or_theme_path = $this->current_path;
$name = basename( $plugin_or_theme_path );
$old_context = $this->current_type . ' ' . $name;
return $old_context;
}
private function get_lang_code( $lang_locale ) {
global $wpdb;
if ( ! isset( $this->lang_codes[ $lang_locale ] ) ) {
$this->lang_codes[ $lang_locale ] = $wpdb->get_var( $wpdb->prepare( "SELECT code FROM {$wpdb->prefix}icl_locale_map WHERE locale=%s", $lang_locale ) );
}
return $this->lang_codes[ $lang_locale ];
}
private function get_string_id( $original, $domain, $gettext_context ) {
$this->warm_cache( $domain );
$string_context_name = md5( $gettext_context . md5( $original ) );
$string_id = isset( $this->registered_strings[ $domain ] ['context-name'] [ $string_context_name ] ) ? $this->registered_strings[ $domain ] ['context-name'] [ $string_context_name ] : null;
return $string_id;
}
private function fix_string_context( $string_id, $new_string_context ) {
global $wpdb;
$string = $wpdb->get_row( $wpdb->prepare( "SELECT gettext_context, name FROM {$wpdb->prefix}icl_strings WHERE id=%d", $string_id ) );
$domain_name_context_md5 = md5( $new_string_context . $string->name . $string->gettext_context );
$wpdb->update(
$wpdb->prefix . 'icl_strings',
array(
'context' => $new_string_context,
'domain_name_context_md5' => $domain_name_context_md5,
),
array( 'id' => $string_id ),
array( '%s', '%s' ),
'%d'
);
}
protected function set_stats( $key, $item ) {
$string_settings = apply_filters( 'wpml_get_setting', false, 'st' );
foreach ( $this->get_domains_found() as $name => $count ) {
$old_count = isset( $string_settings[ $key ][ $item ][ $name ] ) ?
$string_settings[ $key ][ $item ][ $name ] :
0;
$string_settings[ $key ][ $item ][ $name ] = $old_count + $count;
}
do_action( 'wpml_set_setting', 'st', $string_settings, true );
}
public function store_results( $string, $domain, $_gettext_context, $file, $line ) {
global $wpdb;
$domain = $domain ? $domain : 'WordPress';
if ( ! isset( $this->domains_found[ $this->currently_scanning ] [ $domain ] ) ) {
$this->domains_found[ $this->currently_scanning ] [ $domain ] = 1;
} else {
$this->domains_found[ $this->currently_scanning ] [ $domain ] += 1;
}
if ( ! in_array( $domain, $this->domains ) ) {
$this->domains[] = $domain;
// clear existing entries (both source and page type)
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}icl_string_positions WHERE string_id IN
(SELECT id FROM {$wpdb->prefix}icl_strings WHERE context = %s)",
$domain
)
);
}
$string = str_replace( '\n', "\n", $string );
$string = str_replace( array( '\"', "\\'" ), array( '"', "'" ), $string );
// replace extra backslashes added by _potx_process_file
$string = str_replace( array( '\\\\' ), array( '\\' ), $string );
$string = stripcslashes( $string );
global $__icl_registered_strings;
if ( ! isset( $__icl_registered_strings ) ) {
$__icl_registered_strings = array();
}
if ( ! isset( $__icl_registered_strings[ $domain . '||' . $string . '||' . $_gettext_context ] ) ) {
$name = md5( $string );
$this->fix_existing_string_with_wrong_context( $string, $domain, $_gettext_context );
$this->register_string( $domain, $_gettext_context, $name, $string );
$__icl_registered_strings[ $domain . '||' . $string . '||' . $_gettext_context ] = true;
}
// store position in source
$this->track_string(
$string,
array(
'domain' => $domain,
'context' => $_gettext_context,
),
ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_SOURCE,
$file,
$line
);
}
private function register_string( $domain, $context, $name, $string ) {
$this->warm_cache( $domain );
if ( ! isset( $this->registered_strings[ $domain ] ['context-name'] [ md5( $context . $name ) ] ) ) {
if ( $context ) {
$string_context = array(
'domain' => $domain,
'context' => $context,
);
} else {
$string_context = $domain;
}
$string_id = icl_register_string( $string_context, $name, $string );
$this->registered_strings[ $domain ] ['context-name'] [ md5( $context . $name ) ] = $string_id;
}
}
private function warm_cache( $domain ) {
if ( ! isset( $this->registered_strings[ $domain ] ) ) {
$this->registered_strings[ $domain ] = array(
'context-name' => array(),
'value' => array(),
);
$results = $this->get_strings_mapper()->get_all_by_context( $domain );
foreach ( $results as $result ) {
$this->registered_strings[ $domain ] ['context-name'] [ md5( $result['gettext_context'] . $result['name'] ) ] = $result['id'];
}
}
}
public function track_string( $text, $context, $kind = ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_PAGE, $file = null, $line = null ) {
list ( $domain, $gettext_context ) = wpml_st_extract_context_parameters( $context );
// get string id
$string_id = $this->get_string_id( $text, $domain, $gettext_context );
if ( $string_id ) {
$str_pos_mapper = $this->get_string_positions_mapper();
$string_records_count = $str_pos_mapper->get_count_of_positions_by_string_and_kind( $string_id, $kind );
if ( ICL_STRING_TRANSLATION_STRING_TRACKING_THRESHOLD > $string_records_count ) {
if ( $kind == ICL_STRING_TRANSLATION_STRING_TRACKING_TYPE_PAGE ) {
// get page url
$https = isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ? 's' : '';
$position = 'http' . $https . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
} else {
$file = $this->get_file_name_converter()->transform_realpath_to_reference( $file );
$position = $file . '::' . $line;
}
if ( ! $str_pos_mapper->is_string_tracked( $string_id, $position, $kind ) && ! $this->is_string_preview() ) {
$str_pos_mapper->insert( $string_id, $position, $kind );
}
}
}
}
protected function add_stat( $text ) {
$this->scan_stats[] = $text;
}
protected function get_scan_stats() {
return $this->scan_stats;
}
protected function add_scanned_file( $file ) {
$this->scanned_files[] = $this->format_path_for_display( $file );
}
protected function get_scanned_files() {
return $this->scanned_files;
}
protected function cleanup_wrong_contexts() {
global $wpdb;
$old_context = $this->get_old_context();
/** @var array $results */
$results = $wpdb->get_results(
$wpdb->prepare(
"
SELECT id, name, value
FROM {$wpdb->prefix}icl_strings
WHERE context = %s",
$old_context
)
);
foreach ( $results as $string ) {
// See if the string has no translations
/** @var array $old_translations */
$old_translations = $wpdb->get_results(
$wpdb->prepare(
"
SELECT id, language, status, value
FROM {$wpdb->prefix}icl_string_translations
WHERE string_id = %d",
$string->id
)
);
if ( ! $old_translations ) {
// We don't have any translations so we can delete the string.
$wpdb->delete( $wpdb->prefix . 'icl_strings', array( 'id' => $string->id ), array( '%d' ) );
} else {
// check if we have a new string in the right context
$domains = $this->get_domains_found();
foreach ( $domains as $domain => $count ) {
$new_string_id = $wpdb->get_var(
$wpdb->prepare(
"
SELECT id
FROM {$wpdb->prefix}icl_strings
WHERE context = %s AND name = %s AND value = %s",
$domain,
$string->name,
$string->value
)
);
if ( $new_string_id ) {
// See if it has the same translations
/** @var array $new_translations */
$new_translations = $wpdb->get_results(
$wpdb->prepare(
"
SELECT id, language, status, value
FROM {$wpdb->prefix}icl_string_translations
WHERE string_id = %d",
$new_string_id
)
);
foreach ( $new_translations as $new_translation ) {
foreach ( $old_translations as $index => $old_translation ) {
if ( $new_translation->language == $old_translation->language &&
$new_translation->status == $old_translation->status &&
$new_translation->value == $old_translation->value ) {
unset( $old_translations[ $index ] );
}
}
}
if ( ! $old_translations ) {
// We don't have any old translations that are not in the new strings so we can delete the string.
$wpdb->delete( $wpdb->prefix . 'icl_strings', array( 'id' => $string->id ), array( '%d' ) );
break;
}
}
}
}
}
// Rename the context for any strings that are in the old context
// This way the update message will no longer show.
$obsolete_context = str_replace( 'plugin ', '', $old_context );
$obsolete_context = str_replace( 'theme ', '', $obsolete_context );
$obsolete_context = $obsolete_context . ' (obsolete)';
$string_update_context = $wpdb->get_results(
$wpdb->prepare(
"
SELECT id FROM {$wpdb->prefix}icl_strings
WHERE context = %s
",
$old_context
),
ARRAY_A
);
if ( $string_update_context ) {
$wpdb->query(
$wpdb->prepare(
"
UPDATE {$wpdb->prefix}icl_strings
SET context = %s
WHERE id IN ( " . implode( ',', wp_list_pluck( $string_update_context, 'id' ) ) . ' )
',
$obsolete_context
)
);
}
}
protected function copy_old_translations( $contexts, $prefix ) {
foreach ( $contexts as $context ) {
$old_strings = $this->get_strings_by_context( $prefix . ' ' . $context );
if ( 0 === count( $old_strings ) ) {
continue;
}
$old_translations = $this->get_strings_translations( $old_strings );
$new_strings = $this->get_strings_by_context( $context );
$new_translations = $this->get_strings_translations( $new_strings );
foreach ( $old_translations as $old_translation ) {
// see if we have a new translation.
$found = false;
foreach ( $new_translations as $new_translation ) {
if ( $new_translation->string_id == $old_translation->string_id &&
$new_translation->language == $old_translation->language ) {
$found = true;
break;
}
}
if ( ! $found ) {
// Copy the old translation to the new string.
// Find the original
foreach ( $old_strings as $old_string ) {
if ( $old_string->id == $old_translation->string_id ) {
// See if we have the same string in the new strings
foreach ( $new_strings as $new_string ) {
if ( $new_string->value == $old_string->value ) {
// Add the old translation to new string.
icl_add_string_translation( $new_string->id, $old_translation->language, $old_translation->value, ICL_TM_COMPLETE );
break;
}
}
break;
}
}
}
}
}
}
/**
* @param string $context
*
* @return array
*/
private function get_strings_by_context( $context ) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"
SELECT id, name, value
FROM {$wpdb->prefix}icl_strings
WHERE context = %s",
$context
)
);
}
/**
* @param array $strings
*
* @return array<\stdClass>
*/
private function get_strings_translations( $strings ) {
global $wpdb;
$translations = array();
if ( count( $strings ) ) {
foreach ( array_chunk( $strings, 100 ) as $chunk ) {
$ids = array();
foreach ( $chunk as $string ) {
$ids[] = $string->id;
}
$ids = implode( ',', $ids );
$rows = $wpdb->get_results(
"
SELECT id, string_id, language, status, value
FROM {$wpdb->prefix}icl_string_translations
WHERE string_id IN ({$ids})"
);
$translations = array_merge( $translations, $rows );
}
}
return $translations;
}
protected function remove_notice( $notice_id ) {
global $wpml_st_admin_notices;
if ( isset( $wpml_st_admin_notices ) ) {
/** @var WPML_ST_Themes_And_Plugins_Updates $wpml_st_admin_notices */
$wpml_st_admin_notices->remove_notice( $notice_id );
}
}
/**
* @return WPML_ST_DB_Mappers_Strings
*/
public function get_strings_mapper() {
if ( null === $this->strings_mapper ) {
global $wpdb;
$this->strings_mapper = new WPML_ST_DB_Mappers_Strings( $wpdb );
}
return $this->strings_mapper;
}
/**
* @param WPML_ST_DB_Mappers_Strings $strings_mapper
*/
public function set_strings_mapper( WPML_ST_DB_Mappers_Strings $strings_mapper ) {
$this->strings_mapper = $strings_mapper;
}
/**
* @return WPML_ST_DB_Mappers_String_Positions
*/
public function get_string_positions_mapper() {
if ( null === $this->string_positions_mapper ) {
global $wpdb;
$this->string_positions_mapper = new WPML_ST_DB_Mappers_String_Positions( $wpdb );
}
return $this->string_positions_mapper;
}
/**
* @param WPML_ST_DB_Mappers_String_Positions $string_positions_mapper
*/
public function set_string_positions_mapper( WPML_ST_DB_Mappers_String_Positions $string_positions_mapper ) {
$this->string_positions_mapper = $string_positions_mapper;
}
/**
* @return WPML_File_Name_Converter
*/
public function get_file_name_converter() {
if ( null === $this->file_name_converter ) {
$this->file_name_converter = new WPML_File_Name_Converter();
}
return $this->file_name_converter;
}
/**
* @param WPML_File_Name_Converter $converter
*/
public function set_file_name_converter( WPML_File_Name_Converter $converter ) {
$this->file_name_converter = $converter;
}
/**
* @return WPML_File
*/
protected function get_wpml_file() {
if ( ! $this->wpml_file ) {
$this->wpml_file = new WPML_File();
}
return $this->wpml_file;
}
private function is_string_preview() {
$is_string_preview = false;
if ( array_key_exists( 'icl_string_track_value', $_GET ) || array_key_exists( 'icl_string_track_context', $_GET ) ) {
$is_string_preview = true;
}
return $is_string_preview;
}
/** @return bool */
protected function scan_php_and_mo_files() {
return array_key_exists( 'scan_mo_files', $_POST );
}
protected function scan_only_mo_files() {
return array_key_exists( 'scan_only_mo_files', $_POST );
}
/**
* @param string $path
*
* @return string
*/
private function format_path_for_display( $path ) {
$path = stripslashes( $path );
$path = $this->get_wpml_file()->get_relative_path( $path );
$path = $this->get_wpml_file()->fix_dir_separator( $path );
return $path;
}
}

View File

@@ -0,0 +1,44 @@
<?php
class WPML_Theme_String_Scanner extends WPML_String_Scanner implements IWPML_ST_String_Scanner {
public function scan() {
$this->current_type = 'theme';
$this->scan_starting( $this->current_type );
$theme_info = wp_get_theme();
$text_domain = $theme_info->get( 'TextDomain' );
$current_theme_name = array_key_exists( 'theme', $_POST ) ? $_POST['theme'] : '';
$this->current_path = $current_theme_name ? get_theme_root() . '/' . $current_theme_name : '';
$current_theme = wp_get_theme( $current_theme_name );
$this->text_domain = $current_theme->get( 'TextDomain' );
$this->init_text_domain( $text_domain );
$this->scan_theme_files();
$this->set_stats( 'theme_localization_domains', $current_theme_name );
if ( $theme_info && ! is_wp_error( $theme_info ) ) {
$this->remove_notice( $theme_info->get( 'Name' ) );
}
$this->scan_response();
}
private function scan_theme_files() {
require_once WPML_ST_PATH . '/inc/potx.php';
if ( array_key_exists( 'files', $_POST ) ) {
foreach ( $_POST['files'] as $file ) {
$file = filter_var( $file, FILTER_SANITIZE_STRING );
if ( $this->file_hashing->hash_changed( $file ) ) {
$this->add_stat( sprintf( __( 'Scanning file: %s', 'wpml-string-translation' ), $file ) );
$this->add_scanned_file( $file );
_potx_process_file( $file, 0, array( $this, 'store_results' ), '_potx_save_version', $this->get_default_domain() );
} else {
$this->add_stat( sprintf( __( 'Skipping file: %s', 'wpml-string-translation' ), $file ) );
}
}
}
}
}

View File

@@ -0,0 +1,106 @@
an,an
ar,ar
az,az
az_TR,az-tr
bg_BG,bg
bn_BD,bn
bs_BA,bs
ca,ca
ckb,ckb
co,co
cs_CZ,cs
cy,cy
da_DK,da
de_DE,de
de_CH,de
dv,dv
el,el
en_CA,en-ca
en_GB,en-gb
eo,eo
es_CL,es-cl
es_ES,es
es_PE,es-pe
es_VE,es-ve
et,et
eu,eu
fa_AF,fa-af
fa_IR,fa
fi,fi
fo,fo
fr_FR,fr
fy,fy
ga,ga
gd,gd
gl_ES,gl
gu,gu
haw_U,haw
he_IL,he
hi_IN,hi
hr,hr
hu_HU,hu
hy,hy
id_ID,id
is_IS,is
it_IT,it
ja,ja
jv_ID,jv
ka_GE,ka
kea,kea
kk,kk
kn,kn
ko_KR,ko
ku,ku
ky_KY,ky
la,la
li,li
lo,lo
lt,lt
lv,lv
me_ME,me
mg_MG,mg
mk_MK,mk
ml_IN,ml
mn,mn
mr,mr
ms_MY,ms
my_MM,mya
nb_NO,nb
ne_NP,ne
nl,nl
nl_BE,nl
nl_NL,nl
nn_NO,nn
os,os
pa_IN,pa
pl_PL,pl
pt_BR,pt-br
pt_PT,pt
ro_RO,ro
ru_RU,ru
ru_UA,ru-ua
sa_IN,sa-in
si_LK,si
sk_SK,sk
sl_SI,sl
so_SO,so
sq,sq
sr_RS,sr
srd,srd
su_ID,su
sv_SE,sv
sw,sw
ta_IN,ta
ta_LK,ta-lk
te,te
tg,tg
th,th
tl,tl
tr,tr
ug_CN,ug
uk,uk
ur,ur
uz_UZ,uz
vi,vi
zh_CN,zh-cn
zh_TW,zh-tw
1 an an
2 ar ar
3 az az
4 az_TR az-tr
5 bg_BG bg
6 bn_BD bn
7 bs_BA bs
8 ca ca
9 ckb ckb
10 co co
11 cs_CZ cs
12 cy cy
13 da_DK da
14 de_DE de
15 de_CH de
16 dv dv
17 el el
18 en_CA en-ca
19 en_GB en-gb
20 eo eo
21 es_CL es-cl
22 es_ES es
23 es_PE es-pe
24 es_VE es-ve
25 et et
26 eu eu
27 fa_AF fa-af
28 fa_IR fa
29 fi fi
30 fo fo
31 fr_FR fr
32 fy fy
33 ga ga
34 gd gd
35 gl_ES gl
36 gu gu
37 haw_U haw
38 he_IL he
39 hi_IN hi
40 hr hr
41 hu_HU hu
42 hy hy
43 id_ID id
44 is_IS is
45 it_IT it
46 ja ja
47 jv_ID jv
48 ka_GE ka
49 kea kea
50 kk kk
51 kn kn
52 ko_KR ko
53 ku ku
54 ky_KY ky
55 la la
56 li li
57 lo lo
58 lt lt
59 lv lv
60 me_ME me
61 mg_MG mg
62 mk_MK mk
63 ml_IN ml
64 mn mn
65 mr mr
66 ms_MY ms
67 my_MM mya
68 nb_NO nb
69 ne_NP ne
70 nl nl
71 nl_BE nl
72 nl_NL nl
73 nn_NO nn
74 os os
75 pa_IN pa
76 pl_PL pl
77 pt_BR pt-br
78 pt_PT pt
79 ro_RO ro
80 ru_RU ru
81 ru_UA ru-ua
82 sa_IN sa-in
83 si_LK si
84 sk_SK sk
85 sl_SI sl
86 so_SO so
87 sq sq
88 sr_RS sr
89 srd srd
90 su_ID su
91 sv_SE sv
92 sw sw
93 ta_IN ta
94 ta_LK ta-lk
95 te te
96 tg tg
97 th th
98 tl tl
99 tr tr
100 ug_CN ug
101 uk uk
102 ur ur
103 uz_UZ uz
104 vi vi
105 zh_CN zh-cn
106 zh_TW zh-tw

View File

@@ -0,0 +1,130 @@
<?php
class WPML_Package_Admin_Lang_Switcher {
private $package;
private $args;
public function __construct( $package, $args ) {
$this->package = new WPML_Package( $package );
$this->args = $args;
add_action( 'wp_before_admin_bar_render', array( $this, 'admin_language_switcher' ) );
add_action( 'wp_after_admin_bar_render', array( $this, 'add_meta_box' ) );
}
function admin_language_switcher() {
global $wp_admin_bar, $wpdb, $sitepress;
$parent = 'WPML_PACKAGE_LANG';
$package_language = $this->package->get_package_language();
if ( ! $package_language ) {
$package_language = $sitepress->get_default_language();
}
$wpml_pt_meta = new WPML_Package_Translation_Metabox( $this->package, $wpdb, $sitepress, $this->args );
$package_language_name = $wpml_pt_meta->get_package_language_name();
$wp_admin_bar->add_menu(
array(
'parent' => false,
'id' => $parent,
'title' => '<img width="18" height="12" src="' . $sitepress->get_flag_url( $package_language ) . '"> ' . $package_language_name,
)
);
}
function add_meta_box() {
global $wpdb, $sitepress;
$metabox = '<div id="wpml-package-admin-bar-popup" class="wpml-package-popup">';
$wpml_pt_meta = new WPML_Package_Translation_Metabox( $this->package, $wpdb, $sitepress, $this->args );
$metabox .= $wpml_pt_meta->get_metabox();
$metabox .= '</div>';
$metabox .= $this->add_js();
// This is required when a new package is created but it doesn't have any translated content yet.
// https://onthegosystems.myjetbrains.com/youtrack/issue/wpmlst-556
WPML_Simple_Language_Selector::enqueue_scripts();
wp_enqueue_style( 'wpml-package-adminbar-popup', WPML_ST_URL . '/res/css/wpml-package-adminbar-popup.css', array(), ICL_SITEPRESS_VERSION );
echo $metabox;
}
function add_js() {
ob_start();
?>
<script type="text/javascript">
var WPML_Package_Translation = WPML_Package_Translation || {};
WPML_Package_Translation.AdminLanguageSwitcher = function () {
var self = this;
self.init = function () {
self.link = jQuery('#wp-admin-bar-WPML_PACKAGE_LANG').find('.ab-item');
self.link.hover(self.show_lang_box, self.hide_lang_box);
self.popup = jQuery('#wpml-package-admin-bar-popup');
self.popup.css('left', self.link.offset().left);
self.popup.hover(self.show_lang_box, self.hide_on_leave);
jQuery('#icl_package_language').off('wpml-package-language-changed');
jQuery('#icl_package_language').on('wpml-package-language-changed', self.package_language_changed);
};
self.show_lang_box = function() {
self.popup.css('top', self.link.offset().top + self.link.height() - window.scrollTop);
self.popup.show();
};
self.hide_lang_box = function() {
self.popup.hide();
};
self.hide_on_leave = function(e) {
// Make sure we are fully leaving the popup because this is sometimes
// triggered when we open the language select and then move the mouse.
var x = e.clientX;
var y = e.clientY;
var pos = self.popup.offset();
if ( x < pos.left || x > pos.left + self.popup.width() || y < pos.top || y > pos.top + self.popup.height()) {
self.popup.fadeOut(800);
}
};
self.package_language_changed = function( e, lang ) {
self.link.html( self.find_flag(lang) + ' ' + lang);
self.popup.hide();
};
self.find_flag = function( lang ) {
var flag = '';
jQuery('.js-simple-lang-selector option').each( function() {
if (jQuery(this).text().trim() == lang.trim()) {
flag = '<img width="18" height="12" src="' + jQuery(this).data('flag_url') + '">';
}
});
return flag;
};
self.init();
};
jQuery(document).ready(
function () {
WPML_Package_Translation.admin_language_switcher = new WPML_Package_Translation.AdminLanguageSwitcher();
}
);
</script>
<?php
return ob_get_clean();
}
}

View File

@@ -0,0 +1,4 @@
<?php
define( 'LINE', 0 );
define( 'AREA', 1 );
define( 'VISUAL', 2 );

View File

@@ -0,0 +1,42 @@
<?php
class WPML_ST_Package_Factory {
/** @var WPML_WP_Cache_Factory $cache_factory */
private $cache_factory;
public function __construct( WPML_WP_Cache_Factory $cache_factory = null ) {
$this->cache_factory = $cache_factory;
wp_cache_add_non_persistent_groups( __CLASS__ );
}
/**
* @param \stdClass|\WPML_Package|array|int $package_data
*
* @return WPML_Package
*/
public function create( $package_data ) {
$cache_item = $this->get_cache_item( $package_data );
if ( ! $cache_item->exists() ) {
$cache_item->set( new WPML_Package( $package_data ) );
}
return $cache_item->get();
}
/**
* @param array|WPML_Package $package_data
*
* @return WPML_WP_Cache_Item
*/
private function get_cache_item( $package_data ) {
if ( ! $this->cache_factory ) {
$this->cache_factory = new WPML_WP_Cache_Factory();
}
$package_key = md5( json_encode( $package_data ) );
return $this->cache_factory->create_cache_item( __CLASS__, $package_key );
}
}

View File

@@ -0,0 +1,9 @@
<?php
class WPML_Package_Exception extends Exception {
public $type;
public function __construct( $type = '', $message = '', $code = 0 ) {
parent::__construct( $message, $code );
$this->type = $type;
}
}

View File

@@ -0,0 +1,720 @@
<?php
class WPML_Package_Helper {
const PREFIX_BATCH_STRING = 'batch-string-';
private $default_language;
private $last_registered_string_id;
protected $registered_strings;
private $package_cleanup;
private $package_factory;
private $cache_group;
function __construct() {
$this->registered_strings = array();
$this->cache_group = 'string_package';
$this->package_cleanup = new WPML_ST_Package_Cleanup();
$this->package_factory = new WPML_ST_Package_Factory();
}
public function set_package_factory( WPML_ST_Package_Factory $factory ) {
$this->package_factory = $factory;
}
/**
* @param int $package_id
*/
protected function delete_package( $package_id ) {
// delete the strings and the translations
$this->delete_package_strings( $package_id );
$tm = new WPML_Package_TM( $this->package_factory->create( $package_id ) );
$tm->delete_translation_jobs();
$tm->delete_translations();
global $wpdb;
$delete_query = "DELETE FROM {$wpdb->prefix}icl_string_packages WHERE id=%d";
$delete_prepare = $wpdb->prepare( $delete_query, $package_id );
$wpdb->query( $delete_prepare );
}
/**
* @param int $package_id
*
* @return array
*/
protected function get_strings_ids_from_package_id( $package_id ) {
global $wpdb;
$string_ids_query = "SELECT id FROM {$wpdb->prefix}icl_strings WHERE string_package_id=%d";
$string_ids_prepare = $wpdb->prepare( $string_ids_query, $package_id );
$string_ids = $wpdb->get_col( $string_ids_prepare );
return $string_ids;
}
/**
* @param int $package_id
*/
protected function delete_package_strings( $package_id ) {
$strings = $this->get_strings_ids_from_package_id( $package_id );
foreach ( $strings as $string_id ) {
do_action( 'wpml_st_delete_all_string_data', $string_id );
}
}
protected function loaded() {
$this->default_language = icl_get_default_language();
}
/**
* @param string $string_value
* @param string $string_name
* @param array|WPML_Package $package
* @param string $string_title
* @param string $string_type
*/
final function register_string_action( $string_value, $string_name, $package, $string_title, $string_type ) {
$this->register_string_for_translation( $string_value, $string_name, $package, $string_title, $string_type );
return $this->last_registered_string_id;
}
/**
* @param int $default
* @param \stdClass|\WPML_Package|array|int $package
* @param string $string_name
* @param string $string_value
*
* @return bool|int|mixed
*/
function string_id_from_package_filter( $default, $package, $string_name, $string_value ) {
$string_id = $this->get_string_id_from_package( $package, $string_name, $string_value );
if ( ! $string_id ) {
return $default;
}
return $string_id;
}
function string_title_from_id_filter( $default, $string_id ) {
global $wpdb;
$string_title_query = 'SELECT title FROM ' . $wpdb->prefix . 'icl_strings WHERE id=%d';
$string_title_sql = $wpdb->prepare( $string_title_query, array( $string_id ) );
$string_title = $wpdb->get_var( $string_title_sql );
if ( ! $string_title ) {
$string_title = $default;
}
return $string_title;
}
/**
* @param string $string_value
* @param string $string_name
* @param array|WPML_Package $package
* @param string $string_title
* @param string $string_type
*
* @return string
*/
final public function register_string_for_translation( $string_value, $string_name, $package, $string_title, $string_type ) {
$package = $this->package_factory->create( $package );
$package_id = $package->ID;
if ( ! $package_id ) {
// Need to create a new record.
if ( $package->has_kind_and_name() ) {
$package_id = self::create_new_package( $package );
$package = $this->package_factory->create( $package );
}
}
if ( $package_id ) {
$this->maybe_update_package( $package );
$tm = new WPML_Package_TM( $package );
$tm->validate_translations();
$this->init_package_registered_strings( $package_id );
$string_name = $package->sanitize_string_name( $string_name );
$this->set_package_registered_strings( $package_id, $string_type, $string_title, $string_name, $string_value );
$this->last_registered_string_id = $this->register_string_with_wpml( $package, $string_name, $string_title, $string_type, $string_value );
$this->package_cleanup->record_register_string( $package, $this->last_registered_string_id );
}
return $string_value;
}
final function get_string_context_from_package( $package ) {
$package = $this->package_factory->create( $package );
return $package->get_string_context_from_package();
}
/**
* @param WPML_Package $package
* @param string $string_name
* @param string $string_title
* @param string $string_type
* @param string $string_value
*
* @return bool|int|mixed
*/
final function register_string_with_wpml( $package, $string_name, $string_title, $string_type, $string_value ) {
global $wpdb;
$string_id = $this->get_string_id_from_package( $package, $string_name, $string_value );
if ( $string_id ) {
$package_storage = new WPML_ST_Package_Storage( $package->ID, $wpdb );
$did_update = $package_storage->update( $string_title, $string_type, $string_value, $string_id );
if ( $did_update ) {
$this->flush_cache();
$package->flush_cache();
}
}
return $string_id;
}
final function translate_string( $string_value, $string_name, $package ) {
$result = $string_value;
if ( is_string( $string_value ) ) {
$package = $this->package_factory->create( $package );
if ( $package ) {
$sanitized_string_name = $package->sanitize_string_name( $string_name );
$result = $package->translate_string( $string_value, $sanitized_string_name );
}
}
return $result;
}
final function get_translated_strings( $strings, $package ) {
$package = $this->package_factory->create( $package );
return $package->get_translated_strings( $strings );
}
final function set_translated_strings( $translations, $package ) {
$package = $this->package_factory->create( $package );
$package->set_translated_strings( $translations );
}
final function get_translatable_types( $types ) {
global $wpdb;
$package_kinds = $wpdb->get_results( "SELECT kind, kind_slug FROM {$wpdb->prefix}icl_string_packages WHERE id>0" );
// Add any packages found to the $types array
foreach ( $package_kinds as $package_data ) {
$package_kind_slug = $package_data->kind_slug;
$package_kind = $package_data->kind;
$kinds_added = array_keys( $types );
if ( ! in_array( $package_kind_slug, $kinds_added ) ) {
$translatable_type = new stdClass();
$translatable_type->name = $package_kind_slug;
$translatable_type->label = $package_kind;
$translatable_type->prefix = 'package';
$translatable_type->labels = new stdClass();
$translatable_type->labels->singular_name = $package_kind;
$translatable_type->labels->name = $package_kind;
$translatable_type->external_type = 1;
$types[ $package_kind_slug ] = $translatable_type;
}
}
return $types;
}
/**
* @param WPML_Package $item
* @param int|WP_Post|WPML_Package $package
* @param string $type
*
* @return bool|WPML_Package
*/
final public function get_translatable_item( $item, $package, $type = 'package' ) {
if ( $type === 'package' || explode( '_', $type )[0] === 'package' ) {
$tm = new WPML_Package_TM( $item );
return $tm->get_translatable_item( $package );
}
return $item;
}
final function get_post_title( $title, $package_id ) {
$package = $this->package_factory->create( $package_id );
if ( $package ) {
$title = $package->kind . ' - ' . $package->title;
}
return $title;
}
final function get_editor_string_name( $name, $package ) {
$package = $this->package_factory->create( $package );
$package_id = $package->ID;
$title = $this->get_editor_string_element( $name, $package_id, 'title' );
if ( $title && $title != '' ) {
$name = $title;
} elseif (
\WPML\FP\Str::includes( self::PREFIX_BATCH_STRING, $name )
&& ( $string = $this->get_st_string_by_batch_name( $name ) )
) {
$name = $this->empty_if_md5( $string->get_name() )
?: ( $string->get_gettext_context() ?: ( $string->get_context() ?: $name ) );
}
return $name;
}
private function empty_if_md5( $str ) {
return preg_replace( '#^((.+)( - ))?([a-z0-9]{32})$#', '$2', $str );
}
/**
* @param string $batch_string_name
*
* @return WPML_ST_String|null
* @throws \WPML\Auryn\InjectionException
*/
private function get_st_string_by_batch_name( $batch_string_name ) {
$string_id = (int) \WPML\FP\Str::replace( self::PREFIX_BATCH_STRING, '', $batch_string_name );
if ( $string_id ) {
$string_factory = WPML\Container\make( WPML_ST_String_Factory::class );
return $string_factory->find_by_id( $string_id );
}
return null;
}
final function get_editor_string_style( $style, $field_type, $package ) {
$package = $this->package_factory->create( $package );
$package_id = $package->ID;
$element_style = $this->get_editor_string_element( $field_type, $package_id, 'type' );
if ( $element_style ) {
$style = 0;
if ( defined( $element_style ) ) {
$style = constant( $element_style );
}
}
return $style;
}
final public function get_element_id_from_package_filter( $default, $package_id ) {
global $wpdb;
$element_id_query = 'SELECT name FROM ' . $wpdb->prefix . 'icl_string_packages WHERE ID=%d';
$element_id_prepare = $wpdb->prepare( $element_id_query, $package_id );
$element_id = $wpdb->get_var( $element_id_prepare );
if ( ! $element_id ) {
$element_id = $default;
}
return $element_id;
}
final public function get_package_type( $type, $post_id ) {
$package = $this->package_factory->create( $post_id );
if ( $package ) {
return $this->get_package_context( $package );
} else {
return $type;
}
}
final public function get_package_type_prefix( $type, $post_id ) {
if ( $type == 'package' ) {
$package = $this->package_factory->create( $post_id );
if ( $package ) {
$type = $package->get_string_context_from_package();
}
}
return $type;
}
/**
* @param string $language_for_element
* @param \WPML_Package $current_document
*
* @return null|string
*/
final public function get_language_for_element( $language_for_element, $current_document ) {
if ( $this->is_a_package( $current_document ) ) {
global $sitepress;
$language_for_element = $sitepress->get_language_for_element( $current_document->ID, $current_document->get_translation_element_type() );
}
return $language_for_element;
}
final protected function get_package_context( $package ) {
$package = $this->package_factory->create( $package );
return $package->kind_slug . '-' . $package->name;
}
final function delete_packages_ajax() {
if ( ! $this->verify_ajax_call( 'wpml_package_nonce' ) ) {
die( 'verification failed' );
}
$packages_ids = $_POST['packages'];
$this->delete_packages( $packages_ids );
exit;
}
final function delete_package_action( $name, $kind ) {
$package_data['name'] = $name;
$package_data['kind'] = $kind;
$package = $this->package_factory->create( $package_data );
if ( $package && $package->ID && $this->is_a_package( $package ) ) {
$this->delete_package( $package->ID );
$this->flush_cache();
}
}
/** @param int $post_id */
final public function remove_post_packages( $post_id ) {
$packages = $this->get_post_string_packages( array(), $post_id );
foreach ( $packages as $package ) {
$this->delete_package( $package->ID );
}
}
final protected function delete_packages( $packages_ids ) {
$flush_cache = false;
foreach ( $packages_ids as $package_id ) {
$this->delete_package( $package_id );
$flush_cache = true;
}
if ( $flush_cache ) {
$this->flush_cache();
}
}
final function change_package_lang_ajax() {
global $wpdb, $sitepress;
if ( ! $this->verify_ajax_call( 'wpml_package_nonce' ) ) {
die( 'verification failed' );
}
$package_id = $_POST['package_id'];
$package = $this->package_factory->create( $package_id );
$package->set_strings_language( $_POST['package_lang'] );
$package_job = new WPML_Package_TM( $package );
$package_job->set_language_details( $_POST['package_lang'] );
$args = filter_var_array( json_decode( base64_decode( $_POST['args'] ) ), FILTER_SANITIZE_STRING );
$package_metabox = new WPML_Package_Translation_Metabox( $package, $wpdb, $sitepress, $args );
$response = array(
'metabox' => $package_metabox->get_metabox_status(),
'lang' => $package_metabox->get_package_language_name(),
);
wp_send_json( $response );
}
/**
* @param string $string_name
* @param int $package_id
* @param string $column
*
* @return string
*/
private function get_editor_string_element( $string_name, $package_id, $column ) {
global $wpdb;
$element_query = 'SELECT ' . $column . "
FROM {$wpdb->prefix}icl_strings
WHERE string_package_id=%d AND name=%s";
$element_prepared = $wpdb->prepare( $element_query, array( $package_id, $string_name ) );
$element = $wpdb->get_var( $element_prepared );
return $element;
}
private function flush_cache() {
// delete the cache key we use
wp_cache_delete( 'get_all_packages', $this->cache_group );
}
/**
* @param WPML_Package $package
*
* @return int
*/
final public static function create_new_package( WPML_Package $package ) {
$package_id = $package->create_new_package_record();
$tm = new WPML_Package_TM( $package );
$tm->update_package_translations( true );
return (int) $package_id;
}
/**
* @param int $package_id
*/
private function init_package_registered_strings( $package_id ) {
if ( ! isset( $this->registered_strings[ $package_id ] ) ) {
$this->registered_strings[ $package_id ] = array( 'strings' => array() );
}
}
/**
* @param int $package_id
* @param string $string_type
* @param string $string_title
* @param string $string_name
* @param string $string_value
*/
private function set_package_registered_strings( $package_id, $string_type, $string_title, $string_name, $string_value ) {
$this->registered_strings[ $package_id ]['strings'][ $string_name ] = array(
'title' => $string_title,
'kind' => $string_type,
'value' => $string_value,
);
}
/**
* @param \stdClass|\WPML_Package|array|int $package
* @param string $string_name
* @param string $string_value
*
* @return bool|int|mixed
*/
private function get_string_id_from_package( $package, $string_name, $string_value ) {
if ( ! $package instanceof WPML_Package ) {
$package = $this->package_factory->create( $package );
}
return $package->get_string_id_from_package( $string_name, $string_value );
}
final function get_external_id_from_package( $package ) {
return 'external_' . $package['kind_slug'] . '_' . $package['ID'];
}
final function get_string_context( $package ) {
return sanitize_title_with_dashes( $package['kind_slug'] . '-' . $package['name'] );
}
final function get_package_id( $package, $from_cache = true ) {
global $wpdb;
static $cache = array();
if ( $this->is_a_package( $package ) ) {
$package = object_to_array( $package );
}
$key = $this->get_string_context( $package );
if ( ! $from_cache || ! array_key_exists( $key, $cache ) ) {
$package_id_query = "SELECT ID FROM {$wpdb->prefix}icl_string_packages WHERE kind_slug = %s AND name = %s";
$package_id_prepare = $wpdb->prepare( $package_id_query, array( $package['kind_slug'], $package['name'] ) );
$package_id = $wpdb->get_var( $package_id_prepare );
if ( ! $package_id ) {
return false;
}
$cache[ $key ] = $package_id;
}
return $cache[ $key ];
}
final public function get_all_packages() {
global $wpdb;
$cache_key = 'get_all_packages';
$cache_found = false;
$all_packages = wp_cache_get( $cache_key, $this->cache_group, false, $cache_found );
if ( ! $cache_found ) {
$all_packages = array();
$all_packages_data_query = "SELECT * FROM {$wpdb->prefix}icl_string_packages";
$all_packages_data = $wpdb->get_results( $all_packages_data_query );
foreach ( $all_packages_data as $package_data ) {
$package = $this->package_factory->create( $package_data );
$all_packages[ $package->ID ] = $package;
}
if ( $all_packages ) {
wp_cache_set( $cache_key, $all_packages, $this->cache_group );
}
}
return $all_packages;
}
final protected function is_a_package( $element ) {
return is_a( $element, 'WPML_Package' );
}
protected function verify_ajax_call( $ajax_action ) {
return isset( $_POST['wpnonce'] ) && wp_verify_nonce( $_POST['wpnonce'], $ajax_action );
}
protected function sanitize_string_name( $string_name ) {
$string_name = preg_replace( '/[ \[\]]+/', '-', $string_name );
return $string_name;
}
public function refresh_packages() {
// TODO: deprecated.
// This is required to support Layouts 1.0
do_action( 'WPML_register_string_packages', 'layout', array() );
// TODO: END deprecated.
do_action( 'wpml_register_string_packages' );
}
/**
* @param WPML_Package $package
*/
private function maybe_update_package( $package ) {
if ( $package->new_title ) {
$package->title = $package->new_title;
$package->update_package_record();
}
}
public function change_language_of_strings( $strings, $lang ) {
global $wpdb;
$all_ok = true;
$strings_in = implode( ',', $strings );
$string_packages_query = "SELECT DISTINCT string_package_id FROM {$wpdb->prefix}icl_strings WHERE id IN ($strings_in)";
$package_ids = $wpdb->get_col( $string_packages_query );
foreach ( $package_ids as $package_id ) {
if ( $package_id ) {
$package = $this->package_factory->create( $package_id );
if ( ! $package->are_all_strings_included( $strings ) ) {
$all_ok = false;
break;
}
}
}
if ( $all_ok ) {
$this->set_packages_language( $package_ids, $lang );
}
$response = array();
$response['success'] = $all_ok;
if ( ! $all_ok ) {
$response['error'] = __( 'Some of the strings selected belong to packages. You can only change the language of these strings if all strings in the packages are selected.', 'wpml-string-translation' );
}
return $response;
}
public function change_language_of_strings_in_domain( $domain, $langs, $to_lang ) {
global $wpdb;
if ( ! empty( $langs ) ) {
foreach ( $langs as &$lang ) {
$lang = "'" . $lang . "'";
}
$langs = implode( ',', $langs );
$string_packages_query = "SELECT DISTINCT string_package_id FROM {$wpdb->prefix}icl_strings WHERE context='%s' AND language IN ($langs)";
$string_packages_query = $wpdb->prepare( $string_packages_query, $domain );
$package_ids = $wpdb->get_col( $string_packages_query );
$this->set_packages_language( $package_ids, $to_lang );
}
}
private function set_packages_language( $package_ids, $lang ) {
foreach ( $package_ids as $package_id ) {
if ( $package_id ) {
$package = $this->package_factory->create( $package_id );
$package->set_strings_language( $lang );
$package_job = new WPML_Package_TM( $package );
$package_job->set_language_details( $lang );
}
}
}
/**
* @param null|array $packages
* @param int $post_id
*
* @return WPML_Package[]
*/
public function get_post_string_packages( $packages, $post_id ) {
global $wpdb;
$query = $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}icl_string_packages WHERE post_id = %d", $post_id );
$package_ids = $wpdb->get_col( $query );
$packages = $packages ? $packages : array();
foreach ( $package_ids as $package_id ) {
$packages[ $package_id ] = $this->package_factory->create( $package_id );
}
return $packages;
}
/**
* @param int $string_id
* @param string $language
* @param string|null $value
* @param int|bool $status
* @param int|null $translator_id
* @param int|null $translation_service
* @param int|null $batch_id
*/
public function add_string_translation_action( $string_id, $language, $value = null, $status = false, $translator_id = null, $translation_service = null, $batch_id = null ) {
icl_add_string_translation( $string_id, $language, $value, $status, $translator_id, $translation_service, $batch_id );
}
/**
* @param mixed $package
* @param int $package_id
*
* @return WPML_Package
*/
public function get_string_package( $package, $package_id ) {
return $this->package_factory->create( $package_id );
}
public function start_string_package_registration_action( $package ) {
$this->package_cleanup->record_existing_strings( $this->package_factory->create( $package ) );
}
public function delete_unused_package_strings_action( $package ) {
$this->package_cleanup->delete_unused_strings( $this->package_factory->create( $package ) );
}
}

View File

@@ -0,0 +1,168 @@
<?php
class WPML_Package_Translation_HTML_Packages {
var $load_priority = 101;
public function __construct() {
add_action( 'WPML_PT_HTML', array( $this, 'loaded' ), $this->load_priority );
}
public function loaded() {
$this->set_admin_hooks();
}
private function set_admin_hooks() {
if ( is_admin() ) {
add_action( 'wpml_pt_package_table_columns', array( $this, 'render_package_table_columns' ), 10, 1 );
add_action( 'wpml_pt_package_table_options', array( $this, 'package_translation_menu_options' ), 10, 1 );
add_action( 'wpml_pt_package_table_body', array( $this, 'package_translation_menu_body' ), 10, 1 );
add_action( 'wpml_pt_package_status', array( $this, 'render_string_package_status' ), 10, 3 );
}
}
public static function package_translation_menu() {
$packages = apply_filters( 'wpml_pt_all_packages', array() );
$package_kinds = array();
if ( $packages ) {
foreach ( $packages as $package ) {
$kind_slugs = array_keys( $package_kinds );
if ( ! in_array( $package->kind_slug, $kind_slugs ) ) {
$package_kinds[ $package->kind_slug ] = $package->kind;
}
}
}
$table_title = __( 'Package Management', 'wpml-string-translation' );
$package_kind_label = __( 'Display packages for this kind:', 'wpml-string-translation' );
$package_kind_options[- 1] = __( 'Display packages for this kind:', 'wpml-string-translation' );
foreach ( $package_kinds as $kind_slug => $kind ) {
$package_kind_options[ $kind_slug ] = $kind;
}
?>
<h2><?php echo $table_title; ?></h2>
<p>
<label for="package_kind_filter"><?php echo $package_kind_label; ?></label>
<select id="package_kind_filter">
<?php do_action( 'wpml_pt_package_table_options', $package_kind_options ); ?>
</select>
</p>
<table id="icl_package_translations" class="widefat" cellspacing="0">
<thead>
<?php do_action( 'wpml_pt_package_table_columns', 'thead' ); ?>
</thead>
<tfoot>
<?php do_action( 'wpml_pt_package_table_columns', 'tfoot' ); ?>
</tfoot>
<tbody>
<?php do_action( 'wpml_pt_package_table_body', $packages ); ?>
</tbody>
</table>
<br/>
<input id="delete_packages" type="button" class="button-primary" value="<?php echo __( 'Delete Selected Packages', 'wpml-string-translation' ); ?>" disabled="disabled"/>
&nbsp;
<span class="spinner"></span>
<span style="display:none" class="js-delete-confirm-message"><?php echo __( "Are you sure you want to delete these packages?\nTheir strings and translations will be deleted too.", 'wpml-string-translation' ); ?></span>
<?php
wp_nonce_field( 'wpml_package_nonce', 'wpml_package_nonce' );
}
public function package_translation_menu_options( $package_kind_options ) {
foreach ( $package_kind_options as $option_value => $option_label ) {
?>
<option value="<?php echo esc_attr( $option_value ); ?>"><?php echo $option_label; ?></option>
<?php
}
}
/**
* @param array<\WPML_Package> $packages
*/
public function package_translation_menu_body( $packages ) {
if ( ! $packages ) {
$this->package_translation_menu_no_packages();
} else {
$this->package_translation_menu_items( $packages );
}
}
public function render_package_table_columns( $position ) {
?>
<tr>
<th scope="col" class="manage-column column-cb check-column">
<label for="select_all_package_<?php echo $position; ?>" style="display: none;">
<?php _e( 'Select All', 'wpml-string-translation' ); ?>
</label>
<input id="select_all_package_<?php echo $position; ?>" class="js_package_all_cb" type="checkbox"/>
</th>
<th scope="col"><?php echo __( 'Kind', 'wpml-string-translation' ); ?></th>
<th scope="col"><?php echo __( 'Name', 'wpml-string-translation' ); ?></th>
<th scope="col"><?php echo __( 'Info', 'wpml-string-translation' ); ?></th>
</tr>
<?php
}
public function render_string_package_status( $string_count, $translation_in_progress, $default_package_language ) {
$package_statuses[] = sprintf( __( 'Contains %s strings', 'wpml-string-translation' ), $string_count );
if ( $translation_in_progress ) {
$package_statuses[] = '(' . __( 'Translation is in progress', 'wpml-string-translation' ) . ')';
}
$package_statuses[] = __( 'Default package language', 'wpml-string-translation' ) . ': ' . $default_package_language;
echo implode( ' - ', $package_statuses );
}
private function package_translation_menu_no_packages() {
?>
<tr>
<td colspan="6" align="center">
<strong><?php echo __( 'No packages found', 'wpml-string-translation' ); ?></strong>
</td>
</tr>
<?php
}
/**
* @param array<\WPML_Package> $packages
*/
private function package_translation_menu_items( $packages ) {
global $wpdb, $sitepress;
foreach ( $packages as $package ) {
$package_language_code = $package->get_package_language();
$package_language = $sitepress->get_display_language_name( $package_language_code );
$tm = new WPML_Package_TM( $package );
$translation_in_progress = $tm->is_translation_in_progress();
$package_id = $package->ID;
$string_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}icl_strings WHERE string_package_id=%d", $package_id ) );
$disabled = disabled( $translation_in_progress, true, false );
?>
<tr id="row_<?php echo $package_id; ?>" class="js_package js_package_<?php echo esc_attr( $package->kind_slug ); ?>">
<td>
<input id="package_<?php echo $package_id; ?>" class="js_package_row_cb" type="checkbox" value="<?php echo $package_id; ?>" <?php echo $disabled; ?>/>
</td>
<td class="js-package-kind">
<?php echo $package->kind; ?>
</td>
<td>
<label for="package_<?php echo $package_id; ?>"><?php echo esc_html( $package->title ); ?></label>
</td>
<td>
<?php
do_action( 'wpml_pt_package_status', $string_count, $translation_in_progress, $package_language );
?>
</td>
</tr>
<?php
}
}
}

View File

@@ -0,0 +1,444 @@
<?php
class WPML_Package_Translation_Metabox {
private $args;
private $active_languages;
private $container_attributes_html;
private $dashboard_link;
private $strings_link;
private $default_language;
private $main_container_attributes;
private $show_description;
private $show_link;
private $show_status;
private $show_title;
private $status_container_attributes;
private $status_container_attributes_html;
private $status_container_tag;
private $status_element_tag;
private $title_tag;
public $metabox_data;
/**
* @var SitePress
*/
private $sitepress;
private $translation_statuses;
/**
* @var wpdb
*/
private $wpdb;
/**
* @var \WPML_Package
*/
private $package;
/**
* @var string
*/
private $package_language;
/**
* WPML_Package_Translation_Metabox constructor.
*
* @param stdClass|WPML_Package|array|int $package
* @param \wpdb $wpdb
* @param \SitePress $sitepress
* @param array<string,mixed> $args
*/
public function __construct( $package, $wpdb, $sitepress, $args = array() ) {
$this->wpdb = $wpdb;
$this->sitepress = $sitepress;
$this->package = new WPML_Package( $package );
$this->args = $args;
if ( $this->got_package() ) {
$this->dashboard_link = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php&sm=dashboard&type=' . $this->package->kind_slug . '&lang=' . $this->package->get_package_language() );
$this->strings_link = admin_url( 'admin.php?page=' . WPML_ST_FOLDER . '/menu/string-translation.php&context=' . $this->package->get_string_context_from_package() );
}
$this->parse_arguments( $args );
$this->init_metabox_data();
}
private function init_metabox_data() {
$this->metabox_data = array();
$this->active_languages = $this->sitepress->get_active_languages();
$this->default_language = $this->sitepress->get_default_language();
$this->package_language = $this->package->get_package_language();
$this->package_language = $this->package_language ? $this->package_language : $this->default_language;
$this->metabox_data['title'] = __( 'WPML Translation', 'wpml-string-translation' );
if ( $this->is_package_language_active() ) {
$this->metabox_data['translate_title'] = __( 'Send to translation', 'wpml-string-translation' );
} else {
$this->metabox_data['translate_title'] = __( 'Translate strings', 'wpml-string-translation' );
}
if ( $this->got_package() ) {
$this->metabox_data['statuses_title'] = __( 'Translation status:', 'wpml-string-translation' );
$this->init_translation_statuses();
$this->metabox_data['package_language_title'] = sprintf( __( 'Language of this %1$s is %2$s', 'wpml-string-translation' ), $this->package->kind, $this->get_lang_selector() );
} else {
$this->metabox_data['package_language_title'] = '';
$this->metabox_data['statuses_title'] = __( 'There is nothing to translate.', 'wpml-string-translation' );
}
}
private function get_lang_selector() {
$disable = $this->check_if_language_change_is_ok() ? '' : 'disabled="disabled" ';
ob_start();
$languages = $this->active_languages;
if ( ! $this->is_package_language_active() ) {
$languages = array_merge( array( $this->sitepress->get_language_details( $this->package_language ) ), $languages );
} else {
$languages = $this->active_languages;
}
$selector = new WPML_Simple_Language_Selector( $this->sitepress );
$selector->render(
array(
'id' => 'icl_package_language',
'name' => 'icl_package_language',
'show_please_select' => false,
'languages' => $languages,
'selected' => $this->package_language,
'disabled' => ! $this->check_if_language_change_is_ok(),
'echo' => true,
)
);
?>
<span class="spinner"></span>
<?php
return ob_get_clean();
}
public function get_package_language_name() {
if ( $this->is_package_language_active() ) {
return $this->active_languages[ $this->package_language ]['display_name'];
} else {
$all_languages = $this->sitepress->get_languages();
return $all_languages[ $this->package_language ]['display_name'];
}
}
private function get_lang_switcher_js() {
ob_start();
?>
<script type="text/javascript">
var WPML_Package_Translation = WPML_Package_Translation || {};
WPML_Package_Translation.MetaBox = function () {
var self = this;
self.init = function () {
jQuery('#icl_package_language').off('change');
jQuery('#icl_package_language').on('change', self.switch_lang);
};
self.switch_lang = function() {
self.disable_controls(true);
var data = {
action: 'wpml_change_package_lang',
wpnonce: jQuery('#wpml_package_nonce').val(),
package_id: jQuery('#wpml_package_id').val(),
args: jQuery('#wpml_package_args').val(),
package_lang: jQuery(this).val()
};
jQuery.ajax(
{
url: ajaxurl,
type: 'post',
data: data,
dataType: 'json',
success: function (response) {
var position = jQuery('#wpml_package_status').prev();
jQuery('#wpml_package_status').remove();
jQuery(response.metabox).insertAfter(position);
jQuery('#icl_package_language').trigger('wpml-package-language-changed', [response.lang]);
self.disable_controls(false);
}
});
};
self.disable_controls = function(state) {
var lang_switcher = jQuery('#icl_package_language');
if (state) {
lang_switcher.next('.spinner').show();
lang_switcher.prop('disabled', true);
lang_switcher.closest('div').find('a').prop('disabled', true);
} else {
lang_switcher.next('.spinner').hide();
lang_switcher.prop('disabled', false);
lang_switcher.closest('div').find('a').prop('disabled', false);
}
};
self.init();
};
jQuery(document).ready(
function () {
WPML_Package_Translation.meta_box = new WPML_Package_Translation.MetaBox();
}
);
</script>
<?php
return ob_get_clean();
}
function get_metabox() {
$result = '';
$result .= '<div ' . $this->container_attributes_html . '>';
if ( $this->show_title ) {
if ( $this->title_tag ) {
$result .= $this->get_tag( $this->title_tag );
}
$result .= $this->metabox_data['title'];
if ( $this->title_tag ) {
$result .= $this->get_tag( $this->title_tag, 'closed' );
}
}
if ( $this->show_description ) {
$result .= '<div>' . $this->metabox_data['package_language_title'] . '</div>';
}
if ( $this->show_status ) {
$result .= '<p>' . $this->metabox_data['statuses_title'] . '</p>';
}
$result .= $this->get_metabox_status();
$result .= '</div>';
$result .= wp_nonce_field( 'wpml_package_nonce', 'wpml_package_nonce', true, false );
$result .= '<input type="hidden" id="wpml_package_id" value="' . $this->package->ID . '" />';
$result .= '<input type="hidden" id="wpml_package_args" value="' . base64_encode( wp_json_encode( $this->args ) ) . '" />';
if ( ! defined( 'DOING_AJAX' ) ) {
$result .= $this->get_lang_switcher_js();
}
return $result;
}
public function get_metabox_status() {
$result = '';
if ( $this->got_package() ) {
$result .= '<div id="wpml_package_status">';
if ( $this->show_status && $this->metabox_data['statuses'] ) {
if ( $this->status_container_tag ) {
$result .= $this->get_tag( $this->status_container_tag . ' ' . $this->status_container_attributes_html );
}
foreach ( $this->metabox_data['statuses'] as $code => $status ) {
$result .= $this->get_tag( $this->status_element_tag );
$result .= '<img width="18" height="12" src="' . $this->sitepress->get_flag_url( $code ) . '"> ' . $status['name'] . ' : ' . $status['status'];
$result .= $this->get_tag( $this->status_element_tag, 'closed' );
}
if ( $this->status_container_tag ) {
$result .= $this->get_tag( $this->status_container_tag, 'closed' );
}
}
if ( $this->show_link ) {
if ( $this->is_package_language_active() ) {
$result .= '<p><a style="float:right" class="button-secondary" href="' . $this->dashboard_link . '" target="_blank">' . $this->metabox_data['translate_title'] . '</a></p>';
} else {
$result .= '<p><a style="float:right" class="button-secondary" href="' . $this->strings_link . '" target="_blank">' . $this->metabox_data['translate_title'] . '</a></p>';
}
}
$result .= '<br /><br /></div>';
}
return $result;
}
private function check_if_language_change_is_ok() {
$ok = true;
foreach ( $this->translation_statuses as $status ) {
if ( isset( $status->status_code ) && $status->status_code != 0 ) {
$ok = false;
break;
}
}
if ( $ok ) {
$translations = $this->package->get_translated_strings( array() );
foreach ( $translations as $string ) {
foreach ( $string as $lang => $data ) {
if ( $data['status'] == ICL_STRING_TRANSLATION_COMPLETE ) {
$ok = false;
break;
}
}
if ( ! $ok ) {
break;
}
}
}
return $ok;
}
/**
* @param array<string,mixed> $attributes
*
* @return string
*/
private function attributes_to_string( $attributes ) {
$result = '';
foreach ( $attributes as $key => $value ) {
if ( $result ) {
$result .= ' ';
}
$result .= esc_html( $key ) . '="' . esc_attr( $value ) . '"';
}
return $result;
}
function get_post_translations() {
$element_type = $this->package->get_package_element_type();
$trid = $this->sitepress->get_element_trid( $this->package->ID, $element_type );
return $this->sitepress->get_element_translations( $trid, $element_type );
}
private function get_tag( $tag, $closed = false ) {
$result = '<';
if ( $closed ) {
$result .= '/';
}
$result .= $tag . '>';
return $result;
}
/**
* @param array<string,string|array<string,mixed> > $args
*/
private function parse_arguments( $args ) {
$default_args = array(
'show_title' => true,
'show_description' => true,
'show_status' => true,
'show_link' => true,
'title_tag' => 'h2',
'status_container_tag' => 'ul',
'status_element_tag' => 'li',
'main_container_attributes' => array(),
'status_container_attributes' => array( 'style' => 'padding-left: 10px' ),
);
$args = array_merge( $default_args, $args );
$this->show_title = $args['show_title'];
$this->show_description = $args['show_description'];
$this->show_status = $args['show_status'];
$this->show_link = $args['show_link'];
$this->title_tag = $args['title_tag'];
$this->status_container_tag = $args['status_container_tag'];
$this->status_element_tag = $args['status_element_tag'];
$this->main_container_attributes = $args['main_container_attributes'];
$this->status_container_attributes = $args['status_container_attributes'];
$this->container_attributes_html = $this->attributes_to_string( $this->main_container_attributes );
$this->status_container_attributes_html = $this->attributes_to_string( $this->status_container_attributes );
}
/**
* @return bool
*/
private function got_package() {
return $this->package && $this->package->ID;
}
private function get_translation_statuses() {
$post_translations = $this->get_post_translations();
$status = array();
foreach ( $post_translations as $language => $translation ) {
$res_query = "SELECT status as status_code, needs_update FROM {$this->wpdb->prefix}icl_translation_status WHERE translation_id=%d";
$res_args = array( $translation->translation_id );
$res_prepare = $this->wpdb->prepare( $res_query, $res_args );
/** @var \stdClass $res */
$res = $this->wpdb->get_row( $res_prepare );
if ( $res ) {
$res->status = $res->status_code;
switch ( $res->status ) {
case ICL_TM_WAITING_FOR_TRANSLATOR:
$res->status = __( 'Waiting for translator', 'wpml-string-translation' );
break;
case ICL_TM_IN_PROGRESS:
$res->status = __( 'In progress', 'wpml-string-translation' );
break;
case ICL_TM_NEEDS_UPDATE:
$res->status = '';
break;
case ICL_TM_COMPLETE:
$res->status = __( 'Complete', 'wpml-string-translation' );
break;
default:
$res->status = __( 'Not translated', 'wpml-string-translation' );
break;
}
if ( $res->needs_update ) {
if ( $res->status ) {
$res->status .= ' - ';
}
$res->status .= __( 'Needs update', 'wpml-string-translation' );
}
$status[ $language ] = $res;
}
}
return $status;
}
private function init_translation_statuses() {
$this->metabox_data['statuses'] = array();
$this->translation_statuses = $this->get_translation_statuses();
foreach ( $this->active_languages as $language_data ) {
if ( $language_data['code'] != $this->package_language ) {
$display_name = $language_data['display_name'];
$this->metabox_data['statuses'][ $language_data['code'] ] = array(
'name' => $display_name,
'status' => $this->get_status_value( $language_data ),
);
}
}
}
private function get_status_value( $language_data ) {
if ( isset( $this->translation_statuses[ $language_data['code'] ] ) ) {
$status_value = $this->translation_statuses[ $language_data['code'] ]->status;
} else {
$tm = new WPML_Package_TM( $this->package );
if ( $tm->is_in_basket( $language_data['code'] ) ) {
$status_value = __( 'In translation basket', 'wpml-string-translation' );
} else {
$status_value = __( 'Not translated', 'wpml-string-translation' );
}
}
return $status_value;
}
private function is_package_language_active() {
return in_array( $this->package_language, array_keys( $this->active_languages ) );
}
}

View File

@@ -0,0 +1,183 @@
<?php
class WPML_Package_Translation_Schema {
const OPTION_NAME = 'wpml-package-translation-db-updates-run';
const REQUIRED_VERSION = '0.0.2';
private static $table_name;
static function run_update() {
$updates_run = get_option( self::OPTION_NAME, array() );
if ( defined( 'WPML_PT_VERSION_DEV' ) ) {
delete_option( 'wpml-package-translation-string-packages-table-updated' );
if ( ( $key = array_search( WPML_PT_VERSION_DEV, $updates_run ) ) !== false ) {
unset( $updates_run[ $key ] );
}
}
if ( ! in_array( self::REQUIRED_VERSION, $updates_run ) ) {
// We need to make sure we build everything for 0.0.2 because users may
// only be updating the string translation plugin and may not do an
// activation.
self::build_icl_string_packages_table();
self::build_icl_string_packages_columns_if_required();
self::fix_icl_string_packages_ID_column();
self::build_icl_strings_columns_if_required();
$updates_run[] = self::REQUIRED_VERSION;
update_option( self::OPTION_NAME, $updates_run );
}
}
private static function current_table_has_column( $column ) {
global $wpdb;
$cols = $wpdb->get_results( 'SHOW COLUMNS FROM `' . self::$table_name . '`' );
$found = false;
foreach ( $cols as $col ) {
if ( $col->Field == $column ) {
$found = true;
break;
}
}
return $found;
}
private static function add_string_package_id_to_icl_strings() {
global $wpdb;
$sql = 'ALTER TABLE `' . self::$table_name . '`
ADD `string_package_id` BIGINT unsigned NULL AFTER value,
ADD INDEX (`string_package_id`)';
return $wpdb->query( $sql );
}
private static function add_type_to_icl_strings() {
global $wpdb;
$sql = 'ALTER TABLE `' . self::$table_name . "` ADD `type` VARCHAR(40) NOT NULL DEFAULT 'LINE' AFTER string_package_id";
return $wpdb->query( $sql );
}
private static function add_title_to_icl_strings() {
global $wpdb;
$sql = 'ALTER TABLE `' . self::$table_name . '` ADD `title` VARCHAR(160) NULL AFTER type';
return $wpdb->query( $sql );
}
public static function build_icl_strings_columns_if_required() {
global $wpdb;
if ( ! get_option( 'wpml-package-translation-string-table-updated' ) ) {
self::$table_name = $wpdb->prefix . 'icl_strings';
if ( $wpdb->get_var( "SHOW TABLES LIKE '" . self::$table_name . "'" ) == self::$table_name ) {
if ( ! self::current_table_has_column( 'string_package_id' ) ) {
self::add_string_package_id_to_icl_strings();
}
if ( ! self::current_table_has_column( 'type' ) ) {
self::add_type_to_icl_strings();
}
if ( ! self::current_table_has_column( 'title' ) ) {
self::add_title_to_icl_strings();
}
update_option( 'wpml-package-translation-string-table-updated', true, 'no' );
}
}
}
private static function build_icl_string_packages_columns_if_required() {
global $wpdb;
if ( get_option( 'wpml-package-translation-string-packages-table-updated' ) != '0.0.2' ) {
self::$table_name = $wpdb->prefix . 'icl_string_packages';
if ( $wpdb->get_var( "SHOW TABLES LIKE '" . self::$table_name . "'" ) == self::$table_name ) {
if ( ! self::current_table_has_column( 'kind_slug' ) ) {
self::add_kind_slug_to_icl_string_packages();
}
self::update_kind_slug();
if ( ! self::current_table_has_column( 'view_link' ) ) {
self::add_view_link_to_icl_string_packages();
}
update_option( 'wpml-package-translation-string-packages-table-updated', '0.0.2', 'no' );
}
}
}
private static function add_kind_slug_to_icl_string_packages() {
global $wpdb;
$sql = 'ALTER TABLE `' . self::$table_name . "` ADD `kind_slug` varchar(160) DEFAULT '' NOT NULL AFTER `ID`";
$result = $wpdb->query( $sql );
return $result;
}
private static function add_view_link_to_icl_string_packages() {
global $wpdb;
$sql = 'ALTER TABLE `' . self::$table_name . '` ADD `view_link` TEXT NOT NULL AFTER `edit_link`';
return $wpdb->query( $sql );
}
private static function fix_icl_string_packages_ID_column() {
global $wpdb;
self::$table_name = $wpdb->prefix . 'icl_string_packages';
if ( self::current_table_has_column( 'id' ) ) {
$sql = 'ALTER TABLE `' . self::$table_name . '` CHANGE id ID BIGINT UNSIGNED NOT NULL auto_increment;';
$wpdb->query( $sql );
}
}
private static function build_icl_string_packages_table() {
global $wpdb;
$charset_collate = SitePress_Setup::get_charset_collate();
self::$table_name = $wpdb->prefix . 'icl_string_packages';
$sql = '
CREATE TABLE IF NOT EXISTS `' . self::$table_name . '` (
`ID` bigint(20) unsigned NOT NULL auto_increment,
`kind_slug` varchar(160) NOT NULL,
`kind` varchar(160) NOT NULL,
`name` varchar(160) NOT NULL,
`title` varchar(160) NOT NULL,
`edit_link` TEXT NOT NULL,
`view_link` TEXT NOT NULL,
`post_id` INTEGER DEFAULT NULL,
`word_count` VARCHAR(2000) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ' . $charset_collate . '';
if ( $wpdb->query( $sql ) === false ) {
throw new Exception( $wpdb->last_error );
}
}
private static function update_kind_slug() {
global $wpdb;
$sql = 'SELECT kind FROM ' . self::$table_name . " WHERE IFNULL(kind_slug,'')='' GROUP BY kind";
$kinds = $wpdb->get_col( $sql );
$result = ( count( $kinds ) == 0 );
foreach ( $kinds as $kind ) {
$kind_slug = sanitize_title_with_dashes( $kind );
$result = $wpdb->update( self::$table_name, array( 'kind_slug' => $kind_slug ), array( 'kind' => $kind ) );
if ( ! $result ) {
break;
}
}
return $result;
}
}

View File

@@ -0,0 +1,29 @@
<?php
class WPML_Package_ST {
public function get_string_element( $string_id, $column = false ) {
global $wpdb;
$package_query = "SELECT * FROM {$wpdb->prefix}icl_strings WHERE id=%d";
$package_prepare = $wpdb->prepare( $package_query, array( $string_id ) );
$result = $wpdb->get_row( $package_prepare );
if ( $result && $column && isset( $result[ $column ] ) ) {
$result = $result[ $column ];
}
return $result;
}
public function get_string_title( $title, $string_details ) {
$string_title = $this->get_string_element( $string_details['string_id'], 'title' );
if ( $string_title ) {
return $string_title;
} else {
return $title;
}
}
}

View File

@@ -0,0 +1,215 @@
<?php
class WPML_Package_TM_Jobs {
/**
* @var WPML_Package
*/
protected $package;
protected function __construct( $package ) {
$this->package = $package;
}
final public function validate_translations( $create_if_missing = true ) {
$trid = $this->get_trid( $create_if_missing );
return $trid != false;
}
final protected function get_trid( $create_if_missing = true ) {
global $sitepress;
$package = $this->package;
$post_trid = $sitepress->get_element_trid( $package->ID, $package->get_translation_element_type() );
if ( ! $post_trid && $create_if_missing ) {
$this->set_language_details();
$post_trid = $sitepress->get_element_trid( $package->ID, $package->get_translation_element_type() );
}
return $post_trid;
}
final public function set_language_details( $language_code = null ) {
global $sitepress;
$package = $this->package;
$post_id = $package->ID;
$post = $this->get_translatable_item( $post_id );
$post_id = $post->ID;
$element_type = $package->get_translation_element_type();
if ( ! $language_code ) {
$language_code = icl_get_default_language();
}
$sitepress->set_element_language_details( $post_id, $element_type, false, $language_code, null, false );
}
final public function get_translatable_item( $package ) {
// for TranslationManagement::send_jobs
if ( ! is_a( $package, 'WPML_Package' ) ) {
$package = new WPML_Package( $package );
}
return $package;
}
final public function delete_translation_jobs() {
global $wpdb;
$post_translations = $this->get_post_translations();
foreach ( $post_translations as $lang => $translation ) {
$rid_query = "SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d";
$rid_prepare = $wpdb->prepare( $rid_query, array( $translation->translation_id ) );
$rid = $wpdb->get_var( $rid_prepare );
if ( $rid ) {
$job_id_query = "SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d";
$job_id_prepare = $wpdb->prepare( $job_id_query, array( $rid ) );
$job_id = $wpdb->get_var( $job_id_prepare );
if ( $job_id ) {
$wpdb->delete( $wpdb->prefix . 'icl_translate_job', array( 'job_id' => $job_id ) );
$wpdb->delete( $wpdb->prefix . 'icl_translate', array( 'job_id' => $job_id ) );
}
}
}
}
final public function delete_translations() {
global $sitepress;
$sitepress->delete_element_translation(
$this->get_trid(),
$this->package->get_translation_element_type()
);
}
final public function get_post_translations() {
global $sitepress;
$package = $this->package;
$translation_element_type = $package->get_translation_element_type();
$trid = $this->get_trid();
return $sitepress->get_element_translations( $trid, $translation_element_type );
}
final protected function update_translation_job_needs_update( $job_id ) {
global $wpdb;
$update_data = array( 'translated' => 0 );
$update_where = array( 'job_id' => $job_id );
$wpdb->update( "{$wpdb->prefix}icl_translate_job", $update_data, $update_where );
}
final function update_translation_job( $rid, $post ) {
$updated = $this->update_translation_job_fields( $rid, $post );
$deleted = $this->delete_translation_job_fields( $rid, $post );
return ( $updated || $deleted );
}
private function delete_translation_job_fields( $rid, $post ) {
$deleted = false;
$job_id = $this->get_translation_job_id( $rid );
$translation_fields = $this->get_translations_job_fields( $job_id );
foreach ( $translation_fields as $field_type => $el ) {
// delete fields that are no longer present
if ( $el->field_translate && ! isset( $post->string_data[ $field_type ] ) ) {
$this->delete_translation_field( $el->tid );
if ( ! $deleted ) {
$deleted = true;
}
}
}
return $deleted;
}
/**
* @param int $rid
* @param WPML_Package $post
*
* @return bool
*/
private function update_translation_job_fields( $rid, $post ) {
$updated_any = false;
$job_id = $this->get_translation_job_id( $rid );
$translation_fields = $this->get_translations_job_fields( $job_id );
if ( isset( $post->string_data ) && is_array( $post->string_data ) ) {
foreach ( $post->string_data as $field_type => $field_value ) {
$updated = $this->insert_update_translation_job_field( $field_value, $translation_fields, $field_type, $job_id );
if ( ! $updated_any && $updated ) {
$updated_any = true;
}
}
}
return $updated_any;
}
protected function get_translation_job_id( $rid ) {
global $wpdb;
$job_id_query = "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid";
$job_id_prepare = $wpdb->prepare( $job_id_query, $rid );
$job_id = $wpdb->get_var( $job_id_prepare );
return $job_id;
}
private function get_translations_job_fields( $job_id ) {
global $wpdb;
$translation_fields_query = "SELECT field_type, field_data, tid, field_translate
FROM {$wpdb->prefix}icl_translate
WHERE job_id=%d";
$translation_fields_query = $wpdb->prepare( $translation_fields_query, $job_id );
$translation_fields = $wpdb->get_results( $translation_fields_query, OBJECT_K );
return $translation_fields;
}
private function delete_translation_field( $tid ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'icl_translate', array( 'tid' => $tid ), array( '%d' ) );
}
private function insert_update_translation_job_field( $field_value, $translation_fields, $field_type, $job_id ) {
$updated = false;
$field_data = base64_encode( $field_value );
if ( ! isset( $translation_fields[ $field_type ] ) ) {
$this->insert_translation_field( $job_id, $field_type, $field_data );
$updated = true;
} elseif ( $translation_fields[ $field_type ]->field_data != $field_data ) {
$this->update_translation_field( $field_data, $translation_fields, $field_type );
$updated = true;
}
return $updated;
}
private function update_translation_field( $field_data, $translation_fields, $field_type ) {
global $wpdb;
$wpdb->update(
$wpdb->prefix . 'icl_translate',
array(
'field_data' => $field_data,
'field_finished' => 0,
),
array( 'tid' => $translation_fields[ $field_type ]->tid )
);
}
private function insert_translation_field( $job_id, $field_type, $field_data ) {
global $wpdb;
$data = array(
'job_id' => $job_id,
'content_id' => 0,
'field_type' => $field_type,
'field_format' => 'base64',
'field_translate' => 1,
'field_data' => $field_data,
'field_data_translated' => 0,
'field_finished' => 0,
);
$wpdb->insert( $wpdb->prefix . 'icl_translate', $data );
}
}

View File

@@ -0,0 +1,380 @@
<?php
class WPML_Package_TM extends WPML_Package_TM_Jobs {
public function __construct( $package ) {
parent::__construct( $package );
}
public function get_translation_statuses() {
global $sitepress;
$package = $this->package;
$post_trid = $this->get_trid();
$items = array();
if ( $post_trid ) {
$translation_element_type = $package->get_translation_element_type();
$post_translations = $sitepress->get_element_translations( $post_trid, $translation_element_type );
foreach ( $post_translations as $lang => $translation ) {
$translation->trid = $post_trid;
$item[] = $this->set_translation_status( $package, $translation, $lang );
}
} else {
$items[] = $package;
}
return $items;
}
/**
* @param \WPML_Package $package
* @param \stdClass $translation
* @param string $lang
*
* @return \WPML_Package
*/
private function set_translation_status( $package, $translation, $lang ) {
global $wpdb;
if ( ! $package->trid ) {
$package->trid = $translation->trid;
}
$res_query = "SELECT status, needs_update, md5 FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d";
$res_args = array( $translation->translation_id );
$res_prepare = $wpdb->prepare( $res_query, $res_args );
$res = $wpdb->get_row( $res_prepare );
$_suffix = str_replace( '-', '_', $lang );
$status = ICL_TM_NOT_TRANSLATED;
if ( $res ) {
$status = $res->status;
$index = 'needs_update_' . $_suffix;
$package->$index = $res->needs_update;
}
$index = 'status_' . $_suffix;
$package->$index = apply_filters( 'wpml_translation_status', $status, $translation->trid, $lang, 'package' );
return $package;
}
public function is_translation_in_progress() {
global $wpdb;
$post_translations = $this->get_post_translations();
foreach ( $post_translations as $lang => $translation ) {
$res_query = "SELECT status, needs_update, md5
FROM {$wpdb->prefix}icl_translation_status
WHERE translation_id=%d";
$res_prepared = $wpdb->prepare( $res_query, array( $translation->translation_id ) );
$res = $wpdb->get_row( $res_prepared );
if ( $res && $res->status == ICL_TM_IN_PROGRESS ) {
return true;
}
}
return false;
}
/**
* Update translations
*
* @param bool $is_new_package
* @param bool $needs_update - when deleting single field we do not need to change the translation status of the form
*
* @return bool
*/
public function update_package_translations( $is_new_package, $needs_update = true ) {
global $iclTranslationManagement;
$updated = false;
$package = $this->package;
$trid = $this->get_trid();
if ( $is_new_package ) {
$this->set_language_details();
return true;
}
$item_md5_translations = $this->get_item_md5_translations( $trid );
if ( $item_md5_translations ) {
$md5 = $iclTranslationManagement->post_md5( $this->package );
if ( $md5 != $item_md5_translations[0]->md5 ) { // all translations need update
$translation_package = $iclTranslationManagement->create_translation_package( $this->package );
foreach ( $item_md5_translations as $translation ) {
$translation_id = $translation->translation_id;
$previous_state = $this->get_translation_state( $translation );
$data = array();
if ( ! empty( $previous_state ) ) {
$data['previous_state'] = serialize( $previous_state );
}
$data = array(
'translation_id' => $translation_id,
'translation_package' => serialize( $translation_package ),
'md5' => $md5,
);
if ( $needs_update ) {
$data['needs_update'] = 1;
}
$update_result = $iclTranslationManagement->update_translation_status( $data );
$rid = $update_result[0];
$this->update_translation_job( $rid, $this->package );
// change job status only when needs update
if ( $needs_update ) {
$job_id = $this->get_translation_job_id( $rid );
if ( $job_id ) {
$this->update_translation_job_needs_update( $job_id );
$updated = true;
}
}
}
}
}
return $updated;
}
private function get_item_md5_translations( $trid ) {
global $wpdb;
$item_md5_translations_query = "
SELECT t.translation_id, s.md5
FROM {$wpdb->prefix}icl_translations t
NATURAL JOIN {$wpdb->prefix}icl_translation_status s
WHERE t.trid=%d
AND t.source_language_code IS NOT NULL";
$item_md5_translations_prepare = $wpdb->prepare( $item_md5_translations_query, $trid );
$item_md5_translations = $wpdb->get_results( $item_md5_translations_prepare );
return $item_md5_translations;
}
private function get_translation_state( $translation ) {
global $wpdb;
$previous_state_query = "
SELECT status, translator_id, needs_update, md5, translation_service, translation_package, timestamp, links_fixed
FROM {$wpdb->prefix}icl_translation_status
WHERE translation_id = %d
";
$previous_state_prepare = $wpdb->prepare( $previous_state_query, $translation->translation_id );
$previous_state = $wpdb->get_row( $previous_state_prepare, ARRAY_A );
return $previous_state;
}
public function add_package_to_basket( $translation_action, $source_language, $target_language ) {
if ( ! $this->validate_basket_package_item( $translation_action, $source_language ) ) {
return false;
}
if ( $this->is_duplicate_or_do_nothing( $translation_action ) ) {
$this->duplicate_or_do_nothing( $translation_action, $target_language );
} elseif ( $translation_action == 1 ) {
$this->send_package_to_basket( $source_language, $target_language );
}
return true;
}
private function duplicate_package( $package_id ) {
// TODO: [WPML 3.3] duplication to be done
// $this->make_duplicate( $package_id, $language_code );
}
/**
* @param int $translation_action
* @param string $source_language
*
* @return bool
*/
private function validate_basket_package_item( $translation_action, $source_language ) {
ICL_AdminNotifier::remove_message( 'the_basket_items_notification' );
$package = $this->package;
$package_id = $package->ID;
$basket = TranslationProxy_Basket::get_basket();
$package_is_valid = true;
$basket_source_language = TranslationProxy_Basket::get_source_language();
if ( $basket && $basket_source_language ) {
if ( $source_language != $basket_source_language ) {
TranslationProxy_Basket::add_message(
array(
'type' => 'update',
'text' => __(
'You cannot add packages in this language to the basket since it already contains posts, packages or strings of another source language!
Either submit the current basket and then add the post or delete the posts of differing language in the current basket',
'wpml-string-translation'
),
)
);
TranslationProxy_Basket::update_basket();
$package_is_valid = false;
}
}
$select_one_lang_message = __( 'Please select at least one language to translate into.', 'wpml-string-translation' );
if ( ! $translation_action ) {
TranslationProxy_Basket::add_message(
array(
'type' => 'error',
'text' => $select_one_lang_message,
)
);
$package_is_valid = false;
} else {
TranslationProxy_Basket::remove_message( $select_one_lang_message );
}
if ( ! $package_id ) {
TranslationProxy_Basket::add_message(
array(
'type' => 'error',
'text' => __( 'Please select at least one document to translate.', 'wpml-string-translation' ),
)
);
$package_is_valid = false;
return $package_is_valid;
}
return $package_is_valid;
}
private function is_duplicate_or_do_nothing( $translation_action ) {
return $translation_action == 0 || $translation_action == 2;
}
private function duplicate_or_do_nothing( $translation_action, $target_language ) {
$basket = TranslationProxy_Basket::get_basket();
$package = $this->package;
$package_id = $package->ID;
// iterate posts ids, check if they are in wp_options
// if they are set to translate for this particular language
// end then remove it
// check if we have this post in wp_options
// end remove
if ( isset( $basket['package'][ $package_id ]['to_langs'][ $target_language ] ) ) {
unset( $basket['package'][ $package_id ]['to_langs'][ $target_language ] );
TranslationProxy_Basket::update_basket( $basket );
}
// if user want to duplicate this post, lets do this
if ( $translation_action == 2 ) {
$this->duplicate_package( $package_id );
}
}
/**
* @param string $source_language
* @param string $target_language
*
* @throws WPML_Package_Exception
*/
public function send_package_to_basket( $source_language, $target_language ) {
global $sitepress, $iclTranslationManagement;
$package = $this->package;
$package_id = $package->ID;
$basket = TranslationProxy_Basket::get_basket();
$language_name = $sitepress->get_display_language_name( $target_language );
$send_to_basket = true;
$package_helper = new WPML_Package_Helper();
$post = $package_helper->get_translatable_item( null, $package_id );
$post_title = esc_html( $post->title );
$element_type = $package->get_element_type_prefix() . '_' . $post->kind_slug;
$trid = $sitepress->get_element_trid( $package_id, $element_type );
$job_id = $iclTranslationManagement->get_translation_job_id( $trid, $target_language );
if ( $job_id ) {
$job_details = $iclTranslationManagement->get_translation_job( $job_id );
$send_to_basket = $this->validate_package_status( $job_details, $post_title, $language_name );
}
if ( $send_to_basket ) {
$basket['package'][ $package_id ]['from_lang'] = $source_language;
$basket['package'][ $package_id ]['to_langs'][ $target_language ] = 1;
// set basket language if not already set
if ( ! isset( $basket['source_language'] ) ) {
$basket['source_language'] = $source_language;
}
}
TranslationProxy_Basket::update_basket( $basket );
}
/**
* @param \stdClass $job_details
* @param string $post_title
* @param string $language_name
*
* @return bool
*/
private function validate_package_status( $job_details, $post_title, $language_name ) {
$send_to_basket = true;
$message_args = array();
if ( ! $job_details->needs_update ) {
if ( $job_details->status == ICL_TM_IN_PROGRESS ) {
$message_args = array(
'type' => 'update',
'text' => sprintf(
__(
'Post "%1$s" will be ignored for %2$s, because translation is already in progress.',
'wpml-string-translation'
),
$post_title,
$language_name
),
);
$send_to_basket = false;
} elseif ( $job_details->status == ICL_TM_WAITING_FOR_TRANSLATOR ) {
$message_args = array(
'type' => 'update',
'text' => sprintf(
__(
'Post "%1$s" will be ignored for %2$s, because translation is already waiting for translator.',
'wpml-string-translation'
),
$post_title,
$language_name
),
);
$send_to_basket = false;
}
}
if ( ! $send_to_basket ) {
TranslationProxy_Basket::add_message( $message_args );
TranslationProxy_Basket::update_basket();
}
return $send_to_basket;
}
public function is_in_basket( $target_lang ) {
if ( class_exists( 'TranslationProxy_Basket' ) ) {
$basket = TranslationProxy_Basket::get_basket();
return isset( $basket['package'][ $this->package->ID ]['to_langs'][ $target_lang ] );
} else {
return false;
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
class WPML_Package_Translation_UI {
var $load_priority = 101;
private $menu_root = '';
const MENU_SLUG = 'wpml-package-management';
public function __construct() {
add_action( 'wpml_loaded', array( $this, 'loaded' ), $this->load_priority );
}
public function loaded() {
if ( $this->passed_dependencies() ) {
$this->set_admin_hooks();
do_action( 'WPML_PT_HTML' );
}
}
private function passed_dependencies() {
return defined( 'ICL_SITEPRESS_VERSION' )
&& defined( 'WPML_ST_VERSION' )
&& defined( 'WPML_TM_VERSION' );
}
private function set_admin_hooks() {
if ( is_admin() ) {
add_action( 'wpml_admin_menu_configure', array( $this, 'menu' ) );
add_action( 'wpml_admin_menu_root_configured', array( $this, 'main_menu_configured' ), 10, 2 );
add_action( 'admin_register_scripts', array( $this, 'admin_register_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
}
}
public function main_menu_configured( $menu_id, $root_slug ) {
if ( 'WPML' === $menu_id ) {
$this->menu_root = $root_slug;
}
}
/**
* @param string $menu_id
*/
public function menu( $menu_id ) {
if ( 'WPML' !== $menu_id || ! defined( 'ICL_PLUGIN_PATH' ) ) {
return;
}
global $sitepress;
if ( ! isset( $sitepress ) || ( method_exists( $sitepress, 'get_setting' ) && ! $sitepress->get_setting( 'setup_complete' ) ) ) {
return;
}
global $sitepress_settings;
if ( ! isset( $sitepress_settings['existing_content_language_verified'] ) || ! $sitepress_settings['existing_content_language_verified'] ) {
return;
}
if ( current_user_can( 'wpml_manage_string_translation' ) ) {
$menu = array();
$menu['order'] = 1300;
$menu['page_title'] = __( 'Packages', 'wpml-string-translation' );
$menu['menu_title'] = __( 'Packages', 'wpml-string-translation' );
$menu['capability'] = 'wpml_manage_string_translation';
$menu['menu_slug'] = self::MENU_SLUG;
$menu['function'] = array(
'WPML_Package_Translation_HTML_Packages',
'package_translation_menu',
);
do_action( 'wpml_admin_menu_register_item', $menu );
$this->admin_register_scripts();
}
}
function admin_register_scripts() {
wp_register_script( 'wpml-package-trans-man-script', WPML_PACKAGE_TRANSLATION_URL . '/resources/js/wpml_package_management.js', array( 'jquery' ) );
}
function admin_enqueue_scripts( $hook ) {
if ( get_plugin_page_hookname( self::MENU_SLUG, $this->menu_root ) === $hook ) {
wp_enqueue_script( 'wpml-package-trans-man-script' );
}
}
}

View File

@@ -0,0 +1,806 @@
<?php
class WPML_Package_Translation extends WPML_Package_Helper {
var $load_priority = 100;
var $package_translation_active;
var $admin_lang_switcher = null;
function __construct() {
parent::__construct();
add_action( 'wpml_loaded', array( $this, 'loaded' ), $this->load_priority, 1 );
}
function loaded( SitePress $sitepress = null ) {
parent::loaded();
if ( null === $sitepress ) {
global $sitepress;
}
$is_setup_complete = (bool) $sitepress->get_setting( 'setup_complete', false );
if ( $is_setup_complete ) {
$this->run_db_update();
}
if ( $this->passed_dependencies() && $is_setup_complete ) {
$this->add_admin_hooks();
$this->add_global_hooks();
if ( is_admin() ) {
if ( $this->is_refresh_required() ) {
add_action( 'init', array( $this, 'refresh_packages' ), 999, 0 );
$this->set_refresh_not_required();
}
}
}
}
private function add_admin_hooks() {
if ( is_admin() || $this->is_doing_xmlrpc() || wpml_is_rest_request() ) {
add_action( 'wp_ajax_wpml_delete_packages', array( $this, 'delete_packages_ajax' ) );
add_action( 'wp_ajax_wpml_change_package_lang', array( $this, 'change_package_lang_ajax' ) );
/* Core hooks */
add_filter( 'wpml_pt_all_packages', array( $this, 'get_all_packages' ) );
/* Translation hooks for other plugins to use */
add_filter( 'wpml_tm_element_type', array( $this, 'get_element_type' ), 10, 2 );
add_filter( 'wpml_tm_dashboard_title_locations', array( $this, 'add_title_db_location' ), 10, 2 );
add_filter( 'wpml_string_title_from_id', array( $this, 'string_title_from_id_filter' ), 10, 2 );
// TODO: deprecated, use the 'wpml_register_string' action
add_filter( 'WPML_register_string', array( $this, 'register_string_for_translation' ), 10, 5 );
add_action( 'wpml_add_string_translation', array( $this, 'add_string_translation_action' ), 10, 7 );
// TODO: These 3 hooks are deprecated. They are needed for Layouts 1.0. Consider removing them after Layouts 1.2 is released
add_filter( 'WPML_get_translated_strings', array( $this, 'get_translated_strings' ), 10, 2 );
add_action( 'WPML_set_translated_strings', array( $this, 'set_translated_strings' ), 10, 2 );
add_action( 'WPML_show_package_language_ui', array( $this, 'show_language_selector' ), 10, 2 );
add_filter( 'wpml_get_translated_strings', array( $this, 'get_translated_strings' ), 10, 2 );
add_action( 'wpml_set_translated_strings', array( $this, 'set_translated_strings' ), 10, 2 );
add_action( 'wpml_show_package_language_ui', array( $this, 'show_language_selector' ), 10, 2 );
add_action( 'wpml_show_package_language_admin_bar', array( $this, 'show_admin_bar_language_selector' ), 10, 2 );
/* WPML hooks */
add_filter( 'wpml_get_translatable_types', array( $this, 'get_translatable_types' ), 10, 1 );
add_filter( 'wpml_get_translatable_item', array( $this, 'get_translatable_item' ), 10, 3 );
add_filter( 'wpml_external_item_url', array( $this, 'get_package_edit_url' ), 10, 2 );
add_filter( 'wpml_external_item_link', array( $this, 'get_package_edit_link' ), 10, 3 );
add_filter( 'wpml_get_external_item_title', array( $this, 'get_package_title' ), 10, 3 );
add_filter( 'wpml_element_id_from_package', array( $this, 'get_element_id_from_package_filter' ), 10, 2 );
add_filter( 'wpml_get_package_type', array( $this, 'get_package_type' ), 10, 2 );
add_filter( 'wpml_get_package_type_prefix', array( $this, 'get_package_type_prefix' ), 10, 2 );
add_filter( 'wpml_language_for_element', array( $this, 'get_language_for_element' ), 10, 2 );
add_filter( 'wpml_st_get_string_package', array( $this, 'get_string_package' ), 10, 2 );
add_action( 'wpml_save_external', [ $this, 'save_package_translations' ], 10, 3 );
/* Translation queue hooks */
add_filter( 'wpml_tm_external_translation_job_title', array( $this, 'get_post_title' ), 10, 2 );
add_filter( 'wpml_tm_add_to_basket', array( $this, 'add_to_basket' ), 10, 2 );
add_filter( 'wpml_tm_translation_jobs_basket', array( $this, 'update_translation_jobs_basket' ), 10, 3 );
add_filter( 'wpml_tm_basket_items_types', array( $this, 'basket_items_types' ), 10, 2 );
/*
TM Hooks */
// This is called by \TranslationManagement::send_all_jobs - The hook is dynamically built.
add_filter( 'wpml_tm_dashboard_sql', array( $this, 'tm_dashboard_sql_filter' ), 10, 1 );
/* Translation editor hooks */
add_filter( 'wpml_tm_editor_string_name', array( $this, 'get_editor_string_name' ), 10, 2 );
add_filter( 'wpml_tm_editor_string_style', array( $this, 'get_editor_string_style' ), 10, 3 );
/*
API Hooks */
// @deprecated @since 3.2 Use 'wpml_delete_package'
add_action( 'wpml_delete_package_action', array( $this, 'delete_package_action' ), 10, 2 );
add_action( 'deleted_post', array( $this, 'remove_post_packages' ) );
}
}
private function is_doing_xmlrpc() {
return ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST );
}
private function add_global_hooks() {
// TODO: deprecated, use the 'wpml_translate_string' filter
add_filter( 'WPML_translate_string', array( $this, 'translate_string' ), 10, 3 );
add_filter( 'wpml_translate_string', array( $this, 'translate_string' ), 10, 3 );
add_action( 'wpml_register_string', array( $this, 'register_string_action' ), 10, 5 );
add_action( 'wpml_start_string_package_registration', array( $this, 'start_string_package_registration_action' ), 10, 1 );
add_action( 'wpml_delete_unused_package_strings', array( $this, 'delete_unused_package_strings_action' ), 10, 1 );
add_action( 'wpml_delete_package', array( $this, 'delete_package_action' ), 10, 2 );
add_filter( 'wpml_st_get_post_string_packages', array( $this, 'get_post_string_packages' ), 10, 2 );
add_filter( 'wpml_string_id_from_package', array( $this, 'string_id_from_package_filter' ), 10, 4 );
/* API Hooks */
add_filter( 'wpml_is_external', array( $this, 'is_external' ), 10, 2 );
}
private function run_db_update() {
if ( is_admin() ) {
WPML_Package_Translation_Schema::run_update();
}
}
/**
* @return bool
*/
private function passed_dependencies() {
return defined( 'ICL_SITEPRESS_VERSION' )
&& defined( 'WPML_ST_VERSION' )
&& defined( 'WPML_TM_VERSION' );
}
public function add_title_db_location( $locations ) {
global $wpdb;
$locations[ $wpdb->prefix . 'icl_string_packages' ] = array(
'id_column' => 'ID',
'title_column' => 'title',
'element_type_prefix' => 'package',
'date_column' => false,
);
return $locations;
}
function get_package_edit_url( $url, $post_id ) {
$package = new WPML_Package( $post_id );
if ( ! $package ) {
return false;
} //not ours
if ( isset( $package->edit_link ) ) {
$url = $package->edit_link;
}
return $url;
}
public function get_package_title( $title, $kind, $id ) {
$package = new WPML_Package( $id );
if ( ! $package ) {
return $title;
} //not ours
if ( isset( $package->title ) ) {
$title = $package->title;
}
return $title;
}
function get_package_view_link( $link, $post_id, $hide_if_missing_link = false ) {
$package = new WPML_Package( $post_id );
if ( ! $package ) {
return $link;
} //not ours
return $this->build_package_link( $package->view_link, $package->title, $hide_if_missing_link );
}
function get_package_edit_link( $link, $post_id, $hide_if_missing_link = false ) {
$package = new WPML_Package( $post_id );
if ( ! $package ) {
return $link;
} //not ours
return $this->build_package_link( $package->edit_link, esc_html( $package->title ), $hide_if_missing_link );
}
private function build_package_link( $url, $title, $hide_if_missing_link = false ) {
$link = '<a href="' . $url . '">' . $title . '</a>';
if ( false === $hide_if_missing_link ) {
if ( ! $url ) {
$link = $title;
}
} elseif ( ! $url ) {
$link = '';
}
return $link;
}
/**
* @param stdClass|WPML_Package|array|int $package
* @param array<string,mixed> $args
*/
function show_language_selector( $package, $args = array() ) {
global $wpdb, $sitepress;
$wpml_pt_meta = new WPML_Package_Translation_Metabox( $package, $wpdb, $sitepress, $args );
echo $wpml_pt_meta->get_metabox();
}
/**
* @param stdClass|WPML_Package|array|int $package
* @param array<string,mixed> $args
*/
function show_admin_bar_language_selector( $package, $args = array() ) {
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-admin-lang-switcher.class.php';
$this->admin_lang_switcher = new WPML_Package_Admin_Lang_Switcher( $package, $args );
}
function cleanup_translation_jobs_basket_packages( $translation_jobs_basket ) {
if ( empty( $translation_jobs_basket['packages'] ) ) {
return;
}
foreach ( $translation_jobs_basket['packages'] as $id => $data ) {
if ( ! new WPML_Package( $id ) ) {
TranslationProxy_Basket::delete_item_from_basket( $id );
}
}
}
public function update_translation_jobs_basket( $translation_jobs_cart, $translation_jobs_basket, $item_type ) {
if ( $item_type == 'package' ) {
if ( ! isset( $translation_jobs_basket[ $item_type ] ) ) {
return false;
}
$packages = $translation_jobs_basket[ $item_type ];
if ( empty( $packages ) ) {
return false;
}
$this->cleanup_translation_jobs_basket_packages( $translation_jobs_basket );
global $sitepress;
$packages_ids = array_keys( $packages );
foreach ( $packages_ids as $package_id ) {
$package = new WPML_Package( $package_id );
if ( $package ) {
if ( ! isset( $package->ID ) || ! $package->ID ) {
TranslationProxy_Basket::delete_item_from_basket( $package_id, 'package' );
continue;
}
$package_source_language = $packages[ $package_id ]['from_lang'];
$package_target_languages = $packages[ $package_id ]['to_langs'];
$language_names = $this->languages_to_csv( $package_target_languages );
$final_post = array();
$final_post['post_title'] = $package->title;
$final_post['post_notes'] = get_post_meta( $package->ID, '_icl_translator_note', true );
$final_post['post_type'] = $package->kind;
if ( isset( $package->post_status ) ) {
$final_post['post_status'] = $package->post_status;
}
if ( isset( $package->post_date ) ) {
$final_post['post_date'] = $package->post_date;
}
$final_post['from_lang'] = $package_source_language;
$final_post['from_lang_string'] = ucfirst( $sitepress->get_display_language_name( $package_source_language, $sitepress->get_admin_language() ) );
$final_post['to_langs'] = $package_target_languages;
$final_post['to_langs_string'] = implode( ', ', $language_names );
$translation_jobs_cart[ $package_id ] = $final_post;
}
}
}
return $translation_jobs_cart;
}
public function basket_items_types( $item_types ) {
$item_types['package'] = 'custom';
return $item_types;
}
/**
* @param array<string,string> $package_target_languages
*
* @return array<string>
*/
private function languages_to_csv( $package_target_languages ) {
global $sitepress;
$language_names = array();
foreach ( $package_target_languages as $language_code => $value ) {
$language_names[] = ucfirst( $sitepress->get_display_language_name( $language_code, $sitepress->get_admin_language() ) );
}
return $language_names;
}
function is_external( $result, $type ) {
return $result || is_a( $type, 'WPML_Package' ) || $type == 'package';
}
public function get_element_type( $type, $element ) {
if ( $this->is_a_package( $element ) ) {
$type = 'package';
}
return $type;
}
/**
* @param array<string,string> $attributes
*
* @return string
*/
public function attributes_to_string( $attributes ) {
$result = '';
foreach ( $attributes as $key => $value ) {
if ( $result ) {
$result .= ' ';
}
$result .= esc_html( $key ) . '="' . esc_attr( $value ) . '"';
}
return $result;
}
/**
* @param string $kind_slug
*
* @return string
*/
public static function get_package_element_type( $kind_slug ) {
if ( is_object( $kind_slug ) ) {
$kind_slug = $kind_slug->kind_slug;
}
if ( is_array( $kind_slug ) ) {
$kind_slug = $kind_slug['kind_slug'];
}
return 'package_' . $kind_slug;
}
/**
* @param array<string,string> $package
*
* @return bool
*/
public function package_has_kind( $package ) {
return isset( $package['kind'] ) && $package['kind'];
}
/**
* @param array<string,string> $package
*
* @return bool
*/
public function package_has_name( $package ) {
return isset( $package['name'] ) && $package['name'];
}
/**
* @param array<string,string> $package
*
* @return bool
*/
public function package_has_title( $package ) {
return isset( $package['title'] ) && $package['title'];
}
/**
* @param array<string,string> $package
*
* @return bool
*/
public function package_has_kind_and_name( $package ) {
return $this->package_has_kind( $package ) && $this->package_has_name( $package );
}
/**
* @param string $string_name
*
* @return mixed
*/
public function sanitize_string_with_underscores( $string_name ) {
return preg_replace( '/[ \[\]]+/', '_', $string_name );
}
function new_external_item( $type, $package_item, $get_string_data = false ) {
// create a new external item for the Translation Dashboard or for translation jobs
$package_id = $package_item['ID'];
$item = new stdClass();
$item->external_type = true;
$item->type = $type;
$item->ID = $package_id;
$item->post_type = $type;
$item->post_id = 'external_' . $item->post_type . '_' . $package_item['ID'];
$item->post_date = '';
$item->post_status = __( 'Active', 'wpml-string-translation' );
$item->post_title = $package_item['title'];
$item->is_translation = false;
if ( $get_string_data ) {
$item->string_data = $this->_get_package_strings( $package_item );
}
return $item;
}
function get_package_from_external_id( $post_id ) {
global $wpdb;
$packages = $wpdb->get_col( "SELECT ID FROM {$wpdb->prefix}icl_string_packages WHERE ID>0" );
foreach ( $packages as $package_id ) {
$package = new WPML_Package( $package_id );
$test = $this->get_external_id_from_package( $package );
if ( is_string( $post_id ) && $post_id == $test ) {
return $package;
}
}
return false; // not a package type
}
function _get_package_strings( $package_item ) {
global $wpdb;
$strings = array();
$package_item_id = $package_item['ID'];
$results = $wpdb->get_results( $wpdb->prepare( "SELECT name, value FROM {$wpdb->prefix}icl_strings WHERE string_package_id=%d", $package_item_id ) );
foreach ( $results as $result ) {
$string_name = (bool) $package_item_id === true && strpos(
$result->name,
(string) $package_item_id
) === 0
? substr( $result->name, strlen( $package_item_id ) + 1 ) : $result->name;
$strings[ $string_name ] = $result->value;
}
// Add/update any registered strings
if ( isset( $this->registered_strings[ $package_item_id ]['strings'] ) ) {
foreach ( $this->registered_strings[ $package_item_id ]['strings'] as $id => $string_data ) {
$strings[ $id ] = $string_data['value'];
}
}
return $strings;
}
function get_link( $item, $package_item, $anchor, $hide_empty ) {
if ( $item == '' ) {
$package_item = $this->get_package_from_external_id( $package_item );
if ( ! $package_item ) {
return '';
}
$has_link = isset( $package_item->edit_link ) && $package_item->edit_link;
if ( false === $anchor ) {
if ( $has_link ) {
$anchor = '<a href="' . $package_item->edit_link . '">' . $package_item['title'] . '</a>';
} elseif ( ! $hide_empty ) {
$anchor = $package_item['title'];
}
} else {
if ( $has_link ) {
$anchor = '<a href="' . $package_item['edit_link'] . '">' . $anchor . '</a>';
}
}
$item = $anchor;
}
return $item;
}
/**
* Update translations
*
* @param int $package_id
* @param bool $is_new - set to true for newly created form (first save without fields)
* @param bool $needs_update - when deleting single field we do not need to change the translation status of the form
*
* @internal param array $item - package information
*/
function update_package_translations( $package_id, $is_new, $needs_update = true ) {
global $sitepress, $wpdb, $iclTranslationManagement;
$item = $this->get_package_details( $package_id );
$post_id = $this->get_external_id_from_package( new WPML_Package( $package_id ) );
$post = $this->get_translatable_item( null, $post_id );
if ( ! $post ) {
return;
}
$default_lang = $sitepress->get_default_language();
$icl_el_type = $this->get_package_element_type( $item );
$trid = $sitepress->get_element_trid( $item['ID'], $icl_el_type );
if ( $is_new ) {
$sitepress->set_element_language_details( $post->ID, $icl_el_type, false, $default_lang, null, false );
// for new package nothing more to do
return;
}
$sql = "
SELECT t.translation_id, s.md5
FROM {$wpdb->prefix}icl_translations t
NATURAL JOIN {$wpdb->prefix}icl_translation_status s
WHERE t.trid=%d
AND t.source_language_code IS NOT NULL
";
$element_translations = $wpdb->get_results( $wpdb->prepare( $sql, $trid ) );
if ( ! empty( $element_translations ) ) {
$md5 = $iclTranslationManagement->post_md5( $post );
if ( $md5 != $element_translations[0]->md5 ) { // all translations need update
$translation_package = $iclTranslationManagement->create_translation_package( $post );
foreach ( $element_translations as $trans ) {
$_prevstate = $wpdb->get_row(
$wpdb->prepare(
"
SELECT status, translator_id, needs_update, md5, translation_service, translation_package, timestamp, links_fixed
FROM {$wpdb->prefix}icl_translation_status
WHERE translation_id = %d
",
$trans->translation_id
),
ARRAY_A
);
if ( ! empty( $_prevstate ) ) {
$data['_prevstate'] = serialize( $_prevstate );
}
$data = array(
'translation_id' => $trans->translation_id,
'translation_package' => serialize( $translation_package ),
'md5' => $md5,
);
// update only when something changed (we do not need to change status when deleting a field)
if ( $needs_update ) {
$data['needs_update'] = 1;
}
$update_result = $iclTranslationManagement->update_translation_status( $data );
$rid = $update_result[0];
$this->update_icl_translate( $rid, $post );
// change job status only when needs update
if ( $needs_update ) {
$job_id = $wpdb->get_var( $wpdb->prepare( "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid", $rid ) );
if ( $job_id ) {
$wpdb->update( "{$wpdb->prefix}icl_translate_job", array( 'translated' => 0 ), array( 'job_id' => $job_id ), array( '%d' ), array( '%d' ) );
}
}
}
}
}
}
/**
* Functions to update translations when packages are modified in admin
*
* @param int $rid
* @param \stdClass $post
*/
function update_icl_translate( $rid, $post ) {
global $wpdb;
$job_id = $wpdb->get_var( $wpdb->prepare( "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid", $rid ) );
$elements = $wpdb->get_results(
$wpdb->prepare(
"SELECT field_type, field_data, tid, field_translate FROM {$wpdb->prefix}icl_translate
WHERE job_id=%d",
$job_id
),
OBJECT_K
);
foreach ( $post->string_data as $field_type => $field_value ) {
$field_data = base64_encode( $field_value );
if ( ! isset( $elements[ $field_type ] ) ) {
// insert new field
$data = array(
'job_id' => $job_id,
'content_id' => 0,
'field_type' => $field_type,
'field_format' => 'base64',
'field_translate' => 1,
'field_data' => $field_data,
'field_data_translated' => 0,
'field_finished' => 0,
);
$wpdb->insert( $wpdb->prefix . 'icl_translate', $data );
} elseif ( $elements[ $field_type ]->field_data != $field_data ) {
// update field value
$wpdb->update(
$wpdb->prefix . 'icl_translate',
array(
'field_data' => $field_data,
'field_finished' => 0,
),
array( 'tid' => $elements[ $field_type ]->tid )
);
}
}
foreach ( $elements as $field_type => $el ) {
// delete fields that are no longer present
if ( $el->field_translate && ! isset( $post->string_data[ $field_type ] ) ) {
$wpdb->delete( $wpdb->prefix . 'icl_translate', array( 'tid' => $el->tid ), array( '%d' ) );
}
}
}
private function get_package_details( $package_id ) {
global $wpdb;
static $cache = array();
if ( ! isset( $cache[ $package_id ] ) ) {
$item = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_string_packages WHERE ID=%d", $package_id ), ARRAY_A );
$cache[ $package_id ] = $item;
}
return $cache[ $package_id ];
}
function get_string_context_title( $context, $string_details ) {
global $wpdb;
static $cache = array();
if ( ! isset( $cache[ $context ] ) ) {
$package_id = $wpdb->get_var( $wpdb->prepare( "SELECT string_package_id FROM {$wpdb->prefix}icl_strings WHERE id=%d", $string_details['string_id'] ) );
if ( $package_id ) {
$package_details = $this->get_package_details( $package_id );
if ( $package_details ) {
$cache[ $context ] = $package_details->kind . ' - ' . $package_details->title;
} else {
$cache[ $context ] = $context;
}
} else {
$cache[ $context ] = $context;
}
}
return $cache[ $context ];
}
function get_string_title( $title, $string_details ) {
global $wpdb;
$string_title = $wpdb->get_var( $wpdb->prepare( "SELECT title FROM {$wpdb->prefix}icl_strings WHERE id=%d", $string_details['string_id'] ) );
if ( $string_title ) {
return $string_title;
} else {
return $title;
}
}
function _get_post_translations( $package ) {
global $sitepress;
if ( is_object( $package ) ) {
$package = get_object_vars( $package );
}
$element_type = $this->get_package_element_type( $package['kind_slug'] );
$trid = $sitepress->get_element_trid( $package['ID'], $element_type );
return $sitepress->get_element_translations( $trid, $element_type );
}
function _is_translation_in_progress( $package ) {
global $wpdb;
$post_translations = self::_get_post_translations( $package );
foreach ( $post_translations as $lang => $translation ) {
$res = $wpdb->get_row( $wpdb->prepare( "SELECT status, needs_update, md5 FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation->translation_id ) );
if ( $res && $res->status == ICL_TM_IN_PROGRESS ) {
return true;
}
}
return false;
}
function _delete_translation_job( $package_id ) {
global $wpdb;
$package = $this->get_package_details( $package_id );
$post_translations = $this->_get_post_translations( $package );
foreach ( $post_translations as $lang => $translation ) {
$rid = $wpdb->get_var( $wpdb->prepare( "SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation->translation_id ) );
if ( $rid ) {
$job_id = $wpdb->get_var( $wpdb->prepare( "SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid ) );
if ( $job_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}icl_translate WHERE job_id=%d", $job_id ) );
}
}
}
}
public function add_to_basket( $data ) {
if ( isset( $data['tr_action'] ) ) {
$posts_ids = TranslationProxy_Basket::get_elements_ids( $data, 'package' );
foreach ( $posts_ids as $id ) {
$post_id = $id;
$source_language = $data['translate_from'];
$target_languages = $data['tr_action'];
foreach ( $target_languages as $translate_to => $translation_action ) {
$package = new WPML_Package( $post_id );
$tm = new WPML_Package_TM( $package );
$tm->add_package_to_basket( $translation_action, $source_language, $translate_to );
}
}
}
}
function _no_wpml_warning() {
?>
<div class="message error">
<p>
<?php printf( __( 'WPML Package Translation is enabled but not effective. It requires <a href="%s">WPML</a>, String Translation and Translation Management in order to work.', 'wpml-string-translation' ), 'http://wpml.org/' ); ?>
</p>
</div>
<?php
}
public function tm_dashboard_sql_filter( $sql ) {
global $wpdb;
$sql .= " AND i.element_id NOT IN ( SELECT ID FROM {$wpdb->prefix}icl_string_packages WHERE post_id IS NOT NULL AND element_type = 'package_layout' )";
return $sql;
}
/**
* @return bool
*/
private function is_refresh_required() {
$refresh_required = get_option( 'wpml-package-translation-refresh-required', 'yes' );
return 'yes' === $refresh_required || '1' === $refresh_required;
}
private function set_refresh_not_required() {
update_option( 'wpml-package-translation-refresh-required', 'no', false );
}
public function save_package_translations( $element_type_prefix, $job, $decoder ) {
if ( $element_type_prefix === 'package' ) {
$element_type_prefix = $this->get_package_type_prefix( $element_type_prefix, $job->original_doc_id );
foreach ( $job->elements as $field ) {
if ( $field->field_translate ) {
$string_id = icl_st_is_registered_string( $element_type_prefix, $field->field_type );
if ( ! $string_id ) {
icl_register_string( $element_type_prefix, $field->field_type, $decoder( $field->field_data, $field->field_format ) );
$string_id = icl_st_is_registered_string( $element_type_prefix, $field->field_type );
}
if ( $string_id ) {
icl_add_string_translation( $string_id, $job->language_code, $decoder( $field->field_data_translated, $field->field_format ), ICL_TM_COMPLETE );
}
}
}
}
}
}

View File

@@ -0,0 +1,537 @@
<?php
class WPML_Package {
const CACHE_GROUP = 'WPML_Package';
public $ID;
public $view_link;
public $edit_link;
public $is_translation;
public $string_data;
public $title;
public $new_title;
public $kind_slug;
public $kind;
public $trid;
public $name;
public $translation_element_type;
public $post_id;
private $element_type_prefix;
/**
* @param stdClass|WPML_Package|array|int $data_item
*/
function __construct( $data_item ) {
$this->element_type_prefix = 'package';
$this->view_link = '';
$this->edit_link = '';
$this->post_id = null;
if ( $data_item ) {
if ( is_object( $data_item ) ) {
$data_item = get_object_vars( $data_item );
}
if ( is_numeric( $data_item ) ) {
$data_item = $this->init_from_id( $data_item, ARRAY_A );
}
if ( isset( $data_item['title'] ) ) {
$this->new_title = $data_item['title'];
}
if ( $data_item && is_array( $data_item ) ) {
$this->init_from_array( $data_item );
}
$this->new_title = $this->new_title != $this->title ? $this->new_title : null;
}
}
private function init_from_id( $id, $output = OBJECT ) {
global $wpdb;
$packages_query = "SELECT * FROM {$wpdb->prefix}icl_string_packages WHERE id=%s";
$packages_prepared = $wpdb->prepare( $packages_query, $id );
$package = $wpdb->get_row( $packages_prepared, $output );
return $package;
}
public function __get( $property ) {
if ( $property == 'id' ) {
_deprecated_argument( 'id', '0.0.2', "Property 'id' is deprecated. Please use 'ID'." );
return $this->ID;
}
if ( $property == 'post_id' ) {
return $this->ID;
}
if ( $property == 'post_title' ) {
return $this->title;
}
if ( $property == 'post_type' ) {
return $this->kind_slug;
}
return null;
}
public function __set( $property, $value ) {
if ( $property == 'id' ) {
_deprecated_argument( 'id', '0.0.2', "Property 'id' is deprecated. Please use 'ID'." );
$this->ID = $value;
} else {
$this->$property = $value;
}
}
public function __isset( $property ) {
if ( $property == 'id' ) {
return isset( $this->ID );
}
return false;
}
public function __unset( $property ) {
if ( $property == 'id' ) {
unset( $this->$property );
}
}
public function get_translation_element_type() {
return $this->translation_element_type;
}
public function get_package_post_id() {
return $this->get_element_type_prefix() . '_' . $this->kind_slug . '_' . $this->ID;
}
public function get_element_type_prefix() {
return $this->element_type_prefix;
}
public function set_package_post_data() {
$this->translation_element_type = $this->element_type_prefix . '_' . $this->kind_slug;
$this->update_strings_data();
}
public function update_strings_data() {
$strings = [];
$package_id = $this->ID;
if ( $package_id ) {
$results = $this->get_package_strings();
foreach ( $results as $result ) {
$string_name = $this->get_package_string_name_from_st_name( $result );
$strings[ $string_name ] = $result->value;
}
$this->string_data = $strings;
}
}
/**
* @param bool $refresh
*
* @return mixed
*/
public function get_package_strings( $refresh = false ) {
global $wpdb;
$package_id = $this->ID;
$results = false;
if ( $package_id ) {
$cache = new WPML_WP_Cache( self::CACHE_GROUP );
$cache_key = 'strings:' . $package_id;
$found = false;
$results = $cache->get( $cache_key, $found );
if ( ! $found || $refresh ) {
$results_query = "
SELECT id, name, value, wrap_tag, type, title
FROM {$wpdb->prefix}icl_strings
WHERE string_package_id=%d
ORDER BY location, id ASC";
$results_prepare = $wpdb->prepare( $results_query, $package_id );
$results = $wpdb->get_results( $results_prepare );
$cache->set( $cache_key, $results );
}
}
return $results;
}
public function set_strings_language( $language_code ) {
global $wpdb;
$package_id = $this->ID;
if ( $package_id ) {
$update_query = "UPDATE {$wpdb->prefix}icl_strings SET language=%s WHERE string_package_id=%d";
$update_prepare = $wpdb->prepare( $update_query, $language_code, $package_id );
$wpdb->query( $update_prepare );
}
}
/**
* @param \stdClass $result
*
* @return string
*/
private function get_package_string_name_from_st_name( $result ) {
// package string name is the same as the string name.
return $result->name;
}
private function sanitize_attributes() {
if ( isset( $this->name ) ) {
$this->name = $this->sanitize_string_name( $this->name );
}
if ( ( ! isset( $this->title ) || $this->title === '' ) && isset( $this->name ) ) {
$this->title = $this->name;
}
if ( ! isset( $this->edit_link ) ) {
$this->edit_link = '';
}
$this->sanitize_kind();
}
public function create_new_package_record() {
$this->sanitize_attributes();
$package_id = $this->package_exists();
if ( ! $package_id ) {
global $wpdb;
$data = array(
'kind_slug' => $this->kind_slug,
'kind' => $this->kind,
'name' => $this->name,
'title' => $this->title,
'edit_link' => $this->edit_link,
'view_link' => $this->view_link,
'post_id' => $this->post_id,
);
$wpdb->insert( $wpdb->prefix . 'icl_string_packages', $data );
$package_id = $wpdb->insert_id;
$this->ID = $package_id;
}
return $package_id;
}
public function update_package_record() {
$result = false;
if ( $this->ID ) {
global $wpdb;
$update_data = array(
'kind_slug' => $this->kind_slug,
'kind' => $this->kind,
'name' => $this->name,
'title' => $this->title,
'edit_link' => $this->edit_link,
'view_link' => $this->view_link,
);
$update_where = array(
'ID' => $this->ID,
);
$result = $wpdb->update( $wpdb->prefix . 'icl_string_packages', $update_data, $update_where );
}
return $result;
}
public function get_package_id() {
return $this->ID;
}
public function sanitize_string_name( $string_name ) {
$string_name = preg_replace( '/[ \[\]]+/', '-', $string_name );
return $string_name;
}
function translate_string( $string_value, $sanitized_string_name ) {
$package_id = $this->get_package_id();
if ( $package_id ) {
$sanitized_string_name = $this->sanitize_string_name( $sanitized_string_name );
$string_context = $this->get_string_context_from_package();
$string_name = $sanitized_string_name;
return icl_translate( $string_context, $string_name, $string_value );
} else {
return $string_value;
}
}
function get_string_context_from_package() {
return $this->kind_slug . '-' . $this->name;
}
public function get_string_id_from_package( $string_name, $string_value ) {
$package_id = $this->get_package_id();
$string_context = $this->get_string_context_from_package();
$string_data = array(
'context' => $string_context,
'name' => $string_name,
);
/**
* @param int|null $default
* @param array $string_data {
*
* @type string $context
* @type string $name Optional
* }
*/
$string_id = apply_filters( 'wpml_string_id', null, $string_data );
if ( ! $string_id ) {
$string_id = icl_register_string( $string_context, $string_name, $string_value, null, $this->get_package_language() );
}
return $string_id;
}
function get_translated_strings( $strings ) {
$package_id = $this->get_package_id();
if ( $package_id ) {
$results = $this->get_package_strings();
foreach ( $results as $result ) {
$translations = icl_get_string_translations_by_id( $result->id );
if ( ! empty( $translations ) ) {
$string_name = $this->get_package_string_name_from_st_name( $result );
$strings[ $string_name ] = $translations;
}
}
}
return $strings;
}
function set_translated_strings( $translations ) {
global $wpdb;
$this->sanitize_attributes();
$package_id = $this->get_package_id();
if ( $package_id ) {
foreach ( $translations as $string_name => $languages ) {
$string_id_query = "SELECT id FROM {$wpdb->prefix}icl_strings WHERE name='%s'";
$string_id_prepare = $wpdb->prepare( $string_id_query, $string_name );
$string_id = $wpdb->get_var( $string_id_prepare );
foreach ( $languages as $language_code => $language_data ) {
icl_add_string_translation( $string_id, $language_code, $language_data['value'], $language_data['status'] );
}
}
}
}
private function init_from_array( $args ) {
foreach ( $args as $key => $value ) {
if ( 'id' == $key ) {
$key = 'ID';
}
$this->$key = $value;
}
$this->sanitize_attributes();
if ( $this->package_id_exists() || $this->package_name_and_kind_exists() ) {
$this->set_package_from_db();
}
$this->set_package_post_data();
}
public function has_kind_and_name() {
return ( isset( $this->kind ) && isset( $this->name ) && $this->kind && $this->name );
}
private function set_package_from_db() {
$package = false;
if ( $this->package_id_exists() ) {
$package = $this->get_package_from_id();
} elseif ( $this->package_name_and_kind_exists() ) {
$package = $this->get_package_from_name_and_kind();
}
if ( $package ) {
$this->object_to_package( $package );
}
$this->sanitize_kind();
}
private function get_package_from_id() {
$result = false;
if ( $this->has_id() ) {
global $wpdb;
$package_query = "SELECT * FROM {$wpdb->prefix}icl_string_packages WHERE ID=%d";
$package_prepared = $wpdb->prepare( $package_query, array( $this->ID ) );
$result = $wpdb->get_row( $package_prepared );
}
return $result;
}
private function get_package_from_name_and_kind() {
global $wpdb;
$cache = new WPML_WP_Cache( self::CACHE_GROUP );
$cache_key = 'name-kind:' . $this->kind_slug . $this->name;
$found = false;
$result = $cache->get( $cache_key, $found );
if ( ! $found ) {
$package_query = "SELECT * FROM {$wpdb->prefix}icl_string_packages WHERE kind_slug=%s AND name=%s";
$package_prepared = $wpdb->prepare( $package_query, array( $this->kind_slug, $this->name ) );
$result = $wpdb->get_row( $package_prepared );
if ( $result ) {
$cache->set( $cache_key, $result );
}
}
return $result;
}
private function package_name_and_kind_exists() {
$result = false;
if ( $this->has_kind_and_name() ) {
$result = (bool) $this->get_package_from_name_and_kind();
}
return $result;
}
private function package_id_exists() {
$result = false;
if ( $this->has_id() ) {
global $wpdb;
$package_query = "SELECT ID FROM {$wpdb->prefix}icl_string_packages WHERE ID=%d";
$package_prepared = $wpdb->prepare( $package_query, array( $this->ID ) );
$result = $wpdb->get_var( $package_prepared );
}
return $result;
}
/**
* @return bool|mixed
*/
protected function package_exists() {
$existing_package = false;
if ( $this->has_id() ) {
$existing_package = $this->package_id_exists();
} elseif ( $this->has_kind_and_name() ) {
$existing_package = $this->package_name_and_kind_exists();
}
return $existing_package;
}
/**
* @return bool
*/
private function has_id() {
return isset( $this->ID ) && $this->ID;
}
/**
* @param \stdClass $package
*/
private function object_to_package( $package ) {
$this->ID = $package->ID;
$this->kind_slug = $package->kind_slug;
$this->kind = $package->kind;
$this->name = $package->name;
$this->title = $package->title;
$this->edit_link = $package->edit_link;
$this->view_link = $package->view_link;
}
private function get_kind_from_slug() {
global $wpdb;
if ( $this->kind_slug ) {
$kinds_query = "SELECT kind FROM {$wpdb->prefix}icl_string_packages WHERE kind_slug=%s GROUP BY kind";
$kinds_prepared = $wpdb->prepare( $kinds_query, $this->kind_slug );
$kinds = $wpdb->get_col( $kinds_prepared );
if ( count( $kinds ) > 1 ) {
throw new WPML_Package_Exception( 'error', 'Package contains multiple kinds' );
}
if ( $kinds ) {
return $kinds[0];
}
}
return null;
}
private function sanitize_kind() {
if ( isset( $this->kind ) && ( ! isset( $this->kind_slug ) || trim( $this->kind_slug ) === '' ) ) {
$this->kind_slug = sanitize_title_with_dashes( $this->kind );
}
if ( $this->kind == $this->kind_slug ) {
$this->kind = $this->get_kind_from_slug();
}
}
public function get_package_element_type() {
return 'package_' . $this->kind_slug;
}
/**
* @return string|null
*/
public function get_package_language() {
global $sitepress;
if ( $this->post_id ) {
$details = null;
$post = get_post( $this->post_id );
if ( $post ) {
$details = $sitepress->get_element_language_details( $this->post_id, 'post_' . $post->post_type );
}
} else {
$element_type = $this->get_package_element_type();
$details = $sitepress->get_element_language_details( $this->ID, $element_type );
}
if ( $details ) {
return $details->language_code;
} else {
return null;
}
}
public function are_all_strings_included( $strings ) {
// check to see if all the strings in this package are present in $strings
$package_strings = $this->get_package_strings();
if ( is_array( $package_strings ) ) {
foreach ( $package_strings as $string ) {
if ( ! in_array( $string->id, $strings ) ) {
return false;
}
}
}
return true;
}
public function flush_cache() {
$cache = new WPML_WP_Cache( self::CACHE_GROUP );
$cache->flush_group_cache();
}
}

View File

@@ -0,0 +1,113 @@
/*globals jQuery, ajaxurl*/
/*jshint browser:true, devel:true*/
var WPML_Package_Translation = WPML_Package_Translation || {};
(function () {
"use strict";
WPML_Package_Translation.ManagementPage = function () {
var self = this;
self.init = function () {
jQuery('.js_package_all_cb').on('change', self.check_uncheck_all);
jQuery('.js_package_row_cb').on('change', self.enable_disable_delete);
jQuery('#delete_packages').on('click', self.delete_selected_packages);
jQuery('#package_kind_filter').on('change', self.filter_by_kind);
};
self.check_uncheck_all = function () {
var package_all_cb = jQuery('.js_package_all_cb').first();
var checked = package_all_cb.prop('checked');
jQuery('.js_package_row_cb').each(
function () {
if (!jQuery(this).is(':disabled')) {
jQuery(this).prop('checked', checked);
}
}
);
self.enable_disable_delete();
};
self.enable_disable_delete = function () {
var enable = jQuery('.js_package_row_cb:checked:visible').length !== 0;
jQuery('#delete_packages').prop('disabled', !enable);
};
self.delete_selected_packages = function () {
if (confirm(jQuery('.js-delete-confirm-message').html())) {
jQuery('#delete_packages').prop('disabled', true);
jQuery('.spinner').css( 'float', 'none' ).addClass( 'is-active' );
var selected = jQuery('.js_package_row_cb:checked:visible');
var packages = [];
selected.each(
function () {
packages.push(jQuery(this).val());
}
);
var data = {
action: 'wpml_delete_packages',
wpnonce: jQuery('#wpml_package_nonce').attr('value'),
packages: packages
};
jQuery.ajax(
{
url: ajaxurl,
type: 'post',
data: data,
success: function () {
selected.each(
function () {
var package_row = jQuery(this).closest('.js_package');
package_row.fadeOut(
1000, function () {
jQuery(this).remove();
}
);
}
);
jQuery('.spinner').removeClass( 'is-active' );
}
}
);
}
};
self.all_selected = function () {
var kind_slug = jQuery('#package_kind_filter').val();
return kind_slug === '-1';
};
self.filter_by_kind = function () {
var kind_slug = jQuery('#package_kind_filter').val();
var icl_package_translations = jQuery('#icl_package_translations');
var icl_package_translations_body = icl_package_translations.find('tbody');
if (self.all_selected()) {
icl_package_translations_body.find('tr').show();
} else {
icl_package_translations_body.find('tr').hide();
icl_package_translations_body.find('tr.js_package.js_package_' + kind_slug).show();
}
self.enable_disable_delete();
};
self.init();
};
jQuery(
function () {
WPML_Package_Translation.management_page = new WPML_Package_Translation.ManagementPage();
}
);
}());

View File

@@ -0,0 +1,30 @@
<?php
/*
WPML Package Translation
This is now part of String Translation.
*/
if ( defined( 'WPML_PACKAGE_TRANSLATION' ) ) {
return;
}
define( 'WPML_PACKAGE_TRANSLATION', '0.0.2' );
define( 'WPML_PACKAGE_TRANSLATION_PATH', dirname( __FILE__ ) );
define( 'WPML_PACKAGE_TRANSLATION_URL', WPML_ST_URL . '/inc/' . basename( WPML_PACKAGE_TRANSLATION_PATH ) );
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-constants.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-exception.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-helper.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-ui.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-html-packages.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-metabox.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-st.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-tm-jobs.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation-tm.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package.class.php';
require WPML_PACKAGE_TRANSLATION_PATH . '/inc/wpml-package-translation.class.php';
$WPML_package_translation = new WPML_Package_Translation();
$WPML_Package_Translation_UI = new WPML_Package_Translation_UI();
$WPML_Package_Translation_HTML_Packages = new WPML_Package_Translation_HTML_Packages();

View File

@@ -0,0 +1,29 @@
<?php
function wpml_st_pos_scan_store_results( $string, $domain, $context, $file, $line ) {
global $__wpml_st_po_file_content;
static $strings = array();
$key = md5( $domain . $context . $string );
//avoid duplicates
if ( isset( $strings[ $key ] ) ) {
return false;
}
$strings[ $key ] = true;
$file = @file( $file );
if ( ! empty( $file ) ) {
$__wpml_st_po_file_content .= PHP_EOL;
$__wpml_st_po_file_content .= '# ' . @trim( $file[ $line - 2 ] ) . PHP_EOL;
$__wpml_st_po_file_content .= '# ' . @trim( $file[ $line - 1 ] ) . PHP_EOL;
$__wpml_st_po_file_content .= '# ' . @trim( $file[ $line ] ) . PHP_EOL;
}
//$__wpml_st_po_file_content .= 'msgid "'.str_replace('"', '\"', $string).'"' . PHP_EOL;
$__wpml_st_po_file_content .= PHP_EOL;
if ( $context ) {
$__wpml_st_po_file_content .= 'msgctxt "' . addslashes( $context ) . '"' . PHP_EOL;
}
$__wpml_st_po_file_content .= 'msgid "' . $string . '"' . PHP_EOL;
$__wpml_st_po_file_content .= 'msgstr ""' . PHP_EOL;
}

View File

@@ -0,0 +1,714 @@
<?php
/**
* @package wpml-core
*/
// $Id: potx.inc,v 1.1.2.17.2.7.2.19.4.1 2009/07/19 12:54:42 goba Exp $
/**
* @file
* Extraction API used by the web and command line interface.
*
* This include file implements the default string and file version
* storage as well as formatting of POT files for web download or
* file system level creation. The strings, versions and file contents
* are handled with global variables to reduce the possible memory overhead
* and API clutter of passing them around. Custom string and version saving
* functions can be implemented to use the functionality provided here as an
* API for Drupal code to translatable string conversion.
*
* The potx-cli.php script can be used with this include file as
* a command line interface to string extraction. The potx.module
* can be used as a web interface for manual extraction.
*
* For a module using potx as an extraction API, but providing more
* sophisticated functionality on top of it, look into the
* 'Localization server' module: http://drupal.org/project/l10n_server
*/
/**
* Silence status reports.
*/
define('POTX_STATUS_SILENT', 0);
/**
* Drupal message based status reports.
*/
define('POTX_STATUS_MESSAGE', 1);
/**
* Command line status reporting.
*
* Status goes to standard output, errors to standard error.
*/
define('POTX_STATUS_CLI', 2);
/**
* Structured array status logging.
*
* Useful for coder review status reporting.
*/
define('POTX_STATUS_STRUCTURED', 3);
/**
* Core parsing mode:
* - .info files folded into general.pot
* - separate files generated for modules
*/
define('POTX_BUILD_CORE', 0);
/**
* Multiple files mode:
* - .info files folded into their module pot files
* - separate files generated for modules
*/
define('POTX_BUILD_MULTIPLE', 1);
/**
* Single file mode:
* - all files folded into one pot file
*/
define('POTX_BUILD_SINGLE', 2);
/**
* Save string to both installer and runtime collection.
*/
define('POTX_STRING_BOTH', 0);
/**
* Save string to installer collection only.
*/
define('POTX_STRING_INSTALLER', 1);
/**
* Save string to runtime collection only.
*/
define('POTX_STRING_RUNTIME', 2);
/**
* Parse source files in Drupal 5.x format.
*/
define('POTX_API_5', 5);
/**
* Parse source files in Drupal 6.x format.
*
* Changes since 5.x documented at http://drupal.org/node/114774
*/
define('POTX_API_6', 6);
/**
* Parse source files in Drupal 7.x format.
*
* Changes since 6.x documented at http://drupal.org/node/224333
*/
define('POTX_API_7', 7);
/**
* When no context is used. Makes it easy to look these up.
*/
define('POTX_CONTEXT_NONE', NULL);
/**
* When there was a context identification error.
*/
define('POTX_CONTEXT_ERROR', FALSE);
/**
* Process a file and put extracted information to the given parameters.
*
* @param string $file_path Complete path to file to process.
* @param int $strip_prefix An integer denoting the number of chars to strip from filepath for output.
* @param callable|string $save_callback Callback function to use to save the collected strings.
* @param callable|string $version_callback Callback function to use to save collected version numbers.
* @param string $default_domain Default domain to be used if one can't be found.
*/
function _potx_process_file($file_path,
$strip_prefix = 0,
$save_callback = '_potx_save_string',
$version_callback = '_potx_save_version',
$default_domain = '') {
global $_potx_tokens, $_potx_lookup;
// Always grab the CVS version number from the code
if ( !wpml_st_file_path_is_valid( $file_path ) ) {
return;
}
$code = file_get_contents($file_path);
$file_name = $strip_prefix > 0 ? substr($file_path, $strip_prefix) : $file_path;
_potx_find_version_number($code, $file_name, $version_callback);
// Extract raw PHP language tokens.
$raw_tokens = token_get_all($code);
unset($code);
// Remove whitespace and possible HTML (the later in templates for example),
// count line numbers so we can include them in the output.
$_potx_tokens = array();
$_potx_lookup = array();
$token_number = 0;
$line_number = 1;
// Fill array for finding token offsets quickly.
$src_tokens = array(
'__', 'esc_attr__', 'esc_html__', '_e', 'esc_attr_e', 'esc_html_e',
'_x', 'esc_attr_x', 'esc_html_x', '_ex',
'_n', '_nx'
);
foreach ($raw_tokens as $token) {
if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) {
if (is_array($token)) {
$token[] = $line_number;
if ($token[0] == T_STRING || ($token[0] == T_VARIABLE && in_array($token[1], $src_tokens))) {
if (!isset($_potx_lookup[$token[1]])) {
$_potx_lookup[$token[1]] = array();
}
$_potx_lookup[$token[1]][] = $token_number;
}
}
$_potx_tokens[] = $token;
$token_number++;
}
// Collect line numbers.
if (is_array($token)) {
$line_number += count(explode("\n", $token[1])) - 1;
}
else {
$line_number += count(explode("\n", $token)) - 1;
}
}
unset($raw_tokens);
if(!empty($src_tokens))
foreach($src_tokens as $tk){
_potx_find_t_calls_with_context($file_name, $save_callback, $tk, $default_domain);
}
}
/**
* Escape quotes in a strings depending on the surrounding
* quote type used.
*
* @param string $str The strings to escape
*/
function _potx_format_quoted_string($str) {
$quo = substr($str, 0, 1);
$str = substr($str, 1, -1);
if ($quo == '"') {
$str = stripcslashes($str);
}
else {
$str = strtr($str, array("\\'" => "'", "\\\\" => "\\"));
}
return addcslashes($str, "\0..\37\\\"");
}
/**
* @param string $string
*
* @return string
*/
function wpml_potx_unquote_context_or_domain( $string ) {
$quote_type = mb_substr( $string, 0, 1 );
return trim( $string, $quote_type );
}
/**
* Output a marker error with an extract of where the error was found.
*
* @param string $file Name of file
* @param int $line Line number of error
* @param string $marker Function name with which the error was identified
* @param int $ti Index on the token array
* @param string $error Helpful error message for users.
* @param string $docs_url Documentation reference.
*/
function _potx_marker_error($file, $line, $marker, $ti, $error, $docs_url = NULL) {
global $_potx_tokens;
$tokens = '';
$ti += 2;
$tc = count($_potx_tokens);
$par = 1;
while ((($tc - $ti) > 0) && $par) {
if (is_array($_potx_tokens[$ti])) {
$tokens .= $_potx_tokens[$ti][1];
}
else {
$tokens .= $_potx_tokens[$ti];
if ($_potx_tokens[$ti] == "(") {
$par++;
}
else if ($_potx_tokens[$ti] == ")") {
$par--;
}
}
$ti++;
}
potx_status('error', $error, $file, $line, $marker .'('. $tokens, $docs_url);
}
/**
* Status notification function.
*
* @param string $op Operation to perform or type of message text.
* - set: sets the reporting mode to $value
* use one of the POTX_STATUS_* constants as $value
* - get: returns the list of error messages recorded
* if $value is true, it also clears the internal message cache
* - error: sends an error message in $value with optional $file and $line
* - status: sends a status message in $value
* @param string $value Value depending on $op.
* @param string $file Name of file the error message is related to.
* @param int $line Number of line the error message is related to.
* @param string $excerpt Excerpt of the code in question, if available.
* @param string $docs_url URL to the guidelines to follow to fix the problem.
*/
function potx_status($op, $value = NULL, $file = NULL, $line = NULL, $excerpt = NULL, $docs_url = NULL) {
static $mode = POTX_STATUS_CLI;
static $messages = array();
switch ($op) {
case 'set':
// Setting the reporting mode.
$mode = $value;
return;
case 'get':
// Getting the errors. Optionally deleting the messages.
$errors = $messages;
if (!empty($value)) {
$messages = array();
}
return $errors;
case 'error':
case 'status':
// Location information is required in 3 of the four possible reporting
// modes as part of the error message. The structured mode needs the
// file, line and excerpt info separately, not in the text.
$location_info = '';
if (($mode != POTX_STATUS_STRUCTURED) && isset($file)) {
if (isset($line)) {
if (isset($excerpt)) {
$location_info = potx_t('At %excerpt in %file on line %line.', array('%excerpt' => $excerpt, '%file' => $file, '%line' => $line));
}
else {
$location_info = potx_t('In %file on line %line.', array('%file' => $file, '%line' => $line));
}
}
else {
if (isset($excerpt)) {
$location_info = potx_t('At %excerpt in %file.', array('%excerpt' => $excerpt, '%file' => $file));
}
else {
$location_info = potx_t('In %file.', array('%file' => $file));
}
}
}
// Documentation helpers are provided as readable text in most modes.
$read_more = '';
if (($mode != POTX_STATUS_STRUCTURED) && isset($docs_url)) {
$read_more = ($mode == POTX_STATUS_CLI) ? potx_t('Read more at @url', array('@url' => $docs_url)) : potx_t('Read more at <a href="@url">@url</a>', array('@url' => $docs_url));
}
// Error message or progress text to display.
switch ($mode) {
case POTX_STATUS_CLI:
if(defined('STDERR') && defined('STDOUT')){
fwrite($op == 'error' ? STDERR : STDOUT, join("\n", array($value, $location_info, $read_more)) ."\n\n");
}
break;
case POTX_STATUS_SILENT:
if ($op == 'error') {
$messages[] = join(' ', array($value, $location_info, $read_more));
}
break;
case POTX_STATUS_STRUCTURED:
if ($op == 'error') {
$messages[] = array($value, $file, $line, $excerpt, $docs_url);
}
break;
}
return;
}
}
/**
* Detect all occurances of t()-like calls.
*
* These sequences are searched for:
* T_STRING("$function_name") + "(" + T_CONSTANT_ENCAPSED_STRING + ")"
* T_STRING("$function_name") + "(" + T_CONSTANT_ENCAPSED_STRING + ","
*
* @param string $file Name of file parsed.
* @param callable $save_callback Callback function used to save strings.
* @param string $function_name The name of the function to look for (could be 't', '$t', 'st'
* or any other t-like function).
* @param int $string_mode String mode to use: POTX_STRING_INSTALLER, POTX_STRING_RUNTIME or
* POTX_STRING_BOTH.
*/
function _potx_find_t_calls($file, $save_callback, $function_name = 't', $string_mode = POTX_STRING_RUNTIME) {
global $_potx_tokens, $_potx_lookup;
// Lookup tokens by function name.
if (isset($_potx_lookup[$function_name])) {
foreach ($_potx_lookup[$function_name] as $ti) {
list($ctok, $par, $mid, $rig) = array($_potx_tokens[$ti], $_potx_tokens[$ti+1], $_potx_tokens[$ti+2], $_potx_tokens[$ti+3]);
list($type, $string, $line) = $ctok;
if ($par == "(") {
if (in_array($rig, array(")", ","))
&& (is_array($mid) && ($mid[0] == T_CONSTANT_ENCAPSED_STRING))) {
// This function is only used for context-less call types.
$save_callback(_potx_format_quoted_string($mid[1]), POTX_CONTEXT_NONE, $file, $line, $string_mode);
}
else {
// $function_name() found, but inside is something which is not a string literal.
_potx_marker_error($file, $line, $function_name, $ti, potx_t('The first parameter to @function() should be a literal string. There should be no variables, concatenation, constants or other non-literal strings there.', array('@function' => $function_name)), 'http://drupal.org/node/322732');
}
}
}
}
}
/**
* Detect all occurances of t()-like calls from Drupal 7 (with context).
*
* These sequences are searched for:
* T_STRING("$function_name") + "(" + T_CONSTANT_ENCAPSED_STRING + ")"
* T_STRING("$function_name") + "(" + T_CONSTANT_ENCAPSED_STRING + ","
* and then an optional value for the replacements and an optional array
* for the options with an optional context key.
*
* @param string $file Name of file parsed.
* @param callable $save_callback Callback function used to save strings.
* @param string $function_name
* @param string $default_domain
* @param int $string_mode String mode to use: POTX_STRING_INSTALLER, POTX_STRING_RUNTIME or
* POTX_STRING_BOTH.
*
* @internal param $function_name The name of the function to look for (could be 't', '$t', 'st'* The name of the function to look for (could be 't', '$t', 'st'
* or any other t-like function). Drupal 7 only supports context on t().
*/
function _potx_find_t_calls_with_context(
$file,
$save_callback,
$function_name = '_e',
$default_domain = '',
$string_mode = POTX_STRING_RUNTIME
) {
global $_potx_tokens, $_potx_lookup;
$filter_by_domain = isset( $_GET['domain'] ) ? filter_var( $_GET['domain'], FILTER_SANITIZE_STRING ) : null;
// Lookup tokens by function name.
if ( isset( $_potx_lookup[ $function_name ] ) ) {
foreach ( $_potx_lookup[ $function_name ] as $ti ) {
list( $ctok, $par, $mid, $rig ) = array(
$_potx_tokens[ $ti ],
$_potx_tokens[ $ti + 1 ],
$_potx_tokens[ $ti + 2 ],
$_potx_tokens[ $ti + 3 ]
);
list( $type, $string, $line ) = $ctok;
if ( $par == "(" ) {
if ( in_array( $rig, array( ")", "," ) )
&& ( is_array( $mid ) && ( $mid[ 0 ] == T_CONSTANT_ENCAPSED_STRING ) )
) {
$context = false;
// By default, there is no context.
$domain = POTX_CONTEXT_NONE;
if ( $rig == ',' ) {
if ( in_array( $function_name, array( '_x', '_ex', 'esc_attr_x', 'esc_html_x' ), true ) ) {
$domain_offset = 6;
$context_offset = 4;
} elseif ( $function_name == '_n' ) {
$domain_offset = _potx_find_end_of_function( $ti, '(', ')' ) - 1 - $ti;
$context_offset = false;
$text_plural = $_potx_tokens[ $ti + 4 ][ 1 ];
} elseif ( $function_name == '_nx' ) {
$domain_offset = _potx_find_end_of_function( $ti, '(', ')' ) - 1 - $ti;
$context_offset = $domain_offset - 2;
$text_plural = $_potx_tokens[ $ti + 4 ][ 1 ];
} else {
$domain_offset = 4;
$context_offset = false;
}
if ( ! isset( $_potx_tokens[ $ti + $domain_offset ][ 1 ] )
|| ! preg_match( '#^(\'|")(.+)#', $_potx_tokens[ $ti + $domain_offset ][ 1 ] )
) {
if ( $default_domain ) {
$domain = $default_domain;
} else {
continue;
}
} else {
$domain = wpml_potx_unquote_context_or_domain( $_potx_tokens[ $ti + $domain_offset ][ 1 ] );
}
// exception for gettext calls with contexts
if ( false !== $context_offset && isset( $_potx_tokens[ $ti + $context_offset ] ) ) {
if ( ! preg_match( '#^(\'|")(.+)#', @$_potx_tokens[ $ti + $context_offset ][ 1 ] ) ) {
$constant_val = @constant( $_potx_tokens[ $ti + $context_offset ][ 1 ] );
if ( ! is_null( $constant_val ) ) {
$context = $constant_val;
} else {
if ( function_exists( @$_potx_tokens[ $ti + $context_offset ][ 1 ] ) ) {
$context = @$_potx_tokens[ $ti + $context_offset ][ 1 ]();
if ( empty( $context ) ) {
continue;
}
} else {
continue;
}
}
} else {
$context = wpml_potx_unquote_context_or_domain( $_potx_tokens[ $ti + $context_offset ][ 1 ] );
}
} else {
$context = false;
}
}
if (
$domain !== POTX_CONTEXT_ERROR &&
( ! $filter_by_domain || $filter_by_domain === $domain ) &&
is_callable( $save_callback, false, $callback_name )
) {
// Only save if there was no error in context parsing.
call_user_func( $save_callback,
_potx_format_quoted_string( $mid[ 1 ] ),
$domain,
@strval( $context ),
$file,
$line,
$string_mode );
if ( isset( $text_plural ) ) {
call_user_func( $save_callback,
_potx_format_quoted_string( $text_plural ),
$domain,
$context,
$file,
$line,
$string_mode );
}
}
} else {
// $function_name() found, but inside is something which is not a string literal.
_potx_marker_error( $file,
$line,
$function_name,
$ti,
potx_t( 'The first parameter to @function() should be a literal string. There should be no variables, concatenation, constants or other non-literal strings there.',
array( '@function' => $function_name ) ),
'http://drupal.org/node/322732' );
}
}
}
}
}
/**
* Helper function to look up the token closing the current function.
*
* @param string $here The token at the function name
*/
function _potx_find_end_of_function($here, $open = '{', $close = '}') {
global $_potx_tokens;
// Seek to open brace.
while (is_array($_potx_tokens[$here]) || $_potx_tokens[$here] != $open) {
$here++;
}
$nesting = 1;
while ($nesting > 0) {
$here++;
if (!is_array($_potx_tokens[$here])) {
if ($_potx_tokens[$here] == $close) {
$nesting--;
}
if ($_potx_tokens[$here] == $open) {
$nesting++;
}
}
}
return $here;
}
/**
* Helper to move past potx_t() and format_plural() arguments in search of context.
*
* @param string $here The token before the start of the arguments
*/
function _potx_skip_args($here) {
global $_potx_tokens;
$nesting = 0;
// Go through to either the end of the function call or to a comma
// after the current position on the same nesting level.
while (!(($_potx_tokens[$here] == ',' && $nesting == 0) ||
($_potx_tokens[$here] == ')' && $nesting == -1))) {
$here++;
if (!is_array($_potx_tokens[$here])) {
if ($_potx_tokens[$here] == ')') {
$nesting--;
}
if ($_potx_tokens[$here] == '(') {
$nesting++;
}
}
}
// If we run out of nesting, it means we reached the end of the function call,
// so we skipped the arguments but did not find meat for looking at the
// specified context.
return ($nesting == 0 ? $here : FALSE);
}
/**
* Helper to find the value for 'context' on t() and format_plural().
*
* @param int $tf Start position of the original function.
* @param int $ti Start position where we should search from.
* @param string $file Full path name of file parsed.
* @param string $function_name The name of the function to look for. Either 'format_plural' or 't'
* given that Drupal 7 only supports context on these.
*/
function _potx_find_context($tf, $ti, $file, $function_name) {
global $_potx_tokens;
// Start from after the comma and skip the possible arguments for the function
// so we can look for the context.
if (($ti = _potx_skip_args($ti)) && ($_potx_tokens[$ti] == ',')) {
// Now we actually might have some definition for a context. The $options
// argument is coming up, which might have a key for context.
echo "TI:" . $ti."\n";
list($com, $arr, $par) = array($_potx_tokens[$ti], $_potx_tokens[$ti+1], $_potx_tokens[$ti+2]);
if ($com == ',' && $arr[1] == 'array' && $par == '(') {
$nesting = 0;
$ti += 3;
// Go through to either the end of the array or to the key definition of
// context on the same nesting level.
while (!((is_array($_potx_tokens[$ti]) && (in_array($_potx_tokens[$ti][1], array('"context"', "'context'"))) && ($_potx_tokens[$ti][0] == T_CONSTANT_ENCAPSED_STRING) && ($nesting == 0)) ||
($_potx_tokens[$ti] == ')' && $nesting == -1))) {
$ti++;
if (!is_array($_potx_tokens[$ti])) {
if ($_potx_tokens[$ti] == ')') {
$nesting--;
}
if ($_potx_tokens[$ti] == '(') {
$nesting++;
}
}
}
if ($nesting == 0) {
// Found the 'context' key on the top level of the $options array.
list($arw, $str) = array($_potx_tokens[$ti+1], $_potx_tokens[$ti+2]);
if (is_array($arw) && $arw[1] == '=>' && is_array($str) && $str[0] == T_CONSTANT_ENCAPSED_STRING) {
return _potx_format_quoted_string($str[1]);
}
else {
list($type, $string, $line) = $_potx_tokens[$ti];
// @todo: fix error reference.
_potx_marker_error($file, $line, $function_name, $tf, potx_t('The context element in the options array argument to @function() should be a literal string. There should be no variables, concatenation, constants or other non-literal strings there.', array('@function' => $function_name)), 'http://drupal.org/node/322732');
// Return with error.
return POTX_CONTEXT_ERROR;
}
}
else {
// Did not found 'context' key in $options array.
return POTX_CONTEXT_NONE;
}
}
}
// After skipping args, we did not find a comma to look for $options.
return POTX_CONTEXT_NONE;
}
/**
* Get the exact CVS version number from the file, so we can
* push that into the generated output.
*
* @param string $code Complete source code of the file parsed.
* @param string $file Name of the file parsed.
* @param callable $version_callback Callback used to save the version information.
*/
function _potx_find_version_number($code, $file, $version_callback) {
// Prevent CVS from replacing this pattern with actual info.
if (preg_match('!\\$I'.'d: ([^\\$]+) Exp \\$!', $code, $version_info)) {
$version_callback($version_info[1], $file);
}
else {
// Unknown version information.
$version_callback($file .': n/a', $file);
}
}
/**
* Default $version_callback used by the potx system. Saves values
* to a global array to reduce memory consumption problems when
* passing around big chunks of values.
*
* @param string $value The version number value of $file. If NULL, the collected
* values are returned.
* @param string $file Name of file where the version information was found.
*/
function _potx_save_version($value = NULL, $file = NULL) {
global $_potx_versions;
if (isset($value)) {
$_potx_versions[$file] = $value;
}
else {
return $_potx_versions;
}
}
/**
* Default $save_callback used by the potx system. Saves values
* to global arrays to reduce memory consumption problems when
* passing around big chunks of values.
*
* @param string $value The string value. If NULL, the array of collected values
* are returned for the given $string_mode.
* @param string $context From Drupal 7, separate contexts are supported. POTX_CONTEXT_NONE is
* the default, if the code does not specify a context otherwise.
* @param string $file Name of file where the string was found.
* @param int $line Line number where the string was found.
* @param int $string_mode String mode: POTX_STRING_INSTALLER, POTX_STRING_RUNTIME
* or POTX_STRING_BOTH.
*/
function _potx_save_string($value = NULL, $context = NULL, $file = NULL, $line = 0, $string_mode = POTX_STRING_RUNTIME) {
global $_potx_strings, $_potx_install;
if (isset($value)) {
switch ($string_mode) {
case POTX_STRING_BOTH:
// Mark installer strings as duplicates of runtime strings if
// the string was both recorded in the runtime and in the installer.
$_potx_install[$value][$context][$file][] = $line .' (dup)';
// Break intentionally missing.
case POTX_STRING_RUNTIME:
// Mark runtime strings as duplicates of installer strings if
// the string was both recorded in the runtime and in the installer.
$_potx_strings[$value][$context][$file][] = $line . ($string_mode == POTX_STRING_BOTH ? ' (dup)' : '');
break;
case POTX_STRING_INSTALLER:
$_potx_install[$value][$context][$file][] = $line;
break;
}
}
else {
return ($string_mode == POTX_STRING_RUNTIME ? $_potx_strings : $_potx_install);
}
}
function potx_t( $string, $args = array() ) {
return strtr ( $string, $args );
}

View File

@@ -0,0 +1,78 @@
<?php
function wpml_st_parse_config( $file_or_object ) {
global $wpdb;
require_once WPML_ST_PATH . '/inc/admin-texts/wpml-admin-text-import.class.php';
$config = new WPML_Admin_Text_Configuration( $file_or_object );
$config_array = $config->get_config_array();
if ( ! empty( $config_array ) ) {
$config_handler = $file_or_object;
if ( isset( $file_or_object->type, $file_or_object->admin_text_context ) ) {
$config_handler = $file_or_object->type . $file_or_object->admin_text_context;
}
$st_records = new WPML_ST_Records( $wpdb );
$import = new WPML_Admin_Text_Import( $st_records, new WPML_WP_API() );
$config_handler_hash = md5( serialize( $config_handler ) );
$import->parse_config( $config_array, $config_handler_hash );
}
}
add_action( 'wpml_parse_config_file', 'wpml_st_parse_config', 10, 1 );
add_action( 'wpml_parse_custom_config', 'wpml_st_parse_config', 10, 1 );
/**
* Action run on the wp_loaded hook that registers widget titles,
* tagline and bloginfo as well as the current theme's strings when
* String translation is first activated
*/
function wpml_st_initialize_basic_strings() {
/** @var WPML_String_Translation $WPML_String_Translation */
global $sitepress, $pagenow, $WPML_String_Translation;
$load_action = new WPML_ST_WP_Loaded_Action(
$sitepress,
$WPML_String_Translation,
$pagenow,
isset( $_GET['page'] ) ? $_GET['page'] : ''
);
if ( $sitepress->is_setup_complete() ) {
$load_action->run();
}
}
if ( is_admin() ) {
add_action( 'wp_loaded', 'wpml_st_initialize_basic_strings' );
}
/**
* @param string $old
* @param string $new
*/
function icl_st_update_blogname_actions( $old, $new ) {
icl_st_update_string_actions(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_DOMAIN,
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGNAME,
$old,
$new,
true
);
}
/**
* @param string $old
* @param string $new
*/
function icl_st_update_blogdescription_actions( $old, $new ) {
icl_st_update_string_actions(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_DOMAIN,
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGDESCRIPTION,
$old,
$new,
true
);
}

View File

@@ -0,0 +1,69 @@
<?php
use function WPML\Container\make;
/**
* @param array $source_languages
*
* @return array[]
*/
function filter_tm_source_langs( $source_languages ) {
global $wpdb, $sitepress;
static $tm_filter;
if ( ! $tm_filter ) {
$tm_filter = new WPML_TM_Filters( $wpdb, $sitepress );
}
return $tm_filter->filter_tm_source_langs( $source_languages );
}
/**
*
* @param bool $assigned_correctly
* @param string $string_translation_id in the format used by
* TM functionality as
* "string|{$string_translation_id}"
* @param int $translator_id
* @param int|string $service
*
* @return bool
*/
function wpml_st_filter_job_assignment( $assigned_correctly, $string_translation_id, $translator_id, $service ) {
global $wpdb, $sitepress;
$tm_filter = new WPML_TM_Filters( $wpdb, $sitepress );
return $tm_filter->job_assigned_to_filter( $assigned_correctly, $string_translation_id, $translator_id, $service );
}
add_filter( 'wpml_tm_allowed_source_languages', 'filter_tm_source_langs', 10, 1 );
add_filter( 'wpml_job_assigned_to_after_assignment', 'wpml_st_filter_job_assignment', 10, 4 );
/**
* @deprecated since WPML ST 3.0.0
*
* @param string $val
*
* @return string
* @throws \WPML\Auryn\InjectionException
*/
function wpml_st_blog_title_filter( $val ) {
/** @var WPML_ST_Blog_Name_And_Description_Hooks $filter */
$filter = make( WPML_ST_Blog_Name_And_Description_Hooks::class );
return $filter->option_blogname_filter( $val );
}
/**
* @deprecated since WPML ST 3.0.0
*
* @param string $val
*
* @return string
* @throws \WPML\Auryn\InjectionException
*/
function wpml_st_blog_description_filter( $val ) {
/** @var WPML_ST_Blog_Name_And_Description_Hooks $filter */
$filter = make( WPML_ST_Blog_Name_And_Description_Hooks::class );
return $filter->option_blogdescription_filter( $val );
}

View File

@@ -0,0 +1,180 @@
<?php
class WPML_Localization {
/**
* @var \wpdb
*/
private $wpdb;
/**
* WPML_Localization constructor.
*
* @param wpdb $wpdb
*/
public function __construct( wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
public function get_theme_localization_stats( $theme_localization_domains = array() ) {
if ( empty( $theme_localization_domains ) || ! is_array( $theme_localization_domains ) ) {
$theme_localization_domains = icl_get_sub_setting( 'st', 'theme_localization_domains' );
}
return $this->get_domain_stats( $theme_localization_domains, 'theme' );
}
public function get_domain_stats( $localization_domains, $default, $no_wordpress = false, $count_in_progress_as_completed = false ) {
$results = array();
if ( $localization_domains ) {
$domains = array();
foreach ( (array) $localization_domains as $domain ) {
if ( ! ( $no_wordpress && 'WordPress' === $domain ) ) {
$domains[] = $domain ? $domain : $default;
}
}
if ( ! empty( $domains ) ) {
$sql = "SELECT context, status, COUNT(id) AS c
FROM {$this->wpdb->prefix}icl_strings
WHERE context IN ('" . join( "','", $domains ) . "')
GROUP BY context, status";
$results = $this->wpdb->get_results( $sql );
}
}
return $this->results_to_array( $results, $count_in_progress_as_completed );
}
public function get_localization_stats( $component_type ) {
$localization_data = $this->get_localization_data( $component_type );
$results = array();
$all_domains = array();
foreach ( $localization_data as $component => $localization_domains ) {
$all_domains = array_merge( $all_domains, array_keys( $localization_domains ) );
}
$all_results = $this->get_domain_stats( $all_domains, $component_type, true );
foreach ( $localization_data as $component => $localization_domains ) {
$domains = array_keys( $localization_domains );
foreach ( $domains as $domain ) {
if ( array_key_exists( $domain, $all_results ) ) {
$results[ $component ][ $domain ] = $all_results[ $domain ];
}
}
}
return $results;
}
private function get_localization_data( $component_type ) {
$localization_data = apply_filters( 'wpml_sub_setting', array(), 'st', 'plugin' === $component_type ? 'plugin_localization_domains' : 'theme_localization_domains' );
if ( ! is_array( current( $localization_data ) ) ) {
if ( 'plugin' === $component_type ) {
return array();
}
$localization_data = array();
foreach ( wp_get_themes() as $theme_folder => $theme ) {
if ( $theme->get( 'TextDomain' ) ) {
$localization_data[ $theme_folder ] = array( $theme->get( 'TextDomain' ) => 0 );
}
}
}
return $localization_data;
}
public function get_wrong_plugin_localization_stats() {
$results = $this->wpdb->get_results(
"
SELECT context, status, COUNT(id) AS c
FROM {$this->wpdb->prefix}icl_strings
WHERE context LIKE ('plugin %')
GROUP BY context, status
"
);
return $this->results_to_array( $results );
}
public function get_wrong_theme_localization_stats() {
$results = $this->wpdb->get_results(
"
SELECT context, status, COUNT(id) AS c
FROM {$this->wpdb->prefix}icl_strings
WHERE context LIKE ('theme %')
GROUP BY context, status
"
);
$results = $this->results_to_array( $results );
$theme_path = TEMPLATEPATH;
$old_theme_context = 'theme ' . basename( $theme_path );
unset( $results[ $old_theme_context ] );
return $results;
}
public function does_theme_require_rescan() {
$theme_path = TEMPLATEPATH;
$old_theme_context = 'theme ' . basename( $theme_path );
$result = $this->wpdb->get_var(
$this->wpdb->prepare(
"
SELECT COUNT(id) AS c
FROM {$this->wpdb->prefix}icl_strings
WHERE context = %s",
$old_theme_context
)
);
return $result ? true : false;
}
public function get_most_popular_domain( $plugin ) {
$plugin_localization_domains = icl_get_sub_setting( 'st', 'plugin_localization_domains' );
$most_popular = '';
$most_count = 0;
foreach ( $plugin_localization_domains[ $plugin ] as $name => $count ) {
if ( $name == 'WordPress' || $name == 'default' ) {
continue;
}
if ( $count > $most_count ) {
$most_popular = $name;
$most_count = $count;
}
}
return $most_popular;
}
private function results_to_array( $results, $count_in_progress_as_completed = false ) {
$stats = array();
foreach ( $results as $r ) {
if ( ! isset( $stats[ $r->context ]['complete'] ) ) {
$stats[ $r->context ]['complete'] = 0;
}
if ( ! isset( $stats[ $r->context ]['incomplete'] ) ) {
$stats[ $r->context ]['incomplete'] = 0;
}
if (
$r->status == ICL_TM_COMPLETE ||
( $count_in_progress_as_completed && $r->status == ICL_TM_IN_PROGRESS )
) {
$stats[ $r->context ]['complete'] += $r->c;
} else {
$stats[ $r->context ]['incomplete'] += $r->c;
}
}
return $stats;
}
}

View File

@@ -0,0 +1,203 @@
<?php
class WPML_ST_String_Update {
private $wpdb;
/**
* WPML_ST_String_Update constructor.
*
* @param wpdb $wpdb
*/
public function __construct( wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
/**
* Updates an original string without changing its id or its translations
*
* @param string $domain
* @param string $name
* @param string $old_value
* @param string $new_value
* @param bool|false $force_complete , @see \WPML_ST_String_Update::handle_status_change
*
* @return int|null
*/
public function update_string( $domain, $name, $old_value, $new_value, $force_complete = false ) {
if ( $new_value != $old_value ) {
$string = $this->get_initial_string( $name, $domain, $old_value, $new_value );
$this->wpdb->update(
$this->wpdb->prefix . 'icl_strings',
array( 'value' => $new_value ),
array( 'id' => $string->id )
);
$is_widget = $domain === WPML_ST_WIDGET_STRING_DOMAIN;
if ( $is_widget && $new_value ) {
$this->update_widget_name( $string->name, $old_value, $new_value );
}
$this->handle_status_change( $string, $force_complete || $is_widget );
/**
* This action is fired when a string original value is modified.
*
* @since 3.0.0
*
* @param string $domain
* @param string $name
* @param string $old_value
* @param string $new_value
* @param bool|false $force_complete
* @param stdClass $string
*/
do_action( 'wpml_st_update_string', $domain, $name, $old_value, $new_value, $force_complete, $string );
}
return isset( $string ) && isset( $string->id ) ? $string->id : null;
}
/**
* @param string $string
* @return string
*/
function sanitize_string( $string ) {
return html_entity_decode( $string, ENT_QUOTES );
}
/**
* Handles string status changes resulting from the string update
*
* @param object $string
* @param bool $force_complete if true, all translations will be marked as complete even though a string's original value has been updated,
* currently this applies to blogname and tagline strings
*/
private function handle_status_change( $string, $force_complete ) {
if ( $string->status == ICL_TM_COMPLETE || $string->status == ICL_STRING_TRANSLATION_PARTIAL ) {
$new_status = $force_complete ? ICL_TM_COMPLETE : ICL_TM_NEEDS_UPDATE;
foreach (
array(
'icl_string_translations' => 'string_id',
'icl_strings' => 'id',
) as $table_name => $id_col
) {
$this->wpdb->update(
$this->wpdb->prefix . $table_name,
array( 'status' => $new_status ),
array( $id_col => $string->id )
);
}
}
}
/**
* @param string $name
* @param string $context
* @param string $old_value
* @param string $new_value
*
* @return object
*/
private function get_initial_string( $name, $context, $old_value, $new_value ) {
$string = $this->read_string_from_db( $name, $context );
if ( ! $string ) {
if ( $context !== WPML_ST_WIDGET_STRING_DOMAIN ) {
icl_register_string( $context, $name, $new_value );
} else {
list( $res, $name ) = $this->update_widget_name( $name, $old_value, $new_value );
if ( ! $res ) {
icl_register_string( $context, $name, $new_value );
}
}
}
$string = $this->read_string_from_db( $name, $context );
return $string;
}
/**
* Reads a strings id,value,status and name directly from the database without any caching.
*
* @param string $name
* @param string $context
*
* @return object|null
*/
private function read_string_from_db( $name, $context ) {
return $this->wpdb->get_row(
$this->wpdb->prepare(
" SELECT id, value, status, name
FROM {$this->wpdb->prefix}icl_strings
WHERE context = %s
AND name = %s
LIMIT 1",
$context,
$name
)
);
}
/**
* Updates a widgets string name if it's value got changed, since widget string's name and value are coupled.
* Changes in value necessitate changes in the name. @see \icl_sw_filters_widget_title and \icl_sw_filters_widget_body
*
* @param string $name
* @param string $old_value
* @param string $new_value
*
* @return array
*/
private function update_widget_name( $name, $old_value, $new_value ) {
$res = 0;
if ( 0 === strpos( $name, 'widget title - ' ) ) {
$name = 'widget title - ' . md5( $new_value );
$old_name = 'widget title - ' . md5( $old_value );
if ( $this->read_string_from_db( $name, WPML_ST_WIDGET_STRING_DOMAIN ) ) {
$old_string = $this->read_string_from_db( $old_name, WPML_ST_WIDGET_STRING_DOMAIN );
if ( $old_string ) {
$this->delete_old_widget_title_string_if_new_already_exists( $old_string );
}
} else {
$res = $this->write_widget_update_to_db( WPML_ST_WIDGET_STRING_DOMAIN, $old_name, $name );
}
} elseif ( 0 === strpos( $name, 'widget body - ' ) ) {
$name = 'widget body - ' . md5( $new_value );
$res = $this->write_widget_update_to_db(
WPML_ST_WIDGET_STRING_DOMAIN,
'widget body - ' . md5( $old_value ),
$name
);
}
return array( $res, $name );
}
/**
* Writes updates to a widget strings name to the icl_strings table.
*
* @param string $context
* @param string $old_name
* @param string $new_name
*
* @return false|int false on error, 1 on successful update and 0 if no update took place
*/
private function write_widget_update_to_db( $context, $old_name, $new_name ) {
return $this->wpdb->update(
$this->wpdb->prefix . 'icl_strings',
array(
'name' => $new_name,
'domain_name_context_md5' => md5( WPML_ST_WIDGET_STRING_DOMAIN . $new_name ),
),
array(
'context' => $context,
'name' => $old_name,
)
);
}
private function delete_old_widget_title_string_if_new_already_exists( $string ) {
$this->wpdb->delete( $this->wpdb->prefix . 'icl_string_translations', array( 'string_id' => $string->id ) );
$this->wpdb->delete( $this->wpdb->prefix . 'icl_strings', array( 'id' => $string->id ) );
}
}

View File

@@ -0,0 +1,947 @@
<?php
/**
* WPML_String_Translation class file.
*
* @package WPML\ST
*/
use WPML\ST\Gettext\AutoRegisterSettings;
use WPML\ST\StringsFilter\Translator;
use function WPML\Container\make;
/**
* Class WPML_String_Translation
*/
class WPML_String_Translation {
const CACHE_GROUP = 'wpml-string-translation';
private $load_priority = 400;
private $messages = array();
private $string_filters = array();
private $active_languages;
private $current_string_language_cache = array();
/** @var WPML_ST_String_Factory $string_factory */
private $string_factory;
/**
* @var string
*/
private $admin_language;
/**
* @var bool
*/
private $is_admin_action_from_referer;
/** @var SitePress $sitepress */
protected $sitepress;
/**
* @var WPML_WP_Cache
*/
private $cache;
/**
* @param SitePress $sitepress
* @param WPML_ST_String_Factory $string_factory
*/
public function __construct( SitePress $sitepress, WPML_ST_String_Factory $string_factory ) {
$this->sitepress = $sitepress;
$this->string_factory = $string_factory;
}
/**
* Sets up basic actions hooked by ST
*/
public function set_basic_hooks() {
if ( $this->sitepress->get_wp_api()->constant( 'WPML_TM_VERSION' ) ) {
add_action( 'wpml_tm_loaded', array( $this, 'load' ) );
} else {
add_action(
'wpml_loaded',
array( $this, 'load' ),
$this->load_priority
);
}
add_action(
'plugins_loaded',
array( $this, 'check_db_for_gettext_context' ),
1000
);
add_action(
'wpml_language_has_switched',
array( $this, 'wpml_language_has_switched' )
);
}
/**
* Populates the internal cache for all language codes.
*
* @used-by WPML_String_Translation::get_string_filter to not load string filters
* for languages that do not
* exist.
* @used-by WPML_String_Translation::get_admin_string_filter See above.
*/
function init_active_languages() {
$this->active_languages = array_keys( $this->sitepress->get_languages() );
}
function load() {
global $sitepress, $wpdb;
if ( ! $sitepress || ! $sitepress->get_setting( 'setup_complete' ) ) {
return;
}
$this->plugin_localization();
$factory = new WPML_ST_Upgrade_Command_Factory( $wpdb, $sitepress );
$upgrade = new WPML_ST_Upgrade( $sitepress, $factory );
$upgrade->run();
$this->init_active_languages();
$wpml_string_shortcode = new WPML\ST\Shortcode( $wpdb );
$wpml_string_shortcode->init_hooks();
wpml_st_load_admin_texts();
add_action( 'init', array( $this, 'init' ) );
$action_filter_loader = new WPML_Action_Filter_Loader();
$action_filter_loader->load(
array(
'WPML_Slug_Translation_Factory',
)
);
add_filter( 'pre_update_option_blogname', array( $this, 'pre_update_option_blogname' ), 5, 2 );
add_filter( 'pre_update_option_blogdescription', array( $this, 'pre_update_option_blogdescription' ), 5, 2 );
// Handle Admin Notices
add_action( 'icl_ajx_custom_call', array( $this, 'ajax_calls' ), 10, 2 );
/**
* @deprecated 3.3 - Each string has its own language now.
*/
add_filter( 'WPML_ST_strings_language', array( $this, 'get_strings_language' ) );
add_filter( 'wpml_st_strings_language', array( $this, 'get_strings_language' ) );
add_action( 'wpml_st_delete_all_string_data', array( $this, 'delete_all_string_data' ), 10, 1 );
add_filter( 'wpml_st_string_status', array( $this, 'get_string_status_filter' ), 10, 2 );
add_filter( 'wpml_string_id', array( $this, 'get_string_id_filter' ), 10, 2 );
add_filter( 'wpml_get_string_language', array( $this, 'get_string_language_filter' ), 10, 3 );
do_action( 'wpml_st_loaded' );
}
function init() {
global $wpdb, $sitepress;
if ( is_admin() ) {
wp_enqueue_style( 'thickbox' );
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'thickbox' );
$reset = new WPML_ST_Reset( $wpdb );
add_action( 'wpml_reset_plugins_after', array( $reset, 'reset' ) );
}
add_action( 'wpml_admin_menu_configure', array( $this, 'menu' ) );
add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
$current_page = array_key_exists( 'page', $_GET ) ? $_GET['page'] : '';
if ( $current_page && is_admin() ) {
$allowed_pages_for_resources = array( WPML_ST_FOLDER . '/menu/string-translation.php' );
if ( in_array( $current_page, $allowed_pages_for_resources, true ) && current_user_can( 'manage_options' ) && empty( $_POST ) ) {
wp_enqueue_script( 'wpml-st-change-lang', WPML_ST_URL . '/res/js/change_string_lang.js', array( 'jquery', 'jquery-ui-dialog', 'wpml-st-scripts' ), WPML_ST_VERSION );
}
$allowed_pages_for_resources[] = ICL_PLUGIN_FOLDER . '/menu/theme-localization.php';
if ( in_array( $current_page, $allowed_pages_for_resources, true ) ) {
wp_enqueue_script( 'wp-color-picker' );
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wpml-st-settings', WPML_ST_URL . '/res/js/settings.js', array( 'jquery' ), WPML_ST_VERSION );
wp_enqueue_script( 'wpml-st-scripts', WPML_ST_URL . '/res/js/scripts.js', array( 'jquery', 'jquery-ui-dialog' ), WPML_ST_VERSION );
wp_enqueue_script( OTGS_Assets_Handles::POPOVER_TOOLTIP );
wp_enqueue_style( OTGS_Assets_Handles::POPOVER_TOOLTIP );
wp_enqueue_script( 'wpml-auto-register-strings', WPML_ST_URL . '/res/js/auto-register-strings.js', array( 'jquery', 'jquery-ui-dialog', 'wpml-st-scripts' ), WPML_ST_VERSION );
wp_enqueue_script( 'wpml-st-change-domian-lang', WPML_ST_URL . '/res/js/change_string_domain_lang.js', array( 'jquery', 'jquery-ui-dialog' ), WPML_ST_VERSION );
wp_enqueue_script( 'wpml-st-translation_basket', WPML_ST_URL . '/res/js/wpml_string_translation_basket.js', array( 'jquery' ), WPML_ST_VERSION );
wp_enqueue_script( 'wpml-plugin-list-table-filter', WPML_ST_URL . '/res/js/wpml-plugin-list-table-filter.js', array( 'jquery' ), WPML_ST_VERSION );
wp_enqueue_style( 'wpml-st-styles', WPML_ST_URL . '/res/css/style.css', array(), WPML_ST_VERSION );
wp_enqueue_style( 'wpml-dialog', ICL_PLUGIN_URL . '/res/css/dialog.css', array( 'otgs-dialogs' ), ICL_SITEPRESS_VERSION );
wp_enqueue_style( 'wp-jquery-ui-dialog' );
}
}
add_action( 'wpml_custom_localization_type', array( $this, 'localization_type_ui' ) );
add_action( 'wp_ajax_st_theme_localization_rescan', array( $this, 'scan_theme_for_strings' ) );
add_action( 'wp_ajax_st_plugin_localization_rescan', array( $this, 'scan_plugins_for_strings' ) );
add_action( 'wp_ajax_icl_st_pop_download', array( $this, 'plugin_po_file_download' ) );
add_action( 'wp_ajax_wpml_change_string_lang', array( $this, 'change_string_lang_ajax_callback' ) );
add_action( 'wp_ajax_wpml_change_string_lang_of_domain', array( $this, 'change_string_lang_of_domain_ajax_callback' ) );
// auto-registration settings: saving excluded contexts
/** @var AutoRegisterSettings $auto_register_settings */
$auto_register_settings = WPML\Container\make( AutoRegisterSettings::class );
add_action( 'wp_ajax_wpml_st_exclude_contexts', array( $auto_register_settings, 'saveExcludedContexts' ) );
return true;
}
function plugin_localization() {
load_plugin_textdomain( 'wpml-string-translation', false, WPML_ST_FOLDER . '/locale' );
}
/**
* @param string $context
* @param string $name
* @param string|false $original_value
* @param boolean|null $has_translation
* @param null|string $target_lang
*
* @return string|bool
* @since 2.2.3
*
*/
function translate_string( $context, $name, $original_value = false, &$has_translation = null, $target_lang = null ) {
return icl_translate( $context, $name, $original_value, false, $has_translation, $target_lang );
}
function add_message( $text, $type = 'updated' ) {
$this->messages[] = array(
'type' => $type,
'text' => $text,
);
}
function show_messages() {
if ( ! empty( $this->messages ) ) {
foreach ( $this->messages as $m ) {
printf( '<div class="%s fade"><p>%s</p></div>', $m['type'], $m['text'] );
}
}
}
function ajax_calls( $call, $data ) {
require_once WPML_ST_PATH . '/inc/admin-texts/wpml-admin-text-configuration.php';
switch ( $call ) {
case 'icl_st_delete_strings':
$arr = explode( ',', $data['value'] );
wpml_unregister_string_multi( $arr );
echo '1';
break;
}
}
/**
* @param string $menu_id
*/
function menu( $menu_id ) {
if ( 'WPML' !== $menu_id ) {
return;
}
if ( ! $this->sitepress || ! $this->sitepress->get_wp_api()->constant( 'ICL_PLUGIN_PATH' ) ) {
return;
}
$setup_complete = apply_filters( 'wpml_get_setting', false, 'setup_complete' );
if ( ! $setup_complete ) {
return;
}
global $wpdb;
$existing_content_language_verified = apply_filters(
'wpml_get_setting',
false,
'existing_content_language_verified'
);
if ( ! $existing_content_language_verified ) {
return;
}
if ( current_user_can( 'wpml_manage_string_translation' ) || current_user_can( 'manage_translations' ) ) {
$menu = array();
$menu['order'] = 800;
$menu['page_title'] = __( 'String Translation', 'wpml-string-translation' );
$menu['menu_title'] = __( 'String Translation', 'wpml-string-translation' );
$menu['capability'] = current_user_can( 'wpml_manage_string_translation' ) ? 'wpml_manage_string_translation' : 'manage_translations';
$menu['menu_slug'] = WPML_ST_FOLDER . '/menu/string-translation.php';
do_action( 'wpml_admin_menu_register_item', $menu );
}
}
function plugin_action_links( $links, $file ) {
$this_plugin = basename( WPML_ST_PATH ) . '/plugin.php';
if ( $file == $this_plugin ) {
$links[] = '<a href="admin.php?page=' . WPML_ST_FOLDER . '/menu/string-translation.php">' .
__( 'Configure', 'wpml-string-translation' ) . '</a>';
}
return $links;
}
public function localization_type_ui() {
$plugin_localization_factory = new WPML_ST_Plugin_Localization_UI_Factory();
$plugin_localization = $plugin_localization_factory->create();
$theme_localization_factory = new WPML_ST_Theme_Localization_UI_Factory();
$theme_localization = $theme_localization_factory->create();
$localization = new WPML_Theme_Plugin_Localization_UI();
echo $localization->render( $theme_localization );
echo $localization->render( $plugin_localization );
}
function scan_theme_for_strings() {
require_once WPML_ST_PATH . '/inc/gettext/wpml-theme-string-scanner.class.php';
$file_hashing = new WPML_ST_File_Hashing();
$scan_for_strings = new WPML_Theme_String_Scanner( wpml_get_filesystem_direct(), $file_hashing );
$scan_for_strings->scan();
}
function scan_plugins_for_strings() {
require_once WPML_ST_PATH . '/inc/gettext/wpml-plugin-string-scanner.class.php';
$file_hashing = new WPML_ST_File_Hashing();
$scan_for_strings = new WPML_Plugin_String_Scanner( wpml_get_filesystem_direct(), $file_hashing );
$scan_for_strings->scan();
}
function plugin_po_file_download( $file = false, $recursion = 0 ) {
global $__wpml_st_po_file_content;
if ( empty( $file ) && ! empty( $_GET['file'] ) ) {
$file = WPML_PLUGINS_DIR . '/' . filter_var( $_GET['file'], FILTER_SANITIZE_STRING );
}
if ( empty( $file ) && ! wpml_st_file_path_is_valid( $file ) ) {
return;
}
if ( is_null( $__wpml_st_po_file_content ) ) {
$__wpml_st_po_file_content = '';
}
require_once WPML_ST_PATH . '/inc/potx.php';
require_once WPML_ST_PATH . '/inc/potx-callback.php';
if ( is_file( $file ) && WPML_PLUGINS_DIR == dirname( $file ) ) {
_potx_process_file( $file, 0, 'wpml_st_pos_scan_store_results', '_potx_save_version', '' );
} else {
if ( ! $recursion ) {
$file = dirname( $file );
}
if ( is_dir( $file ) ) {
$dh = opendir( $file );
while ( $dh && false !== ( $f = readdir( $dh ) ) ) {
if ( 0 === strpos( $f, '.' ) ) {
continue;
}
$this->plugin_po_file_download( $file . '/' . $f, $recursion + 1 );
}
} elseif ( preg_match( '#(\.php|\.inc)$#i', $file ) ) {
_potx_process_file( $file, 0, 'wpml_st_pos_scan_store_results', '_potx_save_version', '' );
}
}
if ( ! $recursion ) {
$po = WPML_PO_Parser::get_po_file_header();
$po .= $__wpml_st_po_file_content;
$filename = isset( $_GET['domain'] ) ?
filter_var( $_GET['domain'], FILTER_SANITIZE_STRING ) :
basename( $file );
header( 'Content-Type: application/force-download' );
header( 'Content-Type: application/octet-stream' );
header( 'Content-Type: application/download' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Content-Disposition: attachment; filename="' . $filename . '.po"' );
header( 'Content-Length: ' . strlen( $po ) );
echo $po;
exit( 0 );
}
}
/**
* @param string $string value of a string
* @param string $lang_code language code of the string
*
* @return int number of words in the string
*/
public function estimate_word_count( $string, $lang_code ) {
$string = strip_tags( $string );
return in_array(
$lang_code,
array(
'ja',
'ko',
'zh-hans',
'zh-hant',
'mn',
'ne',
'hi',
'pa',
'ta',
'th',
)
) ? strlen( $string ) / 6
: count( explode( ' ', $string ) );
}
function cancel_remote_translation( $rid ) {
/** @var wpdb $wpdb */
global $wpdb;
$translation_ids = $wpdb->get_col(
$wpdb->prepare(
" SELECT string_translation_id
FROM {$wpdb->prefix}icl_string_status
WHERE rid = %d",
$rid
)
);
$cancel_count = 0;
foreach ( $translation_ids as $translation_id ) {
$res = (bool) $this->cancel_local_translation( $translation_id );
$cancel_count = $res ? $cancel_count + 1 : $cancel_count;
}
return $cancel_count;
}
function cancel_local_translation( $id, $return_original_id = false ) {
global $wpdb;
$string_id = $wpdb->get_var(
$wpdb->prepare(
" SELECT string_id
FROM {$wpdb->prefix}icl_string_translations
WHERE id=%d AND status IN (%d, %d)",
$id,
ICL_TM_IN_PROGRESS,
ICL_TM_WAITING_FOR_TRANSLATOR
)
);
if ( $string_id ) {
$wpdb->update(
$wpdb->prefix . 'icl_string_translations',
array(
'status' => ICL_TM_NOT_TRANSLATED,
'translation_service' => null,
'translator_id' => null,
'batch_id' => null,
),
array( 'id' => $id )
);
icl_update_string_status( $string_id );
$res = $return_original_id ? $string_id : $id;
} else {
$res = false;
}
return $res;
}
/**
* @param string $value
* @param string $old_value
*
* @return array|string
*/
function pre_update_option_blogname( $value, $old_value ) {
return $this->pre_update_option_settings(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGNAME,
$value,
$old_value
);
}
/**
* @param string $value
* @param string $old_value
*
* @return array|string
*/
function pre_update_option_blogdescription( $value, $old_value ) {
return $this->pre_update_option_settings(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGDESCRIPTION,
$value,
$old_value
);
}
/**
* @param string $option name of the option
* @param string|array $value new value of the option
* @param string|array $old_value currently saved value for the option
*
* @return string|array the value actually to be written into the wp_options table
*/
function pre_update_option_settings( $option, $value, $old_value ) {
$wp_api = $this->sitepress->get_wp_api();
if ( $wp_api->is_multisite()
&& $wp_api->ms_is_switched()
&& ! $this->sitepress->get_setting( 'setup_complete' )
) {
return $value;
}
$option = new WPML_ST_Admin_Blog_Option(
$this->sitepress,
$this,
$option
);
return $option->pre_update_filter( $old_value, $value );
}
/**
* Instantiates a new admin option translation object
*
* @param string $option_name
* @param string $language_code
*
* @return WPML_ST_Admin_Option_Translation
*/
public function get_admin_option( $option_name, $language_code = '' ) {
return new WPML_ST_Admin_Option_Translation(
$this->sitepress,
$this,
$option_name,
$language_code
);
}
/**
* @return WPML_ST_String_Factory
*/
public function string_factory() {
return $this->string_factory;
}
/**
* @param string $lang_code
*/
public function clear_string_filter( $lang_code ) {
unset( $this->string_filters[ $lang_code ] );
}
/**
* @param string $lang
*
* @return WPML_Displayed_String_Filter
*/
public function get_string_filter( $lang ) {
if ( true === (bool) $this->active_languages && in_array( $lang, $this->active_languages, true ) ) {
return $this->get_admin_string_filter( $lang );
} else {
return null;
}
}
/**
* @param string $lang
*
* @return mixed|\WPML_Register_String_Filter|null
* @throws \WPML\Auryn\InjectionException
*/
public function get_admin_string_filter( $lang ) {
global $sitepress_settings, $wpdb, $sitepress;
if ( isset( $sitepress_settings['st']['db_ok_for_gettext_context'] ) ) {
if ( ! ( isset( $this->string_filters[ $lang ] )
&& 'WPML_Register_String_Filter' == get_class( $this->string_filters[ $lang ] ) )
) {
$this->string_filters[ $lang ] = isset( $this->string_filters[ $lang ] ) ? $this->string_filters[ $lang ] : false;
/** @var AutoRegisterSettings $auto_register_settings */
$auto_register_settings = WPML\Container\make( AutoRegisterSettings::class );
$this->string_filters[ $lang ] = new WPML_Register_String_Filter(
$wpdb,
$sitepress,
$this->string_factory,
make( Translator::class, [ ':language' => $lang ] ),
$auto_register_settings->getExcludedDomains()
);
}
return $this->string_filters[ $lang ];
} else {
return null;
}
}
/**
* @deprecated 3.3 - Each string has its own language now.
*/
public function get_strings_language( $language = '' ) {
$string_settings = $this->get_strings_settings();
$string_language = $language ? $language : 'en';
if ( isset( $string_settings['strings_language'] ) ) {
$string_language = $string_settings['strings_language'];
}
return $string_language;
}
public function delete_all_string_data( $string_id ) {
global $wpdb;
$icl_string_positions_query = "DELETE FROM {$wpdb->prefix}icl_string_positions WHERE string_id=%d";
$icl_string_status_query = "DELETE FROM {$wpdb->prefix}icl_string_status WHERE string_translation_id IN (SELECT id FROM {$wpdb->prefix}icl_string_translations WHERE string_id=%d)";
$icl_string_translations_query = "DELETE FROM {$wpdb->prefix}icl_string_translations WHERE string_id=%d";
$icl_strings_query = "DELETE FROM {$wpdb->prefix}icl_strings WHERE id=%d";
$icl_string_positions_prepare = $wpdb->prepare( $icl_string_positions_query, $string_id );
$icl_string_status_prepare = $wpdb->prepare( $icl_string_status_query, $string_id );
$icl_string_translations_prepare = $wpdb->prepare( $icl_string_translations_query, $string_id );
$icl_strings_prepare = $wpdb->prepare( $icl_strings_query, $string_id );
$wpdb->query( $icl_string_positions_prepare );
$wpdb->query( $icl_string_status_prepare );
$wpdb->query( $icl_string_translations_prepare );
$wpdb->query( $icl_strings_prepare );
}
public function get_strings_settings() {
global $sitepress;
if ( version_compare( ICL_SITEPRESS_VERSION, '3.2', '<' ) ) {
global $sitepress_settings;
$string_settings = isset( $sitepress_settings['st'] ) ? $sitepress_settings['st'] : array();
} else {
$string_settings = $sitepress ? $sitepress->get_string_translation_settings() : array();
}
$string_settings['strings_language'] = 'en';
if ( ! isset( $string_settings['icl_st_auto_reg'] ) ) {
$string_settings['icl_st_auto_reg'] = 'disable';
}
$string_settings['strings_per_page'] = ICL_STRING_TRANSLATION_AUTO_REGISTER_THRESHOLD;
return $string_settings;
}
/**
* @param null $empty Not used, but needed for the hooked filter
* @param int $string_id
*
* @return null|string
*/
public function get_string_status_filter( $empty = null, $string_id = 0 ) {
return $this->get_string_status( $string_id );
}
/**
* @param int|null $default Set the default value to return in case no string or more than one string is found
* @param array $string_data {
*
* @type string $context
* @type string $name Optional
* }
* @return int|null If there is more than one string_id, it will return the value set in $default.
*/
public function get_string_id_filter( $default = null, $string_data = array() ) {
$result = $default;
$string_id = $this->get_string_id( $string_data );
return $string_id ? $string_id : $result;
}
private function get_string_status( $string_id ) {
global $wpdb;
$status = $wpdb->get_var(
$wpdb->prepare(
"
SELECT MIN(status)
FROM {$wpdb->prefix}icl_string_translations
WHERE
string_id=%d
",
$string_id
)
);
return $status !== null ? (int) $status : null;
}
/**
* @param array $string_data {
*
* @type string $context
* @type string $name Optional
* }
* @return int|null
*/
private function get_string_id( $string_data ) {
$context = isset( $string_data['context'] ) ? $string_data['context'] : null;
$name = isset( $string_data['name'] ) ? $string_data['name'] : null;
$result = null;
if ( $name && $context ) {
global $wpdb;
$string_id_query = "SELECT id FROM {$wpdb->prefix}icl_strings WHERE context=%s";
$string_id_args = array( $context );
if ( $name ) {
$string_id_query .= ' AND name=%s';
$string_id_args[] = $name;
}
$string_id_prepare = $wpdb->prepare( $string_id_query, $string_id_args );
$string_id = $wpdb->get_var( $string_id_prepare );
$result = (int) $string_id;
}
return $result;
}
/**
* @param null $empty Not used, but needed for the hooked filter
* @param string $domain
* @param string $name
*
* @return null|string
*/
public function get_string_language_filter( $empty = null, $domain = '', $name = '' ) {
global $wpdb;
$key = md5( $domain . '_' . $name );
list( $string_lang, $found ) = $this->get_cache()->get_with_found( $key );
if ( ! $found ) {
$string_query = "SELECT language FROM {$wpdb->prefix}icl_strings WHERE context=%s AND name=%s";
$string_prepare = $wpdb->prepare( $string_query, $domain, $name );
$string_lang = $wpdb->get_var( $string_prepare );
$this->get_cache()->set( $key, $string_lang, 600 );
}
return $string_lang;
}
/**
* @param WPML_WP_Cache $cache
*/
public function set_cache( WPML_WP_Cache $cache ) {
$this->cache = $cache;
}
/**
* @return WPML_WP_Cache
*/
public function get_cache() {
if ( null === $this->cache ) {
$this->cache = new WPML_WP_Cache( self::CACHE_GROUP );
}
return $this->cache;
}
function check_db_for_gettext_context() {
$string_settings = apply_filters( 'wpml_get_setting', [], 'st' );
if ( ! isset( $string_settings['db_ok_for_gettext_context'] ) ) {
if ( function_exists( 'icl_table_column_exists' ) && icl_table_column_exists( 'icl_strings', 'domain_name_context_md5' ) ) {
$string_settings['db_ok_for_gettext_context'] = true;
do_action( 'wpml_set_setting', 'st', $string_settings, true );
}
}
}
public function initialize_wp_and_widget_strings() {
$this->check_db_for_gettext_context();
icl_register_string(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_DOMAIN,
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGNAME,
get_option( 'blogname' )
);
icl_register_string(
WPML_ST_Blog_Name_And_Description_Hooks::STRING_DOMAIN,
WPML_ST_Blog_Name_And_Description_Hooks::STRING_NAME_BLOGDESCRIPTION,
get_option( 'blogdescription' )
);
wpml_st_init_register_widget_titles();
// create a list of active widgets
$active_text_widgets = array();
$widgets = (array) get_option( 'sidebars_widgets' );
foreach ( $widgets as $k => $w ) {
if ( 'wp_inactive_widgets' != $k && $k != 'array_version' ) {
if ( is_array( $widgets[ $k ] ) ) {
foreach ( $widgets[ $k ] as $v ) {
if ( preg_match( '#text-([0-9]+)#i', $v, $matches ) ) {
$active_text_widgets[] = $matches[1];
}
}
}
}
}
$widget_text = get_option( 'widget_text' );
if ( is_array( $widget_text ) ) {
foreach ( $widget_text as $k => $w ) {
if ( ! empty( $w ) && isset( $w['title'], $w['text'] ) && in_array( $k, $active_text_widgets ) && $w['text'] ) {
icl_register_string( WPML_ST_WIDGET_STRING_DOMAIN, 'widget body - ' . md5( $w['text'] ), $w['text'] );
}
}
}
}
/**
* Returns the language the current string is to be translated into.
*
* @param string $name
*
* @return string
*/
public function get_current_string_language( $name ) {
if ( isset( $this->current_string_language_cache[ $name ] ) ) {
return $this->current_string_language_cache[ $name ];
}
$key = 'current_language';
$found = false;
$current_language = WPML_Non_Persistent_Cache::get( $key, 'WPML_String_Translation', $found );
if ( ! $found ) {
$wp_api = $this->sitepress->get_wp_api();
$current_language = $wp_api->constant( 'DOING_AJAX' )
&& $this->is_admin_action_from_referer()
? $this->sitepress->user_lang_by_authcookie()
: $this->sitepress->get_current_language();
WPML_Non_Persistent_Cache::set( $key, $current_language, 'WPML_String_Translation' );
}
if ( $this->should_use_admin_language()
&& ! WPML_ST_Blog_Name_And_Description_Hooks::is_string( $name ) ) {
$admin_display_lang = $this->get_admin_language();
$current_language = $admin_display_lang ? $admin_display_lang : $current_language;
}
$ret = apply_filters(
'icl_current_string_language',
$current_language,
$name
);
$this->current_string_language_cache[ $name ] = $ret === 'all'
? $this->sitepress->get_default_language() : $ret;
return $this->current_string_language_cache[ $name ];
}
public function should_use_admin_language() {
$key = 'should_use_admin_language';
$found = false;
$should_use_admin_language = WPML_Non_Persistent_Cache::get( $key, 'WPML_String_Translation', $found );
if ( ! $found ) {
$wp_api = $this->sitepress->get_wp_api();
$should_use_admin_language = $wp_api->constant( 'WP_ADMIN' ) && ( $this->is_admin_action_from_referer() || ! $wp_api->constant( 'DOING_AJAX' ) );
WPML_Non_Persistent_Cache::set( $key, $should_use_admin_language, 'WPML_String_Translation' );
}
return $should_use_admin_language;
}
/**
* @return string
*/
public function get_admin_language() {
if ( $this->sitepress->is_wpml_switch_language_triggered() ) {
return $this->sitepress->get_admin_language();
}
if ( ! $this->admin_language ) {
$this->admin_language = $this->sitepress->get_admin_language();
}
return $this->admin_language;
}
/**
* @return bool
*/
private function is_admin_action_from_referer() {
if ( $this->is_admin_action_from_referer === null ) {
$this->is_admin_action_from_referer = $this->sitepress->check_if_admin_action_from_referer();
}
return $this->is_admin_action_from_referer;
}
public function wpml_language_has_switched() {
// clear the current language cache
$this->current_string_language_cache = array();
}
public function change_string_lang_ajax_callback() {
if ( ! $this->verify_ajax_call( 'wpml_change_string_language_nonce' ) ) {
die( 'verification failed' );
}
global $wpdb;
$change_string_language_dialog = new WPML_Change_String_Language_Select( $wpdb, $this->sitepress );
$string_ids = array_map( 'intval', $_POST['strings'] );
$lang = filter_var( isset( $_POST['language'] ) ? $_POST['language'] : '', FILTER_SANITIZE_SPECIAL_CHARS );
$response = $change_string_language_dialog->change_language_of_strings( $string_ids, $lang );
wp_send_json( $response );
}
public function change_string_lang_of_domain_ajax_callback() {
if ( ! $this->verify_ajax_call( 'wpml_change_string_domain_language_nonce' ) ) {
die( 'verification failed' );
}
global $wpdb, $sitepress;
$change_string_language_domain_dialog = new WPML_Change_String_Domain_Language_Dialog( $wpdb, $sitepress, $this->string_factory );
$response = $change_string_language_domain_dialog->change_language_of_strings(
$_POST['domain'],
isset( $_POST['langs'] ) ? $_POST['langs'] : array(),
$_POST['language'],
$_POST['use_default'] == 'true'
);
wp_send_json( $response );
}
private function verify_ajax_call( $ajax_action ) {
return isset( $_POST['wpnonce'] ) && wp_verify_nonce( $_POST['wpnonce'], $ajax_action );
}
}