HEX
Server: Apache/2.4.18 (Ubuntu)
System: Linux phubuntu06.apexhosting.com 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64
User: master06 (1000)
PHP: 7.0.33-0ubuntu0.16.04.16
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
Upload Files
File: //proc/thread-self/cwd/wp-content/plugins/divi-builder-2/includes/builder/ab-testing.php
<?php
// Prevent file from being loaded directly
if ( ! defined( 'ABSPATH' ) ) {
	die( -1 );
}

if ( ! defined( 'ET_PB_AB_DB_VERSION' ) ) {
	define( 'ET_PB_AB_DB_VERSION', 1.0 );
}

/**
 * AJAX endpoint for builder data
 * @return void
 */
function et_pb_ab_builder_data() {
	$defaults = array(
		'et_pb_ab_nonce'    => false,
		'et_pb_ab_test_id'  => false,
		'et_pb_ab_duration' => 'week',
	);

	$post = wp_parse_args( $_POST, $defaults );

	// Verify nonce
	if ( ! wp_verify_nonce( $post['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) {
		die( -1 );
	}

	// Verify user permission
	if ( ! current_user_can( 'edit_posts' ) || ! et_pb_is_allowed( 'ab_testing' ) ) {
		die( -1 );
	}

	// Whitelist the duration value
	$duration = in_array( $post['et_pb_ab_duration'], et_pb_ab_get_stats_data_duration() ) ? $post['et_pb_ab_duration'] : $defaults['et_pb_ab_duration'];

	// Get data
	$output = et_pb_ab_get_stats_data( intval( $post['et_pb_ab_test_id'] ), $duration );

	// Print output
	die( json_encode( $output ) );
}
add_action( 'wp_ajax_et_pb_ab_builder_data', 'et_pb_ab_builder_data' );

/**
 * Get Split testing subject ranking data
 * @return array
 */
function et_pb_ab_get_saved_subjects_ranks( $post_id ) {
	global $post;

	// Make sure that there are $post_id
	if ( ! isset( $post_id ) && isset( $post->ID ) ) {
		$post_id = $post->ID;
	}

	// Get list of subjects
	$subject_list = get_post_meta( $post_id, '_et_pb_ab_subjects', true );
	$subjects_ids = explode( ',', $subject_list );
	$subjects     = array();
	$goal_slug    = et_pb_ab_get_goal_module( $post_id );
	$rank_metrics = in_array( $goal_slug, et_pb_ab_get_modules_have_conversions() ) ? 'conversions' : 'clicks';

	if ( ! empty( $subjects_ids ) ) {
		// Get conversion rate data
		$subjects_ranks = et_pb_ab_get_subjects_ranks( $post_id, $rank_metrics, 'all' );

		// Sort from high to low and mantain key association
		arsort( $subjects_ranks );

		// Loop saved subject ids
		foreach ( $subjects_ids as $subject_id ) {
			$subject_key = 'subject_' . $subject_id;
			$subject_rank = isset( $subjects_ranks[ $subject_key ] ) ? array_search( $subjects_ranks[ $subject_key ], array_values( $subjects_ranks ) ) + 1 : false;

			// Check whether current subject has saved conversion rate data or not
			if ( $subject_rank ) {
				$subjects[ $subject_key ] = array(
					'percentage' => esc_html( $subjects_ranks[ $subject_key ] . '%' ),
					'rank'       => esc_attr( $subject_rank ),
				);
			}
		}
	}

	return $subjects;
}

/**
 * Define ranking-based subject color
 * @return array
 */
function et_pb_ab_get_subject_rank_colors() {
	return array_map( 'et_sanitize_alpha_color', apply_filters( 'et_pb_ab_get_subject_rank_colors', array(
		'#F3CB57',
		'#F8B852',
		'#F8A653',
		'#F88F55',
		'#F87356',
		'#F95A57',
		'#EA5552',
		'#DB514F',
		'#CE4441',
		'#BF2F2C',
		'#AA201C',
		'#920E08',
		'#7E0000',
	) ) );
}

/**
 * Print Split testing subject-ranking color scheme
 *
 * @return string inline CSS styling for subject rank
 */
function et_pb_ab_get_subject_rank_colors_style() {
	$style  = '';
	$colors = et_pb_ab_get_subject_rank_colors();
	$index  = 1;

	foreach ( $colors as $color ) {
		$style .= sprintf(
			'.et_pb_ab_subject.rank-%1$s .et_pb_module_block,
			.et_pb_ab_subject.rank-%1$s.et_pb_section .et-pb-controls,
			.et_pb_ab_subject.rank-%1$s.et_pb_row .et-pb-controls,
			.et_pb_ab_subject.rank-%1$s.et_pb_module_block {
				background: %2$s;
			}',
			esc_html( $index ),
			esc_html( $color )
		);
		$index++;
	}

	return $style;
}

/**
 * Get subjects' ranks
 *
 * @param int    post ID
 * @param string ranking basis. This can be any value on data's subjects_totals
 *               view_page|read_page|view_goal|read_goal|click_goal|con_goal|clicks|reads|bounces|engagements|conversions
 * @param string duration of the data that is used
 * @return array key = `subject_` + subject_id as key and the value as value, sorted in ascending
 */
function et_pb_ab_get_subjects_ranks( $post_id, $ranking_basis = 'engagements', $duration = 'week' ) {
	$data = et_pb_ab_get_stats_data( $post_id, $duration );
	$subjects = et_pb_ab_get_subjects( $post_id, 'array', 'subject_' );

	if ( isset( $data['subjects_totals'] ) && ! empty( $data['subjects_totals'] ) && ! empty( $subjects ) ) {
		// Pluck data
		$ranks = wp_list_pluck( $data['subjects_totals'], $ranking_basis );

		// Remove inactive subjects from ranks
		foreach ( $ranks as $rank_key => $rank_value ) {
			if ( ! in_array( $rank_key, $subjects ) ) {
				unset( $ranks[ $rank_key ] );
			}
		}

		// Sort rank
		arsort( $ranks );
	} else {
		$ranks = array();
	}

	return $ranks;
}

/**
 * Get formatted stats data that is used by builder's Split testing stats
 *
 * @param int    post ID
 * @param string day|week|month|all duration of stats
 * @param string has to be in Y-m-d H:i:s format
 * @return array stats data
 */
function et_pb_ab_get_stats_data( $post_id, $duration = 'week', $time = false, $force_update = false, $is_cron_task = false ) {
	global $wpdb;

	$post_id = intval( $post_id );
	$goal_slug    = et_pb_ab_get_goal_module( $post_id );
	$rank_metrics = in_array( $goal_slug, et_pb_ab_get_modules_have_conversions() ) ? 'conversions' : 'clicks';

	// Get subjects
	$subjects    = et_pb_ab_get_subjects( $post_id, 'array', 'subject_', $is_cron_task );
	$subjects_id = et_pb_ab_get_subjects( $post_id, 'array', false, $is_cron_task );

	// Get cached data
	$cached_data = get_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration );

	// Get rank coloring scheme
	$subject_rank_colors = et_pb_ab_get_subject_rank_colors();

	// return cached logs if exist and if force_update == false
	if ( $cached_data && ! $force_update ) {
		// Remove inactive subjects
		if ( isset( $cached_data['subjects_id'] ) && ! empty( $cached_data['subjects_id'] ) ) {
			foreach ( $cached_data['subjects_id'] as $subject_id_key => $subject_id_value ) {
				if ( ! in_array( $subject_id_value, $subjects_id ) ) {
					unset( $cached_data['subjects_id'][ $subject_id_key ] );
				}
			}
		}

		if ( isset( $cached_data['subjects_logs'] ) && ! empty( $cached_data['subjects_logs'] ) ) {
			foreach ( $cached_data['subjects_logs'] as $subject_log_id => $subject_logs ) {
				if ( ! in_array( $subject_log_id, $subjects ) ) {
					unset( $cached_data['subjects_logs'][ $subject_log_id ] );
				}
			}
		}

		if ( isset( $cached_data['subjects_analysis'] ) && ! empty( $cached_data['subjects_analysis'] ) ) {
			foreach ( $cached_data['subjects_analysis'] as $subject_analysis_id => $subject_analysis ) {
				if ( ! in_array( $subject_analysis_id, $subjects ) ) {
					unset( $cached_data['subjects_analysis'][ $subject_analysis_id ] );
				}
			}
		}

		if ( isset( $cached_data['subjects_totals'] ) && ! empty( $cached_data['subjects_totals'] ) ) {
			$subject_totals_index = 0;
			foreach ( $cached_data['subjects_totals'] as $subject_total_id => $subject_totals ) {
				if ( ! in_array( $subject_total_id, $subjects ) ) {
					unset( $cached_data['subjects_totals'][ $subject_total_id ] );
					continue;
				}
			}

			// Rank by engagement
			$cached_subjects_ranks = wp_list_pluck( $cached_data['subjects_totals'], $rank_metrics );
			$cached_subjects_ranks_index = 0;

			// Sort from high to low, mantain keys
			arsort( $cached_subjects_ranks );

			// Push color data
			foreach ( $cached_subjects_ranks as $subject_rank_id => $subject_rank_value ) {
				$cached_data['subjects_totals'][ $subject_rank_id ]['color'] = isset( $subject_rank_colors[ $cached_subjects_ranks_index ] ) ? $subject_rank_colors[ $cached_subjects_ranks_index ] : '#7E0000';

				$cached_subjects_ranks_index++;
			}
		}

		return $cached_data;
	}

	$table_name = $wpdb->prefix . 'et_divi_ab_testing_stats';

	// do nothing if no stats table exists in current WP
	if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) == $table_name ) {
		return false;
	}

	// Main placeholder
	$event_types       = et_pb_ab_get_event_types();
	$analysis_types    = et_pb_ab_get_analysis_types();
	$analysis_formulas = et_pb_ab_get_analysis_formulas();
	$time              = $time ? $time : date( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
	$stats             = array(
		'subjects_id'       => $subjects_id,
		'subjects_logs'     => array(),
		'subjects_analysis' => array(),
		'subjects_totals'   => array(),
		'events_totals'     => array(),
		'dates'             => array(),
	);

	// Get all logs in test
	switch ( $duration ) {
		case 'all':
			$date_range_interval = 'week';
			$query = $wpdb->prepare(
				"SELECT subject_id, event, YEARWEEK(record_date) AS 'date', COUNT(id) AS 'count' FROM {$table_name} WHERE test_id = %d GROUP BY subject_id, YEARWEEK(record_date), event",
				$post_id
			);
			break;

		case 'month':
			$date_range_interval = 'day';
			$query = $wpdb->prepare(
				"SELECT subject_id, event, DATE(record_date) AS 'date', COUNT(id) AS 'count' FROM {$table_name} WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 MONTH ) GROUP BY subject_id, DAYOFMONTH(record_date), event",
				$post_id,
				$time,
				$time
			);
			break;

		case 'day':
			$date_range_interval = 'hour';
			$query = $wpdb->prepare(
				"SELECT subject_id, event, DATE_FORMAT(record_date, %s) AS 'date', COUNT(id) AS 'count' FROM {$table_name} WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 DAY ) GROUP BY subject_id, HOUR(record_date), event",
				'%Y-%m-%d %H:00',
				$post_id,
				$time,
				$time
			);
			break;

		default:
			$date_range_interval = 'day';
			$query = $wpdb->prepare(
				"SELECT subject_id, event, DATE(record_date) AS 'date', COUNT(id) AS 'count' FROM {$table_name} WHERE test_id = %d AND record_date <= %s AND record_date > DATE_SUB( %s, INTERVAL 1 WEEK ) GROUP BY subject_id, DAYOFMONTH(record_date), event",
				$post_id,
				$time,
				$time
			);
			break;
	}

	$results = $wpdb->get_results( $query );
	if ( ! empty( $results ) ) {
		// Get min and max timestamp based on query result
		$min_max_date = et_pb_ab_get_min_max_timestamp( $results, $date_range_interval );

		// Create default list
		$date_list = et_pb_ab_get_date_range( $min_max_date['min'], $min_max_date['max'], $date_range_interval );

		// Insert date list to main placeholder
		$stats['dates'] = $date_list;

		// Format YYYYWW format on all-time stats into human-readable format (M jS)
		foreach ( $stats['dates'] as $date_key => $date_time ) {
			if ( 'all' === $duration ) {
				$stats['dates'][ $date_key ] = date( 'M jS', strtotime( substr( $date_time, 0, 4 ) . 'W' . substr( $date_time, 4, 2 ) ) );
			} else if ( 'day' === $duration ) {
				$stats['dates'][ $date_key ] = date( 'H:i', strtotime( $date_time ) );
			} else {
				$stats['dates'][ $date_key ] = date( 'M jS', strtotime( $date_time ) );
			}
		}

		// Fill subject logs placeholder with proper default
		$stats['subjects_logs'] = array_fill_keys(
			$subjects,
			array_fill_keys(
				$event_types,
				array_fill_keys(
					$date_list,
					0
				)
			)
		);

		// Loop query result and place into placeholder
		foreach ( $results as $log ) {
			if ( ! in_array( $log->subject_id, $subjects_id ) ) {
				continue;
			}

			$stats['subjects_logs'][ "subject_{$log->subject_id}" ][ $log->event ][ $log->date ] = $log->count;
		}

		// Determine logs' totals and run analysis
		foreach ( $stats['subjects_logs'] as $subject_log_id => $subject_log ) {

			// Push stats total data
			foreach ( $subject_log as $log_type => $logs ) {
				$stats['subjects_totals'][ $subject_log_id ][ $log_type ] = array_sum( $logs );
			}

			// Run analysis for stats' total data
			foreach ( $analysis_types as $analysis_type ) {
				$numerator_event   = $analysis_formulas[ $analysis_type  ]['numerator'];
				$denominator_event = $analysis_formulas[ $analysis_type  ]['denominator'];
				$numerator         = isset( $stats['subjects_totals'][ $subject_log_id ][ $numerator_event ] ) ? $stats['subjects_totals'][ $subject_log_id ][ $numerator_event ] : 0;
				$denominator       = isset( $stats['subjects_totals'][ $subject_log_id ][ $denominator_event ] ) ? $stats['subjects_totals'][ $subject_log_id ][ $denominator_event ] : 0;
				$analysis          = $denominator === 0 ? 0 : floatval( number_format( ( $numerator / $denominator ) * 100, 2 ) );

				if ( $analysis_formulas[ $analysis_type ]['inverse'] ) {
					$analysis = 100 - $analysis;
				}

				$stats['subjects_totals'][ $subject_log_id ][ $analysis_type ] = $analysis;
			}

			// Run analysis for each log date
			foreach ( $date_list as $log_date ) {

				// Run analysis per analysis type
				foreach ( $analysis_types as $analysis_type ) {
					$numerator_event   = $analysis_formulas[ $analysis_type  ]['numerator'];
					$denominator_event = $analysis_formulas[ $analysis_type  ]['denominator'];
					$numerator         = isset( $stats['subjects_logs'][ $subject_log_id ][ $numerator_event ][ $log_date ] ) ? intval( $stats['subjects_logs'][ $subject_log_id ][ $numerator_event ][ $log_date ] ) : 0;
					$denominator       = isset( $stats['subjects_logs'][ $subject_log_id ][ $denominator_event ][ $log_date ] ) ? intval( $stats['subjects_logs'][ $subject_log_id ][ $denominator_event ][ $log_date ] ) : 0;
					$analysis          = $denominator === 0 ? 0 : floatval( number_format( ( $numerator / $denominator ) * 100, 2 ) );

					if ( $analysis_formulas[ $analysis_type ]['inverse'] ) {
						$analysis = 100 - $analysis;
					}

					$stats['subjects_analysis'][ $subject_log_id ][ $analysis_type ][ $log_date ] = $analysis;
				}
			}
		}

		// Push total events data
		foreach ( $event_types as $event_type ) {
			$stats['events_totals'][ $event_type ] = array_sum( wp_list_pluck( $stats['subjects_totals'], $event_type ) );
		}

		foreach ( $analysis_types as $analysis_type ) {
			$analysis_data = wp_list_pluck( $stats['subjects_totals'], $analysis_type );
			$analysis_count = count( $analysis_data );
			$stats['events_totals'][ $analysis_type ] = floatval( number_format( array_sum( $analysis_data ) / $analysis_count, 2 ) );
		}

		// Rank by engagement
		$subjects_ranks = wp_list_pluck( $stats['subjects_totals'], $rank_metrics );
		$subjects_ranks_index = 0;

		// Sort from high to low, mantain keys
		arsort( $subjects_ranks );

		// Push color data
		foreach ( $subjects_ranks as $subject_rank_id => $subject_rank_value ) {
			$stats['subjects_totals'][ $subject_rank_id ]['color'] = isset( $subject_rank_colors[ $subjects_ranks_index ] ) ? $subject_rank_colors[ $subjects_ranks_index ] : '#7E0000';

			$subjects_ranks_index++;
		}

		// update cache
		set_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration, $stats, DAY_IN_SECONDS );
	} else {
		// remove the cache if no logs found
		delete_transient( 'et_pb_ab_' . $post_id . '_stats_' . $duration );
		return false;
	}

	return $stats;
}

