[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API: WP_REST_Global_Styles_Revisions_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 6.3.0 8 */ 9 10 /** 11 * Core class used to access global styles revisions via the REST API. 12 * 13 * @since 6.3.0 14 * 15 * @see WP_REST_Controller 16 */ 17 class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { 18 /** 19 * Parent post type. 20 * 21 * @since 6.3.0 22 * @var string 23 */ 24 protected $parent_post_type; 25 26 /** 27 * The base of the parent controller's route. 28 * 29 * @since 6.3.0 30 * @var string 31 */ 32 protected $parent_base; 33 34 /** 35 * Constructor. 36 * 37 * @since 6.3.0 38 */ 39 public function __construct() { 40 $this->parent_post_type = 'wp_global_styles'; 41 $this->rest_base = 'revisions'; 42 $this->parent_base = 'global-styles'; 43 $this->namespace = 'wp/v2'; 44 } 45 46 /** 47 * Registers the controller's routes. 48 * 49 * @since 6.3.0 50 * @since 6.5.0 Added route to fetch individual global styles revisions. 51 */ 52 public function register_routes() { 53 register_rest_route( 54 $this->namespace, 55 '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base, 56 array( 57 'args' => array( 58 'parent' => array( 59 'description' => __( 'The ID for the parent of the revision.' ), 60 'type' => 'integer', 61 ), 62 ), 63 array( 64 'methods' => WP_REST_Server::READABLE, 65 'callback' => array( $this, 'get_items' ), 66 'permission_callback' => array( $this, 'get_item_permissions_check' ), 67 'args' => $this->get_collection_params(), 68 ), 69 'schema' => array( $this, 'get_public_item_schema' ), 70 ) 71 ); 72 73 register_rest_route( 74 $this->namespace, 75 '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', 76 array( 77 'args' => array( 78 'parent' => array( 79 'description' => __( 'The ID for the parent of the global styles revision.' ), 80 'type' => 'integer', 81 ), 82 'id' => array( 83 'description' => __( 'Unique identifier for the global styles revision.' ), 84 'type' => 'integer', 85 ), 86 ), 87 array( 88 'methods' => WP_REST_Server::READABLE, 89 'callback' => array( $this, 'get_item' ), 90 'permission_callback' => array( $this, 'get_item_permissions_check' ), 91 'args' => array( 92 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 93 ), 94 ), 95 'schema' => array( $this, 'get_public_item_schema' ), 96 ) 97 ); 98 } 99 100 /** 101 * Retrieves the query params for collections. 102 * 103 * Inherits from WP_REST_Controller::get_collection_params(), 104 * also reflects changes to return value WP_REST_Revisions_Controller::get_collection_params(). 105 * 106 * @since 6.3.0 107 * 108 * @return array Collection parameters. 109 */ 110 public function get_collection_params() { 111 $collection_params = parent::get_collection_params(); 112 $collection_params['context']['default'] = 'view'; 113 $collection_params['offset'] = array( 114 'description' => __( 'Offset the result set by a specific number of items.' ), 115 'type' => 'integer', 116 ); 117 unset( $collection_params['search'] ); 118 unset( $collection_params['per_page']['default'] ); 119 120 return $collection_params; 121 } 122 123 /** 124 * Returns decoded JSON from post content string, 125 * or a 404 if not found. 126 * 127 * @since 6.3.0 128 * 129 * @param string $raw_json Encoded JSON from global styles custom post content. 130 * @return Array|WP_Error 131 */ 132 protected function get_decoded_global_styles_json( $raw_json ) { 133 $decoded_json = json_decode( $raw_json, true ); 134 135 if ( is_array( $decoded_json ) && isset( $decoded_json['isGlobalStylesUserThemeJSON'] ) && true === $decoded_json['isGlobalStylesUserThemeJSON'] ) { 136 return $decoded_json; 137 } 138 139 return new WP_Error( 140 'rest_global_styles_not_found', 141 __( 'Cannot find user global styles revisions.' ), 142 array( 'status' => 404 ) 143 ); 144 } 145 146 /** 147 * Returns paginated revisions of the given global styles config custom post type. 148 * 149 * The bulk of the body is taken from WP_REST_Revisions_Controller->get_items, 150 * but global styles does not require as many parameters. 151 * 152 * @since 6.3.0 153 * 154 * @param WP_REST_Request $request The request instance. 155 * @return WP_REST_Response|WP_Error 156 */ 157 public function get_items( $request ) { 158 $parent = $this->get_parent( $request['parent'] ); 159 160 if ( is_wp_error( $parent ) ) { 161 return $parent; 162 } 163 164 $global_styles_config = $this->get_decoded_global_styles_json( $parent->post_content ); 165 166 if ( is_wp_error( $global_styles_config ) ) { 167 return $global_styles_config; 168 } 169 170 if ( wp_revisions_enabled( $parent ) ) { 171 $registered = $this->get_collection_params(); 172 $query_args = array( 173 'post_parent' => $parent->ID, 174 'post_type' => 'revision', 175 'post_status' => 'inherit', 176 'posts_per_page' => -1, 177 'orderby' => 'date ID', 178 'order' => 'DESC', 179 ); 180 181 $parameter_mappings = array( 182 'offset' => 'offset', 183 'page' => 'paged', 184 'per_page' => 'posts_per_page', 185 ); 186 187 foreach ( $parameter_mappings as $api_param => $wp_param ) { 188 if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { 189 $query_args[ $wp_param ] = $request[ $api_param ]; 190 } 191 } 192 193 $revisions_query = new WP_Query(); 194 $revisions = $revisions_query->query( $query_args ); 195 $offset = isset( $query_args['offset'] ) ? (int) $query_args['offset'] : 0; 196 $page = (int) $query_args['paged']; 197 $total_revisions = $revisions_query->found_posts; 198 199 if ( $total_revisions < 1 ) { 200 // Out-of-bounds, run the query again without LIMIT for total count. 201 unset( $query_args['paged'], $query_args['offset'] ); 202 $count_query = new WP_Query(); 203 $count_query->query( $query_args ); 204 205 $total_revisions = $count_query->found_posts; 206 } 207 208 if ( $revisions_query->query_vars['posts_per_page'] > 0 ) { 209 $max_pages = (int) ceil( $total_revisions / (int) $revisions_query->query_vars['posts_per_page'] ); 210 } else { 211 $max_pages = $total_revisions > 0 ? 1 : 0; 212 } 213 if ( $total_revisions > 0 ) { 214 if ( $offset >= $total_revisions ) { 215 return new WP_Error( 216 'rest_revision_invalid_offset_number', 217 __( 'The offset number requested is larger than or equal to the number of available revisions.' ), 218 array( 'status' => 400 ) 219 ); 220 } elseif ( ! $offset && $page > $max_pages ) { 221 return new WP_Error( 222 'rest_revision_invalid_page_number', 223 __( 'The page number requested is larger than the number of pages available.' ), 224 array( 'status' => 400 ) 225 ); 226 } 227 } 228 } else { 229 $revisions = array(); 230 $total_revisions = 0; 231 $max_pages = 0; 232 $page = (int) $request['page']; 233 } 234 235 $response = array(); 236 237 foreach ( $revisions as $revision ) { 238 $data = $this->prepare_item_for_response( $revision, $request ); 239 $response[] = $this->prepare_response_for_collection( $data ); 240 } 241 242 $response = rest_ensure_response( $response ); 243 244 $response->header( 'X-WP-Total', (int) $total_revisions ); 245 $response->header( 'X-WP-TotalPages', (int) $max_pages ); 246 247 $request_params = $request->get_query_params(); 248 $base_path = rest_url( sprintf( '%s/%s/%d/%s', $this->namespace, $this->parent_base, $request['parent'], $this->rest_base ) ); 249 $base = add_query_arg( urlencode_deep( $request_params ), $base_path ); 250 251 if ( $page > 1 ) { 252 $prev_page = $page - 1; 253 254 if ( $prev_page > $max_pages ) { 255 $prev_page = $max_pages; 256 } 257 258 $prev_link = add_query_arg( 'page', $prev_page, $base ); 259 $response->link_header( 'prev', $prev_link ); 260 } 261 if ( $max_pages > $page ) { 262 $next_page = $page + 1; 263 $next_link = add_query_arg( 'page', $next_page, $base ); 264 265 $response->link_header( 'next', $next_link ); 266 } 267 268 return $response; 269 } 270 271 /** 272 * Retrieves one global styles revision from the collection. 273 * 274 * @since 6.5.0 275 * 276 * @param WP_REST_Request $request Full details about the request. 277 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 278 */ 279 public function get_item( $request ) { 280 $parent = $this->get_parent( $request['parent'] ); 281 if ( is_wp_error( $parent ) ) { 282 return $parent; 283 } 284 285 $revision = $this->get_revision( $request['id'] ); 286 if ( is_wp_error( $revision ) ) { 287 return $revision; 288 } 289 290 $response = $this->prepare_item_for_response( $revision, $request ); 291 return rest_ensure_response( $response ); 292 } 293 294 /** 295 * Gets the global styles revision, if the ID is valid. 296 * 297 * @since 6.5.0 298 * 299 * @param int $id Supplied ID. 300 * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise. 301 */ 302 protected function get_revision( $id ) { 303 $error = new WP_Error( 304 'rest_post_invalid_id', 305 __( 'Invalid global styles revision ID.' ), 306 array( 'status' => 404 ) 307 ); 308 309 if ( (int) $id <= 0 ) { 310 return $error; 311 } 312 313 $revision = get_post( (int) $id ); 314 if ( empty( $revision ) || empty( $revision->ID ) || 'revision' !== $revision->post_type ) { 315 return $error; 316 } 317 318 return $revision; 319 } 320 321 /** 322 * Checks the post_date_gmt or modified_gmt and prepare any post or 323 * modified date for single post output. 324 * 325 * Duplicate of WP_REST_Revisions_Controller::prepare_date_response. 326 * 327 * @since 6.3.0 328 * 329 * @param string $date_gmt GMT publication time. 330 * @param string|null $date Optional. Local publication time. Default null. 331 * @return string|null ISO8601/RFC3339 formatted datetime, otherwise null. 332 */ 333 protected function prepare_date_response( $date_gmt, $date = null ) { 334 if ( '0000-00-00 00:00:00' === $date_gmt ) { 335 return null; 336 } 337 338 if ( isset( $date ) ) { 339 return mysql_to_rfc3339( $date ); 340 } 341 342 return mysql_to_rfc3339( $date_gmt ); 343 } 344 345 /** 346 * Prepares the revision for the REST response. 347 * 348 * @since 6.3.0 349 * 350 * @param WP_Post $post Post revision object. 351 * @param WP_REST_Request $request Request object. 352 * @return WP_REST_Response|WP_Error Response object. 353 */ 354 public function prepare_item_for_response( $post, $request ) { 355 $parent = $this->get_parent( $request['parent'] ); 356 $global_styles_config = $this->get_decoded_global_styles_json( $post->post_content ); 357 358 if ( is_wp_error( $global_styles_config ) ) { 359 return $global_styles_config; 360 } 361 362 $fields = $this->get_fields_for_response( $request ); 363 $data = array(); 364 365 if ( ! empty( $global_styles_config['styles'] ) || ! empty( $global_styles_config['settings'] ) ) { 366 $global_styles_config = ( new WP_Theme_JSON( $global_styles_config, 'custom' ) )->get_raw_data(); 367 if ( rest_is_field_included( 'settings', $fields ) ) { 368 $data['settings'] = ! empty( $global_styles_config['settings'] ) ? $global_styles_config['settings'] : new stdClass(); 369 } 370 if ( rest_is_field_included( 'styles', $fields ) ) { 371 $data['styles'] = ! empty( $global_styles_config['styles'] ) ? $global_styles_config['styles'] : new stdClass(); 372 } 373 } 374 375 if ( rest_is_field_included( 'author', $fields ) ) { 376 $data['author'] = (int) $post->post_author; 377 } 378 379 if ( rest_is_field_included( 'date', $fields ) ) { 380 $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); 381 } 382 383 if ( rest_is_field_included( 'date_gmt', $fields ) ) { 384 $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); 385 } 386 387 if ( rest_is_field_included( 'id', $fields ) ) { 388 $data['id'] = (int) $post->ID; 389 } 390 391 if ( rest_is_field_included( 'modified', $fields ) ) { 392 $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); 393 } 394 395 if ( rest_is_field_included( 'modified_gmt', $fields ) ) { 396 $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); 397 } 398 399 if ( rest_is_field_included( 'parent', $fields ) ) { 400 $data['parent'] = (int) $parent->ID; 401 } 402 403 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 404 $data = $this->add_additional_fields_to_object( $data, $request ); 405 $data = $this->filter_response_by_context( $data, $context ); 406 407 return rest_ensure_response( $data ); 408 } 409 410 /** 411 * Retrieves the revision's schema, conforming to JSON Schema. 412 * 413 * @since 6.3.0 414 * 415 * @return array Item schema data. 416 */ 417 public function get_item_schema() { 418 if ( $this->schema ) { 419 return $this->add_additional_fields_schema( $this->schema ); 420 } 421 422 $schema = array( 423 '$schema' => 'http://json-schema.org/draft-04/schema#', 424 'title' => "{$this->parent_post_type}-revision", 425 'type' => 'object', 426 // Base properties for every revision. 427 'properties' => array( 428 429 /* 430 * Adds settings and styles from the WP_REST_Revisions_Controller item fields. 431 * Leaves out GUID as global styles shouldn't be accessible via URL. 432 */ 433 'author' => array( 434 'description' => __( 'The ID for the author of the revision.' ), 435 'type' => 'integer', 436 'context' => array( 'view', 'edit', 'embed' ), 437 ), 438 'date' => array( 439 'description' => __( "The date the revision was published, in the site's timezone." ), 440 'type' => 'string', 441 'format' => 'date-time', 442 'context' => array( 'view', 'edit', 'embed' ), 443 ), 444 'date_gmt' => array( 445 'description' => __( 'The date the revision was published, as GMT.' ), 446 'type' => 'string', 447 'format' => 'date-time', 448 'context' => array( 'view', 'edit' ), 449 ), 450 'id' => array( 451 'description' => __( 'Unique identifier for the revision.' ), 452 'type' => 'integer', 453 'context' => array( 'view', 'edit', 'embed' ), 454 ), 455 'modified' => array( 456 'description' => __( "The date the revision was last modified, in the site's timezone." ), 457 'type' => 'string', 458 'format' => 'date-time', 459 'context' => array( 'view', 'edit' ), 460 ), 461 'modified_gmt' => array( 462 'description' => __( 'The date the revision was last modified, as GMT.' ), 463 'type' => 'string', 464 'format' => 'date-time', 465 'context' => array( 'view', 'edit' ), 466 ), 467 'parent' => array( 468 'description' => __( 'The ID for the parent of the revision.' ), 469 'type' => 'integer', 470 'context' => array( 'view', 'edit', 'embed' ), 471 ), 472 473 // Adds settings and styles from the WP_REST_Global_Styles_Controller parent schema. 474 'styles' => array( 475 'description' => __( 'Global styles.' ), 476 'type' => array( 'object' ), 477 'context' => array( 'view', 'edit' ), 478 ), 479 'settings' => array( 480 'description' => __( 'Global settings.' ), 481 'type' => array( 'object' ), 482 'context' => array( 'view', 'edit' ), 483 ), 484 ), 485 ); 486 487 $this->schema = $schema; 488 489 return $this->add_additional_fields_schema( $this->schema ); 490 } 491 492 /** 493 * Checks if a given request has access to read a single global style. 494 * 495 * @since 6.3.0 496 * 497 * @param WP_REST_Request $request Full details about the request. 498 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 499 */ 500 public function get_item_permissions_check( $request ) { 501 $post = $this->get_parent( $request['parent'] ); 502 if ( is_wp_error( $post ) ) { 503 return $post; 504 } 505 506 /* 507 * The same check as WP_REST_Global_Styles_Controller::get_item_permissions_check. 508 */ 509 if ( ! current_user_can( 'read_post', $post->ID ) ) { 510 return new WP_Error( 511 'rest_cannot_view', 512 __( 'Sorry, you are not allowed to view revisions for this global style.' ), 513 array( 'status' => rest_authorization_required_code() ) 514 ); 515 } 516 517 return true; 518 } 519 520 /** 521 * Gets the parent post, if the ID is valid. 522 * 523 * Duplicate of WP_REST_Revisions_Controller::get_parent. 524 * 525 * @since 6.3.0 526 * 527 * @param int $parent_post_id Supplied ID. 528 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. 529 */ 530 protected function get_parent( $parent_post_id ) { 531 $error = new WP_Error( 532 'rest_post_invalid_parent', 533 __( 'Invalid post parent ID.' ), 534 array( 'status' => 404 ) 535 ); 536 537 if ( (int) $parent_post_id <= 0 ) { 538 return $error; 539 } 540 541 $parent_post = get_post( (int) $parent_post_id ); 542 543 if ( empty( $parent_post ) || empty( $parent_post->ID ) 544 || $this->parent_post_type !== $parent_post->post_type 545 ) { 546 return $error; 547 } 548 549 return $parent_post; 550 } 551 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Apr 27 08:20:02 2024 | Cross-referenced by PHPXref |