AJAX endpoints
The plugin exposes four AJAX endpoints. There is no REST API — all data exchange happens over admin-ajax.php with nonce gating.
wp_ajax_get_views
Section titled “wp_ajax_get_views”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.
filter_by values
Section titled “filter_by values”| 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 ); }} );wp_ajax_bpv_install_plugin
Section titled “wp_ajax_bpv_install_plugin”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_pluginslug=jetonomynonce=<bpvAdmin.installNonce>Success response:
{ "success": true, "data": { "message": "Jetonomy installed and activated.", "status": "active" } }Security model
Section titled “Security model”Installing a plugin is remote code execution, so this endpoint is deliberately narrow. Three properties hold, and none of them should be relaxed:
- 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 returns404 Unknown plugin.for anything else. There is no code path by which a request can name its own package URL. - 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. - Package host pinned. The download URL the store returns is verified to be on
wbcomdesigns.comor a subdomain before it is handed toPlugin_Upgrader, so a redirect or a compromised response cannot pull a package from anywhere else.
Error responses
Section titled “Error responses”| 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.
Extending the catalog
Section titled “Extending the catalog”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.
REST API
Section titled “REST API”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.
Where to next
Section titled “Where to next”- Database schema — the underlying table.
- Hooks and filters — extend on insert.

