Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.00% covered (warning)
75.00%
27 / 36
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
New_Episode_Prefill
75.00% covered (warning)
75.00%
27 / 36
20.00% covered (danger)
20.00%
1 / 5
27.89
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
70.00% covered (warning)
70.00%
7 / 10
0.00% covered (danger)
0.00%
0 / 1
5.68
 assign_category
78.57% covered (warning)
78.57%
11 / 14
0.00% covered (danger)
0.00%
0 / 1
7.48
 prefill_block_content
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
6.29
 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 a media
12 * block: the Podcast Episode block on qualifying plans, a core Audio block otherwise.
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        add_filter( 'default_content', array( __CLASS__, 'prefill_block_content' ), 10, 2 );
55    }
56
57    /**
58     * Assign the configured podcast category to the initial auto-draft only, so
59     * later user saves aren't overridden. Self-unhooks after handling one draft.
60     *
61     * @param int      $post_id Post ID.
62     * @param \WP_Post $post    Post object.
63     * @param bool     $update  True for updates, false for inserts.
64     */
65    public static function assign_category( $post_id, $post, $update ) {
66        if ( $update ) {
67            return;
68        }
69        if ( ! ( $post instanceof \WP_Post ) ) {
70            return;
71        }
72        if ( ! self::is_supported_post( $post ) || 'auto-draft' !== $post->post_status ) {
73            return;
74        }
75        if ( ! current_user_can( 'edit_post', $post_id ) ) {
76            return;
77        }
78
79        $category_id = (int) get_option( 'podcasting_category_id', 0 );
80        if ( $category_id <= 0 ) {
81            return;
82        }
83
84        wp_set_post_categories( $post_id, array( $category_id ) );
85
86        self::$handled_post_id = (int) $post_id;
87        remove_action( 'wp_insert_post', array( __CLASS__, 'assign_category' ), 10 );
88    }
89
90    /**
91     * Inject the new post's initial media block: the Podcast Episode block with
92     * product access, else a core Audio block. No-op if `$content` is already set.
93     *
94     * @param string   $content Default post content.
95     * @param \WP_Post $post    Post object.
96     * @return string
97     */
98    public static function prefill_block_content( $content, $post ) {
99        if ( ! self::is_supported_post( $post ) ) {
100            return $content;
101        }
102        if ( self::$handled_post_id > 0 && (int) $post->ID !== self::$handled_post_id ) {
103            return $content;
104        }
105        if ( '' !== trim( (string) $content ) ) {
106            return $content;
107        }
108
109        remove_filter( 'default_content', array( __CLASS__, 'prefill_block_content' ), 10 );
110
111        return Podcast_Gate::has_product_access()
112            ? "<!-- wp:jetpack/podcast-episode /-->\n"
113            : "<!-- wp:audio /-->\n";
114    }
115
116    /**
117     * Whether the post is a core `post` we prefill for.
118     *
119     * @param mixed $post Candidate post object.
120     * @return bool
121     */
122    private static function is_supported_post( $post ) {
123        return $post instanceof \WP_Post && 'post' === $post->post_type;
124    }
125}