plugin_settings = new Settings\Plugin_Settings( $this ); $this->form_settings = new Settings\Form_Settings( $this ); parent::pre_init(); } /** * Performs enqueuing tasks for JS, redirection, event save state, and pagination. * * @since 1.0.0 */ public function init() { parent::init(); add_action( 'gform_confirmation', array( $this, 'force_text_confirmation' ), 1000, 4 ); add_action( 'wp_head', array( $this, 'maybe_install_analytics' ) ); add_action( 'wp_head', array( $this, 'maybe_install_tag_manager_header' ) ); add_action( 'admin_notices', array( $this, 'maybe_display_authentication_notice' ) ); add_action( 'wp_body_open', array( $this, 'maybe_install_tag_manager_body' ) ); add_filter( 'gform_ajax_submission_result', array( $this, 'set_post_ajax_submission_vars' ), 10, 1 ); // General pagination. add_action( 'gform_post_paging', array( $this, 'pagination' ), 10, 3 ); } /** * Add Admin handlers. */ public function init_admin() { $this->plugin_settings->maybe_update_auth_tokens(); parent::init_admin(); } /** * Add Ajax handlers. */ public function init_ajax() { add_action( 'wp_ajax_nopriv_get_entry_meta', array( $this, 'ajax_get_entry_meta' ) ); add_action( 'wp_ajax_get_entry_meta', array( $this, 'ajax_get_entry_meta' ) ); add_action( 'wp_ajax_nopriv_save_entry_meta', array( $this, 'ajax_save_entry_meta' ) ); add_action( 'wp_ajax_save_entry_meta', array( $this, 'ajax_save_entry_meta' ) ); add_action( 'wp_ajax_save_google_analytics_data', array( $this, 'ajax_save_google_analytics_data' ) ); add_action( 'wp_ajax_save_google_tag_manager_data', array( $this, 'ajax_save_google_tag_manager_data' ) ); add_action( 'wp_ajax_save_manual_configuration_data', array( $this, 'ajax_save_manual_configuration_data' ) ); add_action( 'wp_ajax_get_ga4_data_streams', array( $this, 'ajax_get_ga4_data_streams' ) ); add_action( 'wp_ajax_get_gtm_workspaces', array( $this, 'ajax_get_gtm_workspaces' ) ); add_action( 'wp_ajax_get_gtm_containers', array( $this, 'ajax_get_gtm_containers' ) ); add_action( 'wp_ajax_redirect_to_api', array( $this, 'ajax_redirect_to_api' ) ); add_action( 'wp_ajax_disconnect_account', array( $this, 'ajax_disconnect_account' ) ); add_action( 'wp_ajax_gf_ga_log_event_sent', array( $this, 'ajax_log_ga_event_sent' ) ); add_action( 'wp_ajax_nopriv_gf_ga_log_event_sent', array( $this, 'ajax_log_ga_event_sent' ) ); parent::init_ajax(); } /** * Initializes the Google Analytics API if credentials are valid. * * @since 1.0 * * @return bool|null API initialization state. Returns null if no authentication token is provided. */ public function initialize_api() { // If the API is already initializes, return true. if ( ! is_null( $this->api ) ) { return true; } // Initialize Google Analytics API library. if ( ! class_exists( 'GF_Google_Analytics_API' ) ) { require_once 'includes/class-gf-google-analytics-api.php'; } // Get the authentication token. $auth_token = self::get_options( 'auth_token' ); $mode = self::get_options( '', 'mode' ); if ( empty( $mode ) ) { $mode = self::get_options( '', 'tempmode' ); } $token = isset( $auth_token['token'] ) ? $auth_token['token'] : ''; $date_created = isset( $auth_token['date_created'] ) ? $auth_token['date_created'] : 0; // If the authentication token is not set, return null. if ( empty( $auth_token ) || rgblank( $token ) || 'unset' === $mode || rgblank( $mode ) ) { return null; } // Initialize a new Google Analytics API instance. $google_analytics_api = new GF_Google_Analytics_API( $this, $token ); if ( time() > ( $date_created + 3600 ) ) { // Access token expires in 1 hour = 3600 seconds. // Log that authentication test failed. $this->log_debug( __METHOD__ . '(): API tokens expired, start refreshing.' ); // Refresh token. $auth_response = $google_analytics_api->refresh_token( $auth_token['refresh'] ); if ( ! is_wp_error( $auth_response ) ) { $auth_settings = array( 'token' => rgars( $auth_response, 'token/access_token' ), 'refresh' => rgars( $auth_response, 'token/refresh_token' ), 'date_created' => rgars( $auth_response, 'token/created' ), ); // Save plugin settings. $this->update_options( $auth_settings, 'auth_token' ); $this->log_debug( __METHOD__ . '(): API access token has been refreshed.' ); } else { $this->log_debug( __METHOD__ . '(): API access token failed to be refreshed; ' . $auth_response->get_error_message() ); return false; } } // Assign Google Analytics API instance to the Add-On instance. $this->api = $google_analytics_api; return true; } /** * Maybe display an admin notice if Google Analytics needs to be reauthenticated. * * @since 2.0 */ public function maybe_display_authentication_notice() { if ( ! $this->requires_api_reauthentication() ) { return; } $message = sprintf( /* translators: 1: reauthentication version number 2: open tag, 3: close tag */ esc_html__( 'Gravity Forms Google Analytics v%1$s requires re-authentication in order to correctly send data to Google Analytics 4. Please %2$sdisconnect and reconnect%3$s to ensure data can be sent to GA4.', 'gravityformsgoogleanalytics' ), self::LAST_REAUTHENTICATION_VERSION, '', '' ) ?>

array( 'href' => true ) ) ); ?>

