HEX
Server: LiteSpeed
System: Linux node612.namehero.net 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: dfwparty (1186)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: //home/dfwparty/dfwchat.net/wp-content/plugins/buddyboss-tools/migration/peepso/ajax.php
<?php
/**
 * PeepSo migration AJAX handlers.
 *
 * Provides the admin-ajax endpoints that drive the PeepSo migration modal:
 * scanning source data, running migration batches, persisting state and batch
 * size, and resetting a migration. Each handler verifies capability and nonce
 * before doing any work.
 *
 * @package BuddyBoss\Tools\Migration\PeepSo
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Scan PeepSo data and return per-category record counts.
 *
 * Responds with the source record counts, step inventory, batch size, and
 * current migration state so the modal can show live figures when it opens.
 *
 * @since 1.0.0
 *
 * @return void
 */
function bb_tools_peepso_ajax_scan() {
	if ( ! bb_tools_current_user_can() ) {
		wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
	}

	check_ajax_referer( 'bb_tools_migration', '_ajax_nonce' );

	if ( ! bb_tools_peepso_is_active() ) {
		wp_send_json_error( array( 'message' => __( 'PeepSo is not installed or active. Migration is unavailable.', 'buddyboss-tools' ) ), 400 );
	}

	if ( ! class_exists( 'BB_Tools_Peepso_Data_Scanner' ) ) {
		wp_send_json_error( array( 'message' => __( 'PeepSo scanner not available.', 'buddyboss-tools' ) ), 500 );
	}

	try {
		$scanner = new BB_Tools_Peepso_Data_Scanner();
		$counts  = $scanner->get_all_counts();
	} 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: PeepSo scan failed: %s', $e->getMessage() ) );
		}
		wp_send_json_error( array( 'message' => __( 'Unable to scan PeepSo data. Check the WordPress debug log for details.', 'buddyboss-tools' ) ), 500 );
	}

	// autoload = false: only read when the modal opens.
	update_option( 'bb_tools_peepso_scan_counts', $counts, false );

	$state = get_option( 'bb_tools_peepso_migration_state', array() );
	$state = is_array( $state ) ? $state : array();

	wp_send_json_success(
		array(
			'counts'     => $counts,
			'steps'      => bb_tools_peepso_step_inventory(),
			'batch_size' => (int) get_option( 'bb_tools_peepso_batch_size', 25 ),
			'started'    => (bool) get_option( 'bb_tools_peepso_migration_started', false ),
			'status'     => isset( $state['status'] ) ? sanitize_key( $state['status'] ) : '',
			'step_data'  => ( isset( $state['step_data'] ) && is_array( $state['step_data'] ) ) ? array_values( $state['step_data'] ) : array(),
		)
	);
}
add_action( 'wp_ajax_bb_tools_peepso_scan', 'bb_tools_peepso_ajax_scan' );

/**
 * Run one migration batch and report the next step.
 *
 * Dispatches the requested step to the migration engine and returns its progress
 * along with the next step and parameters, letting the modal drive the migration
 * one batch at a time until it finishes.
 *
 * @since 1.0.0
 *
 * @return void
 */
