46 * @since 4.7.0 |
54 * @since 4.7.0 |
47 * |
55 * |
48 * @param string $parent_post_type Post type of the parent. |
56 * @param string $parent_post_type Post type of the parent. |
49 */ |
57 */ |
50 public function __construct( $parent_post_type ) { |
58 public function __construct( $parent_post_type ) { |
51 $this->parent_post_type = $parent_post_type; |
59 $this->parent_post_type = $parent_post_type; |
|
60 $post_type_object = get_post_type_object( $parent_post_type ); |
|
61 $parent_controller = $post_type_object->get_rest_controller(); |
|
62 |
|
63 if ( ! $parent_controller ) { |
|
64 $parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); |
|
65 } |
|
66 |
|
67 $this->parent_controller = $parent_controller; |
52 $this->rest_base = 'revisions'; |
68 $this->rest_base = 'revisions'; |
53 $post_type_object = get_post_type_object( $parent_post_type ); |
|
54 $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; |
69 $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; |
55 $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; |
70 $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; |
56 $this->parent_controller = $post_type_object->get_rest_controller(); |
71 $this->meta = new WP_REST_Post_Meta_Fields( $parent_post_type ); |
57 |
|
58 if ( ! $this->parent_controller ) { |
|
59 $this->parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); |
|
60 } |
|
61 } |
72 } |
62 |
73 |
63 /** |
74 /** |
64 * Registers the routes for revisions based on post types supporting revisions. |
75 * Registers the routes for revisions based on post types supporting revisions. |
65 * |
76 * |
124 ), |
135 ), |
125 ), |
136 ), |
126 'schema' => array( $this, 'get_public_item_schema' ), |
137 'schema' => array( $this, 'get_public_item_schema' ), |
127 ) |
138 ) |
128 ); |
139 ); |
129 |
|
130 } |
140 } |
131 |
141 |
132 /** |
142 /** |
133 * Get the parent post, if the ID is valid. |
143 * Get the parent post, if the ID is valid. |
134 * |
144 * |
135 * @since 4.7.2 |
145 * @since 4.7.2 |
136 * |
146 * |
137 * @param int $parent Supplied ID. |
147 * @param int $parent_post_id Supplied ID. |
138 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. |
148 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. |
139 */ |
149 */ |
140 protected function get_parent( $parent ) { |
150 protected function get_parent( $parent_post_id ) { |
141 $error = new WP_Error( |
151 $error = new WP_Error( |
142 'rest_post_invalid_parent', |
152 'rest_post_invalid_parent', |
143 __( 'Invalid post parent ID.' ), |
153 __( 'Invalid post parent ID.' ), |
144 array( 'status' => 404 ) |
154 array( 'status' => 404 ) |
145 ); |
155 ); |
146 if ( (int) $parent <= 0 ) { |
156 |
|
157 if ( (int) $parent_post_id <= 0 ) { |
147 return $error; |
158 return $error; |
148 } |
159 } |
149 |
160 |
150 $parent = get_post( (int) $parent ); |
161 $parent_post = get_post( (int) $parent_post_id ); |
151 if ( empty( $parent ) || empty( $parent->ID ) || $this->parent_post_type !== $parent->post_type ) { |
162 |
|
163 if ( empty( $parent_post ) || empty( $parent_post->ID ) |
|
164 || $this->parent_post_type !== $parent_post->post_type |
|
165 ) { |
152 return $error; |
166 return $error; |
153 } |
167 } |
154 |
168 |
155 return $parent; |
169 return $parent_post; |
156 } |
170 } |
157 |
171 |
158 /** |
172 /** |
159 * Checks if a given request has access to get revisions. |
173 * Checks if a given request has access to get revisions. |
160 * |
174 * |
292 |
306 |
293 $total_revisions = $count_query->found_posts; |
307 $total_revisions = $count_query->found_posts; |
294 } |
308 } |
295 |
309 |
296 if ( $revisions_query->query_vars['posts_per_page'] > 0 ) { |
310 if ( $revisions_query->query_vars['posts_per_page'] > 0 ) { |
297 $max_pages = ceil( $total_revisions / (int) $revisions_query->query_vars['posts_per_page'] ); |
311 $max_pages = (int) ceil( $total_revisions / (int) $revisions_query->query_vars['posts_per_page'] ); |
298 } else { |
312 } else { |
299 $max_pages = $total_revisions > 0 ? 1 : 0; |
313 $max_pages = $total_revisions > 0 ? 1 : 0; |
300 } |
314 } |
301 |
315 |
302 if ( $total_revisions > 0 ) { |
316 if ( $total_revisions > 0 ) { |
332 |
346 |
333 $response->header( 'X-WP-Total', (int) $total_revisions ); |
347 $response->header( 'X-WP-Total', (int) $total_revisions ); |
334 $response->header( 'X-WP-TotalPages', (int) $max_pages ); |
348 $response->header( 'X-WP-TotalPages', (int) $max_pages ); |
335 |
349 |
336 $request_params = $request->get_query_params(); |
350 $request_params = $request->get_query_params(); |
337 $base = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s/%d/%s', $this->namespace, $this->parent_base, $request['parent'], $this->rest_base ) ) ); |
351 $base_path = rest_url( sprintf( '%s/%s/%d/%s', $this->namespace, $this->parent_base, $request['parent'], $this->rest_base ) ); |
|
352 $base = add_query_arg( urlencode_deep( $request_params ), $base_path ); |
338 |
353 |
339 if ( $page > 1 ) { |
354 if ( $page > 1 ) { |
340 $prev_page = $page - 1; |
355 $prev_page = $page - 1; |
341 |
356 |
342 if ( $prev_page > $max_pages ) { |
357 if ( $prev_page > $max_pages ) { |
370 |
385 |
371 /** |
386 /** |
372 * Retrieves one revision from the collection. |
387 * Retrieves one revision from the collection. |
373 * |
388 * |
374 * @since 4.7.0 |
389 * @since 4.7.0 |
|
390 * @since 6.5.0 Added a condition to check that parent id matches revision parent id. |
375 * |
391 * |
376 * @param WP_REST_Request $request Full details about the request. |
392 * @param WP_REST_Request $request Full details about the request. |
377 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
393 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
378 */ |
394 */ |
379 public function get_item( $request ) { |
395 public function get_item( $request ) { |
385 $revision = $this->get_revision( $request['id'] ); |
401 $revision = $this->get_revision( $request['id'] ); |
386 if ( is_wp_error( $revision ) ) { |
402 if ( is_wp_error( $revision ) ) { |
387 return $revision; |
403 return $revision; |
388 } |
404 } |
389 |
405 |
|
406 if ( (int) $parent->ID !== (int) $revision->post_parent ) { |
|
407 return new WP_Error( |
|
408 'rest_revision_parent_id_mismatch', |
|
409 /* translators: %d: A post id. */ |
|
410 sprintf( __( 'The revision does not belong to the specified parent with id of "%d"' ), $parent->ID ), |
|
411 array( 'status' => 404 ) |
|
412 ); |
|
413 } |
|
414 |
390 $response = $this->prepare_item_for_response( $revision, $request ); |
415 $response = $this->prepare_item_for_response( $revision, $request ); |
391 return rest_ensure_response( $response ); |
416 return rest_ensure_response( $response ); |
392 } |
417 } |
393 |
418 |
394 /** |
419 /** |
402 public function delete_item_permissions_check( $request ) { |
427 public function delete_item_permissions_check( $request ) { |
403 $parent = $this->get_parent( $request['parent'] ); |
428 $parent = $this->get_parent( $request['parent'] ); |
404 if ( is_wp_error( $parent ) ) { |
429 if ( is_wp_error( $parent ) ) { |
405 return $parent; |
430 return $parent; |
406 } |
431 } |
407 |
|
408 $parent_post_type = get_post_type_object( $parent->post_type ); |
|
409 |
432 |
410 if ( ! current_user_can( 'delete_post', $parent->ID ) ) { |
433 if ( ! current_user_can( 'delete_post', $parent->ID ) ) { |
411 return new WP_Error( |
434 return new WP_Error( |
412 'rest_cannot_delete', |
435 'rest_cannot_delete', |
413 __( 'Sorry, you are not allowed to delete revisions of this post.' ), |
436 __( 'Sorry, you are not allowed to delete revisions of this post.' ), |
535 * Prepares the revision for the REST response. |
558 * Prepares the revision for the REST response. |
536 * |
559 * |
537 * @since 4.7.0 |
560 * @since 4.7.0 |
538 * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support. |
561 * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support. |
539 * |
562 * |
|
563 * @global WP_Post $post Global post object. |
|
564 * |
540 * @param WP_Post $item Post revision object. |
565 * @param WP_Post $item Post revision object. |
541 * @param WP_REST_Request $request Request object. |
566 * @param WP_REST_Request $request Request object. |
542 * @return WP_REST_Response Response object. |
567 * @return WP_REST_Response Response object. |
543 */ |
568 */ |
544 public function prepare_item_for_response( $item, $request ) { |
569 public function prepare_item_for_response( $item, $request ) { |
545 // Restores the more descriptive, specific name for use within this method. |
570 // Restores the more descriptive, specific name for use within this method. |
546 $post = $item; |
571 $post = $item; |
|
572 |
547 $GLOBALS['post'] = $post; |
573 $GLOBALS['post'] = $post; |
548 |
574 |
549 setup_postdata( $post ); |
575 setup_postdata( $post ); |
550 |
576 |
551 $fields = $this->get_fields_for_response( $request ); |
577 $fields = $this->get_fields_for_response( $request ); |
612 'raw' => $post->post_excerpt, |
638 'raw' => $post->post_excerpt, |
613 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ), |
639 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ), |
614 ); |
640 ); |
615 } |
641 } |
616 |
642 |
|
643 if ( rest_is_field_included( 'meta', $fields ) ) { |
|
644 $data['meta'] = $this->meta->get_value( $post->ID, $request ); |
|
645 } |
|
646 |
617 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
647 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
618 $data = $this->add_additional_fields_to_object( $data, $request ); |
648 $data = $this->add_additional_fields_to_object( $data, $request ); |
619 $data = $this->filter_response_by_context( $data, $context ); |
649 $data = $this->filter_response_by_context( $data, $context ); |
620 $response = rest_ensure_response( $data ); |
650 $response = rest_ensure_response( $data ); |
621 |
651 |
622 if ( ! empty( $data['parent'] ) ) { |
652 if ( ! empty( $data['parent'] ) ) { |
623 $response->add_link( 'parent', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->parent_base, $data['parent'] ) ) ); |
653 $response->add_link( 'parent', rest_url( rest_get_route_for_post( $data['parent'] ) ) ); |
624 } |
654 } |
625 |
655 |
626 /** |
656 /** |
627 * Filters a revision returned from the REST API. |
657 * Filters a revision returned from the REST API. |
628 * |
658 * |
744 } |
774 } |
745 |
775 |
746 if ( ! empty( $parent_schema['properties']['guid'] ) ) { |
776 if ( ! empty( $parent_schema['properties']['guid'] ) ) { |
747 $schema['properties']['guid'] = $parent_schema['properties']['guid']; |
777 $schema['properties']['guid'] = $parent_schema['properties']['guid']; |
748 } |
778 } |
|
779 |
|
780 $schema['properties']['meta'] = $this->meta->get_field_schema(); |
749 |
781 |
750 $this->schema = $schema; |
782 $this->schema = $schema; |
751 |
783 |
752 return $this->add_additional_fields_schema( $this->schema ); |
784 return $this->add_additional_fields_schema( $this->schema ); |
753 } |
785 } |