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,59 @@
<?php
namespace WPML\Troubleshooting;
class AssignTranslationStatusToDuplicates {
public static function run() {
global $sitepress, $iclTranslationManagement;
$active_language_codes = array_keys( $sitepress->get_active_languages() );
$duplicated_posts = self::get_duplicates();
$updated_items = 0;
foreach ( $duplicated_posts as $original_post_id ) {
$element_type = 'post_' . get_post_type( $original_post_id );
$trid = $sitepress->get_element_trid( $original_post_id, $element_type );
$element_language_details = $sitepress->get_element_translations( $trid, $element_type );
$item_updated = false;
foreach ( $active_language_codes as $code ) {
if ( ! isset( $element_language_details[ $code ] ) ) {
continue;
}
/** @var \stdClass $element_translation */
$element_translation = $element_language_details[ $code ];
if ( ! isset( $element_translation->element_id ) || $element_translation->original ) {
continue;
}
$translation = $iclTranslationManagement->get_element_translation(
$element_translation->element_id,
$code,
$element_type
);
if ( ! $translation ) {
$status_helper = wpml_get_post_status_helper();
$status_helper->set_status( $element_translation->element_id, ICL_TM_DUPLICATE );
$item_updated = true;
}
}
if ( $item_updated ) {
$updated_items ++;
}
if ( $updated_items >= 20 ) {
break;
}
}
return $updated_items;
}
/**
* @return array
*/
private static function get_duplicates() {
global $wpdb;
$duplicated_posts_sql = "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key='_icl_lang_duplicate_of' AND meta_value<>'' GROUP BY meta_value;";
return $wpdb->get_col( $duplicated_posts_sql );
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace WPML\TM\Troubleshooting;
use WPML\LIB\WP\Nonce;
use WPML\Core\WP\App\Resources;
use WPML\TM\ATE\AutoTranslate\Endpoint\CancelJobs;
use WPML\TM\ATE\Hooks\JobActionsFactory;
class Loader implements \IWPML_Backend_Action {
public function add_hooks() {
add_action( 'after_setup_complete_troubleshooting_functions', [ $this, 'render' ], 7 );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueueScripts' ] );
}
public function render() {
echo '<div id="wpml-troubleshooting-container" style="margin: 5px 0; width: 350px;"></div>';
}
public function enqueueScripts( $hook ) {
if ( WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' === $hook ) {
$enqueue = Resources::enqueueApp( 'troubleshooting' );
$enqueue(
[
'name' => 'troubleshooting',
'data' => [
'refreshLicense' => [
'nonce' => Nonce::create( 'update_site_key_wpml' ),
],
'endpoints' => [
'cancelJobs' => CancelJobs::class,
],
],
]
);
}
}
}

View File

@@ -0,0 +1,156 @@
<?php
class WPML_Debug_Information {
/** @var wpdb $wpdb */
public $wpdb;
/** @var SitePress $sitepress */
protected $sitepress;
/**
* @param wpdb $wpdb
* @param SitePress $sitepress
*/
public function __construct( $wpdb, $sitepress ) {
$this->wpdb = $wpdb;
$this->sitepress = $sitepress;
}
public function run() {
$info = array( 'core', 'plugins', 'theme', 'extra-debug' );
$output = array();
foreach ( $info as $type ) {
switch ( $type ) {
case 'core':
$output['core'] = $this->get_core_info();
break;
case 'plugins':
$output['plugins'] = $this->get_plugins_info();
break;
case 'theme':
$output['theme'] = $this->get_theme_info();
break;
case 'extra-debug':
$output['extra-debug'] = apply_filters( 'icl_get_extra_debug_info', array() );
break;
}
}
return $output;
}
function get_core_info() {
$core = array(
'Wordpress' => array(
'Multisite' => is_multisite() ? 'Yes' : 'No',
'SiteURL' => site_url(),
'HomeURL' => home_url(),
'Version' => get_bloginfo( 'version' ),
'PermalinkStructure' => get_option( 'permalink_structure' ),
'PostTypes' => implode( ', ', get_post_types( '', 'names' ) ),
'PostStatus' => implode( ', ', get_post_stati() ),
'RestEnabled' => wpml_is_rest_enabled() ? 'Yes' : 'No',
),
'Server' => array(
'jQueryVersion' => wp_script_is( 'jquery', 'registered' ) ? $GLOBALS['wp_scripts']->registered['jquery']->ver : __( 'n/a', 'bbpress' ),
'PHPVersion' => $this->sitepress->get_wp_api()->phpversion(),
'MySQLVersion' => $this->wpdb->db_version(),
'ServerSoftware' => $_SERVER['SERVER_SOFTWARE'],
),
'PHP' => array(
'MemoryLimit' => ini_get( 'memory_limit' ),
'WP Memory Limit' => WP_MEMORY_LIMIT,
'UploadMax' => ini_get( 'upload_max_filesize' ),
'PostMax' => ini_get( 'post_max_size' ),
'TimeLimit' => ini_get( 'max_execution_time' ),
'MaxInputVars' => ini_get( 'max_input_vars' ),
'MBString' => $this->sitepress->get_wp_api()->extension_loaded( 'mbstring' ),
'libxml' => $this->sitepress->get_wp_api()->extension_loaded( 'libxml' ),
),
);
return $core;
}
function get_plugins_info() {
$plugins = $this->sitepress->get_wp_api()->get_plugins();
$active_plugins = $this->sitepress->get_wp_api()->get_option( 'active_plugins' );
$active_plugins_info = array();
foreach ( $active_plugins as $plugin ) {
if ( isset( $plugins[ $plugin ] ) ) {
unset( $plugins[ $plugin ]['Description'] );
$active_plugins_info[ $plugin ] = $plugins[ $plugin ];
}
}
$mu_plugins = get_mu_plugins();
$dropins = get_dropins();
$output = array(
'active_plugins' => $active_plugins_info,
'mu_plugins' => $mu_plugins,
'dropins' => $dropins,
);
return $output;
}
function get_theme_info() {
if ( $this->sitepress->get_wp_api()->get_bloginfo( 'version' ) < '3.4' ) {
/** @var \WP_Theme $current_theme */
$current_theme = get_theme_data( get_stylesheet_directory() . '/style.css' );
$theme = $current_theme;
unset( $theme['Description'] );
unset( $theme['Status'] );
unset( $theme['Tags'] );
} else {
$theme = array(
'Name' => $this->sitepress->get_wp_api()->get_theme_name(),
'ThemeURI' => $this->sitepress->get_wp_api()->get_theme_URI(),
'Author' => $this->sitepress->get_wp_api()->get_theme_author(),
'AuthorURI' => $this->sitepress->get_wp_api()->get_theme_authorURI(),
'Template' => $this->sitepress->get_wp_api()->get_theme_template(),
'Version' => $this->sitepress->get_wp_api()->get_theme_version(),
'TextDomain' => $this->sitepress->get_wp_api()->get_theme_textdomain(),
'DomainPath' => $this->sitepress->get_wp_api()->get_theme_domainpath(),
'ParentName' => $this->sitepress->get_wp_api()->get_theme_parent_name(),
);
}
return $theme;
}
function do_json_encode( $data ) {
$json_options = 0;
if ( defined( 'JSON_HEX_TAG' ) ) {
$json_options += JSON_HEX_TAG;
}
if ( defined( 'JSON_HEX_APOS' ) ) {
$json_options += JSON_HEX_APOS;
}
if ( defined( 'JSON_HEX_QUOT' ) ) {
$json_options += JSON_HEX_QUOT;
}
if ( defined( 'JSON_HEX_AMP' ) ) {
$json_options += JSON_HEX_AMP;
}
if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) {
$json_options += JSON_UNESCAPED_UNICODE;
}
if ( version_compare( $this->sitepress->get_wp_api()->phpversion(), '5.3.0', '<' ) ) {
$json_data = wp_json_encode( $data );
} else {
$json_data = wp_json_encode( $data, $json_options );
}
return $json_data;
}
}

View File

@@ -0,0 +1,231 @@
<?php
class WPML_Fix_Type_Assignments extends WPML_WPDB_And_SP_User {
/**
* WPML_Fix_Type_Assignments constructor.
*
* @param SitePress $sitepress
*/
public function __construct( $sitepress ) {
$wpdb = $sitepress->wpdb();
parent::__construct( $wpdb, $sitepress );
}
/**
* Runs various database repair and cleanup actions on icl_translations.
*
* @return int Number of rows in icl_translations that were fixed
*/
public function run() {
$rows_fixed = $this->fix_broken_duplicate_rows();
$rows_fixed += $this->fix_missing_original();
$rows_fixed += $this->fix_wrong_source_language();
$rows_fixed += $this->fix_broken_taxonomy_assignments();
$rows_fixed += $this->fix_broken_post_assignments();
$rows_fixed += $this->fix_broken_type_assignments();
icl_cache_clear();
wp_cache_init();
return $rows_fixed;
}
/**
* Deletes rows from icl_translations that are duplicated in terms of their
* element id and within their meta type ( post,taxonomy,package ...),
* with the duplicate actually being of the correct type.
*
* @return int number of rows fixed
*/
private function fix_broken_duplicate_rows() {
$rows_fixed = $this->wpdb->query(
"
DELETE t
FROM {$this->wpdb->prefix}icl_translations i
JOIN {$this->wpdb->prefix}icl_translations t
ON i.element_id = t.element_id
AND SUBSTRING_INDEX(i.element_type, '_', 1) =
SUBSTRING_INDEX(t.element_type, '_', 1)
AND i.element_type != t.element_type
AND i.translation_id != t.translation_id
JOIN (SELECT
CONCAT('post_', p.post_type) AS element_type,
p.ID AS element_id
FROM {$this->wpdb->posts} p
UNION ALL
SELECT
CONCAT('tax_', tt.taxonomy) AS element_type,
tt.term_taxonomy_id AS element_id
FROM {$this->wpdb->term_taxonomy} tt) AS data
ON data.element_id = i.element_id
AND data.element_type = i.element_type"
);
if ( 0 < $rows_fixed ) {
do_action(
'wpml_translation_update',
array(
'type' => 'delete',
'rows_affected' => $rows_fixed,
)
);
}
return $rows_fixed;
}
/**
* Fixes all taxonomy term rows in icl_translations, which have a corrupted
* element_type set, different from the one actually set in the term_taxonomy
* table.
*
* @return int number of rows fixed
*/
private function fix_broken_taxonomy_assignments() {
$rows_fixed = $this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_translations t
JOIN {$this->wpdb->term_taxonomy} tt
ON tt.term_taxonomy_id = t.element_id
AND t.element_type LIKE 'tax%'
AND t.element_type <> CONCAT('tax_', tt.taxonomy)
SET t.element_type = CONCAT('tax_', tt.taxonomy)"
);
if ( 0 < $rows_fixed ) {
do_action(
'wpml_translation_update',
array(
'context' => 'tax',
'type' => 'element_type_update',
'rows_affected' => $rows_fixed,
)
);
}
return $rows_fixed;
}
/**
* Fixes all post rows in icl_translations, which have a corrupted
* element_type set, different from the one actually set in the wp_posts
* table.
*
* @return int number of rows fixed
*/
private function fix_broken_post_assignments() {
$rows_fixed = $this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_translations t
JOIN {$this->wpdb->posts} p
ON p.ID = t.element_id
AND t.element_type LIKE 'post%'
AND t.element_type <> CONCAT('post_', p.post_type)
SET t.element_type = CONCAT('post_', p.post_type)"
);
if ( 0 < $rows_fixed ) {
do_action(
'wpml_translation_update',
array(
'context' => 'tax',
'type' => 'element_type_update',
'rows_affected' => $rows_fixed,
)
);
}
return $rows_fixed;
}
/**
* Fixes all instances of a different element_type having been set for
* an original element and it's translation, by setting the original's type
* on the corrupted translation rows.
*
* @return int number of rows fixed
*/
private function fix_broken_type_assignments() {
$rows_fixed = $this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_translations t
JOIN {$this->wpdb->prefix}icl_translations c
ON c.trid = t.trid
AND c.language_code != t.language_code
SET t.element_type = c.element_type
WHERE c.source_language_code IS NULL
AND t.source_language_code IS NOT NULL"
);
if ( 0 < $rows_fixed ) {
do_action(
'wpml_translation_update',
array(
'type' => 'element_type_update',
'rows_affected' => $rows_fixed,
)
);
}
return $rows_fixed;
}
/**
* Fixes all rows that have an empty string instead of NULL or a source language
* equal to its actual language set by setting the source language to NULL.
*
* @return int number of rows fixed
*/
private function fix_wrong_source_language() {
return $this->wpdb->query(
"UPDATE {$this->wpdb->prefix}icl_translations
SET source_language_code = NULL
WHERE source_language_code = ''
OR source_language_code = language_code"
);
}
/**
* Fixes instances of the source element of a trid being missing, by assigning
* the oldest element ( determined by the lowest element_id ) as the original
* element in a trid.
*
* @return int number of rows fixed
*/
private function fix_missing_original() {
$broken_elements = $this->wpdb->get_results(
" SELECT MIN(iclt.element_id) AS element_id, iclt.trid
FROM {$this->wpdb->prefix}icl_translations iclt
LEFT JOIN {$this->wpdb->prefix}icl_translations iclo
ON iclt.trid = iclo.trid
AND iclo.source_language_code IS NULL
WHERE iclo.translation_id IS NULL
GROUP BY iclt.trid"
);
$rows_affected = 0;
foreach ( $broken_elements as $element ) {
$rows_affected_per_element = $this->wpdb->query(
$this->wpdb->prepare(
"UPDATE {$this->wpdb->prefix}icl_translations
SET source_language_code = NULL
WHERE trid = %d AND element_id = %d",
$element->trid,
$element->element_id
)
);
if ( 0 < $rows_affected_per_element ) {
do_action(
'wpml_translation_update',
array( 'trid' => $element->trid )
);
}
$rows_affected += $rows_affected_per_element;
}
return $rows_affected;
}
}