/**
 * Outputs get data stats duration
 *
 * @return array of data
 */
function et_pb_ab_get_stats_data_duration() {
	return apply_filters( 'et_pb_ab_get_stats_data_duration', array(
		'day',
		'week',
		'month',
		'all',
	) );
}

/**
 * Get subjects of particular post / Split test
 *
 * @param int    post id
 * @param string array|string type of output
 * @param mixed  string|bool  prefix that should be prepended
 */
function et_pb_ab_get_subjects( $post_id, $type = 'array', $prefix = false, $is_cron_task = false ) {
	$subjects_data = get_post_meta( $post_id, '_et_pb_ab_subjects', true );
	$fb_enabled = function_exists( 'et_fb_enabled' ) ? et_fb_enabled() : false;

	// Get autosave/draft subjects if post hasn't been published
	if ( ! $is_cron_task && ! $subjects_data && $fb_enabled && 'publish' !== get_post_status() ) {
		$subjects_data = get_post_meta( $post_id, '_et_pb_ab_subjects_draft', true );
	}

	// If user wants string
	if ( 'string' === $type ) {
		return $subjects_data;
	}

	// Convert into array
	$subjects = explode(',', $subjects_data );

	if ( ! empty( $subjects ) && $prefix ) {

		$prefixed_subjects = array();

		// Loop subject, add prefix
		foreach ( $subjects as $subject ) {
			$prefixed_subjects[] = $prefix . (string) $subject;
		}

		return $prefixed_subjects;
	}

	return $subjects;
}

