Skip to content

AJAX endpoints

The plugin exposes four AJAX endpoints. There is no REST API — all data exchange happens over admin-ajax.php with nonce gating.

Backs the Members Views leaderboard table in the admin panel (and the legacy ag-Grid data grid used in pre-1.5.0 builds).

Field Value
Action get_views
Availability Logged-in only; requires manage_options capability
Nonce action bp-member-view-nonce
Nonce parameter security
Localized via bpvAdmin.ajaxNonce (admin JS bundle)
Response JSON array of per-user aggregates

Response shape:

[
{
"name": "Jane Doe (janedoe)",
"weekly": 12,
"monthly": 47,
"yearly": 310
}
]

Each object in the array represents one member who has been viewed. name is the display name and login concatenated (display_name (user_login)). weekly, monthly, and yearly are view counts for the last 7 days, last 30 days, and last 365 days respectively.

fetch( ajaxurl, {
method: 'POST',
body: new URLSearchParams({
action: 'get_views',
security: bpvAdmin.ajaxNonce,
}),
} ).then( r => r.json() ).then( console.log );

wp_ajax_bp_profile_views_load_chart_widget

Section titled “wp_ajax_bp_profile_views_load_chart_widget”

Backs the chart on the front-end Views sub-tab. Returns aggregate view counts grouped by date for the requested user and time filter.

Field Value
Action bp_profile_views_load_chart_widget
Availability Logged-in only; the user_id in the request must equal get_current_user_id()
Nonce action ajax-nonce
Nonce parameter nonce
Localized via bp_profile_views_object.ajax_nonce (public JS bundle)
Method POST
Required parameters user_id, filter_by, nonce
Response JSON object with keys, values, and total

Response shape:

{
"keys": ["Jun 1, 26", "Jun 2, 26", "Jun 3, 26"],
"values": [3, 7, 2],
"total": 12
}

keys and values are parallel arrays consumable directly by Chart.js. total is the row count for the selected period.

Value Date filter applied
all No date constraint
7 created BETWEEN {today-6 days} AND {today} 23:59:59
30 created BETWEEN {today-29 days} AND {today} 23:59:59

The front-end dropdown sends numeric day-count strings ('7', '30') for windowed views, and 'all' for the all-time view. The handler converts the numeric string to a date range using strtotime.

Requesting user_id for a different user than the caller returns a 403 error response. Omitting nonce or sending an invalid nonce also returns 403.

wp_ajax_bp_profile_views_toggle_visitor_setting

Section titled “wp_ajax_bp_profile_views_toggle_visitor_setting”

Lets a logged-in member toggle their own tracking opt-out without a page reload. This endpoint is only wired when allow_user_settings is 'yes' in General settings.

Field Value
Action bp_profile_views_toggle_visitor_setting
Availability Logged-in only (wp_ajax_ only — no wp_ajax_nopriv_ variant)
Nonce action bp-profile-views-member-toggle
Nonce parameter nonce
Method POST
Required parameters nonce, enabled ('1' = allow tracking, '0' = opt out)
Response {"success": true, "data": {"enabled": true}}

The endpoint always writes to get_current_user_id()’s own visitor_setting user meta. It never accepts a user_id parameter, so the update target cannot be manipulated via the request body.

const form = new FormData();
form.append( 'action', 'bp_profile_views_toggle_visitor_setting' );
form.append( 'nonce', myNonce );
form.append( 'enabled', '0' ); // '0' = opt out, '1' = allow tracking
fetch( '/wp-admin/admin-ajax.php', {
method: 'POST',
credentials: 'same-origin',
body: form,
} ).then( r => r.json() ).then( res => {
if ( res.success ) {
console.log( 'Preference saved, enabled:', res.data.enabled );
}
} );

Backs the one-click install buttons on the Discover tab. Added in 1.5.3.

Field Value
Action bpv_install_plugin
Availability Logged-in only; requires the install_plugins capability
Nonce action bpv_install_plugin
Nonce parameter nonce
Localized via bpvAdmin.installNonce (admin JS bundle)
Handler Bpv_Plugin_Installer::ajax_install_plugin()

Request body:

action=bpv_install_plugin
slug=jetonomy
nonce=<bpvAdmin.installNonce>

Success response:

{ "success": true, "data": { "message": "Jetonomy installed and activated.", "status": "active" } }

Installing a plugin is remote code execution, so this endpoint is deliberately narrow. Three properties hold, and none of them should be relaxed:

  1. Slug-only input against a fixed catalog. The request sends a slug, never a URL or a path. The handler resolves it with get( $slug ) against the hard-coded catalog and returns 404 Unknown plugin. for anything else. There is no code path by which a request can name its own package URL.
  2. Capability checked twice — in the AJAX handler and again inside install(), so the check holds regardless of caller. The nonce is CSRF protection, not authorisation.
  3. Package host pinned. The download URL the store returns is verified to be on wbcomdesigns.com or a subdomain before it is handed to Plugin_Upgrader, so a redirect or a compromised response cannot pull a package from anywhere else.
Condition HTTP Message
Bad or missing nonce 403 -1 (WordPress default)
No install_plugins capability 403 You do not have permission to install plugins.
Slug not in the catalog 404 Unknown plugin.
Store unreachable 200 success: false with a “could not reach the store” message
Store declined the download 200 success: false including the store’s HTTP status and reason
Filesystem not writable 200 success: false asking you to install from the Plugins screen

Already-active products short-circuit to true without a store call. Installed-but-inactive products are activated without re-downloading.

The catalog is filterable — see bpv_install_catalog. Entries added through the filter get the same treatment: a slug not present in the final catalog is still rejected.

The plugin does not register any custom REST namespace. If you need to expose view data over REST, write a thin custom controller against {prefix}bp_profile_views and gate it behind an appropriate permission callback:

add_action( 'rest_api_init', function () {
register_rest_route( 'bpv/v1', '/views/(?P<user_id>\d+)', array(
'methods' => 'GET',
'permission_callback' => fn() => current_user_can( 'read' ),
'callback' => function ( $request ) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT viewer_id, created
FROM {$wpdb->prefix}bp_profile_views
WHERE user_id = %d
ORDER BY created DESC
LIMIT 50",
(int) $request['user_id']
)
);
},
) );
} );

This is not part of the plugin’s stable surface — adapt to your own authorization and data-shape requirements.