<?php
/*
Plugin Name: LEVO Tracking Data for Gravity Forms
Plugin URI: https://levohealth.com
Description: Capture UTM codes and page level data such as entry URL, referring page URL, and user IP; determine lead source based on UTM codes and referrer, save all data to cookies, and inject into Gravity Forms hidden fields.
Author: LEVO Health
Version: 1.4.1
*/

add_filter( 'widget_text', 'do_shortcode' );
add_action( 'wp_loaded', 'LEVO_Tracking_Data::capture_data' );

class LEVO_Tracking_Data {
    /**
     * Function to capture UTM codes, referring URLs, and other page data to set in tracking cookies
     */
    public static function capture_data() {
        // set current domain
        $current_domain = $_SERVER['HTTP_HOST'];
        $base_url = ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://' ) . $current_domain;

        // set cookie expiry date for 90 days in the future
        $cookie_expiry = time() + ( 3600 * 24 * 90 );

        // set the current URL from server vars
        $current_url = $base_url . $_SERVER['REQUEST_URI'];

        // set entry referrer
        if ( empty( $_COOKIE['entry_http_referrer'] ) && ! empty( $_SERVER['HTTP_REFERER'] ) ) {
            setcookie( 'entry_http_referrer', $_SERVER['HTTP_REFERER'], $cookie_expiry, '/', $current_domain );
        }

        // set entry URL
        if ( empty( $_COOKIE['entry_url'] ) ) {
            setcookie( 'entry_url', $current_url, $cookie_expiry, '/', $current_domain );
        }

        // set user IP
        if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            $user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            $user_ip = $_SERVER['REMOTE_ADDR'];
        }
        if ( empty( $_COOKIE['user_ip'] ) && ! empty( $user_ip ) ) {
            setcookie( 'user_ip', $user_ip, $cookie_expiry, '/', $current_domain );
        }

