diff --git a/docs/extending/query-output_schema.md b/docs/extending/query-output_schema.md index d236caa31..e95d2007c 100644 --- a/docs/extending/query-output_schema.md +++ b/docs/extending/query-output_schema.md @@ -16,6 +16,7 @@ Unless your API returns a single value, `type` will be constructed of an associa - `null` - `number` - `string` +- `title` - `url` - `uuid` diff --git a/example/rest-api/art-institute/art-institute.php b/example/rest-api/art-institute/art-institute.php index d7e333ca1..8603d6db9 100644 --- a/example/rest-api/art-institute/art-institute.php +++ b/example/rest-api/art-institute/art-institute.php @@ -71,7 +71,7 @@ function register_aic_block(): void { ], 'title' => [ 'name' => 'Title', - 'type' => 'string', + 'type' => 'title', 'path' => '$.title', ], 'image_id' => [ @@ -136,7 +136,7 @@ function register_aic_block(): void { ], 'title' => [ 'name' => 'Title', - 'type' => 'string', + 'type' => 'title', 'path' => '$.title', ], 'image_url' => [ diff --git a/inc/Editor/BlockPatterns/BlockPatterns.php b/inc/Editor/BlockPatterns/BlockPatterns.php index aa0060868..a3102e4a9 100644 --- a/inc/Editor/BlockPatterns/BlockPatterns.php +++ b/inc/Editor/BlockPatterns/BlockPatterns.php @@ -112,12 +112,16 @@ public static function register_default_block_pattern( string $block_name, strin $bindings['heading']['content'] = [ $field, $name ]; break; } - + $bindings['paragraphs'][] = [ 'content' => [ $field, $name ], ]; break; + case 'title': + $bindings['heading']['content'] = [ $field, $name ]; + break; + case 'image_alt': $bindings['image']['alt'] = [ $field, $name ]; break; diff --git a/inc/Integrations/SalesforceD2C/SalesforceD2CIntegration.php b/inc/Integrations/SalesforceD2C/SalesforceD2CIntegration.php index d4c89d315..7ad9dd32a 100644 --- a/inc/Integrations/SalesforceD2C/SalesforceD2CIntegration.php +++ b/inc/Integrations/SalesforceD2C/SalesforceD2CIntegration.php @@ -75,7 +75,7 @@ private static function get_queries( SalesforceD2CDataSource $data_source ): arr 'name' => [ 'name' => 'Name', 'path' => '$.name', - 'type' => 'string', + 'type' => 'title', ], 'sku' => [ 'name' => 'SKU', @@ -134,7 +134,7 @@ private static function get_queries( SalesforceD2CDataSource $data_source ): arr 'name' => [ 'name' => 'Name', 'path' => '$.name', - 'type' => 'string', + 'type' => 'title', ], 'image_url' => [ 'name' => 'Image URL', diff --git a/inc/Integrations/Shopify/ShopifyIntegration.php b/inc/Integrations/Shopify/ShopifyIntegration.php index 52cd75afe..c55649cbd 100644 --- a/inc/Integrations/Shopify/ShopifyIntegration.php +++ b/inc/Integrations/Shopify/ShopifyIntegration.php @@ -69,7 +69,7 @@ public static function get_queries( ShopifyDataSource $data_source ): array { 'title' => [ 'name' => 'Title', 'path' => '$.data.product.title', - 'type' => 'string', + 'type' => 'title', ], 'variant_id' => [ 'name' => 'Variant ID', @@ -109,7 +109,7 @@ public static function get_queries( ShopifyDataSource $data_source ): array { 'title' => [ 'name' => 'Product title', 'path' => '$.node.title', - 'type' => 'string', + 'type' => 'title', ], ], ], diff --git a/inc/Sanitization/Sanitizer.php b/inc/Sanitization/Sanitizer.php index 8bb6193da..afb57a51a 100644 --- a/inc/Sanitization/Sanitizer.php +++ b/inc/Sanitization/Sanitizer.php @@ -119,6 +119,9 @@ public static function sanitize_primitive_type( string $type_name, mixed $value case 'string': return sanitize_text_field( strval( $value ) ); + case 'title': + return sanitize_text_field( strval( $value ) ); + case 'button_text': case 'html': case 'id': diff --git a/inc/Validation/ConfigSchemas.php b/inc/Validation/ConfigSchemas.php index 2f570c748..778add598 100644 --- a/inc/Validation/ConfigSchemas.php +++ b/inc/Validation/ConfigSchemas.php @@ -275,6 +275,7 @@ private static function generate_http_query_config_schema(): array { 'image_alt', 'image_url', 'markdown', + 'title', // 'json_path' is omitted since it likely has no user utility. 'url', 'uuid', diff --git a/inc/Validation/Types.php b/inc/Validation/Types.php index 8b4b285be..aaeda478d 100644 --- a/inc/Validation/Types.php +++ b/inc/Validation/Types.php @@ -104,6 +104,10 @@ public static function url(): array { return self::generate_primitive_type( 'url' ); } + public static function title(): array { + return self::generate_primitive_type( 'title' ); + } + public static function uuid(): array { return self::generate_primitive_type( 'uuid' ); } diff --git a/inc/Validation/Validator.php b/inc/Validation/Validator.php index aedbd03d4..f8aaa65bd 100644 --- a/inc/Validation/Validator.php +++ b/inc/Validation/Validator.php @@ -47,10 +47,16 @@ private function check_type( array $type, mixed $value = null ): bool|WP_Error { if ( Types::is_primitive( $type ) ) { $type_name = Types::get_type_name( $type ); - if ( $this->check_primitive_type( $type_name, $value ) ) { + $result = $this->check_primitive_type( $type_name, $value ); + + if ( true === $result ) { return true; } + if ( is_wp_error( $result ) ) { + return $result; + } + return $this->create_error( sprintf( 'Value must be a %s', $type_name ), $value ); } @@ -232,7 +238,7 @@ private function check_non_primitive_type( array $type, mixed $value ): bool|WP_ } } - private function check_primitive_type( string $type_name, mixed $value ): bool { + private function check_primitive_type( string $type_name, mixed $value ): bool|WP_Error { switch ( $type_name ) { case 'any': return true; @@ -256,6 +262,7 @@ private function check_primitive_type( string $type_name, mixed $value ): bool { case 'html': case 'image_alt': case 'markdown': + case 'title': return is_string( $value ); case 'button_text': @@ -274,7 +281,7 @@ private function check_primitive_type( string $type_name, mixed $value ): bool { return wp_is_uuid( $value ); default: - return false; + return $this->create_error( 'Unknown type', $type_name ); } } diff --git a/src/blocks/remote-data-container/components/item-list/ItemList.tsx b/src/blocks/remote-data-container/components/item-list/ItemList.tsx index fe95a7ccd..773cef05b 100644 --- a/src/blocks/remote-data-container/components/item-list/ItemList.tsx +++ b/src/blocks/remote-data-container/components/item-list/ItemList.tsx @@ -85,7 +85,7 @@ export function ItemList( props: ItemListProps ) { // Find title field from availableBindings by checking type const titleField = Object.entries( availableBindings ).find( - ( [ _, binding ] ) => binding.type === 'string' && binding.name.toLowerCase() === 'title' + ( [ _, binding ] ) => binding.type === 'title' )?.[ 0 ]; // Find media field from availableBindings by checking type diff --git a/src/blocks/remote-data-container/config/constants.ts b/src/blocks/remote-data-container/config/constants.ts index 4b77256e2..da3f65b04 100644 --- a/src/blocks/remote-data-container/config/constants.ts +++ b/src/blocks/remote-data-container/config/constants.ts @@ -33,4 +33,5 @@ export const TEXT_FIELD_TYPES = [ 'markdown', 'number', 'string', + 'title', ]; diff --git a/tests/inc/Validation/ValidatorTest.php b/tests/inc/Validation/ValidatorTest.php index 798b5d8f9..741160161 100644 --- a/tests/inc/Validation/ValidatorTest.php +++ b/tests/inc/Validation/ValidatorTest.php @@ -29,6 +29,7 @@ public function testValidPrimitiveTypes(): void { 'image_url' => Types::image_url(), 'json_path' => Types::json_path(), 'markdown' => Types::markdown(), + 'title' => Types::title(), 'url' => Types::url(), 'uuid' => Types::uuid(), ] ); @@ -52,6 +53,7 @@ public function testValidPrimitiveTypes(): void { 'image_url' => 'https://example.com/image.jpg', 'json_path' => '$.foo.bar', 'markdown' => '# Hello, world!', + 'title' => 'A Title', 'url' => 'https://example.com/foo', 'uuid' => '123e4567-e89b-12d3-a456-426614174000', ] ) ); @@ -352,6 +354,31 @@ public function testInvalidIds( mixed $invalid_value ): void { $this->assertStringStartsWith( 'Value must be a id:', $result->get_error_message() ); } + public function testInvalidNonPrimitiveType(): void { + $schema = [ '@type' => 'invented' ]; + + $validator = new Validator( $schema ); + + $result = $validator->validate( 'hello, world!' ); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertSame( 'Unknown type: invented', $result->get_error_message() ); + } + + public function testInvalidPrimitiveType(): void { + $schema = [ + '@primitive' => true, + '@type' => 'invented', + ]; + + $validator = new Validator( $schema ); + + $result = $validator->validate( 'hello, world!' ); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertSame( 'Unknown type: invented', $result->get_error_message() ); + } + public function testCallable(): void { $schema = Types::callable();