/**
 * Get list of Split testing event type
 *
 * @return array of event types
 */
function et_pb_ab_get_event_types() {
	return apply_filters( 'et_pb_ab_get_event_types', array(
		'view_page',
		'read_page',
		'view_goal',
		'read_goal',
		'click_goal',
		'con_goal',
		'con_short',
	) );
}

/**
 * Get min and max timestamp from returned MySQL query
 *
 * @param array  MySQL returned value. Expected to be array( array ( 'date' => 'YYYY-MM-DD' ) ) format
 * @param string day|week
 * @return array using min and max key
 */
function et_pb_ab_get_min_max_timestamp( $query_result, $interval = 'day' ) {
	$output = array(
		'min' => false,
		'max' => false,
	);

	// Get all available dates from logs
	$dates  = array_unique( wp_list_pluck( $query_result, 'date' ) );

	// Sort low-to-high and reset array keys
	sort( $dates );

	// Get min and max dates from logs
	$min_date = $dates[0];
	$max_date = $dates[ ( count( $dates ) - 1 ) ];

	switch ( $interval ) {
		case 'week':
			$output['min'] = strtotime( substr( $min_date, 0, 4 ) . 'W' . substr( $min_date, 4, 2 ) );
			$output['max'] = strtotime( substr( $max_date, 0, 4 ) . 'W' . substr( $max_date, 4, 2 ) );
			break;

		default:
			$output['min'] = strtotime( $min_date );
			$output['max'] = strtotime( $max_date );
			break;
	}

	return $output;
}

