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 | json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php +93 -48 12.7.316.3-a.1 View file →
@@ -8,8 +8,12 @@
8 8 * Delete a post: /sites/%s/posts/%d/delete
9 9 * Restore a post: /sites/%s/posts/%d/restore
10 10 */
11 11
12 +if ( ! defined( 'ABSPATH' ) ) {
13 + exit( 0 );
14 +}
15 +
12 16 new WPCOM_JSON_API_Update_Post_v1_2_Endpoint(
13 17 array(
14 18 'description' => 'Create a post.',
15 19 'group' => 'posts',
@@ -105,17 +109,17 @@
105 109 'autosave' => '(bool) True if the post was saved automatically.',
106 110 ),
107 111
108 112 'request_format' => array(
109 - 'date' => "(ISO 8601 datetime) The post's creation time.",
110 - 'title' => '(HTML) The post title.',
111 - 'content' => '(HTML) The post content.',
112 - 'excerpt' => '(HTML) An optional post excerpt.',
113 - 'slug' => '(string) The name (slug) for the post, used in URLs.',
114 - 'author' => '(string) The username or ID for the user to assign the post to.',
115 - 'publicize' => '(array|bool) True or false if the post be shared to external services. An array of services if we only want to share to a select few. Defaults to true.',
116 - 'publicize_message' => '(string) Custom message to be shared to external services.',
117 - 'status' => array(
113 + 'date' => "(ISO 8601 datetime) The post's creation time.",
114 + 'title' => '(HTML) The post title.',
115 + 'content' => '(HTML) The post content.',
116 + 'excerpt' => '(HTML) An optional post excerpt.',
117 + 'slug' => '(string) The name (slug) for the post, used in URLs.',
118 + 'author' => '(string) The username or ID for the user to assign the post to.',
119 + 'publicize' => '(array|bool) True or false if the post be shared to external services. An array of services if we only want to share to a select few. Defaults to true.',
120 + 'publicize_message' => '(string) Custom message to be shared to external services.',
121 + 'status' => array(
118 122 'publish' => 'Publish the post.',
119 123 'private' => 'Privately publish the post.',
120 124 'draft' => 'Save the post as a draft.',
121 125 'future' => 'Schedule the post (alias for publish; you must also set a future date).',
@@ -121,31 +125,32 @@
121 125 'future' => 'Schedule the post (alias for publish; you must also set a future date).',
122 126 'pending' => 'Mark the post as pending editorial approval.',
123 127 'trash' => 'Set the post as trashed.',
124 128 ),
125 - 'sticky' => array(
129 + 'sticky' => array(
126 130 'false' => 'Post is not marked as sticky.',
127 131 'true' => 'Stick the post to the front page.',
128 132 ),
129 - 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
130 - 'parent' => "(int) The post ID of the new post's parent.",
131 - 'terms' => '(object) Mapping of taxonomy to comma-separated list or array of term names',
132 - 'terms_by_id' => '(object) Mapping of taxonomy to comma-separated list or array of term IDs',
133 - 'categories' => '(array|string) Comma-separated list or array of category names',
134 - 'categories_by_id' => '(array|string) Comma-separated list or array of category IDs',
135 - 'tags' => '(array|string) Comma-separated list or array of tag names',
136 - 'tags_by_id' => '(array|string) Comma-separated list or array of tag IDs',
137 - 'format' => array_merge( array( 'default' => 'Use default post format' ), get_post_format_strings() ),
138 - 'discussion' => '(object) A hash containing one or more of the following boolean values, which default to the blog\'s discussion preferences: `comments_open`, `pings_open`',
139 - 'likes_enabled' => '(bool) Should the post be open to likes?',
140 - 'menu_order' => '(int) (Pages only) the order pages should appear in. Use 0 to maintain alphabetical order.',
141 - 'page_template' => '(string) (Pages Only) The page template this page should use.',
142 - 'sharing_enabled' => '(bool) Should sharing buttons show on this post?',
143 - 'featured_image' => '(string) The post ID of an existing attachment to set as the featured image. Pass an empty string to delete the existing image.',
144 - 'media' => '(media) An array of files to attach to the post. To upload media, the entire request should be multipart/form-data encoded. Multiple media items will be displayed in a gallery. Accepts jpg, jpeg, png, gif, pdf, doc, ppt, odt, pptx, docx, pps, ppsx, xls, xlsx, key. Audio and Video may also be available. See <code>allowed_file_types</code> in the options resposne of the site endpoint. <br /><br /><strong>Example</strong>:<br />' .
133 + 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
134 + 'parent' => "(int) The post ID of the new post's parent.",
135 + 'terms' => '(object) Mapping of taxonomy to comma-separated list or array of term names',
136 + 'terms_by_id' => '(object) Mapping of taxonomy to comma-separated list or array of term IDs',
137 + 'categories' => '(array|string) Comma-separated list or array of category names',
138 + 'categories_by_id' => '(array|string) Comma-separated list or array of category IDs',
139 + 'tags' => '(array|string) Comma-separated list or array of tag names',
140 + 'tags_by_id' => '(array|string) Comma-separated list or array of tag IDs',
141 + 'format' => array_merge( array( 'default' => 'Use default post format' ), get_post_format_strings() ),
142 + 'discussion' => '(object) A hash containing one or more of the following boolean values, which default to the blog\'s discussion preferences: `comments_open`, `pings_open`',
143 + 'likes_enabled' => '(bool) Should the post be open to likes?',
144 + 'menu_order' => '(int) (Pages only) the order pages should appear in. Use 0 to maintain alphabetical order.',
145 + 'page_template' => '(string) (Pages Only) The page template this page should use.',
146 + 'sharing_enabled' => '(bool) Should sharing buttons show on this post?',
147 + 'featured_image' => '(string) The post ID of an existing attachment to set as the featured image. Pass an empty string to delete the existing image.',
148 + 'media' => '(media) An array of files to attach to the post. To upload media, the entire request should be multipart/form-data encoded. Multiple media items will be displayed in a gallery. Accepts jpg, jpeg, png, gif, pdf, doc, ppt, odt, pptx, docx, pps, ppsx, xls, xlsx, key. Audio and Video may also be available. See <code>allowed_file_types</code> in the options resposne of the site endpoint. <br /><br /><strong>Example</strong>:<br />' .
145 149 "<code>curl \<br />--form 'title=Image' \<br />--form 'media[]=@/path/to/file.jpg' \<br />-H 'Authorization: BEARER your-token' \<br />'https://public-api.wordpress.com/rest/v1/sites/123/posts/new'</code>",
146 - 'media_urls' => '(array) An array of URLs for images to attach to a post. Sideloads the media in for a post.',
147 - 'metadata' => '(array) Array of metadata objects containing the following properties: `key` (metadata key), `id` (meta ID), `previous_value` (if set, the action will only occur for the provided previous value), `value` (the new value to set the meta to), `operation` (the operation to perform: `update` or `add`; defaults to `update`). All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with proper capabilities. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.',
150 + 'media_urls' => '(array) An array of URLs for images to attach to a post. Sideloads the media in for a post.',
151 + 'metadata' => '(array) Array of metadata objects containing the following properties: `key` (metadata key), `id` (meta ID), `previous_value` (if set, the action will only occur for the provided previous value), `value` (the new value to set the meta to), `operation` (the operation to perform: `update` or `add`; defaults to `update`). All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with proper capabilities. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.',
152 + 'if_not_modified_since' => '(ISO 8601 datetime) If the post has been modified since this time, the post will not be updated.',
148 153 ),
149 154
150 155 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/82974409/posts/881',
151 156
@@ -168,8 +173,10 @@
168 173
169 174 // phpcs:disable PEAR.NamingConventions.ValidClassName.Invalid
170 175 /**
171 176 * Update post v1.2 endpoint class.
177 + *
178 + * @phan-constructor-used-for-side-effects
172 179 */
173 180 class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Post_v1_1_Endpoint {
174 181 /**
175 182 * Create or update a post.
@@ -194,9 +201,9 @@
194 201 }
195 202
196 203 // unhook publicize, it's hooked again later -- without this, skipping services is impossible.
197 204 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
198 - remove_action( 'save_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ), 100, 2 );
205 + remove_action( 'save_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ), 100 );
199 206
200 207 if ( $this->should_load_theme_functions( $post_id ) ) {
201 208 $this->load_theme_functions();
202 209 }
@@ -256,8 +263,13 @@
256 263 if ( ! is_array( $input ) || ! $input ) {
257 264 return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
258 265 }
259 266
267 + $post = get_post( $post_id );
268 + if ( ! $post || is_wp_error( $post ) ) {
269 + return new WP_Error( 'unknown_post', 'Unknown post', 404 );
270 + }
271 +
260 272 if ( isset( $input['status'] ) && 'trash' === $input['status'] && ! current_user_can( 'delete_post', $post_id ) ) {
261 273 return new WP_Error( 'unauthorized', 'User cannot delete post', 403 );
262 274 }
263 275
@@ -265,13 +277,8 @@
265 277 if ( isset( $input['status'] ) && 'future' === $input['status'] ) {
266 278 $input['status'] = 'publish';
267 279 }
268 280
269 - $post = get_post( $post_id );
270 - if ( ! $post || is_wp_error( $post ) ) {
271 - return new WP_Error( 'unknown_post', 'Unknown post', 404 );
272 - }
273 -
274 281 $_post_type = ( ! empty( $input['type'] ) ) ? $input['type'] : $post->post_type;
275 282 $post_type = get_post_type_object( $_post_type );
276 283
277 284 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
@@ -276,8 +283,14 @@
276 283
277 284 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
278 285 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
279 286 }
287 + // The input `if_not_modified_since` input is the format ISO 8601 datetime and get converted to `if_not_modified_since_gmt` and `if_not_modified_since`
288 + if ( ! empty( $input['if_not_modified_since_gmt'] ) ) {
289 + if ( mysql2date( 'U', $post->post_modified_gmt ) > mysql2date( 'U', $input['if_not_modified_since_gmt'] ) ) {
290 + return new WP_Error( 'old-revision', 'There is a revision of this post that is more recent.', 409 );
291 + }
292 + }
280 293
281 294 if ( ! empty( $input['author'] ) ) {
282 295 $author_id = parent::parse_and_set_author( $input['author'], $_post_type );
283 296 unset( $input['author'] );
@@ -289,9 +302,9 @@
289 302 if ( ( isset( $input['status'] ) && 'publish' === $input['status'] ) && 'publish' !== $post->post_status && ! current_user_can( 'publish_post', $post->ID ) ) {
290 303 $input['status'] = 'pending';
291 304 }
292 305 $last_status = $post->post_status;
293 - $new_status = isset( $input['status'] ) ? $input['status'] : $last_status;
306 + $new_status = $input['status'] ?? $last_status;
294 307
295 308 // Make sure that drafts get the current date when transitioning to publish if not supplied in the post.
296 309 // Similarly, scheduled posts that are manually published before their scheduled date should have the date reset.
297 310 $date_in_past = ( strtotime( $post->post_date_gmt ) < time() );
@@ -314,11 +327,16 @@
314 327 }
315 328
316 329 // If date is set, $this->input will set date_gmt, date still needs to be adjusted.
317 330 if ( isset( $input['date_gmt'] ) ) {
318 - $gmt_offset = get_option( 'gmt_offset' );
319 - $time_with_offset = strtotime( $input['date_gmt'] ) + $gmt_offset * HOUR_IN_SECONDS;
320 - $input['date'] = gmdate( 'Y-m-d H:i:s', $time_with_offset );
331 + $date_gmt_timestamp = strtotime( $input['date_gmt'] );
332 + if ( $date_gmt_timestamp ) {
333 + $gmt_offset = (int) get_option( 'gmt_offset' );
334 + $time_with_offset = $date_gmt_timestamp + $gmt_offset * HOUR_IN_SECONDS;
335 + $input['date'] = gmdate( 'Y-m-d H:i:s', $time_with_offset );
336 + } else { // Invalid input.
337 + unset( $input['date_gmt'] );
338 + }
321 339 }
322 340
323 341 if ( ! empty( $author_id ) && get_current_user_id() !== $author_id ) {
324 342 if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
@@ -489,12 +507,12 @@
489 507 $insert['menu_order'] = $input['menu_order'];
490 508 unset( $input['menu_order'] );
491 509 }
492 510
493 - $publicize = isset( $input['publicize'] ) ? $input['publicize'] : null;
511 + $publicize = $input['publicize'] ?? null;
494 512 unset( $input['publicize'] );
495 513
496 - $publicize_custom_message = isset( $input['publicize_message'] ) ? $input['publicize_message'] : null;
514 + $publicize_custom_message = $input['publicize_message'] ?? null;
497 515 unset( $input['publicize_message'] );
498 516
499 517 if ( isset( $input['featured_image'] ) ) {
500 518 $featured_image = trim( $input['featured_image'] );
@@ -501,18 +519,18 @@
501 519 $delete_featured_image = empty( $featured_image );
502 520 unset( $input['featured_image'] );
503 521 }
504 522
505 - $metadata = isset( $input['metadata'] ) ? $input['metadata'] : null;
523 + $metadata = $input['metadata'] ?? null;
506 524 unset( $input['metadata'] );
507 525
508 - $likes = isset( $input['likes_enabled'] ) ? $input['likes_enabled'] : null;
526 + $likes = $input['likes_enabled'] ?? null;
509 527 unset( $input['likes_enabled'] );
510 528
511 - $sharing = isset( $input['sharing_enabled'] ) ? $input['sharing_enabled'] : null;
529 + $sharing = $input['sharing_enabled'] ?? null;
512 530 unset( $input['sharing_enabled'] );
513 531
514 - $sticky = isset( $input['sticky'] ) ? $input['sticky'] : null;
532 + $sticky = $input['sticky'] ?? null;
515 533 unset( $input['sticky'] );
516 534
517 535 foreach ( $input as $key => $value ) {
518 536 $insert[ "post_$key" ] = $value;
@@ -527,10 +545,13 @@
527 545 }
528 546
529 547 $has_media = ! empty( $input['media'] ) ? count( $input['media'] ) : false;
530 548 $has_media_by_url = ! empty( $input['media_urls'] ) ? count( $input['media_urls'] ) : false;
549 + $media_files = array();
550 + $media_urls = array();
551 + $media_attrs = array();
552 + $media_id_string = '';
531 553
532 - $media_id_string = '';
533 554 if ( $has_media || $has_media_by_url ) {
534 555 $media_files = ! empty( $input['media'] ) ? $input['media'] : array();
535 556 $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array();
536 557 $media_attrs = ! empty( $input['media_attrs'] ) ? $input['media_attrs'] : array();
@@ -543,8 +564,28 @@
543 564 $is_dtp_fb_post = true;
544 565 add_filter( 'rest_api_allowed_public_metadata', array( $this, 'dtp_fb_allowed_metadata' ) );
545 566 }
546 567
568 + /**
569 + * Log Media details for a Post creation request.
570 + * Temporary logging for media data.
571 + *
572 + * @see p1709028174665519-slack-CDLH4C1UZ
573 + *
574 + * @since 13.2
575 + *
576 + * @param bool $is_dtp_fb_post Is this for a Facebook import?
577 + * @param int $blog_id Blog ID.
578 + * @param array $input Whole input.
579 + * @param array $media_files File upload data.
580 + * @param array $media_urls URLs to fetch.
581 + * @param array $media_attrs Attributes corresponding to each entry in `$media_files`/`$media_urls`.
582 + * @param array $media_results
583 + * - media_ids: IDs created, by index in `$media_files`/`$media_urls`.
584 + * - errors: Errors encountered, by index in `$media_files`/`$media_urls`.
585 + */
586 + do_action( 'jetpack_dtp_fb_media', $is_dtp_fb_post, $blog_id, $input, $media_files, $media_urls, $media_attrs, $media_results );
587 +
547 588 if ( $new ) {
548 589 if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) {
549 590 switch ( ( $has_media + $has_media_by_url ) ) {
550 591 case 0:
@@ -568,9 +609,9 @@
568 609 break;
569 610 }
570 611 }
571 612
572 - $insert['post_date'] = isset( $insert['post_date'] ) ? $insert['post_date'] : '';
613 + $insert['post_date'] ??= '';
573 614
574 615 if ( $is_dtp_fb_post ) {
575 616 $insert = $this->dtp_fb_preprocess_post( $insert, $metadata );
576 617 }
@@ -579,8 +620,9 @@
579 620 if ( 0 === $post_id ) {
580 621 $post_id = wp_insert_post( add_magic_quotes( $insert ), true );
581 622 }
582 623 } else {
624 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $post is set and validated several blocks earlier if $new (only set once) is falsy.
583 625 $insert['ID'] = $post->ID;
584 626
585 627 // wp_update_post ignores date unless edit_date is set
586 628 // See: https://codex.wordpress.org/Function_Reference/wp_update_post#Scheduling_posts
@@ -665,9 +707,9 @@
665 707
666 708 // Set sharing status of the post.
667 709 if ( $new ) {
668 710 $sharing_enabled = isset( $sharing ) ? (bool) $sharing : true;
669 - if ( false === $sharing_enabled ) {
711 + if ( ! $sharing_enabled ) {
670 712 update_post_meta( $post_id, 'sharing_disabled', 1 );
671 713 }
672 714 } elseif ( isset( $sharing ) && true === $sharing ) {
673 715 delete_post_meta( $post_id, 'sharing_disabled' );
@@ -924,9 +966,9 @@
924 966 */
925 967 protected function should_load_theme_functions( $post_id = null ) {
926 968 if ( empty( $post_id ) ) {
927 969 $input = $this->input( true );
928 - $type = $input['type'];
970 + $type = $input['type'] ?? null;
929 971 } else {
930 972 $type = get_post_type( $post_id );
931 973 }
932 974
@@ -970,8 +1012,11 @@
970 1012 'latitude' => $fb_point['latitude'],
971 1013 ),
972 1014 'title' => $fb_point['name'],
973 1015 );
1016 + }
1017 + if ( ! function_exists( 'map_block_from_geo_points' ) ) {
1018 + require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/map/map.php';
974 1019 }
975 1020 $map_block = map_block_from_geo_points( $geo_points );
976 1021
977 1022 $post['post_content'] = $map_block . $post['post_content'];