PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | _inc/blogging-prompts.php +136 -21 12.5.2 → 16.3-a.1 View file →
@@ -4,8 +4,12 @@
4 4 *
5 5 * @package automattic/jetpack
6 6 */
7 7
8 +if ( ! defined( 'ABSPATH' ) ) {
9 + exit( 0 );
10 +}
11 +
8 12 /**
9 13 * Hooked functions.
10 14 */
11 15
@@ -23,13 +27,18 @@
23 27
24 28 add_filter( 'rest_api_allowed_public_metadata', 'jetpack_blogging_prompts_add_meta_data' );
25 29
26 30 /**
27 - * Sets up a new post as an answer to a blogging prompt.
31 + * Sets up a new post as an answer to a blogging prompt (classic new-post screen).
28 32 *
29 33 * When we know a user is explicitly answering a prompt, pre-populate the post meta to mark the post as a prompt response,
30 34 * in case they decide to remove the block from the post content, preventing they meta from being added later.
31 35 *
36 + * REST creations (e.g. the Write editor's POST /wp/v2/posts?answer_prompt=…) are
37 + * handled by jetpack_setup_blogging_prompt_response_rest() on rest_after_insert_post
38 + * instead — that hook runs after the REST controller sets the request's tags, so the
39 + * prompt tags we add aren't overwritten.
40 + *
32 41 * Called on `wp_insert_post` hook.
33 42 *
34 43 * @param int $post_id ID of post being inserted.
35 44 * @return void
@@ -34,26 +43,75 @@
34 43 * @param int $post_id ID of post being inserted.
35 44 * @return void
36 45 */
37 46 function jetpack_setup_blogging_prompt_response( $post_id ) {
47 + if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
48 + return;
49 + }
50 +
51 + if ( ! jetpack_is_new_post_screen() ) {
52 + return;
53 + }
54 +
38 55 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Clicking a prompt response link can happen from notifications, Calypso, wp-admin, email, etc and only sets up a response post (tag, meta, prompt text); the user must take action to actually publish the post.
39 - $prompt_id = isset( $_GET['answer_prompt'] ) && absint( $_GET['answer_prompt'] ) ? absint( $_GET['answer_prompt'] ) : false;
56 + $prompt_id = isset( $_GET['answer_prompt'] ) ? absint( $_GET['answer_prompt'] ) : 0;
57 + if ( $prompt_id ) {
58 + jetpack_apply_blogging_prompt_response( $post_id, $prompt_id );
59 + }
60 +}
40 61
41 - if ( ! jetpack_is_new_post_screen() || ! $prompt_id ) {
62 +add_action( 'wp_insert_post', 'jetpack_setup_blogging_prompt_response' );
63 +
64 +/**
65 + * Sets up a REST-created post (e.g. the Write editor) as an answer to a prompt.
66 + *
67 + * Runs on `rest_after_insert_post`, which fires after the REST controller has set
68 + * the request's tags — so the prompt tags added here survive.
69 + *
70 + * @param WP_Post $post Inserted post object.
71 + * @param WP_REST_Request $request Request object.
72 + * @param bool $creating True when creating, false when updating.
73 + * @return void
74 + */
75 +function jetpack_setup_blogging_prompt_response_rest( $post, $request, $creating ) {
76 + if ( ! $creating || ! $post instanceof WP_Post || 'post' !== $post->post_type ) {
42 77 return;
43 78 }
44 79
80 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET param forwarded by the Write editor on the create request; only sets up a response post (tag, meta).
81 + $prompt_id = isset( $_GET['answer_prompt'] ) ? absint( $_GET['answer_prompt'] ) : 0;
82 + if ( $prompt_id ) {
83 + jetpack_apply_blogging_prompt_response( $post->ID, $prompt_id );
84 + }
85 +}
86 +
87 +add_action( 'rest_after_insert_post', 'jetpack_setup_blogging_prompt_response_rest', 10, 3 );
88 +
89 +/**
90 + * Stamp a post as a blogging-prompt answer: prompt-key meta + prompt tags.
91 + *
92 + * Shared by the classic (wp_insert_post) and REST (rest_after_insert_post) entry
93 + * points so both mark the answer identically.
94 + *
95 + * @param int $post_id Post ID.
96 + * @param int $prompt_id Prompt ID.
97 + * @return void
98 + */
99 +function jetpack_apply_blogging_prompt_response( $post_id, $prompt_id ) {
45 100 // Make sure the prompt exists.
46 101 $prompt = jetpack_get_blogging_prompt_by_id( $prompt_id );
47 102
48 - if ( $prompt ) {
49 - update_post_meta( $post_id, '_jetpack_blogging_prompt_key', $prompt_id );
50 - wp_add_post_tags( $post_id, array( 'dailyprompt', "dailyprompt-$prompt_id" ) );
103 + if ( ! $prompt ) {
104 + return;
51 105 }
106 +
107 + update_post_meta( $post_id, '_jetpack_blogging_prompt_key', $prompt_id );
108 + wp_add_post_tags( $post_id, array( 'dailyprompt', "dailyprompt-$prompt_id" ) );
109 + if ( is_array( $prompt ) && array_key_exists( 'bloganuary_id', $prompt ) ) {
110 + wp_add_post_tags( $post_id, array( 'bloganuary', $prompt['bloganuary_id'] ) );
111 + }
52 112 }
53 113
54 -add_action( 'wp_insert_post', 'jetpack_setup_blogging_prompt_response' );
55 -
56 114 /**
57 115 * When a published posts answers a blogging prompt, store the prompt id in the post meta.
58 116 *
59 117 * @param int $post_id Post ID.
@@ -66,16 +124,16 @@
66 124 if ( ! $post instanceof WP_Post ) {
67 125 return;
68 126 }
69 127
70 - $post_type = isset( $post->post_type ) ? $post->post_type : null;
71 - $post_content = isset( $post->post_content ) ? $post->post_content : null;
128 + $post_type = $post->post_type ?? null;
129 + $post_content = $post->post_content ?? null;
72 130
73 131 if ( 'post' !== $post_type || ! $post_content ) {
74 132 return;
75 133 }
76 134
77 - $new_status = isset( $post->post_status ) ? $post->post_status : null;
135 + $new_status = $post->post_status ?? null;
78 136 $old_status = $post_before && isset( $post_before->post_status ) ? $post_before->post_status : null;
79 137
80 138 // Make sure we are publishing a post, and it's not already published.
81 139 if ( 'publish' !== $new_status || 'publish' === $old_status ) {
@@ -81,21 +139,47 @@
81 139 if ( 'publish' !== $new_status || 'publish' === $old_status ) {
82 140 return;
83 141 }
84 142
85 - $blocks = parse_blocks( $post->post_content );
86 - foreach ( $blocks as $block ) {
87 - if ( 'jetpack/blogging-prompt' === $block['blockName'] ) {
88 - $prompt_id = isset( $block['attrs']['promptId'] ) ? absint( $block['attrs']['promptId'] ) : null;
89 - $has_prompt_tag = has_tag( 'dailyprompt', $post ) || ( $prompt_id && has_tag( "dailyprompt-{$prompt_id}", $post ) );
143 + $scanner = \Automattic\Block_Scanner::create( $post->post_content );
144 + if ( ! $scanner ) {
145 + return;
146 + }
90 147
91 - if ( $prompt_id && $has_prompt_tag && count( $blocks ) > 1 ) {
92 - update_post_meta( $post->ID, '_jetpack_blogging_prompt_key', $prompt_id );
148 + $prompt_id = null;
149 + $total_blocks = 0;
150 + $found_prompt_block = false;
151 +
152 + while ( $scanner->next_delimiter() ) {
153 + if ( $scanner->opens_block() ) {
154 + ++$total_blocks;
155 +
156 + if ( ! $found_prompt_block && $scanner->is_block_type( 'jetpack/blogging-prompt' ) ) {
157 + $attributes = $scanner->allocate_and_return_parsed_attributes();
158 + if ( $attributes && isset( $attributes['promptId'] ) ) {
159 + $prompt_id = absint( $attributes['promptId'] );
160 + }
161 + $found_prompt_block = true;
93 162 }
94 163
95 - break;
164 + // Early exit: if we found the prompt and have >1 blocks, we have all info needed
165 + if ( $found_prompt_block && $total_blocks > 1 ) {
166 + break;
167 + }
96 168 }
97 169 }
170 +
171 + if ( ! $found_prompt_block || ! $prompt_id || $total_blocks <= 1 ) {
172 + return;
173 + }
174 +
175 + $has_prompt_tag = has_tag( 'dailyprompt', $post ) || has_tag( "dailyprompt-{$prompt_id}", $post );
176 +
177 + if ( ! $has_prompt_tag ) {
178 + return;
179 + }
180 +
181 + update_post_meta( $post->ID, '_jetpack_blogging_prompt_key', $prompt_id );
98 182 }
99 183
100 184 add_action( 'wp_after_insert_post', 'jetpack_mark_if_post_answers_blogging_prompt', 10, 4 );
101 185
@@ -103,12 +187,41 @@
103 187 * Utility functions.
104 188 */
105 189
106 190 /**
191 + * Build the blogging-prompts route for the REST context we're running in.
192 + *
193 + * The endpoint sets `wpcom_is_site_specific_endpoint`, so WordPress.com's
194 + * centralized REST API registers it site-scoped, as
195 + * `/wpcom/v3/sites/<blog_id>/blogging-prompts/<id>`. Everywhere else — Atomic,
196 + * self-hosted, and ordinary wp-admin requests on Simple — it registers at
197 + * `/wpcom/v3/blogging-prompts/<id>`. Which shape is live depends on the request
198 + * we happen to be running inside, and asking for the wrong one just 404s and
199 + * silently loses the prompt, so resolve it against the route table each time.
200 + *
201 + * Call this only after the endpoint file is required, so its routes are in
202 + * place by the time `rest_get_server()` fires `rest_api_init`.
203 + *
204 + * @since 16.2
205 + *
206 + * @param int $prompt_id ID of the prompt to fetch.
207 + * @return string REST route for that prompt.
208 + */
209 +function jetpack_get_blogging_prompt_route( $prompt_id ) {
210 + foreach ( array_keys( rest_get_server()->get_routes() ) as $route ) {
211 + if ( str_starts_with( $route, '/wpcom/v3/blogging-prompts/' ) ) {
212 + return sprintf( '/wpcom/v3/blogging-prompts/%d', $prompt_id );
213 + }
214 + }
215 +
216 + return sprintf( '/wpcom/v3/sites/%d/blogging-prompts/%d', get_current_blog_id(), $prompt_id );
217 +}
218 +
219 +/**
107 220 * Retrieve a blogging prompt by prompt ID.
108 221 *
109 222 * @param int $prompt_id ID of the prompt fetch.
110 - * @return stdClass|null Prompt object or null.
223 + * @return array|null Prompt object or null.
111 224 */
112 225 function jetpack_get_blogging_prompt_by_id( $prompt_id ) {
113 226 // Ensure the REST API endpoint we need is loaded.
114 227 require_once __DIR__ . '/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v3-endpoint-blogging-prompts.php';
@@ -113,12 +226,14 @@
113 226 // Ensure the REST API endpoint we need is loaded.
114 227 require_once __DIR__ . '/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v3-endpoint-blogging-prompts.php';
115 228
116 229 $locale = get_locale();
117 - $route = sprintf( '/wpcom/v3/blogging-prompts/%d', $prompt_id );
230 + $route = jetpack_get_blogging_prompt_route( $prompt_id );
118 231
119 232 $request = new WP_REST_Request( 'GET', $route );
120 233 $request->set_param( '_locale', $locale );
234 + $request->set_param( 'force_year', gmdate( 'Y' ) );
235 +
121 236 $response = rest_do_request( $request );
122 237
123 238 if ( $response->is_error() || WP_Http::OK !== $response->get_status() ) {
124 239 return null;