Skip to content

Commit

Permalink
Font Library: Font Collection backend (#54098)
Browse files Browse the repository at this point in the history
Add extensibility capabilities to the Font Library.
Provides a way to provide collections of typographic fonts by code.

---------

Co-authored-by: Tonya Mork <tonya.mork@automattic.com>
  • Loading branch information
matiasbenedetto and hellofromtonya committed Aug 31, 2023
1 parent 8cd5bff commit 607ef90
Show file tree
Hide file tree
Showing 17 changed files with 806 additions and 76 deletions.
95 changes: 95 additions & 0 deletions lib/experimental/fonts/font-library/class-wp-font-collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Font Collection class.
*
* This file contains the Font Collection class definition.
*
* @package WordPress
* @subpackage Font Library
* @since 6.4.0
*/

if ( class_exists( 'WP_Font_Collection' ) ) {
return;
}

/**
* Font Collection class.
*
* @since 6.4.0
*/
class WP_Font_Collection {

/**
* Font collection configuration.
*
* @since 6.4.0
*
* @var array
*/
private $config;

/**
* WP_Font_Collection constructor.
*
* @since 6.4.0
*
* @param array $config Font collection config options.
* See {@see wp_register_font_collection()} for the supported fields.
* @throws Exception If the required parameters are missing.
*/
public function __construct( $config ) {
if ( empty( $config ) || ! is_array( $config ) ) {
throw new Exception( 'Font Collection config options is required as a non-empty array.' );
}

if ( empty( $config['id'] ) || ! is_string( $config['id'] ) ) {
throw new Exception( 'Font Collection config ID is required as a non-empty string.' );
}

if ( empty( $config['name'] ) || ! is_string( $config['name'] ) ) {
throw new Exception( 'Font Collection config name is required as a non-empty string.' );
}

if ( empty( $config['data_json_file'] ) || ! is_string( $config['data_json_file'] ) ) {
throw new Exception( 'Font Collection config "data_json_file" option is required as a non-empty string.' );
}

$this->config = $config;
}

/**
* Gets the font collection config.
*
* @since 6.4.0
*
* @return array An array containing the font collection config.
*/
public function get_config() {
return $this->config;
}

/**
* Gets the font collection data.
*
* @since 6.4.0
*
* @return array|WP_Error An array containing the list of font families in theme.json format on success,
* else an instance of WP_Error on failure.
*/
public function get_data() {
if ( ! file_exists( $this->config['data_json_file'] ) ) {
return new WP_Error( 'font_collection_file_error', __( 'Font Collection data JSON file does not exist.', 'gutenberg' ) );
}

$data = file_get_contents( $this->config['data_json_file'] );
if ( empty( $data ) ) {
return new WP_Error( 'font_collection_read_error', __( 'Error reading the Font Collection data JSON file contents.', 'gutenberg' ) );
}

$collection_data = $this->get_config();
$collection_data['data'] = $data;
unset( $collection_data['data_json_file'] );
return $collection_data;
}
}
55 changes: 55 additions & 0 deletions lib/experimental/fonts/font-library/class-wp-font-library.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,61 @@ class WP_Font_Library {
'woff2' => 'font/woff2',
);

/**
* Font collections.
*
* @since 6.4.0
*
* @var array
*/
private static $collections = array();

/**
* Register a new font collection.
*
* @since 6.4.0
*
* @param array $config Font collection config options.
* See {@see wp_register_font_collection()} for the supported fields.
* @return WP_Font_Collection|WP_Error A font collection is it was registered successfully and a WP_Error otherwise.
*/
public static function register_font_collection( $config ) {
$new_collection = new WP_Font_Collection( $config );

if ( isset( self::$collections[ $config['id'] ] ) ) {
return new WP_Error( 'font_collection_registration_error', 'Font collection already registered.' );
}

self::$collections[ $config['id'] ] = $new_collection;
return $new_collection;
}

/**
* Gets all the font collections available.
*
* @since 6.4.0
*
* @return array List of font collections.
*/
public static function get_font_collections() {
return self::$collections;
}

/**
* Gets a font collection.
*
* @since 6.4.0
*
* @param string $id Font collection id.
* @return array List of font collections.
*/
public static function get_font_collection( $id ) {
if ( array_key_exists( $id, self::$collections ) ) {
return self::$collections[ $id ];
}
return new WP_Error( 'font_collection_not_found', 'Font collection not found.' );
}

/**
* Gets the upload directory for fonts.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,66 @@ public function register_routes() {
),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/collections',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_font_collections' ),
'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/collections' . '/(?P<id>[\/\w-]+)',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_font_collection' ),
'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
),
)
);
}

/**
* Gets a font collection.
*
* @since 6.4.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_font_collection( $request ) {
$id = $request->get_param( 'id' );
$collection = WP_Font_Library::get_font_collection( $id );

if ( is_wp_error( $collection ) ) {
$collection->add_data( array( 'status' => 404 ) );
return $collection;
}

return new WP_REST_Response( $collection->get_data() );
}

/**
* Gets the font collections available.
*
* @since 6.4.0
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_font_collections() {
$collections = array();
foreach ( WP_Font_Library::get_font_collections() as $collection ) {
$collections[] = $collection->get_config();
}

return new WP_REST_Response( $collections, 200 );
}

/**
Expand Down
24 changes: 22 additions & 2 deletions lib/experimental/fonts/font-library/font-library.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*
* @since 6.4.0
*/
function gutenberg_init_font_library() {
function gutenberg_init_font_library_routes() {
// @core-merge: This code will go into Core's `create_initial_post_types()`.
$args = array(
'public' => true,
Expand All @@ -33,5 +33,25 @@ function gutenberg_init_font_library() {
$font_library_controller->register_routes();
}

add_action( 'rest_api_init', 'gutenberg_init_font_library' );
add_action( 'rest_api_init', 'gutenberg_init_font_library_routes' );


if ( ! function_exists( 'wp_register_font_collection' ) ) {
/**
* Registers a new Font Collection in the Font Library.
*
* @since 6.4.0
*
* @param string[] $config {
* Font collection associative array of configuration options.
*
* @type string $id The font collection's unique ID.
* @type string $data_json_file The font collection's data JSON file.
* }
* @return WP_Font_Collection|WP_Error A font collection is it was registered
* successfully, else WP_Error.
*/
function wp_register_font_collection( $config ) {
return WP_Font_Library::register_font_collection( $config );
}
}
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ function gutenberg_is_experiment_enabled( $name ) {
( defined( 'FONTS_LIBRARY_ENABLE' ) && FONTS_LIBRARY_ENABLE )
) {
// Loads the Font Library.
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family.php';
Expand Down
92 changes: 92 additions & 0 deletions phpunit/tests/fonts/font-library/wpFontCollection/__construct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
/**
* Test WP_Font_Collection::__construct().
*
* @package WordPress
* @subpackage Font Library
*
* @group fonts
* @group font-library
*
* @covers WP_Font_Collection::__construct
*/
class Tests_Fonts_WpFontCollection_Construct extends WP_UnitTestCase {

public function test_should_initialize_data() {
$property = new ReflectionProperty( WP_Font_Collection::class, 'config' );
$property->setAccessible( true );

$config = array(
'id' => 'my-collection',
'name' => 'My Collection',
'description' => 'My collection description',
'data_json_file' => 'my-collection-data.json',
);
$font_collection = new WP_Font_Collection( $config );

$actual = $property->getValue( $font_collection );
$property->setAccessible( false );

$this->assertSame( $config, $actual );
}

/**
* @dataProvider data_should_throw_exception
*
* @param mixed $config Config of the font collection.
* @param string $expected_exception_message Expected exception message.
*/
public function test_should_throw_exception( $config, $expected_exception_message ) {
$this->expectException( 'Exception' );
$this->expectExceptionMessage( $expected_exception_message );
new WP_Font_Collection( $config );
}

/**
* Data provider.
*
* @return array
*/
public function data_should_throw_exception() {
return array(
'no id' => array(
array(
'name' => 'My Collection',
'description' => 'My collection description',
'data_json_file' => 'my-collection-data.json',
),
'Font Collection config ID is required as a non-empty string.',
),

'no config' => array(
'',
'Font Collection config options is required as a non-empty array.',
),

'empty array' => array(
array(),
'Font Collection config options is required as a non-empty array.',
),

'boolean instead of config array' => array(
false,
'Font Collection config options is required as a non-empty array.',
),

'null instead of config array' => array(
null,
'Font Collection config options is required as a non-empty array.',
),

'missing data_json_file' => array(
array(
'id' => 'my-collection',
'name' => 'My Collection',
'description' => 'My collection description',
),
'Font Collection config "data_json_file" option is required as a non-empty string.',
),

);
}
}
Loading

0 comments on commit 607ef90

Please sign in to comment.