wp/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
changeset 19 3d72ae0968f4
parent 18 be944660c56a
child 21 48c4eec2b7e6
--- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php	Wed Sep 21 18:19:35 2022 +0200
+++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php	Tue Sep 27 16:37:53 2022 +0200
@@ -17,6 +17,12 @@
 class WP_REST_Themes_Controller extends WP_REST_Controller {
 
 	/**
+	 * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
+	 * Excludes invalid directory name characters: `/:<>*?"|`.
+	 */
+	const PATTERN = '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?';
+
+	/**
 	 * Constructor.
 	 *
 	 * @since 5.0.0
@@ -50,12 +56,13 @@
 
 		register_rest_route(
 			$this->namespace,
-			'/' . $this->rest_base . '/(?P<stylesheet>[\w-]+)',
+			sprintf( '/%s/(?P<stylesheet>%s)', $this->rest_base, self::PATTERN ),
 			array(
 				'args'   => array(
 					'stylesheet' => array(
-						'description' => __( "The theme's stylesheet. This uniquely identifies the theme." ),
-						'type'        => 'string',
+						'description'       => __( "The theme's stylesheet. This uniquely identifies the theme." ),
+						'type'              => 'string',
+						'sanitize_callback' => array( $this, '_sanitize_stylesheet_callback' ),
 					),
 				),
 				array(
@@ -69,6 +76,18 @@
 	}
 
 	/**
+	 * Sanitize the stylesheet to decode endpoint.
+	 *
+	 * @since 5.9.0
+	 *
+	 * @param string $stylesheet The stylesheet name.
+	 * @return string Sanitized stylesheet.
+	 */
+	public function _sanitize_stylesheet_callback( $stylesheet ) {
+		return urldecode( $stylesheet );
+	}
+
+	/**
 	 * Checks if a given request has access to read the theme.
 	 *
 	 * @since 5.0.0
@@ -204,12 +223,15 @@
 	 * Prepares a single theme output for response.
 	 *
 	 * @since 5.0.0
+	 * @since 5.9.0 Renamed `$theme` to `$item` to match parent class for PHP 8 named parameter support.
 	 *
-	 * @param WP_Theme        $theme   Theme object.
+	 * @param WP_Theme        $item    Theme object.
 	 * @param WP_REST_Request $request Request object.
 	 * @return WP_REST_Response Response object.
 	 */
-	public function prepare_item_for_response( $theme, $request ) {
+	public function prepare_item_for_response( $item, $request ) {
+		// Restores the more descriptive, specific name for use within this method.
+		$theme  = $item;
 		$data   = array();
 		$fields = $this->get_fields_for_response( $request );
 
@@ -311,6 +333,21 @@
 
 		$response->add_links( $this->prepare_links( $theme ) );
 
+		if ( $theme->get_stylesheet() === wp_get_theme()->get_stylesheet() ) {
+			// This creates a record for the active theme if not existent.
+			$id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
+		} else {
+			$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+			$id       = isset( $user_cpt['ID'] ) ? $user_cpt['ID'] : null;
+		}
+
+		if ( $id ) {
+			$response->add_link(
+				'https://api.w.org/user-global-styles',
+				rest_url( 'wp/v2/global-styles/' . $id )
+			);
+		}
+
 		/**
 		 * Filters theme data returned from the REST API.
 		 *