708 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			708 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Class Font_Controller
 | 
						|
 *
 | 
						|
 * @link      https://github.com/googleforcreators/web-stories-wp
 | 
						|
 *
 | 
						|
 * @copyright 2021 Google LLC
 | 
						|
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * Copyright 2021 Google LLC
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     https://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
declare(strict_types = 1);
 | 
						|
 | 
						|
namespace Google\Web_Stories\REST_API;
 | 
						|
 | 
						|
use stdClass;
 | 
						|
use WP_Error;
 | 
						|
use WP_Post;
 | 
						|
use WP_Query;
 | 
						|
use WP_REST_Posts_Controller;
 | 
						|
use WP_REST_Request;
 | 
						|
use WP_REST_Response;
 | 
						|
use WP_REST_Server;
 | 
						|
 | 
						|
/**
 | 
						|
 * Font_Controller class.
 | 
						|
 *
 | 
						|
 * @phpstan-type Font array{
 | 
						|
 *   family: string,
 | 
						|
 *   fallbacks?: string[],
 | 
						|
 *   weights?: array<int, array{0: int, 1: int}>,
 | 
						|
 *   styles?: string[],
 | 
						|
 *   variants?: string[],
 | 
						|
 *   service?: string,
 | 
						|
 *   metrics?: mixed,
 | 
						|
 *   id?: string,
 | 
						|
 *   url?: string
 | 
						|
 * }
 | 
						|
 * @phpstan-type SchemaEntry array{
 | 
						|
 *   description: string,
 | 
						|
 *   type: string,
 | 
						|
 *   context: string[],
 | 
						|
 *   default?: mixed,
 | 
						|
 * }
 | 
						|
 * @phpstan-type Schema array{
 | 
						|
 *   properties: array{
 | 
						|
 *     family?: SchemaEntry,
 | 
						|
 *     fallbacks?: SchemaEntry,
 | 
						|
 *     weights?: SchemaEntry,
 | 
						|
 *     styles?: SchemaEntry,
 | 
						|
 *     variants?: SchemaEntry,
 | 
						|
 *     service?: SchemaEntry,
 | 
						|
 *     metrics?: SchemaEntry,
 | 
						|
 *     id?: SchemaEntry,
 | 
						|
 *     url?: SchemaEntry
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
class Font_Controller extends WP_REST_Posts_Controller {
 | 
						|
	/**
 | 
						|
	 * Registers the routes for posts.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @see register_rest_route()
 | 
						|
	 */
 | 
						|
	public function register_routes(): void {
 | 
						|
		register_rest_route(
 | 
						|
			$this->namespace,
 | 
						|
			'/' . $this->rest_base,
 | 
						|
			[
 | 
						|
				[
 | 
						|
					'methods'             => WP_REST_Server::READABLE,
 | 
						|
					'callback'            => [ $this, 'get_items' ],
 | 
						|
					'permission_callback' => [ $this, 'get_items_permissions_check' ],
 | 
						|
					'args'                => $this->get_collection_params(),
 | 
						|
				],
 | 
						|
				[
 | 
						|
					'methods'             => WP_REST_Server::CREATABLE,
 | 
						|
					'callback'            => [ $this, 'create_item' ],
 | 
						|
					'permission_callback' => [ $this, 'create_item_permissions_check' ],
 | 
						|
					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
 | 
						|
				],
 | 
						|
				'schema' => [ $this, 'get_public_item_schema' ],
 | 
						|
			]
 | 
						|
		);
 | 
						|
 | 
						|
		register_rest_route(
 | 
						|
			$this->namespace,
 | 
						|
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
 | 
						|
			[
 | 
						|
				'args'   => [
 | 
						|
					'id' => [
 | 
						|
						'description' => __( 'Unique identifier for the font.', 'web-stories' ),
 | 
						|
						'type'        => 'integer',
 | 
						|
					],
 | 
						|
				],
 | 
						|
				[
 | 
						|
					'methods'             => WP_REST_Server::DELETABLE,
 | 
						|
					'callback'            => [ $this, 'delete_item' ],
 | 
						|
					'permission_callback' => [ $this, 'delete_item_permissions_check' ],
 | 
						|
				],
 | 
						|
				'schema' => [ $this, 'get_public_item_schema' ],
 | 
						|
			]
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Retrieves a collection of fonts.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.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_items( $request ) {
 | 
						|
		/**
 | 
						|
		 * Fonts list.
 | 
						|
		 *
 | 
						|
		 * @phpstan-var Font[] $fonts
 | 
						|
		 */
 | 
						|
		$fonts = [];
 | 
						|
 | 
						|
		// Retrieve the list of registered collection query parameters.
 | 
						|
		$registered = $this->get_collection_params();
 | 
						|
 | 
						|
		if ( isset( $registered['service'], $request['service'] ) ) {
 | 
						|
			if ( 'all' === $request['service'] || 'builtin' === $request['service'] ) {
 | 
						|
				array_push( $fonts, ...$this->get_builtin_fonts() );
 | 
						|
 | 
						|
				// For custom fonts the searching will be done in WP_Query already.
 | 
						|
				if ( isset( $registered['search'], $request['search'] ) && ! empty( $request['search'] ) ) {
 | 
						|
					/**
 | 
						|
					 * Requested URL.
 | 
						|
					 *
 | 
						|
					 * @var string $search
 | 
						|
					 */
 | 
						|
					$search = $request['search'];
 | 
						|
					$fonts  = array_values(
 | 
						|
						array_filter(
 | 
						|
							$fonts,
 | 
						|
							/**
 | 
						|
							 * Font data.
 | 
						|
							 *
 | 
						|
							 * @param array{family: string} $font
 | 
						|
							 * @return bool
 | 
						|
							 */
 | 
						|
							static fn( array $font ) => false !== stripos( $font['family'], $search )
 | 
						|
						)
 | 
						|
					);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if ( 'all' === $request['service'] || 'custom' === $request['service'] ) {
 | 
						|
				array_push( $fonts, ...$this->get_custom_fonts( $request ) );
 | 
						|
			}
 | 
						|
 | 
						|
			// Filter before doing any sorting.
 | 
						|
			if ( isset( $registered['include'], $request['include'] ) && ! empty( $request['include'] ) ) {
 | 
						|
				/**
 | 
						|
				 * Include list.
 | 
						|
				 *
 | 
						|
				 * @var array{string} $include_list
 | 
						|
				 */
 | 
						|
				$include_list = $request['include'];
 | 
						|
				$include_list = array_map( 'strtolower', $include_list );
 | 
						|
 | 
						|
				$fonts = array_values(
 | 
						|
					array_filter(
 | 
						|
						$fonts,
 | 
						|
						/**
 | 
						|
						 * Font data.
 | 
						|
						 *
 | 
						|
						 * @param array{family: string} $font
 | 
						|
						 * @return bool
 | 
						|
						 */
 | 
						|
						static fn( array $font ): bool => \in_array( strtolower( $font['family'] ), $include_list, true )
 | 
						|
					)
 | 
						|
				);
 | 
						|
			}
 | 
						|
 | 
						|
			if ( 'all' === $request['service'] ) {
 | 
						|
				// Since the built-in fonts and custom fonts both are already sorted,
 | 
						|
				// we only need to sort when including both.
 | 
						|
				usort(
 | 
						|
					$fonts,
 | 
						|
					/**
 | 
						|
					 * Font A and Font B.
 | 
						|
					 *
 | 
						|
					 * @param Font $a
 | 
						|
					 * @param Font $b
 | 
						|
					 * @return int
 | 
						|
					 */
 | 
						|
					static fn( array $a, array $b ): int => strnatcasecmp( $a['family'], $b['family'] )
 | 
						|
				);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return rest_ensure_response( $fonts );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Checks if a given request has access to read posts.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @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( $request ) {
 | 
						|
		$post_type = get_post_type_object( $this->post_type );
 | 
						|
 | 
						|
		if (
 | 
						|
			! $post_type ||
 | 
						|
			! current_user_can( $post_type->cap->read_post ) // phpcs:ignore WordPress.WP.Capabilities.Undetermined
 | 
						|
		) {
 | 
						|
			return new \WP_Error(
 | 
						|
				'rest_forbidden',
 | 
						|
				__( 'Sorry, you are not allowed to list fonts.', 'web-stories' ),
 | 
						|
				[ 'status' => rest_authorization_required_code() ]
 | 
						|
			);
 | 
						|
		}
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Force-deletes a single font.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.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 delete_item( $request ) {
 | 
						|
		$request->set_param( 'force', true );
 | 
						|
 | 
						|
		return parent::delete_item( $request );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prepares a single post output for response.
 | 
						|
	 *
 | 
						|
	 * @SuppressWarnings(PHPMD.NPathComplexity)
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @param WP_Post         $item    Post object.
 | 
						|
	 * @param WP_REST_Request $request Request object.
 | 
						|
	 * @return WP_REST_Response Response object.
 | 
						|
	 */
 | 
						|
	public function prepare_item_for_response( $item, $request ): WP_REST_Response {
 | 
						|
		// Restores the more descriptive, specific name for use within this method.
 | 
						|
		$post            = $item;
 | 
						|
		$GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
 | 
						|
 | 
						|
		setup_postdata( $post );
 | 
						|
 | 
						|
		$fields = $this->get_fields_for_response( $request );
 | 
						|
 | 
						|
		$data = [];
 | 
						|
 | 
						|
		if ( rest_is_field_included( 'id', $fields ) ) {
 | 
						|
			$data['id'] = $post->ID;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( rest_is_field_included( 'family', $fields ) ) {
 | 
						|
			$data['family'] = $post->post_title;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( rest_is_field_included( 'service', $fields ) ) {
 | 
						|
			$data['service'] = 'custom';
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Font data.
 | 
						|
		 *
 | 
						|
		 * @var array<string, mixed>|null $font_data
 | 
						|
		 */
 | 
						|
		$font_data = json_decode( $post->post_content, true );
 | 
						|
 | 
						|
		if ( $font_data ) {
 | 
						|
			foreach ( $font_data as $key => $value ) {
 | 
						|
				if ( rest_is_field_included( $key, $fields ) ) {
 | 
						|
					$data[ $key ] = $value;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Request context.
 | 
						|
		 *
 | 
						|
		 * @var string $context
 | 
						|
		 */
 | 
						|
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 | 
						|
		$data    = $this->add_additional_fields_to_object( $data, $request );
 | 
						|
		$data    = $this->filter_response_by_context( $data, $context );
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Response object.
 | 
						|
		 *
 | 
						|
		 * @var WP_REST_Response $response
 | 
						|
		 */
 | 
						|
		$response = rest_ensure_response( $data );
 | 
						|
 | 
						|
		if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
 | 
						|
			$links = $this->prepare_links( $post );
 | 
						|
			$response->add_links( $links );
 | 
						|
 | 
						|
			if ( ! empty( $links['self']['href'] ) ) {
 | 
						|
				$actions = $this->get_available_actions( $post, $request );
 | 
						|
 | 
						|
				$self = $links['self']['href'];
 | 
						|
 | 
						|
				foreach ( $actions as $rel ) {
 | 
						|
					$response->add_link( $rel, $self );
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		return $response;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Retrieves the query params for the fonts collection.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @return array<string, array<string, mixed>> Collection parameters.
 | 
						|
	 */
 | 
						|
	public function get_collection_params(): array {
 | 
						|
		$query_params = parent::get_collection_params();
 | 
						|
 | 
						|
		$query_params['context']['default'] = 'view';
 | 
						|
 | 
						|
		$query_params['search'] = [
 | 
						|
			'description'       => __( 'Limit results to those matching a string.', 'web-stories' ),
 | 
						|
			'type'              => 'string',
 | 
						|
			'sanitize_callback' => 'sanitize_text_field',
 | 
						|
			'validate_callback' => 'rest_validate_request_arg',
 | 
						|
		];
 | 
						|
 | 
						|
		$query_params['include'] = [
 | 
						|
			'description' => __( 'Limit result set to specific fonts.', 'web-stories' ),
 | 
						|
			'type'        => 'array',
 | 
						|
			'items'       => [
 | 
						|
				'type' => 'string',
 | 
						|
			],
 | 
						|
			'default'     => [],
 | 
						|
		];
 | 
						|
 | 
						|
		$query_params['service'] = [
 | 
						|
			'description'       => __( 'Filter fonts by service.', 'web-stories' ),
 | 
						|
			'type'              => 'string',
 | 
						|
			'sanitize_callback' => 'sanitize_text_field',
 | 
						|
			'default'           => 'all',
 | 
						|
			'enum'              => [
 | 
						|
				'all',
 | 
						|
				'custom',
 | 
						|
				'builtin', // system + fonts.google.com.
 | 
						|
			],
 | 
						|
		];
 | 
						|
 | 
						|
		/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 | 
						|
		return apply_filters( "rest_{$this->post_type}_collection_params", $query_params, $this->post_type );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Retrieves the font's schema, conforming to JSON Schema.
 | 
						|
	 *
 | 
						|
	 * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @return array Item schema data.
 | 
						|
	 *
 | 
						|
	 * @phpstan-return Schema
 | 
						|
	 */
 | 
						|
	public function get_item_schema(): array {
 | 
						|
		if ( $this->schema ) {
 | 
						|
			/**
 | 
						|
			 * Schema.
 | 
						|
			 *
 | 
						|
			 * @phpstan-var Schema $schema
 | 
						|
			 */
 | 
						|
			$schema = $this->add_additional_fields_schema( $this->schema );
 | 
						|
			return $schema;
 | 
						|
		}
 | 
						|
 | 
						|
		$schema = [
 | 
						|
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
 | 
						|
			'title'      => $this->post_type,
 | 
						|
			'type'       => 'object',
 | 
						|
			// Base properties for every font.
 | 
						|
			'properties' => [
 | 
						|
				'family'    => [
 | 
						|
					'description' => __( 'The font family', 'web-stories' ),
 | 
						|
					'type'        => [ 'string', 'null' ],
 | 
						|
					'context'     => [ 'view', 'edit', 'embed' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'fallbacks' => [
 | 
						|
					'description' => __( 'Fallback fonts', 'web-stories' ),
 | 
						|
					'type'        => 'array',
 | 
						|
					'items'       => [
 | 
						|
						'type' => 'string',
 | 
						|
					],
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'weights'   => [
 | 
						|
					'description' => __( 'Font weights', 'web-stories' ),
 | 
						|
					'type'        => 'array',
 | 
						|
					'items'       => [
 | 
						|
						'type'    => 'integer',
 | 
						|
						'minimum' => 0,
 | 
						|
						'maximum' => 900,
 | 
						|
					],
 | 
						|
					'minimum'     => 1,
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'styles'    => [
 | 
						|
					'description' => __( 'Font styles', 'web-stories' ),
 | 
						|
					'type'        => 'array',
 | 
						|
					'items'       => [
 | 
						|
						'type' => 'string',
 | 
						|
					],
 | 
						|
					'minimum'     => 1,
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'variants'  => [
 | 
						|
					'description' => __( 'Font variants', 'web-stories' ),
 | 
						|
					'type'        => 'array',
 | 
						|
					'items'       => [
 | 
						|
						'type'    => 'array',
 | 
						|
						'items'   => [
 | 
						|
							'type'    => 'integer',
 | 
						|
							'minimum' => 0,
 | 
						|
							'maximum' => 900,
 | 
						|
						],
 | 
						|
						'minimum' => 2,
 | 
						|
						'maximum' => 2,
 | 
						|
					],
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'service'   => [
 | 
						|
					'description' => __( 'Font service', 'web-stories' ),
 | 
						|
					'type'        => 'string',
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'readonly'    => true,
 | 
						|
				],
 | 
						|
				'metrics'   => [
 | 
						|
					'description' => __( 'Font metrics', 'web-stories' ),
 | 
						|
					'type'        => 'object',
 | 
						|
					'context'     => [ 'view', 'edit' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
				'id'        => [
 | 
						|
					'description' => __( 'Unique identifier for the font.', 'web-stories' ),
 | 
						|
					'type'        => 'integer',
 | 
						|
					'context'     => [ 'view', 'edit', 'embed' ],
 | 
						|
					'readonly'    => true,
 | 
						|
				],
 | 
						|
				'url'       => [
 | 
						|
					'description' => __( 'Font URL.', 'web-stories' ),
 | 
						|
					'type'        => 'string',
 | 
						|
					'format'      => 'uri',
 | 
						|
					'context'     => [ 'view', 'edit', 'embed' ],
 | 
						|
					'required'    => true,
 | 
						|
				],
 | 
						|
			],
 | 
						|
		];
 | 
						|
 | 
						|
		$this->schema = $schema;
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Schema.
 | 
						|
		 *
 | 
						|
		 * @phpstan-var Schema $schema
 | 
						|
		 */
 | 
						|
		$schema = $this->add_additional_fields_schema( $this->schema );
 | 
						|
		return $schema;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns a list of Google fonts.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @return array<int, mixed> List of Google fonts.
 | 
						|
	 *
 | 
						|
	 * @phpstan-return Font[]
 | 
						|
	 */
 | 
						|
	protected function get_builtin_fonts(): array {
 | 
						|
		$file = WEBSTORIES_PLUGIN_DIR_PATH . 'includes/data/fonts/fonts.json';
 | 
						|
 | 
						|
		if ( ! is_readable( $file ) ) {
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		$content = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
 | 
						|
 | 
						|
		if ( ! $content ) {
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * List of Google Fonts.
 | 
						|
		 *
 | 
						|
		 * @var array|null $fonts
 | 
						|
		 * @phpstan-var Font[]|null $fonts
 | 
						|
		 */
 | 
						|
		$fonts = json_decode( $content, true );
 | 
						|
 | 
						|
		if ( ! $fonts ) {
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		return $fonts;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns a list of custom fonts.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @param WP_REST_Request $request Full details about the request.
 | 
						|
	 * @return array<int, mixed> List of custom fonts.
 | 
						|
	 *
 | 
						|
	 * @phpstan-return Font[]
 | 
						|
	 */
 | 
						|
	protected function get_custom_fonts( $request ): array {
 | 
						|
		// Retrieve the list of registered collection query parameters.
 | 
						|
		$registered = $this->get_collection_params();
 | 
						|
		$args       = [
 | 
						|
			'orderby' => 'title',
 | 
						|
			'order'   => 'ASC',
 | 
						|
		];
 | 
						|
 | 
						|
		/*
 | 
						|
		 * This array defines mappings between public API query parameters whose
 | 
						|
		 * values are accepted as-passed, and their internal WP_Query parameter
 | 
						|
		 * name equivalents (some are the same). Only values which are also
 | 
						|
		 * present in $registered will be set.
 | 
						|
		 */
 | 
						|
		$parameter_mappings = [
 | 
						|
			'search' => 's',
 | 
						|
		];
 | 
						|
 | 
						|
		/*
 | 
						|
		 * For each known parameter which is both registered and present in the request,
 | 
						|
		 * set the parameter's value on the query $args.
 | 
						|
		 */
 | 
						|
		foreach ( $parameter_mappings as $api_param => $wp_param ) {
 | 
						|
			if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
 | 
						|
				$args[ $wp_param ] = $request[ $api_param ];
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Ensure our per_page parameter overrides any provided posts_per_page filter.
 | 
						|
		if ( isset( $registered['per_page'] ) ) {
 | 
						|
			$args['posts_per_page'] = $request['per_page'];
 | 
						|
		}
 | 
						|
 | 
						|
		// Force search to be case-insensitive.
 | 
						|
 | 
						|
		// Force the post_type argument, since it's not a user input variable.
 | 
						|
		$args['post_type'] = $this->post_type;
 | 
						|
		$query_args        = $this->prepare_items_query( $args, $request );
 | 
						|
 | 
						|
		$posts_query  = new WP_Query();
 | 
						|
		$query_result = $posts_query->query( $query_args );
 | 
						|
 | 
						|
		/**
 | 
						|
		 * List of custom fonts.
 | 
						|
		 *
 | 
						|
		 * @var array $posts
 | 
						|
		 * @phpstan-var Font[] $posts
 | 
						|
		 */
 | 
						|
		$posts = [];
 | 
						|
 | 
						|
		/**
 | 
						|
		 * We're expecting a post object.
 | 
						|
		 *
 | 
						|
		 * @var WP_Post $post
 | 
						|
		 */
 | 
						|
		foreach ( $query_result as $post ) {
 | 
						|
			if ( ! $this->check_read_permission( $post ) ) {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			$data    = $this->prepare_item_for_response( $post, $request );
 | 
						|
			$posts[] = $this->prepare_response_for_collection( $data );
 | 
						|
		}
 | 
						|
 | 
						|
		// Reset filter.
 | 
						|
		if ( 'edit' === $request['context'] ) {
 | 
						|
			remove_filter( 'post_password_required', [ $this, 'check_password_required' ] );
 | 
						|
		}
 | 
						|
 | 
						|
		return $posts;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prepares a single post for create.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @param WP_REST_Request $request Request object.
 | 
						|
	 * @return stdClass|WP_Error Post object or WP_Error.
 | 
						|
	 */
 | 
						|
	protected function prepare_item_for_database( $request ) {
 | 
						|
		$prepared_post              = new stdClass();
 | 
						|
		$prepared_post->post_status = 'publish';
 | 
						|
 | 
						|
		$font_data = [];
 | 
						|
 | 
						|
		$fields = [
 | 
						|
			'family',
 | 
						|
			'fallbacks',
 | 
						|
			'weights',
 | 
						|
			'styles',
 | 
						|
			'variants',
 | 
						|
			'metrics',
 | 
						|
			'url',
 | 
						|
		];
 | 
						|
 | 
						|
		$schema = $this->get_item_schema();
 | 
						|
 | 
						|
		foreach ( $fields as $field ) {
 | 
						|
			if ( ! empty( $schema['properties'][ $field ] ) && ! empty( $request[ $field ] ) ) {
 | 
						|
				$font_data[ $field ] = $request[ $field ];
 | 
						|
 | 
						|
				if ( 'family' === $field ) {
 | 
						|
					/**
 | 
						|
					 * Request data.
 | 
						|
					 *
 | 
						|
					 * @var array{family: string} $request
 | 
						|
					 */
 | 
						|
					$font_family = trim( $request['family'] );
 | 
						|
 | 
						|
					$prepared_post->post_title = $font_family;
 | 
						|
 | 
						|
					if ( $this->font_exists( $font_family ) ) {
 | 
						|
						return new \WP_Error(
 | 
						|
							'rest_invalid_field',
 | 
						|
							__( 'A font with this name already exists', 'web-stories' ),
 | 
						|
							[ 'status' => 400 ]
 | 
						|
						);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		$prepared_post->post_content = wp_json_encode( $font_data );
 | 
						|
 | 
						|
		return $prepared_post;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Determines whether a font with the same name already exists.
 | 
						|
	 *
 | 
						|
	 * Performs a case-insensitive comparison.
 | 
						|
	 *
 | 
						|
	 * @since 1.16.0
 | 
						|
	 *
 | 
						|
	 * @param string $font_family Font family.
 | 
						|
	 * @return bool Whether a font with this exact name already exists.
 | 
						|
	 */
 | 
						|
	private function font_exists( string $font_family ): bool {
 | 
						|
		$request = new WP_REST_Request(
 | 
						|
			WP_REST_Server::READABLE,
 | 
						|
			$this->namespace .
 | 
						|
			'/' . $this->rest_base
 | 
						|
		);
 | 
						|
		$request->set_param( 'include', [ $font_family ] );
 | 
						|
		$request->set_param( 'service', 'all' );
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Response object.
 | 
						|
		 *
 | 
						|
		 * @var WP_REST_Response $response
 | 
						|
		 */
 | 
						|
		$response = $this->get_items( $request );
 | 
						|
 | 
						|
		return ! empty( $response->get_data() );
 | 
						|
	}
 | 
						|
}
 |