View File

@@ -0,0 +1,103 @@
<?php
class WPML_Table_Collate_Fix implements IWPML_AJAX_Action, IWPML_Backend_Action, IWPML_DIC_Action {
const AJAX_ACTION = 'fix_tables_collation';
/**
* @var wpdb
*/
private $wpdb;
/** @var WPML_Upgrade_Schema $schema */
private $schema;
public function __construct( wpdb $wpdb, WPML_Upgrade_Schema $schema ) {
$this->wpdb = $wpdb;
$this->schema = $schema;
}
public function add_hooks() {
add_action(
'wpml_troubleshooting_after_fix_element_type_collation',
array(
$this,
'render_troubleshooting_button',
)
);
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'fix_collate_ajax' ) );
add_action( 'upgrader_process_complete', array( $this, 'fix_collate' ), PHP_INT_MAX );
}
public function fix_collate_ajax() {
if ( isset( $_POST['nonce'] )
&& wp_verify_nonce( $_POST['nonce'], self::AJAX_ACTION )
) {
$this->fix_collate();
wp_send_json_success();
}
}
public function render_troubleshooting_button() {
?>
<p>
<input id="wpml_fix_tables_collation" type="button" class="button-secondary"
value="<?php _e( 'Fix WPML tables collation', 'sitepress' ); ?>"/><br/>
<?php wp_nonce_field( self::AJAX_ACTION, 'wpml-fix-tables-collation-nonce' ); ?>
<small style="margin-left:10px;"><?php esc_attr_e( 'Fixes the collation of WPML tables in order to match the collation of default WP tables.', 'sitepress' ); ?></small>
</p>
<?php
}
public function enqueue_scripts( $hook ) {
if ( WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' === $hook ) {
wp_enqueue_script( 'wpml-fix-tables-collation', ICL_PLUGIN_URL . '/res/js/fix-tables-collation.js', array( 'jquery' ), ICL_SITEPRESS_VERSION );
}
}
public function fix_collate() {
if ( did_action( 'upgrader_process_complete' ) > 1 ) {
return;
}
$wp_default_table_data = $this->wpdb->get_row(
$this->wpdb->prepare( 'SHOW TABLE status LIKE %s', $this->wpdb->posts )
);
if ( isset( $wp_default_table_data->Collation ) ) {
$charset = $this->schema->get_default_charset();
foreach ( $this->get_all_wpml_tables() as $table ) {
$table = reset( $table );
$table_data = $this->wpdb->get_row(
$this->wpdb->prepare( 'SHOW TABLE status LIKE %s', $table )
);
if ( isset( $table_data->Collation ) && $table_data->Collation !== $wp_default_table_data->Collation ) {
$this->wpdb->query(
$this->wpdb->prepare(
'ALTER TABLE ' . $table . ' CONVERT TO CHARACTER SET %s COLLATE %s',
$charset,
$wp_default_table_data->Collation
)
);
}
}
}
}
/**
* @return array
*/
private function get_all_wpml_tables() {
return $this->wpdb->get_results(
$this->wpdb->prepare(
'SHOW TABLES LIKE %s',
$this->wpdb->prefix . 'icl_%'
),
ARRAY_A
);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Class WPML_Troubleshoot_Action
*
* @author onTheGoSystems
*/
class WPML_Troubleshoot_Action {
const SYNC_POSTS_TAXONOMIES_SLUG = 'synchronize_posts_taxonomies';
/**
* @return bool
*/
public function is_valid_request() {
$response = false;
if ( array_key_exists( 'nonce', $_POST ) && array_key_exists( 'debug_action', $_POST )
&& self::SYNC_POSTS_TAXONOMIES_SLUG === $_POST['debug_action']
) {
$response = wp_verify_nonce( $_POST['nonce'], $_POST['debug_action'] );
if ( ! $response ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid nonce.', 'sitepress' ) ) );
return $response;
}
}
return $response;
}
}

View File

@@ -0,0 +1,91 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_Troubleshoot_Sync_Posts_Taxonomies
*/
class WPML_Troubleshoot_Sync_Posts_Taxonomies {
const BATCH_SIZE = 5;
/** @var SitePress $sitepress */
private $sitepress;
/** @var WPML_Term_Translation_Utils $term_translation_utils */
private $term_translation_utils;
public function __construct( SitePress $sitePress, WPML_Term_Translation_Utils $term_translation_utils ) {
$this->sitepress = $sitePress;
$this->term_translation_utils = $term_translation_utils;
}
public function run() {
if ( ! array_key_exists( 'post_type', $_POST ) || ! array_key_exists( 'batch_number', $_POST ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Some parameters are missing for this request.', 'sitepress' ) ) );
return;
}
$post_type = Sanitize::stringProp( 'post_type', $_POST );
$batch_number = (int) filter_var( $_POST['batch_number'], FILTER_SANITIZE_NUMBER_INT );
$posts = $this->get_posts_batch( $post_type, $batch_number );
$this->synchronize_batch( $posts );
$new_batch_number = $batch_number + 1;
$response_data = array(
'post_type' => $post_type,
'batch_number' => $new_batch_number,
'message' => sprintf( esc_html__( 'Running now batch #%d', 'sitepress' ), $new_batch_number ),
);
if ( count( $posts ) < self::BATCH_SIZE ) {
$total_posts_processed = ( $batch_number * self::BATCH_SIZE ) + count( $posts );
$response_data['completed'] = true;
$response_data['message'] = sprintf( esc_html__( 'Completed: %1$d posts were processed for "%2$s".', 'sitepress' ), $total_posts_processed, $post_type );
}
wp_send_json_success( $response_data );
}
/**
* @param string $type
* @param int $batch_number
*
* @return array
*/
private function get_posts_batch( $type, $batch_number ) {
$this->sitepress->switch_lang( $this->sitepress->get_default_language() );
$args = array(
'post_type' => $type,
'offset' => $batch_number * self::BATCH_SIZE,
'order_by' => 'ID',
'order' => 'ASC',
'posts_per_page' => self::BATCH_SIZE,
'suppress_filters' => false,
);
$posts = get_posts( $args );
$this->sitepress->switch_lang();
return $posts;
}
/**
* @param array $posts
*/
private function synchronize_batch( $posts ) {
$active_languages = $this->sitepress->get_active_languages();
foreach ( $active_languages as $language_code => $active_language ) {
foreach ( $posts as $post ) {
$this->term_translation_utils->sync_terms( $post->ID, $language_code );
}
}
}
}