/**
 * Get all days between min and max dates from logs
 *
 * @param int start date timestamp
 * @param int end date timestamp
 * @param string day|week interval of rage
 * @return array of dates
 */
function et_pb_ab_get_date_range( $min_date_timestamp, $max_date_timestamp, $interval = 'day' ) {
	$day_timestamp = $min_date_timestamp;
	$full_dates    = array();

	switch ( $interval ) {
		case 'week':
			$date_format = 'YW';
			$time_interval = '+1 week';
			break;

		case 'hour':
			$date_format = 'Y-m-d H:i';
			$time_interval = '+1 hour';
			break;

		default:
			$date_format = 'Y-m-d';
			$time_interval = '+1 day';
			break;
	}

	while ( $day_timestamp <= $max_date_timestamp ) {
		$full_dates[] = date( $date_format, $day_timestamp );
		$day_timestamp = strtotime( $time_interval, $day_timestamp );
	}

	return $full_dates;
}

/**
 * Get list of Split analysis types
 *
 * @return array analysis types
 */
function et_pb_ab_get_analysis_types() {
	return apply_filters( 'et_pb_ab_get_analysis_types', array(
		'clicks',
		'reads',
		'bounces',
		'engagements',
		'conversions',
		'shortcode_conversions',
	) );
}

