Plugin Directory

Changeset 3254205


Ignore:
Timestamp:
03/11/2025 04:05:42 PM (13 months ago)
Author:
creativehassan
Message:

Update to the nounce issue fixed.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • snap-pixel/tags/1.8.0/includes/class-snap-pixel-core.php

    r3252605 r3254205  
    99// If this file is called directly, abort.
    1010if ( ! defined( 'ABSPATH' ) ) {
    11     exit;
     11    exit;
    1212}
    1313
     
    1717class Snap_Pixel_Core {
    1818
    19     /**
    20     * Pixel ID.
    21     *
    22     * @var string
    23     */
    24     private $pixel_id;
    25 
    26     /**
    27     * User email.
    28     *
    29     * @var string
    30     */
    31     private $user_email;
    32 
    33     /**
    34     * Settings for different page types.
    35     *
    36     * @var array
    37     */
    38     private $settings = array();
    39    
    40     /**
    41     * Flag to track if base pixel code has been output.
    42     *
    43     * @var bool
    44     */
    45     private $base_pixel_output = false;
    46 
    47     /**
    48     * Initialize the class and set its properties.
    49     */
    50     public function __construct() {
    51         $this->load_settings();
    52        
    53         // Add pixel code to appropriate pages
    54         add_action( 'template_redirect', array( $this, 'place_pixel_code' ) );
    55        
    56         // Register AJAX handler for product data
    57         add_action( 'wp_ajax_snapchat_product_data', array( $this, 'get_product_data' ) );
    58         add_action( 'wp_ajax_nopriv_snapchat_product_data', array( $this, 'get_product_data' ) );
    59        
    60         // Enqueue frontend assets
    61         add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) );
    62        
    63         // Set user email after WordPress is fully loaded
    64         add_action( 'init', array( $this, 'set_user_email' ) );
    65     }
    66 
    67     /**
    68     * Load settings from database.
    69     */
    70     private function load_settings() {
    71         $settings = get_option( 'snapchat_pixel_code', array() );
    72        
    73         $this->pixel_id = isset( $settings['pixel_id'] ) ? $settings['pixel_id'] : '';
    74         $this->user_email = isset( $settings['user_email'] ) ? $settings['user_email'] : get_option( 'admin_email' );
    75        
    76         // Load page type settings
    77         $this->settings = array(
    78             'homepage'      => isset( $settings['homepage'] ) ? $settings['homepage'] : '',
    79             'pages'         => isset( $settings['pages'] ) ? $settings['pages'] : '',
    80             'posts'         => isset( $settings['posts'] ) ? $settings['posts'] : '',
    81             'search'        => isset( $settings['search'] ) ? $settings['search'] : '',
    82             'categories'    => isset( $settings['categories'] ) ? $settings['categories'] : '',
    83             'tags'          => isset( $settings['tags'] ) ? $settings['tags'] : '',
    84             'viewcart'      => isset( $settings['viewcart'] ) ? $settings['viewcart'] : '',
    85             'checkout'      => isset( $settings['checkout'] ) ? $settings['checkout'] : '',
    86             'paymentinfo'   => isset( $settings['paymentinfo'] ) ? $settings['paymentinfo'] : '',
    87             'addtocart'     => isset( $settings['addtocart'] ) ? $settings['addtocart'] : '',
    88             'ajax_addtocart' => isset( $settings['ajax_addtocart'] ) ? $settings['ajax_addtocart'] : '',
    89         );
    90     }
    91 
    92     /**
    93     * Set user email after WordPress is fully loaded.
    94     */
    95     public function set_user_email() {
    96         // Override user email if user is logged in
    97         if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) {
    98             $current_user = wp_get_current_user();
    99             $this->user_email = $current_user->user_email;
    100         }
    101     }
    102 
    103     /**
    104     * Enqueue frontend assets.
    105     */
    106     public function enqueue_frontend_assets() {
    107         wp_register_script(
    108             'snap-pixel',
    109             SNAP_PIXEL_PLUGIN_URL . 'assets/js/snap-pixel.js',
    110             array( 'jquery' ),
    111             SNAP_PIXEL_VERSION,
    112             false // Load in header
    113         );
    114        
    115         wp_localize_script(
    116             'snap-pixel',
    117             'snappixel',
    118             array(
    119                 'ajaxurl' => admin_url( 'admin-ajax.php' ),
    120                 'nonce'   => wp_create_nonce( 'snapchat_product_data' ),
    121                 'prevent_duplicates' => true,
    122             )
    123         );
    124        
    125         wp_enqueue_script( 'snap-pixel' );
    126     }
    127 
    128     /**
    129     * Place pixel code on appropriate pages.
    130     */
    131     public function place_pixel_code() {
    132         global $post;
    133        
    134         // Don't proceed if pixel ID is not set
    135         if ( empty( $this->pixel_id ) ) {
    136             return;
    137         }
    138        
    139         $woo_active = $this->is_woocommerce_active();
    140         $woo_access = get_option( 'snapchat_pixel_wooacces', 'no' );
    141        
    142         // Track if we've added the base pixel code
    143         $base_pixel_added = false;
    144        
    145         // Homepage
    146         if ( ( is_home() || is_front_page() ) && 'checked' === $this->settings['homepage'] ) {
    147             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    148             $base_pixel_added = true;
    149         }
    150         // Pages
    151         elseif ( is_page() && 'checked' === $this->settings['pages'] ) {
    152             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    153             $base_pixel_added = true;
    154            
    155             // WooCommerce checkout page
    156             if ( $woo_active && 'yes' === $woo_access && function_exists( 'is_checkout' ) ) {
    157                 $page_slug = $post->post_name;
    158                
    159                 if ( 'checkout' === $page_slug ) {
    160                     // This is checking for the existence of a URL parameter, not processing form data,
    161                     // so nonce verification is not applicable here
    162                     if ( ! isset( $_GET['key'] ) && 'checked' === $this->settings['checkout'] ) {
    163                         add_action( 'wp_footer', array( $this, 'output_checkout_pixel_code' ) );
    164                     } elseif ( 'checked' === $this->settings['paymentinfo'] ) {
    165                         if ( is_wc_endpoint_url( 'order-received' ) ) {
    166                             add_action( 'wp_footer', array( $this, 'output_purchase_pixel_code' ) );
    167                         } else {
    168                             add_action( 'wp_footer', array( $this, 'output_billing_pixel_code' ) );
    169                         }
    170                     }
    171                 }
    172             }
    173         }
    174         // Posts
    175         elseif ( ( is_single() && 'checked' === $this->settings['posts'] ) || ( function_exists( 'is_product' ) && is_product() ) ) {
    176             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    177             $base_pixel_added = true;
    178            
    179             // WooCommerce product page
    180             if ( $woo_active && 'yes' === $woo_access ) {
    181                 add_action( 'wp_footer', array( $this, 'output_viewcontent_pixel_code' ) );
    182             }
    183         }
    184         // Search
    185         elseif ( is_search() && 'checked' === $this->settings['search'] ) {
    186             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    187             $base_pixel_added = true;
    188             add_action( 'wp_footer', array( $this, 'output_search_pixel_code' ) );
    189         }
    190         // Category archives
    191         elseif ( is_category() && 'checked' === $this->settings['categories'] ) {
    192             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    193             $base_pixel_added = true;
    194         }
    195         // Tag archives
    196         elseif ( is_tag() && 'checked' === $this->settings['tags'] ) {
    197             add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    198             $base_pixel_added = true;
    199         }
    200        
    201         // If we haven't added the base pixel code yet, but we're on a WooCommerce page that needs it
    202         if ( !$base_pixel_added && $woo_active && 'yes' === $woo_access ) {
    203             // Cart page
    204             if ( function_exists( 'is_cart' ) && is_cart() ) {
    205                 add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    206             }
    207             // Checkout page not covered above
    208             elseif ( function_exists( 'is_checkout' ) && is_checkout() && !is_page() ) {
    209                 add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
    210             }
    211         }
    212     }
    213 
    214     /**
    215     * Output base pixel code.
    216     */
    217     public function output_base_pixel_code() {
    218         // Check if base pixel has already been output
    219         if ( $this->base_pixel_output ) {
    220             return;
    221         }
    222        
    223         // Set flag to prevent duplicate output
    224         $this->base_pixel_output = true;
    225        
    226         global $post;
    227        
    228         $product_id = '';
    229        
    230         // Check if we're on a product page
    231         if ( $this->is_woocommerce_active() && function_exists( 'is_product' ) && is_product() && $post ) {
    232             $product = wc_get_product( $post->ID );
    233             if ( $product ) {
    234                 $product_id = $product->get_id();
    235             }
    236         }
    237        
    238         ?>
    239         <!-- Snapchat Pixel Code -->
    240         <script type='text/javascript'>
    241             (function(win, doc, sdk_url) {
    242                 if (win.snaptr) return;
    243                 var tr = win.snaptr = function() {
    244                     tr.handleRequest ? tr.handleRequest.apply(tr, arguments) : tr.queue.push(arguments);
    245                 };
    246                 tr.queue = [];
    247                 var s = 'script';
    248                 var new_script_section = doc.createElement(s);
    249                 new_script_section.async = !0;
    250                 new_script_section.src = sdk_url;
    251                 var insert_pos = doc.getElementsByTagName(s)[0];
    252                 insert_pos.parentNode.insertBefore(new_script_section, insert_pos);
    253             })(window, document, 'https://sc-static.net/scevent.min.js');
    254 
    255             snaptr('init', '<?php echo esc_js( $this->pixel_id ); ?>', {
    256                 'user_email': '<?php echo esc_js( $this->user_email ); ?>'
    257             });
    258            
    259             var item_ids = <?php if ( $product_id ) { ?> {'item_ids': ["<?php echo esc_js( $product_id ); ?>"]} <?php } else { echo "0"; } ?>;
    260             snaptr('track', 'PAGE_VIEW', item_ids);
    261         </script>
    262         <!-- End Snapchat Pixel Code -->
    263         <?php
    264         // Fire action for add-ons
    265         $event_data = array();
    266         if (isset($item_ids)) {
    267             $event_data['item_ids'] = $item_ids;
    268         }
    269         do_action( 'snap_pixel_event_fired', 'PAGE_VIEW', $event_data );
    270         ?>
    271         <?php
    272     }
    273 
    274     /**
    275     * Output search pixel code.
    276     */
    277     public function output_search_pixel_code() {
    278         global $wp_query;
    279        
    280         $search_term = get_search_query();
    281        
    282         ?>
    283         <!-- SEARCH pixel event by Snap Pixel Plugin -->
    284         <script>
    285             snaptr('track', 'SEARCH', {
    286                 'search_string': "<?php echo esc_js( $search_term ); ?>"
    287             });
    288         </script>
    289         <!-- End SEARCH pixel event by Snap Pixel Plugin -->
    290         <?php
    291         // Fire action for add-ons
    292         $event_data = array(
    293             'search_string' => $search_term
    294         );
    295         do_action( 'snap_pixel_event_fired', 'SEARCH', $event_data );
    296         ?>
    297         <?php
    298     }
    299 
    300     /**
    301     * Output view content pixel code for product pages.
    302     */
    303     public function output_viewcontent_pixel_code() {
    304         global $post;
    305        
    306         if ( ! $this->is_woocommerce_active() ) {
    307             return;
    308         }
    309        
    310         $product = wc_get_product( $post->ID );
    311         if ( ! $product ) {
    312             return;
    313         }
    314        
    315         $product_id = $product->get_id();
    316         $product_price = $product->get_price();
    317         $product_currency = get_woocommerce_currency();
    318        
    319         // Get product category
    320         $terms = get_the_terms( $product_id, 'product_cat' );
    321         $product_category = '';
    322        
    323         if ( $terms && ! is_wp_error( $terms ) ) {
    324             $product_category = $terms[0]->name;
    325         }
    326        
    327         ?>
    328         <!-- VIEW_CONTENT pixel event by Snap Pixel Plugin -->
    329         <script>
    330             snaptr('track', 'VIEW_CONTENT', {
    331                 'item_category': "<?php echo esc_js( $product_category ); ?>",
    332                 'item_ids': ["<?php echo esc_js( $product_id ); ?>"],
    333                 'price': <?php echo esc_js( $product_price ); ?>,
    334                 'currency': "<?php echo esc_js( $product_currency ); ?>"
    335             });
    336         </script>
    337         <!-- End VIEW_CONTENT pixel event by Snap Pixel Plugin -->
    338         <?php
    339         // Fire action for add-ons
    340         $event_data = array(
    341             'item_category' => $product_category,
    342             'item_ids' => array($product_id),
    343             'price' => $product_price,
    344             'currency' => $product_currency
    345         );
    346         do_action( 'snap_pixel_event_fired', 'VIEW_CONTENT', $event_data );
    347         ?>
    348         <?php
    349     }
    350 
    351     /**
    352     * Output checkout pixel code.
    353     */
    354     public function output_checkout_pixel_code() {
    355         if ( ! $this->is_woocommerce_active() ) {
    356             return;
    357         }
    358        
    359         global $woocommerce;
    360        
    361         $price = $woocommerce->cart->total;
    362         $product_currency = get_woocommerce_currency();
    363         $num_items = $woocommerce->cart->get_cart_contents_count();
    364         $product_id = '';
    365        
    366         foreach ( WC()->cart->get_cart() as $cart_item ) {
    367             $product_id = $cart_item['product_id'];
    368             break;
    369         }
    370        
    371         ?>
    372         <!-- START_CHECKOUT pixel event by Snap Pixel Plugin -->
    373         <script>
    374             snaptr('track', 'START_CHECKOUT', {
    375                 'currency': "<?php echo esc_js( $product_currency ); ?>",
    376                 'price': <?php echo esc_js( $price ); ?>,
    377                 'num_items': <?php echo esc_js( $num_items ); ?>,
    378                 'item_ids': ["<?php echo esc_js( $product_id ); ?>"]
    379             });
    380         </script>
    381         <!-- End START_CHECKOUT pixel event by Snap Pixel Plugin -->
    382         <?php
    383         // Fire action for add-ons
    384         $event_data = array(
    385             'currency' => $product_currency,
    386             'price' => $price,
    387             'num_items' => $num_items,
    388             'item_ids' => array($product_id)
    389         );
    390         do_action( 'snap_pixel_event_fired', 'START_CHECKOUT', $event_data );
    391         ?>
    392         <?php
    393     }
    394 
    395     /**
    396     * Output purchase pixel code.
    397     */
    398     public function output_purchase_pixel_code() {
    399         if ( ! $this->is_woocommerce_active() ) {
    400             return;
    401         }
    402        
    403         global $woocommerce;
    404        
    405         // Check if key exists and verify nonce if it's from a form submission
    406         if ( isset( $_GET['key'] ) ) {
    407             $key = sanitize_text_field( wp_unslash( $_GET['key'] ) );
    408            
    409             // For WooCommerce order-received endpoint, the key is part of the URL and not from a form submission
    410             // so we don't need to verify a nonce in this specific case. This is a standard WooCommerce URL parameter
    411             // used for order identification and not for processing form submissions.
    412         } else {
    413             $key = '';
    414         }
    415        
    416         $order_id = $this->get_order_id_by_key( $key );
    417        
    418         if ( ! $order_id ) {
    419             return;
    420         }
    421        
    422         $order = wc_get_order( $order_id );
    423         if ( ! $order ) {
    424             return;
    425         }
    426        
    427         $order_total = $order->get_total();
    428         $product_currency = get_woocommerce_currency();
    429         $num_items = $woocommerce->cart->get_cart_contents_count();
    430        
    431         // Get product IDs from the order items
    432         $product_ids = array();
    433         foreach ( $order->get_items() as $item ) {
    434             // Access product_id directly from the item data
    435             $item_data = $item->get_data();
    436             $product_id = isset( $item_data['product_id'] ) ? $item_data['product_id'] : 0;
    437            
    438             if ( $product_id ) {
    439                 $product_ids[] = $product_id;
    440             }
    441         }
    442         if(count($product_ids) > 0){
    443             $content_ids_string = $product_ids;
    444         } else {
    445             $content_ids_string = $order_id;
    446         }
    447        
    448         ?>
    449         <!-- PURCHASE pixel event by Snap Pixel Plugin -->
    450         <script>
    451             snaptr('track', 'PURCHASE', {
    452                 'currency': "<?php echo esc_js( $product_currency ); ?>",
    453                 'price': <?php echo esc_js( $order_total ); ?>,
    454                 'transaction_id': "<?php echo esc_js( $order_id ); ?>",
    455                 'item_ids': content_ids_string
    456             });
    457            
    458             snaptr('track', 'ADD_BILLING');
    459         </script>
    460         <!-- End PURCHASE pixel event by Snap Pixel Plugin -->
    461         <?php
    462         // Fire action for add-ons
    463         $event_data = array(
    464             'currency' => $product_currency,
    465             'price' => $order_total,
    466             'transaction_id' => $order_id,
    467             'item_ids' => $content_ids_string
    468         );
    469         do_action( 'snap_pixel_event_fired', 'PURCHASE', $event_data );
    470        
    471         // Fire action for ADD_BILLING event
    472         do_action( 'snap_pixel_event_fired', 'ADD_BILLING', array() );
    473         ?>
    474         <?php
    475     }
    476 
    477     /**
    478     * Output add to cart pixel code.
    479     */
    480     public function output_addtocart_pixel_code() {
    481         if ( ! $this->is_woocommerce_active() ) {
    482             return;
    483         }
    484        
    485         global $post;
    486        
    487         $product_currency = get_woocommerce_currency();
    488         $product_price = '';
    489        
    490         if ( $post && function_exists( 'wc_get_product' ) ) {
    491             $product = wc_get_product( $post->ID );
    492             if ( $product ) {
    493                 $product_price = $product->get_price();
    494             }
    495         }
    496        
    497         ?>
    498         <!-- ADD_CART pixel event by Snap Pixel Plugin -->
    499         <script>
    500             jQuery('body').on('added_to_cart', function(e, h, w, button) {
    501                 var product_id = button.data("product_id");
    502                 var product_result = get_product_record(product_id);
    503                 var product_price = "<?php echo esc_js( $product_price ); ?>";
    504                
    505                 setTimeout(function() {
    506                     product_price = jQuery("#return_response").val();
    507                     snaptr('track', 'ADD_CART', {
    508                         'currency': "<?php echo esc_js( $product_currency ); ?>",
    509                         'price': product_price,
    510                         'item_category': "",
    511                         'item_ids': [product_id]
    512                     });
    513                 }, 1500);
    514             });
    515         </script>
    516         <!-- End ADD_CART pixel event by Snap Pixel Plugin -->
    517         <?php
    518         // Fire action for add-ons
    519         $event_data = array(
    520             'currency' => $product_currency,
    521             'price' => $product_price
    522         );
    523         do_action( 'snap_pixel_event_fired', 'ADD_CART', $event_data );
    524         ?>
    525         <?php
    526     }
    527 
    528     /**
    529     * Output billing pixel code.
    530     */
    531     public function output_billing_pixel_code() {
    532         ?>
    533         <script type='text/javascript'>
    534             snaptr('track', 'ADD_BILLING');
    535         </script>
    536         <?php
    537         // Fire action for add-ons
    538         do_action( 'snap_pixel_event_fired', 'ADD_BILLING', array() );
    539     }
    540 
    541     /**
    542     * AJAX handler for getting product data.
    543     */
    544     public function get_product_data() {
    545         // Verify nonce if provided, but don't require it for backward compatibility
    546         if ( isset( $_REQUEST['_wpnonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'snapchat_product_data' ) ) {
    547             wp_send_json_error( 'Invalid security token' );
    548             wp_die();
    549         }
    550        
    551         if ( isset( $_REQUEST['snap_product_id'] ) && ! empty( $_REQUEST['snap_product_id'] ) ) {
    552             $product_id = absint( $_REQUEST['snap_product_id'] );
    553            
    554             if ( function_exists( 'wc_get_product' ) ) {
    555                 $product = wc_get_product( $product_id );
    556                
    557                 if ( $product ) {
    558                     $product_price = $product->get_price();
    559                     wp_send_json_success( $product_price );
    560                     wp_die();
    561                 }
    562             }
    563         }
    564        
    565         wp_send_json_error( 'Error' );
    566         wp_die();
    567     }
    568 
    569     /**
    570     * Get order ID by order key.
    571     *
    572     * @param string $key Order key.
    573     * @return int|bool Order ID or false if not found.
    574     */
    575     private function get_order_id_by_key( $key ) {
    576         if ( empty( $key ) ) {
    577             return false;
    578         }
    579        
    580         // Check cache first
    581         $cache_key = 'snap_pixel_order_' . md5($key);
    582         $order_id = wp_cache_get( $cache_key, 'snap_pixel' );
    583        
    584         if ( false !== $order_id ) {
    585             return $order_id;
    586         }
    587        
    588         global $wpdb;
    589        
    590         $result = $wpdb->get_row(
    591             $wpdb->prepare(
    592                 "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key='_order_key' AND meta_value=%s ORDER BY meta_id DESC LIMIT 1",
    593                 $key
    594             )
    595         );
    596        
    597         if ( $result ) {
    598             // Cache the result for future use
    599             wp_cache_set( $cache_key, $result->post_id, 'snap_pixel', 3600 ); // Cache for 1 hour
    600             return $result->post_id;
    601         }
    602        
    603         // Cache the negative result as well
    604         wp_cache_set( $cache_key, 0, 'snap_pixel', 3600 ); // Cache for 1 hour
    605         return false;
    606     }
    607 
    608     /**
    609     * Check if WooCommerce is active.
    610     *
    611     * @return bool
    612     */
    613     private function is_woocommerce_active() {
    614         return in_array(
    615             'woocommerce/woocommerce.php',
    616             apply_filters( 'active_plugins', get_option( 'active_plugins' ) )
    617         );
    618     }
     19    /**
     20    * Pixel ID.
     21    *
     22    * @var string
     23    */
     24    private $pixel_id;
     25
     26    /**
     27    * User email.
     28    *
     29    * @var string
     30    */
     31    private $user_email;
     32
     33    /**
     34    * Settings for different page types.
     35    *
     36    * @var array
     37    */
     38    private $settings = array();
     39   
     40    /**
     41    * Flag to track if base pixel code has been output.
     42    *
     43    * @var bool
     44    */
     45    private $base_pixel_output = false;
     46
     47    /**
     48    * Initialize the class and set its properties.
     49    */
     50    public function __construct() {
     51        $this->load_settings();
     52       
     53        // Add pixel code to appropriate pages
     54        add_action( 'template_redirect', array( $this, 'place_pixel_code' ) );
     55       
     56        // Register AJAX handler for product data
     57        add_action( 'wp_ajax_snapchat_product_data', array( $this, 'get_product_data' ) );
     58        add_action( 'wp_ajax_nopriv_snapchat_product_data', array( $this, 'get_product_data' ) );
     59       
     60        // Enqueue frontend assets
     61        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) );
     62       
     63        // Set user email after WordPress is fully loaded
     64        add_action( 'init', array( $this, 'set_user_email' ) );
     65    }
     66
     67    /**
     68    * Load settings from database.
     69    */
     70    private function load_settings() {
     71        $settings = get_option( 'snapchat_pixel_code', array() );
     72       
     73        $this->pixel_id = isset( $settings['pixel_id'] ) ? $settings['pixel_id'] : '';
     74        $this->user_email = isset( $settings['user_email'] ) ? $settings['user_email'] : get_option( 'admin_email' );
     75       
     76        // Load page type settings
     77        $this->settings = array(
     78            'homepage'      => isset( $settings['homepage'] ) ? $settings['homepage'] : '',
     79            'pages'         => isset( $settings['pages'] ) ? $settings['pages'] : '',
     80            'posts'         => isset( $settings['posts'] ) ? $settings['posts'] : '',
     81            'search'        => isset( $settings['search'] ) ? $settings['search'] : '',
     82            'categories'    => isset( $settings['categories'] ) ? $settings['categories'] : '',
     83            'tags'          => isset( $settings['tags'] ) ? $settings['tags'] : '',
     84            'viewcart'      => isset( $settings['viewcart'] ) ? $settings['viewcart'] : '',
     85            'checkout'      => isset( $settings['checkout'] ) ? $settings['checkout'] : '',
     86            'paymentinfo'   => isset( $settings['paymentinfo'] ) ? $settings['paymentinfo'] : '',
     87            'addtocart'     => isset( $settings['addtocart'] ) ? $settings['addtocart'] : '',
     88            'ajax_addtocart' => isset( $settings['ajax_addtocart'] ) ? $settings['ajax_addtocart'] : '',
     89        );
     90    }
     91
     92    /**
     93    * Set user email after WordPress is fully loaded.
     94    */
     95    public function set_user_email() {
     96        // Override user email if user is logged in
     97        if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) {
     98            $current_user = wp_get_current_user();
     99            $this->user_email = $current_user->user_email;
     100        }
     101    }
     102
     103    /**
     104    * Enqueue frontend assets.
     105    */
     106    public function enqueue_frontend_assets() {
     107        wp_register_script(
     108            'snap-pixel',
     109            SNAP_PIXEL_PLUGIN_URL . 'assets/js/snap-pixel.js',
     110            array( 'jquery' ),
     111            SNAP_PIXEL_VERSION,
     112            false // Load in header
     113        );
     114       
     115        wp_localize_script(
     116            'snap-pixel',
     117            'snappixel',
     118            array(
     119                'ajaxurl' => admin_url( 'admin-ajax.php' ),
     120                'nonce'   => wp_create_nonce( 'snapchat_product_data' ),
     121                'prevent_duplicates' => true,
     122            )
     123        );
     124       
     125        wp_enqueue_script( 'snap-pixel' );
     126    }
     127
     128    /**
     129    * Place pixel code on appropriate pages.
     130    */
     131    public function place_pixel_code() {
     132        global $post;
     133       
     134        // Don't proceed if pixel ID is not set
     135        if ( empty( $this->pixel_id ) ) {
     136            return;
     137        }
     138       
     139        $woo_active = $this->is_woocommerce_active();
     140        $woo_access = get_option( 'snapchat_pixel_wooacces', 'no' );
     141       
     142        // Track if we've added the base pixel code
     143        $base_pixel_added = false;
     144       
     145        // Homepage
     146        if ( ( is_home() || is_front_page() ) && 'checked' === $this->settings['homepage'] ) {
     147            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     148            $base_pixel_added = true;
     149        }
     150        // Pages
     151        elseif ( is_page() && 'checked' === $this->settings['pages'] ) {
     152            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     153            $base_pixel_added = true;
     154           
     155            // WooCommerce checkout page
     156            if ( $woo_active && 'yes' === $woo_access && function_exists( 'is_checkout' ) ) {
     157                $page_slug = $post->post_name;
     158               
     159                if ( 'checkout' === $page_slug ) {
     160                    // This is checking for the existence of a URL parameter, not processing form data,
     161                    // so nonce verification is not applicable here
     162                    if ( ! isset( $_GET['key'] ) && 'checked' === $this->settings['checkout'] ) {
     163                        add_action( 'wp_footer', array( $this, 'output_checkout_pixel_code' ) );
     164                    } elseif ( 'checked' === $this->settings['paymentinfo'] ) {
     165                        if ( is_wc_endpoint_url( 'order-received' ) ) {
     166                            add_action( 'wp_footer', array( $this, 'output_purchase_pixel_code' ) );
     167                        } else {
     168                            add_action( 'wp_footer', array( $this, 'output_billing_pixel_code' ) );
     169                        }
     170                    }
     171                }
     172            }
     173        }
     174        // Posts
     175        elseif ( ( is_single() && 'checked' === $this->settings['posts'] ) || ( function_exists( 'is_product' ) && is_product() ) ) {
     176            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     177            $base_pixel_added = true;
     178           
     179            // WooCommerce product page
     180            if ( $woo_active && 'yes' === $woo_access ) {
     181                add_action( 'wp_footer', array( $this, 'output_viewcontent_pixel_code' ) );
     182            }
     183        }
     184        // Search
     185        elseif ( is_search() && 'checked' === $this->settings['search'] ) {
     186            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     187            $base_pixel_added = true;
     188            add_action( 'wp_footer', array( $this, 'output_search_pixel_code' ) );
     189        }
     190        // Category archives
     191        elseif ( is_category() && 'checked' === $this->settings['categories'] ) {
     192            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     193            $base_pixel_added = true;
     194        }
     195        // Tag archives
     196        elseif ( is_tag() && 'checked' === $this->settings['tags'] ) {
     197            add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     198            $base_pixel_added = true;
     199        }
     200       
     201        // If we haven't added the base pixel code yet, but we're on a WooCommerce page that needs it
     202        if ( !$base_pixel_added && $woo_active && 'yes' === $woo_access ) {
     203            // Cart page
     204            if ( function_exists( 'is_cart' ) && is_cart() ) {
     205                add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     206            }
     207            // Checkout page not covered above
     208            elseif ( function_exists( 'is_checkout' ) && is_checkout() && !is_page() ) {
     209                add_action( 'wp_head', array( $this, 'output_base_pixel_code' ), 2 );
     210            }
     211        }
     212    }
     213
     214    /**
     215    * Output base pixel code.
     216    */
     217    public function output_base_pixel_code() {
     218        // Check if base pixel has already been output
     219        if ( $this->base_pixel_output ) {
     220            return;
     221        }
     222       
     223        // Set flag to prevent duplicate output
     224        $this->base_pixel_output = true;
     225       
     226        global $post;
     227       
     228        $product_id = '';
     229       
     230        // Check if we're on a product page
     231        if ( $this->is_woocommerce_active() && function_exists( 'is_product' ) && is_product() && $post ) {
     232            $product = wc_get_product( $post->ID );
     233            if ( $product ) {
     234                $product_id = $product->get_id();
     235            }
     236        }
     237       
     238        ?>
     239        <!-- Snapchat Pixel Code -->
     240        <script type='text/javascript'>
     241            (function(win, doc, sdk_url) {
     242                if (win.snaptr) return;
     243                var tr = win.snaptr = function() {
     244                    tr.handleRequest ? tr.handleRequest.apply(tr, arguments) : tr.queue.push(arguments);
     245                };
     246                tr.queue = [];
     247                var s = 'script';
     248                var new_script_section = doc.createElement(s);
     249                new_script_section.async = !0;
     250                new_script_section.src = sdk_url;
     251                var insert_pos = doc.getElementsByTagName(s)[0];
     252                insert_pos.parentNode.insertBefore(new_script_section, insert_pos);
     253            })(window, document, 'https://sc-static.net/scevent.min.js');
     254
     255            snaptr('init', '<?php echo esc_js( $this->pixel_id ); ?>', {
     256                'user_email': '<?php echo esc_js( $this->user_email ); ?>'
     257            });
     258           
     259            var item_ids = <?php if ( $product_id ) { ?> {'item_ids': ["<?php echo esc_js( $product_id ); ?>"]} <?php } else { echo "0"; } ?>;
     260            snaptr('track', 'PAGE_VIEW', item_ids);
     261        </script>
     262        <!-- End Snapchat Pixel Code -->
     263        <?php
     264        // Fire action for add-ons
     265        $event_data = array();
     266        if (isset($item_ids)) {
     267            $event_data['item_ids'] = $item_ids;
     268        }
     269        do_action( 'snap_pixel_event_fired', 'PAGE_VIEW', $event_data );
     270        ?>
     271        <?php
     272    }
     273
     274    /**
     275    * Output search pixel code.
     276    */
     277    public function output_search_pixel_code() {
     278        global $wp_query;
     279       
     280        $search_term = get_search_query();
     281       
     282        ?>
     283        <!-- SEARCH pixel event by Snap Pixel Plugin -->
     284        <script>
     285            snaptr('track', 'SEARCH', {
     286                'search_string': "<?php echo esc_js( $search_term ); ?>"
     287            });
     288        </script>
     289        <!-- End SEARCH pixel event by Snap Pixel Plugin -->
     290        <?php
     291        // Fire action for add-ons
     292        $event_data = array(
     293            'search_string' => $search_term
     294        );
     295        do_action( 'snap_pixel_event_fired', 'SEARCH', $event_data );
     296        ?>
     297        <?php
     298    }
     299
     300    /**
     301    * Output view content pixel code for product pages.
     302    */
     303    public function output_viewcontent_pixel_code() {
     304        global $post;
     305       
     306        if ( ! $this->is_woocommerce_active() ) {
     307            return;
     308        }
     309       
     310        $product = wc_get_product( $post->ID );
     311        if ( ! $product ) {
     312            return;
     313        }
     314       
     315        $product_id = $product->get_id();
     316        $product_price = $product->get_price();
     317        $product_currency = get_woocommerce_currency();
     318       
     319        // Get product category
     320        $terms = get_the_terms( $product_id, 'product_cat' );
     321        $product_category = '';
     322       
     323        if ( $terms && ! is_wp_error( $terms ) ) {
     324            $product_category = $terms[0]->name;
     325        }
     326       
     327        ?>
     328        <!-- VIEW_CONTENT pixel event by Snap Pixel Plugin -->
     329        <script>
     330            snaptr('track', 'VIEW_CONTENT', {
     331                'item_category': "<?php echo esc_js( $product_category ); ?>",
     332                'item_ids': ["<?php echo esc_js( $product_id ); ?>"],
     333                'price': <?php echo esc_js( $product_price ); ?>,
     334                'currency': "<?php echo esc_js( $product_currency ); ?>"
     335            });
     336        </script>
     337        <!-- End VIEW_CONTENT pixel event by Snap Pixel Plugin -->
     338        <?php
     339        // Fire action for add-ons
     340        $event_data = array(
     341            'item_category' => $product_category,
     342            'item_ids' => array($product_id),
     343            'price' => $product_price,
     344            'currency' => $product_currency
     345        );
     346        do_action( 'snap_pixel_event_fired', 'VIEW_CONTENT', $event_data );
     347        ?>
     348        <?php
     349    }
     350
     351    /**
     352    * Output checkout pixel code.
     353    */
     354    public function output_checkout_pixel_code() {
     355        if ( ! $this->is_woocommerce_active() ) {
     356            return;
     357        }
     358       
     359        global $woocommerce;
     360       
     361        $price = $woocommerce->cart->total;
     362        $product_currency = get_woocommerce_currency();
     363        $num_items = $woocommerce->cart->get_cart_contents_count();
     364        $product_id = '';
     365       
     366        foreach ( WC()->cart->get_cart() as $cart_item ) {
     367            $product_id = $cart_item['product_id'];
     368            break;
     369        }
     370       
     371        ?>
     372        <!-- START_CHECKOUT pixel event by Snap Pixel Plugin -->
     373        <script>
     374            snaptr('track', 'START_CHECKOUT', {
     375                'currency': "<?php echo esc_js( $product_currency ); ?>",
     376                'price': <?php echo esc_js( $price ); ?>,
     377                'num_items': <?php echo esc_js( $num_items ); ?>,
     378                'item_ids': ["<?php echo esc_js( $product_id ); ?>"]
     379            });
     380        </script>
     381        <!-- End START_CHECKOUT pixel event by Snap Pixel Plugin -->
     382        <?php
     383        // Fire action for add-ons
     384        $event_data = array(
     385            'currency' => $product_currency,
     386            'price' => $price,
     387            'num_items' => $num_items,
     388            'item_ids' => array($product_id)
     389        );
     390        do_action( 'snap_pixel_event_fired', 'START_CHECKOUT', $event_data );
     391        ?>
     392        <?php
     393    }
     394
     395    /**
     396    * Output purchase pixel code.
     397    */
     398    public function output_purchase_pixel_code() {
     399        if ( ! $this->is_woocommerce_active() ) {
     400            return;
     401        }
     402       
     403        global $woocommerce;
     404       
     405        // Check if key exists and verify nonce if it's from a form submission
     406        if ( isset( $_GET['key'] ) ) {
     407            $key = sanitize_text_field( wp_unslash( $_GET['key'] ) );
     408           
     409            // For WooCommerce order-received endpoint, the key is part of the URL and not from a form submission
     410            // so we don't need to verify a nonce in this specific case. This is a standard WooCommerce URL parameter
     411            // used for order identification and not for processing form submissions.
     412        } else {
     413            $key = '';
     414        }
     415       
     416        $order_id = $this->get_order_id_by_key( $key );
     417       
     418        if ( ! $order_id ) {
     419            return;
     420        }
     421       
     422        $order = wc_get_order( $order_id );
     423        if ( ! $order ) {
     424            return;
     425        }
     426       
     427        $order_total = $order->get_total();
     428        $product_currency = get_woocommerce_currency();
     429        $num_items = $woocommerce->cart->get_cart_contents_count();
     430       
     431        // Get product IDs from the order items
     432        $product_ids = array();
     433        foreach ( $order->get_items() as $item ) {
     434            // Access product_id directly from the item data
     435            $item_data = $item->get_data();
     436            $product_id = isset( $item_data['product_id'] ) ? $item_data['product_id'] : 0;
     437           
     438            if ( $product_id ) {
     439                $product_ids[] = $product_id;
     440            }
     441        }
     442        if(count($product_ids) > 0){
     443            $content_ids_string = $product_ids;
     444        } else {
     445            $content_ids_string = $order_id;
     446        }
     447       
     448        ?>
     449        <!-- PURCHASE pixel event by Snap Pixel Plugin -->
     450        <script>
     451            snaptr('track', 'PURCHASE', {
     452                'currency': "<?php echo esc_js( $product_currency ); ?>",
     453                'price': <?php echo esc_js( $order_total ); ?>,
     454                'transaction_id': "<?php echo esc_js( $order_id ); ?>",
     455                'item_ids': content_ids_string
     456            });
     457           
     458            snaptr('track', 'ADD_BILLING');
     459        </script>
     460        <!-- End PURCHASE pixel event by Snap Pixel Plugin -->
     461        <?php
     462        // Fire action for add-ons
     463        $event_data = array(
     464            'currency' => $product_currency,
     465            'price' => $order_total,
     466            'transaction_id' => $order_id,
     467            'item_ids' => $content_ids_string
     468        );
     469        do_action( 'snap_pixel_event_fired', 'PURCHASE', $event_data );
     470       
     471        // Fire action for ADD_BILLING event
     472        do_action( 'snap_pixel_event_fired', 'ADD_BILLING', array() );
     473        ?>
     474        <?php
     475    }
     476
     477    /**
     478    * Output add to cart pixel code.
     479    */
     480    public function output_addtocart_pixel_code() {
     481        if ( ! $this->is_woocommerce_active() ) {
     482            return;
     483        }
     484       
     485        global $post;
     486       
     487        $product_currency = get_woocommerce_currency();
     488        $product_price = '';
     489       
     490        if ( $post && function_exists( 'wc_get_product' ) ) {
     491            $product = wc_get_product( $post->ID );
     492            if ( $product ) {
     493                $product_price = $product->get_price();
     494            }
     495        }
     496       
     497        ?>
     498        <!-- ADD_CART pixel event by Snap Pixel Plugin -->
     499        <script>
     500            jQuery('body').on('added_to_cart', function(e, h, w, button) {
     501                var product_id = button.data("product_id");
     502                var product_result = get_product_record(product_id);
     503                var product_price = "<?php echo esc_js( $product_price ); ?>";
     504               
     505                setTimeout(function() {
     506                    product_price = jQuery("#return_response").val();
     507                    snaptr('track', 'ADD_CART', {
     508                        'currency': "<?php echo esc_js( $product_currency ); ?>",
     509                        'price': product_price,
     510                        'item_category': "",
     511                        'item_ids': [product_id]
     512                    });
     513                }, 1500);
     514            });
     515        </script>
     516        <!-- End ADD_CART pixel event by Snap Pixel Plugin -->
     517        <?php
     518        // Fire action for add-ons
     519        $event_data = array(
     520            'currency' => $product_currency,
     521            'price' => $product_price
     522        );
     523        do_action( 'snap_pixel_event_fired', 'ADD_CART', $event_data );
     524        ?>
     525        <?php
     526    }
     527
     528    /**
     529    * Output billing pixel code.
     530    */
     531    public function output_billing_pixel_code() {
     532        ?>
     533        <script type='text/javascript'>
     534            snaptr('track', 'ADD_BILLING');
     535        </script>
     536        <?php
     537        // Fire action for add-ons
     538        do_action( 'snap_pixel_event_fired', 'ADD_BILLING', array() );
     539    }
     540
     541    /**
     542    * AJAX handler for getting product data.
     543    */
     544    public function get_product_data() {
     545        // Verify nonce - required for security
     546        if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'snapchat_product_data' ) ) {
     547            wp_send_json_error( 'Invalid or missing security token' );
     548            wp_die();
     549        }
     550       
     551        if ( isset( $_REQUEST['snap_product_id'] ) && ! empty( $_REQUEST['snap_product_id'] ) ) {
     552            $product_id = absint( $_REQUEST['snap_product_id'] );
     553           
     554            if ( function_exists( 'wc_get_product' ) ) {
     555                $product = wc_get_product( $product_id );
     556               
     557                if ( $product ) {
     558                    $product_price = $product->get_price();
     559                    wp_send_json_success( $product_price );
     560                    wp_die();
     561                }
     562            }
     563        }
     564       
     565        wp_send_json_error( 'Error' );
     566        wp_die();
     567    }
     568
     569    /**
     570    * Get order ID by order key.
     571    *
     572    * @param string $key Order key.
     573    * @return int|bool Order ID or false if not found.
     574    */
     575    private function get_order_id_by_key( $key ) {
     576        if ( empty( $key ) ) {
     577            return false;
     578        }
     579       
     580        // Check cache first
     581        $cache_key = 'snap_pixel_order_' . md5($key);
     582        $order_id = wp_cache_get( $cache_key, 'snap_pixel' );
     583       
     584        if ( false !== $order_id ) {
     585            return $order_id;
     586        }
     587       
     588        global $wpdb;
     589       
     590        $result = $wpdb->get_row(
     591            $wpdb->prepare(
     592                "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key='_order_key' AND meta_value=%s ORDER BY meta_id DESC LIMIT 1",
     593                $key
     594            )
     595        );
     596       
     597        if ( $result ) {
     598            // Cache the result for future use
     599            wp_cache_set( $cache_key, $result->post_id, 'snap_pixel', 3600 ); // Cache for 1 hour
     600            return $result->post_id;
     601        }
     602       
     603        // Cache the negative result as well
     604        wp_cache_set( $cache_key, 0, 'snap_pixel', 3600 ); // Cache for 1 hour
     605        return false;
     606    }
     607
     608    /**
     609    * Check if WooCommerce is active.
     610    *
     611    * @return bool
     612    */
     613    private function is_woocommerce_active() {
     614        return in_array(
     615            'woocommerce/woocommerce.php',
     616            apply_filters( 'active_plugins', get_option( 'active_plugins' ) )
     617        );
     618    }
    619619}
Note: See TracChangeset for help on using the changeset viewer.