diff --git a/includes/class-tmdo-rest-api.php b/includes/class-tmdo-rest-api.php index e532af2..08c6803 100644 --- a/includes/class-tmdo-rest-api.php +++ b/includes/class-tmdo-rest-api.php @@ -595,6 +595,35 @@ class TMDO_REST_API { } } + // Strip fields not marked show_in_rest => true in Schema_Registry. + // 'id' and 'post_type' are always retained. The filter below can add/remove + // fields on top of this default restriction. + $registry = TMDO_Schema_Registry::instance(); + $visible_keys = array_fill_keys( + array_merge( + array( 'id', 'post_type' ), + $registry->get_rest_visible_hot_columns( $post_type ), + $registry->get_rest_visible_cold_keys( $post_type ) + ), + true + ); + $data = array_intersect_key( $data, $visible_keys ); + + /** + * Filters the fields returned by GET /wpdo/v1/listings/{id}. + * + * Called after the default show_in_rest restriction. Use this hook to expose + * additional private fields to authenticated users, or to remove fields for + * specific consumers. + * + * @param array $data Field map (already restricted to show_in_rest=true fields). + * @param int $post_id Post ID. + * @param string $post_type Post type slug. + */ + $data = (array) apply_filters( 'wpdo_rest_listing_visible_fields', $data, $post_id, $post_type ); + /** This filter is documented above (tmdo_* is the forward-looking name). */ + $data = (array) apply_filters( 'tmdo_rest_listing_visible_fields', $data, $post_id, $post_type ); + return new WP_REST_Response( $data, 200 ); } @@ -1389,14 +1418,20 @@ class TMDO_REST_API { ) ?: array(); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber - // Rename post_id → id, drop internal columns. - $items = array_map( - function ( array $row ) use ( $post_type ): array { + // Rename post_id → id, drop internal columns, and restrict to show_in_rest fields. + $visible_cols = array_fill_keys( + TMDO_Schema_Registry::instance()->get_rest_visible_hot_columns( $post_type ), + true + ); + $items = array_map( + function ( array $row ) use ( $post_type, $visible_cols ): array { $item = array( 'id' => (int) $row['post_id'], 'post_type' => $post_type, ); unset( $row['post_id'], $row['updated_at'] ); + // Keep only columns with show_in_rest => true (or all if no registry entry). + $row = $visible_cols ? array_intersect_key( $row, $visible_cols ) : $row; return array_merge( $item, $row ); }, $rows diff --git a/includes/class-tmdo-schema-registry.php b/includes/class-tmdo-schema-registry.php index 9b1fec5..971b552 100644 --- a/includes/class-tmdo-schema-registry.php +++ b/includes/class-tmdo-schema-registry.php @@ -94,22 +94,24 @@ class TMDO_Schema_Registry { * - ttl (int) TTL in seconds (Zone B only, null = no expiry). * - cache_group (string) Object cache group (Zone C only). * - cache_ttl (int) Cache TTL in seconds (Zone C only). + * - show_in_rest (bool) Whether to expose via GET /wpdo/v1/listings. Default true. */ public function register( string $provider, array $config ): void { $config = wp_parse_args( $config, array( - 'post_type' => '', - 'entity_type' => '', - 'meta_key' => '', - 'zone' => 'hot', - 'data_type' => 'longtext', - 'column' => '', - 'indexed' => false, - 'ttl' => null, - 'cache_group' => '', - 'cache_ttl' => HOUR_IN_SECONDS, - 'provider' => $provider, + 'post_type' => '', + 'entity_type' => '', + 'meta_key' => '', + 'zone' => 'hot', + 'data_type' => 'longtext', + 'column' => '', + 'indexed' => false, + 'ttl' => null, + 'cache_group' => '', + 'cache_ttl' => HOUR_IN_SECONDS, + 'provider' => $provider, + 'show_in_rest' => true, ) ); @@ -232,6 +234,40 @@ class TMDO_Schema_Registry { return array_unique( $this->cold_fields[ $post_type ] ?? array() ); } + /** + * Returns hot column names for a post type where show_in_rest is true. + * Used by REST endpoints to restrict publicly exposed fields. + * + * @param string $post_type Post type. + * @return string[] Column names that are REST-visible. + */ + public function get_rest_visible_hot_columns( string $post_type ): array { + $visible = array(); + foreach ( $this->fields as $field ) { + if ( 'hot' === $field['zone'] && $field['post_type'] === $post_type && ! empty( $field['show_in_rest'] ) ) { + $visible[] = $field['column']; + } + } + return $visible; + } + + /** + * Returns cold meta keys for a post type where show_in_rest is true. + * Used by REST endpoints to restrict publicly exposed fields. + * + * @param string $post_type Post type. + * @return string[] Meta keys that are REST-visible. + */ + public function get_rest_visible_cold_keys( string $post_type ): array { + $visible = array(); + foreach ( $this->fields as $field ) { + if ( 'cold' === $field['zone'] && $field['post_type'] === $post_type && ! empty( $field['show_in_rest'] ) ) { + $visible[] = $field['meta_key']; + } + } + return array_unique( $visible ); + } + /** * Get warm field configuration. *