File: //home/dfwparty/dfwchat.net/wp-content/plugins/buddyboss-tools/sample-data/ajax.php
<?php
/**
* Sample Data — AJAX handlers.
*
* Two endpoints power the React Sample Data panel:
* - `bb_tools_sample_data_import` — runs requested imports in one round-trip
* - `bb_tools_sample_data_clear` — wipes the imported fixture data
*
* @package BuddyBoss\Tools
* @since 1.0.0
*/
defined( 'ABSPATH' ) || exit;
/**
* AJAX: Import sample data based on the user's checkbox selections.
*
* Expected POST:
* - `_ajax_nonce` — nonce for action `bb_tools_sample_data`
* - `selections[]` — array of fixture keys (e.g. ['users', 'profile', 'groups', 'g-members'])
*
* Response:
* {
* success: true,
* data: {
* imported: [
* { id: 'users', count: 14, label: 'Members' },
* { id: 'profile', count: 14, label: 'Profile fields' },
* ...
* ]
* }
* }
*
* @since 1.0.0
*
* @return void
*/
function bb_tools_sample_data_ajax_import() {
if ( ! bb_tools_current_user_can() ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
}
check_ajax_referer( 'bb_tools_sample_data', '_ajax_nonce' );
$selections = isset( $_POST['selections'] ) && is_array( $_POST['selections'] )
? array_map( 'sanitize_key', wp_unslash( $_POST['selections'] ) )
: array();
if ( empty( $selections ) ) {
wp_send_json_error( array( 'message' => __( 'No items selected.', 'buddyboss-tools' ) ) );
}
// Auto-prepend dependency-chain prerequisites. The React panel mirrors
// Figma + legacy form layout — it does NOT surface "Sample Users" or
// "Sample Groups" toggles, even though the legacy form had them as the
// section parent checkboxes. The 11 demo user accounts and 8 demo groups
// are prerequisites for every downstream importer in their respective
// sections; without them, the sub-importers silently degrade (admin-only
// output for member dependents; zero counts for group dependents).
// `bb_tools_dd_is_imported` short-circuits inside the foreach loop
// below, so these prepends are idempotent across re-imports.
$members_dependents = array( 'profile', 'friends', 'activity', 'messages' );
if ( ! in_array( 'users', $selections, true ) && array_intersect( $selections, $members_dependents ) ) {
array_unshift( $selections, 'users' );
}
$groups_dependents = array( 'g-members', 'g-activity', 'g-forums' );
if ( ! in_array( 'groups', $selections, true ) && array_intersect( $selections, $groups_dependents ) ) {
// Insert `groups` AFTER `users` if both are auto-prepended, so users
// exist before groups need creator IDs to assign.
$users_pos = array_search( 'users', $selections, true );
if ( false !== $users_pos ) {
array_splice( $selections, $users_pos + 1, 0, 'groups' );
} else {
array_unshift( $selections, 'groups' );
}
}
// Map fixture key → ( label, importer callable, marker tuple for bb_tools_dd_update_import ).
$importers = array(
'users' => array( __( 'Members', 'buddyboss-tools' ), 'bb_tools_dd_import_users', array( 'users', 'users' ) ),
'profile' => array( __( 'Profile fields', 'buddyboss-tools' ), 'bb_tools_dd_import_users_profile', array( 'users', 'xprofile' ) ),
'friends' => array( __( 'Connections', 'buddyboss-tools' ), 'bb_tools_dd_import_users_friends', array( 'users', 'friends' ) ),
'activity' => array( __( 'Activity posts', 'buddyboss-tools' ), 'bb_tools_dd_import_users_activity', array( 'users', 'activity' ) ),
'messages' => array( __( 'Private messages', 'buddyboss-tools' ), 'bb_tools_dd_import_users_messages', array( 'users', 'messages' ) ),
'groups' => array( __( 'Groups', 'buddyboss-tools' ), 'bb_tools_dd_import_groups', array( 'groups', 'groups' ) ),
'g-members' => array( __( 'Group members', 'buddyboss-tools' ), 'bb_tools_dd_import_groups_members', array( 'groups', 'members' ) ),
'g-activity' => array( __( 'Group activity', 'buddyboss-tools' ), 'bb_tools_dd_import_groups_activity', array( 'groups', 'activity' ) ),
'g-forums' => array( __( 'Group forums', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_in_groups', array( 'groups', 'forums' ) ),
'forums' => array( __( 'Forums', 'buddyboss-tools' ), 'bb_tools_dd_import_forums', array( 'forums', 'forums' ) ),
'f-topics' => array( __( 'Discussions', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_topics', array( 'forums', 'topics' ) ),
'f-replies' => array( __( 'Replies', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_topics_replies', array( 'forums', 'replies' ) ),
);
$imported = array();
foreach ( $selections as $key ) {
if ( ! isset( $importers[ $key ] ) ) {
continue;
}
list( $label, $callable, $marker ) = $importers[ $key ];
if ( ! is_callable( $callable ) ) {
$imported[] = array(
'id' => $key,
'label' => $label,
'count' => null,
'success' => false,
'message' => __( 'Importer not available.', 'buddyboss-tools' ),
);
continue;
}
// Skip already-imported items (idempotent).
if ( function_exists( 'bb_tools_dd_is_imported' ) && bb_tools_dd_is_imported( $marker[0], $marker[1] ) ) {
$imported[] = array(
'id' => $key,
'label' => $label,
'count' => 0,
'success' => true,
'message' => __( 'Already imported — skipped.', 'buddyboss-tools' ),
);
continue;
}
try {
$result = call_user_func( $callable );
$count = is_array( $result ) ? count( $result ) : (int) $result;
if ( function_exists( 'bb_tools_dd_update_import' ) ) {
bb_tools_dd_update_import( $marker[0], $marker[1] );
}
$imported[] = array(
'id' => $key,
'label' => $label,
'count' => $count,
'success' => true,
'message' => sprintf(
/* translators: 1: count, 2: label */
__( '%1$d %2$s imported successfully', 'buddyboss-tools' ),
$count,
$label
),
);
} catch ( \Throwable $e ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Gated on WP_DEBUG.
error_log( sprintf( 'BB Tools: Sample Data import "%s" failed: %s', $key, $e->getMessage() ) );
}
$imported[] = array(
'id' => $key,
'label' => $label,
'count' => 0,
'success' => false,
'message' => $e->getMessage(),
);
}
}
// Recalculate bbPress aggregate counters for every imported forum + topic.
// Mirrors the legacy block in bp_admin_tools_default_data_save() (lines
// 115-131 of bp-core-tools-default-data.php). Without this, newly imported
// forums display "0 topics / 0 replies" in bbPress UIs until another bbPress
// hook recounts them.
if ( function_exists( 'bp_is_active' ) && bp_is_active( 'forums' ) ) {
$forum_ids = bp_get_option( 'bp_dd_imported_forum_ids', array() );
if ( ! empty( $forum_ids ) && function_exists( 'bbp_update_forum_topic_count' ) && function_exists( 'bbp_update_forum_reply_count' ) ) {
foreach ( (array) $forum_ids as $forum_id ) {
bbp_update_forum_topic_count( $forum_id );
bbp_update_forum_reply_count( $forum_id );
}
}
$topic_ids = bp_get_option( 'bp_dd_imported_topic_ids', array() );
if ( ! empty( $topic_ids ) && function_exists( 'bbp_update_topic_reply_count' ) ) {
foreach ( (array) $topic_ids as $topic_id ) {
bbp_update_topic_reply_count( $topic_id );
}
}
}
wp_send_json_success( array( 'imported' => $imported ) );
}
add_action( 'wp_ajax_bb_tools_sample_data_import', 'bb_tools_sample_data_ajax_import' );
/**
* AJAX: Clear all imported sample data.
*
* Expected POST:
* - `_ajax_nonce` — nonce for action `bb_tools_sample_data`
*
* @since 1.0.0
*
* @return void
*/
function bb_tools_sample_data_ajax_clear() {
if ( ! bb_tools_current_user_can() ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
}
check_ajax_referer( 'bb_tools_sample_data', '_ajax_nonce' );
if ( function_exists( 'bb_tools_dd_clear_db' ) ) {
bb_tools_dd_clear_db();
}
wp_send_json_success( array( 'message' => __( 'Sample data cleared.', 'buddyboss-tools' ) ) );
}
add_action( 'wp_ajax_bb_tools_sample_data_clear', 'bb_tools_sample_data_ajax_clear' );
/**
* Ordered delete plan for the chunked clear endpoint.
*
* Each step is `[ option_key, per_id_callable, chunk_size ]`. The endpoint
* walks the steps in order, processes one chunk from the first non-empty
* option-array per request, shrinks the option as it goes, and returns
* `done:true` only after every option-array is empty AND the marker options
* have been wiped. Chunk sizes are tuned so each request finishes well
* under the FastCGI 30s idle timeout on a small install:
*
* - replies : ~50ms each → 30 per chunk ≈ 1.5s
* - topics : ~50ms each → 30 per chunk ≈ 1.5s
* - forums : ~80ms each → 30 per chunk ≈ 2.4s (wp_delete_post + bbp_delete_forum)
* - groups : ~100ms each → 20 per chunk ≈ 2.0s
* - xprofile : ~50ms each → 30 per chunk ≈ 1.5s
* - members : ~500ms each (bp_core_delete_account) → 5 per chunk ≈ 2.5s
*
* @since 1.0.0
*
* @return array<int, array{0:string, 1:callable, 2:int}>
*/
function bb_tools_sample_data_get_clear_plan() {
return array(
array( 'bp_dd_imported_reply_ids', 'wp_delete_post', 30 ),
array( 'bp_dd_imported_topic_ids', 'wp_delete_post', 30 ),
array(
'bp_dd_imported_forum_ids',
function ( $forum_id ) {
wp_delete_post( $forum_id );
if ( function_exists( 'bbp_delete_forum' ) ) {
bbp_delete_forum( $forum_id );
}
},
30,
),
array( 'bp_dd_imported_group_ids', 'groups_delete_group', 20 ),
array( 'bp_dd_imported_user_xprofile_ids', 'xprofile_delete_field_group', 30 ),
array( 'bp_dd_imported_user_ids', 'bp_core_delete_account', 5 ),
);
}
/**
* AJAX: Clear sample data — one chunk of one delete-step per request.
*
* The React panel polls this endpoint until the response contains
* `done:true`. The server picks the first non-empty option-array from the
* delete plan, processes `chunk_size` IDs through that step's callable,
* writes the shrunk option back, and returns. When every option-array is
* empty, the marker options are wiped and `done:true` is returned.
*
* Splitting the work this way keeps each PHP request under the FastCGI
* idle timeout (30s MAMP, 60s typical PHP-FPM) even on installs with
* thousands of imported records — `bp_core_delete_account` alone runs
* ~500ms per user, so 11 users + heavy data quickly exceeds the timeout.
*
* Expected POST:
* - `_ajax_nonce` — nonce for action `bb_tools_sample_data`
*
* Response:
* {
* success: true,
* data: {
* done: false, // true = nothing left to clear
* step: 'bp_dd_imported_reply_ids',
* processed: 30,
* remaining_step: 145, // after this chunk, this step still has 145 IDs
* }
* }
*
* @since 1.0.0
*
* @return void
*/
function bb_tools_sample_data_ajax_clear_chunk() {
if ( ! bb_tools_current_user_can() ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
}
check_ajax_referer( 'bb_tools_sample_data', '_ajax_nonce' );
if ( function_exists( 'set_time_limit' ) ) {
set_time_limit( 0 );
}
ignore_user_abort( true );
$plan = bb_tools_sample_data_get_clear_plan();
foreach ( $plan as $step ) {
list( $option_key, $callable, $chunk_size ) = $step;
$ids = (array) bp_get_option( $option_key, array() );
if ( empty( $ids ) ) {
continue;
}
// Re-key so array_slice with numeric offsets behaves predictably,
// then take the first $chunk_size IDs.
$ids = array_values( $ids );
$batch = array_slice( $ids, 0, (int) $chunk_size );
$remaining = array_slice( $ids, count( $batch ) );
foreach ( $batch as $id ) {
try {
call_user_func( $callable, $id );
} catch ( \Throwable $e ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Gated on WP_DEBUG.
error_log( sprintf( 'BB Tools: Sample Data clear "%s id %s" failed: %s', $option_key, (string) $id, $e->getMessage() ) );
}
}
}
if ( empty( $remaining ) ) {
bp_delete_option( $option_key );
} else {
bp_update_option( $option_key, $remaining );
}
// One chunk per request — the client polls back for the next one.
wp_send_json_success(
array(
'done' => false,
'step' => $option_key,
'processed' => count( $batch ),
'remaining_step' => count( $remaining ),
)
);
}
// Every data list is empty — wipe the cross-cutting marker options
// (`bp_dd_import_users/_groups/_forums` + `_imported_forums_group_ids` +
// `_imported_user_messages_ids`) so the React panel re-enables every
// checkbox and disables the Clear button on its next render.
if ( function_exists( 'bb_tools_dd_delete_import_records' ) ) {
bb_tools_dd_delete_import_records();
}
wp_send_json_success(
array(
'done' => true,
)
);
}
add_action( 'wp_ajax_bb_tools_sample_data_clear_chunk', 'bb_tools_sample_data_ajax_clear_chunk' );
/**
* Item-key → display-label map. Single source of truth shared by the
* AJAX response messages, the localize blob shipped to the React panel
* (window.bbToolsSampleDataConfig.itemLabels), and any future consumer.
*
* @since 1.0.0
*
* @return array<string, string>
*/
function bb_tools_sample_data_get_item_labels() {
$importers = bb_tools_sample_data_get_importers();
$labels = array();
foreach ( $importers as $key => $tuple ) {
$labels[ $key ] = $tuple[0];
}
return $labels;
}
/**
* Item-key → bool map of which items have already been imported.
*
* Mirrors the legacy form's `bp_dd_imported_disabled()` helper which echoed
* `disabled="disabled" checked="checked"` for any item whose marker exists
* in `bp_dd_import_<group>`. The React panel reads this from the localize
* blob (window.bbToolsSampleDataConfig.alreadyImported) and renders the
* matching checkbox as checked + disabled.
*
* @since 1.0.0
*
* @return array<string, bool>
*/
function bb_tools_sample_data_get_already_imported() {
if ( ! function_exists( 'bb_tools_dd_is_imported' ) ) {
return array();
}
$importers = bb_tools_sample_data_get_importers();
$out = array();
foreach ( $importers as $key => $tuple ) {
$marker = $tuple[2];
$out[ $key ] = (bool) bb_tools_dd_is_imported( $marker[0], $marker[1] );
}
return $out;
}
/**
* Importers map shared by the per-item AJAX handler, the legacy batch
* handler, the item-label helper, and the already-imported helper.
* Each entry: `[ label, importer callable, marker tuple ]`. The marker
* tuple maps to the legacy `bb_tools_dd_is_imported( $group, $item )` /
* `bb_tools_dd_update_import( $group, $item )` pair.
*
* @since 1.0.0
*
* @return array<string, array{0:string, 1:string, 2:array{0:string, 1:string}}>
*/
function bb_tools_sample_data_get_importers() {
return array(
'users' => array( __( 'Members', 'buddyboss-tools' ), 'bb_tools_dd_import_users', array( 'users', 'users' ) ),
'profile' => array( __( 'Profile fields', 'buddyboss-tools' ), 'bb_tools_dd_import_users_profile', array( 'users', 'xprofile' ) ),
'friends' => array( __( 'Connections', 'buddyboss-tools' ), 'bb_tools_dd_import_users_friends', array( 'users', 'friends' ) ),
'activity' => array( __( 'Activity posts', 'buddyboss-tools' ), 'bb_tools_dd_import_users_activity', array( 'users', 'activity' ) ),
'messages' => array( __( 'Private messages', 'buddyboss-tools' ), 'bb_tools_dd_import_users_messages', array( 'users', 'messages' ) ),
'groups' => array( __( 'Groups', 'buddyboss-tools' ), 'bb_tools_dd_import_groups', array( 'groups', 'groups' ) ),
'g-members' => array( __( 'Group members', 'buddyboss-tools' ), 'bb_tools_dd_import_groups_members', array( 'groups', 'members' ) ),
'g-activity' => array( __( 'Group activity', 'buddyboss-tools' ), 'bb_tools_dd_import_groups_activity', array( 'groups', 'activity' ) ),
'g-forums' => array( __( 'Group forums', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_in_groups', array( 'groups', 'forums' ) ),
'forums' => array( __( 'Forums', 'buddyboss-tools' ), 'bb_tools_dd_import_forums', array( 'forums', 'forums' ) ),
'f-topics' => array( __( 'Discussions', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_topics', array( 'forums', 'topics' ) ),
'f-replies' => array( __( 'Replies', 'buddyboss-tools' ), 'bb_tools_dd_import_forums_topics_replies', array( 'forums', 'replies' ) ),
);
}
/**
* Per-item chunk policy for the chunked AJAX importer.
*
* MAMP's FastCGI has a 30s idle timeout and production PHP-FPM typically
* uses 60s — `set_time_limit( 0 )` doesn't help, because the timeout is on
* the FastCGI pipe being silent, not on PHP's own clock. So any importer
* whose full run can exceed the smaller of those numbers must split its
* work across multiple HTTP round-trips. Each entry below caps one round-
* trip to a slice small enough to finish in well under 30s on a typical
* install:
*
* - `g-activity`: 150-row plan, ~600ms per row → 30 rows / chunk ≈ 18s
* - `f-replies` : ~100 topics × 1–7 replies × ~50ms → 30 topics / chunk ≈ 10s
*
* `total_cb` returns the upper bound for that item so the client can detect
* `done`. For `g-activity` the cap is hardcoded in the importer's for-loop.
* For `f-replies` the cap is the count of imported topics (the slice target).
*
* `chunked_cb` adapts the importer's call signature. The two chunked
* importers expose `$offset`/`$limit` in different positions
* (g-activity: positions 0/1; f-replies: positions 1/2 after `$topics`), so
* the AJAX layer can't blindly forward args. Each adapter receives
* `( $offset, $chunk_size )` and is responsible for routing them correctly.
*
* `count_option` (optional) names a WP option whose array size represents the
* cumulative imported count. When present, the AJAX layer reports the per-
* chunk delta (size after − size before) instead of the importer's own return
* value. This is needed when an importer returns the full accumulated ID
* array on every call (the legacy contract) rather than a per-call count.
*
* Items not listed here run unchunked in a single round-trip.
*
* @since 1.0.0
*
* @return array<string, array{chunk:int, total_cb:callable, chunked_cb:callable, count_option?:string}>
*/
function bb_tools_sample_data_get_chunk_policy() {
return array(
'g-activity' => array(
'chunk' => 30,
'total_cb' => function () {
return 150;
},
'chunked_cb' => function ( $offset, $limit ) {
return bb_tools_dd_import_groups_activity( $offset, $limit );
},
),
'f-replies' => array(
'chunk' => 30,
'total_cb' => function () {
return count( (array) bp_get_option( 'bp_dd_imported_topic_ids', array() ) );
},
'chunked_cb' => function ( $offset, $limit ) {
return bb_tools_dd_import_forums_topics_replies( false, $offset, $limit );
},
'count_option' => 'bp_dd_imported_reply_ids',
),
);
}
/**
* AJAX: Import a single sample-data item (chunked architecture).
*
* The React panel fires this endpoint sequentially — one HTTP round-trip per
* selected item, or per sub-batch for chunkable items. Splitting heavy
* importers keeps each PHP request well under the FastCGI idle timeout
* (30s MAMP, 60s typical PHP-FPM). `set_time_limit( 0 )` doesn't help with
* FastCGI idle timeout, so sub-batching is the only real fix.
*
* Expected POST:
* - `_ajax_nonce` — nonce for action `bb_tools_sample_data`
* - `item` — fixture key (one of: users, profile, friends, activity,
* messages, groups, g-members, g-activity, g-forums,
* forums, f-topics, f-replies)
* - `offset` — (chunkable items only) zero-based start index for this
* sub-batch. Default 0.
*
* Response on success:
* {
* success: true,
* data: {
* id: 'g-activity',
* label: 'Group activity',
* count: 30, // rows produced in THIS sub-batch
* success: true,
* message: '30 Group activity imported successfully',
* done: false, // (chunkable) more sub-batches remain
* next_offset: 30 // (chunkable) start index for next call
* }
* }
*
* @since 1.0.0
*
* @return void
*/
function bb_tools_sample_data_ajax_import_one() {
if ( ! bb_tools_current_user_can() ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
}
check_ajax_referer( 'bb_tools_sample_data', '_ajax_nonce' );
// Lift PHP execution-time limit. Note: this does NOT defeat FastCGI idle
// timeout — sub-batching (see chunk_policy) is what handles that.
if ( function_exists( 'set_time_limit' ) ) {
set_time_limit( 0 );
}
ignore_user_abort( true );
$item = isset( $_POST['item'] ) ? sanitize_key( wp_unslash( $_POST['item'] ) ) : '';
if ( empty( $item ) ) {
wp_send_json_error( array( 'message' => __( 'No item specified.', 'buddyboss-tools' ) ) );
}
$importers = bb_tools_sample_data_get_importers();
if ( ! isset( $importers[ $item ] ) ) {
wp_send_json_error( array( 'message' => __( 'Unknown item.', 'buddyboss-tools' ) ) );
}
list( $label, $callable, $marker ) = $importers[ $item ];
if ( ! is_callable( $callable ) ) {
wp_send_json_error(
array(
'id' => $item,
'label' => $label,
'success' => false,
'message' => __( 'Importer not available.', 'buddyboss-tools' ),
)
);
}
$policy = bb_tools_sample_data_get_chunk_policy();
$is_chunked = isset( $policy[ $item ] );
$offset = $is_chunked && isset( $_POST['offset'] ) ? absint( wp_unslash( $_POST['offset'] ) ) : 0;
$chunk_size = $is_chunked ? (int) $policy[ $item ]['chunk'] : 0;
$total = $is_chunked ? (int) call_user_func( $policy[ $item ]['total_cb'] ) : 0;
// Idempotent: skip items already fully imported (marker present). For
// chunked items, the marker is only written on the final sub-batch — so
// a mid-flight chunked run won't false-positive here.
if ( function_exists( 'bb_tools_dd_is_imported' ) && bb_tools_dd_is_imported( $marker[0], $marker[1] ) ) {
wp_send_json_success(
array_merge(
array(
'id' => $item,
'label' => $label,
'count' => 0,
'success' => true,
'message' => __( 'Already imported — skipped.', 'buddyboss-tools' ),
),
$is_chunked ? array(
'done' => true,
'next_offset' => $total,
) : array()
)
);
}
try {
if ( $is_chunked ) {
// For importers whose return value is the FULL accumulated ID
// array (legacy contract — see bb_tools_dd_import_forums_topics_replies),
// compute the per-chunk count from the option's size delta instead
// of the importer's return value. Otherwise sum-on-client would
// over-count across chunks.
$count_option = isset( $policy[ $item ]['count_option'] ) ? (string) $policy[ $item ]['count_option'] : '';
$before = '' !== $count_option ? count( (array) bp_get_option( $count_option, array() ) ) : 0;
$result = call_user_func( $policy[ $item ]['chunked_cb'], $offset, $chunk_size );
if ( '' !== $count_option ) {
$after = count( (array) bp_get_option( $count_option, array() ) );
$count = max( 0, $after - $before );
} else {
$count = is_array( $result ) ? count( $result ) : (int) $result;
}
$next_offset = $offset + $chunk_size;
$done = $next_offset >= $total;
} else {
$result = call_user_func( $callable );
$count = is_array( $result ) ? count( $result ) : (int) $result;
$done = true;
}
// Mark + recount only on the final pass. For unchunked items that's
// the only pass; for chunked items it's when offset+chunk reaches total.
if ( $done ) {
if ( function_exists( 'bb_tools_dd_update_import' ) ) {
bb_tools_dd_update_import( $marker[0], $marker[1] );
}
// Count one usage increment per imported item, on its completing
// pass only (not per chunk). $label is the item's human name.
if ( function_exists( 'bb_record_tool_usage' ) ) {
bb_record_tool_usage( 'sample_data', $item, $label );
}
// Mirror the legacy bbPress recount block from the batch handler.
if ( in_array( $item, array( 'forums', 'f-topics', 'f-replies' ), true ) ) {
if ( function_exists( 'bp_is_active' ) && bp_is_active( 'forums' ) ) {
$forum_ids = bp_get_option( 'bp_dd_imported_forum_ids', array() );
if ( ! empty( $forum_ids ) && function_exists( 'bbp_update_forum_topic_count' ) && function_exists( 'bbp_update_forum_reply_count' ) ) {
foreach ( (array) $forum_ids as $forum_id ) {
bbp_update_forum_topic_count( $forum_id );
bbp_update_forum_reply_count( $forum_id );
}
}
$topic_ids = bp_get_option( 'bp_dd_imported_topic_ids', array() );
if ( ! empty( $topic_ids ) && function_exists( 'bbp_update_topic_reply_count' ) ) {
foreach ( (array) $topic_ids as $topic_id ) {
bbp_update_topic_reply_count( $topic_id );
}
}
}
}
}
$payload = array(
'id' => $item,
'label' => $label,
'count' => $count,
'success' => true,
'message' => sprintf(
/* translators: 1: count, 2: label */
__( '%1$d %2$s imported successfully', 'buddyboss-tools' ),
$count,
$label
),
);
if ( $is_chunked ) {
$payload['done'] = $done;
$payload['next_offset'] = $done ? $total : $next_offset;
}
wp_send_json_success( $payload );
} catch ( \Throwable $e ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Gated on WP_DEBUG.
error_log( sprintf( 'BB Tools: Sample Data import "%s" failed: %s', $item, $e->getMessage() ) );
}
wp_send_json_error(
array(
'id' => $item,
'label' => $label,
'count' => 0,
'success' => false,
'message' => sprintf(
/* translators: %s: import item label */
__( 'Error importing %s. Please try again.', 'buddyboss-tools' ),
$label
),
'detail' => defined( 'WP_DEBUG' ) && WP_DEBUG ? $e->getMessage() : '',
)
);
}
}
add_action( 'wp_ajax_bb_tools_sample_data_import_one', 'bb_tools_sample_data_ajax_import_one' );