Skip to content
Closed
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e002e33
First commit
spacedmonkey May 12, 2020
3346643
Merge branch 'master' into fix/themes-api
spacedmonkey Jun 9, 2020
d363522
Add statuses.
spacedmonkey Jun 9, 2020
04d2fc3
Add tests
spacedmonkey Jun 17, 2020
a1fc463
Fix tests.
spacedmonkey Jun 17, 2020
c8af5d2
Fix tests.
spacedmonkey Jun 17, 2020
1597413
Merge branch 'master' into fix/themes-api
spacedmonkey Jun 17, 2020
9bc53c9
Remove allowed.
spacedmonkey Jun 18, 2020
5cf33f7
Make the logic much simplier.
spacedmonkey Jun 18, 2020
accee13
Improve tests.
spacedmonkey Jun 22, 2020
20e9a1e
Fix linting.
spacedmonkey Jun 22, 2020
93fa7ce
Merge branch 'master' into fix/themes-api
spacedmonkey Jun 26, 2020
a02f03f
Add single theme api.
spacedmonkey Jun 26, 2020
69e872f
Fix lints.
spacedmonkey Jun 26, 2020
caec2fa
Add single tests.
spacedmonkey Jun 26, 2020
f302af4
Fix lint
spacedmonkey Jun 26, 2020
e334704
Fix unit tests.
spacedmonkey Jun 28, 2020
14ab4a3
Merge branch 'master' into fix/themes-api
spacedmonkey Jul 1, 2020
2d1cc0e
Allow reading current theme.
spacedmonkey Jul 2, 2020
0847a4c
Tweak permission checks.
spacedmonkey Jul 2, 2020
91076c7
Fix unit tests.
spacedmonkey Jul 2, 2020
640bf1f
Change method name.
spacedmonkey Jul 3, 2020
4e838b4
Merge remote-tracking branch 'remotes/origin/master' into fix/themes-api
TimothyBJacobs Jul 5, 2020
b9293ca
Merged master and resolved conflicts
Oct 19, 2020
e19cf17
Address PR feedback
Oct 19, 2020
3e5b894
Fix test
Oct 19, 2020
e1ff816
Restore theme status check for theme_supports field data
Oct 20, 2020
e06a1b7
Fix CI feedback - short array syntax is not allowed
Oct 20, 2020
c308345
Merge branch 'master' into fix/themes-api
Oct 20, 2020
f9cf70c
Merge pull request #1 from spacedmonkey/fix/themes-api-enhancements
spacedmonkey Oct 21, 2020
c2855ea
Address PR feedback and add active theme link to WP_REST_Server::get_…
Oct 22, 2020
c617a05
Address PR feedback, fix version updates, fix active theme link names…
Oct 22, 2020
7c0c870
Fixed docblocks versions
Oct 22, 2020
b025e17
Fixed docblocks versions
Oct 22, 2020
e418d97
Remove the logic to fill theme_supports for inactive themes
Oct 23, 2020
142c78e
Fix tests
Oct 23, 2020
138f9eb
Merged master and resolved conflicts
Dec 4, 2020
bb14aba
Merge branch 'fix/themes-api' of github.com:spacedmonkey/wordpress-de…
TimothyBJacobs Jan 2, 2021
458c271
Merge branch 'master' into fix/themes-api
TimothyBJacobs Jan 2, 2021
278a9b9
Miscellaneous fixes and adjustments.
TimothyBJacobs Jan 3, 2021
d672a7b
Also allow users with just switch_themes or manage_network_themes to …
TimothyBJacobs Jan 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/wp-includes/rest-api/class-wp-rest-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,8 @@ public function get_index( $request ) {

$response->add_link( 'help', 'http://v2.wp-api.org/' );

$response = $this->maybe_add_current_theme_link( $response );

/**
* Filters the API root index data.
*
Expand Down Expand Up @@ -1507,4 +1509,24 @@ public function get_headers( $server ) {

return $headers;
}

/**
* Add current theme link to API response for users who have proper permissions.
*
* @since 5.7.0
*
* @param WP_REST_Response $response REST API response.
*
* @return WP_REST_Response REST API response.
*/
protected function maybe_add_current_theme_link( \WP_REST_Response $response ) {
if ( ! current_user_can( 'switch_themes' ) && ! current_user_can( 'manage_network_themes' ) ) {
return $response;
}

$theme = wp_get_theme();
$response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) );