get_plugin_setting( $this->_slug ); return rgar( $plugin_setting, 'enable' ); } /** * Outputs admin scripts to handle form submission in back-end. * * @since 1.0.0 * * @return array */ public function scripts() { $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || rgget( 'gform_debug' ) ? '' : '.min'; $scripts = array( array( 'handle' => 'google_analytics_admin', 'src' => $this->get_base_url() . "/js/google-analytics-admin{$min}.js", 'version' => $this->_version, 'deps' => array( 'jquery', 'wp-ajax-response' ), 'strings' => array( 'update_settings' => wp_strip_all_tags( __( 'Update Settings', 'gravityformsgoogleanalytics' ) ), 'disconnect' => wp_strip_all_tags( __( 'Disconnecting', 'gravityformsgoogleanalytics' ) ), 'redirect' => wp_strip_all_tags( __( 'Redirecting...', 'gravityformsgoogleanalytics' ) ), 'connect' => wp_strip_all_tags( __( 'Connect', 'gravityformsgoogleanalytics' ) ), 'connecting' => wp_strip_all_tags( __( 'Connecting', 'gravityformsgoogleanalytics' ) ), 'workspace_required' => wp_strip_all_tags( __( 'You must select a Workspace', 'gravityformsgoogleanalytics' ) ), 'view_required' => wp_strip_all_tags( __( 'You must select a View', 'gravityformsgoogleanalytics' ) ), 'ga_required' => wp_strip_all_tags( __( 'You must select a Google Analytics account', 'gravityformsgoogleanalytics' ) ), 'gtm_required' => wp_strip_all_tags( __( 'You must select a Tag Manager Account', 'gravityformsgoogleanalytics' ) ), 'spinner' => GFCommon::get_base_url() . '/images/spinner.svg', 'mode_required' => wp_strip_all_tags( __( 'Please select a connection type to continue.', 'gravityformsgoogleanalytics' ) ), ), 'in_footer' => true, 'enqueue' => array( array( 'query' => 'page=gf_settings&subview=gravityformsgoogleanalytics', ), ), ), array( 'handle' => self::FE_SCRIPT_HANDLE, 'src' => $this->get_base_url() . "/js/google-analytics{$min}.js", 'version' => $this->_version, 'deps' => array( 'jquery', 'wp-ajax-response' ), 'in_footer' => true, 'enqueue' => array( array( $this, 'frontend_script_callback' ), ), 'callback' => array( $this, 'frontend_script_strings_callback' ), ), ); return array_merge( parent::scripts(), $scripts ); } /** * Get Gravity API URL. * * @since 1.0 * * @param string $path Path. * * @return string */ public function get_gravity_api_url( $path = '' ) { return ( defined( 'GRAVITY_API_URL' ) ? GRAVITY_API_URL : 'https://gravityapi.com/wp-json/gravityapi/v1' ) . $path; } /** * Retrieve the plugin's options. * * Retrieve the plugin's options based on context. * * @since 1.0.0 * * @param string $context Context to retrieve options for. This is used as an array key. * @param string $key Array key to retrieve. * @param bool $force_reload Whether to retrieve cached options or forcefully retrieve from the database. * * @return mixed All options if no context, or associative array if context is set. Empty array if no options. String if $key is set. */ public static function get_options( $context = '', $key = false, $force_reload = false ) { // Try to get cached options. $options = self::$options; if ( false === $options || true === $force_reload ) { $options = get_option( 'gravityformsaddon_gravityformsgoogleanalytics_settings', array() ); } // Store options. if ( ! is_array( $options ) ) { $options = array(); } // Assign options for caching. self::$options = $options; if ( rgblank( $context ) && $key ) { if ( isset( $options[ $key ] ) ) { return $options[ $key ]; } else { return ''; } } // Attempt to get context. if ( ! empty( $context ) && is_string( $context ) ) { if ( array_key_exists( $context, $options ) ) { if ( false !== $key && is_string( $key ) ) { if ( isset( $options[ $context ][ $key ] ) ) { return $options[ $context ][ $key ]; } } else { return (array) $options[ $context ]; } } else { return array(); } } return $options; } /** * Save plugin options. * * Saves the plugin options based on context. If no context is provided, updates all options. * * @since 1.0.0 * * @param array $options Associative array of plugin options. * @param string $context Array key of which options to update. */ public static function update_options( $options = array(), $context = '' ) { $options_to_save = self::get_options(); if ( ! empty( $context ) && is_string( $context ) ) { $options_to_save[ $context ] = $options; } else { $options_to_save = $options; } update_option( 'gravityformsaddon_gravityformsgoogleanalytics_settings', $options_to_save ); self::$options = $options_to_save; } /** * Save feed settings. * * @since 1.0.0 * * @param int $feed_id The Feed ID. * @param int $form_id The Form ID. * * @return int */ public function maybe_save_feed_settings( $feed_id, $form_id ) { if ( ! rgpost( 'gform-settings-save' ) ) { return $feed_id; } check_admin_referer( $this->_slug . '_save_settings', '_' . $this->_slug . '_save_settings_nonce' ); if ( ! $this->current_user_can_any( $this->_capabilities_form_settings ) ) { GFCommon::add_error_message( esc_html__( "You don't have sufficient permissions to update the form settings.", 'gravityformsgoogleanalytics' ) ); return $feed_id; } // Store a copy of the previous settings for cases where action would only happen if value has changed. $feed = $this->get_feed( $feed_id ); $this->set_previous_settings( $feed['meta'] ); $settings = $this->get_posted_settings(); $sections = $this->get_feed_settings_fields(); $settings = $this->trim_conditional_logic_vales( $settings, $form_id ); $is_valid = $this->validate_settings( $sections, $settings ); if ( $is_valid ) { $settings = $this->filter_settings( $sections, $settings ); $feed_id = $this->save_feed_settings( $feed_id, $form_id, $settings ); if ( $feed_id ) { GFCommon::add_message( $this->get_save_success_message( $sections ) ); } else { GFCommon::add_error_message( $this->get_save_error_message( $sections ) ); } } else { GFCommon::add_error_message( $this->get_save_error_message( $sections ) ); } $redirect_url = add_query_arg( array( 'page' => 'gf_edit_forms', 'view' => 'settings', 'subview' => 'gravityformsgoogleanalytics', 'id' => $form_id, 'fid' => $feed_id, ), admin_url( 'admin.php' ) ); if ( 0 === absint( rgget( 'fid' ) ) ) { ?> log_debug( __METHOD__ . '(): Loading Google Analytics GTAG settings: ' . print_r( $settings, true ) ); // Attempt to get options. $measurement_id = sanitize_text_field( self::get_options( 'ga4_account', 'measurement_id' ) ); // User has requested GA installation. Proceed. echo "\r\n"; ?> log_debug( __METHOD__ . '(): Loading Google Tag Manager Installation Setting: ' . print_r( $settings, true ) ); // Attempt to get options. $gtm_code = sanitize_text_field( self::get_options( 'ga4_account', 'gtm_container_id' ) ); if ( empty( $gtm_code ) ) { return; } // User has requested Tag Manager installation. Proceed. echo "\r\n"; ?> admin_url( 'admin.php' ), 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', 'mode' => $settings_mode, 'nonce' => wp_create_nonce( 'gravityformsgoogleanalytics_ua' ), ); $auth_url = add_query_arg( array( 'mode' => $settings_mode, 'redirect_to' => admin_url( 'admin.php' ), 'state' => base64_encode( json_encode( $state ) ), 'license' => GFCommon::get_key(), ), $this->get_gravity_api_url( '/auth/googleanalytics' ) ); wp_safe_redirect( esc_url_raw( $auth_url ) ); exit(); } /** * Redirects to authentication screen for Google Tag Manager. * * @since 1.0.0 */ public function authenticate_google_tag_manager() { $ga_options = get_option( 'gforms_google_analytics_ga' ); $token = isset( $ga_options['token'] ) ? $ga_options['token'] : false; if ( $token && ( isset( $ga_options['mode'] ) && 'gtm' === $ga_options['mode'] ) ) { return; } $this->log_debug( __METHOD__ . '(): Before Authenticating With Tag Manager: ' . print_r( $ga_options, true ) ); $state = array( 'url' => admin_url( 'admin.php' ), 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', 'mode' => 'gtm', 'nonce' => wp_create_nonce( 'gravityformsgoogleanalytics_ua' ), ); $redirect_to = admin_url( 'admin.php' ); $auth_url = add_query_arg( array( 'mode' => 'gtm', 'state' => base64_encode( json_encode( $state ) ), 'redirect_to' => $redirect_to, 'license' => GFCommon::get_key(), ), $this->get_gravity_api_url( '/auth/googleanalytics' ) ); wp_safe_redirect( esc_url_raw( $auth_url ) ); exit; } /** * Returns an array with Google Analytics codes to be associated with the event. This only applies to Measurement Protocol. * * @since 1.0.0 * * @param array $form Current form object. * @param array $entry Current entry object. * @param array $feed Current feed object. * * @return array Returns an array of Google Analytics codes. */ private function get_ga_codes( $form, $entry = null, $feed = null ) { $ua_ids = array( $this->get_measurement_id() ); /** * Filter: gform_googleanalytics_ua_ids * * Filter all outgoing UA IDs to send events to using the measurement protocol. * * @since 1.0.0 * * @param array $ua_ids UA codes. * @param array $form Gravity Form form object. * @param array $entry Gravity Form Entry Object. * @param array $feed Current feed. * * @return array Google anaylics codes. */ return apply_filters( 'gform_googleanalytics_ua_ids', $ua_ids, $form, $entry, $feed ); } /** * Initialize the pagination events. * * @since 1.0.0 * * @param array $form The form arguments. * @param int $source_page_number The original page number. * @param int $current_page_number The new page number. */ public function pagination( $form, $source_page_number, $current_page_number ) { // Pagination tracking not enabled. Abort. if ( ! rgars( $form, 'gravityformsgoogleanalytics/google_analytics_pagination' ) ) { return; } $mode = self::get_options( '', 'mode' ); $parameters = $this->get_mapped_parameters( $form['gravityformsgoogleanalytics'], 'pagination_parameters', $form, \GFFormsModel::get_current_lead( $form ) ); $parameters = $this->maybe_replace_pagination_merge_tags( $parameters, $source_page_number, $current_page_number ); $is_ajax = isset( $_REQUEST['gform_ajax'] ) ? true : false; $event_name = $this->get_pagination_event_name( $mode, $form ); switch ( $mode ) { case 'gmp': // Send events via server side (measurement protocol). $ua_codes = $this->get_ga_codes( $form ); $this->send_measurement_protocol( $ua_codes, $parameters, $event_name, \GFFormsModel::get_current_page_url() ); break; case 'ga': $this->log_debug( __METHOD__ . '(): Attempting to send pagination event via Google Analytics. Event Name: ' . $event_name . '. Parameters: ' . print_r( $parameters, true ) ); $this->render_script_send_to_ga( $parameters, $event_name, $is_ajax ); break; case 'gtm': $trigger_name = rgar( $form['gravityformsgoogleanalytics'], 'pagination_trigger' ) == 'gf_custom' ? rgar( $form['gravityformsgoogleanalytics'], 'pagination_trigger_custom' ) : rgar( $form['gravityformsgoogleanalytics'], 'pagination_trigger' ); $this->log_debug( __METHOD__ . '(): Attempting to send pagination event via Google Tag Manager. Trigger Name: ' . $trigger_name . '. Parameters: ' . print_r( $parameters, true ) ); $this->render_script_send_to_gtm( $parameters, $trigger_name, $is_ajax ); break; } } /** * Maybe replace the pagination merge tags. * * @since 2.2.0 * * @param array $parameters The pagination parameters. * @param int $source_page_number The source page number. * @param int $current_page_number The target page number. * * @return array */ public function maybe_replace_pagination_merge_tags( $parameters, $source_page_number, $current_page_number ) { foreach ( $parameters as $key => $value ) { $parameters[ $key ] = str_replace( array( '{source_page_number}', '{current_page_number}' ), array( $source_page_number, $current_page_number ), $value ); } return $parameters; } /** * Loads the parameter list with the actual values from the form submission and query string. * * @since 2.0.0 * * @param array $settings Settings array containing the mapped field configuration. * @param string $parameter_setting_name The name of the parameter setting field. * @param array $form The current form object. * @param array $entry The current entry object. * * @return array An array with the mapped parameters */ private function get_mapped_parameters( $settings, $parameter_setting_name, $form, $entry ) { // Adding UTM values to entry. $entry['utm_source'] = rgget( 'utm_source' ); $entry['utm_medium'] = rgget( 'utm_medium' ); $entry['utm_campaign'] = rgget( 'utm_campaign' ); $entry['utm_term'] = rgget( 'utm_term' ); $entry['utm_content'] = rgget( 'utm_content' ); // Loading parameters. return $this->get_generic_map_fields( $settings, $parameter_setting_name, $form, $entry ); } /** * Redirect to Gravity Forms API. * * @since 1.0.0 */ public function ajax_redirect_to_api() { if ( ! wp_verify_nonce( rgpost( 'nonce' ), 'connect_google_analytics' ) || ! $this->current_user_can_any( $this->_capabilities_form_settings ) ) { wp_send_json_error( array( 'errors' => true, 'redirect' => '', ) ); } $state = wp_create_nonce( 'gravityforms_googleanalytics_google_connect' ); if ( get_transient( 'gravityapi_request_' . $this->get_slug() ) ) { delete_transient( 'gravityapi_request_' . $this->get_slug() ); } set_transient( 'gravityapi_request_' . $this->get_slug(), $state, 10 * MINUTE_IN_SECONDS ); $mode = sanitize_text_field( rgpost( 'mode' ) ); $options = self::get_options(); if ( $mode == 'manual' ) { $redirect_url = admin_url( 'admin.php?page=gf_settings&subview=' . $this->_slug ); $options['is_manual'] = true; } else { $action = $mode == 'gtm' ? 'gtmselect' : 'gaselect'; $settings_url = urlencode( admin_url( 'admin.php?page=gf_settings&subview=' . $this->_slug ) . '&action=' . $action ); $redirect_url = add_query_arg( array( 'mode' => sanitize_text_field( rgpost( 'mode' ) ), 'redirect_to' => $settings_url, 'state' => $state, 'license' => GFCommon::get_key(), ), $this->get_gravity_api_url( '/auth/googleanalytics' ) ); // Temporary mode for storing what the user has selected for authorization. $options['tempmode'] = sanitize_text_field( rgpost( 'mode' ) ); $this->log_debug( "Redirecting to Gravity API: {$redirect_url}" ); } // Updating options. self::update_options( $options ); wp_send_json_success( array( 'errors' => false, 'redirect' => esc_url_raw( $redirect_url ), ) ); } /** * Disconnects user from the Google Analytics API. * * @since 1.0.0 */ public function ajax_disconnect_account() { // Verify nonce and capability. $this->verify_ajax_nonce( 'gforms_google_analytics_disconnect' ); $this->verify_capability(); // Deleting option. Token expires in an hour, so no need to revoke it. delete_option( 'gravityformsaddon_gravityformsgoogleanalytics_settings' ); wp_send_json_success( array() ); } /** * Gets views for the selected account and GA code * * @since 2.0.0 */ public function ajax_get_ga4_data_streams() { // Verify nonce, capability and API connection. $this->verify_ajax_nonce(); $this->verify_capability(); $this->verify_api_connection(); // Retrieving data streams. $property = sanitize_text_field( rgpost( 'property' ) ); $data_streams = $this->api->get_data_streams( $property ); if ( is_wp_error( $data_streams ) ) { $this->log_debug( __METHOD__ . '(): Error retrieving Google Analytics data streams associated with the selected property.' ); wp_send_json_error( new WP_Error( 'google_analytics_ga_error', wp_strip_all_tags( __( 'There was an error retrieving Google Analytics data streams.', 'gravityformsgoogleanalytics' ) ) ) ); } // Create select field markup. $html = '
'; wp_send_json_success( $html ); } /** * Gets containers for the selected GTM account * * @since 1.1 */ public function ajax_get_gtm_containers() { // Verify nonce, capability and API connection. $this->verify_ajax_nonce(); $this->verify_capability(); $this->verify_api_connection(); $account_id = rgpost( 'accountId' ); $token = rgpost( 'token' ); // Get containers. $container_response = $this->api->get_tag_manager_containers( array(), $account_id ); if ( is_wp_error( $container_response ) ) { $this->log_debug( __METHOD__ . '(): Error retrieving property containers.' ); $error = new WP_Error( 'google_analytics_gtm_error', $container_response->get_error_message() ); wp_send_json_error( $error ); } // Output HTML. $success = array( 'success' => true ); $html = ''; $html .= '
'; $success['body'] = $html; wp_send_json( $success ); } /** * Gets workspaces for the selected GTM account * * @since 1.0.0 */ public function ajax_get_gtm_workspaces() { // Verify nonce, capability and API connection. $this->verify_ajax_nonce(); $this->verify_capability(); $this->verify_api_connection(); // Get workspace ID. $response = $this->api->get_tag_manager_workspaces( array(), rgpost( 'path' ) ); if ( is_wp_error( $response ) ) { $error = new WP_Error( 'google_analytics_gtm_error', wp_strip_all_tags( __( 'Could not retrieve Tag Manager workspaces', 'gravityformsgoogleanalytics' ) ) ); wp_send_json_error( $error ); } // Output HTML. $success = array( 'success' => true ); $html = ''; $html .= ''; $success['body'] = $html; wp_send_json( $success ); } /** * Updates plugin settings with the provided settings. Overrides parent to correctly update needed settings. * * @since 1.0 * * @param array $settings Plugin settings to be saved. */ public function update_plugin_settings( $settings ) { $current_settings = self::get_options(); // Set the API authentication version. $current_settings['reauth_version'] = self::LAST_REAUTHENTICATION_VERSION; if ( is_array( $current_settings ) ) { $settings = array_merge( $current_settings, $settings ); } update_option( 'gravityformsaddon_' . $this->_slug . '_settings', $settings ); } /** * Saves analytics data to settings and provides redirect callback. * * @since 1.0.0 */ public function ajax_save_google_analytics_data() { // Verify nonce, capability and API connection. $this->verify_ajax_nonce(); $this->verify_capability(); $this->verify_api_connection(); $options = $this->get_google_analytics_options( self::get_options() ); if ( rgar( $options, 'mode' ) === 'gmp' ) { $api_secret = $this->get_api_secret(); if ( is_wp_error( $api_secret ) ) { wp_send_json_error( $api_secret ); } $options['ga4_account']['gmp_api_secret'] = $api_secret['secretValue']; } $this->log_debug( __METHOD__ . '(): Saving Google Analytics settings.' ); $this->update_plugin_settings( $options ); // Build redirect url and return it. $redirect_url = add_query_arg( array( 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', ), admin_url( 'admin.php' ) ); wp_send_json_success( esc_url_raw( $redirect_url ) ); } /** * Gets the API secret for Measurement Protocol. * * @since 2.0.0 * * @return string Returns the API secret. */ private function get_api_secret() { $secrets = $this->api->get_api_secrets( rgpost( 'data_stream_id' ) ); if ( rgar( $secrets, 'measurementProtocolSecrets' ) ) { foreach ( $secrets['measurementProtocolSecrets'] as $secret ) { if ( $secret['displayName'] === 'GravityFormsSecret' ) { return $secret; } } } return $this->api->create_api_secret( rgpost( 'data_stream_id' ) ); } /** * Creates an array with the Google Analytics settings to be saved. * * @since 2.0.0 * * @param array $options Current options. * * @return array Returns an array with the settings set. */ public function get_google_analytics_options( $options ) { $options['auth_token'] = array( 'token' => sanitize_text_field( rgpost( 'token' ) ), 'refresh' => sanitize_text_field( rgpost( 'refresh' ) ), 'date_created' => time(), ); $options['connected'] = true; $options['mode'] = $options['tempmode']; $options['gfgamode'] = $options['tempmode']; $options['ga4_account'] = array( 'account_id' => sanitize_text_field( rgpost( 'account_id' ) ), 'account_name' => sanitize_text_field( rgpost( 'account_name' ) ), 'property_id' => sanitize_text_field( rgpost( 'property_id' ) ), 'property_name' => sanitize_text_field( rgpost( 'property_name' ) ), 'data_stream_name' => sanitize_text_field( rgpost( 'data_stream_name' ) ), 'measurement_id' => sanitize_text_field( rgpost( 'measurement_id' ) ), ); return $options; } /** * Saves analytics data to settings and provides redirect callback. * * @since 1.0.0 */ public function ajax_save_google_tag_manager_data() { // Verify nonce, capability and API connection. $this->verify_ajax_nonce(); $this->verify_capability(); $this->verify_api_connection(); $options = $this->get_tag_manager_options( self::get_options() ); // Save Google Tag Manager data. $this->log_debug( __METHOD__ . '(): Saving Google Tag Manager:' . print_r( $options, true ) ); $this->update_plugin_settings( $options ); $redirect_url = add_query_arg( array( 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', ), admin_url( 'admin.php' ) ); wp_send_json_success( esc_url_raw( $redirect_url ) ); } /** * Creates an array with Tag Manager settings to be saved. * * @since 2.0.0 * * @param array $options Current options. * * @return array Returns an array with the settings set. */ public function get_tag_manager_options( $options ) { $options['auth_token'] = array( 'token' => sanitize_text_field( rgpost( 'token' ) ), 'refresh' => sanitize_text_field( rgpost( 'refresh' ) ), 'date_created' => time(), ); $options['mode'] = 'gtm'; $options['gfgamode'] = $options['mode']; $options['connected'] = true; $options['ga4_account'] = array( 'account_id' => sanitize_text_field( rgpost( 'account_id' ) ), 'account_name' => sanitize_text_field( rgpost( 'account_name' ) ), 'property_id' => sanitize_text_field( rgpost( 'property_id' ) ), 'property_name' => sanitize_text_field( rgpost( 'property_name' ) ), 'data_stream_name' => sanitize_text_field( rgpost( 'data_stream_name' ) ), 'measurement_id' => sanitize_text_field( rgpost( 'measurement_id' ) ), 'gtm_account_id' => sanitize_text_field( rgpost( 'gtm_account_id' ) ), 'gtm_account_name' => sanitize_text_field( rgpost( 'gtm_account_name' ) ), 'gtm_container_id' => sanitize_text_field( rgpost( 'gtm_container' ) ), 'gtm_workspace_id' => sanitize_text_field( rgpost( 'gtm_workspace_id' ) ), 'gtm_workspace_name' => sanitize_text_field( rgpost( 'gtm_workspace_name' ) ), 'gtm_api_path' => sanitize_text_field( rgpost( 'gtm_api_path' ) ), ); return $options; } /** * Saves manual configuration data to settings and provides redirect callback. * * @since 2.0.0 */ public function ajax_save_manual_configuration_data() { // Verify nonce and capability. $this->verify_ajax_nonce(); $this->verify_capability(); $options = $this->get_manual_configuration_options( self::get_options() ); $this->log_debug( __METHOD__ . '(): Saving manual configuration settings:' . print_r( $options, true ) ); $this->update_plugin_settings( $options ); $redirect_url = add_query_arg( array( 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', 'updated' => 'true', ), admin_url( 'admin.php' ) ); wp_send_json_success( esc_url_raw( $redirect_url ) ); } /** * Creates an array with the manual configuration settings to be saved. * * @since 2.0.0 * * @param array $options Current options. * * @return array Returns an array with the settings set. */ public function get_manual_configuration_options( $options ) { $options['mode'] = sanitize_text_field( rgpost( 'mode' ) ); $options['connected'] = false; $options['ga4_account'] = array( 'measurement_id' => sanitize_text_field( rgpost( 'measurement_id' ) ), 'gmp_api_secret' => sanitize_text_field( rgpost( 'gmp_api_secret' ) ), 'gtm_container_id' => sanitize_text_field( rgpost( 'gtm_container' ) ), 'gtm_workspace_id' => sanitize_text_field( rgpost( 'gtm_workspace_id' ) ), ); if ( rgpost( 'ga' ) !== '' ) { $options['ga'] = sanitize_text_field( rgpost( 'ga' ) ); } if ( rgpost( 'install_gtm' ) !== '' ) { $options['install_gtm'] = sanitize_text_field( rgpost( 'install_gtm' ) ); } if( rgpost( 'ua_tracker' ) !== '' ) { $options['ua_tracker'] = sanitize_text_field( rgpost( 'ua_tracker' ) ); } return $options; } private function validate_ajax_nonce() { if ( ! wp_verify_nonce( rgar( $_REQUEST, 'nonce' ), 'gforms_google_analytics_confirmation' ) ) { $this->log_debug( __METHOD__ . '(): Nonce verification was not successful.' ); wp_send_json_error( new WP_Error( 'google_analytics_nonce_error', esc_html__( 'Nonce validation failed.', 'gravityformsgoogleanalytics' ) ) ); } } /** * When a page is redirected, check if event is already sent via Ajax. * * @since 1.0.0 */ public function ajax_get_entry_meta() { // Validate nonce. $this->validate_ajax_nonce(); // Getting paramters. $entry_id = absint( rgpost( 'entry_id' ) ); $feed_id = absint( rgpost( 'feed_id' ) ); // Checking if event for entry id and feed id has been sent. $feeds_sent = gform_get_meta( $entry_id, 'googleanalytics_feeds_sent' ); $has_sent = is_array( $feeds_sent ) && in_array( $feed_id, $feeds_sent ); wp_send_json_success( array( 'event_sent' => $has_sent ) ); } /** * When a page is redirected, save entry meta to avoid duplicate events. * * @since 1.0.0 */ public function ajax_save_entry_meta() { // Validate nonce. $this->validate_ajax_nonce(); // Getting paramters. $entry_id = absint( rgpost( 'entry_id' ) ); $feed_id = absint( rgpost( 'feed_id' ) ); // Adding current feed to meta and saving. $feeds_sent = gform_get_meta( $entry_id, 'googleanalytics_feeds_sent' ); if ( ! is_array( $feeds_sent ) ) { $feeds_sent = array(); } $feeds_sent[] = $feed_id; gform_update_meta( $entry_id, 'googleanalytics_feeds_sent', $feeds_sent ); wp_send_json_success( array( 'meta_saved' => true ) ); } /** * Verifies nonce and sends a JSON error if unsuccessful. * * @since 2.0.0 * * @param string $nonce_action The nonce action to be verified. * * @return void */ private function verify_ajax_nonce( $nonce_action = 'connect_google_analytics' ) { if ( ! wp_verify_nonce( rgpost( 'nonce' ), $nonce_action ) ) { $this->log_debug( __METHOD__ . '(): Nonce validation failed.' ); wp_send_json_error( new WP_Error( 'google_analytics_ga_error', wp_strip_all_tags( __( 'Nonce validation has failed.', 'gravityformsgoogleanalytics' ) ) ) ); } } /** * Verifies Gravity Forms capabilities and sends a JSON error if user doesn't have required permission. * * @since 2.0.0 * * @return void */ private function verify_capability() { if ( ! $this->current_user_can_any( $this->_capabilities_form_settings ) ) { $this->log_debug( __METHOD__ . '(): Permissions for form settings not met.' ); wp_send_json_error( new WP_Error( 'google_analytics_ga_error', wp_strip_all_tags( __( 'User does not have required permissions to setup Google Analytics.', 'gravityformsgoogleanalytics' ) ) ) ); } } /** * Attempts to initialize Google Analytics API and sends a JSON error if unsuccessful. * * @since 2.0.0 * * @return void */ private function verify_api_connection() { if ( ! $this->initialize_api() ) { $this->log_debug( __METHOD__ . '(): Unable to initialize Google Anaylics API.' ); wp_send_json_error( new WP_Error( 'google_analytics_ga_error', wp_strip_all_tags( __( 'Unable to initialize Google Anaylics API.', 'gravityformsgoogleanalytics' ) ) ) ); } } /** * Append event data to the URL. * * @since 2.0.0 * * @param string|array $confirmation Confirmation for the form, can be a page redirect. * @param object $form Form object. * @param object $entry Current Entry. * @param bool $ajax Whether the form was subitted via ajax. * * @return string|array $confirmation */ public function force_text_confirmation( $confirmation, $form, $entry, $ajax ) { // Ignore blank or spam entries if ( empty( $entry ) || rgar( $entry, 'status' ) === 'spam' ) { return $confirmation; } // Ignore confirmations that are not of type redirect and submissions not associated with a GA feed. if ( ! isset( $confirmation['redirect'] ) || ! $this->has_feed( $form['id'] ) ) { return $confirmation; } // Ignore submissions associated with Measurement Protocol. Those will be handled server side and will go throught the standard flow. if ( self::get_options( '', 'mode' ) === 'gmp' ) { return $confirmation; } // Update confirmation to handle redirect. $processing_message = esc_html__( 'Processing... Please wait.', 'gravityformsgoogleanalytics' ); return "
{$processing_message}
" . ''; } /** * Remove unneeded settings. * * @since 1.0.0 */ public function uninstall() { parent::uninstall(); // Delete backed up feeds as well. global $wpdb; $sql = $wpdb->prepare( "DELETE FROM {$wpdb->prefix}gf_addon_feed WHERE addon_slug=%s", 'gravityformsgoogleanalytics_ua_ga4_upgrade' ); $wpdb->query( $sql ); delete_option( 'gravityformsaddon_gravityformsgoogleanalytics_settings' ); delete_option( 'gforms_google_analytics_goal_labels' ); GFCache::delete( 'google_analytics_plugin_settings' ); GFCache::delete( 'all_ga4_feeds_upgraded' ); } /** * Configures the settings for a connected option. * * Allows for disconnecting services and shows current status. * * @since 1.0.0 */ public function settings_connection_method() { $options = self::get_options(); if ( ! isset( $options['mode'] ) ) { $options['mode'] = 'unset'; } $disconnect_uri = esc_url_raw( add_query_arg( array( 'page' => 'gf_settings', 'subview' => 'gravityformsgoogleanalytics', 'action' => 'gfgadisconnect', 'nonce' => wp_create_nonce( 'gforms_google_analytics_disconnect' ), ), admin_url( 'admin.php' ) ) ); $disconnect_link = sprintf( '%s ', esc_url_raw( $disconnect_uri ), esc_html__( 'Disconnect', 'gravityformsgoogleanalytics' ) ); if ( rgar( $options, 'is_manual' ) ) { echo esc_html__( 'Manual Configuration', 'gravityformsgoogleanalytics' ) . ' | ' . wp_kses_post( $disconnect_link ); } elseif ( 'ga' === $options['mode'] ) { echo esc_html__( 'Google Analytics', 'gravityformsgoogleanalytics' ) . ' | ' . wp_kses_post( $disconnect_link ); } elseif ( 'gtm' === $options['mode'] ) { echo esc_html__( 'Google Tag Manager', 'gravityformsgoogleanalytics' ) . ' | ' . wp_kses_post( $disconnect_link ); } elseif ( 'gmp' === $options['mode'] ) { echo esc_html__( 'Google Measurement Protocol', 'gravityformsgoogleanalytics' ) . ' | ' . wp_kses_post( $disconnect_link ); } else { echo esc_html__( 'Not connected', 'gravityformsgoogleanalytics' ); } } /** * Outputs the configured analitics account and data stream. * * @since 2.0.0 */ public function settings_analytics_account() { $options = self::get_options( 'ga4_account' ); if ( ! empty( $options['account_name'] ) ) { echo esc_html( $options['account_name'] ) . ' / ' . esc_html( $options['property_name'] ) . ' / ' . esc_html( $options['data_stream_name'] ); } } /** * Outputs the configured tag manager account information. * * @since 2.0.0 */ public function settings_tag_manager_account() { $options = self::get_options( 'ga4_account' ); if ( ! empty( $options['gtm_account_name'] ) ) { echo esc_html( $options['gtm_account_name'] ) . ' / ' . esc_html( $options['gtm_container_id'] ) . ' / ' . esc_html( $options['gtm_workspace_name'] ); } } /** * Outputs the configured tag manager create assets setting. * * @since 2.0.0 */ public function settings_tag_manager_create_assets() { $options = self::get_options( 'ga4_account' ); echo rgar( $options, 'gtm_create_assets' ) ? 'Yes' : 'No'; } /** * Outputs the configured measurement id * * @since 2.0.0 */ public function settings_measurement_id() { $options = self::get_options( 'ga4_account' ); if ( isset( $options['measurement_id'] ) ) { echo esc_html( $options['measurement_id'] ); } else { esc_html_e( 'Measurement ID not specified.', 'gravityformsgoogleanalytics' ); } } /** * Adds the inline script containing the gforms_google_analytics_frontend_strings variable. * * @since 2.1 * * @return void */ public function frontend_script_strings_callback() { static $done; if ( $done ) { return; } $strings = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'gforms_google_analytics_confirmation' ), 'logging_enabled' => $this->is_logging_enabled(), 'logging_nonce' => wp_create_nonce( 'log_google_analytics_event_sent' ), 'ua_tracker' => self::get_options( '', 'ua_tracker' ), ); if ( function_exists( 'wp_add_inline_script' ) ) { wp_add_inline_script( self::FE_SCRIPT_HANDLE, sprintf( 'var %s_strings = %s;', self::FE_SCRIPT_HANDLE, json_encode( $strings ) ), 'before' ); } else { wp_localize_script( self::FE_SCRIPT_HANDLE, self::FE_SCRIPT_HANDLE . '_strings', $strings ); } $done = true; } /** * Check if the form has an active Google Analytics feed and mode is valid. * * @since 1.0.0 * * @param array $form The form currently being processed. * * @return bool If the script should be enqueued. */ public function frontend_script_callback( $form ) { if ( is_admin() ) { return false; } $settings = self::get_options(); // Check for mode setting. if ( ! $this->is_frontend_scripts_mode_valid( $settings ) ) { return false; } // Load on a redirected page. Skip if measurement protocol is selected. $redirect_action = rgget( 'gfaction' ); if ( $redirect_action ) { return true; } if ( ! $this->has_feed( $form['id'] ) && ! rgars( $form, 'gravityformsgoogleanalytics/google_analytics_pagination' ) ) { return false; } return true; } /** * Determines if the mode is valid for frontend scripts * * @param array $settings The current plugin settings. * * @since 1.0.0 * * @return bool false if there is no gfgamode or it is gmp, true otherwise. */ public function is_frontend_scripts_mode_valid( $settings ) { $mode = rgar( $settings, 'mode' ); if ( empty( $mode ) || $mode === 'gmp' ) { return false; } return true; } /** * Get the tokens from the auth payload, or settings if appropriate * * @since 1.0.0 * * @param array $auth_payload the auth payload returned form Google. * @param array $settings an array of plugin settings. * * @return bool|array false if there is no auth payload or settings array, otherwise an array of auth tokens. */ public function maybe_get_tokens_from_auth_payload( $auth_payload, $settings ) { if ( empty( $auth_payload ) && ! rgar( $settings, 'auth_token' ) ) { return false; } $auth_tokens = array(); if ( $auth_payload ) { $auth_tokens['token'] = rgar( $auth_payload, 'access_token' ); $auth_tokens['refresh'] = rgar( $auth_payload, 'refresh_token' ); } else { $auth_tokens['token'] = rgar( $settings['auth_token'], 'token' ); $auth_tokens['refresh'] = rgar( $settings['auth_token'], 'refresh' ); } return $auth_tokens; } /** * Displays a Google Analytics Account box. * * @since 1.0.0 */ public function settings_ga_select_account() { if ( ! $this->initialize_api() ) { $this->log_debug( __METHOD__ . '(): Unable to initialize Google Analytics API.' ); return false; } if ( ! $this->current_user_can_any( $this->_capabilities_form_settings ) ) { $this->log_debug( __METHOD__ . '(): User does not have Form Settings capability.' ); return false; } $auth_payload = $this->plugin_settings->get_auth_payload(); $auth_tokens = $this->maybe_get_tokens_from_auth_payload( $auth_payload, self::get_options() ); if ( ! $auth_tokens ) { $this->log_debug( __METHOD__ . '(): Unable to retrieve API Token.' ); return false; } $response = $this->api->get_ga4_accounts(); if ( is_wp_error( $response ) ) { $this->log_debug( __METHOD__ . '(): Could not retrieve Google Analytics accounts' ); return esc_html_e( 'It appears that you do not have a Google Analytics account for the account you selected.', 'gravityformsgoogleanalytics' ); } if ( ! isset( $response['accountSummaries'] ) || empty( $response['accountSummaries'] ) ) { $this->connect_error = true; $gravity_url = admin_url( 'admin.php?page=gf_settings&subview=gravityformsgoogleanalytics' ); ?>

', esc_attr( rgar( $auth_tokens, 'token' ) ) ); echo sprintf( '', esc_attr( rgar( $auth_tokens, 'refresh' ) ) ); echo '', esc_attr( $token ) ); echo sprintf( '', esc_attr( $refresh ) ); echo '', esc_attr( wp_create_nonce( 'connect_google_analytics' ) ) ); } /** * Sets an action variable for Google Analytics. * * @since 1.0.0 */ public function settings_ga_action() { echo ''; } /** * Sets an action variable for Google Tag Manager. * * @since 1.0.0 */ public function settings_gtm_action() { echo ''; } /** * Sets an action variable for manual configuration. * * @since 1.0.0 */ public function settings_manual_action() { echo ''; } /** * Update auth tokens. * * @since 1.0.0 */ public function plugin_settings_page() { $this->plugin_settings->maybe_update_auth_tokens(); $this->plugin_settings->maybe_display_settings_updated(); parent::plugin_settings_page(); } /** * Configures the settings which should be rendered on the add-on settings tab. * * @since 1.0.0 */ public function plugin_settings_fields() { return $this->plugin_settings->get_fields(); } /** * Get the Google Analytics UA Code * * @since 1.0.0 * * @return string/bool Returns string UA code, false otherwise */ private function get_measurement_id() { return self::get_options( 'ga4_account', 'measurement_id' ); } /** * Call form_settings from the form settings class. * * @since 1.0.0 * * @param array $form The current form. */ public function form_settings( $form ) { return $this->form_settings->form_settings_page( $form ); } /** * Call form_settings_fields from the form settings class. * * @since 1.0.0 * * @param array $form The current form. */ public function form_settings_fields( $form ) { return $this->form_settings->pagination_form_settings( $form ); } /** * Call feed_settings_fields from the form settings class. * * @since 1.0.0 */ public function feed_settings_fields() { return $this->form_settings->get_feed_settings_fields(); } /** * Call feed_list_columns from the form settings class. * * @since 1.0.0 */ public function feed_list_columns() { return $this->form_settings->feed_list_columns(); } /** * Set feed creation control. * * @since 1.0 * * @return bool */ public function can_create_feed() { $options = self::get_options(); if ( rgar( $options, 'is_manual' ) === true ) { return true; } if ( $this->initialize_api() ) { return true; } return false; } /** * Processes the feed. * * @since 1.0.0 * * @param array $feed The feed to process. * @param array $entry The entry to process. * @param array $form The form the feed is coming from. */ public function process_feed( $feed, $entry, $form ) { $mode = self::get_options( '', 'mode' ); $parameters = $this->get_mapped_parameters( $feed, 'submission_parameters', $form, $entry ); $is_ajax = isset( $_REQUEST['gform_ajax'] ) ? true : false; $event_name = $this->get_submission_event_name( $mode, $feed, $entry, $form ); if ( defined( 'REST_REQUEST' ) && REST_REQUEST && $mode !== 'gmp' ) { $this->add_event_to_rest_response( $entry, $feed, $form, $mode, $parameters, $is_ajax, $event_name ); return $entry; } switch ( $mode ) { case 'gmp': // Send events via server side (measurement protocol) $ua_codes = $this->get_ga_codes( $form, $entry, $feed ); $this->send_measurement_protocol( $ua_codes, $parameters, $event_name, $entry['source_url'] ); break; case 'ga': $this->log_debug( __METHOD__ . '(): Attempting to send event via Google Analytics. Event Name: ' . $event_name . '. Page URL: ' . $entry['source_url'] . '. Parameters: ' . print_r( $parameters, true ) ); $this->render_script_feed_counter(); $this->render_script_send_unique_to_ga( $entry['id'], $feed['id'], $parameters, $event_name, $is_ajax ); break; case 'gtm': $trigger_name = rgar( $feed['meta'], 'submission_trigger' ) !== 'gf_custom' ? rgar( $feed['meta'], 'submission_trigger' ) : rgar( $feed['meta'], 'submission_trigger_custom' ); $this->log_debug( __METHOD__ . '(): Attempting to send event via Google Tag Manager. Trigger Name: ' . $trigger_name . '. Page URL: ' . $entry['source_url'] . '. Parameters: ' . print_r( $parameters, true ) ); $this->render_script_feed_counter(); $this->render_script_send_unique_to_gtm( $entry['id'], $feed['id'], $parameters, $trigger_name, $is_ajax ); break; } return $entry; } /** * Adds the event data to a REST response object in lieu of echoing it in the response. * * @since 2.1.0 * * @param $entry * @param $feed * @param $form * @param $mode * @param $parameters * @param $is_ajax * @param $event_name * * @return void */ protected function add_event_to_rest_response( $entry, $feed, $form, $mode, $parameters, $is_ajax, $event_name ) { $details = array(); switch ( $mode ) { case 'ga': $details = array( 'entry_id' => $entry['id'], 'feed_id' => $feed['id'], 'parameters' => $parameters, 'event_name' => $event_name, ); break; case 'gtm': $trigger_name = rgar( $feed['meta'], 'submission_trigger' ) !== 'gf_custom' ? rgar( $feed['meta'], 'submission_trigger' ) : rgar( $feed['meta'], 'submission_trigger_custom' ); $details = array( 'entry_id' => $entry['id'], 'feed_id' => $feed['id'], 'parameters' => $parameters, 'trigger_name' => $trigger_name, ); break; } add_filter( 'rest_request_after_callbacks', function ( $response ) use ( $details ) { $response->data['ga_event'] = $details; return $response; } ); } /*** * Determines the name of the event to be sent to Google Analytics or Measurement Protocol. * * @since 2.0.0 * * @param string $mode Google analytics connection mode. "ga" for Analytics, "gtm" for Tag Manager, "gmp" for Management Protocol. * @param array $feed Current feed object. * @param array $entry Current entry object. * @param array $form Current form object. * * @return string Returns the event name that will be sent to track the event. */ private function get_submission_event_name( $mode, $feed, $entry, $form ) { /*** * Filters the name of the submission event to be sent to Google Analytics. This filter only applies to Google Analytics or Measurement Protocol (not Tag Manger) * * @since 2.0.0 * * @param string $event_name Event name being filtered. * @param string $mode Google analytics connection mode. "ga" for Analytics, "gtm" for Tag Manager, "gmp" for Management Protocol. * @param array $feed Current feed object. * @param array $entry Current entry object. * @param array $form Current form object. */ return apply_filters( 'gform_googleanalytics_submission_event_name', 'gforms_submission', $mode, $feed, $entry, $form ); } /*** * Determines the name of the pagination event to be sent to Google Analytics or Measurement Protocol. * * @since 2.0.0 * * @param string $mode Google analytics connection mode. "ga" for Analytics and "gmp" for Measurement Protocol. * @param array $form Current form object. * * @return string Returns the event name. */ private function get_pagination_event_name( $mode, $form ) { /*** * Filters the name of the pagination event to be sent to Google Analytics. This filter only applies to Google Analytics or Measurement Protocol (not Tag Manger) * * @since 2.0.0 * * @param string $event_name Event name being filtered. * @param string $mode Google analytics connection mode. "ga" for Analytics, "gtm" for Tag Manager, "gmp" for Management Protocol. * @param array $form Current form object. */ return apply_filters( 'gform_googleanalytics_pagination_event_name', 'gforms_pagination', $mode, $form ); } /** * Renders the script to send an event to Google Analytics, making sure the event is not sent if it has been sent before. * * @since 2.0 * * @param int $entry_id The current entry id. * @param int $feed_id The current feed id. * @param array $parameters An array of event parameters to be sent. * @param string $event_name The event name to be recorded in Google Analytics. * @param bool $is_ajax Wether or not the form submission was done with AJAX enabled. * */ private function render_script_send_unique_to_ga( $entry_id, $feed_id, $parameters, $event_name, $is_ajax ) { $body = "window.parent.GF_Google_Analytics.send_unique_to_ga( '" . esc_js( $entry_id ) . "', '" . esc_js( $feed_id ) . "', " . wp_json_encode( $parameters ) . ", '" . esc_js( $event_name ) . "' );"; $this->render_script_wrapper( $body, $is_ajax ); } /** * Renders the script to send an event to Google Tag Manager, making sure the event is not sent if it has been sent before. * * @since 2.0 * * @param int $entry_id The current entry id. * @param int $feed_id The current feed id. * @param array $parameters An array of event parameters to be sent. * @param string $trigger_name The trigger name to be associated with this event in Tag Manager. * @param bool $is_ajax Wether or not the form submission was done with AJAX enabled. * */ private function render_script_send_unique_to_gtm( $entry_id, $feed_id, $parameters, $trigger_name, $is_ajax ) { $body = "window.parent.GF_Google_Analytics.send_unique_to_gtm( '" . esc_js( $entry_id ) . "', '" . esc_js( $feed_id ) . "', " . wp_json_encode( $parameters ) . ", '" . esc_js( $trigger_name ) . "' );"; $this->render_script_wrapper( $body, $is_ajax ); } /** * Renders the script to send an event to Google Analytics. * * @since 2.0 * * @param array $parameters An array of event parameters to be sent. * @param string $event_name The event name to be recorded in Google Analytics. * @param bool $is_ajax Wether or not the form submission was done with AJAX enabled. * */ private function render_script_send_to_ga( $parameters, $event_name, $is_ajax ) { $body = 'window.parent.GF_Google_Analytics.send_to_ga( ' . wp_json_encode( $parameters ) . ', "' . esc_js( $event_name ) . '" );'; $this->render_script_wrapper( $body, $is_ajax ); } /** * Renders the script to send an event to Google Tag Manager. * * @since 2.0 * * @param array $parameters An array of event parameters to be sent. * @param string $trigger_name The trigger name to be associated with this event in Tag Manager. * @param bool $is_ajax Wether or not the form submission was done with AJAX enabled. * */ private function render_script_send_to_gtm( $parameters, $trigger_name, $is_ajax ) { $body = 'window.parent.GF_Google_Analytics.send_to_gtm( ' . wp_json_encode( $parameters ) . ', "' . esc_js( $trigger_name ) . '" );'; $this->render_script_wrapper( $body, $is_ajax ); } /** * Renders the wrapper script for the specified $script_body script, taking AJAX into account. * * @since 2.0 * * @param string $script_body The script body to be wrapped. * @param bool $is_ajax Wether or not the form submission was done with AJAX enabled. */ private function render_script_wrapper( $script_body, $is_ajax ) { if ( rgpost( 'gform_submission_method' ) === 'ajax' ) { return; } ?> init( $api_secret, $event_name ); // Set document variables. $event->set_document_path( str_replace( home_url(), '', $page_url ) ); $event->set_document_location( esc_url( $page_url ) ); $event_url_parsed = wp_parse_url( home_url() ); $event->set_document_host( $event_url_parsed['host'] ); // Set IP address $event->set_user_ip_address( \GFFormsModel::get_ip() ); // Set document title. global $post; $document_title = isset( $post ) && isset( $post->post_title ) ? sanitize_text_field( $post->post_title ) : esc_html__( 'No title found', 'gravityformsgoogleanalytics' ); $event->set_document_title( $document_title ); // Set submission parameters. $event->set_params( $parameters ); // Begin sending events using the measurement protocol. foreach ( $ga_codes as $code ) { $response = $event->send( $code ); if ( is_wp_error( $response ) ) { $this->log_debug( __METHOD__ . '(): Failed to send event to Google Analytics via Measurement Protocol. Secret (last 4): XXXXXX' . substr( $api_secret, - 4 ) . '. GA code: ' . $code . '. Event Name: ' . $event_name . '. Page URL: ' . $page_url . '. Parameters: ' . print_r( $event->get_params(), true ) ); } else { $this->log_debug( __METHOD__ . '(): Successfully sent event to Google Analytics via Measurement Protocol. Secret (last 4): XXXXXX' . substr( $api_secret, - 4 ) . '. GA code: ' . $code . '. Event Name: ' . $event_name . '. Page URL: ' . $page_url . '. Parameters: ' . print_r( $event->get_params(), true ) ); } } } /** * Retrieves the value for the feed item. * * @since 1.0.0 * * @param mixed $feed The Feed item. * @param mixed $column The Feed column name. * * @return string The column value. */ public function get_column_value( $feed, $column ) { $value = ''; if ( empty( $value ) ) { if ( isset( $feed[ $column ] ) ) { $value = $feed[ $column ]; } elseif ( isset( $feed['meta'][ $column ] ) ) { $value = $feed['meta'][ $column ]; } } return $value; } /** * Get the menu icon for this plugin. * * @since 1.0 * * @return string the class for the plugin menu icon. */ public function get_menu_icon() { return $this->is_gravityforms_supported( '2.5-beta-4' ) ? 'gform-icon--analytics' : 'dashicons-admin-generic'; } /** * Log if an event is successfully sent. * * @since 2.1.0 */ public function ajax_log_ga_event_sent() { $this->verify_ajax_nonce( 'log_google_analytics_event_sent' ); $connection = rgpost( 'connection' ); $parameters = rgpost( 'parameters' ); if ( $connection === 'ga' ) { $event_name = rgpost( 'eventName' ); $this->log_debug( __METHOD__ . '(): Completed sending event via Google Analytics. Event Name: ' . $event_name . '. Parameters: ' . print_r( $parameters, true ) ); } elseif ( $connection = 'gtm' ) { $trigger_name = rgpost( 'triggerName' ); $this->log_debug( __METHOD__ . '(): Completed sending event via Google Tag Manager. Trigger Name: ' . $trigger_name . '. Parameters: ' . print_r( $parameters, true ) ); } } /** * Adds the feed data to the submission results allowing the frontend to send * the Google Analytics event. * * @since 2.3.1 * * @param array $submissionResults The submission results from the ajax submission. * @return array The submission results with the feed data added. */ public function set_post_ajax_submission_vars( $submissionResults ) { if ( ! rgar( $submissionResults, 'is_valid' ) || rgar( $submissionResults, 'is_spam' ) ) { return $submissionResults; } $formId = rgars( $submissionResults, 'form/id' ); $form = rgar( $submissionResults, 'form' ); if ( ! rgar( $submissionResults, 'entry_id' ) ) { // Pagination tracking not enabled. Abort. if ( ! rgars( $form, 'gravityformsgoogleanalytics/google_analytics_pagination' ) ) { return $submissionResults; } $mode = self::get_options( '', 'mode' ); $source_page_number = rgar( $submissionResults, 'source_page_number' ); $current_page_number = rgar( $submissionResults, 'page_number' ); $parameters = $this->get_mapped_parameters( $form['gravityformsgoogleanalytics'], 'pagination_parameters', $form, \GFFormsModel::get_current_lead( $form ) ); $parameters = $this->maybe_replace_pagination_merge_tags( $parameters, $source_page_number, $current_page_number ); $event_name = $this->get_pagination_event_name( $mode, $form ); $submissionResults['ajax_pagination'] = [ 'form_id' => (string) $formId, 'parameters' => $parameters, 'eventName' => $event_name, 'mode' => $mode, ]; $submission_type = rgar( $submissionResults, 'submission_type' ); $submission_method = rgpost( 'gform_submission_method' ); if ( $mode === 'gmp' && 'ajax' === $submission_method && ( $submission_type === 'next' || $submission_type === 'previous' ) ) { $ua_codes = $this->get_ga_codes( $form ); $this->send_measurement_protocol( $ua_codes, $parameters, $event_name, rgpost( 'current_page_url' ) ); } return $submissionResults; } $feeds = $this->get_feeds( $formId ); if ( empty( $feeds ) ) { return $submissionResults; } $entry = GFAPI::get_entry( rgar( $submissionResults, 'entry_id' ) ); if ( is_wp_error( $entry ) ) { $this->log_debug( __METHOD__ . '(): Unable to retrieve entry. Entry ID: ' . rgar( $submissionResults, 'entry_id' ) ); return $submissionResults; } $mode = self::get_options( '', 'mode' ); foreach ( $feeds as $feed ) { if ( ! rgar( $feed, 'is_active' ) || ! $this->is_feed_condition_met( $feed, $form, $entry ) ) { continue; } $parameters = $this->get_mapped_parameters( $feed, 'submission_parameters', $form, $entry ); $submissionResults['ajax_feeds'][] = [ 'form_id' => (string) $formId, 'feed_id' => rgar( $feed, 'id' ), 'parameters' => $parameters, 'eventName' => $this->get_submission_event_name( 'ga', $feed, $entry, $form ), 'mode' => $mode, ]; } return $submissionResults; } }