User Post Type
Create filterable user listings
This plugin lets you connect users to a post type, so that you can create user listings, and filter users like any other post type.
How it works
FacetWP can only index and filter posts (of any (custom) post type). However, WP users are not a post type, so users cannot be filtered directly. To make users filterable by FacetWP, this add-on creates a new (hidden) upt_user
custom post type, and adds a post for each WP user.
The add-on’s settings also let you sync user data to the new upt_user
post type. This data can then be used when setting up facets that can filter users, or to customize user queries with a meta_query
. The synced user data consists of the default user data that is available by for each WP user, but also custom (user) fields that are added with other plugins. The add-on supports custom fields made with BuddyPress and Advanced Custom Fields. See this example to learn how to add custom user fields with Advanced Custom Fields.
Despite the upt_user
custom post type not being directly visible in the WP admin menu, you can still access it to see and check all synced users and their custom fields.
Getting started
Download
Download the add-on from your account and install it as a plugin.
Setup
Here is a short video on how to set up the add-on:

After installing and activating the add-on, browse to Settings > User Post Type
and click the “Sync now” button.
As seen in the image on the right, you can also copy extra custom user fields (other than the default user fields) into the wp_postmeta
table. This is not required to display that custom field data or to use those custom fields as a facet’s data source. Syncing extra custom user fields is only useful/needed if you use these fields in a meta_query
in a custom WP_Query
, as demonstrated in the examples below.
Here’s a list of dropdown choices and corresponding meta_query
keys:
Option | Meta Key |
---|---|
User ID | ID |
User Login | user_login |
User Email | user_email |
User URL | user_url |
Registration Date | user_registered |
User Status | user_status |
Display Name | display_name |
Roles | roles |
Usermeta field | meta-[fieldname] Field keys are prefixed with meta- when synced. So if you make a custom user field with Advanced Custom Field, the field name to use in a custom meta query is meta-[fieldname] . See the example below. |
BuddyPress field | bp-[fieldID] Field IDs are prefixed with bp- when synced. |
How to see and check synced users and custom fields
The hidden upt_user
custom post type that this add-on generates is not directly visible in the WP admin menu, but you can still access its overview screen by using this URL:
https://yourdomain.com/wp-admin/edit.php?post_type=upt_user