return $response;
}
}
218 changes: 183 additions & 35 deletions src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,28 @@ public function register_routes() {
'schema' => array( $this, 'get_item_schema' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<stylesheet>[\w-]+)',
array(
'args' => array(
'stylesheet' => array(
'description' => __( "The theme's stylesheet. This uniquely identifies the theme." ),
'type' => 'string',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

/**
Expand All @@ -58,6 +80,56 @@ public function register_routes() {
* @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
*/
public function get_items_permissions_check( $request ) {
if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
return true;
}

$registered = $this->get_collection_params();
if ( isset( $registered['status'], $request['status'] ) && is_array( $request['status'] ) && array( 'active' ) === $request['status'] ) {
return $this->check_read_active_theme_permission();
}

return new WP_Error(
'rest_user_cannot_view',
__( 'Sorry, you are not allowed to view themes.' ),
array( 'status' => rest_authorization_required_code() )
);
}

/**
* Checks if a given request has access to read the theme.
*
* @since 5.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error True if the request has read access for the item, otherwise WP_Error object.
*/
public function get_item_permissions_check( $request ) {
if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
return true;
}

$wp_theme = wp_get_theme( $request['name'] );
$current_theme = wp_get_theme();
if ( $this->is_same_theme( $wp_theme, $current_theme ) && true === $this->check_read_active_theme_permission() ) {
return true;
}

return new WP_Error(
'rest_cannot_view_active_theme',
__( 'Sorry, you are not allowed to view themes.' ),
array( 'status' => rest_authorization_required_code() )
);
}

/**
* Checks if a theme can be read.
*
* @since 5.7.0
*
* @return bool|WP_Error Whether the theme can be read.
*/
protected function check_read_active_theme_permission() {
if ( current_user_can( 'edit_posts' ) ) {
return true;
}
Expand All @@ -69,12 +141,34 @@ public function get_items_permissions_check( $request ) {
}

return new WP_Error(
'rest_user_cannot_view',
__( 'Sorry, you are not allowed to view themes.' ),
'rest_cannot_view_active_theme',
__( 'Sorry, you are not allowed to view active theme.' ),
array( 'status' => rest_authorization_required_code() )
);
}

/**
* Retrieves a single theme.
*
* @since 5.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_item( $request ) {
$wp_theme = wp_get_theme( $request['stylesheet'] );
if ( ! $wp_theme->exists() ) {
return new WP_Error(
'rest_theme_invalid_slug',
__( 'Invalid theme slug.' ),
array( 'status' => 404 )
);
}
$data = $this->prepare_item_for_response( $wp_theme, $request );

return rest_ensure_response( $data );
}

/**
* Retrieves a collection of themes.
*
Expand All @@ -84,20 +178,25 @@ public function get_items_permissions_check( $request ) {
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {
// Retrieve the list of registered collection query parameters.
$registered = $this->get_collection_params();
$themes = array();
$themes = array();

$active_themes = wp_get_themes();
$current_theme = wp_get_theme();
$status = $request['status'];

if ( isset( $registered['status'], $request['status'] ) && in_array( 'active', $request['status'], true ) ) {
$active_theme = wp_get_theme();
$active_theme = $this->prepare_item_for_response( $active_theme, $request );
$themes[] = $this->prepare_response_for_collection( $active_theme );
foreach ( $active_themes as $theme_name => $theme ) {
$theme_status = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
if ( is_array( $status ) && ! in_array( $theme_status, $status, true ) ) {
continue;
}
$_theme = $this->prepare_item_for_response( $theme, $request );
$themes[] = $this->prepare_response_for_collection( $_theme );
}

$response = rest_ensure_response( $themes );

$response->header( 'X-WP-Total', count( $themes ) );
$response->header( 'X-WP-TotalPages', count( $themes ) );
$response->header( 'X-WP-TotalPages', 1 );

return $response;
}
Expand Down Expand Up @@ -166,38 +265,47 @@ public function prepare_item_for_response( $theme, $request ) {
}
}

$current_theme = wp_get_theme();
if ( rest_is_field_included( 'status', $fields ) ) {
$data['status'] = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
}

if ( rest_is_field_included( 'theme_supports', $fields ) ) {
foreach ( get_registered_theme_features() as $feature => $config ) {
if ( ! is_array( $config['show_in_rest'] ) ) {
continue;
}
if ( $this->is_same_theme( $theme, $current_theme ) ) {
foreach ( get_registered_theme_features() as $feature => $config ) {
if ( ! is_array( $config['show_in_rest'] ) ) {
continue;
}

$name = $config['show_in_rest']['name'];
$name = $config['show_in_rest']['name'];

if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
continue;
}
if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
continue;
}

if ( ! current_theme_supports( $feature ) ) {
$data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
continue;
}
if ( ! current_theme_supports( $feature ) ) {
$data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
continue;
}

$support = get_theme_support( $feature );
$support = get_theme_support( $feature );

if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
$prepare = $config['show_in_rest']['prepare_callback'];
} else {
$prepare = array( $this, 'prepare_theme_support' );
}
if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
$prepare = $config['show_in_rest']['prepare_callback'];
} else {
$prepare = array( $this, 'prepare_theme_support' );
}

$prepared = $prepare( $support, $config, $feature, $request );
$prepared = $prepare( $support, $config, $feature, $request );

if ( is_wp_error( $prepared ) ) {
continue;
}
if ( is_wp_error( $prepared ) ) {
continue;
}

$data['theme_supports'][ $name ] = $prepared;
$data['theme_supports'][ $name ] = $prepared;
}
} else {
$data['theme_supports'] = array();
}
}

Expand All @@ -206,6 +314,8 @@ public function prepare_item_for_response( $theme, $request ) {
// Wrap the data in a response object.
$response = rest_ensure_response( $data );

$response->add_links( $this->prepare_links( $theme ) );

/**
* Filters theme data returned from the REST API.
*
Expand All @@ -219,6 +329,40 @@ public function prepare_item_for_response( $theme, $request ) {
}

/**
* Prepares links for the request.
*
* @since 5.7.0
*
* @param WP_Theme $theme Theme data.
* @return array Links for the given block type.
*/
protected function prepare_links( $theme ) {
return array(
'collection' => array(
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
),
'self' => array(
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_template() ) ),
),
);
}

