[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-templates-controller.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Templates_Controller class
   4   *
   5   * @package    WordPress
   6   * @subpackage REST_API
   7   * @since 5.8.0
   8   */
   9  
  10  /**
  11   * Base Templates REST API Controller.
  12   *
  13   * @since 5.8.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Templates_Controller extends WP_REST_Controller {
  18  
  19      /**
  20       * Post type.
  21       *
  22       * @since 5.8.0
  23       * @var string
  24       */
  25      protected $post_type;
  26  
  27      /**
  28       * Constructor.
  29       *
  30       * @since 5.8.0
  31       *
  32       * @param string $post_type Post type.
  33       */
  34  	public function __construct( $post_type ) {
  35          $this->post_type = $post_type;
  36          $obj             = get_post_type_object( $post_type );
  37          $this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
  38          $this->namespace = ! empty( $obj->rest_namespace ) ? $obj->rest_namespace : 'wp/v2';
  39      }
  40  
  41      /**
  42       * Registers the controllers routes.
  43       *
  44       * @since 5.8.0
  45       * @since 6.1.0 Endpoint for fallback template content.
  46       */
  47  	public function register_routes() {
  48          // Lists all templates.
  49          register_rest_route(
  50              $this->namespace,
  51              '/' . $this->rest_base,
  52              array(
  53                  array(
  54                      'methods'             => WP_REST_Server::READABLE,
  55                      'callback'            => array( $this, 'get_items' ),
  56                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  57                      'args'                => $this->get_collection_params(),
  58                  ),
  59                  array(
  60                      'methods'             => WP_REST_Server::CREATABLE,
  61                      'callback'            => array( $this, 'create_item' ),
  62                      'permission_callback' => array( $this, 'create_item_permissions_check' ),
  63                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
  64                  ),
  65                  'schema' => array( $this, 'get_public_item_schema' ),
  66              )
  67          );
  68  
  69          // Get fallback template content.
  70          register_rest_route(
  71              $this->namespace,
  72              '/' . $this->rest_base . '/lookup',
  73              array(
  74                  array(
  75                      'methods'             => WP_REST_Server::READABLE,
  76                      'callback'            => array( $this, 'get_template_fallback' ),
  77                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
  78                      'args'                => array(
  79                          'slug'            => array(
  80                              'description' => __( 'The slug of the template to get the fallback for' ),
  81                              'type'        => 'string',
  82                              'required'    => true,
  83                          ),
  84                          'is_custom'       => array(
  85                              'description' => __( 'Indicates if a template is custom or part of the template hierarchy' ),
  86                              'type'        => 'boolean',
  87                          ),
  88                          'template_prefix' => array(
  89                              'description' => __( 'The template prefix for the created template. This is used to extract the main template type, e.g. in `taxonomy-books` extracts the `taxonomy`' ),
  90                              'type'        => 'string',
  91                          ),
  92                      ),
  93                  ),
  94              )
  95          );
  96  
  97          // Lists/updates a single template based on the given id.
  98          register_rest_route(
  99              $this->namespace,
 100              // The route.
 101              sprintf(
 102                  '/%s/(?P<id>%s%s)',
 103                  $this->rest_base,
 104                  /*
 105                   * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
 106                   * Excludes invalid directory name characters: `/:<>*?"|`.
 107                   */
 108                  '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
 109                  // Matches the template name.
 110                  '[\/\w%-]+'
 111              ),
 112              array(
 113                  'args'   => array(
 114                      'id' => array(
 115                          'description'       => __( 'The id of a template' ),
 116                          'type'              => 'string',
 117                          'sanitize_callback' => array( $this, '_sanitize_template_id' ),
 118                      ),
 119                  ),
 120                  array(
 121                      'methods'             => WP_REST_Server::READABLE,
 122                      'callback'            => array( $this, 'get_item' ),
 123                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
 124                      'args'                => array(
 125                          'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 126                      ),
 127                  ),
 128                  array(
 129                      'methods'             => WP_REST_Server::EDITABLE,
 130                      'callback'            => array( $this, 'update_item' ),
 131                      'permission_callback' => array( $this, 'update_item_permissions_check' ),
 132                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
 133                  ),
 134                  array(
 135                      'methods'             => WP_REST_Server::DELETABLE,
 136                      'callback'            => array( $this, 'delete_item' ),
 137                      'permission_callback' => array( $this, 'delete_item_permissions_check' ),
 138                      'args'                => array(
 139                          'force' => array(
 140                              'type'        => 'boolean',
 141                              'default'     => false,
 142                              'description' => __( 'Whether to bypass Trash and force deletion.' ),
 143                          ),
 144                      ),
 145                  ),
 146                  'schema' => array( $this, 'get_public_item_schema' ),
 147              )
 148          );
 149      }
 150  
 151      /**
 152       * Returns the fallback template for the given slug.
 153       *
 154       * @since 6.1.0
 155       * @since 6.3.0 Ignore empty templates.
 156       *
 157       * @param WP_REST_Request $request The request instance.
 158       * @return WP_REST_Response|WP_Error
 159       */
 160  	public function get_template_fallback( $request ) {
 161          $hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
 162  
 163          do {
 164              $fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
 165              array_shift( $hierarchy );
 166          } while ( ! empty( $hierarchy ) && empty( $fallback_template->content ) );
 167  
 168          $response = $this->prepare_item_for_response( $fallback_template, $request );
 169  
 170          return rest_ensure_response( $response );
 171      }
 172  
 173      /**
 174       * Checks if the user has permissions to make the request.
 175       *
 176       * @since 5.8.0
 177       *
 178       * @param WP_REST_Request $request Full details about the request.
 179       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 180       */
 181  	protected function permissions_check( $request ) {
 182          /*
 183           * Verify if the current user has edit_theme_options capability.
 184           * This capability is required to edit/view/delete templates.
 185           */
 186          if ( ! current_user_can( 'edit_theme_options' ) ) {
 187              return new WP_Error(
 188                  'rest_cannot_manage_templates',
 189                  __( 'Sorry, you are not allowed to access the templates on this site.' ),
 190                  array(
 191                      'status' => rest_authorization_required_code(),
 192                  )
 193              );
 194          }
 195  
 196          return true;
 197      }
 198  
 199      /**
 200       * Requesting this endpoint for a template like 'twentytwentytwo//home'
 201       * requires using a path like /wp/v2/templates/twentytwentytwo//home. There
 202       * are special cases when WordPress routing corrects the name to contain
 203       * only a single slash like 'twentytwentytwo/home'.
 204       *
 205       * This method doubles the last slash if it's not already doubled. It relies
 206       * on the template ID format {theme_name}//{template_slug} and the fact that
 207       * slugs cannot contain slashes.
 208       *
 209       * @since 5.9.0
 210       * @see https://core.trac.wordpress.org/ticket/54507
 211       *
 212       * @param string $id Template ID.
 213       * @return string Sanitized template ID.
 214       */
 215  	public function _sanitize_template_id( $id ) {
 216          $id = urldecode( $id );
 217  
 218          $last_slash_pos = strrpos( $id, '/' );
 219          if ( false === $last_slash_pos ) {
 220              return $id;
 221          }
 222  
 223          $is_double_slashed = substr( $id, $last_slash_pos - 1, 1 ) === '/';
 224          if ( $is_double_slashed ) {
 225              return $id;
 226          }
 227          return (
 228              substr( $id, 0, $last_slash_pos )
 229              . '/'
 230              . substr( $id, $last_slash_pos )
 231          );
 232      }
 233  
 234      /**
 235       * Checks if a given request has access to read templates.
 236       *
 237       * @since 5.8.0
 238       *
 239       * @param WP_REST_Request $request Full details about the request.
 240       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 241       */
 242  	public function get_items_permissions_check( $request ) {
 243          return $this->permissions_check( $request );
 244      }
 245  
 246      /**
 247       * Returns a list of templates.
 248       *
 249       * @since 5.8.0
 250       *
 251       * @param WP_REST_Request $request The request instance.
 252       * @return WP_REST_Response
 253       */
 254  	public function get_items( $request ) {
 255          $query = array();
 256          if ( isset( $request['wp_id'] ) ) {
 257              $query['wp_id'] = $request['wp_id'];
 258          }
 259          if ( isset( $request['area'] ) ) {
 260              $query['area'] = $request['area'];
 261          }
 262          if ( isset( $request['post_type'] ) ) {
 263              $query['post_type'] = $request['post_type'];
 264          }
 265  
 266          $templates = array();
 267          foreach ( get_block_templates( $query, $this->post_type ) as $template ) {
 268              $data        = $this->prepare_item_for_response( $template, $request );
 269              $templates[] = $this->prepare_response_for_collection( $data );
 270          }
 271  
 272          return rest_ensure_response( $templates );
 273      }
 274  
 275      /**
 276       * Checks if a given request has access to read a single template.
 277       *
 278       * @since 5.8.0
 279       *
 280       * @param WP_REST_Request $request Full details about the request.
 281       * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
 282       */
 283  	public function get_item_permissions_check( $request ) {
 284          return $this->permissions_check( $request );
 285      }
 286  
 287      /**
 288       * Returns the given template
 289       *
 290       * @since 5.8.0
 291       *
 292       * @param WP_REST_Request $request The request instance.
 293       * @return WP_REST_Response|WP_Error
 294       */
 295  	public function get_item( $request ) {
 296          if ( isset( $request['source'] ) && 'theme' === $request['source'] ) {
 297              $template = get_block_file_template( $request['id'], $this->post_type );
 298          } else {
 299              $template = get_block_template( $request['id'], $this->post_type );
 300          }
 301  
 302          if ( ! $template ) {
 303              return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
 304          }
 305  
 306          return $this->prepare_item_for_response( $template, $request );
 307      }
 308  
 309      /**
 310       * Checks if a given request has access to write a single template.
 311       *
 312       * @since 5.8.0
 313       *
 314       * @param WP_REST_Request $request Full details about the request.
 315       * @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
 316       */
 317  	public function update_item_permissions_check( $request ) {
 318          return $this->permissions_check( $request );
 319      }
 320  
 321      /**
 322       * Updates a single template.
 323       *
 324       * @since 5.8.0
 325       *
 326       * @param WP_REST_Request $request Full details about the request.
 327       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 328       */
 329  	public function update_item( $request ) {
 330          $template = get_block_template( $request['id'], $this->post_type );
 331          if ( ! $template ) {
 332              return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
 333          }
 334  
 335          $post_before = get_post( $template->wp_id );
 336  
 337          if ( isset( $request['source'] ) && 'theme' === $request['source'] ) {
 338              wp_delete_post( $template->wp_id, true );
 339              $request->set_param( 'context', 'edit' );
 340  
 341              $template = get_block_template( $request['id'], $this->post_type );
 342              $response = $this->prepare_item_for_response( $template, $request );
 343  
 344              return rest_ensure_response( $response );
 345          }
 346  
 347          $changes = $this->prepare_item_for_database( $request );
 348  
 349          if ( is_wp_error( $changes ) ) {
 350              return $changes;
 351          }
 352  
 353          if ( 'custom' === $template->source ) {
 354              $update = true;
 355              $result = wp_update_post( wp_slash( (array) $changes ), false );
 356          } else {
 357              $update      = false;
 358              $post_before = null;
 359              $result      = wp_insert_post( wp_slash( (array) $changes ), false );
 360          }
 361  
 362          if ( is_wp_error( $result ) ) {
 363              if ( 'db_update_error' === $result->get_error_code() ) {
 364                  $result->add_data( array( 'status' => 500 ) );
 365              } else {
 366                  $result->add_data( array( 'status' => 400 ) );
 367              }
 368              return $result;
 369          }
 370  
 371          $template      = get_block_template( $request['id'], $this->post_type );
 372          $fields_update = $this->update_additional_fields_for_object( $template, $request );
 373          if ( is_wp_error( $fields_update ) ) {
 374              return $fields_update;
 375          }
 376  
 377          $request->set_param( 'context', 'edit' );
 378  
 379          $post = get_post( $template->wp_id );
 380          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 381          do_action( "rest_after_insert_{$this->post_type}", $post, $request, false );
 382  
 383          wp_after_insert_post( $post, $update, $post_before );
 384  
 385          $response = $this->prepare_item_for_response( $template, $request );
 386  
 387          return rest_ensure_response( $response );
 388      }
 389  
 390      /**
 391       * Checks if a given request has access to create a template.
 392       *
 393       * @since 5.8.0
 394       *
 395       * @param WP_REST_Request $request Full details about the request.
 396       * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
 397       */
 398  	public function create_item_permissions_check( $request ) {
 399          return $this->permissions_check( $request );
 400      }
 401  
 402      /**
 403       * Creates a single template.
 404       *
 405       * @since 5.8.0
 406       *
 407       * @param WP_REST_Request $request Full details about the request.
 408       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 409       */
 410  	public function create_item( $request ) {
 411          $prepared_post = $this->prepare_item_for_database( $request );
 412  
 413          if ( is_wp_error( $prepared_post ) ) {
 414              return $prepared_post;
 415          }
 416  
 417          $prepared_post->post_name = $request['slug'];
 418          $post_id                  = wp_insert_post( wp_slash( (array) $prepared_post ), true );
 419          if ( is_wp_error( $post_id ) ) {
 420              if ( 'db_insert_error' === $post_id->get_error_code() ) {
 421                  $post_id->add_data( array( 'status' => 500 ) );
 422              } else {
 423                  $post_id->add_data( array( 'status' => 400 ) );
 424              }
 425  
 426              return $post_id;
 427          }
 428          $posts = get_block_templates( array( 'wp_id' => $post_id ), $this->post_type );
 429          if ( ! count( $posts ) ) {
 430              return new WP_Error( 'rest_template_insert_error', __( 'No templates exist with that id.' ), array( 'status' => 400 ) );
 431          }
 432          $id            = $posts[0]->id;
 433          $post          = get_post( $post_id );
 434          $template      = get_block_template( $id, $this->post_type );
 435          $fields_update = $this->update_additional_fields_for_object( $template, $request );
 436          if ( is_wp_error( $fields_update ) ) {
 437              return $fields_update;
 438          }
 439  
 440          /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 441          do_action( "rest_after_insert_{$this->post_type}", $post, $request, true );
 442  
 443          wp_after_insert_post( $post, false, null );
 444  
 445          $response = $this->prepare_item_for_response( $template, $request );
 446          $response = rest_ensure_response( $response );
 447  
 448          $response->set_status( 201 );
 449          $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $template->id ) ) );
 450  
 451          return $response;
 452      }
 453  
 454      /**
 455       * Checks if a given request has access to delete a single template.
 456       *
 457       * @since 5.8.0
 458       *
 459       * @param WP_REST_Request $request Full details about the request.
 460       * @return true|WP_Error True if the request has delete access for the item, WP_Error object otherwise.
 461       */
 462  	public function delete_item_permissions_check( $request ) {
 463          return $this->permissions_check( $request );
 464      }
 465  
 466      /**
 467       * Deletes a single template.
 468       *
 469       * @since 5.8.0
 470       *
 471       * @param WP_REST_Request $request Full details about the request.
 472       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 473       */
 474  	public function delete_item( $request ) {
 475          $template = get_block_template( $request['id'], $this->post_type );
 476          if ( ! $template ) {
 477              return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
 478          }
 479          if ( 'custom' !== $template->source ) {
 480              return new WP_Error( 'rest_invalid_template', __( 'Templates based on theme files can\'t be removed.' ), array( 'status' => 400 ) );
 481          }
 482  
 483          $id    = $template->wp_id;
 484          $force = (bool) $request['force'];
 485  
 486          $request->set_param( 'context', 'edit' );
 487  
 488          // If we're forcing, then delete permanently.
 489          if ( $force ) {
 490              $previous = $this->prepare_item_for_response( $template, $request );
 491              $result   = wp_delete_post( $id, true );
 492              $response = new WP_REST_Response();
 493              $response->set_data(
 494                  array(
 495                      'deleted'  => true,
 496                      'previous' => $previous->get_data(),
 497                  )
 498              );
 499          } else {
 500              // Otherwise, only trash if we haven't already.
 501              if ( 'trash' === $template->status ) {
 502                  return new WP_Error(
 503                      'rest_template_already_trashed',
 504                      __( 'The template has already been deleted.' ),
 505                      array( 'status' => 410 )
 506                  );
 507              }
 508  
 509              /*
 510               * (Note that internally this falls through to `wp_delete_post()`
 511               * if the Trash is disabled.)
 512               */
 513              $result           = wp_trash_post( $id );
 514              $template->status = 'trash';
 515              $response         = $this->prepare_item_for_response( $template, $request );
 516          }
 517  
 518          if ( ! $result ) {
 519              return new WP_Error(
 520                  'rest_cannot_delete',
 521                  __( 'The template cannot be deleted.' ),
 522                  array( 'status' => 500 )
 523              );
 524          }
 525  
 526          return $response;
 527      }
 528  
 529      /**
 530       * Prepares a single template for create or update.
 531       *
 532       * @since 5.8.0
 533       *
 534       * @param WP_REST_Request $request Request object.
 535       * @return stdClass|WP_Error Changes to pass to wp_update_post.
 536       */
 537  	protected function prepare_item_for_database( $request ) {
 538          $template = $request['id'] ? get_block_template( $request['id'], $this->post_type ) : null;
 539          $changes  = new stdClass();
 540          if ( null === $template ) {
 541              $changes->post_type   = $this->post_type;
 542              $changes->post_status = 'publish';
 543              $changes->tax_input   = array(
 544                  'wp_theme' => isset( $request['theme'] ) ? $request['theme'] : get_stylesheet(),
 545              );
 546          } elseif ( 'custom' !== $template->source ) {
 547              $changes->post_name   = $template->slug;
 548              $changes->post_type   = $this->post_type;
 549              $changes->post_status = 'publish';
 550              $changes->tax_input   = array(
 551                  'wp_theme' => $template->theme,
 552              );
 553              $changes->meta_input  = array(
 554                  'origin' => $template->source,
 555              );
 556          } else {
 557              $changes->post_name   = $template->slug;
 558              $changes->ID          = $template->wp_id;
 559              $changes->post_status = 'publish';
 560          }
 561          if ( isset( $request['content'] ) ) {
 562              if ( is_string( $request['content'] ) ) {
 563                  $changes->post_content = $request['content'];
 564              } elseif ( isset( $request['content']['raw'] ) ) {
 565                  $changes->post_content = $request['content']['raw'];
 566              }
 567          } elseif ( null !== $template && 'custom' !== $template->source ) {
 568              $changes->post_content = $template->content;
 569          }
 570          if ( isset( $request['title'] ) ) {
 571              if ( is_string( $request['title'] ) ) {
 572                  $changes->post_title = $request['title'];
 573              } elseif ( ! empty( $request['title']['raw'] ) ) {
 574                  $changes->post_title = $request['title']['raw'];
 575              }
 576          } elseif ( null !== $template && 'custom' !== $template->source ) {
 577              $changes->post_title = $template->title;
 578          }
 579          if ( isset( $request['description'] ) ) {
 580              $changes->post_excerpt = $request['description'];
 581          } elseif ( null !== $template && 'custom' !== $template->source ) {
 582              $changes->post_excerpt = $template->description;
 583          }
 584  
 585          if ( 'wp_template' === $this->post_type && isset( $request['is_wp_suggestion'] ) ) {
 586              $changes->meta_input     = wp_parse_args(
 587                  array(
 588                      'is_wp_suggestion' => $request['is_wp_suggestion'],
 589                  ),
 590                  $changes->meta_input = array()
 591              );
 592          }
 593  
 594          if ( 'wp_template_part' === $this->post_type ) {
 595              if ( isset( $request['area'] ) ) {
 596                  $changes->tax_input['wp_template_part_area'] = _filter_block_template_part_area( $request['area'] );
 597              } elseif ( null !== $template && 'custom' !== $template->source && $template->area ) {
 598                  $changes->tax_input['wp_template_part_area'] = _filter_block_template_part_area( $template->area );
 599              } elseif ( empty( $template->area ) ) {
 600                  $changes->tax_input['wp_template_part_area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
 601              }
 602          }
 603  
 604          if ( ! empty( $request['author'] ) ) {
 605              $post_author = (int) $request['author'];
 606  
 607              if ( get_current_user_id() !== $post_author ) {
 608                  $user_obj = get_userdata( $post_author );
 609  
 610                  if ( ! $user_obj ) {
 611                      return new WP_Error(
 612                          'rest_invalid_author',
 613                          __( 'Invalid author ID.' ),
 614                          array( 'status' => 400 )
 615                      );
 616                  }
 617              }
 618  
 619              $changes->post_author = $post_author;
 620          }
 621  
 622          /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 623          return apply_filters( "rest_pre_insert_{$this->post_type}", $changes, $request );
 624      }
 625  
 626      /**
 627       * Prepare a single template output for response
 628       *
 629       * @since 5.8.0
 630       * @since 5.9.0 Renamed `$template` to `$item` to match parent class for PHP 8 named parameter support.
 631       * @since 6.3.0 Added `modified` property to the response.
 632       *
 633       * @param WP_Block_Template $item    Template instance.
 634       * @param WP_REST_Request   $request Request object.
 635       * @return WP_REST_Response Response object.
 636       */
 637  	public function prepare_item_for_response( $item, $request ) {
 638          // Restores the more descriptive, specific name for use within this method.
 639          $template = $item;
 640  
 641          $fields = $this->get_fields_for_response( $request );
 642  
 643          // Base fields for every template.
 644          $data = array();
 645  
 646          if ( rest_is_field_included( 'id', $fields ) ) {
 647              $data['id'] = $template->id;
 648          }
 649  
 650          if ( rest_is_field_included( 'theme', $fields ) ) {
 651              $data['theme'] = $template->theme;
 652          }
 653  
 654          if ( rest_is_field_included( 'content', $fields ) ) {
 655              $data['content'] = array();
 656          }
 657          if ( rest_is_field_included( 'content.raw', $fields ) ) {
 658              $data['content']['raw'] = $template->content;
 659          }
 660  
 661          if ( rest_is_field_included( 'content.block_version', $fields ) ) {
 662              $data['content']['block_version'] = block_version( $template->content );
 663          }
 664  
 665          if ( rest_is_field_included( 'slug', $fields ) ) {
 666              $data['slug'] = $template->slug;
 667          }
 668  
 669          if ( rest_is_field_included( 'source', $fields ) ) {
 670              $data['source'] = $template->source;
 671          }
 672  
 673          if ( rest_is_field_included( 'origin', $fields ) ) {
 674              $data['origin'] = $template->origin;
 675          }
 676  
 677          if ( rest_is_field_included( 'type', $fields ) ) {
 678              $data['type'] = $template->type;
 679          }
 680  
 681          if ( rest_is_field_included( 'description', $fields ) ) {
 682              $data['description'] = $template->description;
 683          }
 684  
 685          if ( rest_is_field_included( 'title', $fields ) ) {
 686              $data['title'] = array();
 687          }
 688  
 689          if ( rest_is_field_included( 'title.raw', $fields ) ) {
 690              $data['title']['raw'] = $template->title;
 691          }
 692  
 693          if ( rest_is_field_included( 'title.rendered', $fields ) ) {
 694              if ( $template->wp_id ) {
 695                  /** This filter is documented in wp-includes/post-template.php */
 696                  $data['title']['rendered'] = apply_filters( 'the_title', $template->title, $template->wp_id );
 697              } else {
 698                  $data['title']['rendered'] = $template->title;
 699              }
 700          }
 701  
 702          if ( rest_is_field_included( 'status', $fields ) ) {
 703              $data['status'] = $template->status;
 704          }
 705  
 706          if ( rest_is_field_included( 'wp_id', $fields ) ) {
 707              $data['wp_id'] = (int) $template->wp_id;
 708          }
 709  
 710          if ( rest_is_field_included( 'has_theme_file', $fields ) ) {
 711              $data['has_theme_file'] = (bool) $template->has_theme_file;
 712          }
 713  
 714          if ( rest_is_field_included( 'is_custom', $fields ) && 'wp_template' === $template->type ) {
 715              $data['is_custom'] = $template->is_custom;
 716          }
 717  
 718          if ( rest_is_field_included( 'author', $fields ) ) {
 719              $data['author'] = (int) $template->author;
 720          }
 721  
 722          if ( rest_is_field_included( 'area', $fields ) && 'wp_template_part' === $template->type ) {
 723              $data['area'] = $template->area;
 724          }
 725  
 726          if ( rest_is_field_included( 'modified', $fields ) ) {
 727              $data['modified'] = mysql_to_rfc3339( $template->modified );
 728          }
 729  
 730          if ( rest_is_field_included( 'author_text', $fields ) ) {
 731              $data['author_text'] = self::get_wp_templates_author_text_field( $template );
 732          }
 733  
 734          if ( rest_is_field_included( 'original_source', $fields ) ) {
 735              $data['original_source'] = self::get_wp_templates_original_source_field( $template );
 736          }
 737  
 738          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 739          $data    = $this->add_additional_fields_to_object( $data, $request );
 740          $data    = $this->filter_response_by_context( $data, $context );
 741  
 742          // Wrap the data in a response object.
 743          $response = rest_ensure_response( $data );
 744  
 745          if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
 746              $links = $this->prepare_links( $template->id );
 747              $response->add_links( $links );
 748              if ( ! empty( $links['self']['href'] ) ) {
 749                  $actions = $this->get_available_actions();
 750                  $self    = $links['self']['href'];
 751                  foreach ( $actions as $rel ) {
 752                      $response->add_link( $rel, $self );
 753                  }
 754              }
 755          }
 756  
 757          return $response;
 758      }
 759  
 760      /**
 761       * Returns the source from where the template originally comes from.
 762       *
 763       * @since 6.5.0
 764       *
 765       * @param WP_Block_Template $template_object Template instance.
 766       * @return string                            Original source of the template one of theme, plugin, site, or user.
 767       */
 768  	private static function get_wp_templates_original_source_field( $template_object ) {
 769          if ( 'wp_template' === $template_object->type || 'wp_template_part' === $template_object->type ) {
 770              // Added by theme.
 771              // Template originally provided by a theme, but customized by a user.
 772              // Templates originally didn't have the 'origin' field so identify
 773              // older customized templates by checking for no origin and a 'theme'
 774              // or 'custom' source.
 775              if ( $template_object->has_theme_file &&
 776              ( 'theme' === $template_object->origin || (
 777                  empty( $template_object->origin ) && in_array(
 778                      $template_object->source,
 779                      array(
 780                          'theme',
 781                          'custom',
 782                      ),
 783                      true
 784                  ) )
 785              )
 786              ) {
 787                  return 'theme';
 788              }
 789  
 790              // Added by plugin.
 791              if ( $template_object->has_theme_file && 'plugin' === $template_object->origin ) {
 792                  return 'plugin';
 793              }
 794  
 795              // Added by site.
 796              // Template was created from scratch, but has no author. Author support
 797              // was only added to templates in WordPress 5.9. Fallback to showing the
 798              // site logo and title.
 799              if ( empty( $template_object->has_theme_file ) && 'custom' === $template_object->source && empty( $template_object->author ) ) {
 800                  return 'site';
 801              }
 802          }
 803  
 804          // Added by user.
 805          return 'user';
 806      }
 807  
 808      /**
 809       * Returns a human readable text for the author of the template.
 810       *
 811       * @since 6.5.0
 812       *
 813       * @param WP_Block_Template $template_object Template instance.
 814       * @return string                            Human readable text for the author.
 815       */
 816  	private static function get_wp_templates_author_text_field( $template_object ) {
 817          $original_source = self::get_wp_templates_original_source_field( $template_object );
 818          switch ( $original_source ) {
 819              case 'theme':
 820                  $theme_name = wp_get_theme( $template_object->theme )->get( 'Name' );
 821                  return empty( $theme_name ) ? $template_object->theme : $theme_name;
 822              case 'plugin':
 823                  $plugins = get_plugins();
 824                  $plugin  = $plugins[ plugin_basename( sanitize_text_field( $template_object->theme . '.php' ) ) ];
 825                  return empty( $plugin['Name'] ) ? $template_object->theme : $plugin['Name'];
 826              case 'site':
 827                  return get_bloginfo( 'name' );
 828              case 'user':
 829                  $author = get_user_by( 'id', $template_object->author );
 830                  if ( ! $author ) {
 831                      return __( 'Unknown author' );
 832                  }
 833                  return $author->get( 'display_name' );
 834          }
 835      }
 836  
 837  
 838      /**
 839       * Prepares links for the request.
 840       *
 841       * @since 5.8.0
 842       *
 843       * @param integer $id ID.
 844       * @return array Links for the given post.
 845       */
 846  	protected function prepare_links( $id ) {
 847          $links = array(
 848              'self'       => array(
 849                  'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $id ) ),
 850              ),
 851              'collection' => array(
 852                  'href' => rest_url( rest_get_route_for_post_type_items( $this->post_type ) ),
 853              ),
 854              'about'      => array(
 855                  'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
 856              ),
 857          );
 858  
 859          if ( post_type_supports( $this->post_type, 'revisions' ) ) {
 860              $template = get_block_template( $id, $this->post_type );
 861              if ( $template instanceof WP_Block_Template && ! empty( $template->wp_id ) ) {
 862                  $revisions       = wp_get_latest_revision_id_and_total_count( $template->wp_id );
 863                  $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
 864                  $revisions_base  = sprintf( '/%s/%s/%s/revisions', $this->namespace, $this->rest_base, $id );
 865  
 866                  $links['version-history'] = array(
 867                      'href'  => rest_url( $revisions_base ),
 868                      'count' => $revisions_count,
 869                  );
 870  
 871                  if ( $revisions_count > 0 ) {
 872                      $links['predecessor-version'] = array(
 873                          'href' => rest_url( $revisions_base . '/' . $revisions['latest_id'] ),
 874                          'id'   => $revisions['latest_id'],
 875                      );
 876                  }
 877              }
 878          }
 879  
 880          return $links;
 881      }
 882  
 883      /**
 884       * Get the link relations available for the post and current user.
 885       *
 886       * @since 5.8.0
 887       *
 888       * @return string[] List of link relations.
 889       */
 890  	protected function get_available_actions() {
 891          $rels = array();
 892  
 893          $post_type = get_post_type_object( $this->post_type );
 894  
 895          if ( current_user_can( $post_type->cap->publish_posts ) ) {
 896              $rels[] = 'https://api.w.org/action-publish';
 897          }
 898  
 899          if ( current_user_can( 'unfiltered_html' ) ) {
 900              $rels[] = 'https://api.w.org/action-unfiltered-html';
 901          }
 902  
 903          return $rels;
 904      }
 905  
 906      /**
 907       * Retrieves the query params for the posts collection.
 908       *
 909       * @since 5.8.0
 910       * @since 5.9.0 Added `'area'` and `'post_type'`.
 911       *
 912       * @return array Collection parameters.
 913       */
 914  	public function get_collection_params() {
 915          return array(
 916              'context'   => $this->get_context_param( array( 'default' => 'view' ) ),
 917              'wp_id'     => array(
 918                  'description' => __( 'Limit to the specified post id.' ),
 919                  'type'        => 'integer',
 920              ),
 921              'area'      => array(
 922                  'description' => __( 'Limit to the specified template part area.' ),
 923                  'type'        => 'string',
 924              ),
 925              'post_type' => array(
 926                  'description' => __( 'Post type to get the templates for.' ),
 927                  'type'        => 'string',
 928              ),
 929          );
 930      }
 931  
 932      /**
 933       * Retrieves the block type' schema, conforming to JSON Schema.
 934       *
 935       * @since 5.8.0
 936       * @since 5.9.0 Added `'area'`.
 937       *
 938       * @return array Item schema data.
 939       */
 940  	public function get_item_schema() {
 941          if ( $this->schema ) {
 942              return $this->add_additional_fields_schema( $this->schema );
 943          }
 944  
 945          $schema = array(
 946              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 947              'title'      => $this->post_type,
 948              'type'       => 'object',
 949              'properties' => array(
 950                  'id'              => array(
 951                      'description' => __( 'ID of template.' ),
 952                      'type'        => 'string',
 953                      'context'     => array( 'embed', 'view', 'edit' ),
 954                      'readonly'    => true,
 955                  ),
 956                  'slug'            => array(
 957                      'description' => __( 'Unique slug identifying the template.' ),
 958                      'type'        => 'string',
 959                      'context'     => array( 'embed', 'view', 'edit' ),
 960                      'required'    => true,
 961                      'minLength'   => 1,
 962                      'pattern'     => '[a-zA-Z0-9_\%-]+',
 963                  ),
 964                  'theme'           => array(
 965                      'description' => __( 'Theme identifier for the template.' ),
 966                      'type'        => 'string',
 967                      'context'     => array( 'embed', 'view', 'edit' ),
 968                  ),
 969                  'type'            => array(
 970                      'description' => __( 'Type of template.' ),
 971                      'type'        => 'string',
 972                      'context'     => array( 'embed', 'view', 'edit' ),
 973                  ),
 974                  'source'          => array(
 975                      'description' => __( 'Source of template' ),
 976                      'type'        => 'string',
 977                      'context'     => array( 'embed', 'view', 'edit' ),
 978                      'readonly'    => true,
 979                  ),
 980                  'origin'          => array(
 981                      'description' => __( 'Source of a customized template' ),
 982                      'type'        => 'string',
 983                      'context'     => array( 'embed', 'view', 'edit' ),
 984                      'readonly'    => true,
 985                  ),
 986                  'content'         => array(
 987                      'description' => __( 'Content of template.' ),
 988                      'type'        => array( 'object', 'string' ),
 989                      'default'     => '',
 990                      'context'     => array( 'embed', 'view', 'edit' ),
 991                      'properties'  => array(
 992                          'raw'           => array(
 993                              'description' => __( 'Content for the template, as it exists in the database.' ),
 994                              'type'        => 'string',
 995                              'context'     => array( 'view', 'edit' ),
 996                          ),
 997                          'block_version' => array(
 998                              'description' => __( 'Version of the content block format used by the template.' ),
 999                              'type'        => 'integer',
1000                              'context'     => array( 'edit' ),
1001                              'readonly'    => true,
1002                          ),
1003                      ),
1004                  ),
1005                  'title'           => array(
1006                      'description' => __( 'Title of template.' ),
1007                      'type'        => array( 'object', 'string' ),
1008                      'default'     => '',
1009                      'context'     => array( 'embed', 'view', 'edit' ),
1010                      'properties'  => array(
1011                          'raw'      => array(
1012                              'description' => __( 'Title for the template, as it exists in the database.' ),
1013                              'type'        => 'string',
1014                              'context'     => array( 'view', 'edit', 'embed' ),
1015                          ),
1016                          'rendered' => array(
1017                              'description' => __( 'HTML title for the template, transformed for display.' ),
1018                              'type'        => 'string',
1019                              'context'     => array( 'view', 'edit', 'embed' ),
1020                              'readonly'    => true,
1021                          ),
1022                      ),
1023                  ),
1024                  'description'     => array(
1025                      'description' => __( 'Description of template.' ),
1026                      'type'        => 'string',
1027                      'default'     => '',
1028                      'context'     => array( 'embed', 'view', 'edit' ),
1029                  ),
1030                  'status'          => array(
1031                      'description' => __( 'Status of template.' ),
1032                      'type'        => 'string',
1033                      'enum'        => array_keys( get_post_stati( array( 'internal' => false ) ) ),
1034                      'default'     => 'publish',
1035                      'context'     => array( 'embed', 'view', 'edit' ),
1036                  ),
1037                  'wp_id'           => array(
1038                      'description' => __( 'Post ID.' ),
1039                      'type'        => 'integer',
1040                      'context'     => array( 'embed', 'view', 'edit' ),
1041                      'readonly'    => true,
1042                  ),
1043                  'has_theme_file'  => array(
1044                      'description' => __( 'Theme file exists.' ),
1045                      'type'        => 'bool',
1046                      'context'     => array( 'embed', 'view', 'edit' ),
1047                      'readonly'    => true,
1048                  ),
1049                  'author'          => array(
1050                      'description' => __( 'The ID for the author of the template.' ),
1051                      'type'        => 'integer',
1052                      'context'     => array( 'view', 'edit', 'embed' ),
1053                  ),
1054                  'modified'        => array(
1055                      'description' => __( "The date the template was last modified, in the site's timezone." ),
1056                      'type'        => 'string',
1057                      'format'      => 'date-time',
1058                      'context'     => array( 'view', 'edit' ),
1059                      'readonly'    => true,
1060                  ),
1061                  'author_text'     => array(
1062                      'type'        => 'string',
1063                      'description' => __( 'Human readable text for the author.' ),
1064                      'readonly'    => true,
1065                      'context'     => array( 'view', 'edit', 'embed' ),
1066                  ),
1067                  'original_source' => array(
1068                      'description' => __( 'Where the template originally comes from e.g. \'theme\'' ),
1069                      'type'        => 'string',
1070                      'readonly'    => true,
1071                      'context'     => array( 'view', 'edit', 'embed' ),
1072                      'enum'        => array(
1073                          'theme',
1074                          'plugin',
1075                          'site',
1076                          'user',
1077                      ),
1078                  ),
1079              ),
1080          );
1081  
1082          if ( 'wp_template' === $this->post_type ) {
1083              $schema['properties']['is_custom'] = array(
1084                  'description' => __( 'Whether a template is a custom template.' ),
1085                  'type'        => 'bool',
1086                  'context'     => array( 'embed', 'view', 'edit' ),
1087                  'readonly'    => true,
1088              );
1089          }
1090  
1091          if ( 'wp_template_part' === $this->post_type ) {
1092              $schema['properties']['area'] = array(
1093                  'description' => __( 'Where the template part is intended for use (header, footer, etc.)' ),
1094                  'type'        => 'string',
1095                  'context'     => array( 'embed', 'view', 'edit' ),
1096              );
1097          }
1098  
1099          $this->schema = $schema;
1100  
1101          return $this->add_additional_fields_schema( $this->schema );
1102      }
1103  }


Generated : Fri Apr 26 08:20:02 2024 Cross-referenced by PHPXref