/**
 * Get numerator and denominator of various stats types
 *
 * @return array stats' data type formula
 */
function et_pb_ab_get_analysis_formulas() {
	return apply_filters( 'et_pb_ab_get_analysis_formulas', array(
		'clicks' => array(
			'numerator'   => 'click_goal',
			'denominator' => 'view_page',
			'inverse'     => false,
		),
		'reads' => array(
			'numerator'   => 'read_goal',
			'denominator' => 'view_page',
			'inverse'     => false,
		),
		'bounces' => array(
			'numerator'   => 'read_page',
			'denominator' => 'view_page',
			'inverse'     => true,
		),
		'engagements' => array(
			'numerator'   => 'read_goal',
			'denominator' => 'view_goal',
			'inverse'     => false,
		),
		'conversions' => array(
			'numerator'   => 'con_goal',
			'denominator' => 'view_page',
			'inverse'     => false,
		),
		'shortcode_conversions' => array(
			'numerator'   => 'con_short',
			'denominator' => 'view_page',
			'inverse'     => false,
		),
	) );
}

/**
 * List modules' slug which has conversions support
 *
 * @return array slugs of modules which have conversions support
 */
function et_pb_ab_get_modules_have_conversions() {
	return apply_filters( 'et_pb_ab_get_modules_have_conversions', array(
		'et_pb_shop',
		'et_pb_contact_form',
		'et_pb_signup',
		'et_pb_comments',
	) );
}

