Completed
Push — add/7777-ga-anonymize-ip ( 94dfd5...439382 )
by
unknown
09:06
created

Jetpack_Google_Analytics::insert_code()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 57
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 26
nc 8
nop 1
dl 0
loc 57
rs 8.7433
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
		Copyright 2006  Aaron D. Campbell  (email : [email protected])
4
5
    This program is free software; you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation; either version 2 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
15
    You should have received a copy of the GNU General Public License
16
    along with this program; if not, write to the Free Software
17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
*/
19
20
/**
21
 * Jetpack_Google_Analytics is the class that handles ALL of the plugin functionality.
22
 * It helps us avoid name collisions
23
 * http://codex.wordpress.org/Writing_a_Plugin#Avoiding_Function_Name_Collisions
24
 */
25
class Jetpack_Google_Analytics {
26
27
	/**
28
	 * @var Jetpack_Google_Analytics - Static property to hold our singleton instance
29
	 */
30
	static $instance = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $instance.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
31
32
	/**
33
	 * This is our constructor, which is private to force the use of get_instance()
34
	 *
35
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
36
	 */
37
	private function __construct() {
38
		add_filter( 'jetpack_wga_classic_custom_vars', array( $this, 'jetpack_wga_classic_anonymize_ip' ) );
39
		add_filter( 'jetpack_wga_classic_custom_vars', array( $this, 'jetpack_wga_classic_track_purchases' ) );
40
		add_action( 'wp_footer', array( $this, 'insert_code' ) );
41
		add_filter( 'wp_footer', array( $this, 'jetpack_wga_classic_track_add_to_cart' ) );
42
	}
43
44
	/**
45
	 * Function to instantiate our class and make it a singleton
46
	 */
47
	public static function get_instance() {
48
		if ( ! self::$instance ) {
49
			self::$instance = new self;
50
		}
51
52
		return self::$instance;
53
	}
54
55
	/**
56
	 * Used to generate a tracking URL
57
	 *
58
	 * @param array $track - Must have ['data'] and ['code'].
59
	 * @return string - Tracking URL
60
	 */
61
	private function _get_url( $track ) {
62
		$site_url = ( is_ssl() ? 'https://':'http://' ) . sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ); // Input var okay.
63
		foreach ( $track as $k => $value ) {
64
			if ( strpos( strtolower( $value ), strtolower( $site_url ) ) === 0 ) {
65
				$track[ $k ] = substr( $track[ $k ], strlen( $site_url ) );
66
			}
67
			if ( 'data' === $k ) {
68
				$track[ $k ] = preg_replace( '/^https?:\/\/|^\/+/i', '', $track[ $k ] );
69
			}
70
71
			// This way we don't lose search data.
72
			if ( 'data' === $k && 'search' === $track['code'] ) {
73
				$track[ $k ] = rawurlencode( $track[ $k ] );
74
			} else {
75
				$track[ $k ] = preg_replace( '/[^a-z0-9\.\/\+\?=-]+/i', '_', $track[ $k ] );
76
			}
77
78
			$track[ $k ] = trim( $track[ $k ], '_' );
79
		}
80
		$char = ( strpos( $track['data'], '?' ) === false ) ? '?' : '&amp;';
81
		return str_replace( "'", "\'", "/{$track['code']}/{$track['data']}{$char}referer=" . rawurlencode( isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '' ) ); // Input var okay.
82
	}
83
84
	/**
85
	 * Maybe output or return, depending on the context
86
	 */
87
	private function _output_or_return( $val, $maybe ) {
88
		if ( $maybe ) {
89
			echo $val . "\r\n";
90
		} else {
91
			return $val;
92
		}
93
	}
94
95
	/**
96
	 * This injects the Google Analytics code into the footer of the page.
97
	 *
98
	 * @param bool[optional] $output - defaults to true, false returns but does NOT echo the code.
99
	 */
