| Server IP : 65.108.144.40 / Your IP : 216.73.217.165 Web Server : Apache/2.4.52 (Ubuntu) System : Linux ubuntu-8gb-hel1-1 5.15.0-173-generic #183-Ubuntu SMP Fri Mar 6 13:29:34 UTC 2026 x86_64 User : dev ( 1000) PHP Version : 8.2.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /var/www/html/successkpi/wp-content/plugins/wpe-site-migration/class/Pro/ |
Upload File : |
<?php
namespace DeliciousBrains\WPMDB\Pro;
use DeliciousBrains\WPMDB\Common\BackgroundMigration\BackgroundMigration;
use DeliciousBrains\WPMDB\Common\BackgroundMigration\BackgroundMigrationProcess;
use DeliciousBrains\WPMDB\Common\EntitlementsInterface;
use DeliciousBrains\WPMDB\Common\Error\ErrorLog;
use DeliciousBrains\WPMDB\Common\Filesystem\Filesystem;
use DeliciousBrains\WPMDB\Common\FormData\FormData;
use DeliciousBrains\WPMDB\Common\MigrationPersistence\Persistence;
use DeliciousBrains\WPMDB\Common\Properties\Properties;
use DeliciousBrains\WPMDB\Common\Settings\Settings;
use DeliciousBrains\WPMDB\Common\Util\Util;
use DeliciousBrains\WPMDB\Pro\UI\Template;
use WP_Error;
class UsageTracking {
/**
* @var array
*/
private $settings;
/**
* @var Properties
*/
private $props;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var ErrorLog
*/
private $error_log;
/**
* @var Template
*/
private $template;
/**
* @var FormData
*/
private $form_data;
/**
* @var string
*/
private $api_url;
/**
* @var EntitlementsInterface
*/
private $license;
public function __construct(
Settings $settings,
Filesystem $filesystem,
ErrorLog $error_log,
Template $template,
FormData $form_data,
Properties $properties,
EntitlementsInterface $license
) {
$this->settings = $settings->get_settings();
$this->props = $properties;
$this->filesystem = $filesystem;
$this->error_log = $error_log;
$this->template = $template;
$this->form_data = $form_data;
$this->license = $license;
$this->api_url = apply_filters( 'wpmdb_logging_endpoint_url', 'https://api2.deliciousbrains.com' );
add_action( 'wpmdb_migration_starting', [ $this, 'log_migration_start' ] );
add_filter( 'wpmdb_task_item', [ $this, 'log_migration_in_progress' ], 20, 3 );
add_action( 'wpmdb_track_migration_cancel', [ $this, 'log_migration_cancellation' ] );
add_action( 'wpmdb_track_migration_error', [ $this, 'log_migration_error' ] );
add_action( 'wpmdb_after_finalize_migration', [ $this, 'log_migration_complete' ], 10, 2 );
}
public function register() {
add_action( 'wpmdb_additional_settings_advanced', array( $this, 'template_toggle_usage_tracking' ) );
add_filter( 'wpmdb_notification_strings', [ $this, 'template_notice_enable_usage_tracking' ] );
}
/**
* Gathers data about the migration and formats it ready to send to the
* /complete API endpoint.
*
* @param string $status complete|error|cancelled
* @param array $data
* @param array $state_data
*
* @return array
*/
public function format_migration_update_data( $status, $data, $state_data ) {
$migration_guid = Persistence::getMigrationId( $state_data );
// Switch status to terminated if conditions meant plugin automatically cancelled migration.
if ( 'error' === $status && ! empty( $data['error_code'] ) && 'migration-stuck' === $data['error_code'] ) {
$status = 'terminated';
}
$log_data = [
'migration_guid' => $migration_guid,
'migration_status' => $status,
'last_stage' => isset( $state_data['stage'] ) ? $state_data['stage'] : null,
'error_text' => isset( $data['error_text'] ) ? $data['error_text'] : null,
'error_code' => isset( $data['error_code'] ) ? $data['error_code'] : null,
'error_data' => isset( $data['error_data'] ) ? $this->handle_error_data( $data['error_data'] ) : null,
'migration_stats' => apply_filters( 'wpmdb_migration_stats', Persistence::getMigrationStats() ),
];
// When processing an end of migration update, set the completion time.
if ( 'in-progress' !== $status ) {
$log_data['migration_complete_time'] = time();
}
$filtered_log_data = apply_filters( 'wpmdb_usage_tracking_update_data', $log_data );
// Check that the filter returned an array before assigning. Discard the
// filtered value if it's not valid.
if ( is_array( $filtered_log_data ) ) {
$log_data = $filtered_log_data;
}
// Once we have batch data, we can properly check for current stage.
if ( ! empty( $log_data['batch_data']['stages'] ) ) {
$stage = BackgroundMigration::get_current_stage( $log_data['batch_data'] );
if ( ! empty( $stage ) ) {
$log_data['last_stage'] = $stage;
}
}
return $log_data;
}
/**
* Send migration update to usage DB
*
* @param string $status complete|error|cancelled
* @param array $data
*
* @return void
*/
public function send_migration_update( $status = 'complete', $data = [] ) {
// If there is no state data then this should not attempt any
// further processing.
$state_data = Persistence::getStateData();
if ( is_wp_error( $state_data ) ) {
return;
}
if ( empty( $this->settings['allow_tracking'] ) ) {
return;
}
$log_data = $this->format_migration_update_data( $status, $data, $state_data );
$encoded_log_data = $this->encode_log_data( $log_data );
if ( empty( $encoded_log_data ) ) {
return;
}
$remote_post_args = array(
'timeout' => 60,
'method' => 'POST',
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => $encoded_log_data,
'reject_unsafe_urls' => false,
);
$api_url = $this->api_url . '/complete';
$result = wp_remote_post( $api_url, $remote_post_args );
if ( is_wp_error( $result ) || $result['response']['code'] >= 400 ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( 'Error logging migration event' );
error_log( print_r( $result, true ) );
}
$this->error_log->log_error( 'Error logging Migration event', $result );
}
}
/**
* Log migration in-progress update.
*
* @param array|bool|WP_Error $item The background migration batch item.
* @param BackgroundMigration|null $migration The background migration.
* @param string $identifier The background migration process identifier.
*
* @return array|bool|WP_Error
*
* @handles wpmdb_task_item
*/
public function log_migration_in_progress( $item, $migration, $identifier ) {
if ( empty( $identifier ) ) {
return $item;
}
$timeout_key = $identifier . '_in_progress_timeout';
// At the end of a migration, just clear out the in progress message timeout.
if ( false === $item ) {
delete_site_transient( $timeout_key );
return $item;
}
// Have everything we need to attempt to send an in-progress message?
if (
empty( $item ) ||
is_wp_error( $item ) ||
! is_array( $item ) ||
empty( $item['migration_id'] ) ||
BackgroundMigrationProcess::all_stages_processed( $item ) ||
empty( $migration )
) {
return $item;
}
// Get the migration ID as we'll need it from here on down.
$migration_id = $item['migration_id'];
// Is there a timeout record for a previous migration that should be
// cleaned up before we start sending our first in-progress message?
$timeout = get_site_transient( $timeout_key );
if ( ! empty( $timeout ) && $migration_id !== $timeout ) {
delete_site_transient( $timeout_key );
} elseif ( ! empty( $timeout ) ) {
// Migration's timeout hasn't been reached yet.
return $item;
}
/**
* Change how often in-progress updates are sent.
*
* Returning zero disables sending in-progress updates.
*
* @param int $seconds Default is 300 (every 5 minutes).
*/
$interval = apply_filters( 'wpmdb_in_progress_tracking_interval', 5 * MINUTE_IN_SECONDS );
if ( ! is_int( $interval ) || empty( max( 0, $interval ) ) ) {
return $item;
}
// Log an in-progress update if we can successfully set a timeout until the next update.
if ( set_site_transient( $timeout_key, $migration_id, $interval ) ) {
$this->send_migration_update( 'in-progress' );
}
return $item;
}
/**
* Log migration complete
*
* @param array|WP_Error $state_data
* @param array|bool|WP_Error $result
*
* @return void
*
* @handles wpmdb_after_finalize_migration
*/
public function log_migration_complete( $state_data, $result ) {
if ( is_wp_error( $state_data ) || is_wp_error( $result ) ) {
return;
}
$this->send_migration_update();
}
/**
* Log migration cancellation
*
* @return void
*
* @handles wpmdb_track_migration_cancel
*/
public function log_migration_cancellation() {
$this->send_migration_update( 'cancelled' );
}
/**
* Logs migration error
*
* @param array $error
*
* @handles wpmdb_track_migration_error
*/
public function log_migration_error( $error ) {
$data = [
'error_code' => $error['code'],
'error_text' => $error['message'],
'error_data' => $error['data'],
];
$this->send_migration_update( 'error', $data );
}
/**
* Log Migration Start Event
*
* @param string $migration_id
*
* @return void
*
* @handles wpmdb_migration_started
*/
public function log_migration_start( $migration_id ) {
$license_key = $this->license->get_licence_key();
if ( Util::appEnv() === 'pro' && empty( $license_key ) ) {
return;
}
$state_data = Persistence::getStateData();
if ( is_wp_error( $state_data ) || empty( $migration_id ) ) {
return;
}
$settings = $this->settings;
if ( empty( $settings['allow_tracking'] ) ) {
return;
}
do_action( 'wpmdb_log_migration_event', $state_data );
$api_url = $this->api_url . '/event';
$cookie = false === Persistence::getRemoteWPECookie() ? 0 : 1;
$log_data = array(
'local_timestamp' => time(),
'licence_key' => $license_key,
'cli' => false, // TODO: Is this needed, useful, and obtainable?
'setting-compatibility_plugin_installed' => $this->filesystem->file_exists( $this->props->mu_plugin_dest ),
'remote_cookie' => $cookie,
'local_platform' => $state_data['site_details']['local']['platform'],
'local_profile_count' => count( Persistence::getSavedProfiles() ),
'local_has_root_files' => count( $state_data['site_details']['local']['root'] ) > 0,
'remote_platform' => $state_data['site_details']['remote']['platform'],
'remote_plugins' => $state_data['site_details']['remote']['plugins'],
'remote_wordfence' => $state_data['site_details']['remote']['wordfence'],
);
if ( array_key_exists( 'site_migration', $state_data ) ) {
$log_data['site_migration'] = $state_data['site_migration'];
}
// ***+=== @TODO - revisit usage of parse_migration_form_data
foreach ( $this->form_data->parse_and_save_migration_form_data( $state_data['form_data'] ) as $key => $val ) {
if ( 'connection_info' === $key ) {
continue;
}
//remove items added for background processing
$properties_to_skip = [ 'remote_site', 'local_site', 'media_files', 'theme_plugin_files' ];
if ( ! in_array( $key, $properties_to_skip ) ) {
$log_data[ 'profile-' . $key ] = $val;
}
}
foreach ( $settings as $key => $val ) {
if ( 'profiles' === $key || 'key' === $key ) {
continue;
}
$log_data[ 'setting-' . $key ] = $val;
}
foreach ( $GLOBALS['wpmdb_meta'] as $plugin => $arr ) {
$log_data[ $plugin . '-active' ] = true;
$log_data[ $plugin . '-version' ] = $arr['version'];
}
foreach ( $state_data['site_details'] as $site => $info ) {
$log_data[ $site . '-site_url' ] = $info['site_url'];
$log_data[ $site . '-home_url' ] = $info['home_url'];
$log_data[ $site . '-prefix' ] = $info['prefix'];
$log_data[ $site . '-is_multisite' ] = $info['is_multisite'];
if ( isset( $info['subsites'] ) && is_array( $info['subsites'] ) ) {
$log_data[ $site . '-subsite_count' ] = count( $info['subsites'] );
}
$log_data[ $site . '-is_subdomain_install' ] = $info['is_subdomain_install'];
$log_data[ $site . '-pwp_name' ] = empty( $info['pwp_name'] ) ? '' : $info['pwp_name'];
}
$diagnostic_log = [];
foreach ( $this->error_log->get_diagnostic_info() as $group_name => $data ) {
foreach ( $data as $key => $val ) {
if ( 0 === $key ) {
continue;
}
$key_name = $group_name;
if ( is_string( $key ) ) {
$key_name .= "-{$key}";
}
$diagnostic_log[ $key_name ] = $val;
}
}
$log_data['diagnostic_log'] = $diagnostic_log;
foreach ( $log_data as $key => $val ) {
if ( strpos( $key, 'count' ) !== false || is_array( $val ) ) {
continue;
}
if ( '1' === $val ) {
$log_data[ $key ] = true;
continue;
}
if ( '0' === $val ) {
$log_data[ $key ] = false;
continue;
}
if ( 'true' === $val ) {
$log_data[ $key ] = true;
continue;
}
if ( 'false' === $val ) {
$log_data[ $key ] = false;
}
}
$log_data['migration_guid'] = $migration_id;
$filtered_log_data = apply_filters( 'wpmdb_usage_tracking_start_data', $log_data );
// Check that the filter returned an array before assigning.
// Discard the filtered value if it's not valid.
if ( is_array( $filtered_log_data ) ) {
$log_data = $filtered_log_data;
}
$encoded_log_data = $this->encode_log_data( $log_data );
if ( empty( $encoded_log_data ) ) {
return;
}
$remote_post_args = array(
'timeout' => 60,
'method' => 'POST',
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => $encoded_log_data,
'reject_unsafe_urls' => false,
);
$result = wp_remote_post( $api_url, $remote_post_args );
if ( is_wp_error( $result ) || $result['response']['code'] >= 400 ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( 'Error logging migration event' );
error_log( print_r( $result, true ) );
}
$this->error_log->log_error( 'Error logging Migration event', $result );
}
}
public function template_notice_enable_usage_tracking( $notifications ) {
if ( Util::isPro() && ! is_bool( $this->settings['allow_tracking'] ) ) {
$notifications['notice-enable-usage-tracking'] = [
'message' => $this->template->template_to_string( 'notice-enable-usage-tracking', 'pro' ),
'link' => false,
'id' => 'notice-enable-usage-tracking',
'custom_link' => 'usage_tracking',
];
}
return $notifications;
}
/**
* Escapes error data
*
* @param array|string $error_data
*
* @return mixed
**/
private function handle_error_data( $error_data ) {
if ( is_array( $error_data ) ) {
return Util::sanitize_array_recursive( $error_data, 'htmlspecialchars' );
}
if ( is_string( $error_data ) ) {
return htmlspecialchars( $error_data );
}
return null;
}
/**
* JSON encode data, using partial output on error.
*
* @param array $log_data Data to be sent.
*
* @return string
*/
private function encode_log_data( $log_data ) {
$encoded_log_data = json_encode( $log_data );
if ( false === $encoded_log_data ) {
error_log( 'wpmdb_json_encode_error: ' . json_last_error_msg() );
// Re-encode with errors being replaced and an additional value to say what happened.
if ( is_array( $log_data ) ) {
$log_data['json_encoding_error'] = true;
}
$encoded_log_data = json_encode( $log_data, JSON_PARTIAL_OUTPUT_ON_ERROR );
}
return $encoded_log_data;
}
}