/**
 * Check whether Split testing active on current page
 *
 * @return bool
 */
function et_is_ab_testing_active() {
	$post_id = apply_filters( 'et_is_ab_testing_active_post_id', get_the_ID() );

	$split_test_status = 'on' === get_post_meta( $post_id, '_et_pb_use_ab_testing', true );

	$fb_enabled = function_exists( 'et_fb_enabled' ) ? et_fb_enabled() : false;

	if ( ! $split_test_status && $fb_enabled && 'publish' !== get_post_status() ) {
		$split_test_status = 'on' === get_post_meta( $post_id, '_et_pb_use_ab_testing_draft', true );
	}

	return $split_test_status;
}

/**
 * Check whether split test has report
 *
 * @return bool
 */
function et_pb_ab_has_report( $post_id ) {
	global $wpdb;

	if ( ! et_is_ab_testing_active() ) {
		return false;
	}

	$table_name = $wpdb->prefix . 'et_divi_ab_testing_stats';

	$query = $wpdb->prepare(
		"SELECT * FROM {$table_name} WHERE test_id = %d",
		$post_id
	);

	$result = $wpdb->get_row( $query ) ? true : false;

	return apply_filters( 'et_pb_ab_has_report', $result, $post_id );
}

/**
 * Check the status of the ab db version
 * @return bool
 */
function et_pb_db_status_up_to_date() {
	return ( $ab_db_settings = get_option( 'et_pb_ab_test_settings' ) ) && version_compare( $ab_db_settings['db_version'], ET_PB_AB_DB_VERSION, '>=' );
}

/**
 * Create Split testing table needed for Split testing feature
 *
 * @return void
 */
function et_pb_create_ab_tables() {
	if ( isset( $_POST['et_pb_ab_nonce'] ) && ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) {
		die( -1 );
	}

	// Verify user permission
	if ( ! current_user_can( 'edit_posts' ) || ! et_pb_is_allowed( 'ab_testing' ) ) {
		die( -1 );
	}

	// Verify update is needed
	if ( et_pb_db_status_up_to_date() ) {
		die( -1 );
	}

	global $wpdb;

	$stats_table_name = $wpdb->prefix . 'et_divi_ab_testing_stats';
	$client_subject_table_name = $wpdb->prefix . 'et_divi_ab_testing_clients';

	/*
	 * We'll set the default character set and collation for this table.
	 * If we don't do this, some characters could end up being converted
	 * to just ?'s when saved in our table.
	 */
	$charset_collate = '';

	if ( ! empty( $wpdb->charset ) ) {
		$charset_collate = sprintf(
			'DEFAULT CHARACTER SET %1$s',
			sanitize_text_field( $wpdb->charset )
		);
	}

	if ( ! empty( $wpdb->collate ) ) {
		$charset_collate .= sprintf(
			' COLLATE %1$s',
			sanitize_text_field( $wpdb->collate )
		);
	}

	$sql_stats = "CREATE TABLE $stats_table_name (
		id mediumint(9) NOT NULL AUTO_INCREMENT,
		test_id varchar(20) NOT NULL,
		subject_id varchar(20) NOT NULL,
		record_date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
		event varchar(10) NOT NULL,
		client_id varchar(32) NOT NULL,
		UNIQUE KEY id (id)
	) $charset_collate;";

	$sql_client_subject = "CREATE TABLE $client_subject_table_name (
		id mediumint(9) NOT NULL AUTO_INCREMENT,
		test_id varchar(20) NOT NULL,
		subject_id varchar(20) NOT NULL,
		client_id varchar(32) NOT NULL,
		UNIQUE KEY id (id)
	) $charset_collate;";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';

	dbDelta( array( $sql_stats, $sql_client_subject ) );

	$db_settings = array(
		'db_version' => ET_PB_AB_DB_VERSION,
	);

	update_option( 'et_pb_ab_test_settings', $db_settings );

	// Register Split Testing cron
	et_pb_create_ab_cron();

	die( 'success' );
}
add_action( 'wp_ajax_et_pb_create_ab_tables', 'et_pb_create_ab_tables' );

