first commit
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Editor;
|
||||
|
||||
use WPML\FP\Str;
|
||||
use WPML\FP\Cast;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
use WPML\LIB\WP\Option;
|
||||
use WPML\TM\ATE\ClonedSites\ApiCommunication;
|
||||
use WPML\UIPage;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
class ATEDetailedErrorMessage {
|
||||
|
||||
const ERROR_DETAILS_OPTION = 'wpml_ate_error_details';
|
||||
|
||||
/**
|
||||
* Parses error data and saves it to options table.
|
||||
*
|
||||
* @param $errorResponse
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function saveDetailedError( $errorResponse ) {
|
||||
$errorCode = $errorResponse->get_error_code();
|
||||
$errorMessage = $errorResponse->get_error_message();
|
||||
$errorData = $errorResponse->get_error_data( $errorCode );
|
||||
|
||||
$errorDetails = [
|
||||
'code' => $errorCode,
|
||||
'message' => $errorMessage,
|
||||
'error_data' => $errorData,
|
||||
];
|
||||
|
||||
self::saveErrorDetailsInOptions( $errorDetails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single or multiple formatted error message depending on errors array existence in response.
|
||||
*
|
||||
* @param string $appendText
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function readDetailedError( $appendText = null ) {
|
||||
$errorDetails = Option::getOr( self::ERROR_DETAILS_OPTION, [] );
|
||||
|
||||
$detailedError = self::hasValidExplainedMessage( $errorDetails )
|
||||
? self::formattedDetailedErrors( $errorDetails, $appendText )
|
||||
: (
|
||||
self::isSiteMigrationError( $errorDetails )
|
||||
? self::formattedSiteMigrationError( $errorDetails )
|
||||
: null
|
||||
);
|
||||
|
||||
self::deleteErrorDetailsFromOptions(); // deleting the option after message is ready to be displayed.
|
||||
|
||||
return $detailedError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if valid explained message exists in error response.
|
||||
*
|
||||
* @param array $errorDetails
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private static function hasValidExplainedMessage( $errorDetails ) {
|
||||
$hasExplainedMessage = pipe(
|
||||
Obj::path( [ 'error_data', 0, 'explained_message' ] ),
|
||||
Str::len(),
|
||||
Cast::toBool()
|
||||
);
|
||||
|
||||
return $hasExplainedMessage( $errorDetails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if error is "Site moved or copied" (Happens when error code is 426)
|
||||
*
|
||||
* @param array $errorDetails
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private static function isSiteMigrationError( $errorDetails ) {
|
||||
$isSiteMigrationError = pipe(
|
||||
Obj::prop( 'code' ),
|
||||
Cast::toInt(),
|
||||
Relation::equals( ApiCommunication::SITE_CLONED_ERROR )
|
||||
);
|
||||
|
||||
return $isSiteMigrationError( $errorDetails );
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose of this function is to avoid the case when redirect is happened and data saved in this static class is lost
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function saveErrorDetailsInOptions( $errorDetails ) {
|
||||
Option::updateWithoutAutoLoad( self::ERROR_DETAILS_OPTION, $errorDetails );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the error details from options.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function deleteErrorDetailsFromOptions() {
|
||||
Option::delete( self::ERROR_DETAILS_OPTION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns multiple formatted error messages from errors array.
|
||||
*
|
||||
* @param array $errorDetails
|
||||
* @param string $appendText
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function formattedDetailedErrors( array $errorDetails, $appendText ) {
|
||||
$appendText = $appendText ? '<div>' . $appendText . '</div>' : '';
|
||||
|
||||
$allErrors = '<div>';
|
||||
|
||||
foreach ( Obj::prop( 'error_data', $errorDetails ) as $error ) {
|
||||
$allErrors .= '<div>' . Obj::propOr( '', 'explained_message', $error ) . '</div>';
|
||||
}
|
||||
|
||||
return $allErrors . $appendText . '</div>';
|
||||
}
|
||||
|
||||
private static function formattedSiteMigrationError( $errorDetails ) {
|
||||
return '<div>' . Obj::prop( 'message', $errorDetails ) . '</div>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace WPML\TM\Editor;
|
||||
|
||||
|
||||
use WPML\LIB\WP\Option;
|
||||
|
||||
class ATERetry {
|
||||
|
||||
/**
|
||||
* @param int $jobId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasFailed( $jobId ) {
|
||||
return self::getCount( $jobId ) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $jobId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getCount( $jobId ) {
|
||||
return (int) Option::getOr( self::getOptionName( $jobId ), - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $jobId
|
||||
*/
|
||||
public static function incrementCount( $jobId ) {
|
||||
Option::update( self::getOptionName( $jobId ), self::getCount( $jobId ) + 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $jobId
|
||||
*/
|
||||
public static function reset( $jobId ) {
|
||||
Option::delete( self::getOptionName( $jobId ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $jobId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getOptionName( $jobId ) {
|
||||
return sprintf( 'wpml-ate-job-retry-counter-%d', $jobId );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Editor;
|
||||
|
||||
class ClassicEditorActions {
|
||||
|
||||
public function addHooks() {
|
||||
add_action( 'wp_ajax_wpml_save_job_ajax', [ $this, 'saveJob' ] );
|
||||
}
|
||||
|
||||
public function saveJob() {
|
||||
if ( ! wpml_is_action_authenticated( 'wpml_save_job' ) ) {
|
||||
wp_send_json_error( 'Permission denied.' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$post_data = \WPML_TM_Post_Data::strip_slashes_for_single_quote( $_POST['data'] );
|
||||
parse_str( $post_data, $data );
|
||||
|
||||
/**
|
||||
* It filters job data
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
$data = apply_filters( 'wpml_translation_editor_save_job_data', $data );
|
||||
|
||||
$job = \WPML\Container\make( \WPML_TM_Editor_Job_Save::class );
|
||||
|
||||
$job_details = [
|
||||
'job_type' => $data['job_post_type'],
|
||||
'job_id' => $data['job_post_id'],
|
||||
'target' => $data['target_lang'],
|
||||
'translation_complete' => isset( $data['complete'] ) ? true : false,
|
||||
];
|
||||
$job = apply_filters( 'wpml-translation-editor-fetch-job', $job, $job_details );
|
||||
|
||||
$ajax_response = $job->save( $data );
|
||||
$ajax_response->send_json();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Editor;
|
||||
|
||||
use WPML\FP\Either;
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Left;
|
||||
use WPML\FP\Logic;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
use WPML\FP\Right;
|
||||
use WPML\LIB\WP\User;
|
||||
use WPML\Setup\Option as SetupOption;
|
||||
use WPML\TM\API\Jobs;
|
||||
use WPML\TM\ATE\Log\Entry;
|
||||
use WPML\TM\ATE\Log\Storage;
|
||||
use WPML\TM\ATE\Review\ReviewStatus;
|
||||
use WPML\TM\ATE\Sync\Trigger;
|
||||
use WPML\TM\Jobs\Manual;
|
||||
use WPML\TM\Menu\TranslationQueue\CloneJobs;
|
||||
use function WPML\Container\make;
|
||||
use function WPML\FP\curryN;
|
||||
use function WPML\FP\invoke;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
class Editor {
|
||||
|
||||
const ATE_JOB_COULD_NOT_BE_CREATED = 101;
|
||||
const ATE_EDITOR_URL_COULD_NOT_BE_FETCHED = 102;
|
||||
const ATE_IS_NOT_ACTIVE = 103;
|
||||
|
||||
/** @var CloneJobs */
|
||||
private $clone_jobs;
|
||||
|
||||
/** @var Manual */
|
||||
private $manualJobs;
|
||||
|
||||
/**
|
||||
* Editor constructor.
|
||||
*
|
||||
* @param CloneJobs $clone_jobs
|
||||
* @param Manual $manualJobs
|
||||
*/
|
||||
public function __construct( CloneJobs $clone_jobs, Manual $manualJobs ) {
|
||||
$this->clone_jobs = $clone_jobs;
|
||||
$this->manualJobs = $manualJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function open( $params ) {
|
||||
$shouldOpenCTE = function ( $jobObject ) use ( $params ) {
|
||||
if ( ! \WPML_TM_ATE_Status::is_enabled() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( $this->isNewJobCreated( $params, $jobObject ) ) {
|
||||
return wpml_tm_load_old_jobs_editor()->shouldStickToWPMLEditor( $jobObject->get_id() );
|
||||
}
|
||||
|
||||
return wpml_tm_load_old_jobs_editor()->editorForTranslationsPreviouslyCreatedUsingCTE() === \WPML_TM_Editors::WPML &&
|
||||
wpml_tm_load_old_jobs_editor()->get_current_editor( $jobObject->get_id() ) === \WPML_TM_Editors::WPML;
|
||||
};
|
||||
|
||||
/**
|
||||
* It maybe needed when a job was translated via the Translation Proxy before and now, we want to open it in the editor.
|
||||
*
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return \WPML_Element_Translation_Job
|
||||
*/
|
||||
$maybeUpdateTranslationServiceColumn = function ( $jobObject ) {
|
||||
if ( $jobObject->get_translation_service() !== 'local' ) {
|
||||
$jobObject->set_basic_data_property( 'translation_service', 'local' );
|
||||
Jobs::setTranslationService( $jobObject->get_id(), 'local' );
|
||||
}
|
||||
|
||||
return $jobObject;
|
||||
};
|
||||
|
||||
return Either::of( $params )
|
||||
->map( [ $this->manualJobs, 'createOrReuse' ] )
|
||||
->filter( Logic::isTruthy() )
|
||||
->filter( invoke( 'user_can_translate' )->with( User::getCurrent() ) )
|
||||
->map( $maybeUpdateTranslationServiceColumn )
|
||||
->map( Logic::ifElse( $shouldOpenCTE, $this->displayCTE(), $this->tryToDisplayATE( $params ) ) )
|
||||
->getOrElse( [ 'editor' => \WPML_TM_Editors::NONE, 'jobObject' => null ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function tryToDisplayATE( $params = null, $jobObject = null ) {
|
||||
$fn = curryN( 2, function ( $params, $jobObject ) {
|
||||
$handleNotActiveATE = Logic::ifElse(
|
||||
[ \WPML_TM_ATE_Status::class, 'is_active' ],
|
||||
Either::of(),
|
||||
pipe( $this->handleATEJobCreationError( $params, self::ATE_IS_NOT_ACTIVE ), Either::left() )
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new ATE job when somebody clicks the "pencil" icon to edit existing translation.
|
||||
*
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return Either<\WPML_Element_Translation_Job>
|
||||
*/
|
||||
$cloneCompletedATEJob = function ( $jobObject ) use ( $params ) {
|
||||
if ( $this->isValidATEJob( $jobObject ) && (int) $jobObject->get_status_value() === ICL_TM_COMPLETE ) {
|
||||
$sentFrom = isset( $params['preview'] ) ? Jobs::SENT_FROM_REVIEW : Jobs::SENT_MANUALLY;
|
||||
|
||||
return $this->clone_jobs->cloneCompletedATEJob( $jobObject, $sentFrom )
|
||||
->bimap( $this->handleATEJobCreationError( $params, self::ATE_JOB_COULD_NOT_BE_CREATED ), Fns::identity() );
|
||||
}
|
||||
|
||||
return Either::of( $jobObject );
|
||||
};
|
||||
|
||||
$handleMissingATEJob = function ( $jobObject ) use ( $params ) {
|
||||
// ATE editor is already set. All fine, we can proceed.
|
||||
if ( $this->isValidATEJob( $jobObject ) ) {
|
||||
return Either::of( $jobObject );
|
||||
}
|
||||
|
||||
/**
|
||||
* The new job has been created because either there was no translation at all or translation was "needs update".
|
||||
* The ATE job could not be created inside WPML_TM_ATE_Jobs_Actions::added_translation_jobs ,and we have to return the error message.
|
||||
*/
|
||||
if ( $this->isNewJobCreated( $params, $jobObject ) ) {
|
||||
return Either::left( $this->handleATEJobCreationError( $params, self::ATE_JOB_COULD_NOT_BE_CREATED, $jobObject ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* It creates a corresponding job in ATE for already existing WPML job in such situations:
|
||||
* 1. Previously job was created in CTE, but a user selected the setting to translate existing CTE jobs in ATE
|
||||
* 2. The job used to be handled by the Translation Proxy or the native WP editor
|
||||
* 3. ATE job could not be created before and user clicked "Retry" button
|
||||
* 4. Job was sent via basket and ATE job could not be created
|
||||
*/
|
||||
return $this->createATECounterpartForExistingWPMLJob( $params, $jobObject );
|
||||
};
|
||||
|
||||
return Either::of( $jobObject )
|
||||
->chain( $handleNotActiveATE )
|
||||
->chain( $cloneCompletedATEJob )
|
||||
->chain( $handleMissingATEJob )
|
||||
->map( Fns::tap( pipe( invoke( 'get_id' ), Jobs::setStatus( Fns::__, ICL_TM_IN_PROGRESS ) ) ) )
|
||||
->map( $this->openATE( $params ) )
|
||||
->coalesce( Fns::identity(), Fns::identity() )
|
||||
->get();
|
||||
} );
|
||||
|
||||
return call_user_func_array( $fn, func_get_args() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function displayCTE( $jobObject = null ) {
|
||||
$fn = curryN( 1, function ( $jobObject ) {
|
||||
wpml_tm_load_old_jobs_editor()->set( $jobObject->get_id(), \WPML_TM_Editors::WPML );
|
||||
|
||||
return [ 'editor' => \WPML_TM_Editors::WPML, 'jobObject' => $jobObject ];
|
||||
} );
|
||||
|
||||
return call_user_func_array( $fn, func_get_args() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybeSetReviewStatus( $jobObject ) {
|
||||
if ( Relation::propEq( 'review_status', ReviewStatus::NEEDS_REVIEW, $jobObject->to_array() ) ) {
|
||||
Jobs::setReviewStatus( $jobObject->get_id(), SetupOption::shouldTranslateEverything() ? ReviewStatus::EDITING : null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns an url to place where a user should be redirected. The url contains a job id and error's code.
|
||||
*
|
||||
* @param array $params
|
||||
* @param int $code
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function handleATEJobCreationError( $params = null, $code = null, $jobObject = null ) {
|
||||
$fn = curryN( 3, function ( $params, $code, $jobObject ) {
|
||||
ATERetry::incrementCount( $jobObject->get_id() );
|
||||
|
||||
$retryCount = ATERetry::getCount( $jobObject->get_id() );
|
||||
if ( $retryCount > 0 ) {
|
||||
Storage::add( Entry::retryJob( $jobObject->get_id(),
|
||||
[
|
||||
'retry_count' => ATERetry::getCount( $jobObject->get_id() )
|
||||
]
|
||||
) );
|
||||
}
|
||||
|
||||
return [
|
||||
'editor' => \WPML_TM_Editors::ATE,
|
||||
'url' => add_query_arg( [ 'ateJobCreationError' => $code, 'jobId' => $jobObject->get_id() ], $this->getReturnUrl( $params ) )
|
||||
];
|
||||
} );
|
||||
|
||||
return call_user_func_array( $fn, func_get_args() );
|
||||
}
|
||||
|
||||
/**
|
||||
* It asserts a job's editor.
|
||||
*
|
||||
* @param string $editor
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isJobEditorEqualTo( $editor, $jobObject ) {
|
||||
return $jobObject->get_basic_data_property( 'editor' ) === $editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* It checks if we created a new entry in wp_icl_translate_job table.
|
||||
* It happens when none translation for a specific language has existed so far or when a translation has been "needs update".
|
||||
*
|
||||
* @param array $params
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isNewJobCreated( $params , $jobObject ) {
|
||||
return (int) $jobObject->get_id() !== (int) Obj::prop( 'job_id', $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return callable|Left<array>|Right<\WPML_Element_Translation_Job>
|
||||
*/
|
||||
private function createATECounterpartForExistingWPMLJob( $params, $jobObject ) {
|
||||
if ( $this->clone_jobs->cloneWPMLJob( $jobObject->get_id() ) ) {
|
||||
ATERetry::reset( $jobObject->get_id() );
|
||||
$jobObject->set_basic_data_property( 'editor', \WPML_TM_Editors::ATE );
|
||||
|
||||
return Either::of( $jobObject );
|
||||
}
|
||||
|
||||
return Either::left( $this->handleATEJobCreationError( $params, self::ATE_JOB_COULD_NOT_BE_CREATED, $jobObject ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* At this stage, we know that a corresponding job in ATE is created and we should open ATE editor.
|
||||
* We are trying to do that.
|
||||
*
|
||||
* @param array $params
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return false|mixed
|
||||
*/
|
||||
private function openATE( $params = null, $jobObject = null ) {
|
||||
$fn = curryN( 2, function ( $params, $jobObject ) {
|
||||
$this->maybeSetReviewStatus( $jobObject );
|
||||
|
||||
$editor_url = apply_filters( 'wpml_tm_ate_jobs_editor_url', '', $jobObject->get_id(), $this->getReturnUrl( $params ) );
|
||||
|
||||
if ( $editor_url ) {
|
||||
$response['editor'] = \WPML_TM_Editors::ATE;
|
||||
$response['url'] = $editor_url;
|
||||
$response['jobObject'] = $jobObject;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->handleATEJobCreationError( $params, self::ATE_EDITOR_URL_COULD_NOT_BE_FETCHED, $jobObject );
|
||||
} );
|
||||
|
||||
return call_user_func_array( $fn, func_get_args() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getReturnUrl( $params ) {
|
||||
$return_url = '';
|
||||
|
||||
if ( array_key_exists( 'return_url', $params ) ) {
|
||||
$return_url = filter_var( $params['return_url'], FILTER_SANITIZE_URL );
|
||||
|
||||
$return_url_parts = wp_parse_url( $return_url );
|
||||
|
||||
$admin_url = get_admin_url();
|
||||
$admin_url_parts = wp_parse_url( $admin_url );
|
||||
|
||||
if ( strpos( $return_url_parts['path'], $admin_url_parts['path'] ) === 0 ) {
|
||||
$admin_url_parts['path'] = $return_url_parts['path'];
|
||||
} else {
|
||||
$admin_url_parts = $return_url_parts;
|
||||
}
|
||||
|
||||
$admin_url_parts['query'] = $this->prepareQueryParameters(
|
||||
Obj::propOr( '', 'query', $return_url_parts ),
|
||||
Obj::prop( 'lang', $params )
|
||||
);
|
||||
|
||||
$return_url = http_build_url( $admin_url_parts );
|
||||
}
|
||||
|
||||
return $return_url;
|
||||
}
|
||||
|
||||
private function prepareQueryParameters( $query, $returnLanguage ) {
|
||||
$parameters = [];
|
||||
parse_str( $query, $parameters );
|
||||
|
||||
unset( $parameters['ate_original_id'] );
|
||||
unset( $parameters['back'] );
|
||||
unset( $parameters['complete'] );
|
||||
|
||||
if ( $returnLanguage ) {
|
||||
// We need the lang parameter to display the post list in the language which was used before ATE.
|
||||
$parameters['lang'] = $returnLanguage;
|
||||
}
|
||||
|
||||
return http_build_query( $parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WPML_Element_Translation_Job $jobObject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidATEJob( \WPML_Element_Translation_Job $jobObject ) {
|
||||
return $this->isJobEditorEqualTo( \WPML_TM_Editors::ATE, $jobObject ) &&
|
||||
(int) $jobObject->get_basic_data_property( 'editor_job_id' ) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\TM\Editor;
|
||||
|
||||
use WPML\FP\Cast;
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Logic;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
use WPML\LIB\WP\Hooks;
|
||||
use WPML\LIB\WP\Option;
|
||||
use WPML\TM\API\Jobs;
|
||||
use WPML\UIPage;
|
||||
use function WPML\Container\make;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
class ManualJobCreationErrorNotice implements \IWPML_Backend_Action {
|
||||
|
||||
const RETRY_LIMIT = 3;
|
||||
|
||||
public function add_hooks() {
|
||||
if ( \WPML_TM_ATE_Status::is_enabled() ) {
|
||||
|
||||
Hooks::onAction( 'wp_loaded' )
|
||||
->then( function () {
|
||||
/** @var \WPML_Notices $notices */
|
||||
$notices = make( \WPML_Notices::class );
|
||||
|
||||
if ( isset( $_GET['ateJobCreationError'] ) ) {
|
||||
$notice = $notices->create_notice( __CLASS__, $this->getContent( $_GET ) );
|
||||
|
||||
$notice->set_css_class_types( 'error' );
|
||||
$notice->set_dismissible( false );
|
||||
|
||||
$notices->add_notice( $notice );
|
||||
} else {
|
||||
$notices->remove_notice( 'default', __CLASS__ );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getContent( array $params ) {
|
||||
$isATENotActiveError = pipe( Obj::prop( 'ateJobCreationError' ), Cast::toInt(), Relation::equals( Editor::ATE_IS_NOT_ACTIVE ) );
|
||||
$isRetryLimitExceeded = pipe( Obj::prop( 'jobId' ), [ ATERetry::class, 'getCount' ], Relation::gt( self::RETRY_LIMIT ) );
|
||||
|
||||
return Logic::cond( [
|
||||
[ $isATENotActiveError, [ self::class, 'ateNotActiveMessage' ] ],
|
||||
[ $isRetryLimitExceeded, [ self::class, 'retryMessage' ] ],
|
||||
[ Fns::always( true ), [ self::class, 'retryFailedMessage' ] ]
|
||||
], $params );
|
||||
}
|
||||
|
||||
public static function retryMessage( array $params ) {
|
||||
$returnUrl = \remove_query_arg( [ 'ateJobCreationError', 'jobId' ], Jobs::getCurrentUrl() );
|
||||
$jobEditUrl = Jobs::getEditUrl( $returnUrl, Obj::prop( 'jobId', $params ) );
|
||||
|
||||
$fallbackErrorMessage = sprintf(
|
||||
'<div class="wpml-display-flex wpml-display-flex-center">%1$s <a class="button wpml-margin-left-sm" href="%2$s">%3$s</a></div>',
|
||||
__( "WPML didn't manage to translate this page.", 'wpml-translation-management' ),
|
||||
$jobEditUrl,
|
||||
__( 'Try again', 'wpml-translation-management' )
|
||||
);
|
||||
|
||||
$tryAgainTextLink = sprintf( '<a href="%1$s">%2$s</a>',
|
||||
$jobEditUrl,
|
||||
__( 'Try again', 'wpml-translation-management' ) );
|
||||
|
||||
$ateApiErrorMessage = ATEDetailedErrorMessage::readDetailedError( $tryAgainTextLink );
|
||||
|
||||
return $ateApiErrorMessage ?: $fallbackErrorMessage;
|
||||
}
|
||||
|
||||
public static function retryFailedMessage() {
|
||||
$fallbackErrorMessage = '<div>' .
|
||||
sprintf(
|
||||
__( 'WPML tried to translate this page three times and failed. To get it fixed, contact %s', 'wpml-translation-management' ),
|
||||
'<a target=\'_blank\' href="https://wpml.org/forums/forum/english-support/">' . __( 'WPML support', 'wpml-translation-management' ) . '</a>'
|
||||
) . '</div>';
|
||||
|
||||
$ateApiErrorMessage = ATEDetailedErrorMessage::readDetailedError();
|
||||
|
||||
return $ateApiErrorMessage ?: $fallbackErrorMessage;
|
||||
|
||||
}
|
||||
|
||||
public static function ateNotActiveMessage() {
|
||||
return '<div>' .
|
||||
sprintf(
|
||||
__( 'WPML’s Advanced Translation Editor is enabled but not activated. Go to %s to resolve the issue.', 'wpml-translation-management' ),
|
||||
'<a href="' . UIPage::getTMDashboard() . '">' . __( 'WPML Translation Management Dashboard', 'wpml-translation-management' ) . '</a>'
|
||||
)
|
||||
. '</div>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Editors {
|
||||
const ATE = 'ate';
|
||||
const WPML = 'wpml';
|
||||
const WP = 'wp';
|
||||
const NONE = 'none';
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Old_Jobs_Editor {
|
||||
|
||||
const OPTION_NAME = 'wpml-old-jobs-editor';
|
||||
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/** @var WPML_Translation_Job_Factory */
|
||||
private $job_factory;
|
||||
|
||||
public function __construct( WPML_Translation_Job_Factory $job_factory ) {
|
||||
global $wpdb;
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
$this->job_factory = $job_factory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function get( $job_id ) {
|
||||
$current_editor = $this->get_current_editor( $job_id );
|
||||
|
||||
if ( WPML_TM_Editors::NONE === $current_editor || WPML_TM_Editors::ATE === $current_editor ) {
|
||||
return $current_editor;
|
||||
} else {
|
||||
return get_option( self::OPTION_NAME, null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldStickToWPMLEditor( $job_id ) {
|
||||
$sql = "
|
||||
SELECT job.editor
|
||||
FROM {$this->wpdb->prefix}icl_translate_job job
|
||||
WHERE job.job_id < %d AND job.rid = (
|
||||
SELECT rid FROM {$this->wpdb->prefix}icl_translate_job WHERE job_id = %s
|
||||
)
|
||||
ORDER BY job.job_id DESC
|
||||
";
|
||||
|
||||
$previousJobEditor = $this->wpdb->get_var( $this->wpdb->prepare( $sql, $job_id, $job_id ) );
|
||||
|
||||
return $previousJobEditor === WPML_TM_Editors::WPML && get_option( self::OPTION_NAME, null ) === WPML_TM_Editors::WPML;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function editorForTranslationsPreviouslyCreatedUsingCTE( ) {
|
||||
return get_option( self::OPTION_NAME, WPML_TM_Editors::WPML );
|
||||
}
|
||||
|
||||
public function set( $job_id, $editor ) {
|
||||
$data = [ 'editor' => $editor ];
|
||||
if ( $editor !== WPML_TM_Editors::ATE ) {
|
||||
$data['editor_job_id'] = null;
|
||||
}
|
||||
|
||||
$this->job_factory->update_job_data( $job_id, $data );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $job_id
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_current_editor( $job_id ) {
|
||||
$sql = "SELECT editor FROM {$this->wpdb->prefix}icl_translate_job WHERE job_id = %d";
|
||||
|
||||
return $this->wpdb->get_var( $this->wpdb->prepare( $sql, $job_id ) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user