then( spreadArgs( NonPublicCPTPreview::allowReviewPostTypeQueryVar() ) ); Hooks::onFilter( 'request' ) ->then( spreadArgs( NonPublicCPTPreview::enforceReviewPostTypeIfSet() ) ); Hooks::onFilter( 'the_preview' ) ->then( Hooks::getArgs( [ 0 => 'post' ] ) ) ->then( $this->handleTranslationReview() ); } Hooks::onFilter( 'user_has_cap', 10, 3 ) ->then( spreadArgs( function ( $userCaps, $requiredCaps, $args ) { if ( Relation::propEq( 0, 'edit_post', $args ) ) { $translator = Translators::getCurrent(); if ( $translator->ID ) { $postId = $args[2]; $job = Jobs::getPostJob( $postId, Post::getType( $postId ), WPMLPost::getLang( $postId ) ); if ( ReviewStatus::doesJobNeedReview( $job ) && self::canEditLanguage( $translator, $job ) ) { return Lst::concat( $userCaps, Lst::zipObj( $requiredCaps, Lst::repeat( true, count( $requiredCaps ) ) ) ); } } return $userCaps; } return $userCaps; } ) ); Hooks::onFilter( 'wpml_tm_allowed_translators_for_job', 10, 2 ) ->then( spreadArgs( function ( $allowedTranslators, \WPML_Element_Translation_Job $job ) { $job = $job->to_array(); $translator = Translators::getCurrent(); if ( ReviewStatus::doesJobNeedReview( $job ) && self::canEditLanguage( $translator, $job ) ) { return array_merge( $allowedTranslators, [ $translator->ID ] ); } return $allowedTranslators; } ) ); } private static function canEditLanguage( $translator, $job ) { if ( ! $job ) { return false; } return Lst::includes( Obj::prop('language_code', $job), Obj::pathOr( [], [ 'language_pairs', Obj::prop('source_language_code', $job) ], $translator ) ); } /** * This will ensure to block the standard preview * for non-public CPTs. * * @return bool */ private static function hasValidNonce() { $get = Obj::prop( Fns::__, $_GET ); return (bool) \wp_verify_nonce( $get( 'preview_nonce' ), PreviewLink::getNonceName( (int) $get( 'preview_id' ) ) ); } public function handleTranslationReview() { return function ( $data ) { $post = Obj::prop( 'post', $data ); $jobId = filter_input( INPUT_GET, 'jobId', FILTER_SANITIZE_NUMBER_INT ); if ( $jobId ) { /** * This hooks is fired as soon as a translation review is about to be displayed. * * @since 4.5.0 * * @param int $jobId The job Id. * @param object|\WP_Post $post The job's related object to be reviewed. */ do_action( 'wpml_tm_handle_translation_review', $jobId, $post ); Hooks::onFilter( 'wp_redirect' ) ->then( [ __CLASS__, 'failGracefullyOnPreviewRedirection' ] ); Hooks::onAction( 'template_redirect', PHP_INT_MAX ) ->then( function () { Hooks::onAction( 'wp_footer' ) ->then( [ __CLASS__, 'printReviewToolbarAnchor' ] ); } ); show_admin_bar( false ); $enqueue = Resources::enqueueApp( 'translationReview' ); $enqueue( $this->getData( $jobId, $post ) ); } return $post; }; } public static function printReviewToolbarAnchor() { echo '
'; } /** * @return null This will stop the redirection. */ public static function failGracefullyOnPreviewRedirection() { do_action( 'wp_head' ); self::printReviewToolbarAnchor(); echo '

'. esc_html__( 'Preview is not available', 'wpml-translation-management' ) .'

'. sprintf(esc_html__( 'Click %sEdit Translation%s in the toolbar above to review your translation in the editor.', 'wpml-translation-management' ), '', '') .'

'; return null; } public function getData( $jobId, $post ) { $job = Jobs::get( $jobId ); $editUrl = \add_query_arg( [ 'preview' => 1 ], Jobs::getEditUrl( Jobs::getCurrentUrl(), $jobId ) ); return [ 'name' => 'reviewTranslation', 'data' => [ 'jobEditUrl' => $editUrl, 'nextJobUrl' => NextTranslationLink::get( $job ), 'jobId' => (int) $jobId, 'postId' => $post->ID, 'isPublished' => Relation::propEq( 'post_status', 'publish', $post ) ? 1 : 0, 'needsReview' => ReviewStatus::doesJobNeedReview( $job ), 'completedInATE' => $this->isCompletedInATE( $_GET ), 'needsUpdate' => Relation::propEq( 'review_status', ReviewStatus::EDITING, $job ), 'previousTranslation' => Sanitize::stringProp( 'previousTranslation', $_GET ), 'backUrl' => Obj::prop( 'returnUrl', $_GET ), 'endpoints' => [ 'accept' => AcceptTranslation::class, 'update' => UpdateTranslation::class ], ] ]; } public function isCompletedInATE( $params ) { $completedInATE = pipe( Obj::prop( 'complete_no_changes' ), 'strval', Logic::cond( [ [ Relation::equals( '1' ), Fns::always( 'COMPLETED_WITHOUT_CHANGED' ) ], [ Relation::equals( '0' ), Fns::always( 'COMPLETED' ) ], [ Fns::always( true ), Fns::always( 'NOT_COMPLETED' ) ], ] ) ); return $completedInATE( $params ); } }