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,41 @@
<?php
class WPML_All_Translation_Jobs_Migration_Notice extends WPML_Translation_Jobs_Migration_Notice {
/**
* It gets the definition of the notice's content.
*
* @return array
*/
protected function get_model() {
return array(
'strings' => array(
'title' => __( 'Problem receiving translation jobs?', 'wpml-translation-management' ),
'description' => __( 'WPML needs to update its table of translation jobs, so that your site can continue receiving completed translations. This process will take a few minutes and does not modify content or translations in your site.', 'wpml-translation-management' ),
'button' => __( 'Start update', 'wpml-translation-management' ),
/* translators: this is shown between two number: processed items and total number of items to process */
'of' => __( 'of', 'wpml-translation-management' ),
'jobs_migrated' => __( 'jobs fixed', 'wpml-translation-management' ),
'communicationError' => __(
'The communication error with Translation Proxy has appeared. Please try later.',
'wpml-translation-management'
),
),
'nonce' => wp_nonce_field(
WPML_Translation_Jobs_Migration_Ajax::ACTION,
WPML_Translation_Jobs_Migration_Ajax::ACTION,
false,
false
),
);
}
/**
* It gets the ID of the notice.
*
* @return string
*/
protected function get_notice_id() {
return 'all-translation-jobs-migration';
}
}

View File