100
	public function insert_code( $output = true ) {
101
		// If $output is not a boolean false, set it to true (default).
102
		$output = ( false !== $output);
103
104
		$tracking_id = $this->_get_tracking_code();
105
		if ( empty( $tracking_id ) ) {
106
			return $this->_output_or_return( '<!-- Your Google Analytics Plugin is missing the tracking ID -->', $output );
107
		}
108
109
		// If we're in the admin_area, return without inserting code.
110
		if ( is_admin() ) {
111
			return $this->_output_or_return( '<!-- Your Google Analytics Plugin is set to ignore Admin area -->', $output );
112
		}
113
114
		$custom_vars = array(
115
			"_gaq.push(['_setAccount', '{$tracking_id}']);",
116
		);
117
118
		$track = array();
119
		if ( is_404() ) {
120
			// This is a 404 and we are supposed to track them.
121
			$custom_vars[] = "_gaq.push(['_trackEvent', '404', document.location.href, document.referrer]);";
122
		} elseif ( is_search() ) {
123
			// Set track for searches, if it's a search, and we are supposed to.
124
			$track['data'] = sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ); // Input var okay.
125
			$track['code'] = 'search';
126
		}
127
128
		if ( ! empty( $track ) ) {
129
			$track['url'] = $this->_get_url( $track );
130
			// adjust the code that we output, account for both types of tracking.
131
			$track['url'] = esc_js( str_replace( '&', '&amp;', $track['url'] ) );
132
			$custom_vars[] = "_gaq.push(['_trackPageview','{$track['url']}']);";
133
		} else {
134
			$custom_vars[] = "_gaq.push(['_trackPageview']);";
135
		}
136
137
		$custom_vars = apply_filters( 'jetpack_wga_classic_custom_vars', $custom_vars );
138
139
		// Ref: https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingEcommerce#Example
140
		$async_code = "<!-- Jetpack Google Analytics -->
141
			<script type='text/javascript'>
142
				var _gaq = _gaq || [];
143
				%custom_vars%
144
145
				(function() {
146
					var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
147
					ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
148
					var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
149
				})();
150
			</script>";
151
152
		$custom_vars_string = implode( "\r\n", $custom_vars );
153
		$async_code = str_replace( '%custom_vars%', $custom_vars_string, $async_code );
154
155
		return $this->_output_or_return( $async_code, $output );
156
	}
157
158
	/**
159
	 * Used to get the tracking code option
160
	 *
161
	 * @return tracking code option value.
162
	 */
163
	private function _get_tracking_code() {
164
		$o = get_option( 'jetpack_wga' );
165
166
		if ( isset( $o['code'] ) && preg_match( '#UA-[\d-]+#', $o['code'], $matches ) ) {
167
				return $o['code'];
168
		}
169
170
		return '';
171
	}
172
173
	/**
174
	 * Used to filter in the anonymize IP snippet to the custom vars array for classic analytics
175
	 * Ref https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gat#_gat._anonymizelp
176
	 * @param array custom vars to be filtered
177
	 * @return array possibly updated custom vars
178
	 */
179
	public function jetpack_wga_classic_anonymize_ip( $custom_vars ) {
180
		$o = get_option( 'jetpack_wga' );
181
		$anonymize_ip = isset( $o[ 'anonymize_ip' ] ) ? $o[ 'anonymize_ip' ] : false;
182
		if ( $anonymize_ip ) {
183
			array_push( $custom_vars, "_gaq.push(['_gat._anonymizeIp']);" );
184
		}
185
186
		return $custom_vars;
187
	}
188
189
	/**
190
	 * Used to filter in the order details to the custom vars array for classic analytics
191
	 * @param array custom vars to be filtered
192
	 * @return array possibly updated custom vars
193
	 */