        // set current referrer
        if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
            setcookie( 'current_http_referrer', $_SERVER['HTTP_REFERER'], $cookie_expiry, '/', $current_domain );
            $_COOKIE['current_http_referrer'] = $_SERVER['HTTP_REFERER'];
        }
        // set current URL
        setcookie( 'current_url', $current_url, $cookie_expiry, '/', $current_domain );
        $_COOKIE['current_url'] = $current_url;

        // set lead source
        $lead_source = self::set_lead_source( $base_url );
        if ( ! empty( $lead_source ) ) $_COOKIE['lead_source'] = $lead_source;

        $non_utm_fields = ['lead_source', 'entry_url', 'entry_http_referrer', 'current_url', 'current_http_referrer', 'user_ip'];

        foreach ( $non_utm_fields as $field) {
            self::set_shortcodes_and_filters( $field );
        }

        // set fields for utm codes and special marketing IDs to try and determine the lead source from them
        $fields = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'fbclid'];

        // if utm cookies exist but are from organic, overwrite them
        $utm_medium = ! empty( $_COOKIE['utm_medium'] ) ? $_COOKIE['utm_medium'] : '';
        $cookie_organic = ! empty( $lead_source ) && $utm_medium == 'organic' ? true : false;

        // loop through utm codes to set values and update in shortcodes
        foreach ( $fields as $field ) {
            $cookie_field = '';
            if ( ! empty( $_COOKIE[$field] ) && ! $cookie_organic ) {
                $cookie_field = $_COOKIE[$field];
            }
            if ( ! empty( $_GET[$field] ) && empty( $cookie_field ) ) {
                $cookie_field = htmlspecialchars( $_GET[$field], ENT_QUOTES, 'UTF-8' );
            }
            if ( ! empty( $cookie_field ) ) {
                setcookie( $field, $cookie_field, $cookie_expiry, '/', $current_domain );
                $_COOKIE[$field] = $cookie_field;
            }

            self::set_shortcodes_and_filters( $field );
        }
    }

    /**
     * Function to set up shortcodes and filters based on cookie values to set dynamic Gravity Forms field values
     *
     * @param  string  $field  Dynamic field in a Gravity Form to replace with matching cookie values
     */
    public static function set_shortcodes_and_filters( $field ) {
        // add shortcodes
        //add_shortcode( $field, function() use ( $field ) { return urldecode( $_COOKIE[$field] ); } );
        add_shortcode( $field, function( $atts, $content ) use ( $field ) { return sprintf( $content, urldecode( $_COOKIE[$field] ) ); } );
        add_shortcode( $field . '_i', function( $atts, $content ) use ( $field ) { return sprintf( $content, urldecode( $_COOKIE[preg_replace("/_i$/",'', $field )] ) ); } );

        // filter for gravity forms
        add_filter( 'gform_field_value_' . $field, function() use ( $field ) { return urldecode( $_COOKIE[$field] ); } );
    }

    /**
     * Function to set a "human-readable" lead source based on tracking data in cookies
     *
     * @param  string  $base_url  Base URL of the current page to use in logic
     */
    public static function set_lead_source( $base_url ) {
        $lead_source_cookie = '';
        $lead_source = '';
        $originating_lead_source = '';
        $originating_lead_source_organic = false;

        if ( ! empty( $_COOKIE['lead_source'] ) ) {
            $originating_lead_source = $_COOKIE['lead_source'];
            if ( $originating_lead_source != 'Direct' ) $lead_source_cookie = $originating_lead_source;
            if ( stristr( $lead_source_cookie, 'organic' ) !== false ) $originating_lead_source_organic = true;
        }

        // run through logic to determine the current lead_source (which may be different from an original lead_source)
        // set a qs variable for use in certain checks
        $qs = ! empty( $_SERVER['QUERY_STRING'] ) ? $_SERVER['QUERY_STRING'] : '';

        // first check for a gclid (Google click id) in URL and if found, set as Google
        if ( ! empty( $_GET['gclid'] ) || ! empty( $_COOKIE['gclid'] ) ) $lead_source = 'Google - Paid';

        // now check for an fbclid (Facebook click id) in URL and if found, set as Facebook
        if ( ! empty( $_GET['fbclid'] ) || ! empty( $_COOKIE['fbclid'] ) ) $lead_source = 'Facebook - Paid';

        // see if one of the search engine sources or Facebook can be found in the querystring
        $utm_source = ! empty( $_GET['utm_source'] ) ? strtolower( $_GET['utm_source'] ) : '';
        $utm_medium = ! empty( $_GET['utm_medium'] ) ? strtolower( $_GET['utm_medium'] ) : '';
        // set lead source type Organic or Paid, include {space}-{space} in returned value
        $lead_source_type = self::lead_source_type_by_medium( $utm_medium, $utm_source );

        if ( empty( $lead_source ) && ! empty( $lead_source_type ) ) {
            $sources_array = ['Google', 'Bing', 'Yahoo', 'DuckDuckGo', 'Facebook', 'YouTube', 'Twitter', 'Instagram', 'LinkedIn', 'Pinterest', 'TikTok'];

            // check most common search and social sources against qs and utm_source
            foreach ( $sources_array as $potential_source ) {
                if ( stristr( $qs, $potential_source ) !== false || $utm_source == strtolower( $potential_source ) ) {
                    $lead_source = $potential_source . $lead_source_type;
                    break;
                }
            }
        }

        // if lead source is still empty, do additional tests
        // see if from PR
        if ( empty( $lead_source ) && ! empty( $utm_source ) && $utm_source == 'pr' ) {
            $lead_source = 'Public Relations';
        }

        // see if a link from an email
        elseif ( empty( $lead_source ) && stristr( $utm_medium, 'email' ) !== false ) {
            $lead_source = 'Email Marketing';
        }

        // if no match yet, try to determine from the referring URL
        elseif ( empty( $lead_source ) && ( ! empty( $_SERVER['HTTP_REFERER'] ) || ! empty( $_COOKIE['entry_http_referrer'] ) ) ) {
            $sources_array = ['google.', 'bing.', 'yahoo.', 'ask.com', 'duckduckgo.', 'aol.'];

            if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
                $referring_url = $_SERVER['HTTP_REFERER'];

                // see if one of the search engine sources can be found in the URL
                foreach ( $sources_array as $potential_source ) {
                    if ( stristr( $referring_url, $potential_source ) !== false ) {
                        $lead_source = $potential_source != 'ask.com' ? str_replace( '.', '', $potential_source ) : 'ask.com';
                        $lead_source = $potential_source != 'duckduckgo.' ? $lead_source : 'DuckDuckGo';
                        $lead_source = $potential_source != 'aol.' ? $lead_source : 'AOL';
                        $lead_source = ucwords( $lead_source ) . ' - Organic';
                        break;
                    }
                }
            }
            if ( empty( $lead_source ) && ! empty( $_COOKIE['entry_http_referrer'] ) ) {
                $referring_url = $_COOKIE['entry_http_referrer'];

                // see if one of the search engine sources can be found in the URL
                foreach ( $sources_array as $potential_source ) {
                    if ( stristr( $_COOKIE['entry_http_referrer'], $potential_source ) !== false ) {
                        $lead_source = $potential_source != 'ask.com' ? str_replace( '.', '', $potential_source ) : 'ask.com';
                        $lead_source = $potential_source != 'duckduckgo.' ? $lead_source : 'DuckDuckGo';
                        $lead_source = ucwords( $lead_source ) . ' - Organic';
                        break;
                    }
                }
            }

            // see if the referrer is an email or job site
            if ( empty( $lead_source ) && stristr( $referring_url, 'utm_medium=email' ) !== false ) {
                $lead_source = 'Email Marketing';
            } elseif ( empty( $lead_source ) && stristr( $referring_url, 'indeed.co' ) !== false ) {
                $lead_source = 'Indeed';
            } elseif ( empty( $lead_source ) && stristr( $referring_url, 'glassdoor.co' ) !== false ) {
                $lead_source = 'Glassdoor';
            } elseif ( empty( $lead_source ) && stristr( $referring_url, 'monster.co' ) !== false ) {
                $lead_source = 'Monster';
            } else {
                // see if the referrer is social
                $sources_array = ['Facebook.co', 'Twitter.co', 'Instagram.co', 'YouTube.co', 'LinkedIn.co', 'Pinterest.co', 'TikTok.co'];
                foreach ( $sources_array as $potential_source ) {
                    if ( stristr( $referring_url, $potential_source ) !== false ) {
                        $lead_source = str_replace( '.co', '', $potential_source );
                        $lead_source = $lead_source . ' - Organic';
                        break;
                    }
                }
            }
        }

        // if still no match then send as Direct or Web - Unknown and do not set cookie
        if ( empty( $lead_source_cookie ) && empty( $lead_source ) ) {
            $lead_source = ! empty( $utm_source ) ? ucwords( $utm_source ) : 'Web';

            //remove the protocol because sometime the referrer is the http version of the URL but it should still be set as diret
            if ( ! empty( $_COOKIE['entry_url'] ) ) {
                $entry_url_no_protocol = strtolower( str_replace( ['http://', 'https://'], '', $_COOKIE['entry_url'] ) );
                $base_url_no_protocol = strtolower( str_replace( ['http://', 'https://'], '', $base_url ) );

                if ( $entry_url_no_protocol == $base_url_no_protocol || $entry_url_no_protocol == $base_url_no_protocol . '/' ) {
                    $lead_source = 'Direct';
                }
            }
        } else {
            $cookie_host = $_SERVER['HTTP_HOST'];
            $cookie_expire = time() + ( 3600 * 24 * 90 );
            $new_lead_source_organic = stristr( $lead_source, 'organic' ) !== false ? true : false;

            if ( ( empty( $lead_source_cookie ) && ! empty( $lead_source ) ) || ( $originating_lead_source_organic && ! $new_lead_source_organic ) ) {
                setcookie( 'lead_source', $lead_source, $cookie_expire, '/', $cookie_host );
            } else {
                $lead_source = $lead_source_cookie;
            }
        }

        // dirty fix to ensure Organic sources are set if lead source comes across as just the channel name
        $sources_array = ['Google', 'Bing', 'Yahoo', 'DuckDuckGo', 'Facebook', 'YouTube', 'Twitter', 'Instagram', 'LinkedIn', 'Pinterest', 'TikTok'];
        foreach ( $sources_array as $potential_source ) {
            if ($lead_source == $potential_source) $lead_source .= ' - Organic';
        }
        return $lead_source;
    }

    /**
     * Function to determine lead source type - Paid or Organic - by UTM medium
     * Added extra check on special UTM source of programmatic
     *
     * @param  string  $utm_medium  The UTM medium querystring value in the URL
     * @param  string  $utm_source  The UTM source querystring value in the URL
     */
    public static function lead_source_type_by_medium( $utm_medium, $utm_source='' ) {
        $lead_source_type = '';
        $paid_mediums = ['cpc', 'ppc', 'pso', 'pap', 'display', 'tv', 'video', 'banner'];

        if ( ! empty( $utm_medium ) && ! in_array( $utm_medium, $paid_mediums ) ) {
            $lead_source_type = ' - Organic';
        } elseif ( ! empty( $utm_medium ) ) {
            $lead_source_type = ' - Paid';
        }

        if ( strtolower( $utm_source ) === 'programmatic') {
            $lead_source_type = ' - Paid';
        }

        return $lead_source_type;
    }
}