function bb_tools_peepso_ajax_migrate() {
	if ( ! bb_tools_current_user_can() ) {
		wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
	}

	check_ajax_referer( 'bb_tools_migration', '_ajax_nonce' );

	if ( function_exists( 'set_time_limit' ) ) {
		set_time_limit( 0 );
	}
	ignore_user_abort( true );

	if ( ! bb_tools_peepso_is_active() ) {
		wp_send_json_error( array( 'message' => __( 'PeepSo is not installed or active. Migration is unavailable.', 'buddyboss-tools' ) ), 400 );
	}

	bb_tools_peepso_require_engine();

	$step    = isset( $_POST['step'] ) ? sanitize_key( wp_unslash( $_POST['step'] ) ) : '';
	$limit   = isset( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 0;
	$phase   = isset( $_POST['phase'] ) ? sanitize_key( wp_unslash( $_POST['phase'] ) ) : 'likes';
	$pending = isset( $_POST['pending'] ) ? absint( $_POST['pending'] ) : 0;

	$methods = bb_tools_peepso_step_methods();
	if ( '' === $step || ! isset( $methods[ $step ] ) ) {
		wp_send_json_error( array( 'message' => __( 'Invalid migration step.', 'buddyboss-tools' ) ), 400 );
	}

	update_option( 'bb_tools_peepso_migration_started', 1, false );

	// The ported controller + helpers read the batch offset / phase / pending
	// from the request superglobals; feed them from the validated AJAX inputs.
	$_GET['limit']   = $limit;
	$_POST['limit']  = $limit;
	$_GET['phase']   = $phase;
	$_GET['pending'] = $pending;

	try {
		$migrate  = new BB_Tools_Peepso_Migrate();
		$response = $migrate->{ $methods[ $step ] }();
	} 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: PeepSo migrate step "%s" failed: %s', $step, $e->getMessage() ) );
		}
		wp_send_json_error(
			array(
				'message' => __( 'A migration step failed. Check the WordPress debug log for details.', 'buddyboss-tools' ),
				'step'    => $step,
			),
			500
		);
	}

	$response = is_array( $response ) ? $response : array();
	$done     = ! empty( $response['finished'] );

	$next_step   = '';
	$next_params = array();
	if ( ! $done && ! empty( $response['url'] ) ) {
		$parsed      = bb_tools_peepso_parse_descriptor( $response['url'] );
		$next_step   = $parsed['step'];
		$next_params = $parsed['params'];
	}

	if ( $done && function_exists( 'bb_record_tool_usage' ) ) {
		bb_record_tool_usage( 'migration', 'peepso-community', __( 'PeepSo Community to BuddyBoss', 'buddyboss-tools' ) );
	}

	wp_send_json_success(
		array(
			'class'       => isset( $response['class'] ) ? sanitize_key( $response['class'] ) : $step,
			'counter'     => isset( $response['counter'] ) ? (int) $response['counter'] : 0,
			'total'       => isset( $response['total'] ) ? (int) $response['total'] : 0,
			'skipped'     => ( isset( $response['skipped'] ) && is_array( $response['skipped'] ) ) ? $response['skipped'] : array(),
			'done'        => $done,
			'next_step'   => $next_step,
			'next_params' => $next_params,
		)
	);
}
add_action( 'wp_ajax_bb_tools_peepso_migrate', 'bb_tools_peepso_ajax_migrate' );

/**
 * Persist the migration batch size.
 *
 * Validates the requested batch size against the allowed values and saves it for
 * subsequent migration runs.
 *
 * @since 1.0.0
 *
 * @return void
 */
function bb_tools_peepso_ajax_save_batch_size() {
	if ( ! bb_tools_current_user_can() ) {
		wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
	}

	check_ajax_referer( 'bb_tools_migration', '_ajax_nonce' );

	$allowed = array( 10, 25, 50 );
	$size    = isset( $_POST['batch_size'] ) ? absint( $_POST['batch_size'] ) : 25;
	if ( ! in_array( $size, $allowed, true ) ) {
		$size = 25;
	}

	update_option( 'bb_tools_peepso_batch_size', $size, false );

	wp_send_json_success( array( 'batch_size' => $size ) );
}
add_action( 'wp_ajax_bb_tools_peepso_save_batch_size', 'bb_tools_peepso_ajax_save_batch_size' );

/**
 * Persist migration run state.
 *
 * Stores the current status, counters, per-step data, and log entries so
 * migration progress survives page reloads and closed browser tabs.
 *
 * @since 1.0.0
 *
 * @return void
 */
