Files
hub-insurance/wp-content/plugins/gravityformsgoogleanalytics/class-gf-google-analytics.php
T
2026-07-02 15:54:39 -06:00

2577 lines
80 KiB
PHP

<?php
namespace Gravity_Forms\Gravity_Forms_Google_Analytics;
defined( 'ABSPATH' ) || die();
use GFForms;
use GFFeedAddOn;
use GFCommon;
use GFAPI;
use GFFormsModel;
use GFCache;
use Gravity_Forms\Gravity_Forms_Google_Analytics\Settings;
use WP_Error;
// Include the Gravity Forms Feed Add-On Framework.
GFForms::include_feed_addon_framework();
/**
* Gravity Forms Google Analytics Add-On.
*
* @since 1.0.0
* @package GravityForms
* @author Rocketgenius
* @copyright Copyright (c) 2019, Rocketgenius
*/
class GF_Google_Analytics extends GFFeedAddOn {
/**
* Contains an instance of this class, if available.
*
* @since 1.0
* @var GF_Google_Analytics $_instance If available, contains an instance of this class
*/
private static $_instance = null;
/**
* Defines the version of the Gravity Forms Google Analytics Add-On.
*
* @since 1.0
* @var string $_version Contains the version.
*/
protected $_version = GF_GOOGLE_ANALYTICS_VERSION;
/**
* Defines the minimum Gravity Forms version required.
*
* @since 1.0
* @var string $_min_gravityforms_version The minimum version required.
*/
protected $_min_gravityforms_version = GF_GOOGLE_ANALYTICS_MIN_GF_VERSION;
/**
* Defines the plugin slug.
*
* @since 1.0
* @var string $_slug The slug used for this plugin.
*/
protected $_slug = 'gravityformsgoogleanalytics';
/**
* Defines the main plugin file.
*
* @since 1.0
* @var string $_path The path to the main plugin file, relative to the plugins folder.
*/
protected $_path = 'gravityformsgoogleanalytics/googleanalytics.php';
/**
* Defines the full path to this class file.
*
* @since 1.0
* @var string $_full_path The full path.
*/
protected $_full_path = __FILE__;
/**
* Wrapper class for plugin settings.
*
* @since 1.0
* @var Settings\Plugin_Settings
*/
private $plugin_settings;
/**
* Wrapper class for form settings.
*
* @since 1.0
* @var Settings\Form_Settings
*/
private $form_settings;
/**
* Defines the URL where this add-on can be found.
*
* @since 1.0
* @var string The URL of the Add-On.
*/
protected $_url = 'https://gravityforms.com';
/**
* Defines the title of this add-on.
*
* @since 1.0
* @var string $_title The title of the add-on.
*/
protected $_title = 'Gravity Forms Google Analytics Add-On';
/**
* Defines the short title of the add-on.
*
* @since 1.0
* @var string $_short_title The short title.
*/
protected $_short_title = 'Google Analytics';
/**
* Defines if Add-On should use Gravity Forms servers for update data.
*
* @since 1.0.0
* @access protected
* @var bool
*/
protected $_enable_rg_autoupgrade = true;
/**
* Defines the capabilities needed for the Google Analytics Add-On
*
* @since 1.0.0
* @access protected
* @var array $_capabilities The capabilities needed for the Add-On
*/
protected $_capabilities = array( 'gravityforms_googleanalytics', 'gravityforms_googleanalytics_uninstall' );
/**
* Defines the capability needed to access the Add-On settings page.
*
* @since 1.0.0
* @access protected
* @var string $_capabilities_settings_page The capability needed to access the Add-On settings page.
*/
protected $_capabilities_settings_page = 'gravityforms_googleanalytics';
/**
* Defines the capability needed to access the Add-On form settings page.
*
* @since 1.0.0
* @access protected
* @var string $_capabilities_form_settings The capability needed to access the Add-On form settings page.
*/
protected $_capabilities_form_settings = 'gravityforms_googleanalytics';
/**
* Defines the capability needed to uninstall the Add-On.
*
* @since 1.0.0
* @access protected
* @var string $_capabilities_uninstall The capability needed to uninstall the Add-On.
*/
protected $_capabilities_uninstall = 'gravityforms_googleanalytics_uninstall';
/**
* Stores the plugin's options
*
* @since 1.0.0
* @var array $options
*/
private static $options = false;
/**
* Defines the category if the page is redirected.
*
* @since 1.0.0
*
* @var string Event category for the feed.
*/
private $conversion_category = '';
/**
* Defines the label if the page is redirected.
*
* @since 1.0.0
*
* @var string Event label for the feed.
*/
private $conversion_label = '';
/**
* Defines the action if the page is redirected.
*
* @since 1.0.0
*
* @var string Event action for the feed.
*/
private $conversion_action = '';
/**
* Saves an API instance for Google Authorization.
*
* @since 1.0.0
*
* @var GF_Google_Analytics_API null into object is set.
*/
protected $api = null;
/**
* Sets whether an account has a connection error (i.e., GA or GTM are not installed for an account).
*
* @since 1.0.0
*
* @var bool True if error, false if not.
*/
private $connect_error = false;
/**
* Version of this add-on which requires reauthentication with the API.
*
* Anytime updates are made to this class that requires a site to reauthenticate Gravity Forms with Google Analytics, this
* constant should be updated to the value of GFForms::$version.
*
* @since 2.0
*
* @see GFForms::$version
*/
const LAST_REAUTHENTICATION_VERSION = '2.0';
/**
* The handle used to register and enqueue the frontend scripts.
*
* @since 2.1
*
* @var string
*/
const FE_SCRIPT_HANDLE = 'gforms_google_analytics_frontend';
/**
* Get an instance of this class.
*
* @since 1.0.0
*
* @return GF_Google_Analytics
*/
public static function get_instance() {
if ( null === self::$_instance ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Run add-on pre-initialization processes.
*
* @since 1.0
*/
public function pre_init() {
require_once plugin_dir_path( __FILE__ ) . '/includes/settings/class-plugin-settings.php';
require_once plugin_dir_path( __FILE__ ) . '/includes/settings/class-form-settings.php';
$this->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 <a> tag, 3: close </a> 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,
'<a href="' . esc_attr( $this->get_plugin_settings_url() ) . '">',
'</a>'
)
?>
<div class="gf-notice notice notice-error">
<p><?php echo wp_kses( $message, array( 'a' => array( 'href' => true ) ) ); ?></p>
</div>
<?php
}
/**
* Check whether this add-on needs to be reauthenticated with Google Analytics.
*
* @since 2.0
*
* @return bool
*/
public function requires_api_reauthentication() {
$settings = self::get_options();
// don't show the reauth notice if the add-on is not connected.
if ( ! rgar( $settings, 'mode' ) ) {
return false;
}
return ! empty( $settings ) && version_compare( rgar( $settings, 'reauth_version' ), self::LAST_REAUTHENTICATION_VERSION, '<' );
}
/**
* Determines if logging is enabled for Google Analytics.
*
* @since 2.0.0
*
* @return bool Returns true if logging is enabled. Returns false otherwise
*/
private function is_logging_enabled() {
// Query string override to enable console logging if needed.
if ( rgget( 'gfga_logging' ) == 1 ) {
return true;
}
// Main logging setting is turned off. Do not log.
if ( ! class_exists( 'GFLogging' ) || ! get_option( 'gform_enable_logging' ) ) {
return false;
}
// Get logging setting for plugin.
$plugin_setting = \GFLogging::get_instance()->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' ) ) ) {
?>
<script>
setTimeout( function() {
window.location.href = '<?php echo esc_url_raw( $redirect_url ); ?>';
}, 1 );
</script>
<?php
}
}
/**
* Installs GTAG Google Analytics if user has selected that option in settings.
*
* @since 1.0.0
*/
public function maybe_install_analytics() {
$settings = self::get_options();
if ( ! isset( $settings['ga'] ) ) {
return;
}
if ( 'off' === $settings['ga'] ) {
return;
}
$this->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";
?>
<!-- Google tag (gtag.js) -->
<script async
src="https://www.googletagmanager.com/gtag/js?id=<?php echo esc_html( $measurement_id ); ?>"></script> <?php //phpcs:ignore ?>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push( arguments );
}
gtag( 'js', new Date() );
gtag( 'config', '<?php echo esc_html( $measurement_id ); ?>' );
<?php
/**
* Action: gform_googleanalytics_install_analytics
*
* Allow custom scripting for Google Analytics GTAG
*
* @since 1.0.0
*
* @param string $gmeasurement_id Google Analytics Property ID
*/
do_action( 'gform_googleanalytics_install_analytics', $measurement_id );
?>
</script>
<?php
}
/**
* Installs Google Tag Manager if user has selected that option in settings.
*
* @since 1.0.0
*/
public function maybe_install_tag_manager_header() {
$settings = self::get_options();
if ( ! isset( $settings['install_gtm'] ) ) {
return;
}
if ( 'off' === $settings['install_gtm'] ) {
return;
}
$this->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";
?>
<!-- Google Tag Manager -->
<script>( function( w, d, s, l, i ) {
w[ l ] = w[ l ] || [];
w[ l ].push( {
'gtm.start':
new Date().getTime(), event: 'gtm.js'
} );
var f = d.getElementsByTagName( s )[ 0 ],
j = d.createElement( s ), dl = l != 'dataLayer' ? '&l=' + l : '';
j.async = true;
j.src =
'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
f.parentNode.insertBefore( j, f );
} )( window, document, 'script', 'dataLayer', '<?php echo esc_html( $gtm_code ); ?>' );
<?php
/**
* Action: gform_googleanalytics_install_tag_manager
*
* Allow custom scripting for Google Tag Manager
*
* @since 1.0.0
*
* @param string $gtm_code Google Tag Manager ID
*/
do_action( 'gform_googleanalytics_install_tag_manager', $gtm_code );
?>
</script>
<!-- End Google Tag Manager -->
<?php
}
/**
* Installs Google Tag Manager if user has selected that option in settings.
*
* @since 1.0.0
*/
public function maybe_install_tag_manager_body() {
$settings = self::get_options();
if ( ! isset( $settings['install_gtm'] ) ) {
return;
}
if ( 'off' === $settings['install_gtm'] ) {
return;
}
$gtm_code = sanitize_text_field( self::get_options( 'account', 'container_id' ) );
if ( empty( $gtm_code ) ) {
return;
}
// User has requested Tag Manager installation. Proceed.
?>
<!-- Google Tag Manager (noscript) -->
<noscript>
<iframe src="https://www.googletagmanager.com/ns.html?id=<?php echo esc_html( $gtm_code ); ?>" height="0"
width="0" style="display:none;visibility:hidden"></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
<?php
/**
* Action: gform_googleanalytics_install_tag_manager
*
* Allow custom scripting for Google Tag Manager
*
* @since 1.0.0
*
* @param string $gtm_code Google Tag Manager ID
*/
do_action( 'gform_googleanalytics_install_tag_manager', $gtm_code );
}
/**
* Redirects to authentication screen for Google Analytics.
*
* @since 1.0.0
*/
public function authenticate_google_analytics() {
$ga_options = get_option( 'gforms_google_analytics_ga' );
$token = isset( $ga_options['token'] ) ? $ga_options['token'] : false;
if ( $token && ( isset( $ga_options['mode'] ) && 'ga' === $ga_options['mode'] ) ) {
return;
}
$settings_mode = rgpost( '_gaddon_setting_mode' ) ? rgpost( '_gaddon_setting_mode' ) : 'gmp';
$state = array(
'url' => 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 = '<br /><select name="ga_data_stream">';
$html .= '<option value="">' . esc_html__( 'Select a data stream', 'gravityformsgoogleanalytics' ) . '</option>';
foreach ( $data_streams['dataStreams'] as $data_stream ) {
// Ignore data streams that are not "webStreamData".
if ( ! isset( $data_stream['webStreamData'] ) ) {
continue;
}
$html .= sprintf(
'<option value="%s" data-data-stream-id="%s" data-data-stream-name="%s">%s</option>',
esc_attr( $data_stream['webStreamData']['measurementId'] ),
esc_attr( $data_stream['name'] ),
esc_attr( $data_stream['displayName'] ),
esc_html( $data_stream['displayName'] )
);
}
$html .= '</select>';
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 .= '<br /><select name="gacontainer" id="gacontainer">';
$html .= '<option value="">' . esc_html__( 'Select a Container', 'gravityformsgoogleanalytics' ) . '</option>';
foreach ( $container_response['container'] as $container ) {
$html .= sprintf(
'<option data-account-id="%s" data-path="%s" data-token="%s" value="%s">%s</option>',
esc_attr( $account_id ),
esc_attr( $container['path'] ),
esc_attr( $token ),
esc_attr( $container['publicId'] ),
esc_attr( $container['publicId'] )
);
}
$html .= '</select>';
$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 .= '<select name="gaworkspace">';
$html .= '<option value="">' . esc_html__( 'Select a Workspace', 'gravityformsgoogleanalytics' ) . '</option>';
foreach ( $response as $index => $workspaces ) {
foreach ( $workspaces as $workspace ) {
$html .= sprintf(
'<option value="%s">%s</option>',
esc_attr( $workspace['workspaceId'] ),
esc_html( $workspace['name'] )
);
}
}
$html .= '</select>';
$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 "<div id='gf_{$form['id']}' class='gform_anchor' tabindex='-1'></div><div id='gform_confirmation_wrapper_{$form['id']}' class='gform_confirmation_wrapper'><div id='gform_confirmation_message_{$form['id']}' class='gform_confirmation_message_{$form['id']} gform_confirmation_message'>{$processing_message}</div></div>" .
'<script>
if ( window["all_ga_events_sent"] ) {
document.location.href="' . sanitize_url( $confirmation['redirect'] ) . '";
}
window.parent.addEventListener("googleanalytics/all_events_sent", function(){ document.location.href="' . sanitize_url( $confirmation['redirect'] ) . '" });
</script>';
}
/**
* 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( '<a href="%s" class="gfga-disconnect">%s</a> ', 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' );
?>
<style>
#gform-settings-save {
display: none;
}
</style>
<p><?php esc_html_e( 'It appears that you do not have a Google Analytics account for the account you selected.', 'gravityformsgoogleanalytics' ); ?></p>
<p><a class="button primary"
href="<?php echo esc_url_raw( $gravity_url ); ?>"><?php esc_html_e( 'Return to Settings', 'gravityformsgoogleanalytics' ); ?></a>
</p>
<?php
} else {
echo sprintf( '<input type="hidden" name="gfga_token" value="%s" />', esc_attr( rgar( $auth_tokens, 'token' ) ) );
echo sprintf( '<input type="hidden" name="gfga_refresh" value="%s" />', esc_attr( rgar( $auth_tokens, 'refresh' ) ) );
echo '<select name="gaproperty" id="gaproperty">';
echo '<option value="">' . esc_html__( 'Select an account', 'gravityformsgoogleanalytics' ) . '</option>';
foreach ( $response['accountSummaries'] as $account ) {
?>
<optgroup label="<?php echo esc_attr( $account['displayName'] ); ?>">
<?php
if ( ! isset( $account['propertySummaries'] ) || empty( $account['propertySummaries'] ) ) {
printf( '<option disabled="disabled">%s</option>', esc_html__( 'This account does not have any properties...', 'gravityformsgoogleanalytics' ) );
continue;
}
foreach ( $account['propertySummaries'] as $property ) {
printf(
'<option value="%s" data-account-id="%s" data-account-name="%s" data-property-name="%s">%s</option>',
esc_attr( $property['property'] ),
esc_attr( $account['account'] ),
esc_html( $account['displayName'] ),
esc_html( $property['displayName'] ),
esc_html( $property['displayName'] )
);
}
?>
</optgroup>
<?php
}
echo '</select>';
?>
<br/>
<div id="ga-data-streams"></div>
<?php
}
}
/**
* Displays a Google Tag Manager box.
*
* @since 1.0.0
*/
public function settings_gtm_select() {
$auth_payload = $this->plugin_settings->get_auth_payload();
if ( $this->initialize_api() || empty( $auth_payload ) ) {
if ( ! $this->current_user_can_any( $this->_capabilities_form_settings ) ) {
return;
}
$auth_tokens = $this->maybe_get_tokens_from_auth_payload( $auth_payload, self::get_options() );
if ( ! $auth_tokens ) {
return false;
}
$token = rgar( $auth_tokens, 'token' );
$refresh = rgar( $auth_tokens, 'refresh' );
$body = array();
$response = $this->api->get_tag_manager_account( $body );
if ( is_wp_error( $response ) ) {
return $response->get_error_message();
}
if ( isset( $response['account'] ) ) {
echo sprintf( '<input type="hidden" name="gfga_token" value="%s" />', esc_attr( $token ) );
echo sprintf( '<input type="hidden" name="gfga_refresh" value="%s" />', esc_attr( $refresh ) );
echo '<select name="gtmproperty" id="gtmproperty">';
echo '<option value="">' . esc_html__( 'Select a Tag Manager Account', 'gravityformsgoogleanalytics' ) . '</option>';
$google_tag_manager_array = array();
foreach ( $response['account'] as $account ) {
$google_tag_manager_array[ $account['name'] ] = array();
?>
<option data-account-name="<?php echo esc_attr( $account['name'] ); ?>"
data-account-id="<?php echo esc_attr( $account['accountId'] ); ?>"
data-token="<?php echo esc_attr( rgar( $auth_payload, 'access_token' ) ); ?>"
value="<?php echo esc_attr( $account['name'] ); ?>"><?php echo esc_attr( $account['name'] ); ?></option>
<?php
}
echo '</select>';
?>
<br/>
<div id="gtm-containers"></div>
<br/>
<div id="gtm-workspaces"></div>
<?php
return;
}
// No GTM installed - Display a message.
$this->connect_error = true;
$gravity_url = admin_url( 'admin.php?page=gf_settings&subview=gravityformsgoogleanalytics' );
?>
<style>
#gform-settings-save {
display: none;
}
</style>
<p><?php esc_html_e( 'It appears that you do not have a Google Tag Manager account for the account you selected.', 'gravityformsgoogleanalytics' ); ?></p>
<p><a class="button primary"
href="<?php echo esc_url_raw( $gravity_url ); ?>"><?php esc_html_e( 'Return to Settings', 'gravityformsgoogleanalytics' ); ?></a>
</p>
<?php
}
}
/**
* Get Tag Manager Triggers.
*
* @since 2.0
*
* @param array $body The add-on's options.
* @param string $path The path for the api request.
* @param string $workspace The workspace ID.
*
* @return array Array of triggers
*/
public function get_tag_manager_triggers( $body, $path, $workspace ) {
if ( $this->initialize_api() ) {
$triggers = $this->api->get_tag_manager_triggers( array(), $path, $workspace );
return $triggers;
}
return array();
}
/**
* Get Tag Manager Variables.
*
* @since 2.0
*
* @param array $body The add-on's options.
* @param string $path The path for the api request.
* @param string $workspace The workspace ID.
*
* @return array Array of variables.
*/
public function get_tag_manager_variables( $body, $path, $workspace ) {
if ( $this->initialize_api() ) {
$variables = $this->api->get_tag_manager_variables( array(), $path, $workspace );
return $variables;
}
return array();
}
/**
* Sets a nonce for Google Analytics and GTM
*
* @since 1.0.0
*/
public function settings_nonce_connect() {
echo sprintf( '<input type="hidden" name="gfganonce" value="%s" />', 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 '<input type="hidden" name="gfgaaction" value="ga" />';
}
/**
* Sets an action variable for Google Tag Manager.
*
* @since 1.0.0
*/
public function settings_gtm_action() {
echo '<input type="hidden" name="gfgaaction" value="gtm" />';
}
/**
* Sets an action variable for manual configuration.
*
* @since 1.0.0
*/
public function settings_manual_action() {
echo '<input type="hidden" name="gfgaaction" value="manual" />';
}
/**
* 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;
}
?>
<script>
<?php
if ( $is_ajax ) {
echo $script_body;
} else {
// For non-AJAX forms, script body must be wrapped inside the googleanalytics/script_loaded so that it is
// executed after the google analytics script is loaded.
?>
window.addEventListener( "googleanalytics/script_loaded", function() {
<?php
echo $script_body;
?>
} );
<?php
}
?>
</script>
<?php
}
/**
* Renders the script that will count how many feeds were executed. This is needed so that an event can be fired after all the feeds are sent.
*
* @since 2.0
*/
private function render_script_feed_counter() {
// New ajax can't just write to the page.
if ( rgpost( 'gform_submission_method' ) === 'ajax' ) {
return;
}
?>
<script>
window.parent[ 'ga_feed_count' ] = window.parent[ 'ga_feed_count' ] ? window.parent[ 'ga_feed_count' ] + 1 : 1;
</script>
<?php
}
/**
* Sends an event via the Measurement Protocol.
*
* @since 2.0
*
* @param array $ga_codes An array of GA Codes to receive the event. Each GA code here will receive the event.
* @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 string $page_url The current page URL.
*/
private function send_measurement_protocol( $ga_codes, $parameters, $event_name, $page_url ) {
// Initialize the measurement protocol.
if ( ! class_exists( 'GF_Google_Analytics_Measurement_Protocol' ) ) {
include_once 'includes/class-gf-google-analytics-measurement-protocol.php';
}
$event = new GF_Google_Analytics_Measurement_Protocol();
$api_secret = self::get_options( 'ga4_account', 'gmp_api_secret' );
$event->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;
}
}