Untitled Session
2026-05-28 16:06 UTC · 37 messages · cli
You2026-05-28 16:07 UTC
Find how BuddyPress handles activity posting via AJAX — specifically the wp_ajax handler, the PHP function that processes the post, and how it inserts into the database. I need the full function code and the add_action registration.
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 0}
Tool2026-05-28 16:07 UTC
{"total_count": 0}
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 20, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-core/bp-core-avatars.php", "line": 944, "content": "add_action( 'wp_ajax_bp_avatar_delete', 'bp_avatar_ajax_delete' );"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/w...
Tool2026-05-28 16:07 UTC
{"total_count": 1, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity-admin.php", "line": 137, "content": "add_action( 'wp_ajax_bp-activity-admin-reply', 'bp_activity_admin_reply' );"}]}
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 1, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity-admin.php", "line": 137, "content": "add_action( 'wp_ajax_bp-activity-admin-reply', 'bp_activity_admin_reply' );"}]}
Tool2026-05-28 16:07 UTC
{"total_count": 15, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity-functions.php", "line": 2153, "content": "function bp_activity_post_update( $args = '' ) {"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-conte...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 2, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/classes/class-bp-akismet.php", "line": 164, "content": "\t * default AJAX implementation; see bp_dtheme_post_update()."}, {"path": "/var/lib/docker/volumes/staging_wp_data...
Tool2026-05-28 16:07 UTC
{"total_count": 3, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-legacy/buddypress-functions.php", "line": 185, "content": "\t\t * The \"wp_ajax_\" action is used for logged in users, and \"wp_ajax_nopriv_\""}, {"path": "/var/lib/do...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"content": " 160|\t\t\t'post_update' => 'bp_legacy_theme_post_update',\n 161|\t\t\t'bp_spam_activity' => 'bp_legacy_theme_spam_activity',\n 162|\t\t\t'bp_spam_activity_comment' => 'bp_legacy_theme_spam_activity',\n 163|\n 164|\t\t\t// Groups.\n 165|\t\t\t'g...
Tool2026-05-28 16:07 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Activity: Post action\n 4| *\n 5| * @package BuddyPress\n 6| * @subpackage ActivityActions\n 7| * @since 3.0.0\n 8| */\n 9|\n 10|/**\n 11| * Post user/group activity update.\n 12| *\n 13| * @since 1.2.0\n 14| *\n...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 1, "files": ["/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-legacy/buddypress-functions.php"]}
Tool2026-05-28 16:07 UTC
{"total_count": 5, "files": ["/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-nouveau/js/buddypress-activity-post-form.min.js", "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-nouveau/js/buddypress-activity-post...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 7, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-legacy/buddypress-functions.php", "line": 987, "content": " *"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-...
Tool2026-05-28 16:07 UTC
{"total_count": 39, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-nouveau/includes/activity/ajax.php", "line": 60, "content": "\t\t\t),"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-temp...
Donna2026-05-28 16:07 UTC
Let me read both the legacy and nouveau AJAX handlers in full.
Tool2026-05-28 16:07 UTC
{"content": " 985| *\n 986| * @since 1.2.0\n 987| *\n 988| * @return string|null HTML\n 989| */\n 990|function bp_legacy_theme_post_update() {\n 991|\t$bp = buddypress();\n 992|\n 993|\tif ( ! bp_is_post_request() ) {\n 994|\t\treturn;\n 995|\t}\n 996|\n 997|\t// Check the ...
Tool2026-05-28 16:07 UTC
{"content": " 500| * Processes Activity updates received via a POST request.\n 501| *\n 502| * @since 3.0.0\n 503| *\n 504| * @return string JSON reply\n 505| */\n 506|function bp_nouveau_ajax_post_update() {\n 507|\t$bp = buddypress();\n 508|\n 509|\tif ( ! is_user_logged_in() |...
Donna2026-05-28 16:07 UTC
Now let me get the core database insertion function and the Nouveau AJAX registration.
Tool2026-05-28 16:07 UTC
{"content": " 2145| * @type string $content The content of the activity update.\n 2146| * @type int $user_id Optional. Defaults to the logged-in user.\n 2147| * @type string $error_type Optional. Error type to return. Either 'bool' or 'wp_error'. Defaults to\n 2148| * ...
Tool2026-05-28 16:07 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Activity Ajax functions\n 4| *\n 5| * @since 3.0.0\n 6| * @version 12.0.0\n 7| */\n 8|\n 9|// Exit if accessed directly.\n 10|defined( 'ABSPATH' ) || exit;\n 11|\n 12|add_action( 'admin_init', function () {\n 13|\t$...
Donna2026-05-28 16:07 UTC
Let me also find the `bp_activity_add` function that does the actual database insertion.
Tool2026-05-28 16:07 UTC
{"total_count": 7, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity-functions.php", "line": 2055, "content": " * }"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"content": " 2055| * }\n 2056| * @return WP_Error|bool|int The ID of the activity on success. False on error.\n 2057| */\n 2058|function bp_activity_add( $args = '' ) {\n 2059|\n 2060|\t$r = bp_parse_args(\n 2061|\t\t$args,\n 2062|\t\tarray(\n 2063|\t\t\t'id' => false, ...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"total_count": 7, "matches": [{"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/classes/class-bp-activity-activity.php", "line": 233, "content": "\t *"}, {"path": "/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity...
Donna2026-05-28 16:07 UTC
Tool2026-05-28 16:07 UTC
{"content": " 230|\t * @since 1.0.0\n 231|\t *\n 232|\t * @global wpdb $wpdb WordPress database object.\n 233|\t *\n 234|\t * @return WP_Error|bool True on success.\n 235|\t */\n 236|\tpublic function save() {\n 237|\t\tglobal $wpdb;\n 238|\n 239|\t\t$bp = buddypress();\n 240|\...
Donna2026-05-28 16:07 UTC
Here is a complete summary of how BuddyPress handles activity posting via AJAX.
---
## What I Found
BuddyPress has **three** code paths for posting activity updates, with two AJAX-specific ones (legacy and nouveau themes) and one core database function.
---
### 1. Core AJAX Registration — Legacy Theme
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-legacy/buddypress-functions.php` (line 160)
The legacy theme maps `post_update` to `bp_legacy_theme_post_update`:
```php
'post_update' => 'bp_legacy_theme_post_update',
```
And registers it at line 188–191:
```php
foreach ( $actions as $name => $function ) {
bp_ajax_register_action( $name );
add_action( 'wp_ajax_' . $name, $function );
add_action( 'wp_ajax_nopriv_' . $name, $function );
}
```
So the action hook is `wp_ajax_post_update`.
### 1a. Full Legacy AJAX Handler — `bp_legacy_theme_post_update()`
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-legacy/buddypress-functions.php` (lines 990–1068)
```php
function bp_legacy_theme_post_update() {
$bp = buddypress();
if ( ! bp_is_post_request() ) {
return;
}
// Check the nonce.
check_admin_referer( 'post_update', '_wpnonce_post_update' );
if ( ! is_user_logged_in() ) {
exit( '-1' );
}
if ( empty( $_POST['content'] ) ) {
exit( '-1<div id="message" class="error bp-ajax-message"><p>' . esc_html__( 'Please enter some content to post.', 'buddypress' ) . '</p></div>' );
}
$activity_id = 0;
$item_id = 0;
$object = '';
// Try to get the item id from posted variables.
if ( ! empty( $_POST['item_id'] ) ) {
$item_id = (int) $_POST['item_id'];
}
// Try to get the object from posted variables.
if ( ! empty( $_POST['object'] ) ) {
$object = sanitize_key( $_POST['object'] );
// If the object is not set and we're in a group, set the item id and the object
} elseif ( bp_is_group() ) {
$item_id = bp_get_current_group_id();
$object = 'groups';
}
if ( ! $object && bp_is_active( 'activity' ) ) {
$activity_id = bp_activity_post_update( array( 'content' => $_POST['content'], 'error_type' => 'wp_error' ) );
} elseif ( 'groups' === $object ) {
if ( $item_id && bp_is_active( 'groups' ) )
$activity_id = groups_post_update( array( 'content' => $_POST['content'], 'group_id' => $item_id, 'error_type' => 'wp_error' ) );
} else {
/** This filter is documented in bp-activity/actions/post.php */
$activity_id = apply_filters( 'bp_activity_custom_update', false, $object, $item_id, $_POST['content'] );
}
if ( false === $activity_id ) {
exit( '-1<div id="message" class="error bp-ajax-message"><p>' . esc_html__( 'There was a problem posting your update. Please try again.', 'buddypress' ) . '</p></div>' );
} elseif ( is_wp_error( $activity_id ) && $activity_id->get_error_code() ) {
exit( '-1<div id="message" class="error bp-ajax-message"><p>' . esc_html( $activity_id->get_error_message() ) . '</p></div>' );
}
$last_recorded = ! empty( $_POST['since'] ) ? date( 'Y-m-d H:i:s', intval( $_POST['since'] ) ) : 0;
if ( $last_recorded ) {
$activity_args = array( 'since' => $last_recorded );
$bp->activity->last_recorded = $last_recorded;
add_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
} else {
$activity_args = array( 'include' => $activity_id );
}
if ( bp_has_activities ( $activity_args ) ) {
while ( bp_activities() ) {
bp_the_activity();
bp_get_template_part( 'activity/entry' );
}
}
if ( ! empty( $last_recorded ) ) {
remove_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10 );
}
exit;
}
```
---
### 2. Nouveau Theme AJAX Registration
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-nouveau/includes/activity/ajax.php` (lines 12–81)
Registered via `admin_init`:
```php
add_action( 'admin_init', function () {
$ajax_actions = array(
// ... other actions ...
array(
'post_update' => array(
'function' => 'bp_nouveau_ajax_post_update',
'nopriv' => false,
),
),
// ... more actions ...
);
foreach ( $ajax_actions as $ajax_action ) {
$action = key( $ajax_action );
add_action( 'wp_ajax_' . $action, $ajax_action[ $action ]['function'] );
if ( $ajax_action[ $action ]['nopriv'] ) {
add_action( 'wp_ajax_nopriv_' . $action, $ajax_action[ $action ]['function'] );
}
}
} );
```
### 2a. Full Nouveau AJAX Handler — `bp_nouveau_ajax_post_update()`
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-templates/bp-nouveau/includes/activity/ajax.php` (lines 506–609)
```php
function bp_nouveau_ajax_post_update() {
$bp = buddypress();
if ( ! is_user_logged_in() || empty( $_POST['_wpnonce_post_update'] ) || ! wp_verify_nonce( $_POST['_wpnonce_post_update'], 'post_update' ) ) {
wp_send_json_error();
}
$activity_id = 0;
$item_id = 0;
$object = '';
$is_private = false;
// Try to get the item id from posted variables.
if ( ! empty( $_POST['item_id'] ) ) {
$item_id = (int) $_POST['item_id'];
}
// Try to get the object from posted variables.
if ( ! empty( $_POST['object'] ) ) {
$object = sanitize_key( $_POST['object'] );
// If the object is not set and we're in a group, set the item id and the object
} elseif ( bp_is_group() ) {
$item_id = bp_get_current_group_id();
$object = 'group';
$status = groups_get_current_group()->status;
}
if ( 'user' === $object && bp_is_active( 'activity' ) ) {
$activity_id = bp_activity_post_update(
array(
'content' => $_POST['content'],
'error_type' => 'wp_error',
)
);
} elseif ( 'group' === $object ) {
if ( $item_id && bp_is_active( 'groups' ) ) {
$activity_id = groups_post_update(
array(
'content' => $_POST['content'],
'group_id' => $item_id,
'error_type' => 'wp_error',
)
);
if ( empty( $status ) ) {
if ( ! empty( $bp->groups->current_group->status ) ) {
$status = $bp->groups->current_group->status;
} else {
$group = groups_get_group( array( 'group_id' => $item_id ) );
$status = $group->status;
}
$is_private = 'public' !== $status;
}
}
} else {
/** This filter is documented in bp-activity/actions/post.php */
$activity_id = apply_filters( 'bp_activity_custom_update', false, $object, $item_id, $_POST['content'] );
}
if ( is_wp_error( $activity_id ) ) {
wp_send_json_error(
array(
'message' => $activity_id->get_error_message(),
)
);
} elseif ( empty( $activity_id ) ) {
wp_send_json_error(
array(
'message' => __( 'There was a problem posting your update. Please try again.', 'buddypress' ),
)
);
}
ob_start();
if ( bp_has_activities( array( 'include' => $activity_id, 'show_hidden' => $is_private ) ) ) {
while ( bp_activities() ) {
bp_the_activity();
bp_get_template_part( 'activity/entry' );
}
}
$activity = ob_get_contents();
ob_end_clean();
wp_send_json_success( array(
'id' => $activity_id,
'message' => esc_html__( 'Update posted.', 'buddypress' ) . ' ' . sprintf( '<a href="%s" class="just-posted">%s</a>', esc_url( bp_activity_get_permalink( $activity_id ) ), esc_html__( 'View activity.', 'buddypress' ) ),
'activity' => $activity,
'is_private' => apply_filters( 'bp_nouveau_ajax_post_update_is_private', $is_private ),
'is_directory' => bp_is_activity_directory(),
) );
}
```
---
### 3. Core Database Insertion — `bp_activity_post_update()` and `bp_activity_add()`
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/bp-activity-functions.php`
**`bp_activity_post_update()`** (line 2153) is the intermediate function called by both AJAX handlers. It validates content and user, then delegates to `bp_activity_add()`:
```php
function bp_activity_post_update( $args = '' ) {
$r = bp_parse_args(
$args,
array(
'content' => false,
'user_id' => bp_loggedin_user_id(),
'error_type' => 'bool',
),
'activity_post_update'
);
if ( empty( $r['content'] ) || ! strlen( trim( $r['content'] ) ) ) {
if ( 'wp_error' === $r['error_type'] ) {
return new WP_Error( 'bp_activity_missing_content', __( 'Please enter some content to post.', 'buddypress' ) );
}
return false;
}
if ( bp_is_user_inactive( $r['user_id'] ) ) {
if ( 'wp_error' === $r['error_type'] ) {
return new WP_Error( 'bp_activity_inactive_user', __( 'User account has not yet been activated.', 'buddypress' ) );
}
return false;
}
$activity_content = $r['content'];
$primary_link = bp_core_get_userlink( $r['user_id'], false, true );
$add_content = apply_filters( 'bp_activity_new_update_content', $activity_content );
$add_primary_link = apply_filters( 'bp_activity_new_update_primary_link', $primary_link );
// Now write the values.
$activity_id = bp_activity_add( array(
'user_id' => $r['user_id'],
'content' => $add_content,
'primary_link' => $add_primary_link,
'component' => buddypress()->activity->id,
'type' => 'activity_update',
'error_type' => $r['error_type']
) );
if ( false === $activity_id || is_wp_error( $activity_id ) ) {
return $activity_id;
}
$activity_content = apply_filters( 'bp_activity_latest_update_content', $r['content'], $activity_content );
bp_update_user_meta( bp_loggedin_user_id(), 'bp_latest_update', array(
'id' => $activity_id,
'content' => $activity_content
) );
do_action( 'bp_activity_posted_update', $r['content'], $r['user_id'], $activity_id );
return $activity_id;
}
```
**`bp_activity_add()`** (line 2058) creates a `BP_Activity_Activity` object and calls `save()`, which runs the actual SQL INSERT:
```php
function bp_activity_add( $args = '' ) {
$r = bp_parse_args( $args, array(
'id' => false,
'action' => '',
'content' => '',
'component' => false,
'type' => false,
'primary_link' => '',
'user_id' => bp_loggedin_user_id(),
'item_id' => false,
'secondary_item_id' => false,
'recorded_time' => bp_core_current_time(),
'hide_sitewide' => false,
'is_spam' => false,
'error_type' => 'bool',
), 'activity_add' );
$activity = new BP_Activity_Activity( $r['id'] );
$activity->user_id = $r['user_id'];
$activity->component = $r['component'];
$activity->type = $r['type'];
$activity->content = $r['content'];
$activity->primary_link = $r['primary_link'];
$activity->item_id = $r['item_id'];
$activity->secondary_item_id = $r['secondary_item_id'];
$activity->date_recorded = $r['recorded_time'];
$activity->hide_sitewide = $r['hide_sitewide'];
$activity->is_spam = $r['is_spam'];
$activity->error_type = $r['error_type'];
$activity->action = ! empty( $r['action'] )
? $r['action']
: bp_activity_generate_action_string( $activity );
$save = $activity->save();
if ( 'wp_error' === $r['error_type'] && is_wp_error( $save ) ) {
return $save;
} elseif ( 'bool' === $r['error_type'] && false === $save ) {
return false;
}
do_action( 'bp_activity_add', $r, $activity->id );
return $activity->id;
}
```
### The Actual SQL INSERT
**File:** `/var/lib/docker/volumes/staging_wp_data/_data/wp-content/plugins/buddypress/bp-activity/classes/class-bp-activity-activity.php` (line 313)
Inside `BP_Activity_Activity::save()`:
```php
$q = $wpdb->prepare(
"INSERT INTO {$bp->activity->table_name} ( user_id, component, type, action, content, primary_link, date_recorded, item_id, secondary_item_id, hide_sitewide, is_spam ) VALUES ( %d, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d )",
$this->user_id, $this->component, $this->type, $this->action, $this->content,
$this->primary_link, $this->date_recorded, $this->item_id,
$this->secondary_item_id, $this->hide_sitewide, $this->is_spam
);
```
---
## Summary of the Full Flow
| Step | Legacy Theme | Nouveau Theme |
|------|-------------|---------------|
| **AJAX action hook** | `wp_ajax_post_update` (line 190) | `wp_ajax_post_update` (line 79) |
| **Handler function** | `bp_legacy_theme_post_update()` | `bp_nouveau_ajax_post_update()` |
| **Nonce check** | `check_admin_referer( 'post_update', '_wpnonce_post_update' )` | `wp_verify_nonce( $_POST['_wpnonce_post_update'], 'post_update' )` |
| **Input sanitization** | `sanitize_key()` on object, `(int)` on item_id; content passed raw through `$_POST['content']` | Same pattern |
| **Core insert call** | `bp_activity_post_update()` or `groups_post_update()` | Same, but object value is `'user'`/`'group'` instead of `'groups'` |
| **DB insertion** | `bp_activity_add()` → `BP_Activity_Activity->save()` → raw `INSERT INTO {$bp->activity->table_name}` | Same |
| **Response** | Renders HTML template `activity/entry` then `exit` | Returns JSON via `wp_send_json_success()` with rendered HTML, activity ID, is_private flag |
The legacy handler echoes rendered HTML and calls `exit`, while the nouveau handler uses proper `wp_send_json_success/json_error` with structured JSON responses. Both ultimately call `bp_activity_post_update()` which calls `bp_activity_add()` which instantiates `BP_Activity_Activity`, populates its properties, and calls `save()` which runs the parameterized SQL INSERT.