get_method() || ! preg_match( '#^/wp/v2/posts(/|$)#', $request->get_route() ) ) { return $response; } $meta = $request->get_param( 'meta' ); if ( is_array( $meta ) && array_intersect_key( $meta, array_flip( self::SERVICE_OWNED ) ) ) { $request->set_param( 'meta', array_diff_key( $meta, array_flip( self::SERVICE_OWNED ) ) ); } return $response; } public static function register_meta(): void { $can_edit = static fn( $allowed, $meta_key, $post_id ) => current_user_can( 'edit_post', $post_id ); $nobody = static fn() => false; // The editor's choices and the service's answers are for the editor // (context=edit), not for an anonymous GET of the post - the hash and a // held file's path included. The block editor loads posts with // context=edit, so the sidebar still reads and writes all of them. $edit_only = array( 'schema' => array( 'context' => array( 'edit' ) ) ); // [Δ] Tri-state: '' follows the portal default, so changing the default // reaches every post an editor never touched. self::meta( self::ENABLED, $can_edit, static fn( $v ) => in_array( $v, array( 'yes', 'no' ), true ) ? $v : '', $edit_only ); self::meta( self::VOICE, $can_edit, static fn( $v ) => preg_match( self::VOICE_PATTERN, (string) $v ) ? (string) $v : '', $edit_only ); self::meta( self::ON_SAVE, $can_edit, static fn( $v ) => in_array( $v, self::ON_SAVE_CHOICES, true ) ? $v : '', $edit_only ); self::meta( self::STATUS, $nobody, array( self::class, 'sanitize_status' ), $edit_only ); self::meta( self::PATH, $nobody, array( self::class, 'sanitize_path' ), $edit_only ); self::meta( self::HASH, $nobody, 'sanitize_text_field', $edit_only ); // Plan R11: written by Readerr_Sync only, read by Readerr_Render; never // in REST, so no editor save carries a stale copy of either. self::meta( self::DURATION_MS, $nobody, static fn( $v ) => (string) max( 0, (int) $v ), false ); self::meta( self::QUEUED_AT, $nobody, static fn( $v ) => (string) max( 0, (int) $v ), false ); } private static function meta( string $key, callable $auth, callable $sanitize, $show_in_rest = true ): void { register_post_meta( 'post', $key, array( 'type' => 'string', 'single' => true, 'default' => '', 'show_in_rest' => $show_in_rest, 'auth_callback' => $auth, 'sanitize_callback' => $sanitize, ) ); } public static function sanitize_status( $value ): string { return in_array( $value, self::STATUSES, true ) ? $value : ''; } /** Only a bare path of our own shape — never a URL (§2.6). */ public static function sanitize_path( $value ): string { return preg_match( self::PATH_PATTERN, (string) $value ) ? (string) $value : ''; } }