Files
HTBD/includes/class-translation-store.php
T

218 lines
8.8 KiB
PHP

<?php
defined( 'ABSPATH' ) || exit;
class HTBD_Translation_Store {
public static function table_name() {
global $wpdb;
return $wpdb->prefix . 'hyx_bd_translations';
}
public static function install() {
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$table_name = self::table_name();
$legacy_table = $wpdb->prefix . 'btranslate_translations';
$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Checks the plugin-owned table during activation.
$legacy_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $legacy_table ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Checks for the legacy plugin-owned table during activation.
if ( $legacy_exists === $legacy_table && $table_exists !== $table_name ) {
$wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $legacy_table, $table_name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Migrates persisted translations to the renamed table.
}
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$table_name} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
identity_key char(64) NOT NULL,
source_language varchar(20) NOT NULL,
target_language varchar(20) NOT NULL,
field_context varchar(191) NOT NULL,
source_fingerprint char(64) NOT NULL,
translated_value longtext NULL,
status varchar(20) NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY identity_key (identity_key),
KEY target_language (target_language),
KEY source_fingerprint (source_fingerprint)
) {$charset_collate};";
dbDelta( $sql );
}
public function find_valid( $identity_key ) {
global $wpdb;
return $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The custom translation table is the plugin's persistent cache.
$wpdb->prepare(
"SELECT * FROM %i WHERE identity_key = %s AND status IN ('complete', 'manual') LIMIT 1",
self::table_name(),
$identity_key
),
ARRAY_A
);
}
public function save( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value, $status ) {
global $wpdb;
$now = current_time( 'mysql', true );
return $wpdb->replace( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Writes to the plugin-owned persistent translation cache.
self::table_name(),
array(
'identity_key' => $identity_key,
'source_language' => $source_language,
'target_language' => $target_language,
'field_context' => $field_context,
'source_fingerprint' => $source_fingerprint,
'translated_value' => $translated_value,
'status' => $status,
'created_at' => $now,
'updated_at' => $now,
),
array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
);
}
public function save_manual( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value ) {
return $this->save( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value, 'manual' );
}
public function clear( $target_language = '' ) {
global $wpdb;
if ( '' !== $target_language ) {
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- This explicitly clears one language from the plugin's persistent translation cache.
$wpdb->prepare(
'DELETE FROM %i WHERE target_language = %s',
self::table_name(),
sanitize_key( $target_language )
)
);
}
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- This explicitly clears the plugin's persistent translation cache.
$wpdb->prepare( 'DELETE FROM %i', self::table_name() )
);
}
public function get_post_language_status( $post_id, $target_language ) {
global $wpdb;
return $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The custom translation table is the plugin's persistent cache.
$wpdb->prepare(
"SELECT COUNT(*) AS translated_fields, MAX(updated_at) AS last_translated_at
FROM %i
WHERE target_language = %s
AND status IN ('complete', 'manual')
AND field_context LIKE %s",
self::table_name(),
$target_language,
'post:' . absint( $post_id ) . ':%'
),
ARRAY_A
);
}
public function get_completed_item_counts( $target_languages, $post_ids, $term_ids, $since = '' ) {
$target_languages = array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) );
$post_ids = array_values( array_filter( array_map( 'absint', (array) $post_ids ) ) );
$term_ids = array_values( array_filter( array_map( 'absint', (array) $term_ids ) ) );
if ( empty( $target_languages ) ) {
return array(
'posts' => 0,
'terms' => 0,
);
}
$language_placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
return array(
'posts' => $this->get_completed_item_count( 'post', $post_ids, $target_languages, $language_placeholders, $since ),
'terms' => $this->get_completed_item_count( 'term', $term_ids, $target_languages, $language_placeholders, $since ),
);
}
public function get_failed_items( $target_languages, $post_ids, $term_ids, $since = '' ) {
global $wpdb;
$target_languages = array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) );
$item_contexts = array_merge(
array_map(
static function ( $post_id ) {
return 'post:' . absint( $post_id );
},
(array) $post_ids
),
array_map(
static function ( $term_id ) {
return 'term:' . absint( $term_id );
},
(array) $term_ids
)
);
if ( empty( $target_languages ) || empty( $item_contexts ) ) {
return array();
}
$language_placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
$context_placeholders = implode( ',', array_fill( 0, count( $item_contexts ), '%s' ) );
$query_args = array_merge( $target_languages, $item_contexts );
if ( '' !== $since ) {
$query_args[] = $since;
}
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
return $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reads task failures from the plugin-owned translation table.
$wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- IN clauses contain only generated %s placeholders; the optional clause is fixed SQL.
"SELECT target_language, SUBSTRING_INDEX(field_context, ':', 2) AS item_context, MAX(updated_at) AS failed_at
FROM %i
WHERE status = 'failed'
AND target_language IN ({$language_placeholders})
AND SUBSTRING_INDEX(field_context, ':', 2) IN ({$context_placeholders}){$since_clause}
GROUP BY target_language, item_context
ORDER BY failed_at DESC",
array_merge( array( self::table_name() ), $query_args )
),
ARRAY_A
);
}
private function get_completed_item_count( $item_type, $item_ids, $target_languages, $language_placeholders, $since ) {
global $wpdb;
if ( empty( $item_ids ) ) {
return 0;
}
$contexts = array_map(
static function ( $item_id ) use ( $item_type ) {
return $item_type . ':' . $item_id;
},
$item_ids
);
$context_placeholders = implode( ',', array_fill( 0, count( $contexts ), '%s' ) );
$query_args = array_merge( $target_languages, $contexts );
if ( '' !== $since ) {
$query_args[] = $since;
}
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
return (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The custom translation table is the plugin's persistent cache.
$wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- IN clauses contain only generated %s placeholders; the optional clause is a fixed SQL fragment.
"SELECT COUNT(DISTINCT CONCAT(target_language, '|', SUBSTRING_INDEX(field_context, ':', 2)))
FROM %i
WHERE status IN ('complete', 'manual')
AND target_language IN ({$language_placeholders})
AND SUBSTRING_INDEX(field_context, ':', 2) IN ({$context_placeholders}){$since_clause}",
array_merge( array( self::table_name() ), $query_args )
)
);
}
}