id, $hide_screen) ){ remove_all_actions('admin_notices'); remove_all_actions('all_admin_notices'); } } /** * init * * Plugins loaded init hook * @return void */ function init() { /** * Include required files. */ $this->include_files(); /** * Redirect to option page after plugin activate */ $this->plugin_redirect_option_page(); /** * Add plugin setting link to the plugin. */ add_filter('plugin_action_links_' . PIXELAVO_PLUGIN_BASE, [$this, 'plugins_setting_links']); } /** * Include Require Files */ function include_files() { require_once (PIXELAVO_PL_PATH . 'includes/helper-functions.php'); require_once (PIXELAVO_PL_PATH . 'admin/admin-setting.php'); require_once (PIXELAVO_PL_PATH . 'includes/add-pixel.php'); require_once (PIXELAVO_PL_PATH . 'includes/modifier.php'); if(pixelavo_is_woocommerce_active()) { require_once (PIXELAVO_PL_PATH . 'includes/pixel-events-data.php'); if(pixelavo_get_option('product_feed', 'pixelavo_settings') == 'on') { require_once (PIXELAVO_PL_PATH . 'includes/pixel-feed.php'); } } if(pixelavo_is_edd_active()) { require_once (PIXELAVO_PL_PATH . 'includes/pixel-edd-events-data.php'); if(pixelavo_get_option('edd_product_feed', 'pixelavo_settings') == 'on') { require_once (PIXELAVO_PL_PATH . 'includes/pixel-edd-feed.php'); } } if(!empty(pixelavo_get_custom_events())) { require_once (PIXELAVO_PL_PATH . 'includes/pixel-custom-events-data.php'); } require_once PIXELAVO_PL_PATH . 'includes/pixel-ajax-events-data.php'; if( is_admin() ){ require_once (PIXELAVO_PL_PATH . 'admin/class-notice-manager.php'); require_once (PIXELAVO_PL_PATH . 'admin/class-diagnostic-data.php'); require_once (PIXELAVO_PL_PATH . 'admin/class-notices.php'); } add_action('wp_footer', [$this, 'localizedEventsData'], 11); } function localizedEventsData() { global $pixelavoEventsLocalizedData; if(count($pixelavoEventsLocalizedData) > 0) { wp_localize_script('pixelavo', 'pixelavo_event', $pixelavoEventsLocalizedData); } } /** * Scripts * * Enqueue style and scripts for frontend part * @return void */ function scripts() { if(pixelavo_pixel_status()) { wp_enqueue_script('pixelavo', PIXELAVO_DIR_URL . 'assets/public/js/main.js', ['jquery'], PIXELAVO_VERSION, true); wp_localize_script('pixelavo', 'pixelavo', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('pixelavo_ajax_event_nonce'), 'other_events' => wp_json_encode(array_merge(pixelavo_get_other_events(), ['data' => pixelavo_get_event_common_data()])), 'custom_events' => wp_json_encode(pixelavo_get_custom_events()), 'additional_user_info' => apply_filters('pixelavo_additional_user_info', [], [] ), 'common_params' => pixelavo_get_event_common_data(), 'other_event_options' => [ "download_files" => pixelavo_get_option('download_files', 'pixelavo_download_files'), "page_scroll" => pixelavo_get_option('page_scroll', 'pixelavo_page_scroll'), "time_on_page" => pixelavo_get_option('time_on_page', 'pixelavo_time_on_page'), "form" => pixelavo_get_option('form_submission', 'pixelavo_form_submission'), ], 'is_active' => [ "woo" => pixelavo_is_woocommerce_active(), 'edd' => pixelavo_is_edd_active(), 'fluent_form' => pixelavo_is_fluentform_active(), 'cf7' => pixelavo_is_cf7_active(), 'wpforms' => pixelavo_is_wpforms_active(), 'ht_contactform' => pixelavo_is_ht_contactform_active(), ], ]); wp_localize_script('pixelavo', 'pixelavo_event', []); } } /** * Plugins setting links * @param array plugin default action links * @return array plugin action link */ function plugins_setting_links($links) { $link = sprintf( '%2$s', admin_url('admin.php?page=pixelavo#/pixels'), __( 'Settings', 'pixelavo' ) ); array_unshift($links, $link); return $links; } /** * Plugin Active Hook * Run when plugin is activated */ public function plugin_activate_hook() { // Create database tables for AI features $this->create_ai_tables(); $events = get_option('pixelavo_events', [ 'search' => 'on', 'view_content' => 'on', 'view_category' => 'on', ]); $edd_events = get_option('pixelavo_edd_events', [ 'edd_search' => 'on', 'edd_view_content' => 'on', 'edd_view_category' => 'on', ]); $other_events = get_option('pixelavo_edd_events', [ 'youtube_video' => 'off', 'vimeo_video' => 'off', 'self_hosted_video' => 'off', 'page_scroll' => 'on', 'time_on_page' => 'on', 'download_files' => 'on', 'form_submission' => 'on', 'internal_links' => 'off', 'external_links' => 'off', ]); $download_files = get_option('pixelavo_download_files', [ 'download_files' => [ "download_files_extensions" => ["pdf", "zip"] ] ]); $time_on_page = get_option('pixelavo_time_on_page', [ 'time_on_page' => [ "time_on_page_value" => "10" ] ]); $page_scroll = get_option('pixelavo_page_scroll', [ 'page_scroll' => [ "page_scroll_value" => "30" ] ]); $form_submission = get_option('pixelavo_form_submission', [ 'form_submission' => [ "fluent_form" => [], "contact_form_7" => [], "wp_forms" => [], "ht_contactform" => [], ] ]); update_option('pixelavo_events', $events); update_option('pixelavo_edd_events', $edd_events); update_option('pixelavo_other_events', $other_events); update_option('pixelavo_download_files', $download_files); update_option('pixelavo_time_on_page', $time_on_page); update_option('pixelavo_page_scroll', $page_scroll); update_option('pixelavo_form_submission', $form_submission); /* ViewContent Event Settings */ $pixelavo_wc_view_content = get_option('pixelavo_wc_view_content', [ 'view_content' => [ 'view_content_delay' => 0, ], ]); update_option('pixelavo_wc_view_content', $pixelavo_wc_view_content); /* Purchase Event Settings */ $pixelavo_wc_purchase = get_option('pixelavo_wc_purchase', [ 'purchase' => [ 'purchase_event_trigger' => ['on-hold', 'processing'], 'purchase_additional_info' => 'off', ], ]); update_option('pixelavo_wc_purchase', $pixelavo_wc_purchase); add_option('pixelavo_do_activation_redirect', true); flush_rewrite_rules(); if ( ! get_option( 'pixelavo_installed' ) ) { update_option( 'pixelavo_installed', time() ); } } /** * Create AI database tables * @return void */ private function create_ai_tables() { // Load DatabaseInstaller class if it exists $installer_class = PIXELAVO_PL_PATH . 'admin/settings-panel/includes/classes/Api/Ai/DatabaseInstaller.php'; if (file_exists($installer_class)) { require_once $installer_class; \Pixelavo\Api\Ai\DatabaseInstaller::create_tables(); } } /** * Ensure AI tables exist on admin init * This handles cases where activation hook might have been missed * @return void */ public function ensure_ai_tables_exist() { // Only check once per admin session to avoid performance impact if (get_transient('pixelavo_ai_tables_checked')) { return; } $installer_class = PIXELAVO_PL_PATH . 'admin/settings-panel/includes/classes/Api/Ai/DatabaseInstaller.php'; if (file_exists($installer_class)) { require_once $installer_class; // Check if table exists, create if not if (!\Pixelavo\Api\Ai\DatabaseInstaller::table_exists()) { \Pixelavo\Api\Ai\DatabaseInstaller::create_tables(); } } // Set transient to avoid repeated checks (expires in 12 hours) set_transient('pixelavo_ai_tables_checked', true, 12 * HOUR_IN_SECONDS); } /** * Initialize security notices for AI features * @return void */ public function init_security_notices() { $notices_class = PIXELAVO_PL_PATH . 'admin/settings-panel/includes/classes/Api/Ai/SecurityNotices.php'; if (file_exists($notices_class)) { require_once $notices_class; \Pixelavo\Api\Ai\SecurityNotices::init(); // Enqueue JavaScript for notice dismissal add_action('admin_footer', [\Pixelavo\Api\Ai\SecurityNotices::class, 'enqueue_notice_scripts']); } } public function halloween_notice() { $plugins = get_plugins(); if(!empty($plugins['pixelavo-pro/pixelavo-pro.php'])) { return; } $url = 'https://pixelavo.com/pricing/?utm_source=halloween&utm_medium=dashboard#pixelavo-pricing'; $image = PIXELAVO_PL_URL . '/admin/settings-panel/assets/images/banner/halloween-2025.png'; \Pixelavo_Notice::set_notice( [ 'id' => 'pixelavo-halloween-notice', 'type' => 'notice', 'dismissible' => true, 'message_type' => 'banner', 'banner' => [ 'url' => esc_url_raw($url), 'image' => 'Pixelavo' ], 'close_by' => 'transient' ] ); } /** * User Login * @param string $user_login * @param \WP_User $user * @return void */ public function user_login($user_login, $user) { if(pixelavo_other_event_exists('login')) { // Don't double-fire Login when this is part of a fresh registration // (CompleteRegistration already fired in the same request). if($this->auth_event_queued('CompleteRegistration')) { return; } $this->fire_server_auth_event('Login', 'trackCustom', $user); } } /** * User Register * @param int $user_id * @param \WP_User $userdata * @return void */ public function user_register($user_id, $userdata) { if(pixelavo_other_event_exists('signup')) { $this->fire_server_auth_event('CompleteRegistration', 'track', get_userdata($user_id)); } } /** * Fire an identity event (Login / CompleteRegistration) server-side. * * The Conversions API call is made HERE, from the authenticated wp_login / * user_register hook — this is the server-side source of truth and cannot be * forged by a client. A short-lived cookie then carries ONLY the event name * + the shared event_id to the browser, which fires the fbq pixel for * deduplication. The cookie is genuinely non-authoritative: forging it can * at most make the visitor's own browser fire an fbq pixel — it never * triggers a CAPI call and carries no credentials. * * @param string $event_name * @param string $track 'track' or 'trackCustom' * @param \WP_User|false $user The authenticated user (for matching data). * @return void */ private function fire_server_auth_event($event_name, $track, $user = null) { $event_id = $event_name . '.' . time() . '.' . wp_generate_password(12, false, false); // Make the just-authenticated user available for advanced matching — // wp_login / user_register run before WordPress sets the current user. if($user instanceof \WP_User) { wp_set_current_user($user->ID); } // Fire the Conversions API now. Identity events are not page-scoped, so // skip page-targeting and use the site home as the source URL (there is // no queried page in this hook). The request is non-blocking, so login // and registration are not slowed. // // Do NOT call pixelavo_get_event_common_data() here — it runs query // conditionals (is_singular/is_home/...) which trigger a "called // incorrectly" notice this early (before the main query). A minimal, // page-independent payload is correct for an identity event anyway. $common = ['event_url' => home_url('/')]; pixelavo_run_conversions_api($event_name, $common, $event_id, null, [ 'event_source_url' => home_url('/'), 'skip_page_check' => true, ]); // Hand only a browser descriptor to the client for the fbq pixel. $this->queue_browser_auth_event($event_name, $track, $event_id); } /** * Store a browser-only descriptor (event name + shared event_id) in the * short-lived cookie so the pixel fires fbq once for deduplication. No PII, * no credentials, not a trust boundary. * * @param string $event_name * @param string $track * @param string $event_id * @return void */ private function queue_browser_auth_event($event_name, $track, $event_id) { $queue = []; if(!empty($_COOKIE['pixelavo_srv_evt'])) { $existing = json_decode(wp_unslash($_COOKIE['pixelavo_srv_evt']), true); if(is_array($existing)) { $queue = $existing; } } $queue[] = [ 'name' => $event_name, 'track' => ($track === 'track') ? 'track' : 'trackCustom', 'id' => $event_id, ]; $this->set_server_auth_cookie(wp_json_encode($queue)); } /** * Whether an event of the given name is already in the pending cookie queue. * @param string $name * @return bool */ private function auth_event_queued($name) { if(empty($_COOKIE['pixelavo_srv_evt'])) { return false; } $queue = json_decode(wp_unslash($_COOKIE['pixelavo_srv_evt']), true); if(!is_array($queue)) { return false; } foreach($queue as $event) { if(!empty($event['name']) && $event['name'] === $name) { return true; } } return false; } /** * Set the short-lived carrier cookie for pending auth events. * httponly is intentionally false: the browser JS must read it. It holds no * secret — only event name + server-generated id. * @param string $value * @return void */ private function set_server_auth_cookie($value) { // Reflect immediately so same-request reads (register -> login) see it. $_COOKIE['pixelavo_srv_evt'] = $value; if(headers_sent()) { return; } setcookie('pixelavo_srv_evt', $value, [ 'expires' => time() + 300, 'path' => defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/', 'domain' => defined('COOKIE_DOMAIN') && COOKIE_DOMAIN ? COOKIE_DOMAIN : '', 'secure' => is_ssl(), 'httponly' => false, 'samesite' => 'Lax', ]); } /** * After Active the plugin then redirect to option page * @return void */ public function plugin_redirect_option_page() { if ( get_option( 'pixelavo_do_activation_redirect', false ) ) { delete_option('pixelavo_do_activation_redirect'); if( !isset( $_GET['activate-multi'] ) ){ // phpcs:ignore WordPress.Security.NonceVerification wp_safe_redirect( admin_url("admin.php?page=pixelavo#/pixels") ); exit; } } } function show_diagnostic_notice() { $notice_instance = \Pixelavo_Diagnostic_Data::get_instance(); if ( ! $notice_instance->should_show_notice() ) { return; } ob_start(); $notice_instance->show_notices(); $message = ob_get_clean(); if (! empty( $message ) ) { \Pixelavo_Notice::set_notice( [ 'id' => 'diagnostic-data', 'type' => 'success', 'dismissible' => false, 'message_type' => 'html', 'message' => $message, 'display_after' => ( 7 * DAY_IN_SECONDS ), 'expire_time' => ( 0 * DAY_IN_SECONDS ), 'close_by' => 'transient' ] ); } } function show_rating_notice() { $message = '

'.esc_html__('Thank you for choosing Pixelavo to manage you plugins!','pixelavo').'

'.esc_html__('Would you mind doing us a huge favor by providing your feedback on WordPress? Your support helps us spread the word and greatly boosts our motivation.','pixelavo').'

'; if (! empty( $message ) ) { \Pixelavo_Notice::set_notice( [ 'id' => 'ratting', 'type' => 'info', 'dismissible' => true, 'message_type' => 'html', 'message' => $message, 'display_after' => ( 14 * DAY_IN_SECONDS ), 'close_by' => 'transient' ] ); } } function show_promo_notice() { // remove on next update if(!get_option('force_update_pixelavo_notice_info')) { delete_transient('pixelavo_notice_info'); update_option('force_update_pixelavo_notice_info', true); } $noticeManager = \Pixelavo_Notice_Manager::instance(); $notices = $noticeManager->get_notices_info(); if(!empty($notices)) { foreach ($notices as $notice) { if(empty($notice['disable'])) { \Pixelavo_Notice::set_notice($notice); } } } } } /** * Initialize Base Class */ Base::instance();