@@ -0,0 +1,69 @@
<?php
class WPML_TM_Jobs_Migration_State {
const MIGRATION_DONE_KEY = 'wpml-tm-translation-jobs-migration';
const FIXING_MIGRATION_STATE_KEY = 'wpml-tm-all-translation-jobs-migration';
const MIGRATION_SKIPPED = 'wpml-tm-translation-jobs-migration-skipped';
/**
* The fixing migration has already been run but it contained errors
*/
const FIRST_MIGRATION_FIX_HAS_RUN = 1;
/**
* We've already cleaned logs of the first fixing migration and are ready to run another this time flawless version
*/
const READY_TO_RUN_SECOND_MIGRATION_FIX = 2;
/**
* The final flawless fixing migration has been run
*/
const SECOND_MIGRATION_FIX_HAS_RUN = 3;
/**
* Checks if the original migration has been finished
*
* @return bool
*/
public function is_migrated() {
return (bool) get_option( self::MIGRATION_DONE_KEY );
}
/**
* Checks if the fixing migration ( migration which fixes the flaws of the original migration ) has been run
*
* @return bool
*/
public function is_fixing_migration_done() {
$option = (int) get_option( self::FIXING_MIGRATION_STATE_KEY );
if ( $option === self::FIRST_MIGRATION_FIX_HAS_RUN ) { // clear previous log
update_option( WPML_Translation_Jobs_Migration::MIGRATION_FIX_LOG_KEY, array(), false );
update_option( self::FIXING_MIGRATION_STATE_KEY, self::READY_TO_RUN_SECOND_MIGRATION_FIX, true );
}
return (int) get_option( self::FIXING_MIGRATION_STATE_KEY ) === self::SECOND_MIGRATION_FIX_HAS_RUN;
}
public function mark_migration_as_done() {
update_option( self::MIGRATION_DONE_KEY, 1, true );
// a user has never run the original migration so it does not need the fixing migration
$this->mark_fixing_migration_as_done();
}
public function mark_fixing_migration_as_done() {
update_option( self::FIXING_MIGRATION_STATE_KEY, self::SECOND_MIGRATION_FIX_HAS_RUN, true );
}
/**
* @param bool $flag
*/
public function skip_migration( $flag = true ) {
update_option( self::MIGRATION_SKIPPED, $flag, true );
}
/**
* @return bool
*/
public function is_skipped() {
return (bool) get_option( self::MIGRATION_SKIPPED );
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* The class adds the hook which is triggered in the moment of Translation Service authorization.
* It checks if the migration has been skipped due to lack of activated service and if so, it turns on the migration.
*/
class WPML_TM_Restore_Skipped_Migration implements IWPML_Action {
/** @var WPML_TM_Jobs_Migration_State */
private $migration_state;
/**
* @param WPML_TM_Jobs_Migration_State $migration_state
*/
public function __construct( WPML_TM_Jobs_Migration_State $migration_state ) {
$this->migration_state = $migration_state;
}
public function add_hooks() {
add_action( 'wpml_tm_translation_service_authorized', array( $this, 'restore' ) );
}
public function restore() {
if ( $this->migration_state->is_skipped() ) {
$this->migration_state->skip_migration( false );
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
class WPML_TM_Translation_Jobs_Fix_Summary_Factory implements IWPML_Backend_Action_Loader {
/**
* @return WPML_TM_Translation_Jobs_Fix_Summary
*/
public function create() {
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/translation-jobs-migration/' ) );
return new WPML_TM_Translation_Jobs_Fix_Summary(
new WPML_TM_Translation_Jobs_Fix_Summary_Notice( wpml_get_admin_notices(), $template_service->get_template() ),
new WPML_TM_Jobs_Migration_State()
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
class WPML_TM_Translation_Jobs_Fix_Summary_Notice extends WPML_Translation_Jobs_Migration_Notice {
protected function get_model() {
$tm_url = '<a href="' . admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/main.php' ) . '">' . __( 'Translation Dashboard', 'sitepress' ) . '</a>';
$description = sprintf( __( 'WPML completed updating the translations history. Next, please visit the %s and click on the button to "Check status and get translations".', 'wpml-translation-management' ), $tm_url );
return array(
'strings' => array(
'title' => __( 'Problem receiving translation jobs?', 'sitepress' ),
'description' => $description,
),
);
}
protected function get_notice_id() {
return 'translation-jobs-migration-fix-summary';
}
}

View File

@@ -0,0 +1,55 @@
<?php
class WPML_TM_Translation_Jobs_Fix_Summary {
const INVALID_JOBS_SYNCED_KEY = 'wpml_tm_migration_invalid_jobs_already_synced';
/** @var WPML_TM_Translation_Jobs_Fix_Summary_Notice */
private $notice;
/** @var WPML_TM_Jobs_Migration_State */
private $migration_state;
public function __construct(
WPML_TM_Translation_Jobs_Fix_Summary_Notice $notice,
WPML_TM_Jobs_Migration_State $migration_state
) {
$this->notice = $notice;
$this->migration_state = $migration_state;
}
public function add_hooks() {
add_action( 'init', array( $this, 'display_summary' ) );
add_action(
'wp_ajax_' . WPML_TP_Sync_Ajax_Handler::AJAX_ACTION,
array(
$this,
'mark_invalid_jobs_as_synced',
)
);
}
public function display_summary() {
if ( $this->should_display_summary_notice() ) {
$this->notice->add_notice();
} elseif ( $this->notice->exists() ) {
$this->notice->remove_notice();
}
}
private function should_display_summary_notice() {
if ( ! $this->migration_state->is_fixing_migration_done() ) {
return false;
}
$jobs_with_new_status = get_option( WPML_Translation_Jobs_Migration::MIGRATION_FIX_LOG_KEY );
$jobs_with_new_status = isset( $jobs_with_new_status['status_changed'] ) ? $jobs_with_new_status['status_changed'] : null;
$jobs_already_synced = (int) get_option( self::INVALID_JOBS_SYNCED_KEY ) > 1;
return $jobs_with_new_status && ! $jobs_already_synced;
}
public function mark_invalid_jobs_as_synced() {
update_option( self::INVALID_JOBS_SYNCED_KEY, 2 );
}
}

View File

@@ -0,0 +1,16 @@
<?php
class WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $wpml_post_translations, $wpml_term_translations, $wpdb;
$jobs_migration_repository = new WPML_Translation_Jobs_Migration_Repository( wpml_tm_get_jobs_repository(), true );
$job_factory = wpml_tm_load_job_factory();
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
$cms_id_helper = new WPML_TM_CMS_ID( $wpml_tm_records, $job_factory );
$jobs_migration = new WPML_Translation_Jobs_Migration( $jobs_migration_repository, $cms_id_helper, $wpdb, wpml_tm_get_tp_jobs_api() );
return new WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID( $jobs_migration, wpml_tm_get_jobs_repository() );
}
}

View File

@@ -0,0 +1,73 @@
<?php
class WPML_TM_Troubleshooting_Fix_Translation_Jobs_TP_ID {
const AJAX_ACTION = 'wpml-fix-translation-jobs-tp-id';
private $jobs_migration;
private $jobs_repository;
public function __construct( WPML_Translation_Jobs_Migration $jobs_migration, WPML_TM_Jobs_Repository $jobs_repository ) {
$this->jobs_migration = $jobs_migration;
$this->jobs_repository = $jobs_repository;
}
public function add_hooks() {
add_action(
'wpml_troubleshooting_after_fix_element_type_collation',
array(
$this,
'render_troubleshooting_section',
)
);
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'wp_ajax_' . self::AJAX_ACTION, array( $this, 'fix_tp_id_ajax' ) );
}
public function fix_tp_id_ajax() {
if ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], self::AJAX_ACTION ) ) {
$job_ids = isset( $_POST['job_ids'] ) ? array_map( 'intval', explode( ',', filter_var( $_POST['job_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) ) : array();
$jobs = array();
foreach ( $job_ids as $job_id ) {
if ( ! $job_id ) {
continue;
}
$params = new WPML_TM_Jobs_Search_Params();
$params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
$params->set_job_types( array( WPML_TM_Job_Entity::POST_TYPE, WPML_TM_Job_Entity::PACKAGE_TYPE ) );
$params->set_local_job_id( $job_id );
$jobs[] = current( $this->jobs_repository->get( $params )->getIterator()->getArrayCopy() );
}
if ( $jobs ) {
$this->jobs_migration->migrate_jobs( $jobs, true );
}
wp_send_json_success();
} else {
wp_send_json_error();
}
}
public function enqueue_scripts( $hook ) {
if ( WPML_PLUGIN_FOLDER . '/menu/troubleshooting.php' === $hook ) {
wp_enqueue_script( 'wpml-fix-tp-id', WPML_TM_URL . '/res/js/fix-tp-id.js', array( 'jquery' ), WPML_TM_VERSION );
}
}
public function render_troubleshooting_section() {
?>
<p>
<input id="wpml_fix_tp_id_text" type="text" value=""/><input id="wpml_fix_tp_id_btn" type="button" class="button-secondary" value="<?php esc_attr_e( 'Fix WPML Translation Jobs "tp_id" field', 'sitepress' ); ?>"/><br/>
<?php wp_nonce_field( self::AJAX_ACTION, 'wpml-fix-tp-id-nonce' ); ?>
<small style="margin-left:10px;"><?php esc_attr_e( 'Fixes the "tp_id" field of WPML ranslation jobs and set the status to "in progress" (it requires manual action to re-sync translation status + download translations). It accepts comma separated values of translation job IDs (rid).', 'sitepress' ); ?></small>
</p>
<?php
}
}

View File

@@ -0,0 +1,86 @@
<?php
class WPML_Translation_Jobs_Fixing_Migration_Ajax {
const ACTION = 'wpml_translation_jobs_migration';
const JOBS_MIGRATED_PER_REQUEST = 100;
const PAGINATION_OPTION = 'wpml_translation_jobs_migration_processed';
/** @var WPML_Translation_Jobs_Migration */
private $jobs_migration;
/** @var WPML_Translation_Jobs_Migration_Repository */
private $jobs_repository;
/** @var WPML_TM_Jobs_Migration_State */
private $migration_state;
public function __construct(
WPML_Translation_Jobs_Migration $jobs_migration,
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
WPML_TM_Jobs_Migration_State $migration_state
) {
$this->jobs_migration = $jobs_migration;
$this->jobs_repository = $jobs_repository;
$this->migration_state = $migration_state;
}
public function run_migration() {
if ( ! $this->is_valid_request() ) {
wp_send_json_error();
}
$jobs = $this->jobs_repository->get();
$total_jobs = count( $jobs );
$offset = $this->get_already_processed();
if ( $offset < $total_jobs ) {
$jobs_chunk = array_slice( $jobs, $offset, self::JOBS_MIGRATED_PER_REQUEST );
try {
$this->jobs_migration->migrate_jobs( $jobs_chunk, true );
} catch ( Exception $e ) {
wp_send_json_error( $e->getMessage(), 500 );
return;
}
$done = $total_jobs <= $offset + self::JOBS_MIGRATED_PER_REQUEST;
$jobs_chunk_total = count( $jobs_chunk );
update_option( self::PAGINATION_OPTION, $offset + self::JOBS_MIGRATED_PER_REQUEST );
} else {
$done = true;
$jobs_chunk_total = 0;
}
$result = array(
'totalJobs' => $total_jobs,
'jobsMigrated' => $jobs_chunk_total,
'done' => $done,
);
if ( $done ) {
$this->migration_state->mark_fixing_migration_as_done();
delete_option( self::PAGINATION_OPTION );
}
wp_send_json_success( $result );
}
/**
* @return bool
*/
private function is_valid_request() {
return wp_verify_nonce( $_POST['nonce'], self::ACTION );
}
/**
* @return int
*/
private function get_already_processed() {
return (int) get_option( self::PAGINATION_OPTION, 0 );
}
}

View File

@@ -0,0 +1,68 @@
<?php
class WPML_Translation_Jobs_Migration_Ajax {
const ACTION = 'wpml_translation_jobs_migration';
const JOBS_MIGRATED_PER_REQUEST = 100;
/** @var WPML_Translation_Jobs_Migration */
private $jobs_migration;
/** @var WPML_Translation_Jobs_Migration_Repository */
private $jobs_repository;
/** @var WPML_TM_Jobs_Migration_State */
private $migration_state;
public function __construct(
WPML_Translation_Jobs_Migration $jobs_migration,
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
WPML_TM_Jobs_Migration_State $migration_state
) {
$this->jobs_migration = $jobs_migration;
$this->jobs_repository = $jobs_repository;
$this->migration_state = $migration_state;
}
public function run_migration() {
if ( ! $this->is_valid_request() ) {
wp_send_json_error();
return;
}
$jobs = $this->jobs_repository->get();
$jobs_chunk = array_slice( $jobs, 0, self::JOBS_MIGRATED_PER_REQUEST );
try {
$this->jobs_migration->migrate_jobs( $jobs_chunk );
} catch ( Exception $e ) {
wp_send_json_error( $e->getMessage(), 500 );
return;
}
$done = count( $jobs ) === count( $jobs_chunk );
$total_jobs = count( $jobs );
$jobs_chunk_total = count( $jobs_chunk );
$result = array(
'totalJobs' => $total_jobs,
'jobsMigrated' => $jobs_chunk_total,
'done' => $done,
);
if ( $jobs_chunk_total === $total_jobs ) {
$this->migration_state->mark_migration_as_done();
}
wp_send_json_success( $result );
}
/**
* @return bool
*/
private function is_valid_request() {
return wp_verify_nonce( $_POST['nonce'], self::ACTION );
}
}

View File

@@ -0,0 +1,112 @@
<?php
use WPML\API\Sanitize;
class WPML_Translation_Jobs_Migration_Hooks_Factory implements IWPML_Backend_Action_Loader, IWPML_AJAX_Action_Loader {
/**
* It creates an instance of WPML_Translation_Jobs_Migration_Notice.
*
* @return null|WPML_Translation_Jobs_Migration_Hooks|WPML_TM_Restore_Skipped_Migration
*/
public function create() {
$fixing_migration = false;
$wpml_notices = wpml_get_admin_notices();
$wpml_notices->remove_notice( WPML_Translation_Jobs_Migration_Notice::NOTICE_GROUP_ID, 'all-translation-jobs-migration' );
$wpml_notices->remove_notice( WPML_Translation_Jobs_Migration_Notice::NOTICE_GROUP_ID, 'translation-jobs-migration' );
if ( ! $this->should_add_migration_hooks() ) {
return null;
}
$migration_state = new WPML_TM_Jobs_Migration_State();
if ( $migration_state->is_skipped() ) {
return new WPML_TM_Restore_Skipped_Migration( $migration_state );
}
if ( $migration_state->is_migrated() ) {
if ( $migration_state->is_fixing_migration_done() ) {
return null;
}
$fixing_migration = true;
}
$template_service = new WPML_Twig_Template_Loader( array( WPML_TM_PATH . '/templates/translation-jobs-migration/' ) );
if ( $fixing_migration ) {
$notice = new WPML_All_Translation_Jobs_Migration_Notice( $wpml_notices, $template_service->get_template() );
} else {
$notice = new WPML_Translation_Jobs_Missing_TP_ID_Migration_Notice( $wpml_notices, $template_service->get_template() );
}
$jobs_migration_repository = new WPML_Translation_Jobs_Migration_Repository( wpml_tm_get_jobs_repository(), $fixing_migration );
global $wpml_post_translations, $wpml_term_translations, $wpdb;
$job_factory = wpml_tm_load_job_factory();
$wpml_tm_records = new WPML_TM_Records( $wpdb, $wpml_post_translations, $wpml_term_translations );
$cms_id_helper = new WPML_TM_CMS_ID( $wpml_tm_records, $job_factory );
$jobs_migration = new WPML_Translation_Jobs_Migration( $jobs_migration_repository, $cms_id_helper, $wpdb, wpml_tm_get_tp_jobs_api() );
if ( $fixing_migration ) {
$ajax_handler = new WPML_Translation_Jobs_Fixing_Migration_Ajax(
$jobs_migration,
$jobs_migration_repository,
$migration_state
);
} else {
$ajax_handler = new WPML_Translation_Jobs_Migration_Ajax(
$jobs_migration,
$jobs_migration_repository,
$migration_state
);
}
return new WPML_Translation_Jobs_Migration_Hooks(
$notice,
$ajax_handler,
$jobs_migration_repository,
wpml_get_upgrade_schema(),
$migration_state
);
}
/**
* Check if location is allowed to add migration hooks.
*/
private function should_add_migration_hooks() {
$allowed_uris = array(
'/.*page=sitepress-multilingual-cms.*/',
'/.*page=wpml-string-translation.*/',
'/.*page=tm\/menu\/main.*/',
);
if ( wp_doing_ajax() ) {
return true;
}
$uri = rawurldecode( $this->get_request_uri() );
foreach ( $allowed_uris as $pattern ) {
if ( preg_match( $pattern, $uri ) ) {
return true;
}
}
return false;
}
/**
* Get request uri.
*
* @return string
*/
private function get_request_uri() {
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
return wp_unslash( Sanitize::stringProp( 'REQUEST_URI', $_SERVER ) );
}
return '';
}
}

View File

@@ -0,0 +1,109 @@
<?php
class WPML_Translation_Jobs_Migration_Hooks {
private $notice;
private $ajax_handler;
/** @var WPML_Translation_Jobs_Migration_Repository */
private $jobs_migration_repository;
/** @var WPML_Upgrade_Schema $schema */
private $schema;
/** @var WPML_TM_Jobs_Migration_State */
private $migration_state;
public function __construct(
WPML_Translation_Jobs_Migration_Notice $notice,
$ajax_handler,
WPML_Translation_Jobs_Migration_Repository $jobs_migration_repository,
WPML_Upgrade_Schema $schema,
WPML_TM_Jobs_Migration_State $migration_state
) {
$this->notice = $notice;
$this->ajax_handler = $ajax_handler;
$this->jobs_migration_repository = $jobs_migration_repository;
$this->schema = $schema;
$this->migration_state = $migration_state;
}
public function add_hooks() {
add_action( 'init', array( $this, 'add_hooks_on_init' ), PHP_INT_MAX );
}
public function add_hooks_on_init() {
if ( $this->new_columns_are_not_added_yet() ) {
add_action( 'wpml_tm_lock_ui', array( $this, 'lock_tm_ui' ) );
} elseif ( $this->needs_migration() ) {
$this->notice->add_notice();
add_action( 'wpml_tm_lock_ui', array( $this, 'lock_tm_ui' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action(
'wp_ajax_' . WPML_Translation_Jobs_Migration_Ajax::ACTION,
array( $this->ajax_handler, 'run_migration' )
);
}
}
/**
* @see
* `WPML_TM_Add_TP_Revision_And_TS_Status_Columns_To_Core_Status`
* `WPML_TM_Add_TP_Revision_And_TS_Status_Columns_To_Translation_Status`
* `WPML_TM_Add_TP_ID_Column_To_Translation_Status`
*
* @return bool
*/
private function new_columns_are_not_added_yet() {
$has_columns = $this->schema->does_column_exist( 'icl_core_status', 'tp_revision' )
&& $this->schema->does_column_exist( 'icl_core_status', 'ts_status' )
&& $this->schema->does_column_exist( 'icl_translation_status', 'tp_revision' )
&& $this->schema->does_column_exist( 'icl_translation_status', 'ts_status' )
&& $this->schema->does_column_exist( 'icl_translation_status', 'tp_id' );
return ! $has_columns;
}
public function enqueue_scripts() {
wp_enqueue_script(
'wpml-tm-translation-jobs-migration',
WPML_TM_URL . '/dist/js/translationJobsMigration/app.js',
array(),
WPML_TM_VERSION
);
}
/**
* @return bool
*/
private function needs_migration() {
if ( $this->jobs_migration_repository->get_count() ) {
return ! $this->skip_migration_if_service_is_not_active();
}
$this->migration_state->mark_migration_as_done();
return false;
}
/**
* @return bool
*/
private function skip_migration_if_service_is_not_active() {
if ( ! TranslationProxy::is_current_service_active_and_authenticated() ) {
$this->migration_state->skip_migration( true );
return true;
}
return false;
}
/**
* @return bool
*/
public function lock_tm_ui() {
return true;
}
}

View File

@@ -0,0 +1,80 @@
<?php
abstract class WPML_Translation_Jobs_Migration_Notice {
const NOTICE_GROUP_ID = 'translation-jobs';
const TEMPLATE = 'translation-jobs-migration.twig';
/**
* The instance of \WPML_Notices.
*
* @var \WPML_Notices
*/
private $admin_notices;
/**
* The instance of \IWPML_Template_Service.
*
* @var \IWPML_Template_Service
*/
private $template_service;
/**
* WPML_Translation_Jobs_Migration_Notice constructor.
*
* @param \WPML_Notices $admin_notices An instance of \WPML_Notices.
* @param \IWPML_Template_Service $template_service A class implementing \IWPML_Template_Service.
*/
public function __construct( WPML_Notices $admin_notices, IWPML_Template_Service $template_service ) {
$this->admin_notices = $admin_notices;
$this->template_service = $template_service;
}
/**
* It adds the notice to be shown when conditions meet.
*/
public function add_notice() {
$notice = $this->admin_notices->create_notice( $this->get_notice_id(), $this->get_notice_content(), self::NOTICE_GROUP_ID );
$notice->set_css_class_types( 'notice-error' );
$this->admin_notices->add_notice( $notice );
}
/**
* It removes the notice.
*/
public function remove_notice() {
$this->admin_notices->remove_notice( self::NOTICE_GROUP_ID, $this->get_notice_id() );
}
/**
* It checks is the notice exists.
*
* @return bool
*/
public function exists() {
return (bool) $this->admin_notices->get_notice( $this->get_notice_id(), self::NOTICE_GROUP_ID );
}
/**
* It gets the notice content.
*
* @return string
*/
private function get_notice_content() {
return $this->template_service->show( $this->get_model(), self::TEMPLATE );
}
/**
* It gets the definition of the notice's content.
*
* @return array
*/
abstract protected function get_model();
/**
* It gets the ID of the notice.
*
* @return string
*/
abstract protected function get_notice_id();
}

View File

@@ -0,0 +1,32 @@
<?php
class WPML_Translation_Jobs_Migration_Repository {
private $jobs_repository;
private $all_jobs = false;
public function __construct( WPML_TM_Jobs_Repository $jobs_repository, $all_jobs = false ) {
$this->jobs_repository = $jobs_repository;
$this->all_jobs = $all_jobs;
}
public function get() {
return $this->jobs_repository->get( $this->get_params() )->getIterator()->getArrayCopy();
}
public function get_count() {
return $this->jobs_repository->get_count( $this->get_params() );
}
private function get_params() {
$params = new WPML_TM_Jobs_Search_Params();
$params->set_scope( WPML_TM_Jobs_Search_Params::SCOPE_REMOTE );
$params->set_job_types( array( WPML_TM_Job_Entity::POST_TYPE, WPML_TM_Job_Entity::PACKAGE_TYPE ) );
if ( ! $this->all_jobs ) {
$params->set_tp_id( null );
}
return $params;
}
}

View File

@@ -0,0 +1,218 @@
<?php
class WPML_Translation_Jobs_Migration {
const MIGRATION_FIX_LOG_KEY = 'wpml_fixing_migration_log';
private $jobs_repository;
private $cms_id_builder;
private $wpdb;
private $jobs_api;
public function __construct(
WPML_Translation_Jobs_Migration_Repository $jobs_repository,
WPML_TM_CMS_ID $cms_id_builder,
wpdb $wpdb,
WPML_TP_Jobs_API $jobs_api
) {
$this->jobs_repository = $jobs_repository;
$this->cms_id_builder = $cms_id_builder;
$this->wpdb = $wpdb;
$this->jobs_api = $jobs_api;
}
/**
* @param WPML_TM_Post_Job_Entity[] $jobs
* @param bool $recover_status
*
* @throws WPML_TP_API_Exception
*/
public function migrate_jobs( array $jobs, $recover_status = false ) {
$mapped_jobs = $this->map_cms_id_job_id( $jobs );
if ( $mapped_jobs ) {
$tp_jobs = $this->get_tp_jobs( $mapped_jobs );
foreach ( $jobs as $job ) {
$cms_id = array_key_exists( $job->get_id(), $mapped_jobs ) ? $mapped_jobs[ $job->get_id() ] : '';
list( $tp_id, $revision_id ) = $this->get_tp_id_revision_id( $cms_id, $tp_jobs );
if ( $recover_status ) {
$this->recovery_mode( $job, $tp_id, $revision_id );
} else {
$this->first_migration_mode( $job, $tp_id, $revision_id );
}
}
}
}
/**
* @param array $mapped_jobs
*
* @throws WPML_TP_API_Exception
* @return array
*/
private function get_tp_jobs( array $mapped_jobs ) {
return $this->get_latest_jobs_grouped_by_cms_id(
$this->jobs_api->get_jobs_per_cms_ids( array_values( $mapped_jobs ), true )
);
}
/**
* @param WPML_TM_Post_Job_Entity $job
* @param int $tp_id
* @param int $revision_id
*/
private function recovery_mode( WPML_TM_Post_Job_Entity $job, $tp_id, $revision_id ) {
if ( $tp_id !== $job->get_tp_id() ) {
$new_status = $this->get_new_status( $job, $tp_id );
$this->log( $job->get_id(), $job->get_tp_id(), $tp_id, $job->get_status(), $new_status );
$this->fix_job_fields( $tp_id, $revision_id, $new_status, $job->get_id() );
}
}
/**
* @param WPML_TM_Post_Job_Entity $job
* @param int $tp_id
* @param int $revision_id
*/
private function first_migration_mode( WPML_TM_Post_Job_Entity $job, $tp_id, $revision_id ) {
$this->fix_job_fields( $tp_id, $revision_id, false, $job->get_id() );
}
/**
* @param array $tp_jobs
*
* @return array
*/
private function get_latest_jobs_grouped_by_cms_id( $tp_jobs ) {
$result = array();
foreach ( $tp_jobs as $tp_job ) {
if ( ! isset( $result[ $tp_job->cms_id ] ) ) {
$result[ $tp_job->cms_id ] = $tp_job;
} elseif ( $tp_job->id > $result[ $tp_job->cms_id ]->id ) {
$result[ $tp_job->cms_id ] = $tp_job;
}
}
return $result;
}
/**
* @param WPML_TM_Post_Job_Entity $job
* @param int $new_tp_id
*
* @return bool
*/
private function get_new_status( WPML_TM_Post_Job_Entity $job, $new_tp_id ) {
$new_status = false;
if ( $job->get_tp_id() !== null && $new_tp_id ) {
if ( $job->get_status() === ICL_TM_NOT_TRANSLATED || $this->has_been_completed_after_release( $job ) ) {
$new_status = ICL_TM_IN_PROGRESS;
}
}
return $new_status;
}
/**
* @param WPML_TM_Post_Job_Entity $job
*
* @return bool
* @throws Exception
*/
private function has_been_completed_after_release( WPML_TM_Post_Job_Entity $job ) {
return $job->get_status() === ICL_TM_COMPLETE && $job->get_completed_date() && $job->get_completed_date() > $this->get_4_2_0_release_date();
}
/**
* @param int $cms_id
* @param array $tp_jobs
*
* @return array
*/
private function get_tp_id_revision_id( $cms_id, $tp_jobs ) {
$result = array( 0, 0 );
if ( isset( $tp_jobs[ $cms_id ] ) ) {
$result = array(
$tp_jobs[ $cms_id ]->id,
$tp_jobs[ $cms_id ]->translation_revision,
);
}
return $result;
}
/**
* @param int $tp_id
* @param int $revision_id
* @param int|false $status
* @param int $job_id
*/
private function fix_job_fields( $tp_id, $revision_id, $status, $job_id ) {
$new_data = array(
'tp_id' => $tp_id,
'tp_revision' => $revision_id,
);
if ( $status ) {
$new_data['status'] = $status;
}
$this->wpdb->update(
$this->wpdb->prefix . 'icl_translation_status',
$new_data,
array( 'rid' => $job_id )
);
}
/**
* @param WPML_TM_Post_Job_Entity[] $jobs
*
* @return array
*/
private function map_cms_id_job_id( $jobs ) {
$mapped_jobs = array();
foreach ( $jobs as $job ) {
$cms_id = $this->cms_id_builder->cms_id_from_job_id( $job->get_translate_job_id() );
$mapped_jobs[ $job->get_id() ] = $cms_id;
}
return $mapped_jobs;
}
/**
* @return DateTime
* @throws Exception
*/
private function get_4_2_0_release_date() {
return new DateTime(
defined( 'WPML_4_2_0_RELEASE_DATE' ) ? WPML_4_2_0_RELEASE_DATE : '2019-01-20'
);
}
/**
* @param int $rid
* @param int $old_tp_id
* @param int $new_tp_id
* @param int $old_status
* @param int $new_status
*/
private function log( $rid, $old_tp_id, $new_tp_id, $old_status, $new_status ) {
$log = get_option( self::MIGRATION_FIX_LOG_KEY, array() );
$key = $new_status ? 'status_changed' : 'status_not_changed';
$log[ $key ][ $rid ] = array(
'rid' => $rid,
'old_tp_id' => $old_tp_id,
'new_tp_id' => $new_tp_id,
'old_status' => $old_status,
'new_status' => $new_status,
);
update_option( self::MIGRATION_FIX_LOG_KEY, $log, false );
}
}

View File

@@ -0,0 +1,37 @@
<?php
class WPML_Translation_Jobs_Missing_TP_ID_Migration_Notice extends WPML_Translation_Jobs_Migration_Notice {
/**
* It gets the definition of the notice's content.
*
* @return array
*/
protected function get_model() {
return array(
'strings' => array(
'title' => __( 'WPML Translation Jobs Migration', 'wpml-translation-management' ),
/* translators: the placeholder is replaced with the current version of WPML */
'description' => sprintf( __( 'WPML found some remote jobs on your site that must be migrated in order to work with WPML %s. You might not be able to access some of the WPML administration pages until this migration is fully completed.', 'wpml-translation-management' ), ICL_SITEPRESS_VERSION ),
'button' => __( 'Run now', 'wpml-translation-management' ),
/* translators: this is shown between two number: processed items and total number of items to process */
'of' => __( 'of', 'wpml-translation-management' ),
'jobs_migrated' => __( 'jobs migrated', 'wpml-translation-management' ),
'communicationError' => __(
'The communication error with Translation Proxy has appeared. Please try later.',
'wpml-translation-management'
),
),
'nonce' => wp_nonce_field( WPML_Translation_Jobs_Migration_Ajax::ACTION, WPML_Translation_Jobs_Migration_Ajax::ACTION, false, false ),
);
}
/**
* It gets the ID of the notice.
*
* @return string
*/
protected function get_notice_id() {
return 'translation-jobs-migration';
}
}