Hooks & Filters Reference
This document lists every action hook and filter the plugin exposes for extension. Each entry has been verified against the live code in includes/, public/, and bp-stats.php as of 2.0.1. Fired at line numbers are a navigation aid and move between releases; the file path is the stable part.
2.0.0 note: the entire
wp_ajax_*surface is gone. Any filter or action that used to fire from abp_stats_*_callbackAJAX handler is documented here only if it still fires from the REST controller, service, template, or public class that replaced it. The activity-log display filters (bp_stats_get_user_display,bp_stats_topic_badge, etc.) still apply because the row formatter was inlined intoBp_Stats_REST_Activity_Log::format_datatable_row().
Theme Compatibility Hooks (since 2.0.0)
Section titled “Theme Compatibility Hooks (since 2.0.0)”These three hooks let a child theme or companion plugin teach BP Stats how to read native to a new theme.
bp_stats_theme_compat_map (filter)
Section titled “bp_stats_theme_compat_map (filter)”Map an active WordPress template slug to a CSS compat handle. The handle is then registered via wp_register_style() and enqueued when that template is active.
Signature: apply_filters( 'bp_stats_theme_compat_map', array $map )
Args:
$map(array<string,string>) -template_slug => handlepairs. Default ships:'buddyx'→'bp-stats-theme-buddyx-family''buddyx-pro'→'bp-stats-theme-buddyx-family'
Since: 2.0.0
Fired at: public/class-bp-stats-public.php:167
add_filter( 'bp_stats_theme_compat_map', function ( $map ) { $map['reign'] = 'bp-stats-theme-reign'; return $map;} );The handle bp-stats-theme-reign will be registered with a URL that resolves to public/css/themes/bp-stats-theme-reign.css inside this plugin. To point it at a different file (e.g. ship the stylesheet from a companion plugin), use bp_stats_theme_compat_url.
bp_stats_theme_compat_url (filter)
Section titled “bp_stats_theme_compat_url (filter)”Replace the URL of the stylesheet that ships for a known compat handle.
Signature: apply_filters( 'bp_stats_theme_compat_url', string $url, string $handle )
Args:
$url(string) - Default URL:public/css/themes/{$handle}.cssinside this plugin.$handle(string) - The compat handle being registered.
Since: 2.0.0
Fired at: public/class-bp-stats-public.php:189
add_filter( 'bp_stats_theme_compat_url', function ( $url, $handle ) { if ( 'bp-stats-theme-reign' === $handle ) { return plugin_dir_url( __FILE__ ) . 'assets/css/my-reign-overrides.css'; } return $url;}, 10, 2 );bp_stats_after_theme_compat_enqueue (action)
Section titled “bp_stats_after_theme_compat_enqueue (action)”Fires immediately after a theme compat stylesheet has been enqueued. Use it to enqueue companion assets (per-theme JS tweaks, font preloads, accent JSON, etc.).
Signature: do_action( 'bp_stats_after_theme_compat_enqueue', string $active_template, string $handle )
Args:
$active_template(string) - Value ofget_template()at enqueue time.$handle(string) - The compat handle that was just enqueued.
Since: 2.0.0
Fired at: public/class-bp-stats-public.php:215
add_action( 'bp_stats_after_theme_compat_enqueue', function ( $template, $handle ) { if ( 'bp-stats-theme-reign' === $handle ) { wp_enqueue_script( 'my-reign-bp-stats-tweaks', plugin_dir_url( __FILE__ ) . 'assets/js/reign-tweaks.js', array( 'jquery' ), '1.0.0', true ); }}, 10, 2 );Core Settings
Section titled “Core Settings”bp_stats_general_default_settings (filter)
Section titled “bp_stats_general_default_settings (filter)”Modify the default settings shipped by the plugin (read by get_option( 'bp_stats_general_settings', $default )).
Fired at: bp-stats.php:624
add_filter( 'bp_stats_general_default_settings', function ( $defaults ) { $defaults['show_member_stats'] = 'no'; $defaults['log_retention_days'] = 180; $defaults['anonymize_ip_addresses'] = 'yes'; return $defaults;} );Default array (see bp_stats_general_default_settings() in bp-stats.php):
| Key | Default | Notes |
|---|---|---|
show_member_stats |
'yes' |
Master toggle for the Profile Statistics tab |
show_user_roles |
all WP roles except administrator |
Roles whose stats can be viewed |
show_profile_*_report |
'yes' |
Per-section toggles on the profile page |
show_group_stats |
'yes' |
Master toggle for the Group Statistics tab |
show_group_roles |
['admin','mod','member'] |
Which group roles can view group stats |
show_group_privacy |
['public','private','hidden'] |
Group visibilities that expose stats |
show_group_*_report |
'yes' |
Per-section toggles on the group page |
log_retention_days |
90 |
Used by bp_stats_cleanup_old_logs cron |
anonymize_ip_addresses |
'yes' |
GDPR - last octet → 0 (IPv4), last 80 bits → 0 (IPv6) |
bp_stats_can_view_user_stats (filter)
Section titled “bp_stats_can_view_user_stats (filter)”Control who can view a target user’s statistics. Called both by the page renderer and by permission_member on REST /profile/* routes.
Fired at: bp-stats.php:531
// Let friends of the displayed member see their stats.add_filter( 'bp_stats_can_view_user_stats', function ( $can_view, $user_id ) { if ( function_exists( 'friends_check_friendship' ) && friends_check_friendship( get_current_user_id(), $user_id ) ) { return true; } return $can_view;}, 10, 2 );Args:
$can_view(bool) - Alwaysfalsewhen passed in. The filter only runs afterbp_stats_can_view_user_stats()has already returnedtruefor administrators and for a member viewing their own stats, andfalsewhen the displayed user’s role is not inshow_user_roles. It decides the remaining case: another logged-in member viewing an allowed profile.$user_id(int) - User ID being viewed.
bp_stats_get_user_ip (filter)
Section titled “bp_stats_get_user_ip (filter)”Filter the IP address before storing or returning. Useful for additional anonymization or proxy normalization.
Fired at: bp-stats.php:481
add_filter( 'bp_stats_get_user_ip', function ( $ip ) { // Drop two octets instead of one return preg_replace( '/\.\d+\.\d+$/', '.0.0', $ip );} );Profile and Group Tabs
Section titled “Profile and Group Tabs”These fire from Bp_Stats_Public when the BP navigation is being set up.
bp_stats_statistics_label (filter)
Section titled “bp_stats_statistics_label (filter)”Label for the Statistics tab on member profiles. Default: 'Statistics'.
Fired at: public/class-bp-stats-public.php:2709
add_filter( 'bp_stats_statistics_label', fn () => 'My Activity' );bp_stats_statistics_slug (filter)
Section titled “bp_stats_statistics_slug (filter)”URL slug for the Statistics tab. Default: 'statistics'.
Fired at: public/class-bp-stats-public.php:2710
add_filter( 'bp_stats_statistics_slug', fn () => 'my-activity' );bp_stats_profile_statistics_tab_filter (filter)
Section titled “bp_stats_profile_statistics_tab_filter (filter)”Position of the Statistics tab on member profiles. Default: 80.
Fired at: public/class-bp-stats-public.php:2724
Since 2.0.1 the profile tab is added whenever show_member_stats is yes and the viewer passes bp_stats_can_view_user_stats(), even if every per-chart show_profile_*_report toggle is off: the Impact, Streak and Journey sections still render.
add_filter( 'bp_stats_profile_statistics_tab_filter', fn () => 50 );bp_stats_show_group_stats_tab (filter)
Section titled “bp_stats_show_group_stats_tab (filter)”Show or hide the Statistics tab on group pages.
Fired at: public/class-bp-stats-public.php:2807
add_filter( 'bp_stats_show_group_stats_tab', function ( $show ) { return is_user_logged_in() ? $show : false;} );bp_stats_group_statistics_tab_filter (filter)
Section titled “bp_stats_group_statistics_tab_filter (filter)”Position of the Statistics tab on group pages. Default: 80.
Fired at: public/class-bp-stats-public.php:2822
Activity Tracking
Section titled “Activity Tracking”bp_stats_activity_types_insert (filter)
Section titled “bp_stats_activity_types_insert (filter)”Which activity types get logged when a BP activity is created.
Fired at: public/class-bp-stats-public.php:1328
add_filter( 'bp_stats_activity_types_insert', function ( $types, $args ) { $types[] = 'new_blog_post'; return $types;}, 10, 2 );Default: [ 'activity_update', 'activity_comment', 'activity_status' ]
bp_stats_activity_types_deleted (filter)
Section titled “bp_stats_activity_types_deleted (filter)”Mirror of the above for deletion events.
Fired at: public/class-bp-stats-public.php:1441
bp_stats_max_top_contributors (filter)
Section titled “bp_stats_max_top_contributors (filter)”Cap on the size of the top-contributors collection. Default: 10.
Fired at: public/class-bp-stats-public.php:3738
Analytics Scope
Section titled “Analytics Scope”bp_stats_user_scope_ids (filter - since 2.0.1)
Section titled “bp_stats_user_scope_ids (filter - since 2.0.1)”Restrict every admin analytics query to a subset of members, for example when a companion plugin gives a group leader a dashboard for their own members only.
| Return | Effect |
|---|---|
null or [] (default) |
No restriction, all members counted |
| Array of user IDs | Queries add user_id IN (...) (sender_id for messages, the term relationship object_id for member types); scoped results use their own cache keys |
Non-empty array with no valid ID (e.g. [0], ['abc']) |
Scoped to nobody, charts show zero rather than falling back to all members |
Parameters: $scope_ids (int[]|null), $context (string) - one of engagement, engagement_trends, user_activity, top_contributors, user_health_segments, monthly_retention, member_type_chart, xprofile_field_chart, xprofile_multi_field_chart, daily_active, top_contributors_inline.
add_filter( 'bp_stats_user_scope_ids', function ( $scope_ids, $context ) { if ( current_user_can( 'manage_options' ) ) { return $scope_ids; } return my_plugin_member_ids_for_current_leader();}, 10, 2 );Not yet applied to the Overview summary cards, Pulse, the Users table or the Activity Log.
Resolved by: bp_stats_get_user_scope_ids() in bp-stats.php
User Information Limits
Section titled “User Information Limits”These tune how many items appear in the per-user information drawer (Users tab → row click). All read by the partial includes/admin/partials/bp-stats-user-information.php.
| Filter | Default | Purpose |
|---|---|---|
bp_stats_user_friends_limit |
5 | Friends list size |
bp_stats_user_activity_update_limit |
5 | Recent activity updates |
bp_stats_user_create_group_limit |
5 | Groups created |
bp_stats_user_join_group_limit |
5 | Groups joined |
bp_stats_user_posts_limit |
5 | Blog posts |
add_filter( 'bp_stats_user_activity_update_limit', fn () => 10 );Data Management
Section titled “Data Management”bp_stats_days_to_keep_logs (filter)
Section titled “bp_stats_days_to_keep_logs (filter)”Override the retention setting at runtime. Default: the log_retention_days setting (90); the optimizer enforces a 7-day minimum before the filter runs.
Fired at: BP_Stats_DB_Optimizer::bp_stats_cleanup_old_logs() (includes/class-bp-stats-db-optimizer.php:96) and Bp_Stats_Deactivator (includes/class-bp-stats-deactivator.php:98, default 90)
add_filter( 'bp_stats_days_to_keep_logs', fn () => 180 );bp_stats_clean_logs_on_deactivate (filter)
Section titled “bp_stats_clean_logs_on_deactivate (filter)”bp_stats_clean_options_on_deactivate (filter)
Section titled “bp_stats_clean_options_on_deactivate (filter)”bp_stats_clean_user_meta_on_deactivate (filter)
Section titled “bp_stats_clean_user_meta_on_deactivate (filter)”Opt in to purging data when the plugin is deactivated. All default to false.
Fired at: Bp_Stats_Deactivator
add_filter( 'bp_stats_clean_logs_on_deactivate', '__return_true' );bp_stats_cache_warming_queries (filter)
Section titled “bp_stats_cache_warming_queries (filter)”Customise which queries the hourly warm-cache cron populates.
Fired at: Bp_Stats_Cache::get_cache_warming_queries() (includes/class-stats-cache-handler.php:563)
Activity Log Display
Section titled “Activity Log Display”These fire from Bp_Stats_REST_Activity_Log::format_datatable_row() (inlined from the legacy activity-log class in Phase B+).
| Filter | Args | Where |
|---|---|---|
bp_stats_get_datatable_data |
$row_data (array) |
Final row payload - last chance to munge before send. class-bp-stats-rest-activity-log.php:359 |
bp_stats_get_user_display |
$user_display (string), $log (object) |
Initial value before the default renderer runs. :371 |
bp_stats_user_display |
$user_display (string) |
Final rendered HTML for the user column. :373, :377, :397 |
bp_stats_topic_badge |
$badge_html (string) |
Final rendered topic badge. :419 |
bp_stats_topic_style |
$style (string), $topic (string) |
Per-topic inline style. :444 |
bp_stats_topic_colors |
$topic_colors (array) |
The base color map used to derive $style. :477 |
bp_stats_format_ip_address |
$ip (string) |
IP before display. :490 |
bp_stats_format_ip_address_display |
$ip (string) |
Final IP string after format_ip_address. :499 |
bp_stats_format_meta_data |
$meta (mixed) |
Decoded meta value. :515 |
add_filter( 'bp_stats_topic_colors', function ( $colors ) { $colors['custom_topic'] = '#8B5CF6'; return $colors;} );
add_filter( 'bp_stats_format_ip_address', function ( $ip ) { return preg_replace( '/\.\d+\.\d+$/', '.xxx.xxx', $ip );} );REST Layer
Section titled “REST Layer”bp_stats_rest_search_users (filter - since 2.0.0)
Section titled “bp_stats_rest_search_users (filter - since 2.0.0)”Filter the result set returned by GET /bp-stats/v1/admin/users/search.
Signature: apply_filters( 'bp_stats_rest_search_users', array $results, string $query )
Fired at: includes/rest/class-bp-stats-rest-admin.php:591
add_filter( 'bp_stats_rest_search_users', function ( $results, $query ) { // Trim out users that fail your own visibility check return array_values( array_filter( $results, fn ( $u ) => my_user_is_visible( $u['id'] ) ) );}, 10, 2 );Action Hooks
Section titled “Action Hooks”bp_stats_before_format_activity_log (action)
Section titled “bp_stats_before_format_activity_log (action)”Fires immediately before an activity log row is formatted.
Args: $log (object)
Fired at: includes/rest/class-bp-stats-rest-activity-log.php:321
add_action( 'bp_stats_before_format_activity_log', function ( $log ) { // pre-processing hook} );bp_stats_after_format_activity_log (action)
Section titled “bp_stats_after_format_activity_log (action)”Fires immediately after a row has been formatted.
Args: $row_data (array - the final row payload)
Fired at: includes/rest/class-bp-stats-rest-activity-log.php:357
bp_stats_cache_warmed (action)
Section titled “bp_stats_cache_warmed (action)”Fires at the end of the hourly warm-cache cron pass.
Args: $results (array - warmed, skipped and errors counts)
Fired at: Bp_Stats_Cache::warm_cache() (includes/class-stats-cache-handler.php:505)
add_action( 'bp_stats_cache_warmed', function ( $results ) { error_log( 'BP Stats: hourly cache warm complete' );} );bp_stats_after_theme_compat_enqueue (action - since 2.0.0)
Section titled “bp_stats_after_theme_compat_enqueue (action - since 2.0.0)”Documented in Theme Compatibility Hooks above.
Removed in 2.0.0
Section titled “Removed in 2.0.0”The hooks below used to exist as part of the 1.8.0 AJAX surface. They no longer fire because the handlers they lived on are gone. Any filter still listed above is verified against the live code.
- All
bp_stats_*_callbackAJAX handlers - replaced by the REST surface. The plugin no longer registers anywp_ajax_*action. bp_stats_ajaxnonce action - replaced by the standardX-WP-Nonce(actionwp_rest).wbcom_admin_setting_headershortcode + the twowp_ajax_wbcom_*actions - removed with theadmin/wbcom/wrapper.
If you have a 1.8.0 integration that hooked into a removed filter, the most common landing pad is one of:
| Old (gone) | New |
|---|---|
bp_stats_json_search_users filter |
bp_stats_rest_search_users (since 2.0.0) |
Inline wp_ajax_* permission filters |
The permission_callback chain in Bp_Stats_REST_Controller (override the cap check via user_has_cap) |
Activity-log row filters (bp_stats_get_datatable_data etc.) |
Still apply - the row formatter was inlined into the REST controller |

