first commit
This commit is contained in:
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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() );
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 '';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user