function bb_tools_peepso_ajax_save_state() {
	if ( ! bb_tools_current_user_can() ) {
		wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
	}

	check_ajax_referer( 'bb_tools_migration', '_ajax_nonce' );

	$allowed = array( 'in_progress', 'completed', 'error' );
	$status  = isset( $_POST['status'] ) ? sanitize_key( wp_unslash( $_POST['status'] ) ) : '';
	if ( ! in_array( $status, $allowed, true ) ) {
		wp_send_json_error( array( 'message' => __( 'Invalid status.', 'buddyboss-tools' ) ), 400 );
	}

	$step_data = array();
	$raw_steps = ( isset( $_POST['step_data'] ) && is_string( $_POST['step_data'] ) ) ? json_decode( wp_unslash( $_POST['step_data'] ), true ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Decoded + sanitised field-by-field below.
	if ( is_array( $raw_steps ) ) {
		foreach ( $raw_steps as $index => $data ) {
			$step_data[ absint( $index ) ] = array(
				'counter' => isset( $data['counter'] ) ? absint( $data['counter'] ) : 0,
				'total'   => isset( $data['total'] ) ? absint( $data['total'] ) : 0,
			);
		}
	}

	$log_entries = array();
	$raw_log     = ( isset( $_POST['log_entries'] ) && is_string( $_POST['log_entries'] ) ) ? json_decode( wp_unslash( $_POST['log_entries'] ), true ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Decoded + sanitised field-by-field below.
	if ( is_array( $raw_log ) ) {
		foreach ( $raw_log as $entry ) {
			$log_entries[] = array(
				'type'    => isset( $entry['type'] ) ? sanitize_text_field( $entry['type'] ) : 'INFO',
				'message' => isset( $entry['message'] ) ? sanitize_text_field( $entry['message'] ) : '',
				'time'    => isset( $entry['time'] ) ? sanitize_text_field( $entry['time'] ) : '',
			);
		}
	}

	$state = array(
		'status'          => $status,
		'total_records'   => isset( $_POST['total_records'] ) ? absint( $_POST['total_records'] ) : 0,
		'steps_completed' => isset( $_POST['steps_completed'] ) ? sanitize_text_field( wp_unslash( $_POST['steps_completed'] ) ) : '0/0',
		'elapsed_time'    => isset( $_POST['elapsed_time'] ) ? sanitize_text_field( wp_unslash( $_POST['elapsed_time'] ) ) : '00:00',
		'completed_at'    => current_time( 'mysql' ),
		'step_data'       => $step_data,
		'log_entries'     => $log_entries,
	);

	update_option( 'bb_tools_peepso_migration_state', $state, false );

	if ( 'completed' === $status ) {
		delete_option( 'bb_tools_peepso_migration_started' );
	}

	wp_send_json_success( $state );
}
add_action( 'wp_ajax_bb_tools_peepso_save_state', 'bb_tools_peepso_ajax_save_state' );

/**
 * Reset the migration.
 *
 * Purges migrated BuddyBoss content in client-driven chunks and clears the saved
 * migration state and mappings once the purge completes.
 *
 * @since 1.0.0
 *
 * @return void
 */
function bb_tools_peepso_ajax_reset() {
	if ( ! bb_tools_current_user_can() ) {
		wp_send_json_error( array( 'message' => __( 'Permission denied.', 'buddyboss-tools' ) ), 403 );
	}

	check_ajax_referer( 'bb_tools_migration', '_ajax_nonce' );

	if ( function_exists( 'set_time_limit' ) ) {
		set_time_limit( 0 );
	}

	bb_tools_peepso_require_engine();

	$offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 0;
	$result = array(
		'processed' => 0,
		'total'     => 0,
		'done'      => false,
	);

	// The purge runs in client-driven chunks so Cancel can stop it (the next
	// chunk is simply never requested). Migration state is cleared only on the
	// final chunk, so a cancelled reset leaves state intact and can be resumed.
	try {
		$migrate = new BB_Tools_Peepso_Migrate();
		$result  = $migrate->purge_buddyboss_content( $offset, 6 );

		if ( ! empty( $result['done'] ) ) {
			if ( function_exists( 'bb_tools_peepso_cleanup_migration' ) ) {
				bb_tools_peepso_cleanup_migration();
			}
			delete_option( 'bb_tools_peepso_migration_state' );
			delete_option( 'bb_tools_peepso_migration_started' );
		}
	} 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: PeepSo reset failed: %s', $e->getMessage() ) );
		}
		wp_send_json_error( array( 'message' => __( 'Reset failed. Check the WordPress debug log for details.', 'buddyboss-tools' ) ), 500 );
	}

	wp_send_json_success(
		array(
			'processed' => (int) $result['processed'],
			'total'     => (int) $result['total'],
			'done'      => ! empty( $result['done'] ),
		)
	);
}
add_action( 'wp_ajax_bb_tools_peepso_reset', 'bb_tools_peepso_ajax_reset' );