| 276 | /** |
| 277 | * Checks if a given request has access to create an autosave revision. |
| 278 | * |
| 279 | * Autosave revisions inherit permissions from the parent post, |
| 280 | * check if the current user has permission to edit the post. |
| 281 | * |
| 282 | * @since 5.0.0 |
| 283 | * |
| 284 | * @param WP_REST_Request $request Full details about the request. |
| 285 | * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise. |
| 286 | */ |
| 287 | public function create_item_permissions_check( $request ) { |
| 288 | if ( empty( $request->get_param( 'parent' ) ) ) { |
| 289 | return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) ); |
| 290 | } |
| 291 | |
| 292 | // Set request ID for the permission check. |
| 293 | $dummy_request = clone $request; |
| 294 | $dummy_request['id'] = $request['parent']; |
| 295 | |
| 296 | return $this->parent_controller->update_item_permissions_check( $dummy_request ); |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Creates, updates or deletes an autosave revision. |
| 301 | * |
| 302 | * @since 5.0.0 |
| 303 | * |
| 304 | * @param WP_REST_Request $request Full details about the request. |
| 305 | * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
| 306 | */ |
| 307 | public function create_item( $request ) { |
| 308 | if ( ! defined( 'DOING_AUTOSAVE' ) ) { |
| 309 | define( 'DOING_AUTOSAVE', true ); |
| 310 | } |
| 311 | |
| 312 | $post = get_post( $request->get_param( 'parent' ) ); |
| 313 | |
| 314 | if ( is_wp_error( $post ) ) { |
| 315 | return $post; |
| 316 | } |
| 317 | |
| 318 | $user_id = get_current_user_id(); |
| 319 | $old_autosave = wp_get_post_autosave( $post->ID, $user_id ); |
| 320 | if ( $old_autosave ) { |
| 321 | return new WP_Error( |
| 322 | 'rest_revision_existing_autosave', |
| 323 | __( 'An autosave already exists for this user.' ), |
| 324 | array( |
| 325 | 'status' => WP_Http::CONFLICT, |
| 326 | 'id' => $old_autosave->ID, |
| 327 | ) |
| 328 | ); |
| 329 | } |
| 330 | |
| 331 | $prepared_post = $this->prepare_item_for_database( $request ); |
| 332 | $prepared_post->ID = $post->ID; |
| 333 | |
| 334 | if ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) { |
| 335 | return new WP_Error( |
| 336 | 'rest_cannot_create_for_draft', |
| 337 | __( 'Draft posts may not have autosaves.' ), |
| 338 | array( |
| 339 | 'status' => WP_Http::BAD_REQUEST, |
| 340 | ) |
| 341 | ); |
| 342 | } |
| 343 | |
| 344 | // Non-draft posts: create or update the post autosave. |
| 345 | $autosave_id = _wp_put_post_revision( (array) $prepared_post, true ); |
| 346 | if ( is_wp_error( $autosave_id ) ) { |
| 347 | return $autosave_id; |
| 348 | } |
| 349 | |
| 350 | $autosave = get_post( $autosave_id ); |
| 351 | $fields_update = $this->update_additional_fields_for_object( $autosave, $request ); |
| 352 | |
| 353 | if ( is_wp_error( $fields_update ) ) { |
| 354 | return $fields_update; |
| 355 | } |
| 356 | |
| 357 | $request->set_param( 'context', 'edit' ); |
| 358 | |
| 359 | $response = $this->prepare_item_for_response( $autosave, $request ); |
| 360 | $response = rest_ensure_response( $response ); |
| 361 | |
| 362 | return $response; |
| 363 | } |
| 364 | |
| 365 | /** |
| 366 | * Checks if a given request has access to create an autosave revision. |
| 367 | * |
| 368 | * Autosave revisions inherit permissions from the parent post, |
| 369 | * check if the current user has permission to edit the post. |
| 370 | * |
| 371 | * @since 5.0.0 |
| 372 | * |
| 373 | * @param WP_REST_Request $request Full details about the request. |
| 374 | * @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise. |
| 375 | */ |
| 376 | public function update_item_permissions_check( $request ) { |
| 377 | if ( empty( $request->get_param( 'parent' ) ) ) { |
| 378 | return new WP_Error( 'rest_post_invalid_id', __( 'Invalid item ID.' ), array( 'status' => 404 ) ); |
| 379 | } |
| 380 | |
| 381 | $autosave = $this->get_revision( $request['id'] ); |
| 382 | if ( is_wp_error( $revision ) ) { |
| 383 | return $revision; |
| 384 | } |
| 385 | |
| 386 | if ( ! wp_is_post_autosave( $autosave ) ) { |
| 387 | return new WP_Error( |
| 388 | 'rest_revision_cannot_update', |
| 389 | __( 'Only autosave revisions may be updated.' ), |
| 390 | array( 'status' => WP_Http::BAD_REQUEST ) |
| 391 | ); |
| 392 | } |
| 393 | |
| 394 | // Set request ID for the permission check. |
| 395 | $dummy_request = clone $request; |
| 396 | $dummy_request['id'] = $request['parent']; |
| 397 | |
| 398 | return $this->parent_controller->update_item_permissions_check( $dummy_request ); |
| 399 | } |
| 400 | |
| 401 | /** |
| 402 | * Updates a single revision. |
| 403 | * |
| 404 | * Only allows updating autosave revisions. |
| 405 | * |
| 406 | * @since 4.7.0 |
| 407 | * |
| 408 | * @param WP_REST_Request $request Full details about the request. |
| 409 | * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. |
| 410 | */ |
| 411 | public function update_item( $request ) { |
| 412 | $valid_check = $this->get_parent( $request['parent'] ); |
| 413 | if ( is_wp_error( $valid_check ) ) { |
| 414 | return $valid_check; |
| 415 | } |
| 416 | |
| 417 | $post = $this->prepare_item_for_database( $request ); |
| 418 | |
| 419 | if ( is_wp_error( $post ) ) { |
| 420 | return $post; |
| 421 | } |
| 422 | |
| 423 | // convert the post object to an array, otherwise wp_update_post will expect non-escaped input. |
| 424 | $post_id = wp_update_post( wp_slash( (array) $post ), true ); |
| 425 | |
| 426 | if ( is_wp_error( $post_id ) ) { |
| 427 | if ( 'db_update_error' === $post_id->get_error_code() ) { |
| 428 | $post_id->add_data( array( 'status' => 500 ) ); |
| 429 | } else { |
| 430 | $post_id->add_data( array( 'status' => 400 ) ); |
| 431 | } |
| 432 | return $post_id; |
| 433 | } |
| 434 | |
| 435 | $post = get_post( $post_id ); |
| 436 | |
| 437 | /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */ |
| 438 | do_action( "rest_insert_{$this->post_type}", $post, $request, false ); |
| 439 | |
| 440 | $schema = $this->get_item_schema(); |
| 441 | |
| 442 | $post = get_post( $post_id ); |
| 443 | $fields_update = $this->update_additional_fields_for_object( $post, $request ); |
| 444 | |
| 445 | if ( is_wp_error( $fields_update ) ) { |
| 446 | return $fields_update; |
| 447 | } |
| 448 | |
| 449 | $request->set_param( 'context', 'edit' ); |
| 450 | |
| 451 | $response = $this->prepare_item_for_response( $post, $request ); |
| 452 | |
| 453 | return rest_ensure_response( $response ); |
| 454 | } |
| 455 | |
| 536 | /** |
| 537 | * Prepares a single post for create or update. |
| 538 | * |
| 539 | * @since 4.7.0 |
| 540 | * |
| 541 | * @param WP_REST_Request $request Request object. |
| 542 | * @return stdClass|WP_Error Post object or WP_Error. |
| 543 | */ |
| 544 | protected function prepare_item_for_database( $request ) { |
| 545 | $prepared_post = new stdClass; |
| 546 | |
| 547 | // Post ID. |
| 548 | if ( isset( $request['id'] ) ) { |
| 549 | $existing_post = $this->get_revision( $request['id'] ); |
| 550 | if ( is_wp_error( $existing_post ) ) { |
| 551 | return $existing_post; |
| 552 | } |
| 553 | |
| 554 | $prepared_post->ID = $existing_post->ID; |
| 555 | } |
| 556 | |
| 557 | $schema = $this->get_item_schema(); |
| 558 | |
| 559 | // Post title. |
| 560 | if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) { |
| 561 | if ( is_string( $request['title'] ) ) { |
| 562 | $prepared_post->post_title = $request['title']; |
| 563 | } elseif ( ! empty( $request['title']['raw'] ) ) { |
| 564 | $prepared_post->post_title = $request['title']['raw']; |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | // Post content. |
| 569 | if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) { |
| 570 | if ( is_string( $request['content'] ) ) { |
| 571 | $prepared_post->post_content = $request['content']; |
| 572 | } elseif ( isset( $request['content']['raw'] ) ) { |
| 573 | $prepared_post->post_content = $request['content']['raw']; |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | // Post excerpt. |
| 578 | if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['excerpt'] ) ) { |
| 579 | if ( is_string( $request['excerpt'] ) ) { |
| 580 | $prepared_post->post_excerpt = $request['excerpt']; |
| 581 | } elseif ( isset( $request['excerpt']['raw'] ) ) { |
| 582 | $prepared_post->post_excerpt = $request['excerpt']['raw']; |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | /** |
| 587 | * Filters a post before it is inserted via the REST API. |
| 588 | * |
| 589 | * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug. |
| 590 | * |
| 591 | * @since 4.7.0 |
| 592 | * |
| 593 | * @param stdClass $prepared_post An object representing a single post prepared |
| 594 | * for inserting or updating the database. |
| 595 | * @param WP_REST_Request $request Request object. |
| 596 | */ |
| 597 | return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request ); |
| 598 | |
| 599 | } |
| 600 | |