/**
* Helper function to compare theme to current theme.
*
* @since 5.7.0
*
* @param WP_Theme $theme Theme to compare to active theme.
* @param WP_Theme $current_theme Current active theme.
*
* @return bool
*/
protected function is_same_theme( $theme, $current_theme ) {
return (string) $theme === (string) $current_theme;
}

/**
*
* Prepares the theme support value for inclusion in the REST API response.
*
* @since 5.5.0
Expand Down Expand Up @@ -399,6 +543,11 @@ public function get_item_schema() {
'type' => 'string',
'readonly' => true,
),
'status' => array(
'description' => __( 'A named status for the theme.' ),
'type' => 'string',
'enum' => array( 'inactive', 'active' ),
),
),
);

Expand Down Expand Up @@ -431,10 +580,9 @@ public function get_collection_params() {
'description' => __( 'Limit result set to themes assigned one or more statuses.' ),
'type' => 'array',
'items' => array(
'enum' => array( 'active' ),
'enum' => array( 'active', 'inactive' ),
'type' => 'string',
),
'required' => true,
'sanitize_callback' => array( $this, 'sanitize_theme_status' ),
);

Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/tests/rest-api/rest-schema-setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public function test_expected_routes_in_schema() {
'/wp/v2/block-types/(?P<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)',
'/wp/v2/settings',
'/wp/v2/themes',
'/wp/v2/themes/(?P<stylesheet>[\w-]+)',
'/wp/v2/plugins',
'/wp/v2/plugins/(?P<plugin>[^.\/]+(?:\/[^.\/]+)?)',
'/wp/v2/block-directory/search',
Expand Down
Loading