/**
 * Handle adding the Split testing log record via ajax
 *
 * @return void
 */
function et_pb_update_stats_table() {
	if ( isset( $_POST['et_ab_log_nonce'] ) && ! wp_verify_nonce( $_POST['et_ab_log_nonce'], 'et_ab_testing_log_nonce' ) ) {
		die( -1 );
	}

	$stats_data_json = str_replace( '\\', '',  $_POST['stats_data_array'] );
	$stats_data_array = json_decode( $stats_data_json, true );

	et_pb_add_stats_record( $stats_data_array );

	die( 1 );
}
add_action( 'wp_ajax_et_pb_update_stats_table', 'et_pb_update_stats_table' );
add_action( 'wp_ajax_nopriv_et_pb_update_stats_table', 'et_pb_update_stats_table' );

/**
 * List of valid split testing refresh interval duration
 *
 * @return array
 */
function et_pb_ab_refresh_interval_durations() {
	return apply_filters( 'et_pb_ab_refresh_interval_durations', array(
		'hourly' => 'day',
		'daily'  => 'week',
	));
}

/**
 * Get refresh interval of particular split test
 *
 * @param int     post ID
 * @param string  default interval
 * @return string interval used in particular split test
 */
function et_pb_ab_get_refresh_interval( $post_id, $default = 'hourly' ) {
	$interval = get_post_meta( $post_id, '_et_pb_ab_stats_refresh_interval', true );

	if ( in_array( $interval, array_keys( et_pb_ab_refresh_interval_durations() ) ) ) {
		return apply_filters( 'et_pb_ab_get_refresh_interval', $interval, $post_id );
	}

	return apply_filters( 'et_pb_ab_default_refresh_interval', $default, $post_id );
}

/**
 * Get refresh interval duration of particular split test
 *
 * @param int     post ID
 * @param string  default interval duration
 * @return string test's interval duration
 */
function et_pb_ab_get_refresh_interval_duration( $post_id, $default = 'day' ) {
	$durations = et_pb_ab_refresh_interval_durations();

	$interval = et_pb_ab_get_refresh_interval( $post_id );

	$interval_duration = isset( $durations[ $interval ] ) ? $durations[ $interval ] : $default;

	return apply_filters( 'et_pb_ab_get_refresh_interval_duration', $interval_duration, $post_id );
}

/**
 * Get goal module slug of particular split test
 *
 * @param int     post ID
 * @return string test's goal module slug
 */
function et_pb_ab_get_goal_module( $post_id ) {
	return get_post_meta( $post_id, '_et_pb_ab_goal_module', true );
}

/**
 * Register Divi's Split testing cron
 * There are 2 options - daily and hourly, so schedule 2 events
 * @return void
 */
function et_pb_create_ab_cron() {
	// schedule daily event
	if ( ! wp_next_scheduled( 'et_pb_ab_cron', array( 'interval' => 'daily' ) ) ) {
		wp_schedule_event( time(), 'daily', 'et_pb_ab_cron', array( 'interval' => 'daily' ) );
	}

	// schedule hourly event
	if ( ! wp_next_scheduled( 'et_pb_ab_cron', array( 'interval' => 'hourly' ) ) ) {
		wp_schedule_event( time(), 'hourly', 'et_pb_ab_cron', array( 'interval' => 'hourly' ) );
	}
}

/**
 * Perform Divi's Split testing cron
 *
 * @return void
 */
