PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / _inc / blogging-prompts.php
jetpack / _inc Last commit date
blocks 2 days ago build 2 days ago content-guidelines-ai 2 days ago fonts 1 year ago genericons 4 months ago lib 2 days ago accessible-focus.js 5 years ago blogging-prompts.php 1 week ago class.jetpack-provision.php 7 months ago content-guidelines-ai.js 1 week ago content-guidelines-ai.php 1 week ago crowdsignal-shortcode.js 1 year ago crowdsignal-survey.js 5 years ago deprecate.js 7 months ago facebook-embed.js 4 years ago gallery-settings.js 5 years ago genericons.php 1 year ago jetpack-admin.js 3 years ago jetpack-deactivate-dialog.js 1 year ago jetpack-modules.js 1 year ago jetpack-modules.models.js 7 months ago jetpack-modules.views.js 7 months ago polldaddy-shortcode.js 3 months ago site-switcher-endpoint.php 5 months ago site-switcher.js 5 months ago site-switcher.php 5 months ago social-logos.php 3 months ago twitter-timeline.js 1 month ago
blogging-prompts.php
299 lines
1 <?php
2 /**
3 * Used by the blogging prompt feature.
4 *
5 * @package automattic/jetpack
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit( 0 );
10 }
11
12 /**
13 * Hooked functions.
14 */
15
16 /**
17 * Adds the blogging prompt key post meta to the list of allowed post meta to be updated by rest api.
18 *
19 * @param array $keys Array of post meta keys that are allowed public metadata.
20 *
21 * @return array
22 */
23 function jetpack_blogging_prompts_add_meta_data( $keys ) {
24 $keys[] = '_jetpack_blogging_prompt_key';
25 return $keys;
26 }
27
28 add_filter( 'rest_api_allowed_public_metadata', 'jetpack_blogging_prompts_add_meta_data' );
29
30 /**
31 * Sets up a new post as an answer to a blogging prompt.
32 *
33 * When we know a user is explicitly answering a prompt, pre-populate the post meta to mark the post as a prompt response,
34 * in case they decide to remove the block from the post content, preventing they meta from being added later.
35 *
36 * Called on `wp_insert_post` hook.
37 *
38 * @param int $post_id ID of post being inserted.
39 * @return void
40 */
41 function jetpack_setup_blogging_prompt_response( $post_id ) {
42 // 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.
43 $prompt_id = isset( $_GET['answer_prompt'] ) && absint( $_GET['answer_prompt'] ) ? absint( $_GET['answer_prompt'] ) : false;
44
45 if ( ! jetpack_is_new_post_screen() || ! $prompt_id ) {
46 return;
47 }
48
49 // Make sure the prompt exists.
50 $prompt = jetpack_get_blogging_prompt_by_id( $prompt_id );
51
52 if ( $prompt ) {
53 update_post_meta( $post_id, '_jetpack_blogging_prompt_key', $prompt_id );
54 wp_add_post_tags( $post_id, array( 'dailyprompt', "dailyprompt-$prompt_id" ) );
55 if ( array_key_exists( 'bloganuary_id', $prompt ) ) {
56 wp_add_post_tags( $post_id, array( 'bloganuary', $prompt['bloganuary_id'] ) );
57 }
58 }
59 }
60
61 add_action( 'wp_insert_post', 'jetpack_setup_blogging_prompt_response' );
62
63 /**
64 * When a published posts answers a blogging prompt, store the prompt id in the post meta.
65 *
66 * @param int $post_id Post ID.
67 * @param WP_Post $post Post object.
68 * @param bool $update Whether this is an existing post being updated.
69 * @param null|WP_Post $post_before Null for new posts, the WP_Post object prior
70 * to the update for updated posts.
71 */
72 function jetpack_mark_if_post_answers_blogging_prompt( $post_id, $post, $update, $post_before ) {
73 if ( ! $post instanceof WP_Post ) {
74 return;
75 }
76
77 $post_type = $post->post_type ?? null;
78 $post_content = $post->post_content ?? null;
79
80 if ( 'post' !== $post_type || ! $post_content ) {
81 return;
82 }
83
84 $new_status = $post->post_status ?? null;
85 $old_status = $post_before && isset( $post_before->post_status ) ? $post_before->post_status : null;
86
87 // Make sure we are publishing a post, and it's not already published.
88 if ( 'publish' !== $new_status || 'publish' === $old_status ) {
89 return;
90 }
91
92 $scanner = \Automattic\Block_Scanner::create( $post->post_content );
93 if ( ! $scanner ) {
94 return;
95 }
96
97 $prompt_id = null;
98 $total_blocks = 0;
99 $found_prompt_block = false;
100
101 while ( $scanner->next_delimiter() ) {
102 if ( $scanner->opens_block() ) {
103 ++$total_blocks;
104
105 if ( ! $found_prompt_block && $scanner->is_block_type( 'jetpack/blogging-prompt' ) ) {
106 $attributes = $scanner->allocate_and_return_parsed_attributes();
107 if ( $attributes && isset( $attributes['promptId'] ) ) {
108 $prompt_id = absint( $attributes['promptId'] );
109 }
110 $found_prompt_block = true;
111 }
112
113 // Early exit: if we found the prompt and have >1 blocks, we have all info needed
114 if ( $found_prompt_block && $total_blocks > 1 ) {
115 break;
116 }
117 }
118 }
119
120 if ( ! $found_prompt_block || ! $prompt_id || $total_blocks <= 1 ) {
121 return;
122 }
123
124 $has_prompt_tag = has_tag( 'dailyprompt', $post ) || has_tag( "dailyprompt-{$prompt_id}", $post );
125
126 if ( ! $has_prompt_tag ) {
127 return;
128 }
129
130 update_post_meta( $post->ID, '_jetpack_blogging_prompt_key', $prompt_id );
131 }
132
133 add_action( 'wp_after_insert_post', 'jetpack_mark_if_post_answers_blogging_prompt', 10, 4 );
134
135 /**
136 * Utility functions.
137 */
138
139 /**
140 * Retrieve a blogging prompt by prompt ID.
141 *
142 * @param int $prompt_id ID of the prompt fetch.
143 * @return array|null Prompt object or null.
144 */
145 function jetpack_get_blogging_prompt_by_id( $prompt_id ) {
146 // Ensure the REST API endpoint we need is loaded.
147 require_once __DIR__ . '/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v3-endpoint-blogging-prompts.php';
148
149 $locale = get_locale();
150 $route = sprintf( '/wpcom/v3/blogging-prompts/%d', $prompt_id );
151
152 $request = new WP_REST_Request( 'GET', $route );
153 $request->set_param( '_locale', $locale );
154 $request->set_param( 'force_year', gmdate( 'Y' ) );
155
156 $response = rest_do_request( $request );
157
158 if ( $response->is_error() || WP_Http::OK !== $response->get_status() ) {
159 return null;
160 }
161
162 $prompt = $response->get_data();
163
164 return $prompt;
165 }
166
167 /**
168 * Retrieve daily blogging prompts from the wpcom API and cache them.
169 *
170 * @param int $time Unix timestamp representing the day for which to get blogging prompts.
171 * @return stdClass[]|null Array of blogging prompt objects or null.
172 */
173 function jetpack_get_daily_blogging_prompts( $time = 0 ) {
174 $timestamp = $time ? $time : time();
175
176 // Include prompts from the previous day, just in case someone has an outdated prompt id.
177 $day_before = wp_date( 'Y-m-d', $timestamp - DAY_IN_SECONDS );
178 $locale = get_locale();
179 $transient_key = 'jetpack_blogging_prompt_' . $day_before . '_' . $locale;
180 $daily_prompts = get_transient( $transient_key );
181
182 // Return the cached prompt, if we have it. Otherwise fetch it from the API.
183 if ( false !== $daily_prompts ) {
184 return $daily_prompts;
185 }
186
187 $blog_id = \Jetpack_Options::get_option( 'id' );
188 $path = '/sites/' . rawurldecode( $blog_id ) . '/blogging-prompts?from=' . rawurldecode( $day_before ) . '&number=10&_locale=' . rawurldecode( $locale );
189
190 $args = array(
191 'headers' => array(
192 'Content-Type' => 'application/json',
193 'X-Forwarded-For' => ( new \Automattic\Jetpack\Status\Visitor() )->get_ip( true ),
194 ),
195 // `method` and `url` are needed for using `WPCOM_API_Direct::do_request`
196 // `wpcom_json_api_request_as_user` will generate and overwrite these.
197 'method' => \WP_REST_Server::READABLE,
198 'url' => JETPACK__WPCOM_JSON_API_BASE . '/wpcom/v2' . $path,
199 );
200
201 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
202 // This will load the library, but it may be too late to automatically load any endpoints using WPCOM_API_Direct::register_endpoints.
203 // In that case, call `wpcom_rest_api_v2_load_plugin_files( 'wp-content/rest-api-plugins/endpoints/blogging-prompts.php' )`
204 // on the `init` hook to load the blogging-prompts endpoint before calling this function.
205 require_once WP_CONTENT_DIR . '/lib/wpcom-api-direct/wpcom-api-direct.php';
206 $response = \WPCOM_API_Direct::do_request( $args );
207 } else {
208 $response = \Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_user( $path, 'v2', $args, null, 'wpcom' );
209 }
210 $response_status = wp_remote_retrieve_response_code( $response );
211
212 if ( is_wp_error( $response ) || $response_status !== \WP_Http::OK ) {
213 return null;
214 }
215
216 $body = json_decode( wp_remote_retrieve_body( $response ) );
217
218 if ( ! $body || ! isset( $body->prompts ) ) {
219 return null;
220 }
221
222 $prompts = $body->prompts;
223 set_transient( $transient_key, $prompts, DAY_IN_SECONDS );
224
225 return $prompts;
226 }
227
228 /**
229 * Determines if the site has publish posts or plans to publish posts.
230 *
231 * @return bool
232 */
233 function jetpack_has_or_will_publish_posts() {
234 // Lets count the posts.
235 $count_posts_object = wp_count_posts( 'post' );
236 $count_posts = (int) $count_posts_object->publish + (int) $count_posts_object->future + (int) $count_posts_object->draft;
237
238 return $count_posts_object->publish >= 2 || $count_posts >= 100;
239 }
240
241 /**
242 * Determines if the site has a posts page or shows posts on the front page.
243 *
244 * @return bool
245 */
246 function jetpack_has_posts_page() {
247 // The site is set up to be a blog.
248 if ( 'posts' === get_option( 'show_on_front' ) ) {
249 return true;
250 }
251
252 // There is a page set to show posts.
253 $is_posts_page_set = (int) get_option( 'page_for_posts' ) > 0;
254 if ( $is_posts_page_set ) {
255 return true;
256 }
257
258 return false;
259 }
260
261 /**
262 * Determines if site had the "Write" intent set when created.
263 *
264 * @return bool
265 */
266 function jetpack_has_write_intent() {
267 return 'write' === get_option( 'site_intent', '' );
268 }
269
270 /**
271 * Determines if the current screen (in wp-admin) is creating a new post.
272 *
273 * /wp-admin/post-new.php
274 *
275 * @return bool
276 */
277 function jetpack_is_new_post_screen() {
278 global $current_screen;
279
280 if (
281 $current_screen instanceof \WP_Screen &&
282 'add' === $current_screen->action &&
283 'post' === $current_screen->post_type
284 ) {
285 return true;
286 }
287
288 return false;
289 }
290
291 /**
292 * Determines if the site might have a blog.
293 *
294 * @return bool
295 */
296 function jetpack_is_potential_blogging_site() {
297 return jetpack_has_write_intent() || jetpack_has_posts_page() || jetpack_has_or_will_publish_posts();
298 }
299