hooks(); } return self::$instance; } /** * Registers all necessary backend hooks for the tracking module. * * @return void */ public function hooks(): void { // add_filter( 'adpresso/ads/fields/base', function( $fields ) { // $fields[] = [ // 'id' => 'link_cloaking', // 'type' => 'checkbox', // 'label' => __( 'Link Masking', 'adpresso' ), // 'description' => __( 'Whether to cloak the ad link using a custom URL.', 'adpresso' ), // 'premium' => true, // 'sanitize_type' => 'uint', // 'default' => false, // 'box' => Ad_URL_Box::class, // ]; // // return $fields; // } ); // Initialize the REST controller $controller = new TrackingController(); $controller->hooks(); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_scripts' ] ); add_filter( 'adpresso_script_dependencies', [ $this, 'add_tracking_dependency' ], 10, 2 ); add_action( 'admin_menu', [ $this, 'register_menu_page' ], PHP_INT_MAX - 1 ); add_action( 'adpresso_load_rest_controllers', [ $this, 'register_rest_controllers' ] ); add_action( 'current_screen', [ $this, 'redirect_to_default_view' ] ); } /** * Will be called at the plugin activation to create the database tables. * * @return void */ public static function activate(): void { global $wpdb; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); $charset_collate = $wpdb->get_charset_collate(); // Table for aggregated stats. $stats_table = $wpdb->prefix . 'adpresso_stats'; $sql_stats = "CREATE TABLE {$stats_table} ( stat_date DATE NOT NULL, ad_id BIGINT(20) UNSIGNED NOT NULL, group_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, placement_id BIGINT(20) UNSIGNED NOT NULL, impressions BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, clicks BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, impressions_av BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, campaign_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (stat_date, placement_id, group_id, ad_id, campaign_id) ) {$charset_collate};"; dbDelta( $sql_stats ); // Table for detailed click log (Pro) $clicks_log_table = $wpdb->prefix . 'adpresso_clicks_log'; $sql_clicks_log = "CREATE TABLE {$clicks_log_table} ( log_id BIGINT(20) NOT NULL AUTO_INCREMENT, timestamp_utc DATETIME NOT NULL, ad_id BIGINT(20) UNSIGNED NOT NULL, group_id BIGINT(20) UNSIGNED NOT NULL, placement_id BIGINT(20) UNSIGNED NOT NULL, ip_hash VARCHAR(64) NOT NULL, user_agent TEXT, target_url TEXT, referrer_url TEXT, is_fraudulent TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (log_id), INDEX idx_ad_id (ad_id), INDEX idx_timestamp (timestamp_utc) ) {$charset_collate};"; dbDelta( $sql_clicks_log ); } /** * Register and initializes the tracking REST controllers. * * @return void */ public function register_rest_controllers(): void { $rest_controller = new AnalyticsController(); $rest_controller->hooks(); } /** * Checks if a redirect to the default view should happen. * * @param WP_Screen $screen * * @return void */ public function redirect_to_default_view( WP_Screen $screen ): void { if ( ! $this->stats_page_hook || $screen->id !== $this->stats_page_hook ) { return; } // Check if the user called the page with parameters, e.g. bookmarks. $query_params = $_GET; unset( $query_params['page'], $query_params['message'], $query_params['updated'] ); if ( ! empty( $query_params ) ) { // The user called a specific URL. Do nothing. return; } // The user called the "naked" page. Check for saved views. $user_id = get_current_user_id(); $saved_params = get_user_meta( $user_id, 'adpresso_stats_default_view', true ); if ( ! empty( $saved_params ) && is_array( $saved_params ) ) { // We found a saved view. Redirect. $base_url = admin_url( 'admin.php?page=adpresso-statistics' ); $redirect_url = add_query_arg( $saved_params, $base_url ); wp_safe_redirect( $redirect_url ); exit; } } /** * Add the tracking scripts as dependencies to the main frontend script. * Makes sure that the tracking system was initialized before events will be fired. * * @param array $deps The current array of dependencies. * @param string $handle The handle of the script that is getting registered. * * @return array The modified dependency array. */ public function add_tracking_dependency( array $deps, string $handle ): array { if ( $handle === 'rendering-pipeline' ) { $deps[] = 'adpresso-tracking'; $deps[] = 'adpresso-tracking-impressions'; } return $deps; } /** * Enqueue frontend scripts for tracking. * * @return void */ public function enqueue_frontend_scripts(): void { wp_enqueue_script( 'adpresso-tracking', plugin_dir_url( __FILE__ ) . 'assets/js/tracking.js', [ 'adpresso-core-events', 'adpresso-core-utils', 'adpresso-condition-evaluator', 'adpresso-placement-controller' ], ADPRESSO_VERSION, true ); wp_enqueue_script( 'adpresso-tracking-impressions', plugin_dir_url( __FILE__ ) . 'assets/js/impression-tracker.js', [ 'adpresso-tracking', 'adpresso-core-events' ], ADPRESSO_VERSION, true ); $is_premium = adpresso_fs()->is_premium(); $active_view = false; $external_tracking_config = []; if ( adpresso_fs()->is_premium() ) { $active_view = (bool) Settings::get_setting( 'activate_active_view', true ); $external_settings = Settings::get_setting( 'activate_external', [] ); if ( $active_view ) { wp_enqueue_script( 'adpresso-tracking-pro', ADPRESSO_BASE_URL . 'pro/core/tracking/assets/js/impression-tracker-pro.js', [ 'adpresso-tracking' ], ADPRESSO_VERSION, true ); } if ( ! empty( $external_settings['external'] ) ) { // Mapping: Provider-Key => Settings-Key in the database. $providers = [ 'google_analytics' => 'analytics_ids', 'matomo' => 'matomo_ids', 'clarity' => 'clarity_ids', ]; foreach ( $providers as $provider_key => $setting_key ) { if ( ! empty( $external_settings[ $provider_key ] ) ) { $ids_string = Settings::get_setting( $setting_key, '' ); if ( ! empty( $ids_string ) ) { $clean_array = array_filter( array_map( 'trim', explode( ',', $ids_string ) ) ); if ( ! empty( $clean_array ) ) { $external_tracking_config[ $provider_key ] = [ 'ids' => array_values( $clean_array ), ]; if ( $provider_key === 'google_analytics' ) { $external_tracking_config['google_analytics']['events'] = [ 'impression' => apply_filters( 'adpresso_tracking_google_analytics_event_impression', 'adpresso_impression' ), 'impression_av' => apply_filters( 'adpresso_tracking_google_analytics_event_impression_av', 'adpresso_impression_av' ), 'click' => apply_filters( 'adpresso_tracking_google_analytics_event_click', 'adpresso_click' ), ]; } } } } } } if ( ! empty( $external_tracking_config ) ) { wp_enqueue_script( 'adpresso-tracking-external', ADPRESSO_BASE_URL . 'pro/core/tracking/assets/js/external-tracking.js', [ 'adpresso-tracking' ], ADPRESSO_VERSION, true ); } } $tracking_config = [ 'rest_url' => esc_url_raw( rest_url( 'adpresso/v1/track' ) ), 'prefix' => Settings::get_setting( 'frontend_prefix', Constants::DEFAULT_FRONTEND_PREFIX ), 'is_pro' => $is_premium, 'batch_time' => 3000, 'external_tracking' => $external_tracking_config, 'active_view' => [ 'enabled' => $active_view, 'threshold' => 0.5, 'time' => 1000, ], ]; wp_localize_script( 'adpresso-tracking', 'adpressoTrackingConfig', $tracking_config ); } /** * Registers the statistics page in the AdPresso admin menu. * * @return void */ public function register_menu_page(): void { $this->stats_page_hook = add_submenu_page( Constants::ADPRESSO_SLUG, __( 'AdPresso Statistics', 'adpresso' ), __( 'Statistics', 'adpresso' ), adpresso_get_capability( 'view_analytics' ), 'adpresso-statistics', [ $this, 'render_stats_page' ], 20 ); } /** * Renders the stats page and includes the React app for this site. * * @return void */ public function render_stats_page(): void { /** @var Assets $assets */ $assets = Plugin::get()->globals[ Assets::class ]; $script_handle = 'analytics-app'; $assets->register_asset_from_manifest( 'modules/tracking/assets/build/', false, [ 'wp-element', 'wp-i18n', 'wp-api-fetch' ], [], $script_handle ); $script_handle = 'adpresso-'.$script_handle; wp_enqueue_script( $script_handle ); wp_set_script_translations( $script_handle, 'adpresso' ); wp_enqueue_style( $script_handle ); $user_id = get_current_user_id(); $default_view = get_user_meta( $user_id, 'adpresso_stats_default_view', true ); $localise_array = [ 'restUrl' => esc_url_raw( rest_url() ), 'apiNonce' => wp_create_nonce( 'wp_rest' ), 'defaultView' => is_array( $default_view ) ? $default_view : null, 'logoUrl' => ADPRESSO_BASE_URL . 'admin/assets/img/adpresso-logo.svg', 'dateFormat' => get_option( 'date_format' ), 'timeFormat' => get_option( 'time_format' ), 'isPro' => adpresso_fs()->can_use_premium_code(), 'pricingUrl' => adpresso_fs()->get_upgrade_url(), 'upgradeUrl' => admin_url( 'admin.php?page=adpresso-pricing' ), ]; wp_localize_script( $script_handle, 'adpressoAnalyticsData', $localise_array ); // Create the HTML root element for the React script to hook in. echo '
'; } }