194
	public function jetpack_wga_classic_track_purchases( $custom_vars ) {
195
		global $wp;
196
197
		// Ref: https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingEcommerce#Example
198
		$o = get_option( 'jetpack_wga' );
199
		$ec_track_purchases = isset( $o[ 'ec_track_purchases' ] ) ? $o[ 'ec_track_purchases' ] : false;
200
		$minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( WC_VERSION, '3.0', '>=' );
201
		if ( $ec_track_purchases && $minimum_woocommerce_active && is_order_received_page() ) {
202
			$order_id = isset( $wp->query_vars['order-received'] ) ? $wp->query_vars['order-received'] : 0;
203
			if ( 0 < $order_id && 1 != get_post_meta( $order_id, '_ga_tracked', true ) ) {
204
				$order = new WC_Order( $order_id );
205
206
				// [ '_add_Trans', '123', 'Site Title', '21.00', '1.00', '5.00', 'Snohomish', 'WA', 'USA' ]
207
				$code = "_gaq.push(['_addTrans',
208
					'" . esc_js( $order->get_order_number() ) . "',			// order ID
209
					'" . esc_js( get_bloginfo( 'name' ) ) . "',					// store name
210
					'" . esc_js( $order->get_total() ) . "',						// total
211
					'" . esc_js( $order->get_total_tax() ) . "',				// tax
212
					'" . esc_js( $order->get_total_shipping() ) . "',		// shipping
213
					'" . esc_js( $order->get_billing_city() ) . "',			// city
214
					'" . esc_js( $order->get_billing_state() ) . "',		// state or province
215
					'" . esc_js( $order->get_billing_country() ) . "'		// country
216
				]);";
217
				array_push( $custom_vars, $code );
218
219
				// Order items
220
				if ( $order->get_items() ) {
221
					foreach ( $order->get_items() as $item ) {
222
						$product = $order->get_product_from_item( $item );
223
224
						$code = "_gaq.push(['_addItem',";
225
						$code .= "'" . esc_js( $order->get_order_number() ) . "',";
226
						$code .= "'" . esc_js( $product->get_sku() ? $product->get_sku() : $product->get_id() ) . "',";
227
						$code .= "'" . esc_js( $item['name'] ) . "',";
228
						$code .= "'" . esc_js( self::get_product_categories_concatenated( $product ) ) . "',";
229
						$code .= "'" . esc_js( $order->get_item_total( $item ) ) . "',";
230
						$code .= "'" . esc_js( $item['qty'] ) . "'";
231
						$code .= "]);";
232
						array_push( $custom_vars, $code );
233
					}
234
				} // get_items
235
236
				// Mark the order as tracked
237
				update_post_meta( $order_id, '_ga_tracked', 1 );
238
				array_push( $custom_vars, "_gaq.push(['_trackTrans']);" );
239
			} // order not yet tracked
240
		} // is order received page
241
242
		return $custom_vars;
243
	}
244
245
	/**
246
	 * Gets product categories or varation attributes as a formatted concatenated string
247
	 * @param WC_Product
248
	 * @return string
249
	 */
250
	public function get_product_categories_concatenated( $product ) {
251
		$variation_data = $product->is_type( 'variation' ) ? wc_get_product_variation_attributes( $product->get_id() ) : '';
252
		if ( is_array( $variation_data ) && ! empty( $variation_data ) ) {
253
			$line = wc_get_formatted_variation( $variation_data, true );
254
		} else {
255
			$out = array();
256
			$categories = get_the_terms( $product->get_id(), 'product_cat' );
257
			if ( $categories ) {
258
				foreach ( $categories as $category ) {
259
					$out[] = $category->name;
260
				}
261
			}
262
			$line = join( "/", $out );
263
		}
264
		return $line;
265
	}
266
267
	/**
268
	 * Used to add footer javascript to track user clicking on add-to-cart buttons
269
	 * on single views (.single_add_to_cart_button) and list views (.add_to_cart_button)
270
	 */
271
	public function jetpack_wga_classic_track_add_to_cart() {
272
		$tracking_id = $this->_get_tracking_code();
273
		if ( empty( $tracking_id ) ) {
274
			return;
275
		}
276
277
		$o = get_option( 'jetpack_wga' );
278
		$ec_track_add_to_cart = isset( $o[ 'ec_track_add_to_cart' ] ) ? $o[ 'ec_track_add_to_cart' ] : false;
279
		if ( $ec_track_add_to_cart ) {
280
			if ( is_product() ) { // product page
281
				global $product;
282
				$product_sku_or_id = $product->get_sku() ? $product->get_sku() : "#" + $product->get_id();
283
				wc_enqueue_js(
284
					"jQuery( function( $ ) {
285
						$( '.single_add_to_cart_button' ).click( function() {
286
							_gaq.push(['_trackEvent', 'Products', 'Add to Cart', '#" . esc_js( $product_sku_or_id ) . "']);
287
						} );
288
					} );"
289
				);
290
			} else if ( is_woocommerce() ) { // any other page that uses templates (like product lists, archives, etc)
291
				wc_enqueue_js(
292
					"jQuery( function( $ ) {
293
						$( '.add_to_cart_button:not(.product_type_variable, .product_type_grouped)' ).click( function() {
294
							var label = $( this ).data( 'product_sku' ) ? $( this ).data( 'product_sku' ) : '#' + $( this ).data( 'product_id' );
295
							_gaq.push(['_trackEvent', 'Products', 'Add to Cart', label]);
296
						} );
297
					} );"
298
				);
299
			}
300
		}
301
	}
302
}
303
304
global $jetpack_google_analytics;
305
$jetpack_google_analytics = Jetpack_Google_Analytics::get_instance();
306