Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
71.43% covered (warning)
71.43%
25 / 35
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
New_Episode_Prefill
71.43% covered (warning)
71.43%
25 / 35
20.00% covered (danger)
20.00%
1 / 5
31.29
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 maybe_register_handlers
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
7.73
 assign_category
78.57% covered (warning)
78.57%
11 / 14
0.00% covered (danger)
0.00%
0 / 1
7.48
 prefill_block_content
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
5.39
 is_supported_post
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Server-side prefill for `post-new.php?podcast_episode=1`.
4 *
5 * @package automattic/jetpack-podcast
6 */
7
8namespace Automattic\Jetpack\Podcast;
9
10/**
11 * Prefills the new-post screen with the configured podcast category and, on
12 * Premium, an inserted Podcast Episode block.
13 */
14class New_Episode_Prefill {
15
16    const QUERY_VAR = 'podcast_episode';
17
18    /**
19     * ID of the auto-draft we've already handled this request.
20     *
21     * @var int
22     */
23    private static $handled_post_id = 0;
24
25    /**
26     * Register hooks.
27     */
28    public static function init() {
29        add_action( 'admin_init', array( __CLASS__, 'maybe_register_handlers' ) );
30    }
31
32    /**
33     * Bind on `admin_init` so `$pagenow` is settled.
34     */
35    public static function maybe_register_handlers() {
36        global $pagenow;
37        if ( 'post-new.php' !== $pagenow ) {
38            return;
39        }
40        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
41        if ( ! isset( $_GET[ self::QUERY_VAR ] ) ) {
42            return;
43        }
44        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
45        if ( '1' !== sanitize_text_field( wp_unslash( $_GET[ self::QUERY_VAR ] ) ) ) {
46            return;
47        }
48
49        if ( (int) get_option( 'podcasting_category_id', 0 ) <= 0 ) {
50            return;
51        }
52
53        add_action( 'wp_insert_post', array( __CLASS__, 'assign_category' ), 10, 3 );
54
55        if ( Podcast_Gate::has_product_access() ) {
56            add_filter( 'default_content', array( __CLASS__, 'prefill_block_content' ), 10, 2 );
57        }
58    }
59
60    /**
61     * Assign the configured podcast category to the new auto-draft.
62     *
63     * Narrowed to the initial auto-draft so user-driven saves later in the
64     * session aren't re-overridden; self-unhooks so sibling auto-drafts in the
65     * same request are left alone.
66     *
67     * @param int      $post_id Post ID.
68     * @param \WP_Post $post    Post object.
69     * @param bool     $update  True for updates, false for inserts.
70     */
71    public static function assign_category( $post_id, $post, $update ) {
72        if ( $update ) {
73            return;
74        }
75        if ( ! ( $post instanceof \WP_Post ) ) {
76            return;
77        }
78        if ( ! self::is_supported_post( $post ) || 'auto-draft' !== $post->post_status ) {
79            return;
80        }
81        if ( ! current_user_can( 'edit_post', $post_id ) ) {
82            return;
83        }
84
85        $category_id = (int) get_option( 'podcasting_category_id', 0 );
86        if ( $category_id <= 0 ) {
87            return;
88        }
89
90        wp_set_post_categories( $post_id, array( $category_id ) );
91
92        self::$handled_post_id = (int) $post_id;
93        remove_action( 'wp_insert_post', array( __CLASS__, 'assign_category' ), 10 );
94    }
95
96    /**
97     * Inject a Podcast Episode block as the new post's initial content.
98     *
99     * No-op if another plugin has already filled `$content`, so this composes
100     * politely.
101     *
102     * @param string   $content Default post content.
103     * @param \WP_Post $post    Post object.
104     * @return string
105     */
106    public static function prefill_block_content( $content, $post ) {
107        if ( ! self::is_supported_post( $post ) ) {
108            return $content;
109        }
110        if ( self::$handled_post_id > 0 && (int) $post->ID !== self::$handled_post_id ) {
111            return $content;
112        }
113        if ( '' !== trim( (string) $content ) ) {
114            return $content;
115        }
116
117        remove_filter( 'default_content', array( __CLASS__, 'prefill_block_content' ), 10 );
118
119        return "<!-- wp:jetpack/podcast-episode /-->\n";
120    }
121
122    /**
123     * Whether the post is a core `post` we prefill for.
124     *
125     * @param mixed $post Candidate post object.
126     * @return bool
127     */
128    private static function is_supported_post( $post ) {
129        return $post instanceof \WP_Post && 'post' === $post->post_type;
130    }
131}