Ticket #45016: 45016.diff
File 45016.diff, 20.6 KB (added by , 6 years ago) |
---|
-
src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
1 <?php 2 /** 3 * REST API: WP_REST_Themes_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 5.0.0 8 */ 9 10 /** 11 * Core class used to manage themes via the REST API. 12 * 13 * @since 5.0.0 14 * 15 * @see WP_REST_Controller 16 */ 17 class WP_REST_Themes_Controller extends WP_REST_Controller { 18 19 /** 20 * Constructor. 21 * 22 * @since 5.0.0 23 */ 24 public function __construct() { 25 $this->namespace = 'wp/v2'; 26 $this->rest_base = 'themes'; 27 } 28 29 /** 30 * Registers the routes for the objects of the controller. 31 * 32 * @since 5.0.0 33 * 34 * @see register_rest_route() 35 */ 36 public function register_routes() { 37 register_rest_route( 38 $this->namespace, 39 '/' . $this->rest_base, 40 array( 41 array( 42 'methods' => WP_REST_Server::READABLE, 43 'callback' => array( $this, 'get_items' ), 44 'permission_callback' => array( $this, 'get_items_permissions_check' ), 45 'args' => $this->get_collection_params(), 46 ), 47 'schema' => array( $this, 'get_item_schema' ), 48 ) 49 ); 50 } 51 52 /** 53 * Checks if a given request has access to read the theme. 54 * 55 * @since 5.0.0 56 * 57 * @param WP_REST_Request $request Full details about the request. 58 * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object. 59 */ 60 public function get_items_permissions_check( $request ) { 61 if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) { 62 return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to view themes.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); 63 } 64 65 return true; 66 } 67 68 /** 69 * Retrieves a collection of themes. 70 * 71 * @since 5.0.0 72 * 73 * @param WP_REST_Request $request Full details about the request. 74 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 75 */ 76 public function get_items( $request ) { 77 // Retrieve the list of registered collection query parameters. 78 $registered = $this->get_collection_params(); 79 $themes = array(); 80 81 if ( isset( $registered['status'], $request['status'] ) && in_array( 'active', $request['status'], true ) ) { 82 $active_theme = wp_get_theme(); 83 $active_theme = $this->prepare_item_for_response( $active_theme, $request ); 84 $themes[] = $this->prepare_response_for_collection( $active_theme ); 85 } 86 87 $response = rest_ensure_response( $themes ); 88 89 $response->header( 'X-WP-Total', count( $themes ) ); 90 $response->header( 'X-WP-TotalPages', count( $themes ) ); 91 92 return $response; 93 } 94 95 /** 96 * Prepares a single theme output for response. 97 * 98 * @since 5.0.0 99 * 100 * @param WP_Theme $theme Theme object. 101 * @param WP_REST_Request $request Request object. 102 * @return WP_REST_Response Response object. 103 */ 104 public function prepare_item_for_response( $theme, $request ) { 105 $data = array(); 106 $fields = $this->get_fields_for_response( $request ); 107 108 if ( in_array( 'theme_supports', $fields, true ) ) { 109 $formats = get_theme_support( 'post-formats' ); 110 $formats = is_array( $formats ) ? array_values( $formats[0] ) : array(); 111 $formats = array_merge( array( 'standard' ), $formats ); 112 $data['theme_supports']['formats'] = $formats; 113 114 $data['theme_supports']['post-thumbnails'] = false; 115 $post_thumbnails = get_theme_support( 'post-thumbnails' ); 116 117 if ( $post_thumbnails ) { 118 // $post_thumbnails can contain a nested array of post types. 119 // e.g. array( array( 'post', 'page' ) ). 120 $data['theme_supports']['post-thumbnails'] = is_array( $post_thumbnails ) ? $post_thumbnails[0] : true; 121 } 122 } 123 124 $data = $this->add_additional_fields_to_object( $data, $request ); 125 126 // Wrap the data in a response object. 127 $response = rest_ensure_response( $data ); 128 129 /** 130 * Filters theme data returned from the REST API. 131 * 132 * @since 5.0.0 133 * 134 * @param WP_REST_Response $response The response object. 135 * @param WP_Theme $theme Theme object used to create response. 136 * @param WP_REST_Request $request Request object. 137 */ 138 return apply_filters( 'rest_prepare_theme', $response, $theme, $request ); 139 } 140 141 /** 142 * Retrieves the theme's schema, conforming to JSON Schema. 143 * 144 * @since 5.0.0 145 * 146 * @return array Item schema data. 147 */ 148 public function get_item_schema() { 149 $schema = array( 150 '$schema' => 'http://json-schema.org/draft-04/schema#', 151 'title' => 'theme', 152 'type' => 'object', 153 'properties' => array( 154 'theme_supports' => array( 155 'description' => __( 'Features supported by this theme.', 'gutenberg' ), 156 'type' => 'array', 157 'readonly' => true, 158 'properties' => array( 159 'formats' => array( 160 'description' => __( 'Post formats supported.', 'gutenberg' ), 161 'type' => 'array', 162 'readonly' => true, 163 ), 164 'post-thumbnails' => array( 165 'description' => __( 'Whether the theme supports post thumbnails.', 'gutenberg' ), 166 'type' => array( 'array', 'bool' ), 167 'readonly' => true, 168 ), 169 ), 170 ), 171 ), 172 ); 173 174 return $this->add_additional_fields_schema( $schema ); 175 } 176 177 /** 178 * Retrieves the search params for the themes collection. 179 * 180 * @since 5.0.0 181 * 182 * @return array Collection parameters. 183 */ 184 public function get_collection_params() { 185 $query_params = parent::get_collection_params(); 186 187 $query_params['status'] = array( 188 'description' => __( 'Limit result set to themes assigned one or more statuses.', 'gutenberg' ), 189 'type' => 'array', 190 'items' => array( 191 'enum' => array( 'active' ), 192 'type' => 'string', 193 ), 194 'required' => true, 195 'sanitize_callback' => array( $this, 'sanitize_theme_status' ), 196 ); 197 198 /** 199 * Filter collection parameters for the themes controller. 200 * 201 * @since 5.0.0 202 * 203 * @param array $query_params JSON Schema-formatted collection parameters. 204 */ 205 return apply_filters( 'rest_themes_collection_params', $query_params ); 206 } 207 208 /** 209 * Sanitizes and validates the list of theme status. 210 * 211 * @since 5.0.0 212 * 213 * @param string|array $statuses One or more theme statuses. 214 * @param WP_REST_Request $request Full details about the request. 215 * @param string $parameter Additional parameter to pass to validation. 216 * @return array|WP_Error A list of valid statuses, otherwise WP_Error object. 217 */ 218 public function sanitize_theme_status( $statuses, $request, $parameter ) { 219 $statuses = wp_parse_slug_list( $statuses ); 220 221 foreach ( $statuses as $status ) { 222 $result = rest_validate_request_arg( $status, $request, $parameter ); 223 224 if ( is_wp_error( $result ) ) { 225 return $result; 226 } 227 } 228 229 return $statuses; 230 } 231 } -
src/wp-includes/rest-api.php
233 233 // Settings. 234 234 $controller = new WP_REST_Settings_Controller; 235 235 $controller->register_routes(); 236 237 // Themes. 238 $controller = new WP_REST_Themes_Controller; 239 $controller->register_routes(); 236 240 } 237 241 238 242 /** -
src/wp-settings.php
234 234 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' ); 235 235 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' ); 236 236 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' ); 237 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-themes-controller.php' ); 237 238 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' ); 238 239 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php' ); 239 240 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' ); -
tests/phpunit/tests/rest-api/rest-schema-setup.php
111 111 '/wp/v2/comments', 112 112 '/wp/v2/comments/(?P<id>[\\d]+)', 113 113 '/wp/v2/settings', 114 '/wp/v2/themes', 114 115 ); 115 116 116 117 $this->assertEquals( $expected_routes, $routes ); -
tests/phpunit/tests/rest-api/rest-themes-controller.php
1 <?php 2 /** 3 * Unit tests covering WP_REST_Themes_Controller functionality. 4 * 5 * @package WordPress 6 * @subpackage REST API 7 */ 8 9 /** 10 * @group restapi-themes 11 * @group restapi 12 */ 13 class WP_Test_REST_Themes_Controller extends WP_Test_REST_Controller_Testcase { 14 /** 15 * Subscriber user ID. 16 * 17 * @since 5.0.0 18 * 19 * @var int $subscriber_id 20 */ 21 protected static $subscriber_id; 22 23 /** 24 * Contributor user ID. 25 * 26 * @since 5.0.0 27 * 28 * @var int $contributor_id 29 */ 30 protected static $contributor_id; 31 32 /** 33 * The current theme object. 34 * 35 * @since 5.0.0 36 * 37 * @var WP_Theme $current_theme 38 */ 39 protected static $current_theme; 40 41 /** 42 * The REST API route for the active theme. 43 * 44 * @since 5.0.0 45 * 46 * @var string $themes_route 47 */ 48 protected static $themes_route = '/wp/v2/themes'; 49 50 /** 51 * Performs a REST API request for the active theme. 52 * 53 * @since 5.0.0 54 * 55 * @param string $method Optional. Request method. Default GET. 56 * @return WP_REST_Response The request's response. 57 */ 58 protected function perform_active_theme_request( $method = 'GET' ) { 59 $request = new WP_REST_Request( $method, self::$themes_route ); 60 $request->set_param( 'status', 'active' ); 61 62 return rest_get_server()->dispatch( $request ); 63 } 64 65 /** 66 * Check that common properties are included in a response. 67 * 68 * @since 5.0.0 69 * 70 * @param WP_REST_Response $response Current REST API response. 71 */ 72 protected function check_get_theme_response( $response ) { 73 if ( $response instanceof WP_REST_Response ) { 74 $headers = $response->get_headers(); 75 $response = $response->get_data(); 76 } else { 77 $headers = array(); 78 } 79 80 $this->assertArrayHasKey( 'X-WP-Total', $headers ); 81 $this->assertEquals( 1, $headers['X-WP-Total'] ); 82 $this->assertArrayHasKey( 'X-WP-TotalPages', $headers ); 83 $this->assertEquals( 1, $headers['X-WP-TotalPages'] ); 84 } 85 86 /** 87 * Set up class test fixtures. 88 * 89 * @since 5.0.0 90 * 91 * @param WP_UnitTest_Factory $factory WordPress unit test factory. 92 */ 93 public static function wpSetUpBeforeClass( $factory ) { 94 self::$subscriber_id = $factory->user->create( 95 array( 96 'role' => 'subscriber', 97 ) 98 ); 99 self::$contributor_id = $factory->user->create( 100 array( 101 'role' => 'contributor', 102 ) 103 ); 104 self::$current_theme = wp_get_theme(); 105 106 wp_set_current_user( self::$contributor_id ); 107 } 108 109 /** 110 * Clean up test fixtures. 111 * 112 * @since 5.0.0 113 */ 114 public static function wpTearDownAfterClass() { 115 self::delete_user( self::$subscriber_id ); 116 self::delete_user( self::$contributor_id ); 117 } 118 119 /** 120 * Set up each test method. 121 * 122 * @since 5.0.0 123 */ 124 public function setUp() { 125 parent::setUp(); 126 127 wp_set_current_user( self::$contributor_id ); 128 } 129 130 /** 131 * Theme routes should be registered correctly. 132 * 133 * @ticket 45016 134 */ 135 public function test_register_routes() { 136 $routes = rest_get_server()->get_routes(); 137 $this->assertArrayHasKey( self::$themes_route, $routes ); 138 } 139 140 /** 141 * Test retrieving a collection of themes. 142 * 143 * @ticket 45016 144 */ 145 public function test_get_items() { 146 $response = self::perform_active_theme_request(); 147 148 $this->assertEquals( 200, $response->get_status() ); 149 $data = $response->get_data(); 150 151 $this->check_get_theme_response( $response ); 152 $fields = array( 153 'theme_supports', 154 ); 155 $this->assertEqualSets( $fields, array_keys( $data[0] ) ); 156 } 157 158 /** 159 * An error should be returned when the user does not have the edit_posts capability. 160 * 161 * @ticket 45016 162 */ 163 public function test_get_items_no_permission() { 164 wp_set_current_user( self::$subscriber_id ); 165 $response = self::perform_active_theme_request(); 166 $this->assertErrorResponse( 'rest_user_cannot_view', $response, 403 ); 167 } 168 169 /** 170 * Test an item is prepared for the response. 171 * 172 * @ticket 45016 173 */ 174 public function test_prepare_item() { 175 $response = self::perform_active_theme_request(); 176 $this->assertEquals( 200, $response->get_status() ); 177 $this->check_get_theme_response( $response ); 178 } 179 180 /** 181 * Verify the theme schema. 182 * 183 * @ticket 45016 184 */ 185 public function test_get_item_schema() { 186 $response = self::perform_active_theme_request( 'OPTIONS' ); 187 $data = $response->get_data(); 188 $properties = $data['schema']['properties']; 189 $this->assertEquals( 1, count( $properties ) ); 190 $this->assertArrayHasKey( 'theme_supports', $properties ); 191 192 $this->assertEquals( 2, count( $properties['theme_supports']['properties'] ) ); 193 $this->assertArrayHasKey( 'formats', $properties['theme_supports']['properties'] ); 194 $this->assertArrayHasKey( 'post-thumbnails', $properties['theme_supports']['properties'] ); 195 } 196 197 /** 198 * Should include relevant data in the 'theme_supports' key. 199 * 200 * @ticket 45016 201 */ 202 public function test_theme_supports_formats() { 203 remove_theme_support( 'post-formats' ); 204 $response = self::perform_active_theme_request(); 205 $result = $response->get_data(); 206 $this->assertTrue( isset( $result[0]['theme_supports'] ) ); 207 $this->assertTrue( isset( $result[0]['theme_supports']['formats'] ) ); 208 $this->assertSame( array( 'standard' ), $result[0]['theme_supports']['formats'] ); 209 } 210 211 /** 212 * Test when a theme only supports some post formats. 213 * 214 * @ticket 45016 215 */ 216 public function test_theme_supports_formats_non_default() { 217 add_theme_support( 'post-formats', array( 'aside', 'video' ) ); 218 $response = self::perform_active_theme_request(); 219 $result = $response->get_data(); 220 $this->assertTrue( isset( $result[0]['theme_supports'] ) ); 221 $this->assertTrue( isset( $result[0]['theme_supports']['formats'] ) ); 222 $this->assertSame( array( 'standard', 'aside', 'video' ), $result[0]['theme_supports']['formats'] ); 223 } 224 225 /** 226 * Test when a theme does not support post thumbnails. 227 * 228 * @ticket 45016 229 */ 230 public function test_theme_supports_post_thumbnails_false() { 231 remove_theme_support( 'post-thumbnails' ); 232 $response = self::perform_active_theme_request(); 233 234 $result = $response->get_data(); 235 $this->assertTrue( isset( $result[0]['theme_supports'] ) ); 236 $this->assertTrue( isset( $result[0]['theme_supports']['post-thumbnails'] ) ); 237 $this->assertFalse( $result[0]['theme_supports']['post-thumbnails'] ); 238 } 239 240 /** 241 * Test when a theme supports all post thumbnails. 242 * 243 * @ticket 45016 244 */ 245 public function test_theme_supports_post_thumbnails_true() { 246 remove_theme_support( 'post-thumbnails' ); 247 add_theme_support( 'post-thumbnails' ); 248 $response = self::perform_active_theme_request(); 249 $result = $response->get_data(); 250 $this->assertTrue( isset( $result[0]['theme_supports'] ) ); 251 $this->assertTrue( $result[0]['theme_supports']['post-thumbnails'] ); 252 } 253 254 /** 255 * Test when a theme only supports post thumbnails for certain post types. 256 * 257 * @ticket 45016 258 */ 259 public function test_theme_supports_post_thumbnails_array() { 260 remove_theme_support( 'post-thumbnails' ); 261 add_theme_support( 'post-thumbnails', array( 'post' ) ); 262 $response = self::perform_active_theme_request(); 263 $result = $response->get_data(); 264 $this->assertTrue( isset( $result[0]['theme_supports'] ) ); 265 $this->assertEquals( array( 'post' ), $result[0]['theme_supports']['post-thumbnails'] ); 266 } 267 268 /** 269 * It should be possible to register custom fields to the endpoint. 270 * 271 * @ticket 45016 272 */ 273 public function test_get_additional_field_registration() { 274 $schema = array( 275 'type' => 'integer', 276 'description' => 'Some integer of mine', 277 'enum' => array( 1, 2, 3, 4 ), 278 ); 279 280 register_rest_field( 281 'theme', 282 'my_custom_int', 283 array( 284 'schema' => $schema, 285 'get_callback' => array( $this, 'additional_field_get_callback' ), 286 ) 287 ); 288 289 $response = self::perform_active_theme_request( 'OPTIONS' ); 290 $data = $response->get_data(); 291 292 $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] ); 293 $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] ); 294 295 $response = self::perform_active_theme_request( 'GET' ); 296 $data = $response->get_data(); 297 $this->assertArrayHasKey( 'my_custom_int', $data[0] ); 298 $this->assertSame( 2, $data[0]['my_custom_int'] ); 299 300 global $wp_rest_additional_fields; 301 $wp_rest_additional_fields = array(); 302 } 303 304 /** 305 * Return a value for the custom field. 306 * 307 * @since 5.0.0 308 * 309 * @param array $theme Theme data array. 310 * @return int Additional field value. 311 */ 312 public function additional_field_get_callback( $theme ) { 313 return 2; 314 } 315 316 /** 317 * The create_item() method does not exist for themes. 318 */ 319 public function test_create_item() {} 320 321 /** 322 * The update_item() method does not exist for themes. 323 */ 324 public function test_update_item() {} 325 326 /** 327 * The get_item() method does not exist for themes. 328 */ 329 public function test_get_item() {} 330 331 /** 332 * The delete_item() method does not exist for themes. 333 */ 334 public function test_delete_item() {} 335 336 /** 337 * Context is not supported for themes. 338 */ 339 public function test_context_param() {} 340 } -
tests/qunit/fixtures/wp-api-generated.js
3517 3517 "_links": { 3518 3518 "self": "http://example.org/index.php?rest_route=/wp/v2/settings" 3519 3519 } 3520 }, 3521 "/wp/v2/themes": { 3522 "namespace": "wp/v2", 3523 "methods": [ 3524 "GET" 3525 ], 3526 "endpoints": [ 3527 { 3528 "methods": [ 3529 "GET" 3530 ], 3531 "args": { 3532 "context": { 3533 "required": false, 3534 "description": "Scope under which the request is made; determines fields present in response.", 3535 "type": "string" 3536 }, 3537 "page": { 3538 "required": false, 3539 "default": 1, 3540 "description": "Current page of the collection.", 3541 "type": "integer" 3542 }, 3543 "per_page": { 3544 "required": false, 3545 "default": 10, 3546 "description": "Maximum number of items to be returned in result set.", 3547 "type": "integer" 3548 }, 3549 "search": { 3550 "required": false, 3551 "description": "Limit results to those matching a string.", 3552 "type": "string" 3553 }, 3554 "status": { 3555 "required": true, 3556 "description": "Limit result set to themes assigned one or more statuses.", 3557 "type": "array", 3558 "items": { 3559 "enum": [ 3560 "active" 3561 ], 3562 "type": "string" 3563 } 3564 } 3565 } 3566 } 3567 ], 3568 "_links": { 3569 "self": "http://example.org/index.php?rest_route=/wp/v2/themes" 3570 } 3520 3571 } 3521 3572 } 3522 3573 };