function et_pb_ab_cron( $args ) {
	$all_tests = et_pb_ab_get_all_tests();
	$interval = isset( $args ) ? $args : 'hourly';

	if ( empty( $all_tests ) ) {
		return;
	}

	// update cache for each test and for each duration
	foreach ( $all_tests as $test ) {
		$current_test_interval = et_pb_ab_get_refresh_interval( $test['test_id'] );

		// determine whether or not we should update the stats for current test depending on interval parameter
		if ( $current_test_interval !== $interval ) {
			continue;
		}

		foreach ( et_pb_ab_get_stats_data_duration() as $duration ) {
			et_pb_ab_get_stats_data( $test['test_id'], $duration, false, true, true );
		}
	}
}
add_action( 'et_pb_ab_cron', 'et_pb_ab_cron' );

function et_pb_ab_clear_cache_handler( $test_id ) {
	if ( ! $test_id ) {
		return;
	}

	foreach ( et_pb_ab_get_stats_data_duration() as $duration ) {
		delete_transient( 'et_pb_ab_' . $test_id . '_stats_' . $duration );
	}
}

function et_pb_ab_clear_cache() {
	// Verify nonce
	if ( isset( $_POST['et_pb_ab_nonce'] ) && ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) {
		die( -1 );
	}

	// Verify user permission
	if ( ! current_user_can( 'edit_posts' ) || ! et_pb_is_allowed( 'ab_testing' ) ) {
		die( -1 );
	}

	$test_id = intval( $_POST['et_pb_test_id'] );

	et_pb_ab_clear_cache_handler( $test_id );

	die( 1 );
}

add_action( 'wp_ajax_et_pb_ab_clear_cache', 'et_pb_ab_clear_cache' );

function et_pb_ab_get_all_tests() {
	global $wpdb;

	$table_name = $wpdb->prefix . 'et_divi_ab_testing_stats';

	if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) == $table_name ) {
		return false;
	}

	// construct sql query to get all the test ID from db
	$sql = "SELECT DISTINCT test_id FROM $table_name";

	// cache the data from conversions table
	$all_tests = $wpdb->get_results( $sql, ARRAY_A );

	return $all_tests;
}

function et_pb_ab_clear_stats() {
	// Verify nonce
	if ( isset( $_POST['et_pb_ab_nonce'] ) && ! wp_verify_nonce( $_POST['et_pb_ab_nonce'], 'ab_testing_builder_nonce' ) ) {
		die( -1 );
	}

	// Verify user permission
	if ( ! current_user_can( 'edit_posts' ) || ! et_pb_is_allowed( 'ab_testing' ) ) {
		die( -1 );
	}

	$test_id = intval( $_POST['et_pb_test_id'] );

	et_pb_ab_remove_stats( $test_id );

	et_pb_ab_clear_cache_handler( $test_id );

	die( 1 );
}
add_action( 'wp_ajax_et_pb_ab_clear_stats', 'et_pb_ab_clear_stats' );

/**
 * Remove Split testing log and clear stats cache
 *
 * @param int post ID
 * @return void
 */
function et_pb_ab_remove_stats( $test_id ) {
	global $wpdb;

	$test_id = intval( $test_id );

	et_pb_ab_clear_cache_handler( $test_id );

	$sql_args = array(
		$test_id,
	);

	foreach ( array( 'stats', 'clients' ) as $table_suffix ) {
		$table_name = $wpdb->prefix . 'et_divi_ab_testing_' . $table_suffix;

		if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) == $table_name ) {
			continue;
		}

		// construct sql query to remove value from DB table
		$sql = "DELETE FROM $table_name WHERE test_id = %d";

		$wpdb->query( $wpdb->prepare( $sql, $sql_args ) );
	}
}

/**
 * Shop trigger DOM
 *
 * @return void
 */
function et_pb_ab_shop_trigger() {
	echo '<div class="et_pb_ab_shop_conversion"></div>';
}
add_action( 'woocommerce_thankyou', 'et_pb_ab_shop_trigger' );

/**
 * Tracking shortcode
 *
 * @return void
 */
function et_pb_split_track( $atts ) {
	$settings = shortcode_atts( array(
		'id' => '',
	), $atts );

	$output = sprintf( '<div class="et_pb_ab_split_track" style="display:none;" data-test_id="%1$s"></div>',
		esc_attr( $settings['id'] )
	);

	return $output;
}
add_shortcode( 'et_pb_split_track', 'et_pb_split_track' );