$split( SWCC_Rest_Create_Draft::META_CLAIMS ), 'quotes' => $split( SWCC_Rest_Create_Draft::META_UNVERIFIED_QUOTES ), ); } /** * On a delivered article's edit screen, list the flagged passages above the editor (1.0.5). * * Before this the only pointer was the first passage, quoted on the topic, and the operator * had to carry it over to the article by hand. The block editor hoists admin notices into its * own notice area, so this shows in both editors. * * @return void */ public static function review_notice() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; $post = get_post(); if ( ! $screen instanceof WP_Screen || 'post' !== $screen->base || ! $post instanceof WP_Post || ! current_user_can( 'edit_post', $post->ID ) ) { return; } self::render_review_notice( $post->ID ); } /** * The notice itself, separated so it can be rendered without a screen. * * @param int $post_id A delivered article. * @return void */ public static function render_review_notice( $post_id ) { $passages = self::review_passages( $post_id ); if ( empty( $passages['claims'] ) && empty( $passages['quotes'] ) ) { return; } ?>

post_type || ! current_user_can( 'edit_post', $post->ID ) ) { return $actions; } if ( (int) get_post_meta( $post->ID, SWCC_Topic::META_GENERATED, true ) > 0 ) { if ( ! SWCC_Topic::is_regenerating( $post->ID ) ) { $actions['swcc_regenerate'] = '' . esc_html__( 'Regenerate', 'saidweekly' ) . ''; } return $actions; } $url = wp_nonce_url( add_query_arg( array( 'action' => self::ACTION, 'topic_id' => $post->ID, ), admin_url( 'admin-post.php' ) ), self::ACTION . '_' . $post->ID ); $actions['swcc_generate'] = '' . esc_html__( 'Generate now', 'saidweekly' ) . ''; return $actions; } /** * The nonce-protected URL that regenerates a topic's article. * * @param int $topic_id Topic ID. * @return string */ public static function regenerate_url( $topic_id ) { return wp_nonce_url( add_query_arg( array( 'action' => self::ACTION_REGENERATE, 'topic_id' => (int) $topic_id, ), admin_url( 'admin-post.php' ) ), self::ACTION_REGENERATE . '_' . (int) $topic_id ); } /** * The consequence named before a regeneration, as business.md requires for anything billable. * * @return string */ public static function regenerate_confirmation() { return __( 'Write this article again? This uses 1 credit when it succeeds. If the current draft is unedited it is replaced; otherwise the new article is added beside it.', 'saidweekly' ); } /** * Regenerate a topic's article (admin-post handler). * * @return void */ public static function handle_regenerate() { $topic_id = isset( $_GET['topic_id'] ) ? absint( $_GET['topic_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified below with the topic-specific nonce. if ( 0 === $topic_id || ! current_user_can( 'edit_post', $topic_id ) ) { wp_die( esc_html__( 'You do not have permission to regenerate this topic.', 'saidweekly' ) ); } check_admin_referer( self::ACTION_REGENERATE . '_' . $topic_id ); $result = ( new SWCC_Remote_Content_Generator() )->regenerate( $topic_id ); $args = is_wp_error( $result ) ? array( 'swcc_generation' => 'error', 'swcc_message' => $result->get_error_code(), ) : array( 'swcc_generation' => 'queued' ); wp_safe_redirect( add_query_arg( $args, get_edit_post_link( $topic_id, 'raw' ) ) ); exit; } /** * Handle a nonce-protected Generate Now request. * * @return void */ public static function handle() { $topic_id = isset( $_GET['topic_id'] ) ? absint( $_GET['topic_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified below with the topic-specific nonce. if ( 0 === $topic_id || ! current_user_can( 'edit_post', $topic_id ) ) { wp_die( esc_html__( 'You do not have permission to generate this topic.', 'saidweekly' ) ); } check_admin_referer( self::ACTION . '_' . $topic_id ); $result = ( new SWCC_Remote_Content_Generator() )->generate( $topic_id ); $args = is_wp_error( $result ) ? array( 'swcc_generation' => 'error', 'swcc_message' => $result->get_error_code(), ) : array( 'swcc_generation' => 'queued' ); wp_safe_redirect( add_query_arg( $args, get_edit_post_link( $topic_id, 'raw' ) ) ); exit; } /** * Render the result of a manual generation request. * * @return void */ public static function notice() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- redirect-only display state, never used for a write. $state = isset( $_GET['swcc_generation'] ) ? sanitize_key( wp_unslash( $_GET['swcc_generation'] ) ) : ''; if ( 'queued' === $state ) { echo '

' . esc_html__( 'Generation is queued. This page will show the linked draft when delivery finishes.', 'saidweekly' ) . '

'; } elseif ( 'error' === $state ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- redirect-only display state, never used for a write. $message = isset( $_GET['swcc_message'] ) ? sanitize_key( wp_unslash( $_GET['swcc_message'] ) ) : ''; if ( 'swcc_staging_site' === $message ) { echo '

' . esc_html__( 'This installation is designated staging (SWCC_SITE_ROLE) and never asks for articles.', 'saidweekly' ) . '

'; } elseif ( 'swcc_branding_incomplete' === $message ) { $url = admin_url( 'edit.php?post_type=' . SWCC_Topic::POST_TYPE . '&page=' . SWCC_Branding_Page::PAGE_SLUG ); echo '

' . sprintf( '%2$s', esc_url( $url ), esc_html__( 'Complete the required Branding settings before generating.', 'saidweekly' ) ) . '

'; } else { echo '

' . esc_html__( 'Could not queue generation. Confirm activation and try again.', 'saidweekly' ) . '

'; } } } /** * Return the current local delivery state for one job. * * @return void */ public static function ajax_status() { check_ajax_referer( 'swcc_generation_status' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'saidweekly' ) ), 403 ); } $job_id = isset( $_POST['job_id'] ) ? sanitize_text_field( wp_unslash( $_POST['job_id'] ) ) : ''; // An empty job ID would match any post whose job-ID meta is an empty string rather than // matching nothing, and report an unrelated post as this topic's delivered draft. if ( '' === $job_id ) { wp_send_json_error( array( 'message' => __( 'Missing job ID.', 'saidweekly' ) ), 400 ); } $posts = get_posts( array( 'post_type' => 'post', 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future', 'trash' ), 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- job ID is the delivery idempotency key. array( 'key' => SWCC_Topic::META_JOB_ID, 'value' => $job_id, ), ), ) ); if ( empty( $posts ) ) { wp_send_json_success( array( 'state' => 'queued' ) ); } $post_id = (int) $posts[0]; wp_send_json_success( array( 'state' => 'delivered', 'url' => get_edit_post_link( $post_id, 'raw' ), ) ); } /** * Enqueue the poller that watches a queued topic's job from the editor (1.1.1: a registered * script with its configuration passed through wp_add_inline_script(), not a printed tag). * * @param string $hook_suffix Current admin page identifier. * @return void */ public static function enqueue_status_poll( $hook_suffix ) { if ( 'post.php' !== $hook_suffix ) { return; } $screen = get_current_screen(); if ( ! $screen instanceof WP_Screen || SWCC_Topic::POST_TYPE !== $screen->post_type ) { return; } wp_enqueue_script( 'swcc-generation-status', SWCC_PLUGIN_URL . 'assets/js/generation-status.js', array(), SWCC_VERSION, true ); wp_add_inline_script( 'swcc-generation-status', 'window.swccGenerationStatus = ' . wp_json_encode( array( 'nonce' => wp_create_nonce( 'swcc_generation_status' ), 'label' => __( 'Draft delivered — open it.', 'saidweekly' ), ) ) . ';', 'before' ); } }