HEX
Server: Apache/2.4.41 (Ubuntu)
System: Linux ip-172-31-42-149 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 07:00:04 UTC 2025 aarch64
User: ubuntu (1000)
PHP: 7.4.33
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_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //proc/self/cwd/wp-content/plugins/pixel-caffeine/includes/admin/class-aepc-admin-ca-manager.php
<?php
/**
 * Class manager of the Custom Audiences
 *
 * @package Pixel Caffeine
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Class manager of the Custom Audiences
 *
 * @class AEPC_Admin_CA_Manager
 */
class AEPC_Admin_CA_Manager {

	/**
	 * Save the number of all records, useful for pagination.
	 *
	 * @var int
	 */
	protected static $audiences_count;

	/**
	 * AEPC_Admin_CA_Manager Constructor.
	 *
	 * @return void
	 */
	public static function init() {

		// Add php notice.
		add_action( 'admin_init', array( __CLASS__, 'add_notice_for_facebook_debug' ), 99 );

		// Add custom audience warning for the bug fixed.
		add_action( 'admin_init', array( __CLASS__, 'add_custom_audience_bug_warning' ), 99 );
	}

	/**
	 * Add a notice message that inform the user that can't do anything without facebook connection.
	 *
	 * This notice will be shown only on CA page
	 *
	 * @return void
	 */
	public static function add_notice_for_facebook_debug() {
		// @phpcs:disable WordPress.Security.NonceVerification
		if (
			( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
			&& AEPC_Admin::$api->is_debug()
			&& ! empty( $_GET['page'] )
			&& AEPC_Admin_Menu::$page_id === $_GET['page']
			&& ! empty( $_GET['tab'] )
			&& 'custom-audiences' === $_GET['tab']
		) {
			AEPC_Admin_Notices::add_notice( 'info', 'main', __( '<strong>Development mode</strong> via the AEPC_DEACTIVE_FB_REQUESTS constant being defined in wp-config.php or elsewhere. In this mode any facebook api request will be done.', 'pixel-caffeine' ) );
		}
		// @phpcs:enable
	}

	/**
	 * Add a notice message inform the user that they need to create again the custom audience having taxonomy values
	 * as filter because of a bug fixed.
	 *
	 * @return void
	 */
	public static function add_custom_audience_bug_warning() {
		// @phpcs:disable WordPress.Security.NonceVerification
		if (
			( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
			&& ! empty( $_GET['page'] )
			&& AEPC_Admin_Menu::$page_id === $_GET['page']
			&& ! empty( $_GET['tab'] )
			&& 'custom-audiences' === $_GET['tab']
			&& get_option( 'aepc_show_warning_ca_bug' )
		) {
			AEPC_Admin_Notices::add_notice(
				'warning',
				'main',
				__( '<strong>WARNING</strong>: previously a bug occurred in the the creation of custom audiences. This has now been fixed however any existing custom audiences that had CATEGORY or TAG filters <strong>need to be deleted</strong> and <strong>then created again</strong> to ensure the custom audiences are accurate. Other custom audiences are unaffected.', 'pixel-caffeine' ),
				'ca_bug_warning'
			);
		}
		// @phpcs:enable
	}

	/**
	 * Save the CA with form data
	 *
	 * @param array $post_data The array stack of data to save.
	 *
	 * @return true
	 * @throws Exception Throw general exception when error during the saving.
	 */
	public static function save( $post_data ) {

		// Create arguments.
		$args = self::ca_post_data_adapter( 'add', $post_data );

		// Init a new CA instance.
		$ca = new AEPC_Admin_CA();

		// Save first on Facebook and then on DB.
		$ca->create( $args );

		return true;
	}

	/**
	 * Edit the CA with form data
	 *
	 * @param array $post_data The array stack of data to edit.
	 *
	 * @return true
	 * @throws Exception Throw general exception when error during the editing.
	 */
	public static function edit( $post_data ) {

		// Create arguments.
		$args = self::ca_post_data_adapter( 'edit', $post_data );

		// Init a new CA instance.
		$ca = new AEPC_Admin_CA( $post_data['ca_id'] );

		// Save first on Facebook and then on DB.
		$ca->update( $args );

		return true;
	}

	/**
	 * Delete the CA
	 *
	 * @param int $ca_id The Custom Audience ID to delete.
	 *
	 * @return true
	 * @throws Exception Throw general exception when error during the editing.
	 */
	public static function delete( $ca_id ) {

		// Init a new CA instance.
		$ca = new AEPC_Admin_CA( $ca_id );

		// Remove first on Facebook and then on DB.
		$ca->delete();

		return true;
	}

	/**
	 * Convert the input data from request in structured array to save, used on save and edit actions
	 *
	 * @param string $action 'add' or 'edit'.
	 * @param array  $post_data The raw data from request.
	 *
	 * @return array
	 */
	private static function ca_post_data_adapter( $action, $post_data = array() ) {
		$raw_data = array(
			'name'                  => sanitize_text_field( $post_data['ca_name'] ),
			'description'           => sanitize_text_field( $post_data['ca_description'] ),
			'prefill'               => ! empty( $post_data['ca_prefill'] ),
			'retention'             => intval( $post_data['ca_retention'] ),
			'include_url'           => sanitize_text_field( $post_data['ca_include_url'] ),
			'exclude_url'           => sanitize_text_field( $post_data['ca_exclude_url'] ),
			'include_url_condition' => sanitize_text_field( $post_data['ca_include_url_condition'] ),
			'exclude_url_condition' => sanitize_text_field( $post_data['ca_exclude_url_condition'] ),
			'rule'                  => isset( $post_data['ca_rule'] ) ? $post_data['ca_rule'] : array(),
		);

		// Add include URL into rule.
		if ( ! empty( $raw_data['include_url'] ) ) {
			$raw_data['rule'] = array_merge(
				array(
					array(
						'main_condition' => 'include',
						'event_type'     => 'url',
						'event'          => 'url',
						'conditions'     => array(
							array(
								'operator' => $raw_data['include_url_condition'],
								'value'    => $raw_data['include_url'],
							),
						),
					),
				),
				$raw_data['rule']
			);
		}

		// Add exclude URL into rule.
		if ( ! empty( $raw_data['exclude_url'] ) ) {
			$raw_data['rule'] = array_merge(
				array(
					array(
						'main_condition' => 'exclude',
						'event_type'     => 'url',
						'event'          => 'url',
						'conditions'     => array(
							array(
								'operator' => $raw_data['exclude_url_condition'],
								'value'    => $raw_data['exclude_url'],
							),
						),
					),
				),
				$raw_data['rule']
			);
		}

		// Remove empty conditions.
		foreach ( $raw_data['rule'] as $kr => &$rule ) {

			// Force to add conditions key if it doesn't exist.
			if ( ! isset( $rule['conditions'] ) ) {
				$rule['conditions'] = array();
			}

			foreach ( $rule['conditions'] as $kc => $condition ) {
				if (
					isset( $condition['key'] ) && empty( $condition['key'] )
					|| ! isset( $condition['key'] ) && empty( $condition['value'] )
				) {
					unset( $raw_data['rule'][ $kr ]['conditions'][ $kc ] );
				}
			}
		}

		$args = array(
			'name'        => $raw_data['name'],
			'description' => $raw_data['description'],
			'prefill'     => $raw_data['prefill'],
			'retention'   => $raw_data['retention'],
			'rule'        => $raw_data['rule'],
		);

		// Remove prefill field when edit a custom audience.
		if ( 'edit' === $action ) {
			unset( $args['prefill'] );
		}

		return $args;
	}

	/**
	 * Save a new CA indentically to other already created
	 *
	 * @param array $post_data The array stack of data to duplicate.
	 *
	 * @return true
	 * @throws Exception Throw general exception when error during the duplication.
	 */
	public static function duplicate( $post_data ) {

		$ca = new AEPC_Admin_CA( $post_data['ca_id'] );

		// Exit if ca is not existing.
		if ( ! $ca->exists() ) {
			throw new Exception( __( '<strong>Custom audience cannot duplicated</strong> The cluster you selected does not exist.', 'pixel-caffeine' ), 10 );
		}

		// Exit if no name is defined.
		if ( empty( $post_data['ca_name'] ) ) {
			throw new Exception( __( '<strong>Custom audience cannot duplicated</strong> You have to define a name for the new custom audience.', 'pixel-caffeine' ), 11 );
		}

		// Clone.
		$ca->duplicate( $post_data['ca_name'] );

		return true;
	}

	/**
	 * Refresh the approximate counts of all custom audiences
	 *
	 * @return void
	 */
	public static function refresh_approximate_counts() {
		try {
			$audiences = AEPC_Admin::$api->get_audiences( 'approximate_count' );
		} catch ( Exception $e ) {
			return;
		}

		// Set approximate count for each audience on DB.
		foreach ( $audiences as $audience ) {
			$ca = new AEPC_Admin_CA();
			$ca->populate_by_fb_id( $audience->id );
			$ca->set_size( $audience->approximate_count );
			$ca->update();
		}
	}

	/**
	 * Get the audiences saved on Database
	 *
	 * @param string|array|object $args The query config for the audiences retrieving.
	 *
	 * @return AEPC_Admin_CA[]|stdClass[]
	 */
	public static function get_audiences( $args = array() ) {
		global $wpdb;

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

		$q = wp_parse_args(
			$args,
			array(
				'per_page' => 5,
				// @phpcs:ignore WordPress.Security.NonceVerification
				'paged'    => isset( $_GET['paged'] ) ? intval( $_GET['paged'] ) : 1,
				'orderby'  => 'ID',
				'order'    => 'DESC',
				'return'   => 'objects',
			)
		);

		// Get audiences from db.
		$where   = '';
		$limits  = '';
		$orderby = '';

		// Paging.
		if ( $q['paged'] > 0 ) {
			$page = absint( $q['paged'] );
			if ( ! $page ) {
				$page = 1;
			}

			// If 'offset' is provided, it takes precedence over 'paged'.
			if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
				$q['offset'] = absint( $q['offset'] );
				$pgstrt      = $q['offset'] . ', ';
			} else {
				$pgstrt = absint( ( $page - 1 ) * $q['per_page'] ) . ', ';
			}
			$limits = 'LIMIT ' . $pgstrt . $q['per_page'];
		}

		// Order.
		if ( ! empty( $q['orderby'] ) ) {
			$orderby_array = array();
			if ( is_array( $q['orderby'] ) ) {
				foreach ( $q['orderby'] as $_orderby => $order ) {
					$orderby = addslashes_gpc( urldecode( $_orderby ) );
					$parsed  = "$table_name." . sanitize_key( $orderby );

					if ( ! $parsed ) {
						continue;
					}

					$orderby_array[] = $parsed . ' ' . sanitize_key( $orderby );
				}
				$orderby = implode( ', ', $orderby_array );

			} else {
				$q['orderby'] = urldecode( $q['orderby'] );
				$q['orderby'] = addslashes_gpc( $q['orderby'] );

				foreach ( explode( ' ', $q['orderby'] ) as $orderby ) {
					$parsed = sanitize_key( $orderby );
					// Only allow certain values for safety.
					if ( ! $parsed ) {
						continue;
					}

					$orderby_array[] = $parsed;
				}
				$orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );

				if ( empty( $orderby ) ) {
					$orderby = "$table_name.ID " . $q['order'];
				} elseif ( ! empty( $q['order'] ) ) {
					$orderby .= " {$q['order']}";
				}
			}
		}

		if ( ! empty( $orderby ) ) {
			$orderby = 'ORDER BY ' . $orderby;
		}

		$cache_key             = 'query_' . md5( $where . $orderby . $limits );
		$audiences             = wp_cache_get( $cache_key, 'aepc-audiences' );
		self::$audiences_count = wp_cache_get( 'count_' . $cache_key, 'aepc-audiences' );

		if ( false === $audiences ) {
			// Query.
			$audiences = $wpdb->get_results( "SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->prefix}aepc_custom_audiences WHERE 1=1 $where $orderby $limits" ); // phpcs:ignore WordPress.DB

			// Save the number of all records, useful for pagination.
			self::$audiences_count = $wpdb->get_var( 'SELECT FOUND_ROWS()' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery

			// Cache.
			wp_cache_set( $cache_key, $audiences, 'aepc-audiences' );
			wp_cache_set( 'count_' . $cache_key, self::$audiences_count, 'aepc-audiences' );
		}

		// Return raw_objects if requested.
		if ( 'raw' === $q['return'] ) {
			return $audiences;
		}

		// Get instances.
		foreach ( $audiences as &$audience ) {
			$audience = new AEPC_Admin_CA( $audience->ID );
		}

		return $audiences;
	}

	/**
	 * Return the number of all audiences
	 *
	 * @return int
	 */
	public static function get_all_audiences_count() {
		return self::$audiences_count ?: 0;
	}

}