@@ -821,13 +821,19 @@ private static function build_post_schema( WP_Post_Type $post_type_object ): arr
821821 );
822822
823823 if ( post_type_supports ( $ slug , 'custom-fields ' ) ) {
824+ $ meta_properties = self ::get_meta_value_schema_properties ( $ slug );
825+
824826 $ properties ['meta ' ] = array (
825827 'type ' => 'object ' ,
826828 'description ' => __ ( 'Public post meta key-value pairs. Only present when include.meta is true. ' ),
827829 'additionalProperties ' => array (
828830 'type ' => array ( 'array ' , 'object ' , 'string ' , 'number ' , 'integer ' , 'boolean ' , 'null ' ),
829831 ),
830832 );
833+
834+ if ( ! empty ( $ meta_properties ) ) {
835+ $ properties ['meta ' ]['properties ' ] = $ meta_properties ;
836+ }
831837 }
832838
833839 return array (
@@ -1218,25 +1224,21 @@ static function ( $term ): array {
12181224 }
12191225
12201226 if ( ! empty ( $ include ['meta ' ] ) && post_type_supports ( $ slug , 'custom-fields ' ) ) {
1221- $ meta = get_post_meta ( $ post ->ID );
1222- $ public_meta = array ();
1223- $ allowed_meta_keys = self ::get_allowed_meta_keys ( $ slug );
1224- $ registered_meta = array_merge (
1225- get_registered_meta_keys ( 'post ' ),
1226- get_registered_meta_keys ( 'post ' , $ slug )
1227- );
1227+ $ meta = get_post_meta ( $ post ->ID );
1228+ $ public_meta = array ();
1229+ $ allowed_meta = self ::get_allowed_meta ( $ slug );
12281230
12291231 foreach ( $ meta as $ key => $ values ) {
12301232 // Skip protected meta keys.
12311233 if ( is_protected_meta ( $ key , 'post ' ) ) {
12321234 continue ;
12331235 }
12341236 // Only include meta keys that are explicitly allowed.
1235- if ( ! in_array ( $ key , $ allowed_meta_keys , true ) ) {
1237+ if ( ! isset ( $ allowed_meta [ $ key ] ) ) {
12361238 continue ;
12371239 }
12381240 // Respect the registered 'single' property for consistent behavior with get_post_meta().
1239- $ is_single = ! empty ( $ registered_meta [ $ key ]['single ' ] );
1241+ $ is_single = ! empty ( $ allowed_meta [ $ key ]['single ' ] );
12401242 $ public_meta [ $ key ] = $ is_single ? ( $ values [0 ] ?? null ) : $ values ;
12411243 }
12421244
@@ -1498,19 +1500,124 @@ private static function process_date_top_level( array $input, array &$result ):
14981500 * @return string[] List of allowed meta keys.
14991501 */
15001502 private static function get_allowed_meta_keys ( string $ post_type_slug ): array {
1501- $ registered_meta = array_merge (
1503+ return array_keys ( self ::get_allowed_meta ( $ post_type_slug ) );
1504+ }
1505+
1506+ /**
1507+ * Returns all registered post meta entries that are exposed through abilities for a post type.
1508+ *
1509+ * @since 7.0.0
1510+ *
1511+ * @param string $post_type_slug The post type slug.
1512+ * @return array<string, array<string, mixed>> Allowed meta registration args keyed by meta key.
1513+ */
1514+ private static function get_allowed_meta ( string $ post_type_slug ): array {
1515+ $ registered_meta = self ::get_registered_meta_for_post_type ( $ post_type_slug );
1516+ $ allowed = array ();
1517+
1518+ foreach ( $ registered_meta as $ key => $ args ) {
1519+ if ( self ::is_meta_enabled_in_abilities ( $ args ) ) {
1520+ $ allowed [ $ key ] = $ args ;
1521+ }
1522+ }
1523+
1524+ return $ allowed ;
1525+ }
1526+
1527+ /**
1528+ * Returns all registered post meta entries for a post type, with subtype values overriding global ones.
1529+ *
1530+ * @since 7.0.0
1531+ *
1532+ * @param string $post_type_slug The post type slug.
1533+ * @return array<string, array<string, mixed>> Registered meta args keyed by meta key.
1534+ */
1535+ private static function get_registered_meta_for_post_type ( string $ post_type_slug ): array {
1536+ return array_merge (
15021537 get_registered_meta_keys ( 'post ' ),
15031538 get_registered_meta_keys ( 'post ' , $ post_type_slug )
15041539 );
1540+ }
15051541
1506- $ allowed = array ();
1507- foreach ( $ registered_meta as $ key => $ args ) {
1508- if ( ! empty ( $ args ['show_in_abilities ' ] ) ) {
1509- $ allowed [] = $ key ;
1542+ /**
1543+ * Determines whether a meta key is enabled for abilities.
1544+ *
1545+ * `show_in_abilities` can be either a boolean or an options array.
1546+ *
1547+ * @since 7.0.0
1548+ *
1549+ * @param array<string, mixed> $args Meta registration args.
1550+ * @return bool True if enabled for abilities, false otherwise.
1551+ */
1552+ private static function is_meta_enabled_in_abilities ( array $ args ): bool {
1553+ return ! empty ( $ args ['show_in_abilities ' ] );
1554+ }
1555+
1556+ /**
1557+ * Builds keyed value schema properties for meta keys that provide `show_in_abilities.schema`.
1558+ *
1559+ * Keys without a schema continue using the generic additionalProperties fallback.
1560+ *
1561+ * @since 7.0.0
1562+ *
1563+ * @param string $post_type_slug The post type slug.
1564+ * @return array<string, array<string, mixed>> Value schema properties keyed by meta key.
1565+ */
1566+ private static function get_meta_value_schema_properties ( string $ post_type_slug ): array {
1567+ $ properties = array ();
1568+
1569+ foreach ( self ::get_allowed_meta ( $ post_type_slug ) as $ key => $ args ) {
1570+ if (
1571+ ! is_array ( $ args ['show_in_abilities ' ] )
1572+ || ! isset ( $ args ['show_in_abilities ' ]['schema ' ] )
1573+ || ! is_array ( $ args ['show_in_abilities ' ]['schema ' ] )
1574+ ) {
1575+ continue ;
15101576 }
1577+
1578+ $ properties [ $ key ] = self ::build_meta_value_schema ( $ args );
1579+ }
1580+
1581+ ksort ( $ properties );
1582+
1583+ return $ properties ;
1584+ }
1585+
1586+ /**
1587+ * Builds the value schema for a single meta key.
1588+ *
1589+ * @since 7.0.0
1590+ *
1591+ * @param array<string, mixed> $args Meta registration args.
1592+ * @return array<string, mixed> JSON Schema for the meta value.
1593+ */
1594+ private static function build_meta_value_schema ( array $ args ): array {
1595+ $ schema = array (
1596+ 'type ' => ! empty ( $ args ['type ' ] ) ? $ args ['type ' ] : 'string ' ,
1597+ );
1598+
1599+ if ( ! empty ( $ args ['label ' ] ) ) {
1600+ $ schema ['title ' ] = $ args ['label ' ];
1601+ }
1602+
1603+ if ( ! empty ( $ args ['description ' ] ) ) {
1604+ $ schema ['description ' ] = $ args ['description ' ];
1605+ }
1606+
1607+ if ( array_key_exists ( 'default ' , $ args ) ) {
1608+ $ schema ['default ' ] = $ args ['default ' ];
1609+ }
1610+
1611+ $ schema = array_merge ( $ schema , $ args ['show_in_abilities ' ]['schema ' ] );
1612+
1613+ if ( empty ( $ args ['single ' ] ) ) {
1614+ $ schema = array (
1615+ 'type ' => 'array ' ,
1616+ 'items ' => $ schema ,
1617+ );
15111618 }
15121619
1513- return array_unique ( $ allowed ) ;
1620+ return $ schema ;
15141621 }
15151622
15161623 /**
0 commit comments