ate_api = $ate_api; $this->ate_jobs = $ate_jobs; $this->sitepress = $sitepress; $this->current_screen = $current_screen; $this->translator_activation_records = $translator_activation_records; } public function add_hooks() { add_action( 'wpml_added_translation_job', [ $this, 'added_translation_job' ], 10, 2 ); add_action( 'wpml_added_translation_jobs', [ $this, 'added_translation_jobs' ], 10, 2 ); add_action( 'admin_notices', [ $this, 'handle_messages' ] ); add_filter( 'wpml_tm_ate_jobs_data', [ $this, 'get_ate_jobs_data_filter' ], 10, 2 ); add_filter( 'wpml_tm_ate_jobs_editor_url', [ $this, 'get_editor_url' ], 10, 3 ); } public function handle_messages() { if ( $this->current_screen->id_ends_with( WPML_TM_FOLDER . '/menu/translations-queue' ) ) { if ( array_key_exists( 'message', $_GET ) ) { if ( array_key_exists( 'ate_job_id', $_GET ) ) { $ate_job_id = filter_var( $_GET['ate_job_id'], FILTER_SANITIZE_NUMBER_INT ); $this->resign_job_on_error( $ate_job_id ); } $message = Sanitize::stringProp( 'message', $_GET ); ?>

added_translation_jobs( array( $translation_service => array( $job_id ) ) ); } /** * @param array $jobs * @param int|null $sentFrom * * @return bool|void * @throws \InvalidArgumentException * @throws \RuntimeException */ public function added_translation_jobs( array $jobs, $sentFrom = null ) { $oldEditor = wpml_tm_load_old_jobs_editor(); $job_ids = Fns::reject( [ $oldEditor, 'shouldStickToWPMLEditor' ], Obj::propOr( [], 'local', $jobs ) ); if ( ! $job_ids ) { return; } $jobs = Fns::map( 'wpml_tm_create_ATE_job_creation_model', $job_ids ); $responses = Fns::map( Fns::unary( partialRight( [ $this, 'create_jobs' ], $sentFrom ) ), $this->getChunkedJobs( $jobs ) ); $created_jobs = $this->getResponsesJobs( $responses, $jobs ); if ( $created_jobs ) { $created_jobs = $this->map_response_jobs( $created_jobs ); $this->ate_jobs->warm_cache( array_keys( $created_jobs ) ); foreach ( $created_jobs as $wpml_job_id => $ate_job_id ) { $this->ate_jobs->store( $wpml_job_id, [ JobRecords::FIELD_ATE_JOB_ID => $ate_job_id ] ); $oldEditor->set( $wpml_job_id, WPML_TM_Editors::ATE ); $translationJob = wpml_tm_load_job_factory()->get_translation_job( $wpml_job_id, false, 0, true ); $jobType = $this->getJobType( $translationJob ); wpml_tm_load_job_factory()->update_job_data( $wpml_job_id, [ 'automatic' => $jobType === 'auto' ? 1 : 0 ] ); if ( $sentFrom === Jobs::SENT_RETRY ) { Jobs::setStatus( $wpml_job_id, ICL_TM_WAITING_FOR_TRANSLATOR ); } } $message = __( '%1$s jobs added to the Advanced Translation Editor.', 'wpml-translation-management' ); $this->add_message( 'updated', sprintf( $message, count( $created_jobs ) ), 'wpml_tm_ate_create_job' ); } else { if ( Lst::includes( $sentFrom, [ Jobs::SENT_AUTOMATICALLY, Jobs::SENT_RETRY ] ) ) { if ( $sentFrom === Jobs::SENT_RETRY ) { $updateJob = function ($jobId) { Jobs::incrementRetryCount($jobId); $this->logRetryError( $jobId ); }; } else { $updateJob = function ( $jobId ) use ( $oldEditor ) { $this->logError( $jobId ); $translationJob = wpml_tm_load_job_factory()->get_translation_job( $jobId, false, 0, true ); $jobType = $this->getJobType( $translationJob ); if ( $jobType === 'auto' ) { Jobs::setStatus( $jobId, ICL_TM_ATE_NEEDS_RETRY ); $oldEditor->set( $jobId, WPML_TM_Editors::ATE ); wpml_tm_load_job_factory()->update_job_data( $jobId, [ 'automatic' => 1 ] ); } }; } wpml_collect( $job_ids )->map( $updateJob ); } $this->add_message( 'error', __( 'Jobs could not be created in Advanced Translation Editor. Please try again or contact the WPML support for help.', 'wpml-translation-management' ), 'wpml_tm_ate_create_job' ); } } private function map_response_jobs( $responseJobs ) { $result = []; foreach ( $responseJobs as $rid => $ate_job_id ) { $jobId = \WPML\TM\API\Job\Map::fromRid( $rid ); if ( $jobId ) { $result[ $jobId ] = $ate_job_id; } } return $result; } /** * @param string $type * @param string $message * @param string|null $id */ private function add_message( $type, $message, $id = null ) { do_action( 'wpml_tm_basket_add_message', $type, $message, $id ); } /** * @param array $jobsData * @param int|null $sentFrom * * @return mixed * @throws \InvalidArgumentException */ public function create_jobs( array $jobsData, $sentFrom ) { $setJobType = Logic::ifElse( Fns::always( $sentFrom ), Obj::assoc( 'job_type', $sentFrom ), Fns::identity() ); list( $existing, $new ) = Lst::partition( pipe( Obj::propOr( null, 'existing_ate_id' ), Logic::isNotNull() ), $jobsData['jobs'] ); $isAuto = Relation::propEq( 'type', 'auto', $jobsData ); return Wrapper::of( [ 'jobs' => $new, 'existing_jobs' => Lst::pluck( 'existing_ate_id', $existing ) ] ) ->map( Obj::assoc( 'auto_translate', $isAuto && Option::shouldTranslateEverything() ) ) ->map( Obj::assoc( 'preview', $isAuto && Option::shouldBeReviewed() ) ) ->map( $setJobType ) ->map( 'wp_json_encode' ) ->map( Json::toArray() ) ->map( [ $this->ate_api, 'create_jobs' ] ) ->get(); } /** * After implementation of wpmltm-3211 and wpmltm-3391, we should not find missing ATE IDs anymore. * Some code below seems dead but we'll keep it for now in case we are missing a specific context. * * @link https://onthegosystems.myjetbrains.com/youtrack/issue/wpmltm-3211 * @link https://onthegosystems.myjetbrains.com/youtrack/issue/wpmltm-3391 */ private function get_ate_jobs_data( array $translation_jobs ) { $ate_jobs_data = array(); $skip_getting_data = false; $ate_jobs_to_create = array(); $this->ate_jobs->warm_cache( wpml_collect( $translation_jobs )->pluck( 'job_id' )->toArray() ); foreach ( $translation_jobs as $translation_job ) { if ( $this->is_ate_translation_job( $translation_job ) ) { $ate_job_id = $this->get_ate_job_id( $translation_job->job_id ); // Start of possibly dead code. if ( ! $ate_job_id ) { $ate_jobs_to_create[] = $translation_job->job_id; $skip_getting_data = true; } // End of possibly dead code. if ( ! $skip_getting_data ) { $ate_jobs_data[ $translation_job->job_id ] = [ 'ate_job_id' => $ate_job_id ]; } } } // Start of possibly dead code. if ( ! $this->is_second_attempt_to_get_jobs_data && $ate_jobs_to_create && $this->added_translation_jobs( array( 'local' => $ate_jobs_to_create ) ) ) { $ate_jobs_data = $this->get_ate_jobs_data( $translation_jobs ); $this->is_second_attempt_to_get_jobs_data = true; } // End of possibly dead code. return $ate_jobs_data; } /** * @param string $default_url * @param int $job_id * @param null|string $return_url * * @return string * @throws \InvalidArgumentException */ public function get_editor_url( $default_url, $job_id, $return_url = null ) { $isUserActivated = $this->translator_activation_records->is_current_user_activated(); if ( $isUserActivated || is_admin() ) { $ate_job_id = $this->ate_jobs->get_ate_job_id( $job_id ); if ( $ate_job_id ) { if ( ! $return_url ) { $return_url = add_query_arg( array( 'page' => WPML_TM_FOLDER . '/menu/translations-queue.php', 'ate-return-job' => $job_id, ), admin_url( '/admin.php' ) ); } $ate_job_url = $this->ate_api->get_editor_url( $ate_job_id, $return_url ); if ( $ate_job_url && ! is_wp_error( $ate_job_url ) ) { return $ate_job_url; } } } return $default_url; } /** * @param $ignore * @param array $translation_jobs * * @return array */ public function get_ate_jobs_data_filter( $ignore, array $translation_jobs ) { return $this->get_ate_jobs_data( $translation_jobs ); } private function get_ate_job_id( $job_id ) { return $this->ate_jobs->get_ate_job_id( $job_id ); } /** * @param mixed $response * * @throws \RuntimeException */ protected function check_response_error( $response ) { if ( is_wp_error( $response ) ) { $code = 0; $message = $response->get_error_message(); if ( $response->error_data && is_array( $response->error_data ) ) { foreach ( $response->error_data as $http_code => $error_data ) { $code = $error_data[0]['status']; $message = ''; switch ( (int) $code ) { case self::RESPONSE_ATE_NOT_ACTIVE_ERROR: $wp_admin_url = admin_url( 'admin.php' ); $mcsetup_page = add_query_arg( array( 'page' => WPML_TM_FOLDER . WPML_Translation_Management::PAGE_SLUG_SETTINGS, 'sm' => 'mcsetup', ), $wp_admin_url ); $mcsetup_page .= '#ml-content-setup-sec-1'; $resend_link = '' . esc_html__( 'Resend that email', 'wpml-translation-management' ) . ''; $message .= '

' . esc_html__( 'WPML cannot send these documents to translation because the Advanced Translation Editor is not fully set-up yet.', 'wpml-translation-management' ) . '

' . esc_html__( 'Please open the confirmation email that you received and click on the link inside it to confirm your email.', 'wpml-translation-management' ) . '

' . $resend_link . '

'; break; case self::RESPONSE_ATE_DUPLICATED_SOURCE_ID: case self::RESPONSE_ATE_UNEXPECTED_ERROR: default: $message = '

' . __( 'Advanced Translation Editor error:', 'wpml-translation-management' ) . '

' . $error_data[0]['message'] . '

'; } $message = '

' . $message . '

'; } } /** @var WP_Error $response */ throw new RuntimeException( $message, $code ); } } /** * @param $ate_job_id */ private function resign_job_on_error( $ate_job_id ) { $job_id = $this->ate_jobs->get_wpml_job_id( $ate_job_id ); if ( $job_id ) { wpml_load_core_tm()->resign_translator( $job_id ); } } /** * @param $translation_job * * @return bool */ private function is_ate_translation_job( $translation_job ) { return 'local' === $translation_job->translation_service && WPML_TM_Editors::ATE === $translation_job->editor; } /** * @param array $responses * @param \WPML_TM_ATE_Models_Job_Create[] $sentJobs * * @return array */ private function getResponsesJobs( $responses, $sentJobs ) { $jobs = []; foreach ( $responses as $response ) { try { $this->check_response_error( $response ); if ( $response && isset( $response->jobs ) ) { $jobs = $jobs + (array) $response->jobs; } } catch ( RuntimeException $ex ) { do_action( 'wpml_tm_basket_add_message', 'error', $ex->getMessage() ); } } $existingJobs = wpml_collect( $sentJobs ) ->filter( Obj::prop( 'existing_ate_id' ) ) ->map( Obj::pick( [ 'source_id', 'existing_ate_id' ] ) ) ->keyBy( 'source_id' ) ->map( Obj::prop( 'existing_ate_id' ) ) ->toArray(); return $jobs + $existingJobs; } /** * @param \WPML_TM_ATE_Models_Job_Create[] $jobs * * @return array */ private function getChunkedJobs( $jobs ) { $chunkedJobs = []; $currentChunk = -1; $currentWordCount = 0; $chunkType = 'auto'; $newChunk = function( $chunkType ) use ( &$chunkedJobs, &$currentChunk, &$currentWordCount ) { $currentChunk ++; $currentWordCount = 0; $chunkedJobs[ $currentChunk ] = [ 'type' => $chunkType, 'jobs' => [] ]; }; $newChunk( $chunkType ); foreach ( $jobs as $job ) { /** @var WPML_Element_Translation_Job $translationJob */ $translationJob = wpml_tm_load_job_factory()->get_translation_job( $job->id, false, 0, true ); if ( $translationJob ) { if ( ! Obj::prop( 'existing_ate_id', $job ) ) { $currentWordCount += $translationJob->estimate_word_count(); } $jobType = $this->getJobType( $translationJob ); if ( $jobType !== $chunkType ) { $chunkType = $jobType; $newChunk( $chunkType ); } if ( $currentWordCount > self::CREATE_ATE_JOB_CHUNK_WORDS_LIMIT && count( $chunkedJobs[ $currentChunk ] ) > 0 ) { $newChunk( $chunkType ); } } $chunkedJobs[ $currentChunk ]['jobs'] [] = $job; } $hasJobs = pipe( Obj::prop( 'jobs' ), Lst::length() ); return Fns::filter( $hasJobs, $chunkedJobs ); } /** * @param int $jobId */ private function logRetryError( $jobId ) { $job = Jobs::get( $jobId ); if ( $job && $job->ate_comm_retry_count ) { Storage::add( Entry::retryJob( $jobId, [ 'retry_count' => $job->ate_comm_retry_count ] ) ); } } /** * @param int $jobId */ private function logError( $jobId ) { $job = Jobs::get( $jobId ); if ( $job ) { Storage::add( Entry::retryJob( $jobId, [ 'retry_count' => 0, 'comment' => 'Sending job to ate failed, queued to be sent again.', ] ) ); } } private function getJobType( $translationJob ) { $document = $translationJob->get_original_document(); if ( ! $document || $document instanceof WPML_Package ) { return 'manual'; } else { return $translationJob->get_source_language_code() === Languages::getDefaultCode() && Jobs::isEligibleForAutomaticTranslations( $translationJob->get_id() ) ? 'auto' : 'manual'; } } }