/user-x/
URL in the browser’s status bar.In this UPT Users overview screen, you can see all synced users. These upt_user
posts have a post URL ending in /user-x/
, where x
corresponds with the WP user ID. If needed, you can use this ID to check for discrepancies after syncing. This post URL can be seen by hovering over the “View” link under the post item, as shown in the image on the right.
If you click the “Edit” link of a upt_user
post/user, you can also see all synced custom fields and their values.
If the Custom Fields metabox does not show up, you probably have Advanced Custom Fields installed, which by default hides this metabox. To enable the metabox, add the following ACF hook to your (child) theme’s functions.php:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'acf/settings/remove_wp_meta_box', function( $remove ) { if ( ! is_admin() || ! function_exists( 'get_current_screen' ) ) { return $remove; } $screen = get_current_screen(); if ( $screen->base === 'post' && $screen->post_type === 'upt_user' ) { return false; // Re-enable the Custom Fields metabox } return $remove; }, 20 );
Query all users
To display a list of all users, use WP_Query
(not WP_User_Query
) like so:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
$args = [ 'post_type' => 'upt_user', 'post_status' => 'publish', 'posts_per_page' => 10, ]; $query = new WP_Query( $args );
You can also use a Listing Builder listing, or for more elaborate queries, a Listing Builder listing in Dev mode, as shown below.
Query a subset of users
To query a subset of users, you need to create a custom WP_Query
that uses meta_query
to filter by existing user fields or by custom user fields.
Query users by existing user fields with a meta query
To display only administrators, select “Roles” in the settings page dropdown, then click the blue “Sync now” button. Then you can use the roles
field within meta_query
:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
$args = [ 'post_type' => 'upt_user', 'post_status' => 'publish', 'orderby' => [ 'title' => 'ASC' ], 'posts_per_page' => 30, 'meta_query' => [ [ 'key' => 'roles', 'value' => 'administrator', 'compare' => '=', ] ] ]; $query = new WP_Query( $args );
To do same with multiple roles, in this example administrators and editors:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
$args = [ 'post_type' => 'upt_user', 'post_status' => 'publish', 'orderby' => [ 'title' => 'ASC' ], 'posts_per_page' => 30, 'meta_query' => [ [ 'key' => 'roles', 'value' => [ 'administrator', 'editor' ], 'compare' => 'IN', ] ] ]; $query = new WP_Query( $args );
Query users by one or more custom user fields with a meta query
Say you want to give users a custom status field, and you want to display a user listing that excludes users with a specific status.
To accomplish this, let’s first create the custom status field and attach it to WP’s user profile page.
Using Advanced Custom Fields, create a field group for your user field(s) and set its “Location Rules” to “User Form -> is equal to -> All”, as shown in the image below. Next, create a new custom field in this field group, e.g. my_user_status
. You can add more fields later.

Next, go to Settings > User Post Type, and in the dropdown select the field name my_user_status
. You’ll also see one prefixed with an underscore (_my_user_status
), which you can ignore. Click Save, then click the blue “Sync now” button.
After syncing, the new custom field is now available to use in a meta_query
argument if you query the upt_user
post type, as demonstrated below. Note that you need to make sure all WP users are saved after adding the new field, because otherwise they will not have the field in their postmeta and querying by it will not work as expected.
Now everything is ready to create a user listing query that uses the new user status field. In the following example, we’ll use the Listing Builder in Dev mode, but you can do this in any other listing template type.
Create a new Listing Builder listing and set the “Display” tab to display whatever user data you want. For testing, just use “Display Name” or “Post Title” (which both output the user name). In the “Query” tab, enable “Dev mode” in the top right, and paste the following query arguments:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php return [ "post_type" => ["upt_user"], "post_status" => ["publish"], "meta_query" => [ "relation" => "AND", // State the relation if you use multiple meta queries [ "key" => "roles", // Roles is an existing WP field "compare" => "NOT IN", // Field EXISTS and is NOT IN ... "value" => ["practitioner"], // array of values "type" => "CHAR", ], [ "key" => "meta-my_user_status", // IMPORTANT: Use meta-[fieldname] and make sure the field is synced in UPT settings "compare" => "NOT IN", // Field EXISTS and is NOT IN ... "value" => ["expired"], // array of values "type" => "CHAR", ] ], "orderby" => "meta_value", // Order by the meta key specified in 'meta_key' "meta_key" => "display_name", // The meta key to sort by "order" => "ASC", "posts_per_page" => 20 ];
In the above example we retrieve upt_user
posts that do NOT
have the practioner
role AND for which the my_user_status
field the value is NOT
expired
. We order the listing by the user display_name
, in ascending order (A-Z), and retrieve 20
posts per page.
Important to notice is that in line 14 we use meta-my_user_status
, and not my_user_status
. This is because when syncing WP user fields to the upt_user
post type, all ACF custom fields are prefixed with meta-
.
Display user fields
UPT supports WP’s get_user_meta() function. Just replace $user_id
with UPT()->get_user_id()
:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
echo get_user_meta( UPT()->get_user_id(), 'your_field_name', true );
Outside of The Loop, pass in the post ID like so:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
echo get_user_meta( UPT()->get_user_id( THE_POST_ID ), 'your_field_name', true );
To get a user’s photo, you can use WP’s get_avatar() function, assuming that the image is stored in WP’s default “Profile Picture” field:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
echo get_avatar( UPT()->get_user_id( THE_POST_ID ), 150 );
Add facets that filter by a user field
When creating a new facet, via Settings > FacetWP
, you’ll see a new “User Fields” section in the Data source dropdown.
Finding a user’s associated post
Use the following if you have a $user_id
and want to find the $post_id
of the associated post:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
$post_id = (int) get_user_meta( $user_id, UPT()->meta_key, true );
Prevent a specific user from being synced
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_sync_skip_user', function( $bool, $user_id ) { if ( 1 === $user_id ) { return true; } return $bool; }, 10, 2 );
Prevent users with specific roles from being synced
To prevent users with a specific role from being synced, you can use the following snippet. Change the roles to be skipped in the array in line 3.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_sync_skip_user', function( $bool, $user_id ) { $roles = (array) get_user_by( 'ID', $user_id )->roles; $roles_to_skip = [ 'dormant-student', 'dormant-teacher' ]; // Skip these roles foreach ( $roles_to_skip as $role ) { if ( in_array( $role, $roles ) ) { return true; // Do not sync this user } } return $bool; }, 10, 2 );
This also works for custom user roles made with a plugin, like for example User Role Editor.
You can also do the opposite:
Only sync users with a specific role
To sync only users with a specific role, you can use the following snippet. Change the roles to be synced in the array in line 3.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_sync_skip_user', function( $bool, $user_id ) { $roles = (array) get_user_by( 'ID', $user_id )->roles; $roles_to_preserve = [ 'student','teacher' ]; // Sync only these roles $intersect = array_intersect( $roles, $roles_to_preserve ); if ( empty( $intersect ) ) { return true; // Do not sync this user } return $bool; }, 10, 2 );
Perform post-sync actions
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_action( 'upt_sync_post', function( $post_id, $user_id ) { // do something after a user is synced }, 10, 2 );
Force only certain users to be indexed
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_sync_where', function( $where ) { $where = ' AND u.ID IN (1, 2, 3, 4, 5)'; return $where; });
Force a user sync on a schedule
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_action( 'mysite_sync_users', 'mysite_sync_users_callback' ); function mysite_sync_users_callback() { UPT()->sync->run_sync(); } if ( ! wp_next_scheduled( 'mysite_sync_users' ) ) { wp_schedule_single_event( time() + 28800, 'mysite_sync_users' ); // 28800 seconds = every 8 hours }
Force a single user sync
To manually sync a single user, use this in your code or hook:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
UPT()->sync->run_sync( $user_id );
Note that users are already synced automatically when their profile is updated from the standard wp-admin user profile editor.
Updating the profile triggers WordPress’ profile_update action hook that the User Post Type add-on uses to trigger the sync. Manual syncing would only be needed if updating a user’s profile is done in a way that does not trigger this hook.
Customize register_post_type arguments for the upt_user post type
You can use the upt_post_type_args
hook to customize any arguments of the register_post_type function, when the upt_user
post type is registered.
Two examples:
Force the admin menu to appear
To force the “UPT Users” admin menu to appear:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_post_type_args', function( $args ) { $args['show_in_menu'] = true; return $args; });
Prevent single upt_user posts from being visible
To prevent the single posts from the upt_user
post type from being accessed on the front-end (via for example https://yourdomain.com/upt_user/user-1/
):
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'upt_post_type_args', function( $args ) { $args['publicly_queryable'] = false; return $args; });
Customize access to the settings
If you need to give certain user roles access to the User Post Type add-on settings, you can use the upt_admin_settings_capability hook.
To change access to the main FacetWP plugin settings, and other add-ons, you can use the facetwp_admin_settings_capability hook.
Changelog
0.8.1Dec 16, 2024
- New added `upt_admin_settings_capability` hook
0.8Aug 19, 2024
- Fixed adjust sync priority to improve 3rd party compatibility
0.7.8Feb 26, 2024
- Fixed PHP 8.2 deprecation notices
- Fixed potential name conflict when re-indexing
0.7.7Feb 23, 2022
- Fixed issue preventing value of "0" from getting indexed
0.7.6Nov 2, 2021
- Important Go into `Settings > User Post Type` and hit the "Sync" button
- Fixed issue causing a new meta row to get added on each user save
0.7.5Sep 10, 2021
- Fixed sync issues (prevents needing to manually re-sync in many cases)
0.7.4Jun 8, 2021
- New added `upt_sync_where` hook to customize the SQL WHERE clause
0.7.3Oct 29, 2020
- New added multisite support (only sync users of the current site)