Data Filters
This page documents filters for customizing data providers, meta values, and expression extensions.
| Filter | Description |
|---|---|
unblock/data/providers | Register custom data providers |
unblock/data/provider_global | Turn a global provider on or off |
unblock/data/preview_fixtures | Placeholder values in pattern library previews |
unblock/data/preview_overrides | Replace a field value in the editor preview |
unblock/data/filters | Register custom expression filters |
unblock/data/functions | Register custom expression functions |
unblock/data/post_meta | Filter post meta values |
unblock/data/user_meta | Filter user meta values |
unblock/data/term_meta | Filter term meta values |
unblock/data/option_meta | Pre-filter for option retrieval from Site provider |
unblock/data/meta_transform_value | Convert relational custom fields to objects site-wide |
unblock/data/allowed_options | Whitelist accessible WordPress options |
unblock/data/allowed_meta_keys | Open meta keys: _ keys, user meta keys |
unblock/data/allowed_field | Open a private field, for some objects |
unblock/data/allowed_user | Open a user without a published post |
unblock/data/allowed_post_types | Open a post type WordPress shows nowhere to the public |
unblock/data/allowed_post | Open or close one post |
data/providers
Register custom data providers to expose new data sources in expressions.
| Parameter | Type | Description |
|---|---|---|
$providers | array | Registered providers configuration |
Each provider is an array of fields. Each field requires:
label— Display label for the fieldcallback— Callable that returns the field value. It receives( $context, $args ): the execution context, and the arguments passed in the expression as an indexed array.
Optional field properties (a custom field is public: a callback returning personal data checks access itself):
escape— Escape function (false,'esc_url', etc.). Default:'esc_html'.returns— Provider key of the value the callback returns ('post','user','term','image', a post type or another custom provider), so its fields can be chained.args— The arguments, in order, each[ 'name' => …, 'type' => …, 'default' => … ]: the editor shows them in its hints, and an expression can give one by its name. Filters and functions take the same key.
Example: Register Custom Provider
add_filter( 'unblock/data/providers', function( $providers ) {
$providers['shop'] = [
'name' => [
'label' => __( 'Shop Name', 'flavor' ),
'callback' => fn( $context, $args ) => get_option( 'blogname' ),
],
'url' => [
'label' => __( 'Shop URL', 'flavor' ),
'escape' => 'esc_url',
'callback' => fn( $context, $args ) => home_url( '/shop' ),
],
'phone' => [
'label' => __( 'Phone', 'flavor' ),
'callback' => fn( $context, $args ) => get_option( 'shop_phone', '+1 555-0100' ),
],
];
return $providers;
} );
Use in expressions: {{ shop.name }}, {{ shop.url }}, {{ shop.phone }}
A custom provider is a global root: it works in any expression, and the data picker offers it everywhere. Register it from a plugin or your theme's functions.php: providers are read once, the first time an expression renders.
A provider named after a post type is not a root. It adds fields to the posts of that type: {{ post.price }} on a product.
Name the provider with letters, digits and underscores only, and avoid the words the expression language uses (and, or, not, in, is, true, false, null, loop) and the built-in names (post, user, term, site, author…). A loop alias or a variable with the same name as your provider takes precedence inside its scope.
data/provider_global
Decide whether a provider is a global root. Custom providers are global by default, except those named after a post type. Return false to turn one off.
apply_filters( 'unblock/data/provider_global', bool $global, string $provider );
| Parameter | Type | Description |
|---|---|---|
$global | bool | Whether the provider is a global root |
$provider | string | Provider key |
Example: Disable the Request Provider
add_filter( 'unblock/data/provider_global', function( $global, $provider ) {
return 'request' === $provider ? false : $global;
}, 10, 2 );
site and user always resolve: for them, this filter only affects the data picker.
data/preview_fixtures
Pattern library previews never show your site's real data. There, a global provider shows its placeholder values, or nothing. Declare placeholders for your provider's fields:
add_filter( 'unblock/data/preview_fixtures', function( $fixtures ) {
$fixtures['shop'] = [
'name' => 'Example Shop',
'phone' => '+1 555-0100',
];
return $fixtures;
} );
Values are read as they are: field arguments are ignored.
data/preview_overrides
In the editor preview, global providers show your site's real values. Replace a field's value there, keyed provider.field:
add_filter( 'unblock/data/preview_overrides', function( $overrides ) {
$overrides['shop.phone'] = fn( $context, $args ) => '+1 555-0100';
return $overrides;
} );
data/filters
Register custom expression filters for data transformation.
| Parameter | Type | Description |
|---|---|---|
$filters | array | Registered filters configuration |
Example: Reading Time Filter
The first argument is always the piped value. Arguments passed in the expression follow as an indexed array: $args[0] is the first one, $args[1] the second, etc. Declared in args, an argument can also be given by its name: it reaches the callback at its position, and a skipped one is null:
add_filter( 'unblock/data/filters', function( $filters ) {
$filters['reading_time'] = [
'callback' => fn( $value, $args ) => max( 1, (int) round( str_word_count( strip_tags( $value ) ) / ( $args[0] ?? 200 ) ) ),
'args' => [ [ 'name' => 'words_per_minute', 'type' => 'number', 'default' => 200 ] ],
];
return $filters;
} );
Use in expressions: {{ post.content|reading_time }}, {{ post.content|reading_time(150) }} or {{ post.content|reading_time(words_per_minute: 150) }}
data/functions
Register custom functions callable directly in expressions.
| Parameter | Type | Description |
|---|---|---|
$functions | array | Registered functions configuration |
Example: Current Year Function
Arguments are passed as an indexed array — $args[0] is the first argument, $args[1] the second, etc.:
add_filter( 'unblock/data/functions', function( $functions ) {
$functions['year'] = [
'callback' => fn( $args ) => gmdate( $args[0] ?? 'Y' ),
];
return $functions;
} );
Use in expressions: © {{ year() }} or {{ year('y') }}
Example: Function with Provider Return
Functions that return an entity can declare its provider key in returns, so its fields can be chained:
add_filter( 'unblock/data/functions', function( $functions ) {
$functions['featured_post'] = [
'callback' => function( $args ) {
$id = (int) get_option( 'featured_post_id' );
return $id ? get_post( $id ) : null;
},
'returns' => 'post',
];
return $functions;
} );
Use in expressions: {{ featured_post().title }}
Return the object (get_post(), get_user_by(), get_term()), not its ID: an ID is a plain number and has no fields. returns takes a provider key ('post', 'user', 'term'), not a class name like 'WP_Post'.
data/post_meta
Pre-filter for post meta retrieval. Return a non-null value to override get_metadata().
apply_filters( 'unblock/data/post_meta', null, string $meta_key, int $post_id, array $options );
| Parameter | Type | Description |
|---|---|---|
$value | mixed | Return non-null to override. Default: null |
$meta_key | string | Meta key name |
$post_id | int | Post ID |
$options | array | Additional options from the expression |
This filter receives null as the initial value — not the actual meta value. Return null to let WordPress handle the lookup. Returning any other value skips get_metadata() entirely.
Example: Type Conversion
add_filter( 'unblock/data/post_meta', function( $value, $meta_key, $post_id, $options ) {
if ( $meta_key === 'price' ) {
return floatval( get_post_meta( $post_id, $meta_key, true ) );
}
return null;
}, 10, 4 );
data/user_meta
Pre-filter for user meta retrieval. Return a non-null value to override get_metadata().
apply_filters( 'unblock/data/user_meta', null, string $meta_key, int $user_id, array $options );
| Parameter | Type | Description |
|---|---|---|
$value | mixed | Return non-null to override. Default: null |
$meta_key | string | Meta key name |
$user_id | int | User ID |
$options | array | Additional options from the expression |
Example: Type Conversion
add_filter( 'unblock/data/user_meta', function( $value, $meta_key, $user_id, $options ) {
if ( $meta_key === 'points' ) {
return intval( get_user_meta( $user_id, $meta_key, true ) );
}
return null;
}, 10, 4 );
data/term_meta
Pre-filter for term meta retrieval. Return a non-null value to override get_metadata().
apply_filters( 'unblock/data/term_meta', null, string $meta_key, int $term_id, array $options );
| Parameter | Type | Description |
|---|---|---|
$value | mixed | Return non-null to override. Default: null |
$meta_key | string | Meta key name |
$term_id | int | Term ID |
$options | array | Additional options from the expression |
Example: Fallback Value
add_filter( 'unblock/data/term_meta', function( $value, $meta_key, $term_id, $options ) {
$meta = get_term_meta( $term_id, $meta_key, true );
if ( $meta_key === 'icon_url' && empty( $meta ) ) {
return '/wp-content/themes/flavor/images/default-icon.svg';
}
return $meta ?: null;
}, 10, 4 );
data/option_meta
Pre-filter for option retrieval from the Site provider. Return a non-null value to override get_option().
apply_filters( 'unblock/data/option_meta', null, string $key, array $options );
| Parameter | Type | Description |
|---|---|---|
$value | mixed | Return non-null to override. Default: null |
$key | string | Option key being retrieved |
$options | array | Additional options context |
Example: ACF Options Page
add_filter( 'unblock/data/option_meta', function( $value, $key, $options ) {
if ( function_exists( 'get_field' ) ) {
return get_field( $key, 'option' );
}
return null;
}, 10, 3 );
data/meta_transform_value
Default of the transform_value option of meta() and site.option(), as Timber's timber/meta/transform_value. When true, ACF, Meta Box, Pods and JetEngine relational fields (images, files, galleries, posts, terms, users) come as objects you can chain, and ACF dates as dates. An expression's own {transform_value: false} still wins.
apply_filters( 'unblock/data/meta_transform_value', bool $transform );
| Parameter | Type | Description |
|---|---|---|
$transform | bool | Whether to convert relational fields. Default: false |
Example: convert everywhere
add_filter( 'unblock/data/meta_transform_value', '__return_true' );
data/allowed_options
Whitelist WordPress options accessible via site.option().
| Parameter | Type | Description |
|---|---|---|
$allowed | array | Allowed option names |
Default: ['date_format', 'time_format']
Example: Add Custom Option
add_filter( 'unblock/data/allowed_options', function( $allowed ) {
$allowed[] = 'my_custom_option';
return $allowed;
} );
Example: Theme Settings
add_filter( 'unblock/data/allowed_options', function( $allowed ) {
$allowed[] = 'theme_mods_flavor';
$allowed[] = 'flavor_settings';
$allowed[] = 'social_links';
return $allowed;
} );
Use in expressions: {{ site.option('social_links').twitter }}
data/allowed_meta_keys
Open meta keys that are otherwise private: keys starting with _ (any meta type), and user meta keys, which are private except the names, nickname and bio.
| Parameter | Type | Description |
|---|---|---|
$allowed | array | Allowed meta keys |
$meta_type | string | Meta type: 'post', 'user', or 'term' |
Default: [] (empty: _ keys blocked, user meta keys closed unless opened in Settings > Privacy)
Example: Allow Private Field
add_filter( 'unblock/data/allowed_meta_keys', function( $allowed, $meta_type ) {
if ( $meta_type === 'post' ) {
$allowed[] = '_my_private_field';
}
return $allowed;
}, 10, 2 );
Example: Allow ACF Fields
add_filter( 'unblock/data/allowed_meta_keys', function( $allowed, $meta_type ) {
if ( $meta_type === 'post' ) {
$allowed[] = '_edit_lock';
$allowed[] = '_edit_last';
}
return $allowed;
}, 10, 2 );
Example: Open a User Meta Key
add_filter( 'unblock/data/allowed_meta_keys', function( $allowed, $meta_type ) {
if ( $meta_type === 'user' ) {
$allowed[] = 'job_title';
}
return $allowed;
}, 10, 2 );
data/allowed_field
Decide whether a private field of an object can be read. Receives the answer of Settings > Privacy and has the final word: open a field for some objects only, or close what the settings opened.
| Parameter | Type | Description |
|---|---|---|
$allowed | bool | Whether Settings > Privacy opens the field |
$provider | string | Provider key: 'user', 'site', 'attachment', 'image' |
$field | string | Field name: 'user_email', 'roles', 'admin_email'… |
$object | mixed | The WordPress object the field is read on (WP_User, WP_Post…) |
Example: Open the Email of Editors Only
add_filter( 'unblock/data/allowed_field', function( $allowed, $provider, $field, $user ) {
if ( 'user_email' === $field && in_array( 'editor', $user->roles, true ) ) {
return true;
}
return $allowed;
}, 10, 4 );
data/allowed_user
Decide whether a user can be read at all. By default: the visitor, authors with a published post in a public post type, and members of the public roles (Settings > Privacy).
| Parameter | Type | Description |
|---|---|---|
$allowed | bool | Whether the user is readable by default |
$user_id | int | User ID |
User loops and get_users() already list authors, or the members of the public roles they filter on; this filter then decides each user they return.
Example: Open Members Who Chose to Be Listed
add_filter( 'unblock/data/allowed_user', function( $allowed, $user_id ) {
return $allowed || get_user_meta( $user_id, 'show_in_directory', true );
}, 10, 2 );
data/allowed_post_types
Open post types WordPress shows nowhere to the public (no pages of their own, left out of search): expressions read their published posts and loops list them. By default, the post types opened in Settings > Privacy.
| Parameter | Type | Description |
|---|---|---|
$post_types | string[] | Opened post types |
Drafts and private posts of an opened post type still show only to users who can read them.
Example: Open Testimonials
add_filter( 'unblock/data/allowed_post_types', function( $post_types ) {
$post_types[] = 'testimonial';
return $post_types;
} );
data/allowed_post
Decide whether a post can be read at all, with the last word. By default: a published post of a post type WordPress shows the public (its own pages, or in search) or opened, a post the viewer may read (read_post), and media.
| Parameter | Type | Description |
|---|---|---|
$allowed | bool | Whether the post is readable by default |
$post | WP_Post | Post |
A post that can't be read gives no field. The WooCommerce integration uses it to open a published variation of a readable product.
Example: Open Resources to Logged-in Visitors
add_filter( 'unblock/data/allowed_post', function( $allowed, $post ) {
return $allowed || ( 'resource' === $post->post_type && 'publish' === $post->post_status && is_user_logged_in() );
}, 10, 2 );
Next steps
- Query filters — modify loop query arguments
- Security filters — customize HTML and SVG sanitization
- Actions — plugin lifecycle and form hooks