Changeset 49925
- Timestamp:
- 01/03/2021 09:45:42 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/rest-api/class-wp-rest-server.php
r49918 r49925 1245 1245 1246 1246 $response = new WP_REST_Response( $available ); 1247 1248 1247 $response->add_link( 'help', 'http://v2.wp-api.org/' ); 1248 $this->add_active_theme_link_to_index( $response ); 1249 1249 1250 1250 /** … … 1260 1260 */ 1261 1261 return apply_filters( 'rest_index', $response ); 1262 } 1263 1264 /** 1265 * Adds a link to the active theme for users who have proper permissions. 1266 * 1267 * @since 5.7.0 1268 * 1269 * @param WP_REST_Response $response REST API response. 1270 */ 1271 protected function add_active_theme_link_to_index( WP_REST_Response $response ) { 1272 $should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ); 1273 1274 if ( ! $should_add && current_user_can( 'edit_posts' ) ) { 1275 $should_add = true; 1276 } 1277 1278 if ( ! $should_add ) { 1279 foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { 1280 if ( current_user_can( $post_type->cap->edit_posts ) ) { 1281 $should_add = true; 1282 break; 1283 } 1284 } 1285 } 1286 1287 if ( $should_add ) { 1288 $theme = wp_get_theme(); 1289 $response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) ); 1290 } 1262 1291 } 1263 1292 -
trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
r48782 r49925 48 48 ) 49 49 ); 50 51 register_rest_route( 52 $this->namespace, 53 '/' . $this->rest_base . '/(?P<stylesheet>[\w-]+)', 54 array( 55 'args' => array( 56 'stylesheet' => array( 57 'description' => __( "The theme's stylesheet. This uniquely identifies the theme." ), 58 'type' => 'string', 59 ), 60 ), 61 array( 62 'methods' => WP_REST_Server::READABLE, 63 'callback' => array( $this, 'get_item' ), 64 'permission_callback' => array( $this, 'get_item_permissions_check' ), 65 ), 66 'schema' => array( $this, 'get_public_item_schema' ), 67 ) 68 ); 50 69 } 51 70 … … 59 78 */ 60 79 public function get_items_permissions_check( $request ) { 80 if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) { 81 return true; 82 } 83 84 $registered = $this->get_collection_params(); 85 if ( isset( $registered['status'], $request['status'] ) && is_array( $request['status'] ) && array( 'active' ) === $request['status'] ) { 86 return $this->check_read_active_theme_permission(); 87 } 88 89 return new WP_Error( 90 'rest_cannot_view_themes', 91 __( 'Sorry, you are not allowed to view themes.' ), 92 array( 'status' => rest_authorization_required_code() ) 93 ); 94 } 95 96 /** 97 * Checks if a given request has access to read the theme. 98 * 99 * @since 5.7.0 100 * 101 * @param WP_REST_Request $request Full details about the request. 102 * @return bool|WP_Error True if the request has read access for the item, otherwise WP_Error object. 103 */ 104 public function get_item_permissions_check( $request ) { 105 if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) { 106 return true; 107 } 108 109 $wp_theme = wp_get_theme( $request['stylesheet'] ); 110 $current_theme = wp_get_theme(); 111 112 if ( $this->is_same_theme( $wp_theme, $current_theme ) ) { 113 return $this->check_read_active_theme_permission(); 114 } 115 116 return new WP_Error( 117 'rest_cannot_view_themes', 118 __( 'Sorry, you are not allowed to view themes.' ), 119 array( 'status' => rest_authorization_required_code() ) 120 ); 121 } 122 123 /** 124 * Checks if a theme can be read. 125 * 126 * @since 5.7.0 127 * 128 * @return bool|WP_Error Whether the theme can be read. 129 */ 130 protected function check_read_active_theme_permission() { 61 131 if ( current_user_can( 'edit_posts' ) ) { 62 132 return true; … … 70 140 71 141 return new WP_Error( 72 'rest_ user_cannot_view',73 __( 'Sorry, you are not allowed to view the mes.' ),142 'rest_cannot_view_active_theme', 143 __( 'Sorry, you are not allowed to view the active theme.' ), 74 144 array( 'status' => rest_authorization_required_code() ) 75 145 ); … … 77 147 78 148 /** 79 * Retrieves a collection of themes.80 * 81 * @since 5. 0.0149 * Retrieves a single theme. 150 * 151 * @since 5.7.0 82 152 * 83 153 * @param WP_REST_Request $request Full details about the request. 84 154 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 85 155 */ 156 public function get_item( $request ) { 157 $wp_theme = wp_get_theme( $request['stylesheet'] ); 158 if ( ! $wp_theme->exists() ) { 159 return new WP_Error( 160 'rest_theme_not_found', 161 __( 'Theme not found.' ), 162 array( 'status' => 404 ) 163 ); 164 } 165 $data = $this->prepare_item_for_response( $wp_theme, $request ); 166 167 return rest_ensure_response( $data ); 168 } 169 170 /** 171 * Retrieves a collection of themes. 172 * 173 * @since 5.0.0 174 * 175 * @param WP_REST_Request $request Full details about the request. 176 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 177 */ 86 178 public function get_items( $request ) { 87 // Retrieve the list of registered collection query parameters. 88 $registered = $this->get_collection_params(); 89 $themes = array(); 90 91 if ( isset( $registered['status'], $request['status'] ) && in_array( 'active', $request['status'], true ) ) { 92 $active_theme = wp_get_theme(); 93 $active_theme = $this->prepare_item_for_response( $active_theme, $request ); 94 $themes[] = $this->prepare_response_for_collection( $active_theme ); 179 $themes = array(); 180 181 $active_themes = wp_get_themes(); 182 $current_theme = wp_get_theme(); 183 $status = $request['status']; 184 185 foreach ( $active_themes as $theme_name => $theme ) { 186 $theme_status = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive'; 187 if ( is_array( $status ) && ! in_array( $theme_status, $status, true ) ) { 188 continue; 189 } 190 191 $prepared = $this->prepare_item_for_response( $theme, $request ); 192 $themes[] = $this->prepare_response_for_collection( $prepared ); 95 193 } 96 194 … … 98 196 99 197 $response->header( 'X-WP-Total', count( $themes ) ); 100 $response->header( 'X-WP-TotalPages', count( $themes ));198 $response->header( 'X-WP-TotalPages', 1 ); 101 199 102 200 return $response; … … 167 265 } 168 266 169 if ( rest_is_field_included( 'theme_supports', $fields ) ) { 267 $current_theme = wp_get_theme(); 268 if ( rest_is_field_included( 'status', $fields ) ) { 269 $data['status'] = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive'; 270 } 271 272 if ( rest_is_field_included( 'theme_supports', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) { 170 273 foreach ( get_registered_theme_features() as $feature => $config ) { 171 274 if ( ! is_array( $config['show_in_rest'] ) ) { … … 206 309 // Wrap the data in a response object. 207 310 $response = rest_ensure_response( $data ); 311 312 $response->add_links( $this->prepare_links( $theme ) ); 208 313 209 314 /** … … 220 325 221 326 /** 327 * Prepares links for the request. 328 * 329 * @since 5.7.0 330 * 331 * @param WP_Theme $theme Theme data. 332 * @return array Links for the given block type. 333 */ 334 protected function prepare_links( $theme ) { 335 return array( 336 'self' => array( 337 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_stylesheet() ) ), 338 ), 339 'collection' => array( 340 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), 341 ), 342 ); 343 } 344 345 /** 346 * Helper function to compare two themes. 347 * 348 * @since 5.7.0 349 * 350 * @param WP_Theme $theme_a First theme to compare. 351 * @param WP_Theme $theme_b Second theme to compare. 352 * 353 * @return bool 354 */ 355 protected function is_same_theme( $theme_a, $theme_b ) { 356 return $theme_a->get_stylesheet() === $theme_b->get_stylesheet(); 357 } 358 359 /** 222 360 * Prepares the theme support value for inclusion in the REST API response. 223 361 * … … 400 538 'readonly' => true, 401 539 ), 540 'status' => array( 541 'description' => __( 'A named status for the theme.' ), 542 'type' => 'string', 543 'enum' => array( 'inactive', 'active' ), 544 ), 402 545 ), 403 546 ); … … 426 569 */ 427 570 public function get_collection_params() { 428 $query_params = parent::get_collection_params();429 430 $query_params['status'] = array(431 'description' => __( 'Limit result set to themes assigned one or more statuses.' ),432 'type' => 'array',433 'items' => array(434 'enum' => array( 'active' ),435 'type' => 'string',571 $query_params = array( 572 'status' => array( 573 'description' => __( 'Limit result set to themes assigned one or more statuses.' ), 574 'type' => 'array', 575 'items' => array( 576 'enum' => array( 'active', 'inactive' ), 577 'type' => 'string', 578 ), 436 579 ), 437 'required' => true,438 'sanitize_callback' => array( $this, 'sanitize_theme_status' ),439 580 ); 440 581 … … 453 594 * 454 595 * @since 5.0.0 596 * @deprecated 5.7.0 455 597 * 456 598 * @param string|array $statuses One or more theme statuses. … … 460 602 */ 461 603 public function sanitize_theme_status( $statuses, $request, $parameter ) { 604 _deprecated_function( __METHOD__, '5.7.0' ); 605 462 606 $statuses = wp_parse_slug_list( $statuses ); 463 607 -
trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
r49334 r49925 130 130 '/wp/v2/settings', 131 131 '/wp/v2/themes', 132 '/wp/v2/themes/(?P<stylesheet>[\w-]+)', 132 133 '/wp/v2/plugins', 133 134 '/wp/v2/plugins/(?P<plugin>[^.\/]+(?:\/[^.\/]+)?)', -
trunk/tests/phpunit/tests/rest-api/rest-server.php
r49547 r49925 960 960 $this->assertContains( 'DELETE', $route['methods'] ); 961 961 $this->assertArrayHasKey( '_links', $route ); 962 963 $this->assertArrayHasKey( 'help', $index->get_links() ); 964 $this->assertArrayNotHasKey( 'wp:active-theme', $index->get_links() ); 962 965 } 963 966 … … 1997 2000 } 1998 2001 2002 /** 2003 * @ticket 50152 2004 */ 2005 public function test_index_includes_link_to_active_theme_if_authenticated() { 2006 wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); 2007 2008 $index = rest_do_request( '/' ); 2009 $this->assertArrayHasKey( 'https://api.w.org/active-theme', $index->get_links() ); 2010 } 2011 1999 2012 public function _validate_as_integer_123( $value, $request, $key ) { 2000 2013 if ( ! is_int( $value ) ) { -
trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php
r49603 r49925 31 31 32 32 /** 33 * Admin user ID. 34 * 35 * @since 5.7.0 36 * 37 * @var int $admin_id 38 */ 39 protected static $admin_id; 40 41 /** 33 42 * The current theme object. 34 43 * … … 92 101 */ 93 102 public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { 103 self::$admin_id = $factory->user->create( 104 array( 105 'role' => 'administrator', 106 ) 107 ); 94 108 self::$subscriber_id = $factory->user->create( 95 109 array( … … 115 129 self::delete_user( self::$subscriber_id ); 116 130 self::delete_user( self::$contributor_id ); 131 self::delete_user( self::$admin_id ); 117 132 } 118 133 … … 137 152 $routes = rest_get_server()->get_routes(); 138 153 $this->assertArrayHasKey( self::$themes_route, $routes ); 154 $this->assertArrayHasKey( self::$themes_route . '/(?P<stylesheet>[\\w-]+)', $routes ); 139 155 } 140 156 … … 152 168 $this->check_get_theme_response( $response ); 153 169 $fields = array( 170 '_links', 154 171 'author', 155 172 'author_uri', … … 159 176 'requires_wp', 160 177 'screenshot', 178 'status', 161 179 'stylesheet', 162 180 'tags', … … 171 189 172 190 /** 191 * Test retrieving a collection of inactive themes. 192 * 193 * @ticket 50152 194 */ 195 public function test_get_items_inactive() { 196 wp_set_current_user( self::$admin_id ); 197 $request = new WP_REST_Request( 'GET', self::$themes_route ); 198 $request->set_param( 'status', 'inactive' ); 199 200 $response = rest_get_server()->dispatch( $request ); 201 202 $this->assertEquals( 200, $response->get_status() ); 203 $data = $response->get_data(); 204 205 $fields = array( 206 '_links', 207 'author', 208 'author_uri', 209 'description', 210 'name', 211 'requires_php', 212 'requires_wp', 213 'screenshot', 214 'status', 215 'stylesheet', 216 'tags', 217 'template', 218 'textdomain', 219 'theme_uri', 220 'version', 221 ); 222 $this->assertEqualSets( $fields, array_keys( $data[0] ) ); 223 224 $this->assertContains( 'twentytwenty', wp_list_pluck( $data, 'stylesheet' ) ); 225 $this->assertNotContains( get_stylesheet(), wp_list_pluck( $data, 'stylesheet' ) ); 226 } 227 228 /** 229 * Test retrieving a collection of inactive themes. 230 * 231 * @ticket 50152 232 */ 233 public function test_get_items_active_and_inactive() { 234 wp_set_current_user( self::$admin_id ); 235 $request = new WP_REST_Request( 'GET', self::$themes_route ); 236 $request->set_param( 'status', array( 'active', 'inactive' ) ); 237 238 $response = rest_get_server()->dispatch( $request ); 239 240 $this->assertEquals( 200, $response->get_status() ); 241 $data = $response->get_data(); 242 243 $this->assertContains( 'twentytwenty', wp_list_pluck( $data, 'stylesheet' ) ); 244 $this->assertContains( get_stylesheet(), wp_list_pluck( $data, 'stylesheet' ) ); 245 } 246 247 /** 173 248 * @ticket 46723 174 */ 175 public function test_get_items_logged_out() { 249 * @ticket 50152 250 * @dataProvider data_get_items_by_status 251 */ 252 public function test_get_items_logged_out( $status, $error_code ) { 176 253 wp_set_current_user( 0 ); 177 $response = self::perform_active_theme_request(); 178 $this->assertErrorResponse( 'rest_user_cannot_view', $response, 401 ); 254 $request = new WP_REST_Request( 'GET', self::$themes_route ); 255 $request->set_param( 'status', $status ); 256 257 $response = rest_get_server()->dispatch( $request ); 258 $this->assertErrorResponse( $error_code, $response, 401 ); 179 259 } 180 260 … … 183 263 * 184 264 * @ticket 45016 185 */ 186 public function test_get_items_no_permission() { 265 * @ticket 50152 266 * @dataProvider data_get_items_by_status 267 */ 268 public function test_get_items_no_permission( $status, $error_code ) { 187 269 wp_set_current_user( self::$subscriber_id ); 188 $response = self::perform_active_theme_request(); 189 $this->assertErrorResponse( 'rest_user_cannot_view', $response, 403 ); 270 $request = new WP_REST_Request( 'GET', self::$themes_route ); 271 $request->set_param( 'status', $status ); 272 273 $response = rest_get_server()->dispatch( $request ); 274 $this->assertErrorResponse( $error_code, $response, 403 ); 275 } 276 277 public function data_get_items_by_status() { 278 return array( 279 array( 'active', 'rest_cannot_view_active_theme' ), 280 array( 'active, inactive', 'rest_cannot_view_themes' ), 281 array( 'inactive', 'rest_cannot_view_themes' ), 282 array( '', 'rest_cannot_view_themes' ), 283 ); 284 } 285 286 /** 287 * @ticket 50152 288 * @dataProvider data_get_items_by_status_for_contributor 289 */ 290 public function test_get_items_contributor( $status, $error_code ) { 291 wp_set_current_user( self::$contributor_id ); 292 $request = new WP_REST_Request( 'GET', self::$themes_route ); 293 $request->set_param( 'status', $status ); 294 295 $response = rest_get_server()->dispatch( $request ); 296 297 if ( $error_code ) { 298 $this->assertErrorResponse( $error_code, $response, 403 ); 299 } else { 300 $this->assertEquals( 200, $response->get_status() ); 301 } 302 } 303 304 public function data_get_items_by_status_for_contributor() { 305 return array( 306 array( 'active', '' ), 307 array( 'active, inactive', 'rest_cannot_view_themes' ), 308 array( 'inactive', 'rest_cannot_view_themes' ), 309 array( '', 'rest_cannot_view_themes' ), 310 ); 190 311 } 191 312 … … 222 343 $data = $response->get_data(); 223 344 $properties = $data['schema']['properties']; 224 $this->assertSame( 1 4, count( $properties ) );345 $this->assertSame( 15, count( $properties ) ); 225 346 226 347 $this->assertArrayHasKey( 'author', $properties ); … … 243 364 $this->assertArrayHasKey( 'requires_wp', $properties ); 244 365 $this->assertArrayHasKey( 'screenshot', $properties ); 366 $this->assertArrayHasKey( 'status', $properties ); 245 367 $this->assertArrayHasKey( 'stylesheet', $properties ); 246 368 … … 1084 1206 1085 1207 /** 1086 * The get_item() method does not exist for themes. 1087 */ 1088 public function test_get_item() {} 1208 * Test single theme 1209 * 1210 * @ticket 50152 1211 */ 1212 public function test_get_item() { 1213 wp_set_current_user( self::$admin_id ); 1214 $route = sprintf( '%s/%s', self::$themes_route, WP_DEFAULT_THEME ); 1215 $request = new WP_REST_Request( 'GET', $route ); 1216 $response = rest_get_server()->dispatch( $request ); 1217 1218 $this->assertEquals( 200, $response->get_status() ); 1219 $data = $response->get_data(); 1220 $links = $response->get_links(); 1221 $fields = array( 1222 'author', 1223 'author_uri', 1224 'description', 1225 'name', 1226 'requires_php', 1227 'requires_wp', 1228 'screenshot', 1229 'status', 1230 'stylesheet', 1231 'tags', 1232 'template', 1233 'textdomain', 1234 'theme_uri', 1235 'version', 1236 ); 1237 $fields_links = array( 'collection', 'self' ); 1238 1239 $this->assertEqualSets( $fields, array_keys( $data ) ); 1240 $this->assertEqualSets( $fields_links, array_keys( $links ) ); 1241 } 1242 1243 /** 1244 * @ticket 50152 1245 */ 1246 public function test_get_item_no_permission() { 1247 wp_set_current_user( self::$subscriber_id ); 1248 $request = new WP_REST_Request( 'GET', self::$themes_route . '/' . WP_DEFAULT_THEME ); 1249 $response = rest_get_server()->dispatch( $request ); 1250 $this->assertErrorResponse( 'rest_cannot_view_themes', $response, 403 ); 1251 } 1252 1253 /** 1254 * @ticket 50152 1255 */ 1256 public function test_get_active_item_no_permission() { 1257 wp_set_current_user( self::$subscriber_id ); 1258 $request = new WP_REST_Request( 'GET', self::$themes_route . '/' . get_stylesheet() ); 1259 $response = rest_get_server()->dispatch( $request ); 1260 $this->assertErrorResponse( 'rest_cannot_view_active_theme', $response, 403 ); 1261 } 1262 1263 /** 1264 * @ticket 50152 1265 */ 1266 public function test_get_item_invalid() { 1267 wp_set_current_user( self::$admin_id ); 1268 $request = new WP_REST_Request( 'GET', self::$themes_route . '/invalid' ); 1269 $response = rest_get_server()->dispatch( $request ); 1270 $this->assertErrorResponse( 'rest_theme_not_found', $response, 404 ); 1271 } 1272 1273 /** 1274 * @ticket 50152 1275 */ 1276 public function test_get_active_item_as_contributor() { 1277 $route = sprintf( '%s/%s', self::$themes_route, get_stylesheet() ); 1278 $request = new WP_REST_Request( 'GET', $route ); 1279 $response = rest_get_server()->dispatch( $request ); 1280 1281 $this->assertEquals( 200, $response->get_status() ); 1282 } 1089 1283 1090 1284 /** -
trunk/tests/qunit/fixtures/wp-api-generated.js
r49476 r49925 5785 5785 ], 5786 5786 "args": { 5787 "context": {5788 "description": "Scope under which the request is made; determines fields present in response.",5789 "type": "string",5790 "required": false5791 },5792 "page": {5793 "description": "Current page of the collection.",5794 "type": "integer",5795 "default": 1,5796 "minimum": 1,5797 "required": false5798 },5799 "per_page": {5800 "description": "Maximum number of items to be returned in result set.",5801 "type": "integer",5802 "default": 10,5803 "minimum": 1,5804 "maximum": 100,5805 "required": false5806 },5807 "search": {5808 "description": "Limit results to those matching a string.",5809 "type": "string",5810 "required": false5811 },5812 5787 "status": { 5813 5788 "description": "Limit result set to themes assigned one or more statuses.", … … 5815 5790 "items": { 5816 5791 "enum": [ 5817 "active" 5792 "active", 5793 "inactive" 5818 5794 ], 5819 5795 "type": "string" 5820 5796 }, 5821 "required": true5797 "required": false 5822 5798 } 5823 5799 } … … 5827 5803 "self": "http://example.org/index.php?rest_route=/wp/v2/themes" 5828 5804 } 5805 }, 5806 "/wp/v2/themes/(?P<stylesheet>[\\w-]+)": { 5807 "namespace": "wp/v2", 5808 "methods": [ 5809 "GET" 5810 ], 5811 "endpoints": [ 5812 { 5813 "methods": [ 5814 "GET" 5815 ], 5816 "args": { 5817 "stylesheet": { 5818 "description": "The theme's stylesheet. This uniquely identifies the theme.", 5819 "type": "string", 5820 "required": false 5821 } 5822 } 5823 } 5824 ] 5829 5825 }, 5830 5826 "/wp/v2/plugins": {
Note: See TracChangeset
for help on using the changeset viewer.