Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions backport-changelog/7.1/11559.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ https://github.com/WordPress/wordpress-develop/pull/11559

* https://github.com/WordPress/gutenberg/pull/77260
* https://github.com/WordPress/gutenberg/pull/79623
* https://github.com/WordPress/gutenberg/pull/79686
43 changes: 37 additions & 6 deletions lib/class-wp-rest-icons-controller-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,47 @@ class WP_REST_Icons_Controller_Gutenberg extends WP_REST_Icons_Controller {

/**
* Registers the routes for icons.
*
* Adds a collection-scoped route (`/icons/<namespace>`) in addition to
* the base class's list and single-item routes.
*/
public function register_routes() {
parent::register_routes();
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'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' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<name>[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?/[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)',
array(
'args' => array(
'name' => array(
'description' => __( 'Icon name.', 'gutenberg' ),
'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' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<namespace>[a-z][a-z-]*)',
'/' . $this->rest_base . '/(?P<namespace>[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)',
array(
array(
'methods' => WP_REST_Server::READABLE,
Expand All @@ -54,7 +85,7 @@ public function get_collection_params() {
$query_params['namespace'] = array(
'description' => __( 'Limit results to icons belonging to the given collection slug.', 'gutenberg' ),
'type' => 'string',
'pattern' => '^[a-z][a-z-]*$',
'pattern' => '^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$',
);
return $query_params;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function register_routes() {

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<name>[a-z][a-z0-9-]*/[a-z][a-z0-9-]*)',
'/' . $this->rest_base . '/(?P<name>[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?/[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)',
array(
'args' => array(
'name' => array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ public function register( $collection_slug, $collection_properties ) {
return false;
}

if ( ! preg_match( '/^[a-z0-9-]+$/', $collection_slug ) ) {
if ( ! preg_match( '/^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$/', $collection_slug ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Icon collection slug must only contain lowercase alphanumeric characters and hyphens.', 'gutenberg' ),
__( 'Icon collection slug must start and end with a lowercase letter or digit and contain only lowercase letters, digits, hyphens, and underscores.', 'gutenberg' ),
'7.1.0'
);
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<?php

/**
* Controller which provides REST endpoint for registered icon collections.
*
* @package gutenberg
*/

if ( ! class_exists( 'WP_REST_Icon_Collections_Controller' ) ) {
class WP_REST_Icon_Collections_Controller extends WP_REST_Controller {
/**
* Constructs the controller.
*/
public function __construct() {
$this->namespace = 'wp/v2';
$this->rest_base = 'icon-collections';
}

/**
* Registers the routes for the objects of the controller.
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'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' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<slug>[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)',
array(
'args' => array(
'slug' => array(
'description' => __( 'Icon collection slug.', 'gutenberg' ),
'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' ),
)
);
}

/**
* Checks whether a given request has permission to read icon collections.
*
* @param WP_REST_Request $_request Full details about the request.
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
*/
public function get_items_permissions_check(
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$request
) {
if ( current_user_can( 'edit_posts' ) ) {
return true;
}

foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
if ( current_user_can( $post_type->cap->edit_posts ) ) {
return true;
}
}

return new WP_Error(
'rest_cannot_view',
__( 'Sorry, you are not allowed to view the registered icon collections.', 'gutenberg' ),
array( 'status' => rest_authorization_required_code() )
);
}

/**
* Checks if a given request has access to read a specific icon collection.
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
*/
public function get_item_permissions_check( $request ) {
$check = $this->get_items_permissions_check( $request );
if ( is_wp_error( $check ) ) {
return $check;
}

return true;
}

/**
* Retrieves all icon collections.
*
* @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_items( $request ) {
$response = array();
$collections = WP_Icon_Collections_Registry::get_instance()->get_all_registered();
foreach ( $collections as $collection ) {
$prepared_collection = $this->prepare_item_for_response( $collection, $request );
$response[] = $this->prepare_response_for_collection( $prepared_collection );
}
return rest_ensure_response( $response );
}

/**
* Retrieves a specific icon collection.
*
* @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 ) {
$registry = WP_Icon_Collections_Registry::get_instance();
$collection = $registry->get_registered( $request['slug'] );

if ( null === $collection ) {
return new WP_Error(
'rest_icon_collection_not_found',
sprintf(
/* translators: %s: Icon collection slug. */
__( 'Icon collection not found: "%s".', 'gutenberg' ),
$request['slug']
),
array( 'status' => 404 )
);
}

$data = $this->prepare_item_for_response( $collection, $request );
return rest_ensure_response( $data );
}

/**
* Prepare a raw icon collection before it gets output in a REST API response.
*
* @param array $item Raw icon collection as registered, before any changes.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function prepare_item_for_response( $item, $request ) {
$fields = $this->get_fields_for_response( $request );
$keys = array(
'slug' => 'slug',
'label' => 'label',
'description' => 'description',
);
$data = array();
foreach ( $keys as $item_key => $rest_key ) {
if ( isset( $item[ $item_key ] ) && rest_is_field_included( $rest_key, $fields ) ) {
$data[ $rest_key ] = $item[ $item_key ];
}
}

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
return rest_ensure_response( $data );
}

/**
* Retrieves the icon collection schema, conforming to JSON Schema.
*
* @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' => 'icon-collection',
'type' => 'object',
'properties' => array(
'slug' => array(
'description' => __( 'The icon collection slug.', 'gutenberg' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
),
'label' => array(
'description' => __( 'The icon collection label.', 'gutenberg' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
),
'description' => array(
'description' => __( 'The icon collection description.', 'gutenberg' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
),
),
);

$this->schema = $schema;

return $this->add_additional_fields_schema( $this->schema );
}

/**
* Retrieves the query params for the icon collections collection.
*
* @return array Collection parameters.
*/
public function get_collection_params() {
$query_params = parent::get_collection_params();
$query_params['context']['default'] = 'view';
return $query_params;
}
}
}
9 changes: 9 additions & 0 deletions lib/compat/wordpress-7.1/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ function gutenberg_add_date_wp_template_schema() {
}
add_filter( 'rest_api_init', 'gutenberg_add_date_wp_template_schema' );

/**
* Registers the Icon Collections Registry REST API routes.
*/
function gutenberg_register_icon_collections_controller_endpoints() {
$icon_collections_controller = new WP_REST_Icon_Collections_Controller();
$icon_collections_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_icon_collections_controller_endpoints' );

/**
* Overrides the REST controller for the attachment post type to add support
* for filtering by multiple media types.
Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-7.1/view-config-api.php';
require __DIR__ . '/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php';
require __DIR__ . '/compat/wordpress-7.1/class-wp-icon-collections-registry.php';
require __DIR__ . '/compat/wordpress-7.1/class-wp-rest-icon-collections-controller.php';
require __DIR__ . '/compat/wordpress-7.1/rest-api.php';
require __DIR__ . '/compat/wordpress-7.1/collaboration.php';
require __DIR__ . '/compat/wordpress-7.1/block-bindings.php';
Expand Down
10 changes: 10 additions & 0 deletions packages/core-data/src/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,16 @@ export const rootEntitiesConfig = [
key: 'name',
supportsPagination: false,
},
{
label: __( 'Icon Collections' ),
name: 'iconCollection',
kind: 'root',
baseURL: '/wp/v2/icon-collections',
baseURLParams: { context: 'view' },
plural: 'iconCollections',
key: 'slug',
supportsPagination: false,
},
];

export const deprecatedEntities = {
Expand Down
Loading
Loading