Make WordPress Core

Changeset 57686


Ignore:
Timestamp:
02/21/2024 07:24:14 PM (8 months ago)
Author:
swissspidy
Message:

Editor: Ensure font collection metadata can be properly localized.

Updates wp_register_font_collection() and WP_Font_Collection so that only font families can be loaded from a file or URL.
All metadata, such as name, description, and list of font categories, needs to be passed directly in PHP so that it can be properly localized.

Props swissspidy, mmaattiiaass, grantmkin, youknowriad.
Fixes #60509.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/fonts.php

    r57558 r57686  
    5454
    5555/**
    56  * Registers a new Font Collection in the Font Library.
    57  *
    58  * @since 6.5.0
    59  *
    60  * @param string       $slug Font collection slug. May only contain alphanumeric characters, dashes,
     56 * Registers a new font collection in the font library.
     57 *
     58 * See {@link https://schemas.wp.org/trunk/font-collection.json} for the schema
     59 * the font collection data must adhere to.
     60 *
     61 * @since 6.5.0
     62 *
     63 * @param string $slug Font collection slug. May only contain alphanumeric characters, dashes,
    6164 *                     and underscores. See sanitize_title().
    62  * @param array|string $data_or_file {
    63  *     Font collection data array or a path/URL to a JSON file containing the font collection.
    64  *
    65  *     @link https://schemas.wp.org/trunk/font-collection.json
    66  *
    67  *     @type string $name           Required. Name of the font collection shown in the Font Library.
    68  *     @type string $description    Optional. A short descriptive summary of the font collection. Default empty.
    69  *     @type array  $font_families  Required. Array of font family definitions that are in the collection.
    70  *     @type array  $categories     Optional. Array of categories, each with a name and slug, that are used by the
    71  *                                  fonts in the collection. Default empty.
     65 * @param array  $args {
     66 *     Font collection data.
     67 *
     68 *     @type string       $name          Required. Name of the font collection shown in the Font Library.
     69 *     @type string       $description   Optional. A short descriptive summary of the font collection. Default empty.
     70 *     @type array|string $font_families Required. Array of font family definitions that are in the collection,
     71 *                                       or a string containing the path or URL to a JSON file containing the font collection.
     72 *     @type array        $categories    Optional. Array of categories, each with a name and slug, that are used by the
     73 *                                       fonts in the collection. Default empty.
    7274 * }
    7375 * @return WP_Font_Collection|WP_Error A font collection if it was registered
    7476 *                                     successfully, or WP_Error object on failure.
    7577 */
    76 function wp_register_font_collection( $slug, $data_or_file ) {
    77     return WP_Font_Library::get_instance()->register_font_collection( $slug, $data_or_file );
     78function wp_register_font_collection( string $slug, array $args ) {
     79    return WP_Font_Library::get_instance()->register_font_collection( $slug, $args );
    7880}
    7981
     
    8688 * @return bool True if the font collection was unregistered successfully, else false.
    8789 */
    88 function wp_unregister_font_collection( $slug ) {
     90function wp_unregister_font_collection( string $slug ) {
    8991    return WP_Font_Library::get_instance()->unregister_font_collection( $slug );
    9092}
     
    197199 */
    198200function _wp_register_default_font_collections() {
    199     wp_register_font_collection( 'google-fonts', 'https://s.w.org/images/fonts/17.7/collections/google-fonts-with-preview.json' );
    200 }
     201    wp_register_font_collection(
     202        'google-fonts',
     203        array(
     204            'name'          => _x( 'Google Fonts', 'font collection name' ),
     205            'description'   => __( 'Install from Google Fonts. Fonts are copied to and served from your site.' ),
     206            'font_families' => 'https://s.w.org/images/fonts/17.7/collections/google-fonts-with-preview.json',
     207            'categories'    => array(
     208                array(
     209                    'name' => _x( 'Sans Serif', 'font category' ),
     210                    'slug' => 'sans-serif',
     211                ),
     212                array(
     213                    'name' => _x( 'Display', 'font category' ),
     214                    'slug' => 'display',
     215                ),
     216                array(
     217                    'name' => _x( 'Serif', 'font category' ),
     218                    'slug' => 'serif',
     219                ),
     220                array(
     221                    'name' => _x( 'Handwriting', 'font category' ),
     222                    'slug' => 'handwriting',
     223                ),
     224                array(
     225                    'name' => _x( 'Monospace', 'font category' ),
     226                    'slug' => 'monospace',
     227                ),
     228            ),
     229        )
     230    );
     231}
  • trunk/src/wp-includes/fonts/class-wp-font-collection.php

    r57657 r57686  
    4747     * @since 6.5.0
    4848     *
    49      * @param string       $slug         Font collection slug.
    50      * @param array|string $data_or_file Font collection data array or a path/URL to a JSON file
    51      *                                   containing the font collection.
    52      *                                   See {@see wp_register_font_collection()} for the supported fields.
    53      */
    54     public function __construct( $slug, $data_or_file ) {
     49     * @param string $slug Font collection slug. May only contain alphanumeric characters, dashes,
     50     *                     and underscores. See sanitize_title().
     51     * @param array  $args Font collection data. See wp_register_font_collection() for information on accepted arguments.
     52     */
     53    public function __construct( string $slug, array $args ) {
    5554        $this->slug = sanitize_title( $slug );
    5655        if ( $this->slug !== $slug ) {
     
    6362        }
    6463
    65         if ( is_array( $data_or_file ) ) {
    66             $this->data = $this->sanitize_and_validate_data( $data_or_file );
    67         } else {
     64        $required_properties = array( 'name', 'font_families' );
     65
     66        if ( isset( $args['font_families'] ) && is_string( $args['font_families'] ) ) {
    6867            // JSON data is lazy loaded by ::get_data().
    69             $this->src = $data_or_file;
    70         }
     68            $this->src = $args['font_families'];
     69            unset( $args['font_families'] );
     70
     71            $required_properties = array( 'name' );
     72        }
     73
     74        $this->data = $this->sanitize_and_validate_data( $args, $required_properties );
    7175    }
    7276
     
    7983     */
    8084    public function get_data() {
     85        if ( is_wp_error( $this->data ) ) {
     86            return $this->data;
     87        }
     88
    8189        // If the collection uses JSON data, load it and cache the data/error.
    82         if ( $this->src && empty( $this->data ) ) {
     90        if ( isset( $this->src ) ) {
    8391            $this->data = $this->load_from_json( $this->src );
    8492        }
     
    117125        }
    118126
    119         return $url ? $this->load_from_url( $url ) : $this->load_from_file( $file );
     127        $data = $url ? $this->load_from_url( $url ) : $this->load_from_file( $file );
     128
     129        if ( is_wp_error( $data ) ) {
     130            return $data;
     131        }
     132
     133        $data = array(
     134            'name'          => $this->data['name'],
     135            'font_families' => $data['font_families'],
     136        );
     137
     138        if ( isset( $this->data['description'] ) ) {
     139            $data['description'] = $this->data['description'];
     140        }
     141
     142        if ( isset( $this->data['categories'] ) ) {
     143            $data['categories'] = $this->data['categories'];
     144        }
     145
     146        return $data;
    120147    }
    121148
     
    135162        }
    136163
    137         return $this->sanitize_and_validate_data( $data );
     164        return $this->sanitize_and_validate_data( $data, array( 'font_families' ) );
    138165    }
    139166
     
    155182            $response = wp_safe_remote_get( $url );
    156183            if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
    157                 // translators: %s: Font collection URL.
    158                 return new WP_Error( 'font_collection_request_error', sprintf( __( 'Error fetching the font collection data from "%s".' ), $url ) );
     184                return new WP_Error(
     185                    'font_collection_request_error',
     186                    sprintf(
     187                        // translators: %s: Font collection URL.
     188                        __( 'Error fetching the font collection data from "%s".' ),
     189                        $url
     190                    )
     191                );
    159192            }
    160193
     
    165198
    166199            // Make sure the data is valid before storing it in a transient.
    167             $data = $this->sanitize_and_validate_data( $data );
     200            $data = $this->sanitize_and_validate_data( $data, array( 'font_families' ) );
    168201            if ( is_wp_error( $data ) ) {
    169202                return $data;
     
    181214     * @since 6.5.0
    182215     *
    183      * @param array $data Font collection data to sanitize and validate.
     216     * @param array $data                Font collection data to sanitize and validate.
     217     * @param array $required_properties Required properties that must exist in the passed data.
    184218     * @return array|WP_Error Sanitized data if valid, otherwise a WP_Error instance.
    185219     */
    186     private function sanitize_and_validate_data( $data ) {
     220    private function sanitize_and_validate_data( $data, $required_properties = array() ) {
    187221        $schema = self::get_sanitization_schema();
    188222        $data   = WP_Font_Utils::sanitize_from_schema( $data, $schema );
    189223
    190         $required_properties = array( 'name', 'font_families' );
    191224        foreach ( $required_properties as $property ) {
    192225            if ( empty( $data[ $property ] ) ) {
  • trunk/src/wp-includes/fonts/class-wp-font-library.php

    r57548 r57686  
    3838     * @since 6.5.0
    3939     *
    40      * @param string $slug         Font collection slug.
    41      * @param array  $data_or_file Font collection data array or a path/URL to a JSON file
    42      *                             containing the font collection.
    43      *                             See {@see wp_register_font_collection()} for the supported fields.
     40     * @param string $slug Font collection slug. May only contain alphanumeric characters, dashes,
     41     *                     and underscores. See sanitize_title().
     42     * @param array  $args Font collection data. See wp_register_font_collection() for information on accepted arguments.
    4443     * @return WP_Font_Collection|WP_Error A font collection if it was registered successfully,
    4544     *                                     or WP_Error object on failure.
    4645     */
    47     public function register_font_collection( $slug, $data_or_file ) {
    48         $new_collection = new WP_Font_Collection( $slug, $data_or_file );
     46    public function register_font_collection( string $slug, array $args ) {
     47        $new_collection = new WP_Font_Collection( $slug, $args );
    4948
    5049        if ( $this->is_collection_registered( $new_collection->slug ) ) {
     
    7372     * @return bool True if the font collection was unregistered successfully and false otherwise.
    7473     */
    75     public function unregister_font_collection( $slug ) {
     74    public function unregister_font_collection( string $slug ) {
    7675        if ( ! $this->is_collection_registered( $slug ) ) {
    7776            _doing_it_wrong(
     
    9594     * @return bool True if the font collection is registered and false otherwise.
    9695     */
    97     private function is_collection_registered( $slug ) {
     96    private function is_collection_registered( string $slug ) {
    9897        return array_key_exists( $slug, $this->collections );
    9998    }
     
    118117     * @return WP_Font_Collection|null Font collection object, or null if the font collection doesn't exist.
    119118     */
    120     public function get_font_collection( $slug ) {
     119    public function get_font_collection( string $slug ) {
    121120        if ( $this->is_collection_registered( $slug ) ) {
    122121            return $this->collections[ $slug ];
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php

    r57648 r57686  
    105105
    106106        $response->header( 'X-WP-Total', (int) $total_items );
    107         $response->header( 'X-WP-TotalPages', (int) $max_pages );
     107        $response->header( 'X-WP-TotalPages', $max_pages );
    108108
    109109        $request_params = $request->get_query_params();
  • trunk/tests/phpunit/tests/fonts/font-library/wpFontCollection/getData.php

    r57657 r57686  
    4141        file_put_contents( $mock_file, wp_json_encode( $config ) );
    4242
    43         $collection = new WP_Font_Collection( $slug, $mock_file );
     43        $collection = new WP_Font_Collection(
     44            $slug,
     45            array_merge(
     46                $config,
     47                array( 'font_families' => $mock_file )
     48            )
     49        );
    4450        $data       = $collection->get_data();
    4551
    4652        $this->assertSame( $slug, $collection->slug, 'The slug should match.' );
    47         $this->assertSame( $expected_data, $data, 'The collection data should match.' );
     53        $this->assertEqualSetsWithIndex( $expected_data, $data, 'The collection data should match.' );
    4854    }
    4955
     
    5965
    6066        self::$mock_collection_data = $config;
    61         $collection                 = new WP_Font_Collection( $slug, 'https://example.com/fonts/mock-font-collection.json' );
     67        $collection                 = new WP_Font_Collection(
     68            $slug,
     69            array_merge(
     70                $config,
     71                array(
     72                    'font_families' => 'https://example.com/fonts/mock-font-collection.json',
     73                )
     74            )
     75        );
    6276        $data                       = $collection->get_data();
    6377
     
    6579
    6680        $this->assertSame( $slug, $collection->slug, 'The slug should match.' );
    67         $this->assertSame( $expected_data, $data, 'The collection data should match.' );
     81        $this->assertEqualSetsWithIndex( $expected_data, $data, 'The collection data should match.' );
    6882    }
    6983
     
    7589    public function data_create_font_collection() {
    7690        return array(
    77 
    7891            'font collection with required data' => array(
    7992                'slug'          => 'my-collection',
     
    186199                ),
    187200            ),
    188 
    189201        );
    190202    }
     
    203215        $this->assertWPError( $data, 'Error is not returned when property is missing or invalid.' );
    204216        $this->assertSame(
    205             $data->get_error_code(),
    206217            'font_collection_missing_property',
     218            $data->get_error_code(),
    207219            'Incorrect error code when property is missing or invalid.'
    208220        );
     
    244256        $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' );
    245257
    246         $collection = new WP_Font_Collection( 'my-collection', 'non-existing.json' );
     258        $collection = new WP_Font_Collection(
     259            'my-collection',
     260            array(
     261                'name'          => 'My collection',
     262                'font_families' => 'non-existing.json',
     263            )
     264        );
    247265        $data       = $collection->get_data();
    248266
    249267        $this->assertWPError( $data, 'Error is not returned when invalid file path is provided.' );
    250268        $this->assertSame(
    251             $data->get_error_code(),
    252269            'font_collection_json_missing',
     270            $data->get_error_code(),
    253271            'Incorrect error code when invalid file path is provided.'
    254272        );
     
    259277        file_put_contents( $mock_file, 'invalid-json' );
    260278
    261         $collection = new WP_Font_Collection( 'my-collection', $mock_file );
     279        $collection = new WP_Font_Collection(
     280            'my-collection',
     281            array(
     282                'name'          => 'Invalid collection',
     283                'font_families' => $mock_file,
     284            )
     285        );
    262286
    263287        // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Testing error response returned by `load_from_json`, not the underlying error from `wp_json_file_decode`.
     
    266290        $this->assertWPError( $data, 'Error is not returned with invalid json file contents.' );
    267291        $this->assertSame(
    268             $data->get_error_code(),
    269292            'font_collection_decode_error',
     293            $data->get_error_code(),
    270294            'Incorrect error code with invalid json file contents.'
    271295        );
     
    275299        $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' );
    276300
    277         $collection = new WP_Font_Collection( 'my-collection', 'not-a-url' );
     301        $collection = new WP_Font_Collection(
     302            'my-collection',
     303            array(
     304                'name'          => 'Invalid collection',
     305                'font_families' => 'not-a-url',
     306            )
     307        );
    278308        $data       = $collection->get_data();
    279309
    280310        $this->assertWPError( $data, 'Error is not returned when invalid url is provided.' );
    281311        $this->assertSame(
    282             $data->get_error_code(),
    283312            'font_collection_json_missing',
     313            $data->get_error_code(),
    284314            'Incorrect error code when invalid url is provided.'
    285315        );
     
    289319        add_filter( 'pre_http_request', array( $this, 'mock_request_unsuccessful_response' ), 10, 3 );
    290320
    291         $collection = new WP_Font_Collection( 'my-collection', 'https://example.com/fonts/missing-collection.json' );
     321        $collection = new WP_Font_Collection(
     322            'my-collection',
     323            array(
     324                'name'          => 'Missing collection',
     325                'font_families' => 'https://example.com/fonts/missing-collection.json',
     326            )
     327        );
    292328        $data       = $collection->get_data();
    293329
     
    296332        $this->assertWPError( $data, 'Error is not returned when response is unsuccessful.' );
    297333        $this->assertSame(
    298             $data->get_error_code(),
    299334            'font_collection_request_error',
    300             'Incorrect error code when response is unsuccussful.'
     335            $data->get_error_code(),
     336            'Incorrect error code when response is unsuccessful.'
    301337        );
    302338    }
     
    305341        add_filter( 'pre_http_request', array( $this, 'mock_request_invalid_json' ), 10, 3 );
    306342
    307         $collection = new WP_Font_Collection( 'my-collection', 'https://example.com/fonts/invalid-collection.json' );
     343        $collection = new WP_Font_Collection(
     344            'my-collection',
     345            array(
     346                'name'          => 'Invalid collection',
     347                'font_families' => 'https://example.com/fonts/invalid-collection.json',
     348            )
     349        );
    308350        $data       = $collection->get_data();
    309351
     
    312354        $this->assertWPError( $data, 'Error is not returned when response is invalid json.' );
    313355        $this->assertSame(
    314             $data->get_error_code(),
    315356            'font_collection_decode_error',
     357            $data->get_error_code(),
    316358            'Incorrect error code when response is invalid json.'
    317359        );
  • trunk/tests/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php

    r57548 r57686  
    3939        file_put_contents( $mock_file, '{"name": "Mock Collection", "font_families": [ "mock" ], "categories": [ "mock" ] }' );
    4040
    41         wp_register_font_collection( 'mock-col-slug', $mock_file );
     41        wp_register_font_collection(
     42            'mock-col-slug',
     43            array(
     44                'name'          => 'My collection',
     45                'font_families' => $mock_file,
     46            )
     47        );
    4248    }
    4349
     
    7985
    8086        wp_set_current_user( self::$admin_id );
    81         wp_register_font_collection( 'invalid-collection', 'invalid-collection-file' );
     87        wp_register_font_collection(
     88            'invalid-collection',
     89            array(
     90                'name'          => 'My collection',
     91                'font_families' => 'invalid-collection-file',
     92            )
     93        );
    8294
    8395        $request  = new WP_REST_Request( 'GET', '/wp/v2/font-collections' );
     
    133145        wp_set_current_user( self::$admin_id );
    134146        $slug = 'invalid-collection';
    135         wp_register_font_collection( $slug, 'invalid-collection-file' );
     147        wp_register_font_collection(
     148            $slug,
     149            array(
     150                'name'          => 'My collection',
     151                'font_families' => 'invalid-collection-file',
     152            )
     153        );
    136154
    137155        $request  = new WP_REST_Request( 'GET', '/wp/v2/font-collections/' . $slug );
     
    140158        wp_unregister_font_collection( $slug );
    141159
    142         $this->assertErrorResponse( 'font_collection_json_missing', $response, 500, 'When the collection json file is invalid, the response should return an error for "font_collection_json_missing" with 500 status.' );
     160        $this->assertErrorResponse( 'font_collection_json_missing', $response, 500 );
    143161    }
    144162
     
    151169        wp_set_current_user( 0 );
    152170        $response = rest_get_server()->dispatch( $request );
    153         $this->assertErrorResponse( 'rest_cannot_read', $response, 401, 'The response status should be 401 for non-authenticated users.' );
     171        $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
    154172
    155173        wp_set_current_user( self::$editor_id );
    156174        $response = rest_get_server()->dispatch( $request );
    157         $this->assertErrorResponse( 'rest_cannot_read', $response, 403, 'The response status should be 403 for users without the right permissions.' );
     175        $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
    158176    }
    159177
Note: See TracChangeset for help on using the changeset viewer.