From a1a489e3f6d441f302c5185a5d06d5f63ca78c20 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 26 Jan 2024 19:45:53 -0300 Subject: [PATCH] Font Library: Improve font collection rest controller (#58222) * make WP_Font_Collection object properties public * remove class method no longer used * simplify the acces to the font collection properties * add schema, args, fields and pagination to font collections rest controller * add one test case for params * request a subset of font collection fields * lint * remove not needed default change * update version since * use placeholder for translation * update comment Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> * update translation domain Co-authored-by: Grant Kinney * update wording * set context to edit only * use 'view', 'edit' and 'embed' on the context of all the schema properties * prepare_item_for_response * default context param * args for item endpoint * format php --------- Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> Co-authored-by: Grant Kinney --- .../font-library/class-wp-font-collection.php | 44 ++-- .../font-library/class-wp-font-library.php | 4 +- ...ss-wp-rest-font-collections-controller.php | 234 +++++++++++++++++- .../font-library-modal/resolvers.js | 2 +- phpunit-watcher.yml.dist | 3 + .../wpFontCollection/getConfig.php | 76 ------ .../wpRestFontCollectionsController.php | 16 +- 7 files changed, 255 insertions(+), 124 deletions(-) delete mode 100644 phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php diff --git a/lib/experimental/fonts/font-library/class-wp-font-collection.php b/lib/experimental/fonts/font-library/class-wp-font-collection.php index 1ff96b1343b45..fedcfc2d02e6c 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-collection.php +++ b/lib/experimental/fonts/font-library/class-wp-font-collection.php @@ -27,7 +27,7 @@ class WP_Font_Collection { * * @var string */ - private $slug; + public $slug; /** * The name of the font collection. @@ -36,7 +36,7 @@ class WP_Font_Collection { * * @var string */ - private $name; + public $name; /** * Description of the font collection. @@ -45,7 +45,7 @@ class WP_Font_Collection { * * @var string */ - private $description; + public $description; /** * Source of the font collection. @@ -54,7 +54,7 @@ class WP_Font_Collection { * * @var string */ - private $src; + public $src; /** * Array of font families in the collection. @@ -63,7 +63,7 @@ class WP_Font_Collection { * * @var array */ - private $font_families; + public $font_families; /** * Categories associated with the font collection. @@ -72,7 +72,7 @@ class WP_Font_Collection { * * @var array */ - private $categories; + public $categories; /** @@ -138,34 +138,22 @@ public static function is_config_valid( $config ) { ( empty( $config['src'] ) && empty( $config['font_families'] ) ) || ( ! empty( $config['src'] ) && ! empty( $config['font_families'] ) ) ) { - _doing_it_wrong( __METHOD__, __( 'Font Collection config "src" option OR "font_families" option are required.', 'gutenberg' ), '6.5.0' ); + _doing_it_wrong( + __METHOD__, + sprintf( + /* translators: %1$s: src, %2$s: font_families */ + __( 'Font Collection config "%1$s" option OR "%2$s" option is required.', 'gutenberg' ), + 'src', + 'font_families' + ), + '6.5.0' + ); return false; } return true; } - /** - * Gets the font collection config. - * - * @since 6.5.0 - * - * @return array { - * An array of font collection config. - * - * @type string $slug The font collection's unique slug. - * @type string $name The font collection's name. - * @type string $description The font collection's description. - * } - */ - public function get_config() { - return array( - 'slug' => $this->slug, - 'name' => $this->name, - 'description' => $this->description, - ); - } - /** * Gets the font collection content. * diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php index 51a84b957ea11..b826ffa62aad2 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-library.php +++ b/lib/experimental/fonts/font-library/class-wp-font-library.php @@ -69,7 +69,7 @@ public static function register_font_collection( $config ) { $new_collection = new WP_Font_Collection( $config ); - if ( self::is_collection_registered( $new_collection->get_config()['slug'] ) ) { + if ( self::is_collection_registered( $new_collection->slug ) ) { $error_message = sprintf( /* translators: %s: Font collection slug. */ __( 'Font collection with slug: "%s" is already registered.', 'gutenberg' ), @@ -82,7 +82,7 @@ public static function register_font_collection( $config ) { ); return new WP_Error( 'font_collection_registration_error', $error_message ); } - self::$collections[ $new_collection->get_config()['slug'] ] = $new_collection; + self::$collections[ $new_collection->slug ] = $new_collection; return $new_collection; } diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php index 51fd14fffaa95..dd8301c71371e 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php @@ -44,7 +44,10 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), ) ); @@ -56,7 +59,11 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), ), + 'schema' => array( $this, 'get_public_item_schema' ), ) ); } @@ -68,13 +75,61 @@ public function register_routes() { * * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ - public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $collections = array(); - foreach ( WP_Font_Library::get_font_collections() as $collection ) { - $collections[] = $collection->get_config(); + public function get_items( $request ) { + $collections_all = WP_Font_Library::get_font_collections(); + + $page = $request['page']; + $per_page = $request['per_page']; + $total_items = count( $collections_all ); + $max_pages = ceil( $total_items / $per_page ); + + if ( $page > $max_pages && $total_items > 0 ) { + return new WP_Error( + 'rest_post_invalid_page_number', + __( 'The page number requested is larger than the number of pages available.', 'default' ), + array( 'status' => 400 ) + ); + } + + $collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page ); + + $items = array(); + foreach ( $collections_page as $collection ) { + $item = $this->prepare_item_for_response( $collection, $request ); + if ( is_wp_error( $item ) ) { + return $item; + } + $item = $this->prepare_response_for_collection( $item ); + $items[] = $item; + } + + $response = rest_ensure_response( $items ); + + $response->header( 'X-WP-Total', (int) $total_items ); + $response->header( 'X-WP-TotalPages', (int) $max_pages ); + + $request_params = $request->get_query_params(); + $collection_url = rest_url( $this->namespace . '/' . $this->rest_base ); + $base = add_query_arg( urlencode_deep( $request_params ), $collection_url ); + + if ( $page > 1 ) { + $prev_page = $page - 1; + + if ( $prev_page > $max_pages ) { + $prev_page = $max_pages; + } + + $prev_link = add_query_arg( 'page', $prev_page, $base ); + $response->link_header( 'prev', $prev_link ); + } + if ( $max_pages > $page ) { + $next_page = $page + 1; + $next_link = add_query_arg( 'page', $next_page, $base ); + + $response->link_header( 'next', $next_link ); } - return rest_ensure_response( $collections, 200 ); + return $response; } /** @@ -95,17 +150,170 @@ public function get_item( $request ) { return $collection; } - $config = $collection->get_config(); - $contents = $collection->get_content(); + $item = $this->prepare_item_for_response( $collection, $request ); - // If there was an error getting the collection data, return the error. - if ( is_wp_error( $contents ) ) { - $contents->add_data( array( 'status' => 500 ) ); - return $contents; + if ( is_wp_error( $item ) ) { + return $item; } - $collection_data = array_merge( $config, $contents ); - return rest_ensure_response( $collection_data ); + return $item; + } + + /* + * Prepare a single collection output for response. + * + * @since 6.5.0 + * + * @param WP_Font_Collection $collection Collection object. + * @param WP_REST_Request $request Request object. + * @return array|WP_Error + */ + public function prepare_item_for_response( $collection, $request ) { + $fields = $this->get_fields_for_response( $request ); + $item = array(); + + $config_fields = array( 'slug', 'name', 'description' ); + foreach ( $config_fields as $field ) { + if ( in_array( $field, $fields, true ) ) { + $item[ $field ] = $collection->$field; + } + } + + $data_fields = array( 'font_families', 'categories' ); + if ( in_array( 'font_families', $fields, true ) || in_array( 'categories', $fields, true ) ) { + $content = $collection->get_content(); + + // If there was an error getting the collection data, return the error. + if ( is_wp_error( $content ) ) { + $content->add_data( array( 'status' => 500 ) ); + return $content; + } + + foreach ( $data_fields as $field ) { + if ( in_array( $field, $fields, true ) ) { + $item[ $field ] = $content[ $field ]; + } + } + } + + $response = rest_ensure_response( $item ); + + if ( rest_is_field_included( '_links', $fields ) ) { + $links = $this->prepare_links( $collection ); + $response->add_links( $links ); + } + + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $response->data = $this->add_additional_fields_to_object( $response->data, $request ); + $response->data = $this->filter_response_by_context( $response->data, $context ); + + /** + * Filters a font collection returned from the REST API. + * + * Allows modification of the font collection right before it is returned. + * + * @since 6.5.0 + * + * @param WP_REST_Response $response The response object. + * @param WP_Font_Collection $collection The Font Collection object. + * @param WP_REST_Request $request Request used to generate the response. + */ + return apply_filters( 'rest_prepare_font_collection', $response, $collection, $request ); + } + + /** + * Retrieves the font collection's schema, conforming to JSON Schema. + * + * @since 6.5.0 + * + * @return array Item schema data. + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'font-collection', + 'type' => 'object', + 'properties' => array( + 'slug' => array( + 'description' => __( 'Unique identifier for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), + 'name' => array( + 'description' => __( 'The name for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'description' => array( + 'description' => __( 'The description for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'font_families' => array( + 'description' => __( 'The font families for the font collection.', 'gutenberg' ), + 'type' => 'array', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'categories' => array( + 'description' => __( 'The categories for the font collection.', 'gutenberg' ), + 'type' => 'array', + 'context' => array( 'view', 'edit', 'embed' ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } + + /** + * Prepares links for the request. + * + * @since 6.5.0 + * + * @param WP_Font_Collection $collection Font collection data + * @return array Links for the given font collection. + */ + protected function prepare_links( $collection ) { + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $collection->slug ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), + ), + ); + return $links; + } + + /** + * Retrieves the search params for the font collections. + * + * @since 6.5.0 + * + * @return array Collection parameters. + */ + public function get_collection_params() { + $query_params = parent::get_collection_params(); + + $query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); + + unset( $query_params['search'] ); + + /** + * Filters REST API collection parameters for the font collections controller. + * + * @since 6.5.0 + * + * @param array $query_params JSON Schema-formatted collection parameters. + */ + return apply_filters( 'rest_font_collections_collection_params', $query_params ); } /** diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js index a75fc6cbe78ff..a114630cfc08d 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js @@ -63,7 +63,7 @@ export async function fetchUninstallFontFamily( fontFamilyId ) { export async function fetchFontCollections() { const config = { - path: FONT_COLLECTIONS_URL, + path: `${ FONT_COLLECTIONS_URL }?_fields=slug,name,description`, method: 'GET', }; return await apiFetch( config ); diff --git a/phpunit-watcher.yml.dist b/phpunit-watcher.yml.dist index f2f9da5fbcdbf..165a4d8e66e0b 100644 --- a/phpunit-watcher.yml.dist +++ b/phpunit-watcher.yml.dist @@ -6,3 +6,6 @@ watch: notifications: passingTests: false failingTests: false + +phpunit: + arguments: '--filter Tests_Fonts_WpFontCollection_GetContent' diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php b/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php deleted file mode 100644 index 393de7d22614d..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php +++ /dev/null @@ -1,76 +0,0 @@ -assertSame( $expected_data, $collection->get_config() ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_get_config() { - $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, '{"this is mock data":true}' ); - - return array( - 'with a file' => array( - 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'src' => $mock_file, - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - ), - ), - 'with a url' => array( - 'config' => array( - 'slug' => 'my-collection-with-url', - 'name' => 'My Collection with URL', - 'description' => 'My collection description', - 'src' => 'https://localhost/fonts/mock-font-collection.json', - ), - 'expected_data' => array( - 'slug' => 'my-collection-with-url', - 'name' => 'My Collection with URL', - 'description' => 'My collection description', - ), - ), - 'with font_families' => array( - 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( array() ), - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php index 164f88f3f7b4b..f2140267672d2 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php @@ -154,10 +154,18 @@ public function test_prepare_item() { // Controller does not use test_prepare_item(). } - /** - * @doesNotPerformAssertions - */ public function test_get_item_schema() { - // Controller does not use test_get_item_schema(). + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-collections' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $properties = $data['schema']['properties']; + $this->assertCount( 5, $properties ); + $this->assertArrayHasKey( 'slug', $properties ); + $this->assertArrayHasKey( 'name', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + $this->assertArrayHasKey( 'font_families', $properties ); + $this->assertArrayHasKey( 'categories', $properties ); } }