DuracellTomi's Google Tag Manager for WordPress - Version 1.16

Version Description

This plugin version does not add or update any functionality. After recent events, the code of the plugin has been checked line by line to see where additional security checks can be added. The code has been formatted to better support readability for other programmers.

Deprecated: * gtm4wp_get_the_gtm_tag hook and the corresponding GTM4WP_WPFILTER_GETTHEGTMTAG PHP constant. * gtm4wp_add_global_vars hook and the corresponding GTM4WP_WPFILTER_ADDGLOBALVARS PHP constant. Use gtm4wp_add_global_vars_array / GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY instead. * gtm4wp_after_datalayer hook and the corresponding GTM4WP_WPACTION_AFTER_DATALAYER PHP constant. Use gtm4wp_output_after_datalayer / GTM4WP_WPACTION_AFTER_DATALAYER instead witch can be used in the same way but it is an action instead of a filter.

Upcoming version will come with important changes: * Minimum PHP version will be raised to 7.4: this will allow me to add even more safety measures * Minimum supported WooCommerce version will be raised to WooCommerce 5.0: with this I can remove some very old compatibility code * Deprecated features will be removed (aims to simplify code for better maintenance): * Do not track flag of the browser added into data layer * Legacy version of WooCommerce dynamic remarketing (using ecomm_ parameters)

The goal of all these changes aim to keep the plugin code clean and free from legacy solutions.

Download this release

Release Info

Developer duracelltomi
Plugin Icon 128x128 DuracellTomi's Google Tag Manager for WordPress
Version 1.16
Comparing to
See all releases

Code changes from version 1.15.2 to 1.16

admin/admin-tab-advanced.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GTM4WP options on the Advanced tab.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ $GLOBALS['gtm4wp_advancedfieldtexts'] = array(
12
+ GTM4WP_OPTION_DATALAYER_NAME => array(
13
+ 'label' => esc_html__( 'dataLayer variable name', 'duracelltomi-google-tag-manager' ),
14
+ 'description' => esc_html__( 'In some cases you need to rename the dataLayer variable. You can enter your name here. Leave black for default name: dataLayer', 'duracelltomi-google-tag-manager' ),
15
+ 'phase' => GTM4WP_PHASE_STABLE,
16
+ ),
17
+ GTM4WP_OPTION_ENV_GTM_AUTH => array(
18
+ 'label' => esc_html__( 'Environment gtm_auth parameter', 'duracelltomi-google-tag-manager' ),
19
+ 'description' => esc_html__( 'Enter the gtm_auth parameter of the Google Tag Manager environment that has to be activated on this site. Both gtm_auth and gtm_preview parameters are required to activate the desired environment.', 'duracelltomi-google-tag-manager' ),
20
+ 'phase' => GTM4WP_PHASE_STABLE,
21
+ ),
22
+ GTM4WP_OPTION_ENV_GTM_PREVIEW => array(
23
+ 'label' => esc_html__( 'Environment gtm_preview parameter', 'duracelltomi-google-tag-manager' ),
24
+ 'description' => esc_html__( 'Enter the gtm_auth parameter of the Google Tag Manager environment that has to be activated on this site. Both gtm_auth and gtm_preview parameters are required to activate the desired environment.', 'duracelltomi-google-tag-manager' ),
25
+ 'phase' => GTM4WP_PHASE_STABLE,
26
+ ),
27
+ GTM4WP_OPTION_DONOTTRACK => array(
28
+ 'label' => esc_html__( "Include browser 'Do not track' setting", 'duracelltomi-google-tag-manager' ),
29
+ 'description' => esc_html__( 'Add into the data layer whether the user has asked not to track any website interaction. You may want to respect this and disable all tags if this variable is set in the data layer.', 'duracelltomi-google-tag-manager' ),
30
+ 'phase' => GTM4WP_PHASE_DEPRECATED,
31
+ ),
32
+ GTM4WP_OPTION_LOADEARLY => array(
33
+ 'label' => esc_html__( 'Load GTM container as early as possible', 'duracelltomi-google-tag-manager' ),
34
+ 'description' => esc_html__( 'Turning on this option will load your Google Tag Manager container as early as possible during page load. This can cause issues if you are using jQuery in your custom HTML tags that fire on \'Page View\' events.', 'duracelltomi-google-tag-manager' ),
35
+ 'phase' => GTM4WP_PHASE_STABLE,
36
+ ),
37
+ GTM4WP_OPTION_GTMDOMAIN => array(
38
+ 'label' => esc_html__( 'Container domain name', 'duracelltomi-google-tag-manager' ),
39
+ 'description' => esc_html__( 'Enter your custom domain name if you are using a server side GTM container for tracking. Do not include https:// prefix. Leave this blank to use www.googletagmanager.com', 'duracelltomi-google-tag-manager' ),
40
+ 'phase' => GTM4WP_PHASE_STABLE,
41
+ ),
42
+ GTM4WP_OPTION_NOGTMFORLOGGEDIN => array(
43
+ 'label' => esc_html__( 'User roles to exclude', 'duracelltomi-google-tag-manager' ),
44
+ 'description' => esc_html__( 'Do not load GTM container on the frontend if role of the logged in user is any of this', 'duracelltomi-google-tag-manager' ),
45
+ 'phase' => GTM4WP_PHASE_STABLE,
46
+ ),
47
+ );
admin/admin-tab-basicdata.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GTM4WP options on the Basic data tab.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ $GLOBALS['gtm4wp_includefieldtexts'] = array(
12
+ GTM4WP_OPTION_INCLUDE_POSTTYPE => array(
13
+ 'label' => esc_html__( 'Posttype of current post/archive', 'duracelltomi-google-tag-manager' ),
14
+ 'description' => esc_html__( 'Check this option to include the type of the current post or archive page (post, page or any custom post type).', 'duracelltomi-google-tag-manager' ),
15
+ 'phase' => GTM4WP_PHASE_STABLE,
16
+ ),
17
+ GTM4WP_OPTION_INCLUDE_CATEGORIES => array(
18
+ 'label' => esc_html__( 'Category list of current post/archive', 'duracelltomi-google-tag-manager' ),
19
+ 'description' => esc_html__( 'Check this option to include the category names of the current post or archive page', 'duracelltomi-google-tag-manager' ),
20
+ 'phase' => GTM4WP_PHASE_STABLE,
21
+ ),
22
+ GTM4WP_OPTION_INCLUDE_TAGS => array(
23
+ 'label' => esc_html__( 'Tags of current post', 'duracelltomi-google-tag-manager' ),
24
+ 'description' => esc_html__( 'Check this option to include the tags of the current post.', 'duracelltomi-google-tag-manager' ),
25
+ 'phase' => GTM4WP_PHASE_STABLE,
26
+ ),
27
+ GTM4WP_OPTION_INCLUDE_AUTHORID => array(
28
+ 'label' => esc_html__( 'Post author ID', 'duracelltomi-google-tag-manager' ),
29
+ 'description' => esc_html__( 'Check this option to include the ID of the author on the current post or author page.', 'duracelltomi-google-tag-manager' ),
30
+ 'phase' => GTM4WP_PHASE_STABLE,
31
+ ),
32
+ GTM4WP_OPTION_INCLUDE_AUTHOR => array(
33
+ 'label' => esc_html__( 'Post author name', 'duracelltomi-google-tag-manager' ),
34
+ 'description' => esc_html__( 'Check this option to include the name of the author on the current post or author page.', 'duracelltomi-google-tag-manager' ),
35
+ 'phase' => GTM4WP_PHASE_STABLE,
36
+ ),
37
+ GTM4WP_OPTION_INCLUDE_POSTDATE => array(
38
+ 'label' => esc_html__( 'Post date', 'duracelltomi-google-tag-manager' ),
39
+ 'description' => esc_html__( 'Check this option to include the date of the current post. This will include 4 dataLayer variables: full date, post year, post month, post date.', 'duracelltomi-google-tag-manager' ),
40
+ 'phase' => GTM4WP_PHASE_STABLE,
41
+ ),
42
+ GTM4WP_OPTION_INCLUDE_POSTTITLE => array(
43
+ 'label' => esc_html__( 'Post title', 'duracelltomi-google-tag-manager' ),
44
+ 'description' => esc_html__( 'Check this option to include the title of the current post.', 'duracelltomi-google-tag-manager' ),
45
+ 'phase' => GTM4WP_PHASE_STABLE,
46
+ ),
47
+ GTM4WP_OPTION_INCLUDE_POSTCOUNT => array(
48
+ 'label' => esc_html__( 'Post count', 'duracelltomi-google-tag-manager' ),
49
+ 'description' => esc_html__( 'Check this option to include the count of the posts currently shown on the page and the total number of posts in the category/tag/any taxonomy.', 'duracelltomi-google-tag-manager' ),
50
+ 'phase' => GTM4WP_PHASE_STABLE,
51
+ ),
52
+ GTM4WP_OPTION_INCLUDE_POSTID => array(
53
+ 'label' => esc_html__( 'Post ID', 'duracelltomi-google-tag-manager' ),
54
+ 'description' => esc_html__( 'Check this option to include the post id.', 'duracelltomi-google-tag-manager' ),
55
+ 'phase' => GTM4WP_PHASE_STABLE,
56
+ ),
57
+ GTM4WP_OPTION_INCLUDE_POSTFORMAT => array(
58
+ 'label' => esc_html__( 'Post Format', 'duracelltomi-google-tag-manager' ),
59
+ 'description' => esc_html__( 'Check this option to include the post format.', 'duracelltomi-google-tag-manager' ),
60
+ 'phase' => GTM4WP_PHASE_STABLE,
61
+ ),
62
+ GTM4WP_OPTION_INCLUDE_POSTTERMLIST => array(
63
+ 'label' => esc_html__( 'Post Terms', 'duracelltomi-google-tag-manager' ),
64
+ 'description' => esc_html__( 'Check this option to include taxonomy values associated with a given post.', 'duracelltomi-google-tag-manager' ),
65
+ 'phase' => GTM4WP_PHASE_STABLE,
66
+ ),
67
+ GTM4WP_OPTION_INCLUDE_SEARCHDATA => array(
68
+ 'label' => esc_html__( 'Search data', 'duracelltomi-google-tag-manager' ),
69
+ 'description' => esc_html__( 'Check this option to include the search term, referring page URL and number of results on the search page.', 'duracelltomi-google-tag-manager' ),
70
+ 'phase' => GTM4WP_PHASE_STABLE,
71
+ ),
72
+ GTM4WP_OPTION_INCLUDE_LOGGEDIN => array(
73
+ 'label' => esc_html__( 'Logged in status', 'duracelltomi-google-tag-manager' ),
74
+ 'description' => esc_html__( 'Check this option to include whether there is a logged in user on your website.', 'duracelltomi-google-tag-manager' ),
75
+ 'phase' => GTM4WP_PHASE_STABLE,
76
+ ),
77
+ GTM4WP_OPTION_INCLUDE_USERROLE => array(
78
+ 'label' => esc_html__( 'Logged in user role', 'duracelltomi-google-tag-manager' ),
79
+ 'description' => esc_html__( 'Check this option to include the role of the logged in user.', 'duracelltomi-google-tag-manager' ),
80
+ 'phase' => GTM4WP_PHASE_STABLE,
81
+ ),
82
+ GTM4WP_OPTION_INCLUDE_USERID => array(
83
+ 'label' => esc_html__( 'Logged in user ID', 'duracelltomi-google-tag-manager' ),
84
+ 'description' => esc_html__( 'Check this option to include the ID of the logged in user.', 'duracelltomi-google-tag-manager' ),
85
+ 'phase' => GTM4WP_PHASE_STABLE,
86
+ ),
87
+ GTM4WP_OPTION_INCLUDE_USERNAME => array(
88
+ 'label' => esc_html__( 'Logged in user name', 'duracelltomi-google-tag-manager' ),
89
+ 'description' => esc_html__( 'Check this option to include the username of the logged in user.', 'duracelltomi-google-tag-manager' ),
90
+ 'phase' => GTM4WP_PHASE_STABLE,
91
+ ),
92
+ GTM4WP_OPTION_INCLUDE_USEREMAIL => array(
93
+ 'label' => esc_html__( 'Logged in user email', 'duracelltomi-google-tag-manager' ),
94
+ 'description' => esc_html__( 'Check this option to include the email address of the logged in user.', 'duracelltomi-google-tag-manager' ),
95
+ 'phase' => GTM4WP_PHASE_STABLE,
96
+ ),
97
+ GTM4WP_OPTION_INCLUDE_USERREGDATE => array(
98
+ 'label' => esc_html__( 'Logged in user creation date', 'duracelltomi-google-tag-manager' ),
99
+ 'description' => esc_html__( 'Check this option to include the date of creation (registration) of the logged in user.', 'duracelltomi-google-tag-manager' ),
100
+ 'phase' => GTM4WP_PHASE_STABLE,
101
+ ),
102
+ GTM4WP_OPTION_INCLUDE_VISITOR_IP => array(
103
+ 'label' => esc_html__( 'Visitor IP', 'duracelltomi-google-tag-manager' ),
104
+ 'description' => esc_html__( 'Check this option to include the IP address of the visitor. You might use this to filter internal traffic inside your GTM container. Please be aware that per GDPR its not allowed to transmit this full IP address to Google Analytics or to any other measurement system without explicit consent from the visitor.', 'duracelltomi-google-tag-manager' ),
105
+ 'phase' => GTM4WP_PHASE_STABLE,
106
+ ),
107
+ GTM4WP_OPTION_INCLUDE_BROWSERDATA => array(
108
+ 'label' => esc_html__( 'Browser data *', 'duracelltomi-google-tag-manager' ),
109
+ 'description' => esc_html__( 'Check this option to include the name, version and engine data of the browser the visitor uses.', 'duracelltomi-google-tag-manager' ),
110
+ ),
111
+ GTM4WP_OPTION_INCLUDE_OSDATA => array(
112
+ 'label' => esc_html__( 'OS data *', 'duracelltomi-google-tag-manager' ),
113
+ 'description' => esc_html__( 'Check this option to include the name and version of the operating system the visitor uses.', 'duracelltomi-google-tag-manager' ),
114
+ ),
115
+ GTM4WP_OPTION_INCLUDE_DEVICEDATA => array(
116
+ 'label' => esc_html__( 'Device data *', 'duracelltomi-google-tag-manager' ),
117
+ 'description' => esc_html__( 'Check this option to include the type of device the user is currently using (desktop, tablet or mobile) including manufacturer and model data.', 'duracelltomi-google-tag-manager' ),
118
+ ),
119
+ GTM4WP_OPTION_INCLUDE_MISCGEO => array(
120
+ 'label' => esc_html__( 'Geo data', 'duracelltomi-google-tag-manager' ),
121
+ 'description' => esc_html__( 'Add geo data (latitude, longitude, country, city, etc) of the current visitor (provided by ipstack.com)', 'duracelltomi-google-tag-manager' ),
122
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
123
+ ),
124
+ GTM4WP_OPTION_INCLUDE_MISCGEOAPI => array(
125
+ 'label' => esc_html__( 'IPStack.com API key', 'duracelltomi-google-tag-manager' ),
126
+ 'description' => sprintf(
127
+ // translators: 1: the anchor eleemnt pointing to ipstack.com to register for API keys. 2: closing anchor tag.
128
+ esc_html__(
129
+ 'Enter your IPStack.com API key here. %1$sGet a free API key here%2$s.',
130
+ 'duracelltomi-google-tag-manager'
131
+ ),
132
+ '<a href="https://ipstack.com/product?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress" target="_blank" rel="noopener">',
133
+ '</a>'
134
+ ),
135
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
136
+ ),
137
+ GTM4WP_OPTION_INCLUDE_MISCGEOCF => array(
138
+ 'label' => esc_html__( 'Cloudflare country code', 'duracelltomi-google-tag-manager' ),
139
+ 'description' => esc_html__( 'Add the country code of the user provided by Cloudflare (if Cloudflare is used with your site)', 'duracelltomi-google-tag-manager' ),
140
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
141
+ ),
142
+ GTM4WP_OPTION_INCLUDE_WEATHER => array(
143
+ 'label' => esc_html__( 'Weather data', 'duracelltomi-google-tag-manager' ),
144
+ 'description' => sprintf(
145
+ gtm4wp_safe_admin_html(
146
+ // translators: 1: opening anchor tag linking to ipstack.com product page. 2: closing anchor tag. 3: opening anchor tag linking to OpenWeatherMap pricing page. 4: closing anchor tag.
147
+ __(
148
+ 'Check this option to include the current weather conditions around the current visitor.<br /><br />
149
+ <strong>Attention!</strong> This feature uses %1$sipstack.com%2$s and
150
+ %3$sopenweathermap.org%4$s to collect data.<br />
151
+ Depending on your website\'s traffic, additional fees may apply!<br />
152
+ This plugin caches weather data for 1 hour to lower the need to access those services.<br /><br />
153
+ If you activate weather data, <strong>you will need</strong> to add an IPStack.com API key regardless of whether you
154
+ activate the \'Geo data\' option!',
155
+ 'duracelltomi-google-tag-manager'
156
+ )
157
+ ),
158
+ '<a href="https://ipstack.com/product?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress" target="_blank" rel="noopener">',
159
+ '</a>',
160
+ '<a href="http://openweathermap.org/price?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress" target="_blank" rel="noopener">',
161
+ '</a>'
162
+ ),
163
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
164
+ ),
165
+ GTM4WP_OPTION_INCLUDE_WEATHERUNITS => array(
166
+ 'label' => esc_html__( 'Weather data units', 'duracelltomi-google-tag-manager' ),
167
+ 'description' => esc_html__( 'Select which temperature units you would like to use.', 'duracelltomi-google-tag-manager' ),
168
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
169
+ ),
170
+ GTM4WP_OPTION_INCLUDE_WEATHEROWMAPI => array(
171
+ 'label' => esc_html__( 'OpenWeatherMap API key', 'duracelltomi-google-tag-manager' ),
172
+ 'description' => sprintf(
173
+ // translators: 1: opening anchor tag linking to Open Weather Map's pricing page. 2: closing anchor tag.
174
+ esc_html__(
175
+ 'Enter your OpenWeatherMap API key here. %1$sGet a free API key here%2$s.',
176
+ 'duracelltomi-google-tag-manager'
177
+ ),
178
+ '<a href="http://openweathermap.org/price?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress" target="_blank" rel="noopener">',
179
+ '</a>'
180
+ ),
181
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
182
+ ),
183
+ GTM4WP_OPTION_INCLUDE_SITEID => array(
184
+ 'label' => esc_html__( 'Site ID', 'duracelltomi-google-tag-manager' ),
185
+ 'description' => esc_html__( 'ID of the current site in a WordPress Multisite environment', 'duracelltomi-google-tag-manager' ),
186
+ 'phase' => GTM4WP_PHASE_STABLE,
187
+ ),
188
+ GTM4WP_OPTION_INCLUDE_SITENAME => array(
189
+ 'label' => esc_html__( 'Site name', 'duracelltomi-google-tag-manager' ),
190
+ 'description' => esc_html__( 'Name of the current site in a WordPress Multisite environment', 'duracelltomi-google-tag-manager' ),
191
+ 'phase' => GTM4WP_PHASE_STABLE,
192
+ ),
193
+ );
admin/admin-tab-events.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GTM4WP options on the Events tab.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ $GLOBALS['gtm4wp_eventfieldtexts'] = array(
12
+ GTM4WP_OPTION_EVENTS_FORMMOVE => array(
13
+ 'label' => esc_html__( 'Form fill events (gtm4wp.formElementEnter & gtm4wp.formElementLeave)', 'duracelltomi-google-tag-manager' ),
14
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when a visitor moves between elements of a form (comment, contact, etc).', 'duracelltomi-google-tag-manager' ),
15
+ 'phase' => GTM4WP_PHASE_STABLE,
16
+ ),
17
+ GTM4WP_OPTION_EVENTS_NEWUSERREG => array(
18
+ 'label' => esc_html__( 'New user registration', 'duracelltomi-google-tag-manager' ),
19
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when a new user registration has been completed on the frontend of your site (admin events not included)', 'duracelltomi-google-tag-manager' ),
20
+ 'phase' => GTM4WP_PHASE_STABLE,
21
+ ),
22
+ GTM4WP_OPTION_EVENTS_USERLOGIN => array(
23
+ 'label' => esc_html__( 'User logged in', 'duracelltomi-google-tag-manager' ),
24
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when an existing user has been logged in on the frontend of your site (admin events not included)', 'duracelltomi-google-tag-manager' ),
25
+ 'phase' => GTM4WP_PHASE_STABLE,
26
+ ),
27
+ GTM4WP_OPTION_EVENTS_YOUTUBE => array(
28
+ 'label' => esc_html__( 'YouTube video events', 'duracelltomi-google-tag-manager' ),
29
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when a visitor interacts with a YouTube video embeded on your site.', 'duracelltomi-google-tag-manager' ),
30
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
31
+ ),
32
+ GTM4WP_OPTION_EVENTS_VIMEO => array(
33
+ 'label' => esc_html__( 'Vimeo video events', 'duracelltomi-google-tag-manager' ),
34
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when a visitor interacts with a Vimeo video embeded on your site.', 'duracelltomi-google-tag-manager' ),
35
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
36
+ ),
37
+ GTM4WP_OPTION_EVENTS_SOUNDCLOUD => array(
38
+ 'label' => esc_html__( 'Soundcloud events', 'duracelltomi-google-tag-manager' ),
39
+ 'description' => esc_html__( 'Check this option to include a Tag Manager event when a visitor interacts with a Soundcloud media embeded on your site.', 'duracelltomi-google-tag-manager' ),
40
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
41
+ ),
42
+ );
admin/admin-tab-integrate.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GTM4WP options on the Integrate tab.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ $GLOBALS['gtm4wp_integratefieldtexts'] = array(
12
+ GTM4WP_OPTION_INTEGRATE_WPCF7 => array(
13
+ 'label' => esc_html__( 'Contact Form 7', 'duracelltomi-google-tag-manager' ),
14
+ 'description' => esc_html__( 'Check this to fire dataLayer events after Contact Form 7 submissions (supported events: invalid input, spam detected, form submitted, form submitted and mail sent, form submitted and mail send failed).', 'duracelltomi-google-tag-manager' ),
15
+ 'phase' => GTM4WP_PHASE_STABLE,
16
+ 'plugintocheck' => 'contact-form-7/wp-contact-form-7.php',
17
+ ),
18
+ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC => array(
19
+ 'label' => esc_html__( 'Track classic e-commerce', 'duracelltomi-google-tag-manager' ),
20
+ 'description' => esc_html__( 'This feature is deprecated and will be removed soon! You should upgrade to enhanced ecommerce as soon as possible.', 'duracelltomi-google-tag-manager' ),
21
+ 'phase' => GTM4WP_PHASE_DEPRECATED,
22
+ ),
23
+ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC => array(
24
+ 'label' => esc_html__( 'Track enhanced e-commerce', 'duracelltomi-google-tag-manager' ),
25
+ 'description' => sprintf(
26
+ gtm4wp_safe_admin_html(
27
+ // translators: 1: anchor element linking to Universal Analytics Enhanced Ecommerce docs. 2: closing anchor element. 3: anchor element linking to GTM4WP setup guide for Universal Analytics. 4: closing anchor element. 5: anchor element linking to GTM4WP setup guide for Google Analytics 4. 6: closing anchor element.
28
+ __(
29
+ 'Choose this option if you would like to track e-commerce data using
30
+ %1$senhanced ecommerce tracking%2$s.<br>
31
+ Use the plugin\'s official guides to setup your Google Tag Manager container:<br/>
32
+ <ul><li>%3$sGoogle Analytics 3 / Universal Analytics setup%4$s</li>
33
+ <li>%5$sGoogle Analytics 4 setup%6$s</li></ul>',
34
+ 'duracelltomi-google-tag-manager'
35
+ )
36
+ ),
37
+ '<a href="https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce" target="_blank" rel="noopener">',
38
+ '</a>',
39
+ '<a href="https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking" target="_blank" rel="noopener">',
40
+ '</a>',
41
+ '<a href="https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking-google-analytics-4-ga4-version" target="_blank" rel="noopener">',
42
+ '</a>'
43
+ ),
44
+ 'phase' => GTM4WP_PHASE_STABLE,
45
+ 'plugintocheck' => 'woocommerce/woocommerce.php',
46
+ ),
47
+ GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION => array(
48
+ 'label' => esc_html__( 'Products per impression', 'duracelltomi-google-tag-manager' ),
49
+ 'description' => gtm4wp_safe_admin_html(
50
+ __(
51
+ 'If you have many products shown on product category pages and/or on your site home, you could miss pageviews in Google Analytics due to the
52
+ amount of data that is needed to be sent. To prevent this, you can split product impression data into multiple Google Analytics events by
53
+ entering a number here (minimum 10-15 recommended) and adding gtm4wp.productImpressionEEC into your Google Analytics ecommerce event helper
54
+ tag\'s trigger.<br /><br />Leave this value 0 to include product impression data in your pageview hit.',
55
+ 'duracelltomi-google-tag-manager'
56
+ )
57
+ ),
58
+ 'phase' => GTM4WP_PHASE_STABLE,
59
+ ),
60
+ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP => array(
61
+ 'label' => esc_html__( 'Cart as 1st checkout step', 'duracelltomi-google-tag-manager' ),
62
+ 'description' => esc_html__( 'Enable this to track the cart page as the first checkout step in enhanced ecommerce instead of the checkout page itself', 'duracelltomi-google-tag-manager' ),
63
+ 'phase' => GTM4WP_PHASE_STABLE,
64
+ ),
65
+ GTM4WP_OPTION_INTEGRATE_WCEINCLUDECARTINDL => array(
66
+ 'label' => esc_html__( 'Cart content in data layer', 'duracelltomi-google-tag-manager' ),
67
+ 'description' => esc_html__( 'Enable this option to include the content of the cart in the data layer on each page. Needs WooCommerce v3.2 or newer. Especially useful for site personalization with Google Optimize.', 'duracelltomi-google-tag-manager' ),
68
+ 'phase' => GTM4WP_PHASE_STABLE,
69
+ ),
70
+ GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH => array(
71
+ 'label' => esc_html__( 'Include full category path.', 'duracelltomi-google-tag-manager' ),
72
+ 'description' => esc_html__( 'Check this to inclulde the full category path of each product in enhanced ecommerce tracking. WARNING! This can lead to performance issues on large sites with lots of traffic!', 'duracelltomi-google-tag-manager' ),
73
+ 'phase' => GTM4WP_PHASE_STABLE,
74
+ ),
75
+ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY => array(
76
+ 'label' => esc_html__( 'Taxonomy to be used for product brands', 'duracelltomi-google-tag-manager' ),
77
+ 'description' => esc_html__( 'Select which custom taxonomy is being used to add the brand of products', 'duracelltomi-google-tag-manager' ),
78
+ 'phase' => GTM4WP_PHASE_STABLE,
79
+ ),
80
+ GTM4WP_OPTION_INTEGRATE_WCCUSTOMERDATA => array(
81
+ 'label' => esc_html__( 'Customer data in data layer', 'duracelltomi-google-tag-manager' ),
82
+ 'description' => esc_html__( 'Enable this to add all customer data (billing and shipping data, total number of orders and order value) into the data layer (WooCommerce 3.x required)', 'duracelltomi-google-tag-manager' ),
83
+ 'phase' => GTM4WP_PHASE_STABLE,
84
+ ),
85
+ GTM4WP_OPTION_INTEGRATE_WCORDERDATA => array(
86
+ 'label' => esc_html__( 'Order data in data layer', 'duracelltomi-google-tag-manager' ),
87
+ 'description' => esc_html__( 'Enable this to add all order attribute into the data layer on the order received page regardless and independently from classic and enhanced ecommerce tracking (WooCommerce 3.x required)', 'duracelltomi-google-tag-manager' ),
88
+ 'phase' => GTM4WP_PHASE_STABLE,
89
+ ),
90
+ GTM4WP_OPTION_INTEGRATE_WCEXCLUDETAX => array(
91
+ 'label' => esc_html__( 'Exclude tax from revenue', 'duracelltomi-google-tag-manager' ),
92
+ 'description' => esc_html__( 'Enable this to exclude tax from the revenue variable while generating the purchase data', 'duracelltomi-google-tag-manager' ),
93
+ 'phase' => GTM4WP_PHASE_STABLE,
94
+ ),
95
+ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE => array(
96
+ 'label' => esc_html__( 'Only track orders younger than', 'duracelltomi-google-tag-manager' ),
97
+ 'description' => esc_html__( 'To prevent duplicate transaction tracking at the order received page, enter the maximum age (in minutes) of the order or its payment for the transaction to be measured. Viewing the order received page of older orders will be ignored from transaction tracking, as it is considered to be a measured in an earlier session.', 'duracelltomi-google-tag-manager' ),
98
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
99
+ ),
100
+ GTM4WP_OPTION_INTEGRATE_WCEXCLUDESHIPPING => array(
101
+ 'label' => esc_html__( 'Exclude shipping from revenue', 'duracelltomi-google-tag-manager' ),
102
+ 'description' => esc_html__( 'Enable this to exclude shipping costs from the revenue variable while generating the purchase data', 'duracelltomi-google-tag-manager' ),
103
+ 'phase' => GTM4WP_PHASE_STABLE,
104
+ ),
105
+ GTM4WP_OPTION_INTEGRATE_WCREMARKETING => array(
106
+ 'label' => esc_html__( 'Google Ads Remarketing', 'duracelltomi-google-tag-manager' ),
107
+ 'description' => esc_html__( 'Enable this to add Google Ads dynamic remarketing variables to the dataLayer', 'duracelltomi-google-tag-manager' ),
108
+ 'phase' => GTM4WP_PHASE_DEPRECATED,
109
+ ),
110
+ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL => array(
111
+ 'label' => esc_html__( 'Google Ads Business Vertical', 'duracelltomi-google-tag-manager' ),
112
+ 'description' => sprintf(
113
+ gtm4wp_safe_admin_html(
114
+ // translators: 1: anchor element linking to GTM4WP setup guide for Google Ads dynamic remarketing. 2: closing anchor element.
115
+ __(
116
+ 'Select which vertical category to add next to each product to utilize dynamic remarketing for Google Ads.
117
+ <br />Use the plugin\'s %1$sofficial setup guide for dynamic remarketing%2$s
118
+ to setup your Google Tag Manager container.',
119
+ 'duracelltomi-google-tag-manager'
120
+ )
121
+ ),
122
+ '<a href="https://gtm4wp.com/how-to-articles/how-to-setup-dynamic-remarketing-in-google-ads-adwords" target="_blank" rel="noopener">',
123
+ '</a>'
124
+ ),
125
+ 'phase' => GTM4WP_PHASE_STABLE,
126
+ ),
127
+ GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX => array(
128
+ 'label' => esc_html__( 'Product ID prefix', 'duracelltomi-google-tag-manager' ),
129
+ 'description' => esc_html__( "Some product feed generator plugins prefix product IDs with a fixed text like 'woocommerce_gpf'. You can enter this prefix here so that tags in your website include this prefix as well.", 'duracelltomi-google-tag-manager' ),
130
+ 'phase' => GTM4WP_PHASE_STABLE,
131
+ ),
132
+ GTM4WP_OPTION_INTEGRATE_WCUSESKU => array(
133
+ 'label' => esc_html__( 'Use SKU instead of ID', 'duracelltomi-google-tag-manager' ),
134
+ 'description' => esc_html__( 'Check this to use product SKU instead of the ID of the products for remarketing and ecommerce tracking. Will fallback to ID if no SKU is set.', 'duracelltomi-google-tag-manager' ),
135
+ 'phase' => GTM4WP_PHASE_STABLE,
136
+ ),
137
+ GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG => array(
138
+ 'label' => esc_html__( 'Do not flag orders as being tracked', 'duracelltomi-google-tag-manager' ),
139
+ 'description' => gtm4wp_safe_admin_html(
140
+ __(
141
+ 'Turn this on to prevent the plugin to flag orders as being already tracked.<br /><br />
142
+ Leaving this unchecked ensures that no order data will be tracked multiple times
143
+ in any ad or measurement system.<br />
144
+ Please only turn this feature on if you really need it!',
145
+ 'duracelltomi-google-tag-manager'
146
+ )
147
+ ),
148
+ 'phase' => GTM4WP_PHASE_STABLE,
149
+ ),
150
+
151
+ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS => array(
152
+ 'label' => esc_html__( 'Google Optimize container ID list', 'duracelltomi-google-tag-manager' ),
153
+ 'description' => sprintf(
154
+ gtm4wp_safe_admin_html(
155
+ // translators: 1: opening anchor tag for a link to Google's documentation about the Google Optimize anti-flicker snippet. 2: Closing anchor tag.
156
+ __(
157
+ 'Enter a comma separated list of Google Optimize container IDs that you would like to use on your site.<br />
158
+ This plugin will add the %1$spage-hiding snippet%2$s to your pages.<br />',
159
+ 'duracelltomi-google-tag-manager'
160
+ )
161
+ ),
162
+ '<a href="https://developers.google.com/optimize/#the_page-hiding_snippet_code" target="_blank" rel="noopener">',
163
+ '</a>'
164
+ ) .
165
+ '<br /><span class="goid_validation_error">' .
166
+ esc_html__(
167
+ 'This does not seems to be a valid Google Optimize ID! Valid format: GTM-XXXXXX or OPT-XXXXXX where X can be numbers and
168
+ capital letters. Use comma without any space (,) to enter multpile IDs.',
169
+ 'duracelltomi-google-tag-manager'
170
+ ) .
171
+ '</span>',
172
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
173
+ ),
174
+ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT => array(
175
+ 'label' => esc_html__( 'Google Optimize page-hiding timeout', 'duracelltomi-google-tag-manager' ),
176
+ 'description' => esc_html__( 'Enter here the amount of time in milliseconds that the page-hiding snippet should wait before page content gets visible even if Google Optimize has not been completely loaded yet.', 'duracelltomi-google-tag-manager' ),
177
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
178
+ ),
179
+
180
+ GTM4WP_OPTION_INTEGRATE_AMPID => array(
181
+ 'label' => esc_html__( "Google Tag Manager 'AMP' Container ID", 'duracelltomi-google-tag-manager' ),
182
+ 'description' => sprintf(
183
+ // translators: 1: opening anchor tag for a link pointing to the official GTM help center article about the AMP container snippet 2: Closing anchor tag.
184
+ esc_html__(
185
+ 'Enter a comma separated list of Google Tag Manager container IDs that you would like to use on your site.
186
+ This plugin will add the %1$sAMP GTM snippet%2$s to your AMP pages.',
187
+ 'duracelltomi-google-tag-manager'
188
+ ),
189
+ '<a href="https://support.google.com/tagmanager/answer/6103696?hl=en" target="_blank" rel="noopener">',
190
+ '</a>'
191
+ ) .
192
+ '<br /><span class="ampid_validation_error">' .
193
+ esc_html__(
194
+ 'This does not seems to be a valid Google Tag Manager Container ID! Valid format: GTM-XXXXXX
195
+ where X can be numbers and capital letters. Use comma without any space (,) to enter multpile IDs.',
196
+ 'duracelltomi-google-tag-manager'
197
+ ) .
198
+ '</span>',
199
+ 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
200
+ 'plugintocheck' => 'amp/amp.php',
201
+ ),
202
+
203
+ GTM4WP_OPTION_INTEGRATE_COOKIEBOT => array(
204
+ 'label' => esc_html__( 'Cookiebot auto blocking', 'duracelltomi-google-tag-manager' ),
205
+ 'description' => sprintf(
206
+ // translators: 1: opening anchor tag linking to Cookiebot's documentation about the automatic cookie blocking feature. 2: Closing anchor tag.
207
+ esc_html__(
208
+ 'Enable this checkbox if you wish to use the %1$sautomatic cookie blocking mode of Cookiebot with Google Tag Manager%2$s.',
209
+ 'duracelltomi-google-tag-manager'
210
+ ),
211
+ '<a href="https://support.cookiebot.com/hc/en-us/articles/360009192739-Google-Tag-Manager-and-Automatic-cookie-blocking" target="_blank" rel="noopener">',
212
+ '</a>'
213
+ ),
214
+ 'phase' => GTM4WP_PHASE_STABLE,
215
+ ),
216
+ );
admin/admin-tab-scrolltracking.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GTM4WP options on the Scroll tracking tab.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ $GLOBALS['gtm4wp_scrollerfieldtexts'] = array(
12
+ GTM4WP_OPTION_SCROLLER_ENABLED => array(
13
+ 'label' => esc_html__( 'Enabled', 'duracelltomi-google-tag-manager' ),
14
+ 'description' => esc_html__( 'Enable scroll tracker script on your website.', 'duracelltomi-google-tag-manager' ),
15
+ 'phase' => GTM4WP_PHASE_STABLE,
16
+ ),
17
+ GTM4WP_OPTION_SCROLLER_DEBUGMODE => array(
18
+ 'label' => esc_html__( 'Debug mode', 'duracelltomi-google-tag-manager' ),
19
+ 'description' => esc_html__( 'Fire console.log() commands instead of dataLayer events.', 'duracelltomi-google-tag-manager' ),
20
+ 'phase' => GTM4WP_PHASE_STABLE,
21
+ ),
22
+ GTM4WP_OPTION_SCROLLER_CALLBACKTIME => array(
23
+ 'label' => esc_html__( 'Time delay before location check', 'duracelltomi-google-tag-manager' ),
24
+ 'description' => esc_html__( 'Enter the number of milliseconds after the script checks the current location. It prevents too many events being fired while scrolling.', 'duracelltomi-google-tag-manager' ),
25
+ 'phase' => GTM4WP_PHASE_STABLE,
26
+ ),
27
+ GTM4WP_OPTION_SCROLLER_DISTANCE => array(
28
+ 'label' => esc_html__( 'Minimum distance', 'duracelltomi-google-tag-manager' ),
29
+ 'description' => esc_html__( 'The minimum amount of pixels that a visitor has to scroll before we treat the move as scrolling.', 'duracelltomi-google-tag-manager' ),
30
+ 'phase' => GTM4WP_PHASE_STABLE,
31
+ ),
32
+ GTM4WP_OPTION_SCROLLER_CONTENTID => array(
33
+ 'label' => esc_html__( 'Content ID', 'duracelltomi-google-tag-manager' ),
34
+ 'description' => esc_html__( 'Enter the DOM ID of the content element in your template. Leave it empty for default(content). Do not include the # sign.', 'duracelltomi-google-tag-manager' ),
35
+ 'phase' => GTM4WP_PHASE_STABLE,
36
+ ),
37
+ GTM4WP_OPTION_SCROLLER_READERTIME => array(
38
+ 'label' => esc_html__( 'Scroller time', 'duracelltomi-google-tag-manager' ),
39
+ 'description' => esc_html__( 'Enter the number of seconds after the the scroller user is being treated as a reader, someone who really reads the content, not just scrolls through it.', 'duracelltomi-google-tag-manager' ),
40
+ 'phase' => GTM4WP_PHASE_STABLE,
41
+ ),
42
+ );
admin/admin.php CHANGED
@@ -1,1564 +1,1433 @@
1
- <?php
2
- define( 'GTM4WP_ADMINSLUG', 'gtm4wp-settings' );
3
- define( 'GTM4WP_ADMIN_GROUP', 'gtm4wp-admin-group' );
4
-
5
- define( 'GTM4WP_ADMIN_GROUP_GENERAL', 'gtm4wp-admin-group-general' );
6
- define( 'GTM4WP_ADMIN_GROUP_GTMID', 'gtm4wp-admin-group-gtm-id' );
7
- define( 'GTM4WP_ADMIN_GROUP_CONTAINERON', 'gtm4wp-admin-container-on' );
8
- define( 'GTM4WP_ADMIN_GROUP_COMPATMODE', 'gtm4wp-admin-compat-mode' );
9
- define( 'GTM4WP_ADMIN_GROUP_INFO', 'gtm4wp-admin-group-datalayer-info' );
10
-
11
- define( 'GTM4WP_ADMIN_GROUP_INCLUDES', 'gtm4wp-admin-group-includes' );
12
- define( 'GTM4WP_ADMIN_GROUP_EVENTS', 'gtm4wp-admin-group-events' );
13
- define( 'GTM4WP_ADMIN_GROUP_SCROLLER', 'gtm4wp-admin-group-scroller' );
14
- define( 'GTM4WP_ADMIN_GROUP_BLACKLIST', 'gtm4wp-admin-group-blacklist-tags' );
15
- define( 'GTM4WP_ADMIN_GROUP_INTEGRATION', 'gtm4wp-admin-group-integration' );
16
- define( 'GTM4WP_ADMIN_GROUP_ADVANCED', 'gtm4wp-admin-group-advanced' );
17
- define( 'GTM4WP_ADMIN_GROUP_CREDITS', 'gtm4wp-admin-group-credits' );
18
-
19
- define( 'GTM4WP_USER_NOTICES_KEY', 'gtm4wp_user_notices_dismisses' );
20
-
21
- define( 'GTM4WP_PHASE_STABLE', 'gtm4wp-phase-stable' );
22
- define( 'GTM4WP_PHASE_BETA', 'gtm4wp-phase-beta' );
23
- define( 'GTM4WP_PHASE_EXPERIMENTAL', 'gtm4wp-phase-experimental' );
24
- define( 'GTM4WP_PHASE_DEPRECATED', 'gtm4wp-phase-deprecated' );
25
-
26
- $GLOBALS['gtm4wp_def_user_notices_dismisses'] = array(
27
- 'enter-gtm-code' => false,
28
- 'wc-ga-plugin-warning' => false,
29
- 'wc-gayoast-plugin-warning' => false,
30
- 'php72-warning' => false,
31
- 'deprecated-warning' => false,
32
- );
33
-
34
- $GLOBALS['gtm4wp_includefieldtexts'] = array(
35
- GTM4WP_OPTION_INCLUDE_POSTTYPE => array(
36
- 'label' => __( 'Posttype of current post/archive', 'duracelltomi-google-tag-manager' ),
37
- 'description' => __( 'Check this option to include the type of the current post or archive page (post, page or any custom post type).', 'duracelltomi-google-tag-manager' ),
38
- 'phase' => GTM4WP_PHASE_STABLE,
39
- ),
40
- GTM4WP_OPTION_INCLUDE_CATEGORIES => array(
41
- 'label' => __( 'Category list of current post/archive', 'duracelltomi-google-tag-manager' ),
42
- 'description' => __( 'Check this option to include the category names of the current post or archive page', 'duracelltomi-google-tag-manager' ),
43
- 'phase' => GTM4WP_PHASE_STABLE,
44
- ),
45
- GTM4WP_OPTION_INCLUDE_TAGS => array(
46
- 'label' => __( 'Tags of current post', 'duracelltomi-google-tag-manager' ),
47
- 'description' => __( 'Check this option to include the tags of the current post.', 'duracelltomi-google-tag-manager' ),
48
- 'phase' => GTM4WP_PHASE_STABLE,
49
- ),
50
- GTM4WP_OPTION_INCLUDE_AUTHORID => array(
51
- 'label' => __( 'Post author ID', 'duracelltomi-google-tag-manager' ),
52
- 'description' => __( 'Check this option to include the ID of the author on the current post or author page.', 'duracelltomi-google-tag-manager' ),
53
- 'phase' => GTM4WP_PHASE_STABLE,
54
- ),
55
- GTM4WP_OPTION_INCLUDE_AUTHOR => array(
56
- 'label' => __( 'Post author name', 'duracelltomi-google-tag-manager' ),
57
- 'description' => __( 'Check this option to include the name of the author on the current post or author page.', 'duracelltomi-google-tag-manager' ),
58
- 'phase' => GTM4WP_PHASE_STABLE,
59
- ),
60
- GTM4WP_OPTION_INCLUDE_POSTDATE => array(
61
- 'label' => __( 'Post date', 'duracelltomi-google-tag-manager' ),
62
- 'description' => __( 'Check this option to include the date of the current post. This will include 4 dataLayer variables: full date, post year, post month, post date.', 'duracelltomi-google-tag-manager' ),
63
- 'phase' => GTM4WP_PHASE_STABLE,
64
- ),
65
- GTM4WP_OPTION_INCLUDE_POSTTITLE => array(
66
- 'label' => __( 'Post title', 'duracelltomi-google-tag-manager' ),
67
- 'description' => __( 'Check this option to include the title of the current post.', 'duracelltomi-google-tag-manager' ),
68
- 'phase' => GTM4WP_PHASE_STABLE,
69
- ),
70
- GTM4WP_OPTION_INCLUDE_POSTCOUNT => array(
71
- 'label' => __( 'Post count', 'duracelltomi-google-tag-manager' ),
72
- 'description' => __( 'Check this option to include the count of the posts currently shown on the page and the total number of posts in the category/tag/any taxonomy.', 'duracelltomi-google-tag-manager' ),
73
- 'phase' => GTM4WP_PHASE_STABLE,
74
- ),
75
- GTM4WP_OPTION_INCLUDE_POSTID => array(
76
- 'label' => __( 'Post ID', 'duracelltomi-google-tag-manager' ),
77
- 'description' => __( 'Check this option to include the post id.', 'duracelltomi-google-tag-manager' ),
78
- 'phase' => GTM4WP_PHASE_STABLE,
79
- ),
80
- GTM4WP_OPTION_INCLUDE_POSTFORMAT => array(
81
- 'label' => __( 'Post Format', 'duracelltomi-google-tag-manager' ),
82
- 'description' => __( 'Check this option to include the post format.', 'duracelltomi-google-tag-manager' ),
83
- 'phase' => GTM4WP_PHASE_STABLE,
84
- ),
85
- GTM4WP_OPTION_INCLUDE_POSTTERMLIST => array(
86
- "label" => __( "Post Terms", 'duracelltomi-google-tag-manager' ),
87
- "description" => __( "Check this option to include taxonomy values associated with a given post.", 'duracelltomi-google-tag-manager' ),
88
- "phase" => GTM4WP_PHASE_STABLE
89
- ),
90
- GTM4WP_OPTION_INCLUDE_SEARCHDATA => array(
91
- 'label' => __( 'Search data', 'duracelltomi-google-tag-manager' ),
92
- 'description' => __( 'Check this option to include the search term, referring page URL and number of results on the search page.', 'duracelltomi-google-tag-manager' ),
93
- 'phase' => GTM4WP_PHASE_STABLE,
94
- ),
95
- GTM4WP_OPTION_INCLUDE_LOGGEDIN => array(
96
- 'label' => __( 'Logged in status', 'duracelltomi-google-tag-manager' ),
97
- 'description' => __( 'Check this option to include whether there is a logged in user on your website.', 'duracelltomi-google-tag-manager' ),
98
- 'phase' => GTM4WP_PHASE_STABLE,
99
- ),
100
- GTM4WP_OPTION_INCLUDE_USERROLE => array(
101
- 'label' => __( 'Logged in user role', 'duracelltomi-google-tag-manager' ),
102
- 'description' => __( 'Check this option to include the role of the logged in user.', 'duracelltomi-google-tag-manager' ),
103
- 'phase' => GTM4WP_PHASE_STABLE,
104
- ),
105
- GTM4WP_OPTION_INCLUDE_USERID => array(
106
- 'label' => __( 'Logged in user ID', 'duracelltomi-google-tag-manager' ),
107
- 'description' => __( 'Check this option to include the ID of the logged in user.', 'duracelltomi-google-tag-manager' ),
108
- 'phase' => GTM4WP_PHASE_STABLE,
109
- ),
110
- GTM4WP_OPTION_INCLUDE_USERNAME => array(
111
- 'label' => __( 'Logged in user name', 'duracelltomi-google-tag-manager' ),
112
- 'description' => __( 'Check this option to include the username of the logged in user.', 'duracelltomi-google-tag-manager' ),
113
- 'phase' => GTM4WP_PHASE_STABLE,
114
- ),
115
- GTM4WP_OPTION_INCLUDE_USEREMAIL => array(
116
- 'label' => __( 'Logged in user email', 'duracelltomi-google-tag-manager' ),
117
- 'description' => __( 'Check this option to include the email address of the logged in user.', 'duracelltomi-google-tag-manager' ),
118
- 'phase' => GTM4WP_PHASE_STABLE,
119
- ),
120
- GTM4WP_OPTION_INCLUDE_USERREGDATE => array(
121
- 'label' => __( 'Logged in user creation date', 'duracelltomi-google-tag-manager' ),
122
- 'description' => __( 'Check this option to include the date of creation (registration) of the logged in user.', 'duracelltomi-google-tag-manager' ),
123
- 'phase' => GTM4WP_PHASE_STABLE,
124
- ),
125
- GTM4WP_OPTION_INCLUDE_VISITOR_IP => array(
126
- 'label' => __( 'Visitor IP', 'duracelltomi-google-tag-manager' ),
127
- 'description' => __( 'Check this option to include the IP address of the visitor. You might use this to filter internal traffic inside your GTM container. Please be aware that per GDPR its not allowed to transmit this full IP address to Google Analytics or to any other measurement system without explicit consent from the visitor.', 'duracelltomi-google-tag-manager' ),
128
- 'phase' => GTM4WP_PHASE_STABLE,
129
- ),
130
- GTM4WP_OPTION_INCLUDE_BROWSERDATA => array(
131
- 'label' => __( 'Browser data *', 'duracelltomi-google-tag-manager' ),
132
- 'description' => __( 'Check this option to include the name, version and engine data of the browser the visitor uses.', 'duracelltomi-google-tag-manager' )
133
- ),
134
- GTM4WP_OPTION_INCLUDE_OSDATA => array(
135
- 'label' => __( 'OS data *', 'duracelltomi-google-tag-manager' ),
136
- 'description' => __( 'Check this option to include the name and version of the operating system the visitor uses.', 'duracelltomi-google-tag-manager' )
137
- ),
138
- GTM4WP_OPTION_INCLUDE_DEVICEDATA => array(
139
- 'label' => __( 'Device data *', 'duracelltomi-google-tag-manager' ),
140
- 'description' => __( 'Check this option to include the type of device the user is currently using (desktop, tablet or mobile) including manufacturer and model data.', 'duracelltomi-google-tag-manager' )
141
- ),
142
- GTM4WP_OPTION_INCLUDE_MISCGEO => array(
143
- 'label' => __( 'Geo data', 'duracelltomi-google-tag-manager' ),
144
- 'description' => __( 'Add geo data (latitude, longitude, country, city, etc) of the current visitor (provided by ipstack.com)', 'duracelltomi-google-tag-manager' ),
145
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
146
- ),
147
- GTM4WP_OPTION_INCLUDE_MISCGEOAPI => array(
148
- 'label' => __( 'IPStack.com API key', 'duracelltomi-google-tag-manager' ),
149
- 'description' => sprintf( __( 'Enter your IPStack.com API key here. <a href="%s" target="_blank" rel="noopener">Get a free API key here</a>.', 'duracelltomi-google-tag-manager' ), 'https://ipstack.com/product?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress' ),
150
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
151
- ),
152
- GTM4WP_OPTION_INCLUDE_MISCGEOCF => array(
153
- 'label' => __( 'Cloudflare country code', 'duracelltomi-google-tag-manager' ),
154
- 'description' => __( 'Add the country code of the user provided by Cloudflare (if Cloudflare is used with your site)', 'duracelltomi-google-tag-manager' ),
155
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
156
- ),
157
- GTM4WP_OPTION_INCLUDE_WEATHER => array(
158
- 'label' => __( 'Weather data', 'duracelltomi-google-tag-manager' ),
159
- 'description' => sprintf(
160
- __(
161
- 'Check this option to include the current weather conditions around the current visitor.<br /><br />' .
162
- '<strong>Attention!</strong> This feature uses <a href="%s" target="_blank" rel="noopener">ipstack.com</a> and ' .
163
- '<a href="%s" target="_blank" rel="noopener">openweathermap.org</a> to collect data.<br />' .
164
- "Depending on your website's traffic, additional fees may apply!<br />" .
165
- 'This plugin caches weather data for 1 hour to lower the need to access those services.<br /><br />' .
166
- 'If you activate weather data, <strong>you will need</strong> to add an IPStack.com API key regardless of whether you ' .
167
- "activate the 'Geo data' option!",
168
- 'duracelltomi-google-tag-manager'
169
- ),
170
- 'https://ipstack.com/product?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress',
171
- 'http://openweathermap.org/price?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress'
172
- ),
173
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
174
- ),
175
- GTM4WP_OPTION_INCLUDE_WEATHERUNITS => array(
176
- 'label' => __( 'Weather data units', 'duracelltomi-google-tag-manager' ),
177
- 'description' => __( 'Select which temperature units you would like to use.', 'duracelltomi-google-tag-manager' ),
178
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
179
- ),
180
- GTM4WP_OPTION_INCLUDE_WEATHEROWMAPI => array(
181
- 'label' => __( 'OpenWeatherMap API key', 'duracelltomi-google-tag-manager' ),
182
- 'description' => sprintf( __( 'Enter your OpenWeatherMap API key here. <a href="%s" target="_blank" rel="noopener">Get a free API key here</a>.', 'duracelltomi-google-tag-manager' ), 'http://openweathermap.org/price?utm_source=gtm4wp&utm_medium=link&utm_campaign=gtm4wp-google-tag-manager-for-wordpress' ),
183
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
184
- ),
185
- GTM4WP_OPTION_INCLUDE_SITEID => array(
186
- 'label' => __( 'Site ID', 'duracelltomi-google-tag-manager' ),
187
- 'description' => __( 'ID of the current site in a WordPress Multisite environment', 'duracelltomi-google-tag-manager' ),
188
- 'phase' => GTM4WP_PHASE_STABLE,
189
- ),
190
- GTM4WP_OPTION_INCLUDE_SITENAME => array(
191
- 'label' => __( 'Site name', 'duracelltomi-google-tag-manager' ),
192
- 'description' => __( 'Name of the current site in a WordPress Multisite environment', 'duracelltomi-google-tag-manager' ),
193
- 'phase' => GTM4WP_PHASE_STABLE,
194
- ),
195
- );
196
-
197
- $GLOBALS['gtm4wp_eventfieldtexts'] = array(
198
- GTM4WP_OPTION_EVENTS_FORMMOVE => array(
199
- 'label' => __( 'Form fill events (gtm4wp.formElementEnter & gtm4wp.formElementLeave)', 'duracelltomi-google-tag-manager' ),
200
- 'description' => __( 'Check this option to include a Tag Manager event when a visitor moves between elements of a form (comment, contact, etc).', 'duracelltomi-google-tag-manager' ),
201
- 'phase' => GTM4WP_PHASE_STABLE,
202
- ),
203
- GTM4WP_OPTION_EVENTS_NEWUSERREG => array(
204
- 'label' => __( 'New user registration', 'duracelltomi-google-tag-manager' ),
205
- 'description' => __( 'Check this option to include a Tag Manager event when a new user registration has been completed on the frontend of your site (admin events not included)', 'duracelltomi-google-tag-manager' ),
206
- 'phase' => GTM4WP_PHASE_STABLE,
207
- ),
208
- GTM4WP_OPTION_EVENTS_USERLOGIN => array(
209
- 'label' => __( 'User logged in', 'duracelltomi-google-tag-manager' ),
210
- 'description' => __( 'Check this option to include a Tag Manager event when an existing user has been logged in on the frontend of your site (admin events not included)', 'duracelltomi-google-tag-manager' ),
211
- 'phase' => GTM4WP_PHASE_STABLE,
212
- ),
213
- GTM4WP_OPTION_EVENTS_YOUTUBE => array(
214
- 'label' => __( 'YouTube video events', 'duracelltomi-google-tag-manager' ),
215
- 'description' => __( 'Check this option to include a Tag Manager event when a visitor interacts with a YouTube video embeded on your site.', 'duracelltomi-google-tag-manager' ),
216
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
217
- ),
218
- GTM4WP_OPTION_EVENTS_VIMEO => array(
219
- 'label' => __( 'Vimeo video events', 'duracelltomi-google-tag-manager' ),
220
- 'description' => __( 'Check this option to include a Tag Manager event when a visitor interacts with a Vimeo video embeded on your site.', 'duracelltomi-google-tag-manager' ),
221
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
222
- ),
223
- GTM4WP_OPTION_EVENTS_SOUNDCLOUD => array(
224
- 'label' => __( 'Soundcloud events', 'duracelltomi-google-tag-manager' ),
225
- 'description' => __( 'Check this option to include a Tag Manager event when a visitor interacts with a Soundcloud media embeded on your site.', 'duracelltomi-google-tag-manager' ),
226
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
227
- )
228
- );
229
-
230
- $GLOBALS['gtm4wp_scrollerfieldtexts'] = array(
231
- GTM4WP_OPTION_SCROLLER_ENABLED => array(
232
- 'label' => __( 'Enabled', 'duracelltomi-google-tag-manager' ),
233
- 'description' => __( 'Enable scroll tracker script on your website.', 'duracelltomi-google-tag-manager' ),
234
- 'phase' => GTM4WP_PHASE_STABLE,
235
- ),
236
- GTM4WP_OPTION_SCROLLER_DEBUGMODE => array(
237
- 'label' => __( 'Debug mode', 'duracelltomi-google-tag-manager' ),
238
- 'description' => __( 'Fire console.log() commands instead of dataLayer events.', 'duracelltomi-google-tag-manager' ),
239
- 'phase' => GTM4WP_PHASE_STABLE,
240
- ),
241
- GTM4WP_OPTION_SCROLLER_CALLBACKTIME => array(
242
- 'label' => __( 'Time delay before location check', 'duracelltomi-google-tag-manager' ),
243
- 'description' => __( 'Enter the number of milliseconds after the script checks the current location. It prevents too many events being fired while scrolling.', 'duracelltomi-google-tag-manager' ),
244
- 'phase' => GTM4WP_PHASE_STABLE,
245
- ),
246
- GTM4WP_OPTION_SCROLLER_DISTANCE => array(
247
- 'label' => __( 'Minimum distance', 'duracelltomi-google-tag-manager' ),
248
- 'description' => __( 'The minimum amount of pixels that a visitor has to scroll before we treat the move as scrolling.', 'duracelltomi-google-tag-manager' ),
249
- 'phase' => GTM4WP_PHASE_STABLE,
250
- ),
251
- GTM4WP_OPTION_SCROLLER_CONTENTID => array(
252
- 'label' => __( 'Content ID', 'duracelltomi-google-tag-manager' ),
253
- 'description' => __( 'Enter the DOM ID of the content element in your template. Leave it empty for default(content). Do not include the # sign.', 'duracelltomi-google-tag-manager' ),
254
- 'phase' => GTM4WP_PHASE_STABLE,
255
- ),
256
- GTM4WP_OPTION_SCROLLER_READERTIME => array(
257
- 'label' => __( 'Scroller time', 'duracelltomi-google-tag-manager' ),
258
- 'description' => __( 'Enter the number of seconds after the the scroller user is being treated as a reader, someone who really reads the content, not just scrolls through it.', 'duracelltomi-google-tag-manager' ),
259
- 'phase' => GTM4WP_PHASE_STABLE,
260
- ),
261
- );
262
-
263
- $GLOBALS['gtm4wp_integratefieldtexts'] = array(
264
- GTM4WP_OPTION_INTEGRATE_WPCF7 => array(
265
- 'label' => __( 'Contact Form 7', 'duracelltomi-google-tag-manager' ),
266
- 'description' => __( 'Check this to fire dataLayer events after Contact Form 7 submissions (supported events: invalid input, spam detected, form submitted, form submitted and mail sent, form submitted and mail send failed).', 'duracelltomi-google-tag-manager' ),
267
- 'phase' => GTM4WP_PHASE_STABLE,
268
- 'plugintocheck' => 'contact-form-7/wp-contact-form-7.php',
269
- ),
270
- GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC => array(
271
- 'label' => __( 'Track classic e-commerce', 'duracelltomi-google-tag-manager' ),
272
- 'description' => __( 'This feature is deprecated and will be removed soon! You should upgrade to enhanced ecommerce as soon as possible.', 'duracelltomi-google-tag-manager' ),
273
- 'phase' => GTM4WP_PHASE_DEPRECATED
274
- ),
275
- GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC => array(
276
- 'label' => __( 'Track enhanced e-commerce', 'duracelltomi-google-tag-manager' ),
277
- 'description' => sprintf(
278
- __(
279
- 'Choose this option if you would like to track e-commerce data using '.
280
- '<a href="%s" target="_blank" rel="noopener">enhanced ecommerce tracking</a>.<br>'.
281
- 'Use the plugin\'s official guides to setup your Google Tag Manager container:<br>'.
282
- '<ul><li><a href="%s" target="_blank" rel="noopener">Google Analytics 3 / Universal Analytics setup</a></li>'.
283
- '<li><a href="%s" target="_blank" rel="noopener">Google Analytics 4 setup</a></li></ul>'
284
- , 'duracelltomi-google-tag-manager' )
285
- , 'https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce'
286
- , 'https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking'
287
- , 'https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking-google-analytics-4-ga4-version' ),
288
- 'phase' => GTM4WP_PHASE_STABLE,
289
- 'plugintocheck' => 'woocommerce/woocommerce.php',
290
- ),
291
- GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION => array(
292
- 'label' => __( 'Products per impression', 'duracelltomi-google-tag-manager' ),
293
- 'description' => __(
294
- 'If you have many products shown on product category pages and/or on your site home, you could miss pageviews in Google Analytics due to the ' .
295
- 'amount of data that is needed to be sent. To prevent this, you can split product impression data into multiple Google Analytics events by ' .
296
- 'entering a number here (minimum 10-15 recommended) and adding gtm4wp.productImpressionEEC into your Google Analytics ecommerce event helper ' .
297
- "tag's trigger.<br /><br />Leave this value 0 to include product impression data in your pageview hit.",
298
- 'duracelltomi-google-tag-manager'
299
- ),
300
- 'phase' => GTM4WP_PHASE_STABLE
301
- ),
302
- GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP => array(
303
- 'label' => __( 'Cart as 1st checkout step', 'duracelltomi-google-tag-manager' ),
304
- 'description' => __( 'Enable this to track the cart page as the first checkout step in enhanced ecommerce instead of the checkout page itself', 'duracelltomi-google-tag-manager' ),
305
- 'phase' => GTM4WP_PHASE_STABLE
306
- ),
307
- GTM4WP_OPTION_INTEGRATE_WCEINCLUDECARTINDL => array(
308
- "label" => __( "Cart content in data layer", 'duracelltomi-google-tag-manager' ),
309
- "description" => __( "Enable this option to include the content of the cart in the data layer on each page. Needs WooCommerce v3.2 or newer. Especially useful for site personalization with Google Optimize.", 'duracelltomi-google-tag-manager' ),
310
- "phase" => GTM4WP_PHASE_STABLE
311
- ),
312
- GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH => array(
313
- 'label' => __( 'Include full category path.', 'duracelltomi-google-tag-manager' ),
314
- 'description' => __( 'Check this to inclulde the full category path of each product in enhanced ecommerce tracking. WARNING! This can lead to performance issues on large sites with lots of traffic!', 'duracelltomi-google-tag-manager' ),
315
- 'phase' => GTM4WP_PHASE_STABLE
316
- ),
317
- GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY => array(
318
- "label" => __( "Taxonomy to be used for product brands", 'duracelltomi-google-tag-manager' ),
319
- "description" => __( "Select which custom taxonomy is being used to add the brand of products", 'duracelltomi-google-tag-manager' ),
320
- "phase" => GTM4WP_PHASE_STABLE
321
- ),
322
- GTM4WP_OPTION_INTEGRATE_WCCUSTOMERDATA => array(
323
- 'label' => __( 'Customer data in data layer', 'duracelltomi-google-tag-manager' ),
324
- 'description' => __( 'Enable this to add all customer data (billing and shipping data, total number of orders and order value) into the data layer (WooCommerce 3.x required)', 'duracelltomi-google-tag-manager' ),
325
- 'phase' => GTM4WP_PHASE_STABLE
326
- ),
327
- GTM4WP_OPTION_INTEGRATE_WCORDERDATA => array(
328
- 'label' => __( 'Order data in data layer', 'duracelltomi-google-tag-manager' ),
329
- 'description' => __( 'Enable this to add all order attribute into the data layer on the order received page regardless and independently from classic and enhanced ecommerce tracking (WooCommerce 3.x required)', 'duracelltomi-google-tag-manager' ),
330
- 'phase' => GTM4WP_PHASE_STABLE
331
- ),
332
- GTM4WP_OPTION_INTEGRATE_WCEXCLUDETAX => array(
333
- 'label' => __( 'Exclude tax from revenue', 'duracelltomi-google-tag-manager' ),
334
- 'description' => __( 'Enable this to exclude tax from the revenue variable while generating the purchase data', 'duracelltomi-google-tag-manager' ),
335
- 'phase' => GTM4WP_PHASE_STABLE
336
- ), GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE => array(
337
- 'label' => __( 'Only track orders younger than', 'duracelltomi-google-tag-manager' ),
338
- 'description' => __( 'To prevent duplicate transaction tracking at the order received page, enter the maximum age (in minutes) of the order or its payment for the transaction to be measured. Viewing the order received page of older orders will be ignored from transaction tracking, as it is considered to be a measured in an earlier session.', 'duracelltomi-google-tag-manager' ),
339
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL
340
- ),
341
- GTM4WP_OPTION_INTEGRATE_WCEXCLUDESHIPPING => array(
342
- 'label' => __( 'Exclude shipping from revenue', 'duracelltomi-google-tag-manager' ),
343
- 'description' => __( 'Enable this to exclude shipping costs from the revenue variable while generating the purchase data', 'duracelltomi-google-tag-manager' ),
344
- 'phase' => GTM4WP_PHASE_STABLE
345
- ),
346
- GTM4WP_OPTION_INTEGRATE_WCREMARKETING => array(
347
- 'label' => __( 'Google Ads Remarketing', 'duracelltomi-google-tag-manager' ),
348
- 'description' => __( 'Enable this to add Google Ads dynamic remarketing variables to the dataLayer', 'duracelltomi-google-tag-manager' ),
349
- 'phase' => GTM4WP_PHASE_DEPRECATED
350
- ),
351
- GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL => array(
352
- 'label' => __( 'Google Ads Business Vertical', 'duracelltomi-google-tag-manager' ),
353
- 'description' => sprintf(
354
- __(
355
- 'Select which vertical category to add next to each product to utilize dynamic remarketing for Google Ads.'.
356
- '<br>Use the plugin\'s <a href="%s" target="_blank" rel="noopener">official setup guide for dynamic remarketing</a> '.
357
- 'to setup your Google Tag Manager container.'
358
- , 'duracelltomi-google-tag-manager' )
359
- , 'https://gtm4wp.com/how-to-articles/how-to-setup-dynamic-remarketing-in-google-ads-adwords'),
360
- 'phase' => GTM4WP_PHASE_STABLE
361
- ),
362
- GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX => array(
363
- 'label' => __( 'Product ID prefix', 'duracelltomi-google-tag-manager' ),
364
- 'description' => __( "Some product feed generator plugins prefix product IDs with a fixed text like 'woocommerce_gpf'. You can enter this prefix here so that tags in your website include this prefix as well.", 'duracelltomi-google-tag-manager' ),
365
- 'phase' => GTM4WP_PHASE_STABLE
366
- ),
367
- GTM4WP_OPTION_INTEGRATE_WCUSESKU => array(
368
- 'label' => __( 'Use SKU instead of ID', 'duracelltomi-google-tag-manager' ),
369
- 'description' => __( 'Check this to use product SKU instead of the ID of the products for remarketing and ecommerce tracking. Will fallback to ID if no SKU is set.', 'duracelltomi-google-tag-manager' ),
370
- 'phase' => GTM4WP_PHASE_STABLE
371
- ),
372
- GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG => array(
373
- 'label' => __( 'Do not flag orders as being tracked', 'duracelltomi-google-tag-manager' ),
374
- 'description' => __(
375
- 'Turn this on to prevent the plugin to flag orders as being already tracked.<br /><br />'.
376
- 'Leaving this unchecked ensures that no order data will be tracked multiple times '.
377
- 'in any ad or measurement system.<br />'.
378
- 'Please only turn this feature on if you really need it!',
379
- 'duracelltomi-google-tag-manager'
380
- ),
381
- 'phase' => GTM4WP_PHASE_STABLE
382
- ),
383
-
384
- GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS => array(
385
- 'label' => __( 'Google Optimize container ID list', 'duracelltomi-google-tag-manager' ),
386
- 'description' => sprintf(
387
- __(
388
- 'Enter a comma separated list of Google Optimize container IDs that you would like to use on your site.<br />' .
389
- 'This plugin will add the <a href="%s">page-hiding snippet</a> to your pages.<br />',
390
- 'duracelltomi-google-tag-manager'
391
- ),
392
- 'https://developers.google.com/optimize/#the_page-hiding_snippet_code'
393
- ) .
394
- '<br /><span class="goid_validation_error">' . __( 'This does not seems to be a valid Google Optimize ID! Valid format: GTM-XXXXXX or OPT-XXXXXX where X can be numbers and capital letters. Use comma without any space (,) to enter multpile IDs.', 'duracelltomi-google-tag-manager' ) . '</span>',
395
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
396
- ),
397
- GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT => array(
398
- 'label' => __( 'Google Optimize page-hiding timeout', 'duracelltomi-google-tag-manager' ),
399
- 'description' => __( 'Enter here the amount of time in milliseconds that the page-hiding snippet should wait before page content gets visible even if Google Optimize has not been completely loaded yet.', 'duracelltomi-google-tag-manager' ),
400
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
401
- ),
402
-
403
- GTM4WP_OPTION_INTEGRATE_AMPID => array(
404
- 'label' => __( "Google Tag Manager 'AMP' Container ID", 'duracelltomi-google-tag-manager' ),
405
- 'description' => sprintf( __( 'Enter a comma separated list of Google Tag Manager container IDs that you would like to use on your site. This plugin will add the <a href="%s">AMP GTM snippet</a> to your AMP pages.', 'duracelltomi-google-tag-manager' ), 'https://support.google.com/tagmanager/answer/6103696?hl=en' ) .
406
- '<br /><span class="ampid_validation_error">' . __( 'This does not seems to be a valid Google Tag Manager Container ID! Valid format: GTM-XXXXXX where X can be numbers and capital letters. Use comma without any space (,) to enter multpile IDs.', 'duracelltomi-google-tag-manager' ) . '</span>',
407
- 'phase' => GTM4WP_PHASE_EXPERIMENTAL,
408
- 'plugintocheck' => 'amp/amp.php',
409
- ),
410
-
411
- GTM4WP_OPTION_INTEGRATE_COOKIEBOT => array(
412
- 'label' => __( 'Cookiebot auto blocking', 'duracelltomi-google-tag-manager' ),
413
- 'description' => sprintf( __( 'Enable this checkbox if you wish to use the <a href="%s" target="_blank" rel="noopener">automatic cookie blocking mode of Cookiebot with Google Tag Manager</a>.', 'duracelltomi-google-tag-manager' ), 'https://support.cookiebot.com/hc/en-us/articles/360009192739-Google-Tag-Manager-and-Automatic-cookie-blocking' ),
414
- 'phase' => GTM4WP_PHASE_STABLE
415
- ),
416
- );
417
-
418
- $GLOBALS['gtm4wp_advancedfieldtexts'] = array(
419
- GTM4WP_OPTION_DATALAYER_NAME => array(
420
- 'label' => __( 'dataLayer variable name', 'duracelltomi-google-tag-manager' ),
421
- 'description' => __( 'In some cases you need to rename the dataLayer variable. You can enter your name here. Leave black for default name: dataLayer', 'duracelltomi-google-tag-manager' ),
422
- 'phase' => GTM4WP_PHASE_STABLE,
423
- ),
424
- GTM4WP_OPTION_ENV_GTM_AUTH => array(
425
- 'label' => __( 'Environment gtm_auth parameter', 'duracelltomi-google-tag-manager' ),
426
- 'description' => __( 'Enter the gtm_auth parameter of the Google Tag Manager environment that has to be activated on this site. Both gtm_auth and gtm_preview parameters are required to activate the desired environment.', 'duracelltomi-google-tag-manager' ),
427
- 'phase' => GTM4WP_PHASE_STABLE,
428
- ),
429
- GTM4WP_OPTION_ENV_GTM_PREVIEW => array(
430
- 'label' => __( 'Environment gtm_preview parameter', 'duracelltomi-google-tag-manager' ),
431
- 'description' => __( 'Enter the gtm_auth parameter of the Google Tag Manager environment that has to be activated on this site. Both gtm_auth and gtm_preview parameters are required to activate the desired environment.', 'duracelltomi-google-tag-manager' ),
432
- 'phase' => GTM4WP_PHASE_STABLE,
433
- ),
434
- GTM4WP_OPTION_DONOTTRACK => array(
435
- 'label' => __( "Include browser 'Do not track' setting", 'duracelltomi-google-tag-manager' ),
436
- 'description' => __( 'Add into the data layer whether the user has asked not to track any website interaction. You may want to respect this and disable all tags if this variable is set in the data layer.', 'duracelltomi-google-tag-manager' ),
437
- 'phase' => GTM4WP_PHASE_STABLE,
438
- ),
439
- GTM4WP_OPTION_LOADEARLY => array(
440
- 'label' => __( 'Load GTM container as early as possible', 'duracelltomi-google-tag-manager' ),
441
- 'description' => __( "Turning on this option will load your Google Tag Manager container as early as possible during page load. This can cause issues if you are using jQuery in your custom HTML tags that fire on 'Page View' events.", 'duracelltomi-google-tag-manager' ),
442
- 'phase' => GTM4WP_PHASE_STABLE,
443
- ),
444
- GTM4WP_OPTION_GTMDOMAIN => array(
445
- 'label' => __( 'Container domain name', 'duracelltomi-google-tag-manager' ),
446
- 'description' => __( "Enter your custom domain name if you are using a server side GTM container for tracking. Do not include https:// prefix. Leave this blank to use www.googletagmanager.com", 'duracelltomi-google-tag-manager' ),
447
- 'phase' => GTM4WP_PHASE_STABLE,
448
- ),
449
- GTM4WP_OPTION_NOGTMFORLOGGEDIN => array(
450
- 'label' => __( 'User roles to exclude', 'duracelltomi-google-tag-manager' ),
451
- 'description' => __( "Do not load GTM container on the frontend if role of the logged in user is any of this", 'duracelltomi-google-tag-manager' ),
452
- 'phase' => GTM4WP_PHASE_STABLE,
453
- )
454
- );
455
-
456
- function gtm4wp_admin_output_section( $args ) {
457
- echo '<span class="tabinfo">';
458
-
459
- switch ( $args['id'] ) {
460
- case GTM4WP_ADMIN_GROUP_GENERAL: {
461
- _e( 'This plugin is intended to be used by IT girls&guys and marketing staff. Please be sure you read the <a href="https://developers.google.com/tag-manager/" target="_blank" rel="noopener">Google Tag Manager Help Center</a> before you start using this plugin.<br /><br />', 'duracelltomi-google-tag-manager' );
462
-
463
- break;
464
- }
465
-
466
- case GTM4WP_ADMIN_GROUP_INCLUDES: {
467
- _e( 'Here you can check what data is needed to be included in the dataLayer to be able to access them in Google Tag Manager', 'duracelltomi-google-tag-manager' );
468
- echo '<br />';
469
- printf( __( '* Browser, OS and Device data is provided using <a href="%s">WhichBrowser</a> library.', 'duracelltomi-google-tag-manager' ), 'http://whichbrowser.net/' );
470
-
471
- break;
472
- }
473
-
474
- case GTM4WP_ADMIN_GROUP_EVENTS: {
475
- _e( 'Fire tags in Google Tag Manager on special events on your website', 'duracelltomi-google-tag-manager' );
476
-
477
- break;
478
- }
479
-
480
- case GTM4WP_ADMIN_GROUP_SCROLLER: {
481
- _e( 'Fire tags based on how the visitor scrolls through your page.', 'duracelltomi-google-tag-manager' );
482
- echo '<br />';
483
- printf( __( 'Based on the script originaly posted to <a href="%s">Analytics Talk</a>', 'duracelltomi-google-tag-manager' ), 'http://cutroni.com/blog/2012/02/21/advanced-content-tracking-with-google-analytics-part-1/' );
484
-
485
- break;
486
- }
487
-
488
- case GTM4WP_ADMIN_GROUP_BLACKLIST: {
489
- _e( 'Here you can control which types of tags, triggers and variables can be executed on your site regardless of what tags are included in your container on the Google Tag Manager site. Use this to increase security!', 'duracelltomi-google-tag-manager' );
490
- echo '<br />';
491
- _e( 'Do not modify if you do not know what to do, since it can cause issues with your tag deployment!', 'duracelltomi-google-tag-manager' );
492
- echo '<br />';
493
- _e( 'For example blacklisting everything and only whitelisting the Google Analytics tag without whitelisting the URL variable type will cause your Google Analytics tags to be blocked anyway since the attached triggers (Page View) can not fire!', 'duracelltomi-google-tag-manager' );
494
-
495
- break;
496
- }
497
-
498
- case GTM4WP_ADMIN_GROUP_INTEGRATION: {
499
- _e( 'Google Tag Manager for WordPress can integrate with several popular plugins. Please check the plugins you would like to integrate with:', 'duracelltomi-google-tag-manager' );
500
-
501
- break;
502
- }
503
-
504
- case GTM4WP_ADMIN_GROUP_ADVANCED: {
505
- _e( 'You usually do not need to modify thoose settings. Please be carefull while hacking here.', 'duracelltomi-google-tag-manager' );
506
-
507
- break;
508
- }
509
-
510
- case GTM4WP_ADMIN_GROUP_CREDITS: {
511
- _e( 'Some info about the author of this plugin', 'duracelltomi-google-tag-manager' );
512
-
513
- break;
514
- }
515
- } // end switch
516
-
517
- echo '</span>';
518
- }
519
-
520
- function gtm4wp_admin_output_field( $args ) {
521
- global $gtm4wp_options, $gtm4wp_business_verticals;
522
-
523
- switch ( $args['label_for'] ) {
524
- case GTM4WP_ADMIN_GROUP_GTMID: {
525
- if ( defined( 'GTM4WP_HARDCODED_GTM_ID' ) ) {
526
- $_gtm_id_value = GTM4WP_HARDCODED_GTM_ID;
527
- $_input_readonly = ' readonly="readonly"';
528
- $_warning_after = '<br /><span class="gtm_wpconfig_set">WARNING! Container ID was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the container ID or remove the GTM4WP_HARDCODED_GTM_ID constant!</span>';
529
- } else {
530
- $_gtm_id_value = $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ];
531
- $_input_readonly = '';
532
- $_warning_after = '';
533
- }
534
-
535
- echo '<input type="text" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']" value="' . $_gtm_id_value . '" ' . $_input_readonly . '/><br />' . $args['description'];
536
- echo $_warning_after;
537
- echo '<br /><span class="gtmid_validation_error">' . __( 'This does not seems to be a valid Google Tag Manager ID! Valid format: GTM-XXXXX where X can be numbers and capital letters. Use comma without any space (,) to enter multpile container IDs.', 'duracelltomi-google-tag-manager' ) . '</span>';
538
-
539
- break;
540
- }
541
-
542
- case GTM4WP_ADMIN_GROUP_CONTAINERON: {
543
- echo $args['description'].'<br/><br/>';
544
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[container-on]_1" name="' . GTM4WP_OPTIONS . '[container-on]" value="1" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] != GTM4WP_PLACEMENT_OFF ? 'checked="checked"' : '' ) . '/> ' . __( 'On', 'duracelltomi-google-tag-manager' ) . '<br />';
545
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[container-on]_0" name="' . GTM4WP_OPTIONS . '[container-on]" value="0" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_OFF ? 'checked="checked"' : '' ) . '/> ' . __( 'Off', 'duracelltomi-google-tag-manager' ) . '<br />';
546
-
547
- break;
548
- }
549
-
550
- case GTM4WP_ADMIN_GROUP_COMPATMODE: {
551
- echo $args['description'].'<br/><br/>';
552
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_BODYOPEN_AUTO . '" name="' . GTM4WP_OPTIONS . '[compat-mode]" value="' . GTM4WP_PLACEMENT_BODYOPEN_AUTO . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_BODYOPEN_AUTO || $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_OFF ? 'checked="checked"' : '' ) . '/> ' . __( 'Off (no tweak, right placement)', 'duracelltomi-google-tag-manager' ) . '<br />';
553
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_FOOTER . '" name="' . GTM4WP_OPTIONS . '[compat-mode]" value="' . GTM4WP_PLACEMENT_FOOTER . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_FOOTER ? 'checked="checked"' : '' ) . '/> ' . __( 'Footer of the page (not recommended by Google, Search Console verification will not work)', 'duracelltomi-google-tag-manager' ) . '<br />';
554
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_BODYOPEN . '" name="' . GTM4WP_OPTIONS . '[compat-mode]" value="' . GTM4WP_PLACEMENT_BODYOPEN . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_BODYOPEN ? 'checked="checked"' : '' ) . '/> ' . __( 'Manually coded (needs tweak in your template)', 'duracelltomi-google-tag-manager' ) . '<br />';
555
-
556
- break;
557
- }
558
-
559
- case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']': {
560
- echo '<input type="text" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']" value="' . $gtm4wp_options[ GTM4WP_OPTION_DATALAYER_NAME ] . '" /><br />' . $args['description'];
561
- echo '<br /><span class="datalayername_validation_error">' . __( 'This does not seems to be a valid JavaScript variable name! Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
562
-
563
- break;
564
- }
565
-
566
- case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']': {
567
- if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ) {
568
- $_gtm_auth_value = GTM4WP_HARDCODED_GTM_ENV_AUTH;
569
- $_input_readonly = ' readonly="readonly"';
570
- $_warning_after = '<br /><span class="gtm_wpconfig_set">WARNING! Environment auth parameter was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the parameter value or remove the GTM4WP_HARDCODED_GTM_ENV_AUTH constant!</span>';
571
- } else {
572
- $_gtm_auth_value = $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ];
573
- $_input_readonly = '';
574
- $_warning_after = '';
575
- }
576
-
577
- echo '<input type="text" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']" value="' . $_gtm_auth_value . '" ' . $_input_readonly . '/><br />' . $args['description'];
578
- echo $_warning_after;
579
- echo '<br /><span class="gtmauth_validation_error">' . __( 'This does not seems to be a valid gtm_auth parameter! It should only contain letters, number and the &quot;-&quot; character. Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
580
-
581
- break;
582
- }
583
-
584
- case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']': {
585
- if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' ) ) {
586
- $_gtm_preview_value = GTM4WP_HARDCODED_GTM_ENV_PREVIEW;
587
- $_input_readonly = ' readonly="readonly"';
588
- $_warning_after = '<br /><span class="gtm_wpconfig_set">WARNING! Environment preview parameter was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the parameter value or remove the GTM4WP_HARDCODED_GTM_ENV_PREVIEW constant!</span>';
589
- } else {
590
- $_gtm_preview_value = $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ];
591
- $_input_readonly = '';
592
- $_warning_after = '';
593
- }
594
-
595
- echo '<input type="text" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']" value="' . $_gtm_preview_value . '" ' . $_input_readonly . '/><br />' . $args['description'];
596
- echo $_warning_after;
597
- echo '<br /><span class="gtmpreview_validation_error">' . __( 'This does not seems to be a valid gtm_preview parameter! It should have the format &quot;env-NN&quot; where NN is an integer number. Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
598
-
599
- break;
600
- }
601
-
602
- case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']': {
603
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_0" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']" value="0" ' . ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] == 0 ? 'checked="checked"' : '' ) . '/> ' . __( 'Disable feature: control everything on Google Tag Manager interface', 'duracelltomi-google-tag-manager' ) . '<br />';
604
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_1" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']" value="1" ' . ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] == 1 ? 'checked="checked"' : '' ) . '/> ' . __( 'Allow all, except the checked items on all blacklist tabs (blacklist)', 'duracelltomi-google-tag-manager' ) . '<br />';
605
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_2" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']" value="2" ' . ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] == 2 ? 'checked="checked"' : '' ) . '/> ' . __( 'Block all, except the checked items on all blacklist tabs (whitelist)', 'duracelltomi-google-tag-manager' ) . '<br />';
606
- echo $args['description'];
607
-
608
- break;
609
- }
610
-
611
- case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']': {
612
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']_0" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']" value="0" ' . ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] == 0 ? 'checked="checked"' : '' ) . '/> ' . __( 'Celsius', 'duracelltomi-google-tag-manager' ) . '<br />';
613
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']_1" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']" value="1" ' . ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] == 1 ? 'checked="checked"' : '' ) . '/> ' . __( 'Fahrenheit', 'duracelltomi-google-tag-manager' ) . '<br />';
614
- echo $args['description'];
615
-
616
- break;
617
- }
618
-
619
- case GTM4WP_ADMIN_GROUP_INFO: {
620
- echo $args['description'];
621
-
622
- break;
623
- }
624
-
625
- case GTM4WP_OPTIONS . "[" . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . "]": {
626
- echo '<select id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . ']">';
627
- echo '<option value="">(not set)</option>';
628
-
629
- $gtm4wp_taxonomies = get_taxonomies(array(
630
- "show_ui" => true,
631
- "public" => true,
632
- "_builtin" => false
633
- ), "object", "and");
634
-
635
- foreach( $gtm4wp_taxonomies as $onetaxonomy ) {
636
- echo '<option value="' . $onetaxonomy->name . '"' . ( $gtm4wp_options[GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY] === $onetaxonomy->name ? ' selected="selected"' : '' ) . '>' . $onetaxonomy->label . '</option>';
637
- }
638
-
639
- echo '</select>';
640
-
641
- break;
642
- }
643
-
644
- case GTM4WP_OPTIONS . "[" . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . "]": {
645
- echo '<select id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . ']" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . ']">';
646
-
647
- foreach( $gtm4wp_business_verticals as $vertical_id => $vertical_display_name ) {
648
- echo '<option value="' . $vertical_id . '"' . ( $gtm4wp_options[GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL] === $vertical_id ? ' selected="selected"' : '' ) . '>' . $vertical_display_name . '</option>';
649
- }
650
-
651
- echo '</select><br>' . $args['description'];
652
-
653
- break;
654
- }
655
-
656
- case GTM4WP_OPTIONS . "[" . GTM4WP_OPTION_NOGTMFORLOGGEDIN . "]": {
657
- $roles = get_editable_roles();
658
-
659
- echo $args['description'].'<br/><br/>';
660
-
661
- $saved_roles = explode(",", $gtm4wp_options[GTM4WP_OPTION_NOGTMFORLOGGEDIN]);
662
-
663
- foreach($roles as $role_id => $role_info) {
664
- $role_name = translate_user_role( $role_info['name'] );
665
- echo '<input type="checkbox" id="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']_' . $role_id . '" name="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . '][]" value="' . $role_id . '"' . ( in_array( $role_id, $saved_roles ) ? ' checked="checked"' : '' ) . '><label for="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']_' . $role_id . '">' . $role_name . '</label><br/>';
666
- }
667
-
668
- break;
669
- }
670
-
671
- default: {
672
- if ( preg_match( '/' . GTM4WP_OPTIONS . "\\[blacklist\\-[^\\]]+\\]/i", $args['label_for'] ) ) {
673
- if ( 'blacklist-sandboxed' == $args['entityid'] ) {
674
- echo '<input type="checkbox" id="' . $args['label_for'] . '" name="' . $args['label_for'] . '" value="1" ' . checked( 1, $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_SANDBOXED ], false ) . ' /><br />' . $args['description'];
675
- } else {
676
- echo '<input type="checkbox" id="' . $args['label_for'] . '" name="' . $args['label_for'] . '" value="1" ' . checked( 1, in_array( $args['entityid'], $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] ), false ) . ' /><br />' . $args['description'];
677
- }
678
- } else {
679
- $optval = $gtm4wp_options[ $args['optionfieldid'] ];
680
-
681
- switch ( gettype( $optval ) ) {
682
- case 'boolean': {
683
- echo '<input type="checkbox" id="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" name="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" value="1" ' . checked( 1, $optval, false ) . ' /><br />' . $args['description'];
684
-
685
- if ( isset( $args['plugintocheck'] ) && ( $args['plugintocheck'] != '' ) ) {
686
- if ( is_plugin_active( $args['plugintocheck'] ) ) {
687
- echo '<br />' . __( 'This plugin is <strong class="gtm4wp-plugin-active">active</strong>, it is strongly recommended to enable this integration!', 'duracelltomi-google-tag-manager' );
688
- } else {
689
- echo '<br />' . __( 'This plugin (' . $args['plugintocheck'] . ') is <strong class="gtm4wp-plugin-not-active">not active</strong>, enabling this integration could cause issues on frontend!', 'duracelltomi-google-tag-manager' );
690
- }
691
- }
692
-
693
- break;
694
- }
695
-
696
- case 'integer': {
697
- echo '<input type="number" step="1" min="0" class="small-text" id="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" name="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" value="' . esc_attr( $optval ) . '" /><br />' . $args['description'];
698
-
699
- if ( isset( $args['plugintocheck'] ) && ( $args['plugintocheck'] != '' ) ) {
700
- if ( is_plugin_active( $args['plugintocheck'] ) ) {
701
- echo '<br />' . __( 'This plugin is <strong class="gtm4wp-plugin-active">active</strong>, it is strongly recommended to enable this integration!', 'duracelltomi-google-tag-manager' );
702
- } else {
703
- echo '<br />' . __( 'This plugin is <strong class="gtm4wp-plugin-not-active">not active</strong>, enabling this integration could cause issues on frontend!', 'duracelltomi-google-tag-manager' );
704
- }
705
- }
706
-
707
- break;
708
- }
709
-
710
- default : {
711
- echo '<input type="text" id="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" name="' . GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']" value="' . esc_attr( $optval ) . '" size="80" /><br />' . $args['description'];
712
-
713
- if ( isset( $args['plugintocheck'] ) && ( $args['plugintocheck'] != '' ) ) {
714
- if ( is_plugin_active( $args['plugintocheck'] ) ) {
715
- echo '<br />' . __( 'This plugin is <strong class="gtm4wp-plugin-active">active</strong>, it is strongly recommended to enable this integration!', 'duracelltomi-google-tag-manager' );
716
- } else {
717
- echo '<br />' . __( 'This plugin is <strong class="gtm4wp-plugin-not-active">not active</strong>, enabling this integration could cause issues on frontend!', 'duracelltomi-google-tag-manager' );
718
- }
719
- }
720
- }
721
- } // end switch gettype optval
722
- }
723
- }
724
- } // end switch
725
- }
726
-
727
- function gtm4wp_sanitize_options( $options ) {
728
- global $wpdb, $gtm4wp_entity_ids;
729
-
730
- $output = gtm4wp_reload_options();
731
-
732
- foreach ( $output as $optionname => $optionvalue ) {
733
- if ( isset( $options[ $optionname ] ) ) {
734
- $newoptionvalue = $options[ $optionname ];
735
- } else {
736
- $newoptionvalue = '';
737
- }
738
-
739
- // "include" settings
740
- if ( substr( $optionname, 0, 8 ) == 'include-' ) {
741
- $output[ $optionname ] = (bool) $newoptionvalue;
742
-
743
- // dataLayer events
744
- } elseif ( substr( $optionname, 0, 6 ) == 'event-' ) {
745
- $output[ $optionname ] = (bool) $newoptionvalue;
746
-
747
- // clear oembed transients when feature is enabled because we need to hook into the oembed process to enable some 3rd party APIs
748
- if ( $output[ $optionname ] && ! $optionvalue ) {
749
- if ( GTM4WP_OPTION_EVENTS_YOUTUBE == $optionname ) {
750
- $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%youtube.com%' AND meta_key LIKE '_oembed_%'" );
751
- }
752
-
753
- if ( GTM4WP_OPTION_EVENTS_VIMEO == $optionname ) {
754
- $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%vimeo.com%' AND meta_key LIKE '_oembed_%'" );
755
- }
756
- }
757
-
758
- // blacklist / whitelist entities
759
- } elseif ( substr( $optionname, 0, 10 ) == 'blacklist-' ) {
760
- if ( GTM4WP_OPTION_BLACKLIST_ENABLE === $optionname ) {
761
- $output[ $optionname ] = (int) $options[ GTM4WP_OPTION_BLACKLIST_ENABLE ];
762
- } else if ( GTM4WP_OPTION_BLACKLIST_SANDBOXED == $optionname ) {
763
- $output[ $optionname ] = (bool) $newoptionvalue;
764
- } else if ( GTM4WP_OPTION_BLACKLIST_STATUS == $optionname ) {
765
- $selected_blacklist_entities = array();
766
-
767
- foreach( $gtm4wp_entity_ids as $gtm_entity_group_id => $gtm_entity_group_list ) {
768
- foreach( $gtm_entity_group_list as $gtm_entity_id => $gtm_entity_label ) {
769
- $entity_option_id = 'blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id;
770
- if ( array_key_exists( $entity_option_id, $options ) ) {
771
- $newoptionvalue = (bool) $options[ $entity_option_id ];
772
- if ( $newoptionvalue ) {
773
- $selected_blacklist_entities[] = $gtm_entity_id;
774
- }
775
- }
776
- }
777
- }
778
-
779
- $output[ $optionname ] = implode( ',', $selected_blacklist_entities );
780
- }
781
-
782
- // Google Optimize settings
783
- } elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS ) {
784
- $_goid_val = trim( $newoptionvalue );
785
- if ( '' == $_goid_val ) {
786
- $_goid_list = array();
787
- } else {
788
- $_goid_list = explode( ',', $_goid_val );
789
- }
790
- $_goid_haserror = false;
791
-
792
- foreach ( $_goid_list as $one_go_id ) {
793
- $_goid_haserror = $_goid_haserror || ! preg_match( '/^(GTM|OPT)-[A-Z0-9]+$/', $one_go_id );
794
- }
795
-
796
- if ( $_goid_haserror && ( count( $_goid_list ) > 0 ) ) {
797
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS . ']', __( 'Invalid Google Optimize ID. Valid ID format: GTM-XXXXX or OPT-XXXXX. Use comma without additional space (,) to enter more than one ID.', 'duracelltomi-google-tag-manager' ) );
798
- } else {
799
- $output[ $optionname ] = $newoptionvalue;
800
- }
801
- } elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT ) {
802
- $output[ $optionname ] = (int) $newoptionvalue;
803
-
804
- } elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION ) {
805
- $output[ $optionname ] = (int) $newoptionvalue;
806
-
807
- } elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ) {
808
- $output[ $optionname ] = (int) $newoptionvalue;
809
-
810
- }elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX ) {
811
- $output[ $optionname ] = trim( (string) $newoptionvalue );
812
-
813
- } else if ( $optionname == GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ) {
814
- $output[$optionname] = trim( (string) $newoptionvalue );
815
-
816
- } else if ( $optionname == GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ) {
817
- $output[$optionname] = trim( (string) $newoptionvalue );
818
-
819
- } else if ( $optionname == GTM4WP_OPTION_GTMDOMAIN ) {
820
- // for PHP 7- compatibility
821
- if ( !defined("FILTER_FLAG_HOSTNAME") ) {
822
- define( "FILTER_FLAG_HOSTNAME", 0 );
823
- }
824
-
825
- // remove https:// prefix if used
826
- $newoptionvalue = str_replace( 'https://', '', $newoptionvalue );
827
-
828
- $newoptionvalue = filter_var( $newoptionvalue, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME );
829
- if ( $newoptionvalue === false ) {
830
- $newoptionvalue = '';
831
- }
832
- $output[$optionname] = trim( (string) $newoptionvalue );
833
-
834
- // Accelerated Mobile Pages settings
835
- } elseif ( $optionname == GTM4WP_OPTION_INTEGRATE_AMPID ) {
836
- $_ampid_val = trim( $newoptionvalue );
837
- if ( '' == $_ampid_val ) {
838
- $_ampid_list = array();
839
- } else {
840
- $_ampid_list = explode( ',', $_ampid_val );
841
- }
842
- $_ampid_haserror = false;
843
-
844
- foreach ( $_ampid_list as $one_amp_id ) {
845
- $_ampid_haserror = $_ampid_haserror || ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_amp_id );
846
- }
847
-
848
- if ( $_ampid_haserror && ( count( $_ampid_list ) > 0 ) ) {
849
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_AMPID . ']', __( 'Invalid AMP Google Tag Manager Container ID. Valid ID format: GTM-XXXXX. Use comma without additional space (,) to enter more than one ID.', 'duracelltomi-google-tag-manager' ) );
850
- } else {
851
- $output[ $optionname ] = $newoptionvalue;
852
- }
853
-
854
- // integrations
855
- } elseif ( substr( $optionname, 0, 10 ) == 'integrate-' ) {
856
- $output[ $optionname ] = (bool) $newoptionvalue;
857
-
858
- // GTM code or dataLayer variable name
859
- } elseif ( ( $optionname == GTM4WP_OPTION_GTM_CODE ) || ( $optionname == GTM4WP_OPTION_DATALAYER_NAME ) || ( $optionname == GTM4WP_OPTION_ENV_GTM_AUTH ) || ( $optionname == GTM4WP_OPTION_ENV_GTM_PREVIEW ) ) {
860
- $newoptionvalue = trim( $newoptionvalue );
861
-
862
- if ( $optionname == GTM4WP_OPTION_GTM_CODE ) {
863
- $_gtmid_list = explode( ',', $newoptionvalue );
864
- $_gtmid_haserror = false;
865
-
866
- foreach ( $_gtmid_list as $one_gtm_id ) {
867
- $_gtmid_haserror = $_gtmid_haserror || ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_gtm_id );
868
- }
869
-
870
- if ( $_gtmid_haserror ) {
871
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']', __( 'Invalid Google Tag Manager ID. Valid ID format: GTM-XXXXX. Use comma without additional space (,) to enter more than one container ID.', 'duracelltomi-google-tag-manager' ) );
872
- } else {
873
- $output[ $optionname ] = $newoptionvalue;
874
- }
875
- } elseif ( ( $optionname == GTM4WP_OPTION_DATALAYER_NAME ) && ( $newoptionvalue != '' ) && ( ! preg_match( '/^[a-zA-Z][a-zA-Z0-9_-]*$/', $newoptionvalue ) ) ) {
876
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']', __( "Invalid dataLayer variable name. Please start with a character from a-z or A-Z followed by characters from a-z, A-Z, 0-9 or '_' or '-'!", 'duracelltomi-google-tag-manager' ) );
877
-
878
- } elseif ( ( $optionname == GTM4WP_OPTION_ENV_GTM_AUTH ) && ( $newoptionvalue != '' ) && ( ! preg_match( '/^[a-zA-Z0-9-_]+$/', $newoptionvalue ) ) ) {
879
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']', __( "Invalid gtm_auth environment parameter value. It should only contain letters, numbers or the '-' and '_' characters.", 'duracelltomi-google-tag-manager' ) );
880
-
881
- } elseif ( ( $optionname == GTM4WP_OPTION_ENV_GTM_PREVIEW ) && ( $newoptionvalue != '' ) && ( ! preg_match( '/^env-[0-9]+$/', $newoptionvalue ) ) ) {
882
- add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']', __( "Invalid gtm_preview environment parameter value. It should have the format 'env-NN' where NN is an integer number.", 'duracelltomi-google-tag-manager' ) );
883
-
884
- } else {
885
- $output[ $optionname ] = $newoptionvalue;
886
- }
887
-
888
- // GTM container ON/OFF + compat mode
889
- } elseif ( $optionname == GTM4WP_OPTION_GTM_PLACEMENT ) {
890
- $container_on_off = (bool) $options['container-on'];
891
- $container_compat = (int) $options['compat-mode'];
892
-
893
- if ( !$container_on_off ) {
894
- $output[ $optionname ] = GTM4WP_PLACEMENT_OFF;
895
- } else {
896
- if ( ( $container_compat < 0 ) || ( $container_compat > 2 ) ) {
897
- $container_compat = 2;
898
- }
899
-
900
- $output[ $optionname ] = $container_compat;
901
- }
902
-
903
- // scroll tracking content ID
904
- } elseif ( $optionname == GTM4WP_OPTION_SCROLLER_CONTENTID ) {
905
- $output[ $optionname ] = trim( str_replace( '#', '', $newoptionvalue ) );
906
-
907
- // do not output GTM container code for specific user roles
908
- } elseif ( $optionname == GTM4WP_OPTION_NOGTMFORLOGGEDIN ) {
909
- if ( is_array( $newoptionvalue ) ) {
910
- $output[ $optionname ] = implode(",", $newoptionvalue );
911
- } else {
912
- $output[ $optionname ] = '';
913
- }
914
-
915
- // anything else
916
- } else {
917
- switch ( gettype( $optionvalue ) ) {
918
- case 'boolean': {
919
- $output[ $optionname ] = (bool) $newoptionvalue;
920
-
921
- break;
922
- }
923
-
924
- case 'integer': {
925
- $output[ $optionname ] = (int) $newoptionvalue;
926
-
927
- break;
928
- }
929
-
930
- default: {
931
- $output[ $optionname ] = $newoptionvalue;
932
- }
933
- } // end switch
934
- }
935
- }
936
-
937
- return $output;
938
- }
939
-
940
- function gtm4wp_admin_init() {
941
- global $gtm4wp_includefieldtexts, $gtm4wp_eventfieldtexts, $gtm4wp_integratefieldtexts, $gtm4wp_scrollerfieldtexts,
942
- $gtm4wp_advancedfieldtexts, $gtm4wp_entity_ids;
943
-
944
- register_setting( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS, 'gtm4wp_sanitize_options' );
945
-
946
- add_settings_section(
947
- GTM4WP_ADMIN_GROUP_GENERAL,
948
- __( 'General', 'duracelltomi-google-tag-manager' ),
949
- 'gtm4wp_admin_output_section',
950
- GTM4WP_ADMINSLUG
951
- );
952
-
953
- add_settings_field(
954
- GTM4WP_ADMIN_GROUP_GTMID,
955
- __( 'Google Tag Manager ID', 'duracelltomi-google-tag-manager' ),
956
- 'gtm4wp_admin_output_field',
957
- GTM4WP_ADMINSLUG,
958
- GTM4WP_ADMIN_GROUP_GENERAL,
959
- array(
960
- 'label_for' => GTM4WP_ADMIN_GROUP_GTMID,
961
- 'description' => __( 'Enter your Google Tag Manager ID here. Use comma without space (,) to enter multiple IDs.', 'duracelltomi-google-tag-manager' ),
962
- )
963
- );
964
-
965
- add_settings_field(
966
- GTM4WP_ADMIN_GROUP_CONTAINERON,
967
- __( 'Container code ON/OFF', 'duracelltomi-google-tag-manager' ),
968
- 'gtm4wp_admin_output_field',
969
- GTM4WP_ADMINSLUG,
970
- GTM4WP_ADMIN_GROUP_GENERAL,
971
- array(
972
- 'label_for' => GTM4WP_ADMIN_GROUP_CONTAINERON,
973
- 'description' => __( "Turning OFF the Google Tag Manager container itself will remove both the head and the body part of the container code but leave data layer codes working.<br/>This should be only used in specific cases where you need to place the container code manually or using another tool.", 'duracelltomi-google-tag-manager' ),
974
- )
975
- );
976
-
977
- add_settings_field(
978
- GTM4WP_ADMIN_GROUP_COMPATMODE,
979
- __( 'Container code compatibility mode', 'duracelltomi-google-tag-manager' ),
980
- 'gtm4wp_admin_output_field',
981
- GTM4WP_ADMINSLUG,
982
- GTM4WP_ADMIN_GROUP_GENERAL,
983
- array(
984
- 'label_for' => GTM4WP_ADMIN_GROUP_COMPATMODE,
985
- 'description' => __(
986
- 'Compatibility mode decides where to put the second, so called <code>&lt;noscript&gt;</code> or <code>&lt;iframe&gt;</code> part of the GTM container code.<br />'.
987
- 'This code is usually only executed if your visitor has disabled JavaScript for some reason.<br/>'.
988
- 'It is also mandatory in order to verify your site in Google Search Console using the GTM method.<br/>'.
989
- 'The main GTM container code will be placed into the <code>&lt;head&gt;</code> section of your webpages anyway (where it belongs to).<br/><br/>'.
990
- 'If you select "Manually coded", you need to edit your template files and add the following line just after the opening <code>&lt;body&gt;</code> tag:<br />'.
991
- "<code>&lt;?php if ( function_exists( 'gtm4wp_the_gtm_tag' ) ) { gtm4wp_the_gtm_tag(); } ?&gt;</code>",
992
- 'duracelltomi-google-tag-manager'
993
- ),
994
- )
995
- );
996
-
997
- add_settings_section(
998
- GTM4WP_ADMIN_GROUP_INCLUDES,
999
- __( 'Basic data', 'duracelltomi-google-tag-manager' ),
1000
- 'gtm4wp_admin_output_section',
1001
- GTM4WP_ADMINSLUG
1002
- );
1003
-
1004
- foreach ( $gtm4wp_includefieldtexts as $fieldid => $fielddata ) {
1005
- $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
1006
-
1007
- add_settings_field(
1008
- 'gtm4wp-admin-' . $fieldid . '-id',
1009
- $fielddata['label'] . '<span class="' . $phase . '"></span>',
1010
- 'gtm4wp_admin_output_field',
1011
- GTM4WP_ADMINSLUG,
1012
- GTM4WP_ADMIN_GROUP_INCLUDES,
1013
- array(
1014
- 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
1015
- 'description' => $fielddata['description'],
1016
- 'optionfieldid' => $fieldid,
1017
- )
1018
- );
1019
- }
1020
-
1021
- add_settings_section(
1022
- GTM4WP_ADMIN_GROUP_EVENTS,
1023
- __( 'Events', 'duracelltomi-google-tag-manager' ),
1024
- 'gtm4wp_admin_output_section',
1025
- GTM4WP_ADMINSLUG
1026
- );
1027
-
1028
- foreach ( $gtm4wp_eventfieldtexts as $fieldid => $fielddata ) {
1029
- $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
1030
-
1031
- add_settings_field(
1032
- 'gtm4wp-admin-' . $fieldid . '-id',
1033
- $fielddata['label'] . '<span class="' . $phase . '"></span>',
1034
- 'gtm4wp_admin_output_field',
1035
- GTM4WP_ADMINSLUG,
1036
- GTM4WP_ADMIN_GROUP_EVENTS,
1037
- array(
1038
- 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
1039
- 'description' => $fielddata['description'],
1040
- 'optionfieldid' => $fieldid,
1041
- )
1042
- );
1043
- }
1044
-
1045
- add_settings_section(
1046
- GTM4WP_ADMIN_GROUP_SCROLLER,
1047
- __( 'Scroll tracking', 'duracelltomi-google-tag-manager' ),
1048
- 'gtm4wp_admin_output_section',
1049
- GTM4WP_ADMINSLUG
1050
- );
1051
-
1052
- foreach ( $gtm4wp_scrollerfieldtexts as $fieldid => $fielddata ) {
1053
- $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
1054
-
1055
- add_settings_field(
1056
- 'gtm4wp-admin-' . $fieldid . '-id',
1057
- $fielddata['label'] . '<span class="' . $phase . '"></span>',
1058
- 'gtm4wp_admin_output_field',
1059
- GTM4WP_ADMINSLUG,
1060
- GTM4WP_ADMIN_GROUP_SCROLLER,
1061
- array(
1062
- 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
1063
- 'description' => $fielddata['description'],
1064
- 'optionfieldid' => $fieldid,
1065
- )
1066
- );
1067
- }
1068
-
1069
- add_settings_section(
1070
- GTM4WP_ADMIN_GROUP_BLACKLIST,
1071
- __( 'Security', 'duracelltomi-google-tag-manager' ),
1072
- 'gtm4wp_admin_output_section',
1073
- GTM4WP_ADMINSLUG
1074
- );
1075
-
1076
- add_settings_field(
1077
- GTM4WP_OPTION_BLACKLIST_ENABLE,
1078
- __( 'Enable blacklist/whitelist', 'duracelltomi-google-tag-manager' ),
1079
- 'gtm4wp_admin_output_field',
1080
- GTM4WP_ADMINSLUG,
1081
- GTM4WP_ADMIN_GROUP_BLACKLIST,
1082
- array(
1083
- 'label_for' => GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']',
1084
- 'description' => '',
1085
- 'optionsfieldid' => GTM4WP_OPTION_BLACKLIST_ENABLE
1086
- )
1087
- );
1088
-
1089
- add_settings_field(
1090
- GTM4WP_OPTION_BLACKLIST_SANDBOXED,
1091
- __( 'Custom tag/variable templates', 'duracelltomi-google-tag-manager' ),
1092
- 'gtm4wp_admin_output_field',
1093
- GTM4WP_ADMINSLUG,
1094
- GTM4WP_ADMIN_GROUP_BLACKLIST,
1095
- array(
1096
- 'label_for' => GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_SANDBOXED . ']',
1097
- 'description' => '',
1098
- 'entityid' => GTM4WP_OPTION_BLACKLIST_SANDBOXED
1099
- )
1100
- );
1101
-
1102
- foreach( $gtm4wp_entity_ids as $gtm_entity_group_id => $gtm_entity_group_list ) {
1103
- foreach( $gtm_entity_group_list as $gtm_entity_id => $gtm_entity_label ) {
1104
- add_settings_field(
1105
- 'gtm4wp-admin-blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id . '-id',
1106
- $gtm_entity_label,
1107
- 'gtm4wp_admin_output_field',
1108
- GTM4WP_ADMINSLUG,
1109
- GTM4WP_ADMIN_GROUP_BLACKLIST,
1110
- array(
1111
- 'label_for' => 'gtm4wp-options[blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id . ']',
1112
- 'description' => '',
1113
- 'entityid' => $gtm_entity_id,
1114
- )
1115
- );
1116
- }
1117
- }
1118
-
1119
- add_settings_section(
1120
- GTM4WP_ADMIN_GROUP_INTEGRATION,
1121
- __( 'Integration', 'duracelltomi-google-tag-manager' ),
1122
- 'gtm4wp_admin_output_section',
1123
- GTM4WP_ADMINSLUG
1124
- );
1125
-
1126
- foreach ( $gtm4wp_integratefieldtexts as $fieldid => $fielddata ) {
1127
- $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
1128
-
1129
- add_settings_field(
1130
- 'gtm4wp-admin-' . $fieldid . '-id',
1131
- $fielddata['label'] . '<span class="' . $phase . '"></span>',
1132
- 'gtm4wp_admin_output_field',
1133
- GTM4WP_ADMINSLUG,
1134
- GTM4WP_ADMIN_GROUP_INTEGRATION,
1135
- array(
1136
- 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
1137
- 'description' => $fielddata['description'],
1138
- 'optionfieldid' => $fieldid,
1139
- 'plugintocheck' => isset( $fielddata['plugintocheck'] ) ? $fielddata['plugintocheck'] : '',
1140
- )
1141
- );
1142
- }
1143
-
1144
- add_settings_section(
1145
- GTM4WP_ADMIN_GROUP_ADVANCED,
1146
- __( 'Advanced', 'duracelltomi-google-tag-manager' ),
1147
- 'gtm4wp_admin_output_section',
1148
- GTM4WP_ADMINSLUG
1149
- );
1150
-
1151
- foreach ( $gtm4wp_advancedfieldtexts as $fieldid => $fielddata ) {
1152
- $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
1153
-
1154
- add_settings_field(
1155
- 'gtm4wp-admin-' . $fieldid . '-id',
1156
- $fielddata['label'] . '<span class="' . $phase . '"></span>',
1157
- 'gtm4wp_admin_output_field',
1158
- GTM4WP_ADMINSLUG,
1159
- GTM4WP_ADMIN_GROUP_ADVANCED,
1160
- array(
1161
- 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
1162
- 'description' => $fielddata['description'],
1163
- 'optionfieldid' => $fieldid,
1164
- 'plugintocheck' => isset( $fielddata['plugintocheck'] ) ? $fielddata['plugintocheck'] : '',
1165
- )
1166
- );
1167
- }
1168
-
1169
- add_settings_section(
1170
- GTM4WP_ADMIN_GROUP_CREDITS,
1171
- __( 'Credits', 'duracelltomi-google-tag-manager' ),
1172
- 'gtm4wp_admin_output_section',
1173
- GTM4WP_ADMINSLUG
1174
- );
1175
-
1176
- add_settings_field(
1177
- GTM4WP_ADMIN_GROUP_INFO,
1178
- __( 'Author', 'duracelltomi-google-tag-manager' ),
1179
- 'gtm4wp_admin_output_field',
1180
- GTM4WP_ADMINSLUG,
1181
- GTM4WP_ADMIN_GROUP_CREDITS,
1182
- array(
1183
- 'label_for' => GTM4WP_ADMIN_GROUP_INFO,
1184
- 'description' => '<strong>Thomas Geiger</strong><br />
1185
- Website: <a href="https://gtm4wp.com/" target="_blank" rel="noopener">gtm4wp.com</a><br />
1186
- <a href="https://www.linkedin.com/in/duracelltomi" target="_blank" rel="noopener">Me on LinkedIn</a><br />
1187
- <a href="http://www.linkedin.com/company/jabjab-online-marketing-ltd" target="_blank" rel="noopener">JabJab Online Marketing on LinkedIn</a>',
1188
- )
1189
- );
1190
-
1191
- // apply oembed code changes on the admin as well since the oembed call on the admin is cached by WordPress into a transient
1192
- // that is applied on the frontend later
1193
- require_once dirname( __FILE__ ) . '/../integration/youtube.php';
1194
- require_once dirname( __FILE__ ) . '/../integration/vimeo.php';
1195
- require_once dirname( __FILE__ ) . '/../integration/soundcloud.php';
1196
- }
1197
-
1198
- function gtm4wp_show_admin_page() {
1199
- global $gtp4wp_plugin_url;
1200
- ?>
1201
- <div class="wrap">
1202
- <div id="gtm4wp-icon" class="icon32" style="background-image: url(<?php echo $gtp4wp_plugin_url; ?>admin/images/tag_manager-32.png);"><br /></div>
1203
- <h2><?php _e( 'Google Tag Manager for WordPress options', 'duracelltomi-google-tag-manager' ); ?></h2>
1204
- <form action="options.php" method="post">
1205
- <?php settings_fields( GTM4WP_ADMIN_GROUP ); ?>
1206
- <?php do_settings_sections( GTM4WP_ADMINSLUG ); ?>
1207
- <?php submit_button(); ?>
1208
-
1209
- </form>
1210
- </div>
1211
- <?php
1212
- }
1213
-
1214
- function gtm4wp_add_admin_page() {
1215
- add_options_page(
1216
- __( 'Google Tag Manager for WordPress settings', 'duracelltomi-google-tag-manager' ),
1217
- __( 'Google Tag Manager', 'duracelltomi-google-tag-manager' ),
1218
- 'manage_options',
1219
- GTM4WP_ADMINSLUG,
1220
- 'gtm4wp_show_admin_page'
1221
- );
1222
- }
1223
-
1224
- function gtm4wp_add_admin_js( $hook ) {
1225
- global $gtp4wp_plugin_url;
1226
-
1227
- if ( $hook == 'settings_page_' . GTM4WP_ADMINSLUG ) {
1228
- wp_register_script( 'admin-subtabs', $gtp4wp_plugin_url . 'js/admin-subtabs.js', array(), GTM4WP_VERSION );
1229
-
1230
- $subtabtexts = array(
1231
- 'posttabtitle' => __( 'Posts', 'duracelltomi-google-tag-manager' ),
1232
- 'searchtabtitle' => __( 'Search', 'duracelltomi-google-tag-manager' ),
1233
- 'visitortabtitle' => __( 'Visitors', 'duracelltomi-google-tag-manager' ),
1234
- 'browsertabtitle' => __( 'Browser/OS/Device', 'duracelltomi-google-tag-manager' ),
1235
- 'blocktagstabtitle' => __( 'Blacklist tags', 'duracelltomi-google-tag-manager' ),
1236
- 'blocktriggerstabtitle' => __( 'Blacklist triggers', 'duracelltomi-google-tag-manager' ),
1237
- 'blockmacrostabtitle' => __( 'Blacklist variables', 'duracelltomi-google-tag-manager' ),
1238
- 'wpcf7tabtitle' => __( 'Contact Form 7', 'duracelltomi-google-tag-manager' ),
1239
- 'wctabtitle' => __( 'WooCommerce', 'duracelltomi-google-tag-manager' ),
1240
- 'gotabtitle' => __( 'Google Optimize', 'duracelltomi-google-tag-manager' ),
1241
- 'amptabtitle' => __( 'Accelerated Mobile Pages', 'duracelltomi-google-tag-manager' ),
1242
- 'cookiebottabtitle' => __( 'Cookiebot', 'duracelltomi-google-tag-manager' ),
1243
- 'weathertabtitle' => __( 'Weather & geo data', 'duracelltomi-google-tag-manager' ),
1244
- 'generaleventstabtitle' => __( 'General events', 'duracelltomi-google-tag-manager' ),
1245
- 'mediaeventstabtitle' => __( 'Media events', 'duracelltomi-google-tag-manager' ),
1246
- 'depecratedeventstabtitle' => __( 'Deprecated', 'duracelltomi-google-tag-manager' ),
1247
- 'sitetabtitle' => __( 'Site', 'duracelltomi-google-tag-manager' ),
1248
- 'misctabtitle' => __( 'Misc', 'duracelltomi-google-tag-manager' ),
1249
- );
1250
- wp_localize_script( 'admin-subtabs', 'gtm4wp', $subtabtexts );
1251
-
1252
- wp_enqueue_script( 'admin-subtabs' );
1253
- wp_enqueue_script( 'admin-tabcreator', $gtp4wp_plugin_url . 'js/admin-tabcreator.js', array( 'jquery-core' ), GTM4WP_VERSION );
1254
-
1255
- wp_enqueue_style( 'gtm4wp-validate', $gtp4wp_plugin_url . 'css/admin-gtm4wp.css', array(), GTM4WP_VERSION );
1256
- }
1257
- }
1258
-
1259
- function gtm4wp_admin_head() {
1260
- echo '
1261
- <style type="text/css">
1262
- .gtmid_validation_error,
1263
- .goid_validation_error,
1264
- .goid_ga_validation_error,
1265
- .ampid_validation_error,
1266
- .datalayername_validation_error,
1267
- .gtmauth_validation_error,
1268
- .gtmpreview_validation_error,
1269
- .gtm_wpconfig_set {
1270
- color: #c00;
1271
- font-weight: bold;
1272
- }
1273
- .gtmid_validation_error,
1274
- .goid_validation_error,
1275
- .goid_ga_validation_error,
1276
- .ampid_validation_error,
1277
- .datalayername_validation_error,
1278
- .gtmauth_validation_error,
1279
- .gtmpreview_validation_error {
1280
- display: none;
1281
- }
1282
- </style>
1283
- <script type="text/javascript">
1284
- jQuery(function() {
1285
- jQuery( "#gtm4wp-options\\\\[gtm-code\\\\]" )
1286
- .on( "blur", function() {
1287
- var gtmid_regex = /^GTM-[A-Z0-9]+$/;
1288
- var gtmid_list_str = jQuery( this ).val();
1289
- if ( typeof gtmid_list_str != "string" ) {
1290
- return;
1291
- }
1292
- var gtmid_list = trim( gtmid_list_str ).split( "," );
1293
-
1294
- var gtmid_haserror = false;
1295
- for( var i=0; i<gtmid_list.length; i++ ) {
1296
- gtmid_haserror = gtmid_haserror || !gtmid_regex.test( gtmid_list[ i ] );
1297
- }
1298
-
1299
- if ( gtmid_haserror ) {
1300
- jQuery( ".gtmid_validation_error" )
1301
- .show();
1302
- } else {
1303
- jQuery( ".gtmid_validation_error" )
1304
- .hide();
1305
- }
1306
- });
1307
-
1308
- jQuery( "#gtm4wp-options\\\\[integrate-google-optimize-idlist\\\\]" )
1309
- .on( "blur", function() {
1310
- var goid_regex = /^(GTM|OPT)-[A-Z0-9]+$/;
1311
- var goid_val_str = jQuery( this ).val();
1312
- if ( typeof goid_val_str != "string" ) {
1313
- return;
1314
- }
1315
- var goid_val = trim( goid_val_str );
1316
- if ( "" == goid_val ) {
1317
- goid_list = [];
1318
- } else {
1319
- var goid_list = goid_val.split( "," );
1320
- }
1321
-
1322
- var goid_haserror = false;
1323
- for( var i=0; i<goid_list.length; i++ ) {
1324
- goid_haserror = goid_haserror || !goid_regex.test( goid_list[ i ] );
1325
- }
1326
-
1327
- if ( goid_haserror && (goid_list.length > 0) ) {
1328
- jQuery( ".goid_validation_error" )
1329
- .show();
1330
- } else {
1331
- jQuery( ".goid_validation_error" )
1332
- .hide();
1333
- }
1334
- });
1335
-
1336
- jQuery( "#gtm4wp-options\\\\[integrate-google-optimize-gaid\\\\]" )
1337
- .on( "blur", function() {
1338
- var gogaid_regex = /^UA-[0-9]+-[0-9]+$/;
1339
- var gogaid_val_str = jQuery( this ).val();
1340
- if ( typeof gogaid_val_str != "string" ) {
1341
- return;
1342
- }
1343
- var gogaid_val = trim( gogaid_val_str );
1344
- if ( "" == gogaid_val ) {
1345
- gogaid_list = [];
1346
- } else {
1347
- var gogaid_list = gogaid_val.split( "," );
1348
- }
1349
-
1350
- var gogaid_haserror = false;
1351
- for( var i=0; i<gogaid_list.length; i++ ) {
1352
- gogaid_haserror = gogaid_haserror || !gogaid_regex.test( gogaid_list[ i ] );
1353
- }
1354
-
1355
- if ( gogaid_haserror && (gogaid_list.length > 0) ) {
1356
- jQuery( ".goid_ga_validation_error" )
1357
- .show();
1358
- } else {
1359
- jQuery( ".goid_ga_validation_error" )
1360
- .hide();
1361
- }
1362
- });
1363
-
1364
- jQuery( "#gtm4wp-options\\\\[integrate-amp-gtm\\\\]" )
1365
- .on( "blur", function() {
1366
- var ampid_regex = /^GTM-[A-Z0-9]+$/;
1367
- var ampid_val_str = jQuery( this ).val();
1368
- if ( typeof ampid_val_str != "string" ) {
1369
- return;
1370
- }
1371
- var ampid_val = trim( ampid_val_str );
1372
- if ( "" == ampid_val ) {
1373
- ampid_list = [];
1374
- } else {
1375
- var ampid_list = ampid_val.split( "," );
1376
- }
1377
-
1378
- var ampid_haserror = false;
1379
- for( var i=0; i<ampid_list.length; i++ ) {
1380
- ampid_haserror = ampid_haserror || !ampid_regex.test( gogaid_list[ i ] );
1381
- }
1382
-
1383
- if ( ampid_haserror && (ampid_list.length > 0) ) {
1384
- jQuery( ".ampid_validation_error" )
1385
- .show();
1386
- } else {
1387
- jQuery( ".ampid_validation_error" )
1388
- .hide();
1389
- }
1390
- });
1391
-
1392
- jQuery( "#gtm4wp-options\\\\[gtm-datalayer-variable-name\\\\]" )
1393
- .on( "blur", function() {
1394
- var currentval = jQuery( this ).val();
1395
-
1396
- jQuery( ".datalayername_validation_error" )
1397
- .hide();
1398
-
1399
- if ( currentval != "" ) {
1400
- // I know this is not the exact definition for a variable name but I think other kind of variable names should not be used.
1401
- var gtmvarname_regex = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
1402
- if ( ! gtmvarname_regex.test( currentval ) ) {
1403
- jQuery( ".datalayername_validation_error" )
1404
- .show();
1405
- }
1406
- }
1407
- });
1408
-
1409
- jQuery( "#gtm4wp-options\\\\[gtm-env-gtm-auth\\\\]" )
1410
- .on( "blur", function() {
1411
- var currentval = jQuery( this ).val();
1412
-
1413
- jQuery( ".gtmauth_validation_error" )
1414
- .hide();
1415
-
1416
- if ( currentval != "" ) {
1417
- var gtmauth_regex = /^[a-zA-Z0-9-_]+$/;
1418
- if ( ! gtmauth_regex.test( currentval ) ) {
1419
- jQuery( ".gtmauth_validation_error" )
1420
- .show();
1421
- }
1422
- }
1423
- });
1424
-
1425
- jQuery( "#gtm4wp-options\\\\[gtm-env-gtm-preview\\\\]" )
1426
- .on( "blur", function() {
1427
- var currentval = jQuery( this ).val();
1428
-
1429
- jQuery( ".gtmpreview_validation_error" )
1430
- .hide();
1431
-
1432
- if ( currentval != "" ) {
1433
- var gtmpreview_regex = /^env-[0-9]+$/;
1434
- if ( ! gtmpreview_regex.test( currentval ) ) {
1435
- jQuery( ".gtmpreview_validation_error" )
1436
- .show();
1437
- }
1438
- }
1439
- });
1440
-
1441
- jQuery( document )
1442
- .on( "click", ".gtm4wp-notice .notice-dismiss", function( e ) {
1443
- jQuery.post(ajaxurl, {
1444
- action: "gtm4wp_dismiss_notice",
1445
- noticeid: jQuery( this ).closest(".gtm4wp-notice")
1446
- .attr( "data-href" )
1447
- .substring( 1 )
1448
- });
1449
- });
1450
- });
1451
- </script>';
1452
- }
1453
-
1454
- function gtm4wp_show_warning() {
1455
- global $gtm4wp_options, $gtp4wp_plugin_url, $gtm4wp_integratefieldtexts, $current_user,
1456
- $gtm4wp_def_user_notices_dismisses;
1457
-
1458
- $woo_plugin_active = is_plugin_active( $gtm4wp_integratefieldtexts[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ]['plugintocheck'] );
1459
- if ( $woo_plugin_active && function_exists( 'WC' ) ) {
1460
- $woo = WC();
1461
- } else {
1462
- $woo = null;
1463
- }
1464
-
1465
- $gtm4wp_user_notices_dismisses = get_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, true );
1466
- if ( $gtm4wp_user_notices_dismisses === '' ) {
1467
- if ( is_array( $gtm4wp_def_user_notices_dismisses ) ) {
1468
- $gtm4wp_user_notices_dismisses = $gtm4wp_def_user_notices_dismisses;
1469
- } else {
1470
- $gtm4wp_user_notices_dismisses = array();
1471
- }
1472
- } else {
1473
- $gtm4wp_user_notices_dismisses = @unserialize( $gtm4wp_user_notices_dismisses );
1474
- if ( false === $gtm4wp_user_notices_dismisses || !is_array( $gtm4wp_user_notices_dismisses ) ) {
1475
- $gtm4wp_user_notices_dismisses = array();
1476
- }
1477
- }
1478
- $gtm4wp_user_notices_dismisses = array_merge( $gtm4wp_def_user_notices_dismisses, $gtm4wp_user_notices_dismisses );
1479
-
1480
- if ( ( trim( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) == '' ) && ( false === $gtm4wp_user_notices_dismisses['enter-gtm-code'] ) ) {
1481
- echo '<div class="gtm4wp-notice notice notice-error is-dismissible" data-href="?enter-gtm-code"><p><strong>' . sprintf( __( 'To start using Google Tag Manager for WordPress, please <a href="%s">enter your GTM ID</a>', 'duracelltomi-google-tag-manager' ), 'options-general.php?page=' . GTM4WP_ADMINSLUG ) . '</strong></p></div>';
1482
- }
1483
-
1484
- if ( (
1485
- ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' == $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] )
1486
- ) || (
1487
- ( '' == $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] )
1488
- ) ) {
1489
- echo '<div class="gtm4wp-notice notice notice-error" data-href="?incomplete-gtm-env-config"><p><strong>' . sprintf( __( 'Incomplete Google Tag Manager environment configuration: either gtm_preview or gtm_auth parameter value is missing!', 'duracelltomi-google-tag-manager' ), 'options-general.php?page=' . GTM4WP_ADMINSLUG ) . '</strong></p></div>';
1490
- }
1491
-
1492
- if ( ( false === $gtm4wp_user_notices_dismisses['wc-ga-plugin-warning'] ) || ( false === $gtm4wp_user_notices_dismisses['wc-gayoast-plugin-warning'] ) ) {
1493
- $is_wc_active = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ||
1494
- $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ||
1495
- $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ];
1496
-
1497
- if ( ( false === $gtm4wp_user_notices_dismisses['wc-ga-plugin-warning'] ) && $is_wc_active && is_plugin_active( 'woocommerce-google-analytics-integration/woocommerce-google-analytics-integration.php' ) ) {
1498
- echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?wc-ga-plugin-warning"><p><strong>' . __( 'Notice: you should deactivate the plugin "WooCommerce Google Analytics Integration" if you are using Google Analytics tags inside Google Tag Manager!', 'duracelltomi-google-tag-manager' ) . '</strong></p></div>';
1499
- }
1500
-
1501
- if ( ( false === $gtm4wp_user_notices_dismisses['wc-gayoast-plugin-warning'] ) && $is_wc_active && is_plugin_active( 'google-analytics-for-wordpress/googleanalytics.php' ) ) {
1502
- echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?wc-gayoast-plugin-warning"><p><strong>' . __( 'Notice: you should deactivate the plugin "Google Analytics for WordPress by MonsterInsights" if you are using Google Analytics tags inside Google Tag Manager!', 'duracelltomi-google-tag-manager' ) . '</strong></p></div>';
1503
- }
1504
- }
1505
-
1506
- if ( ( false === $gtm4wp_user_notices_dismisses['php72-warning'] ) && ( version_compare( PHP_VERSION, '7.2.0' ) < 0 ) ) {
1507
- echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?php72-warning"><p><strong>' . __( 'Warning: You are using an outdated version of PHP (v' . PHP_VERSION . ') that might be not compatible with future versions of the plugin Google Tag Manager for WordPress (GTM4WP). Please consider to upgrade your PHP.', 'duracelltomi-google-tag-manager' ) . '</strong></p></div>';
1508
- }
1509
- }
1510
-
1511
- function gtm4wp_dismiss_notice() {
1512
- global $gtm4wp_def_user_notices_dismisses, $current_user;
1513
-
1514
- $gtm4wp_user_notices_dismisses = get_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, true );
1515
- if ( $gtm4wp_user_notices_dismisses === '' ) {
1516
- if ( is_array( $gtm4wp_def_user_notices_dismisses ) ) {
1517
- $gtm4wp_user_notices_dismisses = $gtm4wp_def_user_notices_dismisses;
1518
- } else {
1519
- $gtm4wp_user_notices_dismisses = array();
1520
- }
1521
- } else {
1522
- $gtm4wp_user_notices_dismisses = @unserialize( $gtm4wp_user_notices_dismisses );
1523
- if ( false === $gtm4wp_user_notices_dismisses || !is_array( $gtm4wp_user_notices_dismisses ) ) {
1524
- $gtm4wp_user_notices_dismisses = array();
1525
- }
1526
- }
1527
- $gtm4wp_user_notices_dismisses = array_merge( $gtm4wp_def_user_notices_dismisses, $gtm4wp_user_notices_dismisses );
1528
-
1529
- $noticeid = trim( basename( $_POST['noticeid'] ) );
1530
- if ( array_key_exists( $noticeid, $gtm4wp_user_notices_dismisses ) ) {
1531
- $gtm4wp_user_notices_dismisses[ $noticeid ] = true;
1532
- update_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, serialize( $gtm4wp_user_notices_dismisses ) );
1533
- }
1534
- }
1535
-
1536
- function gtm4wp_add_plugin_action_links( $links, $file ) {
1537
- global $gtp4wp_plugin_basename;
1538
-
1539
- if ( $file != $gtp4wp_plugin_basename ) {
1540
- return $links;
1541
- }
1542
-
1543
- $settings_link = '<a href="' . menu_page_url( GTM4WP_ADMINSLUG, false ) . '">' . esc_html( __( 'Settings' ) ) . '</a>';
1544
-
1545
- array_unshift( $links, $settings_link );
1546
-
1547
- return $links;
1548
- }
1549
-
1550
- function gtm4wp_show_upgrade_notification( $current_plugin_metadata, $new_plugin_metadata ) {
1551
- if ( isset( $new_plugin_metadata->upgrade_notice ) && strlen( trim( $new_plugin_metadata->upgrade_notice ) ) > 0 ) {
1552
- echo '<p style="background-color: #d54e21; padding: 10px; color: #f9f9f9; margin-top: 10px"><strong>Important Upgrade Notice:</strong> ';
1553
- echo esc_html( $new_plugin_metadata->upgrade_notice ), '</p>';
1554
- }
1555
- }
1556
-
1557
- add_action( 'admin_init', 'gtm4wp_admin_init' );
1558
- add_action( 'admin_menu', 'gtm4wp_add_admin_page' );
1559
- add_action( 'admin_enqueue_scripts', 'gtm4wp_add_admin_js' );
1560
- add_action( 'admin_notices', 'gtm4wp_show_warning' );
1561
- add_action( 'admin_head', 'gtm4wp_admin_head' );
1562
- add_filter( 'plugin_action_links', 'gtm4wp_add_plugin_action_links', 10, 2 );
1563
- add_action( 'wp_ajax_gtm4wp_dismiss_notice', 'gtm4wp_dismiss_notice' );
1564
- add_action( 'in_plugin_update_message-duracelltomi-google-tag-manager-for-wordpress/duracelltomi-google-tag-manager-for-wordpress.php', 'gtm4wp_show_upgrade_notification', 10, 2 );
1
+ <?php
2
+ /**
3
+ * Handle WordPress admin page related hooks and functions
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ define( 'GTM4WP_ADMINSLUG', 'gtm4wp-settings' );
12
+ define( 'GTM4WP_ADMIN_GROUP', 'gtm4wp-admin-group' );
13
+
14
+ define( 'GTM4WP_ADMIN_GROUP_GENERAL', 'gtm4wp-admin-group-general' );
15
+ define( 'GTM4WP_ADMIN_GROUP_GTMID', 'gtm4wp-admin-group-gtm-id' );
16
+ define( 'GTM4WP_ADMIN_GROUP_CONTAINERON', 'gtm4wp-admin-container-on' );
17
+ define( 'GTM4WP_ADMIN_GROUP_COMPATMODE', 'gtm4wp-admin-compat-mode' );
18
+ define( 'GTM4WP_ADMIN_GROUP_INFO', 'gtm4wp-admin-group-datalayer-info' );
19
+
20
+ define( 'GTM4WP_ADMIN_GROUP_INCLUDES', 'gtm4wp-admin-group-includes' );
21
+ define( 'GTM4WP_ADMIN_GROUP_EVENTS', 'gtm4wp-admin-group-events' );
22
+ define( 'GTM4WP_ADMIN_GROUP_SCROLLER', 'gtm4wp-admin-group-scroller' );
23
+ define( 'GTM4WP_ADMIN_GROUP_BLACKLIST', 'gtm4wp-admin-group-blacklist-tags' );
24
+ define( 'GTM4WP_ADMIN_GROUP_INTEGRATION', 'gtm4wp-admin-group-integration' );
25
+ define( 'GTM4WP_ADMIN_GROUP_ADVANCED', 'gtm4wp-admin-group-advanced' );
26
+ define( 'GTM4WP_ADMIN_GROUP_CREDITS', 'gtm4wp-admin-group-credits' );
27
+
28
+ define( 'GTM4WP_USER_NOTICES_KEY', 'gtm4wp_user_notices_dismisses_json' );
29
+
30
+ define( 'GTM4WP_PHASE_STABLE', 'gtm4wp-phase-stable' );
31
+ define( 'GTM4WP_PHASE_BETA', 'gtm4wp-phase-beta' );
32
+ define( 'GTM4WP_PHASE_EXPERIMENTAL', 'gtm4wp-phase-experimental' );
33
+ define( 'GTM4WP_PHASE_DEPRECATED', 'gtm4wp-phase-deprecated' );
34
+
35
+ $GLOBALS['gtm4wp_def_user_notices_dismisses'] = array(
36
+ 'enter-gtm-code' => false,
37
+ 'wc-ga-plugin-warning' => false,
38
+ 'wc-gayoast-plugin-warning' => false,
39
+ 'php72-warning' => false,
40
+ 'deprecated-warning' => false,
41
+ );
42
+
43
+ /**
44
+ * Generic function to safely escape translated text that outputs on the admin page.
45
+ * Allows only basic HTML tags for formatting purposes. No anchor element is allowed.
46
+ *
47
+ * @param string $text The admin text that needs escaping.
48
+ * @return string The escaped text.
49
+ */
50
+ function gtm4wp_safe_admin_html( $text ) {
51
+ return wp_kses(
52
+ $text,
53
+ array(
54
+ 'br' => array(),
55
+ 'strong' => array(
56
+ 'style' => array(),
57
+ 'class' => array(),
58
+ ),
59
+ 'em' => array(
60
+ 'style' => array(),
61
+ 'class' => array(),
62
+ ),
63
+ 'p' => array(
64
+ 'style' => array(),
65
+ 'class' => array(),
66
+ ),
67
+ 'span' => array(
68
+ 'style' => array(),
69
+ 'class' => array(),
70
+ ),
71
+ 'code' => array(),
72
+ 'ul' => array(
73
+ 'style' => array(),
74
+ 'class' => array(),
75
+ ),
76
+ 'li' => array(
77
+ 'style' => array(),
78
+ 'class' => array(),
79
+ ),
80
+ )
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Generic function to safely escape text that outputs on the admin page.
86
+ * Works just like gtm4wp_safe_admin_html() but also allows anchor elements.
87
+ *
88
+ * @param string $text The admin text that needs escaping.
89
+ * @return string The escaped text.
90
+ */
91
+ function gtm4wp_safe_admin_html_with_links( $text ) {
92
+ return wp_kses(
93
+ $text,
94
+ array(
95
+ 'br' => array(),
96
+ 'strong' => array(
97
+ 'style' => array(),
98
+ 'class' => array(),
99
+ ),
100
+ 'em' => array(
101
+ 'style' => array(),
102
+ 'class' => array(),
103
+ ),
104
+ 'p' => array(
105
+ 'style' => array(),
106
+ 'class' => array(),
107
+ ),
108
+ 'span' => array(
109
+ 'style' => array(),
110
+ 'class' => array(),
111
+ ),
112
+ 'code' => array(),
113
+ 'ul' => array(
114
+ 'style' => array(),
115
+ 'class' => array(),
116
+ ),
117
+ 'li' => array(
118
+ 'style' => array(),
119
+ 'class' => array(),
120
+ ),
121
+ 'a' => array(
122
+ 'id' => array(),
123
+ 'name' => array(),
124
+ 'href' => array(),
125
+ 'target' => array(),
126
+ 'rel' => array(),
127
+ ),
128
+ )
129
+ );
130
+ }
131
+
132
+ require_once dirname( __FILE__ ) . '/admin-tab-basicdata.php';
133
+ require_once dirname( __FILE__ ) . '/admin-tab-events.php';
134
+ require_once dirname( __FILE__ ) . '/admin-tab-scrolltracking.php';
135
+ require_once dirname( __FILE__ ) . '/admin-tab-integrate.php';
136
+ require_once dirname( __FILE__ ) . '/admin-tab-advanced.php';
137
+
138
+ /**
139
+ * Callback function for add_settings_section(). Outputs the HTML of an admin tab.
140
+ *
141
+ * @see https://developer.wordpress.org/reference/functions/add_settings_section/
142
+ *
143
+ * @param array $args array of tab attributes.
144
+ * @return void
145
+ */
146
+ function gtm4wp_admin_output_section( $args ) {
147
+ echo '<span class="tabinfo">';
148
+
149
+ switch ( $args['id'] ) {
150
+ case GTM4WP_ADMIN_GROUP_GENERAL:
151
+ sprintf(
152
+ // translators: 1: opening anchor tag linking to GTM's developer doc homepage. 2: Closing anchor tag.
153
+ esc_html__(
154
+ 'This plugin is intended to be used by IT and marketing staff. Please be sure you read the
155
+ %1$sGoogle Tag Manager Help Center%2$s before you start using this plugin.<br /><br />',
156
+ 'duracelltomi-google-tag-manager'
157
+ ),
158
+ '<a href="https://developers.google.com/tag-manager/" target="_blank" rel="noopener">',
159
+ '</a>'
160
+ );
161
+
162
+ break;
163
+
164
+ case GTM4WP_ADMIN_GROUP_INCLUDES:
165
+ esc_html_e( 'Here you can check what data is needed to be included in the dataLayer to be able to access them in Google Tag Manager', 'duracelltomi-google-tag-manager' );
166
+ echo '<br />';
167
+ printf(
168
+ /* translators: 1: opening anchor tag that points to WhichBrowser website. 2: closing anchor tag. */
169
+ esc_html__(
170
+ '* Browser, OS and Device data is provided using %1$sWhichBrowser%2$s library.',
171
+ 'duracelltomi-google-tag-manager'
172
+ ),
173
+ '<a href="http://whichbrowser.net/" target="_blank" rel="noopener">',
174
+ '</a>'
175
+ );
176
+
177
+ break;
178
+
179
+ case GTM4WP_ADMIN_GROUP_EVENTS:
180
+ esc_html_e( 'Fire tags in Google Tag Manager on special events on your website', 'duracelltomi-google-tag-manager' );
181
+
182
+ break;
183
+
184
+ case GTM4WP_ADMIN_GROUP_SCROLLER:
185
+ esc_html_e( 'Fire tags based on how the visitor scrolls through your page.', 'duracelltomi-google-tag-manager' );
186
+ echo '<br />';
187
+ printf(
188
+ /* translators: 1: opening anchor tag that points to the corresponding Analytics Talks blog post. 2: closing anchor tag. */
189
+ esc_html__(
190
+ 'Based on the script originaly posted to %1$sAnalytics Talk%2$s',
191
+ 'duracelltomi-google-tag-manager'
192
+ ),
193
+ '<a href="http://cutroni.com/blog/2012/02/21/advanced-content-tracking-with-google-analytics-part-1/" target="_blank" rel="noopener">',
194
+ '</a>'
195
+ );
196
+
197
+ break;
198
+
199
+ case GTM4WP_ADMIN_GROUP_BLACKLIST:
200
+ esc_html_e( 'Here you can control which types of tags, triggers and variables can be executed on your site regardless of what tags are included in your container on the Google Tag Manager site. Use this to increase security!', 'duracelltomi-google-tag-manager' );
201
+ echo '<br />';
202
+ esc_html_e( 'Do not modify if you do not know what to do, since it can cause issues with your tag deployment!', 'duracelltomi-google-tag-manager' );
203
+ echo '<br />';
204
+ esc_html_e( 'For example blacklisting everything and only whitelisting the Google Analytics tag without whitelisting the URL variable type will cause your Google Analytics tags to be blocked anyway since the attached triggers (Page View) can not fire!', 'duracelltomi-google-tag-manager' );
205
+
206
+ break;
207
+
208
+ case GTM4WP_ADMIN_GROUP_INTEGRATION:
209
+ esc_html_e( 'Google Tag Manager for WordPress can integrate with several popular plugins. Please check the plugins you would like to integrate with:', 'duracelltomi-google-tag-manager' );
210
+
211
+ break;
212
+
213
+ case GTM4WP_ADMIN_GROUP_ADVANCED:
214
+ esc_html_e( 'You usually do not need to modify thoose settings. Please be carefull while hacking here.', 'duracelltomi-google-tag-manager' );
215
+
216
+ break;
217
+
218
+ case GTM4WP_ADMIN_GROUP_CREDITS:
219
+ esc_html_e( 'Some info about the author of this plugin', 'duracelltomi-google-tag-manager' );
220
+
221
+ break;
222
+ } // end switch
223
+
224
+ echo '</span>';
225
+ }
226
+
227
+ /**
228
+ * Callback function for add_settings_field() to output the HTML of a specific plugin option
229
+ *
230
+ * @see https://developer.wordpress.org/reference/functions/add_settings_field/
231
+ *
232
+ * @param array $args Field attributes as array key-value pairs. 'label_for' is the unique ID of the option. 'description' is usually outputed below the option field.
233
+ * @return void
234
+ */
235
+ function gtm4wp_admin_output_field( $args ) {
236
+ global $gtm4wp_options, $gtm4wp_business_verticals;
237
+
238
+ switch ( $args['label_for'] ) {
239
+ case GTM4WP_ADMIN_GROUP_GTMID:
240
+ echo wp_kses(
241
+ sprintf(
242
+ '<input type="text" id="%s" name="%s" value="%s"%s />',
243
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']' ),
244
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']' ),
245
+ defined( 'GTM4WP_HARDCODED_GTM_ID' ) ? constant( 'GTM4WP_HARDCODED_GTM_ID' ) : $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ],
246
+ defined( 'GTM4WP_HARDCODED_GTM_ID' ) ? ' readonly="readonly"' : ''
247
+ ),
248
+ array(
249
+ 'input' => array(
250
+ 'type' => array(),
251
+ 'id' => array(),
252
+ 'name' => array(),
253
+ 'value' => array(),
254
+ 'readonly' => array(),
255
+ ),
256
+ )
257
+ );
258
+ echo '<br />';
259
+
260
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
261
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
262
+
263
+ if ( defined( 'GTM4WP_HARDCODED_GTM_ID' ) ) {
264
+ echo '<br /><span class="gtm_wpconfig_set">WARNING! Container ID was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the container ID or remove the GTM4WP_HARDCODED_GTM_ID constant!</span>';
265
+ }
266
+ echo '<br /><span class="gtmid_validation_error">' . esc_html__( 'This does not seems to be a valid Google Tag Manager ID! Valid format: GTM-XXXXX where X can be numbers and capital letters. Use comma without any space (,) to enter multpile container IDs.', 'duracelltomi-google-tag-manager' ) . '</span>';
267
+
268
+ break;
269
+
270
+ case GTM4WP_ADMIN_GROUP_CONTAINERON:
271
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
272
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
273
+ echo '<br/><br/>';
274
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[container-on]_1' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[container-on]' ) . '" value="1" ' . ( GTM4WP_PLACEMENT_OFF !== $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'On', 'duracelltomi-google-tag-manager' ) . '<br />';
275
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[container-on]_0' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[container-on]' ) . '" value="0" ' . ( GTM4WP_PLACEMENT_OFF === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Off', 'duracelltomi-google-tag-manager' ) . '<br />';
276
+
277
+ break;
278
+
279
+ case GTM4WP_ADMIN_GROUP_COMPATMODE:
280
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
281
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
282
+ echo '<br/><br/>';
283
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_BODYOPEN_AUTO ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]' ) . '" value="' . esc_attr( GTM4WP_PLACEMENT_BODYOPEN_AUTO ) . '" ' . ( GTM4WP_PLACEMENT_BODYOPEN_AUTO === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] || GTM4WP_PLACEMENT_OFF === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Off (no tweak, right placement)', 'duracelltomi-google-tag-manager' ) . '<br />';
284
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_FOOTER ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]' ) . '" value="' . esc_attr( GTM4WP_PLACEMENT_FOOTER ) . '" ' . ( GTM4WP_PLACEMENT_FOOTER === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Footer of the page (not recommended by Google, Search Console verification will not work)', 'duracelltomi-google-tag-manager' ) . '<br />';
285
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]_' . GTM4WP_PLACEMENT_BODYOPEN ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[compat-mode]' ) . '" value="' . esc_attr( GTM4WP_PLACEMENT_BODYOPEN ) . '" ' . ( GTM4WP_PLACEMENT_BODYOPEN === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Manually coded (needs tweak in your template)', 'duracelltomi-google-tag-manager' ) . '<br />';
286
+
287
+ break;
288
+
289
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']':
290
+ echo '<input type="text" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']' ) . '" value="' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_DATALAYER_NAME ] ) . '" /><br />';
291
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
292
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
293
+ echo '<br /><span class="datalayername_validation_error">' . esc_html__( 'This does not seems to be a valid JavaScript variable name! Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
294
+
295
+ break;
296
+
297
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']':
298
+ echo wp_kses(
299
+ sprintf(
300
+ '<input type="text" id="%s" name="%s" value="%s"%s />',
301
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']' ),
302
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']' ),
303
+ defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ? constant( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) : $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ],
304
+ defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ? ' readonly="readonly"' : ''
305
+ ),
306
+ array(
307
+ 'input' => array(
308
+ 'type' => array(),
309
+ 'id' => array(),
310
+ 'name' => array(),
311
+ 'value' => array(),
312
+ 'readonly' => array(),
313
+ ),
314
+ )
315
+ );
316
+
317
+ echo '<br />';
318
+
319
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
320
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
321
+
322
+ if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ) {
323
+ echo '<br /><span class="gtm_wpconfig_set">WARNING! Environment auth parameter was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the parameter value or remove the GTM4WP_HARDCODED_GTM_ENV_AUTH constant!</span>';
324
+ }
325
+ echo '<br /><span class="gtmauth_validation_error">' . esc_html__( 'This does not seems to be a valid gtm_auth parameter! It should only contain letters, number and the &quot;-&quot; character. Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
326
+
327
+ break;
328
+
329
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']':
330
+ echo wp_kses(
331
+ sprintf(
332
+ '<input type="text" id="%s" name="%s" value="%s"%s />',
333
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']' ),
334
+ esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']' ),
335
+ defined( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' ) ? constant( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' ) : $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ],
336
+ defined( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' ) ? ' readonly="readonly"' : ''
337
+ ),
338
+ array(
339
+ 'input' => array(
340
+ 'type' => array(),
341
+ 'id' => array(),
342
+ 'name' => array(),
343
+ 'value' => array(),
344
+ 'readonly' => array(),
345
+ ),
346
+ )
347
+ );
348
+
349
+ echo '<br />';
350
+
351
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
352
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
353
+
354
+ if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' ) ) {
355
+ echo '<br /><span class="gtm_wpconfig_set">WARNING! Environment preview parameter was set and fixed in wp-config.php. If you wish to change this value, please edit your wp-config.php and change the parameter value or remove the GTM4WP_HARDCODED_GTM_ENV_PREVIEW constant!</span>';
356
+ }
357
+
358
+ echo '<br /><span class="gtmpreview_validation_error">' . esc_html__( 'This does not seems to be a valid gtm_preview parameter! It should have the format &quot;env-NN&quot; where NN is an integer number. Please check and try again', 'duracelltomi-google-tag-manager' ) . '</span>';
359
+
360
+ break;
361
+
362
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']':
363
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_0' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']' ) . '" value="0" ' . ( 0 === $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Disable feature: control everything on Google Tag Manager interface', 'duracelltomi-google-tag-manager' ) . '<br />';
364
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_1' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']' ) . '" value="1" ' . ( 1 === $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Allow all, except the checked items on all blacklist tabs (blacklist)', 'duracelltomi-google-tag-manager' ) . '<br />';
365
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']_2' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']' ) . '" value="2" ' . ( 2 === $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Block all, except the checked items on all blacklist tabs (whitelist)', 'duracelltomi-google-tag-manager' ) . '<br />';
366
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
367
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
368
+
369
+ break;
370
+
371
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']':
372
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']_0' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']' ) . '" value="0" ' . ( 0 === $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Celsius', 'duracelltomi-google-tag-manager' ) . '<br />';
373
+ echo '<input type="radio" id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']_1' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INCLUDE_WEATHERUNITS . ']' ) . '" value="1" ' . ( 1 === $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] ? 'checked="checked"' : '' ) . '/> ' . esc_html__( 'Fahrenheit', 'duracelltomi-google-tag-manager' ) . '<br />';
374
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
375
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
376
+
377
+ break;
378
+
379
+ case GTM4WP_ADMIN_GROUP_INFO:
380
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
381
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
382
+
383
+ break;
384
+
385
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . ']':
386
+ echo '<select id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY . ']' ) . '">';
387
+ echo '<option value="">(not set)</option>';
388
+
389
+ $gtm4wp_taxonomies = get_taxonomies(
390
+ array(
391
+ 'show_ui' => true,
392
+ 'public' => true,
393
+ '_builtin' => false,
394
+ ),
395
+ 'object',
396
+ 'and'
397
+ );
398
+
399
+ foreach ( $gtm4wp_taxonomies as $onetaxonomy ) {
400
+ echo '<option value="' . esc_attr( $onetaxonomy->name ) . '"' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ] === $onetaxonomy->name ? ' selected="selected"' : '' ) . '>' . esc_html( $onetaxonomy->label ) . '</option>';
401
+ }
402
+
403
+ echo '</select>';
404
+
405
+ break;
406
+
407
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . ']':
408
+ echo '<select id="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL . ']' ) . '">';
409
+
410
+ foreach ( $gtm4wp_business_verticals as $vertical_id => $vertical_display_name ) {
411
+ echo '<option value="' . esc_attr( $vertical_id ) . '"' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] === $vertical_id ? ' selected="selected"' : '' ) . '>' . esc_html( $vertical_display_name ) . '</option>';
412
+ }
413
+
414
+ echo '</select><br>';
415
+
416
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
417
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
418
+
419
+ break;
420
+
421
+ case GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_NOGTMFORLOGGEDIN . ']':
422
+ $roles = get_editable_roles();
423
+
424
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
425
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
426
+ echo '<br/><br/>';
427
+
428
+ $saved_roles = explode( ',', $gtm4wp_options[ GTM4WP_OPTION_NOGTMFORLOGGEDIN ] );
429
+
430
+ foreach ( $roles as $role_id => $role_info ) {
431
+ $role_name = translate_user_role( $role_info['name'] );
432
+ echo '<input type="checkbox" id="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']_' . $role_id ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . '][]' ) . '" value="' . esc_attr( $role_id ) . '"' . esc_attr( in_array( $role_id, $saved_roles, true ) ? ' checked="checked"' : '' ) . '><label for="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']_' . $role_id ) . '">' . esc_html( $role_name ) . '</label><br/>';
433
+ }
434
+
435
+ break;
436
+
437
+ default:
438
+ if ( preg_match( '/' . GTM4WP_OPTIONS . '\\[blacklist\\-[^\\]]+\\]/i', $args['label_for'] ) ) {
439
+ if ( 'blacklist-sandboxed' === $args['entityid'] ) {
440
+ echo '<input type="checkbox" id="' . esc_attr( $args['label_for'] ) . '" name="' . esc_attr( $args['label_for'] ) . '" value="1" ' . checked( 1, $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_SANDBOXED ], false ) . ' /><br />';
441
+ } else {
442
+ echo '<input type="checkbox" id="' . esc_attr( $args['label_for'] ) . '" name="' . esc_attr( $args['label_for'] ) . '" value="1" ' . checked( 1, in_array( $args['entityid'], $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_STATUS ], true ), false ) . ' /><br />';
443
+ }
444
+
445
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
446
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
447
+ } else {
448
+ $optval = $gtm4wp_options[ $args['optionfieldid'] ];
449
+
450
+ switch ( gettype( $optval ) ) {
451
+ case 'boolean':
452
+ echo '<input type="checkbox" id="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" value="1" ' . checked( 1, $optval, false ) . ' /><br />';
453
+
454
+ break;
455
+
456
+ case 'integer':
457
+ echo '<input type="number" step="1" min="0" class="small-text" id="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" value="' . esc_attr( $optval ) . '" /><br />';
458
+
459
+ break;
460
+
461
+ default:
462
+ echo '<input type="text" id="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" name="' . esc_attr( GTM4WP_OPTIONS . '[' . $args['optionfieldid'] . ']' ) . '" value="' . esc_attr( $optval ) . '" size="80" /><br />';
463
+ } // end switch gettype optval
464
+
465
+ // gtm4wp_safe_admin_html_with_links() calls wp_kses().
466
+ echo gtm4wp_safe_admin_html_with_links( $args['description'] ); // phpcs:ignore
467
+
468
+ if ( isset( $args['plugintocheck'] ) && ( '' !== $args['plugintocheck'] ) ) {
469
+ if ( is_plugin_active( $args['plugintocheck'] ) ) {
470
+ echo '<br />' . sprintf(
471
+ // translators: 1: the name of the conflicting plugin being checked. 2: either 'active' or 'inactive' using bolded formatting.
472
+ esc_html__(
473
+ 'This plugin (%1$s) is %2$s, it is strongly recommended to enable this integration!',
474
+ 'duracelltomi-google-tag-manager'
475
+ ),
476
+ esc_html( $args['plugintocheck'] ),
477
+ '<strong class="gtm4wp-plugin-active">active</strong>'
478
+ );
479
+ } else {
480
+ echo '<br />' . sprintf(
481
+ // translators: 1: the name of the conflicting plugin being checked. 2: either 'active' or 'inactive' using bolded formatting.
482
+ esc_html__(
483
+ 'This plugin (%1$s) is %2$s, enabling this integration could cause issues on frontend!',
484
+ 'duracelltomi-google-tag-manager'
485
+ ),
486
+ esc_html( $args['plugintocheck'] ),
487
+ '<strong class="gtm4wp-plugin-not-active">not active</strong>'
488
+ );
489
+ }
490
+ }
491
+ }
492
+ } // end switch args label_for
493
+ }
494
+
495
+ /**
496
+ * Callback function for register_setting(). Sanitizes GTM4WP option values.
497
+ *
498
+ * @see https://developer.wordpress.org/reference/functions/register_setting/
499
+ *
500
+ * @param array $options Array of key-value pairs with GTM4WP options and values.
501
+ * @return mixed The sanitized option value.
502
+ */
503
+ function gtm4wp_sanitize_options( $options ) {
504
+ global $wpdb, $gtm4wp_entity_ids;
505
+
506
+ $output = gtm4wp_reload_options();
507
+
508
+ foreach ( $output as $optionname => $optionvalue ) {
509
+ if ( isset( $options[ $optionname ] ) ) {
510
+ $newoptionvalue = $options[ $optionname ];
511
+ } else {
512
+ $newoptionvalue = '';
513
+ }
514
+
515
+ if ( 'include-' === substr( $optionname, 0, 8 ) ) {
516
+ // "include" settings.
517
+ $output[ $optionname ] = (bool) $newoptionvalue;
518
+
519
+ } elseif ( 'event-' === substr( $optionname, 0, 6 ) ) {
520
+ // dataLayer events.
521
+ $output[ $optionname ] = (bool) $newoptionvalue;
522
+
523
+ // clear oembed transients when feature is enabled because we need to hook into the oembed process to enable some 3rd party APIs.
524
+ if ( $output[ $optionname ] && ! $optionvalue ) {
525
+ if ( GTM4WP_OPTION_EVENTS_YOUTUBE === $optionname ) {
526
+ // TODO: replace with $wpdb->delete() https://developer.wordpress.org/reference/classes/wpdb/delete/.
527
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%youtube.com%' AND meta_key LIKE '_oembed_%'" ); // phpcs:ignore
528
+ }
529
+
530
+ if ( GTM4WP_OPTION_EVENTS_VIMEO === $optionname ) {
531
+ // TODO: replace with $wpdb->delete() https://developer.wordpress.org/reference/classes/wpdb/delete/.
532
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%vimeo.com%' AND meta_key LIKE '_oembed_%'" ); // phpcs:ignore
533
+ }
534
+ }
535
+ } elseif ( 'blacklist-' === substr( $optionname, 0, 10 ) ) {
536
+ // blacklist / whitelist entities.
537
+ if ( GTM4WP_OPTION_BLACKLIST_ENABLE === $optionname ) {
538
+ $output[ $optionname ] = (int) $options[ GTM4WP_OPTION_BLACKLIST_ENABLE ];
539
+ } elseif ( GTM4WP_OPTION_BLACKLIST_SANDBOXED === $optionname ) {
540
+ $output[ $optionname ] = (bool) $newoptionvalue;
541
+ } elseif ( GTM4WP_OPTION_BLACKLIST_STATUS === $optionname ) {
542
+ $selected_blacklist_entities = array();
543
+
544
+ foreach ( $gtm4wp_entity_ids as $gtm_entity_group_id => $gtm_entity_group_list ) {
545
+ foreach ( $gtm_entity_group_list as $gtm_entity_id => $gtm_entity_label ) {
546
+ $entity_option_id = 'blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id;
547
+ if ( array_key_exists( $entity_option_id, $options ) ) {
548
+ $newoptionvalue = (bool) $options[ $entity_option_id ];
549
+ if ( $newoptionvalue ) {
550
+ $selected_blacklist_entities[] = $gtm_entity_id;
551
+ }
552
+ }
553
+ }
554
+ }
555
+
556
+ $output[ $optionname ] = implode( ',', $selected_blacklist_entities );
557
+ }
558
+ } elseif ( GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS === $optionname ) {
559
+ // Google Optimize settings.
560
+ $_goid_val = trim( $newoptionvalue );
561
+ if ( '' === $_goid_val ) {
562
+ $_goid_list = array();
563
+ } else {
564
+ $_goid_list = explode( ',', $_goid_val );
565
+ }
566
+ $_goid_haserror = false;
567
+
568
+ foreach ( $_goid_list as $one_go_id ) {
569
+ $_goid_haserror = $_goid_haserror || ! preg_match( '/^(GTM|OPT)-[A-Z0-9]+$/', $one_go_id );
570
+ }
571
+
572
+ if ( $_goid_haserror && ( count( $_goid_list ) > 0 ) ) {
573
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS . ']', esc_html__( 'Invalid Google Optimize ID. Valid ID format: GTM-XXXXX or OPT-XXXXX. Use comma without additional space (,) to enter more than one ID.', 'duracelltomi-google-tag-manager' ) );
574
+ } else {
575
+ $output[ $optionname ] = $newoptionvalue;
576
+ }
577
+ } elseif ( GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT === $optionname ) {
578
+ $output[ $optionname ] = (int) $newoptionvalue;
579
+ } elseif ( GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION === $optionname ) {
580
+ $output[ $optionname ] = (int) $newoptionvalue;
581
+ } elseif ( GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE === $optionname ) {
582
+ $output[ $optionname ] = (int) $newoptionvalue;
583
+ } elseif ( GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX === $optionname ) {
584
+ $output[ $optionname ] = trim( (string) $newoptionvalue );
585
+ } elseif ( GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY === $optionname ) {
586
+ $output[ $optionname ] = trim( (string) $newoptionvalue );
587
+ } elseif ( GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL === $optionname ) {
588
+ $output[ $optionname ] = trim( (string) $newoptionvalue );
589
+ } elseif ( GTM4WP_OPTION_GTMDOMAIN === $optionname ) {
590
+ // for PHP 7- compatibility.
591
+ if ( ! defined( 'FILTER_FLAG_HOSTNAME' ) ) {
592
+ define( 'FILTER_FLAG_HOSTNAME', 0 );
593
+ }
594
+
595
+ // remove https:// prefix if used.
596
+ $newoptionvalue = str_replace( 'https://', '', $newoptionvalue );
597
+
598
+ $newoptionvalue = filter_var( $newoptionvalue, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME );
599
+ if ( false === $newoptionvalue ) {
600
+ $newoptionvalue = '';
601
+ }
602
+ $output[ $optionname ] = trim( (string) $newoptionvalue );
603
+
604
+ } elseif ( GTM4WP_OPTION_INTEGRATE_AMPID === $optionname ) {
605
+ // Accelerated Mobile Pages settings.
606
+ $_ampid_val = trim( $newoptionvalue );
607
+ if ( '' === $_ampid_val ) {
608
+ $_ampid_list = array();
609
+ } else {
610
+ $_ampid_list = explode( ',', $_ampid_val );
611
+ }
612
+ $_ampid_haserror = false;
613
+
614
+ foreach ( $_ampid_list as $one_amp_id ) {
615
+ $_ampid_haserror = $_ampid_haserror || ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_amp_id );
616
+ }
617
+
618
+ if ( $_ampid_haserror && ( count( $_ampid_list ) > 0 ) ) {
619
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_INTEGRATE_AMPID . ']', esc_html__( 'Invalid AMP Google Tag Manager Container ID. Valid ID format: GTM-XXXXX. Use comma without additional space (,) to enter more than one ID.', 'duracelltomi-google-tag-manager' ) );
620
+ } else {
621
+ $output[ $optionname ] = $newoptionvalue;
622
+ }
623
+ } elseif ( substr( $optionname, 0, 10 ) === 'integrate-' ) {
624
+ // integrations.
625
+ $output[ $optionname ] = (bool) $newoptionvalue;
626
+
627
+ } elseif ( ( GTM4WP_OPTION_GTM_CODE === $optionname ) || ( GTM4WP_OPTION_DATALAYER_NAME === $optionname ) || ( GTM4WP_OPTION_ENV_GTM_AUTH === $optionname ) || ( GTM4WP_OPTION_ENV_GTM_PREVIEW === $optionname ) ) {
628
+ // GTM code or dataLayer variable name.
629
+ $newoptionvalue = trim( $newoptionvalue );
630
+
631
+ if ( GTM4WP_OPTION_GTM_CODE === $optionname ) {
632
+ $_gtmid_list = explode( ',', $newoptionvalue );
633
+ $_gtmid_haserror = false;
634
+
635
+ foreach ( $_gtmid_list as $one_gtm_id ) {
636
+ $_gtmid_haserror = $_gtmid_haserror || ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_gtm_id );
637
+ }
638
+
639
+ if ( $_gtmid_haserror ) {
640
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_CODE . ']', esc_html__( 'Invalid Google Tag Manager ID. Valid ID format: GTM-XXXXX. Use comma without additional space (,) to enter more than one container ID.', 'duracelltomi-google-tag-manager' ) );
641
+ } else {
642
+ $output[ $optionname ] = $newoptionvalue;
643
+ }
644
+ } elseif ( ( GTM4WP_OPTION_DATALAYER_NAME === $optionname ) && ( '' !== $newoptionvalue ) && ( ! preg_match( '/^[a-zA-Z][a-zA-Z0-9_-]*$/', $newoptionvalue ) ) ) {
645
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_DATALAYER_NAME . ']', esc_html__( "Invalid dataLayer variable name. Please start with a character from a-z or A-Z followed by characters from a-z, A-Z, 0-9 or '_' or '-'!", 'duracelltomi-google-tag-manager' ) );
646
+
647
+ } elseif ( ( GTM4WP_OPTION_ENV_GTM_AUTH === $optionname ) && ( '' !== $newoptionvalue ) && ( ! preg_match( '/^[a-zA-Z0-9-_]+$/', $newoptionvalue ) ) ) {
648
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_AUTH . ']', esc_html__( "Invalid gtm_auth environment parameter value. It should only contain letters, numbers or the '-' and '_' characters.", 'duracelltomi-google-tag-manager' ) );
649
+
650
+ } elseif ( ( GTM4WP_OPTION_ENV_GTM_PREVIEW === $optionname ) && ( '' !== $newoptionvalue ) && ( ! preg_match( '/^env-[0-9]+$/', $newoptionvalue ) ) ) {
651
+ add_settings_error( GTM4WP_ADMIN_GROUP, GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_ENV_GTM_PREVIEW . ']', esc_html__( "Invalid gtm_preview environment parameter value. It should have the format 'env-NN' where NN is an integer number.", 'duracelltomi-google-tag-manager' ) );
652
+
653
+ } else {
654
+ $output[ $optionname ] = $newoptionvalue;
655
+ }
656
+ } elseif ( GTM4WP_OPTION_GTM_PLACEMENT === $optionname ) {
657
+ // GTM container ON/OFF + compat mode.
658
+ $container_on_off = (bool) $options['container-on'];
659
+ $container_compat = (int) $options['compat-mode'];
660
+
661
+ if ( ! $container_on_off ) {
662
+ $output[ $optionname ] = GTM4WP_PLACEMENT_OFF;
663
+ } else {
664
+ if ( ( $container_compat < 0 ) || ( $container_compat > 2 ) ) {
665
+ $container_compat = 2;
666
+ }
667
+
668
+ $output[ $optionname ] = $container_compat;
669
+ }
670
+ } elseif ( GTM4WP_OPTION_SCROLLER_CONTENTID === $optionname ) {
671
+ // scroll tracking content ID.
672
+ $output[ $optionname ] = trim( str_replace( '#', '', $newoptionvalue ) );
673
+ } elseif ( GTM4WP_OPTION_NOGTMFORLOGGEDIN === $optionname ) {
674
+ // do not output GTM container code for specific user roles.
675
+ if ( is_array( $newoptionvalue ) ) {
676
+ $output[ $optionname ] = implode( ',', $newoptionvalue );
677
+ } else {
678
+ $output[ $optionname ] = '';
679
+ }
680
+ } else {
681
+ // anything else.
682
+ switch ( gettype( $optionvalue ) ) {
683
+ case 'boolean':
684
+ $output[ $optionname ] = (bool) $newoptionvalue;
685
+ break;
686
+
687
+ case 'integer':
688
+ $output[ $optionname ] = (int) $newoptionvalue;
689
+ break;
690
+
691
+ default:
692
+ $output[ $optionname ] = $newoptionvalue;
693
+ } // end switch.
694
+ }
695
+ }
696
+
697
+ return $output;
698
+ }
699
+
700
+ /**
701
+ * Function for admin_init hook. Adds option page tabs.
702
+ *
703
+ * @see https://developer.wordpress.org/reference/hooks/admin_init/
704
+ *
705
+ * @return void
706
+ */
707
+ function gtm4wp_admin_init() {
708
+ global $gtm4wp_includefieldtexts, $gtm4wp_eventfieldtexts, $gtm4wp_integratefieldtexts, $gtm4wp_scrollerfieldtexts,
709
+ $gtm4wp_advancedfieldtexts, $gtm4wp_entity_ids;
710
+
711
+ register_setting(
712
+ GTM4WP_ADMIN_GROUP,
713
+ GTM4WP_OPTIONS,
714
+ array(
715
+ 'sanitize_callback' => 'gtm4wp_sanitize_options',
716
+ )
717
+ );
718
+
719
+ add_settings_section(
720
+ GTM4WP_ADMIN_GROUP_GENERAL,
721
+ esc_html__( 'General', 'duracelltomi-google-tag-manager' ),
722
+ 'gtm4wp_admin_output_section',
723
+ GTM4WP_ADMINSLUG
724
+ );
725
+
726
+ add_settings_field(
727
+ GTM4WP_ADMIN_GROUP_GTMID,
728
+ esc_html__( 'Google Tag Manager ID', 'duracelltomi-google-tag-manager' ),
729
+ 'gtm4wp_admin_output_field',
730
+ GTM4WP_ADMINSLUG,
731
+ GTM4WP_ADMIN_GROUP_GENERAL,
732
+ array(
733
+ 'label_for' => GTM4WP_ADMIN_GROUP_GTMID,
734
+ 'description' => esc_html__( 'Enter your Google Tag Manager ID here. Use comma without space (,) to enter multiple IDs.', 'duracelltomi-google-tag-manager' ),
735
+ )
736
+ );
737
+
738
+ add_settings_field(
739
+ GTM4WP_ADMIN_GROUP_CONTAINERON,
740
+ esc_html__( 'Container code ON/OFF', 'duracelltomi-google-tag-manager' ),
741
+ 'gtm4wp_admin_output_field',
742
+ GTM4WP_ADMINSLUG,
743
+ GTM4WP_ADMIN_GROUP_GENERAL,
744
+ array(
745
+ 'label_for' => GTM4WP_ADMIN_GROUP_CONTAINERON,
746
+ 'description' => gtm4wp_safe_admin_html( 'Turning OFF the Google Tag Manager container itself will remove both the head and the body part of the container code but leave data layer codes working.<br/>This should be only used in specific cases where you need to place the container code manually or using another tool.', 'duracelltomi-google-tag-manager' ),
747
+ )
748
+ );
749
+
750
+ add_settings_field(
751
+ GTM4WP_ADMIN_GROUP_COMPATMODE,
752
+ esc_html__( 'Container code compatibility mode', 'duracelltomi-google-tag-manager' ),
753
+ 'gtm4wp_admin_output_field',
754
+ GTM4WP_ADMINSLUG,
755
+ GTM4WP_ADMIN_GROUP_GENERAL,
756
+ array(
757
+ 'label_for' => GTM4WP_ADMIN_GROUP_COMPATMODE,
758
+ 'description' => gtm4wp_safe_admin_html(
759
+ __(
760
+ 'Compatibility mode decides where to put the second, so called <code>&lt;noscript&gt;</code> or <code>&lt;iframe&gt;</code> part of the GTM container code.<br />
761
+ This code is usually only executed if your visitor has disabled JavaScript for some reason.<br/>
762
+ It is also mandatory in order to verify your site in Google Search Console using the GTM method.<br/>
763
+ The main GTM container code will be placed into the <code>&lt;head&gt;</code> section of your webpages anyway (where it belongs to).<br/><br/>
764
+ If you select "Manually coded", you need to edit your template files and add the following line just after the opening <code>&lt;body&gt;</code> tag:<br />
765
+ <code>&lt;?php if ( function_exists( \'gtm4wp_the_gtm_tag\' ) ) { gtm4wp_the_gtm_tag(); } ?&gt;</code>',
766
+ 'duracelltomi-google-tag-manager'
767
+ )
768
+ ),
769
+ )
770
+ );
771
+
772
+ add_settings_section(
773
+ GTM4WP_ADMIN_GROUP_INCLUDES,
774
+ esc_html__( 'Basic data', 'duracelltomi-google-tag-manager' ),
775
+ 'gtm4wp_admin_output_section',
776
+ GTM4WP_ADMINSLUG
777
+ );
778
+
779
+ foreach ( $gtm4wp_includefieldtexts as $fieldid => $fielddata ) {
780
+ $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
781
+
782
+ add_settings_field(
783
+ 'gtm4wp-admin-' . $fieldid . '-id',
784
+ $fielddata['label'] . '<span class="' . $phase . '"></span>',
785
+ 'gtm4wp_admin_output_field',
786
+ GTM4WP_ADMINSLUG,
787
+ GTM4WP_ADMIN_GROUP_INCLUDES,
788
+ array(
789
+ 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
790
+ 'description' => $fielddata['description'],
791
+ 'optionfieldid' => $fieldid,
792
+ )
793
+ );
794
+ }
795
+
796
+ add_settings_section(
797
+ GTM4WP_ADMIN_GROUP_EVENTS,
798
+ esc_html__( 'Events', 'duracelltomi-google-tag-manager' ),
799
+ 'gtm4wp_admin_output_section',
800
+ GTM4WP_ADMINSLUG
801
+ );
802
+
803
+ foreach ( $gtm4wp_eventfieldtexts as $fieldid => $fielddata ) {
804
+ $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
805
+
806
+ add_settings_field(
807
+ 'gtm4wp-admin-' . $fieldid . '-id',
808
+ $fielddata['label'] . '<span class="' . $phase . '"></span>',
809
+ 'gtm4wp_admin_output_field',
810
+ GTM4WP_ADMINSLUG,
811
+ GTM4WP_ADMIN_GROUP_EVENTS,
812
+ array(
813
+ 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
814
+ 'description' => $fielddata['description'],
815
+ 'optionfieldid' => $fieldid,
816
+ )
817
+ );
818
+ }
819
+
820
+ add_settings_section(
821
+ GTM4WP_ADMIN_GROUP_SCROLLER,
822
+ esc_html__( 'Scroll tracking', 'duracelltomi-google-tag-manager' ),
823
+ 'gtm4wp_admin_output_section',
824
+ GTM4WP_ADMINSLUG
825
+ );
826
+
827
+ foreach ( $gtm4wp_scrollerfieldtexts as $fieldid => $fielddata ) {
828
+ $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
829
+
830
+ add_settings_field(
831
+ 'gtm4wp-admin-' . $fieldid . '-id',
832
+ $fielddata['label'] . '<span class="' . $phase . '"></span>',
833
+ 'gtm4wp_admin_output_field',
834
+ GTM4WP_ADMINSLUG,
835
+ GTM4WP_ADMIN_GROUP_SCROLLER,
836
+ array(
837
+ 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
838
+ 'description' => $fielddata['description'],
839
+ 'optionfieldid' => $fieldid,
840
+ )
841
+ );
842
+ }
843
+
844
+ add_settings_section(
845
+ GTM4WP_ADMIN_GROUP_BLACKLIST,
846
+ esc_html__( 'Security', 'duracelltomi-google-tag-manager' ),
847
+ 'gtm4wp_admin_output_section',
848
+ GTM4WP_ADMINSLUG
849
+ );
850
+
851
+ add_settings_field(
852
+ GTM4WP_OPTION_BLACKLIST_ENABLE,
853
+ esc_html__( 'Enable blacklist/whitelist', 'duracelltomi-google-tag-manager' ),
854
+ 'gtm4wp_admin_output_field',
855
+ GTM4WP_ADMINSLUG,
856
+ GTM4WP_ADMIN_GROUP_BLACKLIST,
857
+ array(
858
+ 'label_for' => GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_ENABLE . ']',
859
+ 'description' => '',
860
+ 'optionsfieldid' => GTM4WP_OPTION_BLACKLIST_ENABLE,
861
+ )
862
+ );
863
+
864
+ add_settings_field(
865
+ GTM4WP_OPTION_BLACKLIST_SANDBOXED,
866
+ esc_html__( 'Custom tag/variable templates', 'duracelltomi-google-tag-manager' ),
867
+ 'gtm4wp_admin_output_field',
868
+ GTM4WP_ADMINSLUG,
869
+ GTM4WP_ADMIN_GROUP_BLACKLIST,
870
+ array(
871
+ 'label_for' => GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_BLACKLIST_SANDBOXED . ']',
872
+ 'description' => '',
873
+ 'entityid' => GTM4WP_OPTION_BLACKLIST_SANDBOXED,
874
+ )
875
+ );
876
+
877
+ foreach ( $gtm4wp_entity_ids as $gtm_entity_group_id => $gtm_entity_group_list ) {
878
+ foreach ( $gtm_entity_group_list as $gtm_entity_id => $gtm_entity_label ) {
879
+ add_settings_field(
880
+ 'gtm4wp-admin-blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id . '-id',
881
+ $gtm_entity_label,
882
+ 'gtm4wp_admin_output_field',
883
+ GTM4WP_ADMINSLUG,
884
+ GTM4WP_ADMIN_GROUP_BLACKLIST,
885
+ array(
886
+ 'label_for' => 'gtm4wp-options[blacklist-' . $gtm_entity_group_id . '-' . $gtm_entity_id . ']',
887
+ 'description' => '',
888
+ 'entityid' => $gtm_entity_id,
889
+ )
890
+ );
891
+ }
892
+ }
893
+
894
+ add_settings_section(
895
+ GTM4WP_ADMIN_GROUP_INTEGRATION,
896
+ esc_html__( 'Integration', 'duracelltomi-google-tag-manager' ),
897
+ 'gtm4wp_admin_output_section',
898
+ GTM4WP_ADMINSLUG
899
+ );
900
+
901
+ foreach ( $gtm4wp_integratefieldtexts as $fieldid => $fielddata ) {
902
+ $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
903
+
904
+ add_settings_field(
905
+ 'gtm4wp-admin-' . $fieldid . '-id',
906
+ $fielddata['label'] . '<span class="' . $phase . '"></span>',
907
+ 'gtm4wp_admin_output_field',
908
+ GTM4WP_ADMINSLUG,
909
+ GTM4WP_ADMIN_GROUP_INTEGRATION,
910
+ array(
911
+ 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
912
+ 'description' => $fielddata['description'],
913
+ 'optionfieldid' => $fieldid,
914
+ 'plugintocheck' => isset( $fielddata['plugintocheck'] ) ? $fielddata['plugintocheck'] : '',
915
+ )
916
+ );
917
+ }
918
+
919
+ add_settings_section(
920
+ GTM4WP_ADMIN_GROUP_ADVANCED,
921
+ esc_html__( 'Advanced', 'duracelltomi-google-tag-manager' ),
922
+ 'gtm4wp_admin_output_section',
923
+ GTM4WP_ADMINSLUG
924
+ );
925
+
926
+ foreach ( $gtm4wp_advancedfieldtexts as $fieldid => $fielddata ) {
927
+ $phase = isset( $fielddata['phase'] ) ? $fielddata['phase'] : GTM4WP_PHASE_STABLE;
928
+
929
+ add_settings_field(
930
+ 'gtm4wp-admin-' . $fieldid . '-id',
931
+ $fielddata['label'] . '<span class="' . $phase . '"></span>',
932
+ 'gtm4wp_admin_output_field',
933
+ GTM4WP_ADMINSLUG,
934
+ GTM4WP_ADMIN_GROUP_ADVANCED,
935
+ array(
936
+ 'label_for' => 'gtm4wp-options[' . $fieldid . ']',
937
+ 'description' => $fielddata['description'],
938
+ 'optionfieldid' => $fieldid,
939
+ 'plugintocheck' => isset( $fielddata['plugintocheck'] ) ? $fielddata['plugintocheck'] : '',
940
+ )
941
+ );
942
+ }
943
+
944
+ add_settings_section(
945
+ GTM4WP_ADMIN_GROUP_CREDITS,
946
+ esc_html__( 'Credits', 'duracelltomi-google-tag-manager' ),
947
+ 'gtm4wp_admin_output_section',
948
+ GTM4WP_ADMINSLUG
949
+ );
950
+
951
+ add_settings_field(
952
+ GTM4WP_ADMIN_GROUP_INFO,
953
+ esc_html__( 'Author', 'duracelltomi-google-tag-manager' ),
954
+ 'gtm4wp_admin_output_field',
955
+ GTM4WP_ADMINSLUG,
956
+ GTM4WP_ADMIN_GROUP_CREDITS,
957
+ array(
958
+ 'label_for' => GTM4WP_ADMIN_GROUP_INFO,
959
+ 'description' => '<strong>Thomas Geiger</strong><br />
960
+ Website: <a href="https://gtm4wp.com/" target="_blank" rel="noopener">gtm4wp.com</a><br />
961
+ <a href="https://www.linkedin.com/in/duracelltomi" target="_blank" rel="noopener">Me on LinkedIn</a><br />
962
+ <a href="http://www.linkedin.com/company/jabjab-online-marketing-ltd" target="_blank" rel="noopener">JabJab Online Marketing on LinkedIn</a>',
963
+ )
964
+ );
965
+
966
+ // Apply oembed code changes on the admin as well since the oembed call on the admin is cached by WordPress into a transient that is applied on the frontend later.
967
+ require_once dirname( __FILE__ ) . '/../integration/youtube.php';
968
+ require_once dirname( __FILE__ ) . '/../integration/vimeo.php';
969
+ require_once dirname( __FILE__ ) . '/../integration/soundcloud.php';
970
+ }
971
+
972
+ /**
973
+ * Callback function for add_options_page(). Generates the GTM4WP plugin options page.
974
+ *
975
+ * @see https://developer.wordpress.org/reference/functions/add_options_page/
976
+ *
977
+ * @return void
978
+ */
979
+ function gtm4wp_show_admin_page() {
980
+ global $gtp4wp_plugin_url;
981
+ ?>
982
+ <div class="wrap">
983
+ <h2><?php esc_html_e( 'Google Tag Manager for WordPress options', 'duracelltomi-google-tag-manager' ); ?></h2>
984
+ <form action="options.php" method="post">
985
+
986
+ <?php settings_fields( GTM4WP_ADMIN_GROUP ); ?>
987
+ <?php do_settings_sections( GTM4WP_ADMINSLUG ); ?>
988
+ <?php submit_button(); ?>
989
+
990
+ </form>
991
+ </div>
992
+ <?php
993
+ }
994
+
995
+ /**
996
+ * Hook function for admin_menu. Adds the plugin options page into the Settings menu of the WordPress admin.
997
+ *
998
+ * @see https://developer.wordpress.org/reference/hooks/admin_menu/
999
+ *
1000
+ * @return void
1001
+ */
1002
+ function gtm4wp_add_admin_page() {
1003
+ add_options_page(
1004
+ esc_html__( 'Google Tag Manager for WordPress settings', 'duracelltomi-google-tag-manager' ),
1005
+ esc_html__( 'Google Tag Manager', 'duracelltomi-google-tag-manager' ),
1006
+ 'manage_options',
1007
+ GTM4WP_ADMINSLUG,
1008
+ 'gtm4wp_show_admin_page'
1009
+ );
1010
+ }
1011
+
1012
+ /**
1013
+ * Hook function for admin_enqueue_scripts(). Adds the frontend JavaScript code into the WordPress admin when GTM4WP option page is loaded.
1014
+ *
1015
+ * @see https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
1016
+ *
1017
+ * @param string $hook The ID of the option page that is currently being shown.
1018
+ * @return void
1019
+ */
1020
+ function gtm4wp_add_admin_js( $hook ) {
1021
+ global $gtp4wp_plugin_url;
1022
+
1023
+ if ( 'settings_page_' . GTM4WP_ADMINSLUG === $hook ) {
1024
+ // phpcs ignore set due to in_footer set to true does not load the script.
1025
+ wp_register_script( 'admin-subtabs', $gtp4wp_plugin_url . 'js/admin-subtabs.js', array(), GTM4WP_VERSION ); // phpcs:ignore
1026
+
1027
+ $subtabtexts = array(
1028
+ 'posttabtitle' => esc_html__( 'Posts', 'duracelltomi-google-tag-manager' ),
1029
+ 'searchtabtitle' => esc_html__( 'Search', 'duracelltomi-google-tag-manager' ),
1030
+ 'visitortabtitle' => esc_html__( 'Visitors', 'duracelltomi-google-tag-manager' ),
1031
+ 'browsertabtitle' => esc_html__( 'Browser/OS/Device', 'duracelltomi-google-tag-manager' ),
1032
+ 'blocktagstabtitle' => esc_html__( 'Blacklist tags', 'duracelltomi-google-tag-manager' ),
1033
+ 'blocktriggerstabtitle' => esc_html__( 'Blacklist triggers', 'duracelltomi-google-tag-manager' ),
1034
+ 'blockmacrostabtitle' => esc_html__( 'Blacklist variables', 'duracelltomi-google-tag-manager' ),
1035
+ 'wpcf7tabtitle' => esc_html__( 'Contact Form 7', 'duracelltomi-google-tag-manager' ),
1036
+ 'wctabtitle' => esc_html__( 'WooCommerce', 'duracelltomi-google-tag-manager' ),
1037
+ 'gotabtitle' => esc_html__( 'Google Optimize', 'duracelltomi-google-tag-manager' ),
1038
+ 'amptabtitle' => esc_html__( 'Accelerated Mobile Pages', 'duracelltomi-google-tag-manager' ),
1039
+ 'cookiebottabtitle' => esc_html__( 'Cookiebot', 'duracelltomi-google-tag-manager' ),
1040
+ 'weathertabtitle' => esc_html__( 'Weather & geo data', 'duracelltomi-google-tag-manager' ),
1041
+ 'generaleventstabtitle' => esc_html__( 'General events', 'duracelltomi-google-tag-manager' ),
1042
+ 'mediaeventstabtitle' => esc_html__( 'Media events', 'duracelltomi-google-tag-manager' ),
1043
+ 'depecratedeventstabtitle' => esc_html__( 'Deprecated', 'duracelltomi-google-tag-manager' ),
1044
+ 'sitetabtitle' => esc_html__( 'Site', 'duracelltomi-google-tag-manager' ),
1045
+ 'misctabtitle' => esc_html__( 'Misc', 'duracelltomi-google-tag-manager' ),
1046
+ );
1047
+ wp_localize_script( 'admin-subtabs', 'gtm4wp', $subtabtexts );
1048
+
1049
+ wp_enqueue_script( 'admin-subtabs' );
1050
+
1051
+ // phpcs ignore set due to in_footer set to true does not load the script.
1052
+ wp_enqueue_script( 'admin-tabcreator', $gtp4wp_plugin_url . 'js/admin-tabcreator.js', array( 'jquery' ), GTM4WP_VERSION ); // phpcs:ignore
1053
+
1054
+ wp_enqueue_style( 'gtm4wp-admin-css', $gtp4wp_plugin_url . 'css/admin-gtm4wp.css', array(), GTM4WP_VERSION );
1055
+ }
1056
+ }
1057
+
1058
+ /**
1059
+ * Hook function for admin_head(). Adds some inline style and JavaScript into the header of the admin page.
1060
+ *
1061
+ * @see https://developer.wordpress.org/reference/hooks/admin_head/
1062
+ *
1063
+ * @return void
1064
+ */
1065
+ function gtm4wp_admin_head() {
1066
+ echo '
1067
+ <style type="text/css">
1068
+ .gtmid_validation_error,
1069
+ .goid_validation_error,
1070
+ .goid_ga_validation_error,
1071
+ .ampid_validation_error,
1072
+ .datalayername_validation_error,
1073
+ .gtmauth_validation_error,
1074
+ .gtmpreview_validation_error,
1075
+ .gtm_wpconfig_set {
1076
+ color: #c00;
1077
+ font-weight: bold;
1078
+ }
1079
+ .gtmid_validation_error,
1080
+ .goid_validation_error,
1081
+ .goid_ga_validation_error,
1082
+ .ampid_validation_error,
1083
+ .datalayername_validation_error,
1084
+ .gtmauth_validation_error,
1085
+ .gtmpreview_validation_error {
1086
+ display: none;
1087
+ }
1088
+ </style>
1089
+ <script type="text/javascript">
1090
+ jQuery(function() {
1091
+ jQuery( "#gtm4wp-options\\\\[gtm-code\\\\]" )
1092
+ .on( "blur", function() {
1093
+ var gtmid_regex = /^GTM-[A-Z0-9]+$/;
1094
+ var gtmid_list_str = jQuery( this ).val();
1095
+ if ( typeof gtmid_list_str != "string" ) {
1096
+ return;
1097
+ }
1098
+ var gtmid_list = gtmid_list_str.trim().split( "," );
1099
+
1100
+ var gtmid_haserror = false;
1101
+ for( var i=0; i<gtmid_list.length; i++ ) {
1102
+ gtmid_haserror = gtmid_haserror || !gtmid_regex.test( gtmid_list[ i ] );
1103
+ }
1104
+
1105
+ if ( gtmid_haserror ) {
1106
+ jQuery( ".gtmid_validation_error" )
1107
+ .show();
1108
+ } else {
1109
+ jQuery( ".gtmid_validation_error" )
1110
+ .hide();
1111
+ }
1112
+ });
1113
+
1114
+ jQuery( "#gtm4wp-options\\\\[integrate-google-optimize-idlist\\\\]" )
1115
+ .on( "blur", function() {
1116
+ var goid_regex = /^(GTM|OPT)-[A-Z0-9]+$/;
1117
+ var goid_val_str = jQuery( this ).val();
1118
+ if ( typeof goid_val_str != "string" ) {
1119
+ return;
1120
+ }
1121
+ var goid_val = goid_val_str.trim();
1122
+ if ( "" == goid_val ) {
1123
+ goid_list = [];
1124
+ } else {
1125
+ var goid_list = goid_val.split( "," );
1126
+ }
1127
+
1128
+ var goid_haserror = false;
1129
+ for( var i=0; i<goid_list.length; i++ ) {
1130
+ goid_haserror = goid_haserror || !goid_regex.test( goid_list[ i ] );
1131
+ }
1132
+
1133
+ if ( goid_haserror && (goid_list.length > 0) ) {
1134
+ jQuery( ".goid_validation_error" )
1135
+ .show();
1136
+ } else {
1137
+ jQuery( ".goid_validation_error" )
1138
+ .hide();
1139
+ }
1140
+ });
1141
+
1142
+ jQuery( "#gtm4wp-options\\\\[integrate-google-optimize-gaid\\\\]" )
1143
+ .on( "blur", function() {
1144
+ var gogaid_regex = /^UA-[0-9]+-[0-9]+$/;
1145
+ var gogaid_val_str = jQuery( this ).val();
1146
+ if ( typeof gogaid_val_str != "string" ) {
1147
+ return;
1148
+ }
1149
+ var gogaid_val = gogaid_val_str.trim();
1150
+ if ( "" == gogaid_val ) {
1151
+ gogaid_list = [];
1152
+ } else {
1153
+ var gogaid_list = gogaid_val.split( "," );
1154
+ }
1155
+
1156
+ var gogaid_haserror = false;
1157
+ for( var i=0; i<gogaid_list.length; i++ ) {
1158
+ gogaid_haserror = gogaid_haserror || !gogaid_regex.test( gogaid_list[ i ] );
1159
+ }
1160
+
1161
+ if ( gogaid_haserror && (gogaid_list.length > 0) ) {
1162
+ jQuery( ".goid_ga_validation_error" )
1163
+ .show();
1164
+ } else {
1165
+ jQuery( ".goid_ga_validation_error" )
1166
+ .hide();
1167
+ }
1168
+ });
1169
+
1170
+ jQuery( "#gtm4wp-options\\\\[integrate-amp-id\\\\]" )
1171
+ .on( "blur", function() {
1172
+ var ampid_regex = /^GTM-[A-Z0-9]+$/;
1173
+ var ampid_val_str = jQuery( this ).val();
1174
+ if ( typeof ampid_val_str != "string" ) {
1175
+ return;
1176
+ }
1177
+ var ampid_val = ampid_val_str.trim();
1178
+ if ( "" == ampid_val ) {
1179
+ ampid_list = [];
1180
+ } else {
1181
+ var ampid_list = ampid_val.split( "," );
1182
+ }
1183
+
1184
+ var ampid_haserror = false;
1185
+ for( var i=0; i<ampid_list.length; i++ ) {
1186
+ ampid_haserror = ampid_haserror || !ampid_regex.test( ampid_list[ i ] );
1187
+ }
1188
+
1189
+ if ( ampid_haserror && (ampid_list.length > 0) ) {
1190
+ jQuery( ".ampid_validation_error" )
1191
+ .show();
1192
+ } else {
1193
+ jQuery( ".ampid_validation_error" )
1194
+ .hide();
1195
+ }
1196
+ });
1197
+
1198
+ jQuery( "#gtm4wp-options\\\\[gtm-datalayer-variable-name\\\\]" )
1199
+ .on( "blur", function() {
1200
+ var currentval = jQuery( this ).val();
1201
+
1202
+ jQuery( ".datalayername_validation_error" )
1203
+ .hide();
1204
+
1205
+ if ( currentval != "" ) {
1206
+ // I know this is not the exact definition for a variable name but I think other kind of variable names should not be used.
1207
+ var gtmvarname_regex = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
1208
+ if ( ! gtmvarname_regex.test( currentval ) ) {
1209
+ jQuery( ".datalayername_validation_error" )
1210
+ .show();
1211
+ }
1212
+ }
1213
+ });
1214
+
1215
+ jQuery( "#gtm4wp-options\\\\[gtm-env-gtm-auth\\\\]" )
1216
+ .on( "blur", function() {
1217
+ var currentval = jQuery( this ).val();
1218
+
1219
+ jQuery( ".gtmauth_validation_error" )
1220
+ .hide();
1221
+
1222
+ if ( currentval != "" ) {
1223
+ var gtmauth_regex = /^[a-zA-Z0-9-_]+$/;
1224
+ if ( ! gtmauth_regex.test( currentval ) ) {
1225
+ jQuery( ".gtmauth_validation_error" )
1226
+ .show();
1227
+ }
1228
+ }
1229
+ });
1230
+
1231
+ jQuery( "#gtm4wp-options\\\\[gtm-env-gtm-preview\\\\]" )
1232
+ .on( "blur", function() {
1233
+ var currentval = jQuery( this ).val();
1234
+
1235
+ jQuery( ".gtmpreview_validation_error" )
1236
+ .hide();
1237
+
1238
+ if ( currentval != "" ) {
1239
+ var gtmpreview_regex = /^env-[0-9]+$/;
1240
+ if ( ! gtmpreview_regex.test( currentval ) ) {
1241
+ jQuery( ".gtmpreview_validation_error" )
1242
+ .show();
1243
+ }
1244
+ }
1245
+ });
1246
+
1247
+ jQuery( document )
1248
+ .on( "click", ".gtm4wp-notice .notice-dismiss", function( e ) {
1249
+ jQuery.post(ajaxurl, {
1250
+ action: "gtm4wp_dismiss_notice",
1251
+ noticeid: jQuery( this ).closest(".gtm4wp-notice")
1252
+ .attr( "data-href" )
1253
+ .substring( 1 ),
1254
+ nonce: "' . esc_html( wp_create_nonce( 'gtm4wp-notice-dismiss-nonce' ) ) . '"
1255
+ });
1256
+ });
1257
+ });
1258
+ </script>';
1259
+ }
1260
+
1261
+ /**
1262
+ * Hook function for admin_notices. Shows warning messages on the WordPress admin page about possible conflicting plugins.
1263
+ *
1264
+ * @see https://developer.wordpress.org/reference/hooks/admin_notices/
1265
+ *
1266
+ * @return void
1267
+ */
1268
+ function gtm4wp_show_warning() {
1269
+ global $gtm4wp_options, $gtp4wp_plugin_url, $gtm4wp_integratefieldtexts, $current_user,
1270
+ $gtm4wp_def_user_notices_dismisses;
1271
+
1272
+ $woo_plugin_active = is_plugin_active( $gtm4wp_integratefieldtexts[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ]['plugintocheck'] );
1273
+ if ( $woo_plugin_active && function_exists( 'WC' ) ) {
1274
+ $woo = WC();
1275
+ } else {
1276
+ $woo = null;
1277
+ }
1278
+
1279
+ $gtm4wp_user_notices_dismisses = get_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, true );
1280
+ if ( '' === $gtm4wp_user_notices_dismisses ) {
1281
+ if ( is_array( $gtm4wp_def_user_notices_dismisses ) ) {
1282
+ $gtm4wp_user_notices_dismisses = $gtm4wp_def_user_notices_dismisses;
1283
+ } else {
1284
+ $gtm4wp_user_notices_dismisses = array();
1285
+ }
1286
+ } else {
1287
+ $gtm4wp_user_notices_dismisses = json_decode( $gtm4wp_user_notices_dismisses, true );
1288
+ if ( null === $gtm4wp_user_notices_dismisses || ! is_array( $gtm4wp_user_notices_dismisses ) ) {
1289
+ $gtm4wp_user_notices_dismisses = array();
1290
+ }
1291
+ }
1292
+ $gtm4wp_user_notices_dismisses = array_merge( $gtm4wp_def_user_notices_dismisses, $gtm4wp_user_notices_dismisses );
1293
+
1294
+ if ( ( '' === trim( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) ) && ( false === $gtm4wp_user_notices_dismisses['enter-gtm-code'] ) ) {
1295
+ echo '<div class="gtm4wp-notice notice notice-error is-dismissible" data-href="?enter-gtm-code"><p><strong>';
1296
+ echo sprintf(
1297
+ // translators: 1: opening anchor element pointing to the GTM4WP options page. 2: clsing anchor element.
1298
+ esc_html__(
1299
+ 'To start using Google Tag Manager for WordPress, please %1$senter your GTM ID%2$s',
1300
+ 'duracelltomi-google-tag-manager'
1301
+ ),
1302
+ '<a href="' . esc_url( menu_page_url( GTM4WP_ADMINSLUG, false ) ) . '">',
1303
+ '</a>'
1304
+ );
1305
+ echo '</strong></p></div>';
1306
+ }
1307
+
1308
+ if ( (
1309
+ ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' === $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] )
1310
+ ) || (
1311
+ ( '' === $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] )
1312
+ ) ) {
1313
+ echo '<div class="gtm4wp-notice notice notice-error" data-href="?incomplete-gtm-env-config"><p><strong>';
1314
+ esc_html_e(
1315
+ 'Incomplete Google Tag Manager environment configuration: either gtm_preview or gtm_auth parameter value is missing!',
1316
+ 'duracelltomi-google-tag-manager'
1317
+ );
1318
+ echo '</strong></p></div>';
1319
+ }
1320
+
1321
+ if ( ( false === $gtm4wp_user_notices_dismisses['wc-ga-plugin-warning'] ) || ( false === $gtm4wp_user_notices_dismisses['wc-gayoast-plugin-warning'] ) ) {
1322
+ $is_wc_active = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ||
1323
+ $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ||
1324
+ $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ];
1325
+
1326
+ if ( ( false === $gtm4wp_user_notices_dismisses['wc-ga-plugin-warning'] ) && $is_wc_active && is_plugin_active( 'woocommerce-google-analytics-integration/woocommerce-google-analytics-integration.php' ) ) {
1327
+ echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?wc-ga-plugin-warning"><p><strong>' . esc_html__( 'Notice: you should deactivate the plugin "WooCommerce Google Analytics Integration" if you are using Google Analytics tags inside Google Tag Manager!', 'duracelltomi-google-tag-manager' ) . '</strong></p></div>';
1328
+ }
1329
+
1330
+ if ( ( false === $gtm4wp_user_notices_dismisses['wc-gayoast-plugin-warning'] ) && $is_wc_active && is_plugin_active( 'google-analytics-for-wordpress/googleanalytics.php' ) ) {
1331
+ echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?wc-gayoast-plugin-warning"><p><strong>' . esc_html__( 'Notice: you should deactivate the plugin "Google Analytics for WordPress by MonsterInsights" if you are using Google Analytics tags inside Google Tag Manager!', 'duracelltomi-google-tag-manager' ) . '</strong></p></div>';
1332
+ }
1333
+ }
1334
+
1335
+ if ( ( false === $gtm4wp_user_notices_dismisses['php72-warning'] ) && ( version_compare( PHP_VERSION, '7.2.0' ) < 0 ) ) {
1336
+ echo '<div class="gtm4wp-notice notice notice-warning is-dismissible" data-href="?php72-warning"><p><strong>';
1337
+ printf(
1338
+ // translators: %s: PHP version number.
1339
+ esc_html__(
1340
+ 'Warning: You are using an outdated version of PHP (%s) that might be not compatible with future versions of the plugin Google Tag Manager for WordPress (GTM4WP). Please consider to upgrade your PHP.',
1341
+ 'duracelltomi-google-tag-manager'
1342
+ ),
1343
+ PHP_VERSION
1344
+ );
1345
+ echo '</strong></p></div>';
1346
+ }
1347
+ }
1348
+
1349
+ /**
1350
+ * Action run when users dismiss a notice of GTM4WP on the WordPress admin.
1351
+ * Saves the dismissed notice ID as user meta to hide the notice on next pageview.
1352
+ *
1353
+ * @see https://developer.wordpress.org/reference/hooks/wp_ajax_action/
1354
+ *
1355
+ * @return void
1356
+ */
1357
+ function gtm4wp_dismiss_notice() {
1358
+ global $gtm4wp_def_user_notices_dismisses, $current_user;
1359
+
1360
+ check_ajax_referer( 'gtm4wp-notice-dismiss-nonce', 'nonce' );
1361
+
1362
+ $gtm4wp_user_notices_dismisses = get_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, true );
1363
+ if ( '' === $gtm4wp_user_notices_dismisses ) {
1364
+ if ( is_array( $gtm4wp_def_user_notices_dismisses ) ) {
1365
+ $gtm4wp_user_notices_dismisses = $gtm4wp_def_user_notices_dismisses;
1366
+ } else {
1367
+ $gtm4wp_user_notices_dismisses = array();
1368
+ }
1369
+ } else {
1370
+ $gtm4wp_user_notices_dismisses = json_decode( $gtm4wp_user_notices_dismisses, true );
1371
+ if ( null === $gtm4wp_user_notices_dismisses || ! is_array( $gtm4wp_user_notices_dismisses ) ) {
1372
+ $gtm4wp_user_notices_dismisses = array();
1373
+ }
1374
+ }
1375
+ $gtm4wp_user_notices_dismisses = array_merge( $gtm4wp_def_user_notices_dismisses, $gtm4wp_user_notices_dismisses );
1376
+
1377
+ $noticeid = isset( $_POST['noticeid'] ) ? esc_url_raw( wp_unslash( $_POST['noticeid'] ) ) : '';
1378
+ $noticeid = trim( basename( $noticeid ) );
1379
+ if ( array_key_exists( $noticeid, $gtm4wp_user_notices_dismisses ) ) {
1380
+ $gtm4wp_user_notices_dismisses[ $noticeid ] = true;
1381
+ update_user_meta( $current_user->ID, GTM4WP_USER_NOTICES_KEY, wp_json_encode( $gtm4wp_user_notices_dismisses ) );
1382
+ }
1383
+ }
1384
+
1385
+ /**
1386
+ * Action hook for plugin_action_links.
1387
+ * Adds link to the settings page on the Plugins page.
1388
+ *
1389
+ * @see https://developer.wordpress.org/reference/hooks/plugin_action_links/
1390
+ *
1391
+ * @param array $links Existing links that can be extended by this action.
1392
+ * @param string $file Plugin ID where the links belong to.
1393
+ * @return array Array of anchor HTML elements with links that will be shown below the plugin on the Plugins page.
1394
+ */
1395
+ function gtm4wp_add_plugin_action_links( $links, $file ) {
1396
+ global $gtp4wp_plugin_basename;
1397
+
1398
+ if ( $file !== $gtp4wp_plugin_basename ) {
1399
+ return $links;
1400
+ }
1401
+
1402
+ $settings_link = '<a href="' . menu_page_url( GTM4WP_ADMINSLUG, false ) . '">' . esc_html__( 'Settings', 'duracelltomi-google-tag-manager' ) . '</a>';
1403
+
1404
+ array_unshift( $links, $settings_link );
1405
+
1406
+ return $links;
1407
+ }
1408
+
1409
+ /**
1410
+ * Action hook for in_plugin_update_message-file.
1411
+ * Shows upgrade message below plugin description.
1412
+ *
1413
+ * @see https://developer.wordpress.org/reference/hooks/in_plugin_update_message-file/
1414
+ *
1415
+ * @param array $current_plugin_metadata Meta data of the currently active plugin version.
1416
+ * @param object $new_plugin_metadata Meta data of the available new plugin version.
1417
+ * @return void
1418
+ */
1419
+ function gtm4wp_show_upgrade_notification( $current_plugin_metadata, $new_plugin_metadata ) {
1420
+ if ( isset( $new_plugin_metadata->upgrade_notice ) && strlen( trim( $new_plugin_metadata->upgrade_notice ) ) > 0 ) {
1421
+ echo '<p style="background-color: #d54e21; padding: 10px; color: #f9f9f9; margin-top: 10px"><strong>Important Upgrade Notice:</strong> ';
1422
+ echo esc_html( $new_plugin_metadata->upgrade_notice ), '</p>';
1423
+ }
1424
+ }
1425
+
1426
+ add_action( 'admin_init', 'gtm4wp_admin_init' );
1427
+ add_action( 'admin_menu', 'gtm4wp_add_admin_page' );
1428
+ add_action( 'admin_enqueue_scripts', 'gtm4wp_add_admin_js' );
1429
+ add_action( 'admin_notices', 'gtm4wp_show_warning' );
1430
+ add_action( 'admin_head', 'gtm4wp_admin_head' );
1431
+ add_filter( 'plugin_action_links', 'gtm4wp_add_plugin_action_links', 10, 2 );
1432
+ add_action( 'wp_ajax_gtm4wp_dismiss_notice', 'gtm4wp_dismiss_notice' );
1433
+ add_action( 'in_plugin_update_message-duracelltomi-google-tag-manager-for-wordpress/duracelltomi-google-tag-manager-for-wordpress.php', 'gtm4wp_show_upgrade_notification', 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/readoptions.php CHANGED
@@ -445,7 +445,7 @@ function gtm4wp_migrate_blacklist_whitelist( $current_options ) {
445
  function gtm4wp_reload_options() {
446
  global $gtm4wp_defaultoptions, $gtm4wp_business_verticals;
447
 
448
- $storedoptions = (array) get_option( GTM4WP_OPTIONS );
449
  if ( ! is_array( $gtm4wp_defaultoptions ) ) {
450
  $gtm4wp_defaultoptions = array();
451
  }
@@ -454,13 +454,9 @@ function gtm4wp_reload_options() {
454
  $storedoptions = gtm4wp_migrate_blacklist_whitelist( $storedoptions );
455
  }
456
 
457
- // phpcs:ignore
458
  $return_options = array_merge( $gtm4wp_defaultoptions, $storedoptions );
459
- $return_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] = explode( ',', $return_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] );
460
 
461
- if ( defined( 'GTM4WP_HARDCODED_GTM_ID' ) ) {
462
- $return_options[ GTM4WP_OPTION_GTM_CODE ] = constant( 'GTM4WP_HARDCODED_GTM_ID' );
463
- }
464
 
465
  if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ) {
466
  $return_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] = constant( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' );
@@ -470,6 +466,33 @@ function gtm4wp_reload_options() {
470
  $return_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] = constant( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' );
471
  }
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  if ( ! array_key_exists( $return_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ], $gtm4wp_business_verticals ) ) {
474
  $return_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] = $gtm4wp_defaultoptions[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ];
475
  }
445
  function gtm4wp_reload_options() {
446
  global $gtm4wp_defaultoptions, $gtm4wp_business_verticals;
447
 
448
+ $storedoptions = get_option( GTM4WP_OPTIONS, array() );
449
  if ( ! is_array( $gtm4wp_defaultoptions ) ) {
450
  $gtm4wp_defaultoptions = array();
451
  }
454
  $storedoptions = gtm4wp_migrate_blacklist_whitelist( $storedoptions );
455
  }
456
 
 
457
  $return_options = array_merge( $gtm4wp_defaultoptions, $storedoptions );
 
458
 
459
+ $return_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] = explode( ',', $return_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] );
 
 
460
 
461
  if ( defined( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' ) ) {
462
  $return_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] = constant( 'GTM4WP_HARDCODED_GTM_ENV_AUTH' );
466
  $return_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] = constant( 'GTM4WP_HARDCODED_GTM_ENV_PREVIEW' );
467
  }
468
 
469
+ if ( defined( 'GTM4WP_HARDCODED_GTM_ID' ) ) {
470
+ $hardcoded_gtm_id = constant( 'GTM4WP_HARDCODED_GTM_ID' );
471
+
472
+ // validate hard coded GTM ID before overriding stored value.
473
+ $_gtmid_list = explode( ',', $hardcoded_gtm_id );
474
+ $_gtmid_haserror = false;
475
+
476
+ foreach ( $_gtmid_list as $one_gtm_id ) {
477
+ $_gtmid_haserror = $_gtmid_haserror || ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_gtm_id );
478
+ }
479
+
480
+ if ( ! $_gtmid_haserror ) {
481
+ $return_options[ GTM4WP_OPTION_GTM_CODE ] = $hardcoded_gtm_id;
482
+ }
483
+ }
484
+
485
+ // only load the first container if environment parameters are set.
486
+ if (
487
+ ( '' !== $return_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) &&
488
+ ( '' !== $return_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] )
489
+ ) {
490
+ $_gtmid_list = explode( ',', $return_options[ GTM4WP_OPTION_GTM_CODE ] );
491
+ if ( count( $_gtmid_list ) > 0 ) {
492
+ $return_options[ GTM4WP_OPTION_GTM_CODE ] = $_gtmid_list[0];
493
+ }
494
+ }
495
+
496
  if ( ! array_key_exists( $return_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ], $gtm4wp_business_verticals ) ) {
497
  $return_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] = $gtm4wp_defaultoptions[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ];
498
  }
duracelltomi-google-tag-manager-for-wordpress.php CHANGED
@@ -11,7 +11,7 @@
11
  * Plugin Name: GTM4WP
12
  * Plugin URI: https://gtm4wp.com/
13
  * Description: The first Google Tag Manager plugin for WordPress with business goals in mind
14
- * Version: 1.15.2
15
  * Requires at least: 3.4.0
16
  * Requires PHP: 5.6
17
  * Author: Thomas Geiger
@@ -22,10 +22,10 @@
22
  * Domain Path: /languages
23
 
24
  * WC requires at least: 3.2
25
- * WC tested up to: 6.3.1
26
  */
27
 
28
- define( 'GTM4WP_VERSION', '1.15.2' );
29
  define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
30
 
31
  global $gtp4wp_plugin_url, $gtp4wp_plugin_basename;
11
  * Plugin Name: GTM4WP
12
  * Plugin URI: https://gtm4wp.com/
13
  * Description: The first Google Tag Manager plugin for WordPress with business goals in mind
14
+ * Version: 1.16
15
  * Requires at least: 3.4.0
16
  * Requires PHP: 5.6
17
  * Author: Thomas Geiger
22
  * Domain Path: /languages
23
 
24
  * WC requires at least: 3.2
25
+ * WC tested up to: 6.6
26
  */
27
 
28
+ define( 'GTM4WP_VERSION', '1.16' );
29
  define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
30
 
31
  global $gtp4wp_plugin_url, $gtp4wp_plugin_basename;
integration/amp.php CHANGED
@@ -1,161 +1,167 @@
1
- <?php
2
- /**
3
- * AMP (Accelerated Mobile Pages Support) for gtm4wp
4
- *
5
- * This intergration added AMP support when using amp-wp, the existing dataLayer used
6
- * for Google Tag Manager on HTML is loaded and built into AMP compatible code.
7
- *
8
- * @author Vincent Koc <https://github.com/koconder/>
9
- * @package gtm4wp
10
- */
11
-
12
- /**
13
- *
14
- * Todo's
15
- *
16
- * - Better handling of gtm4wp_amp_gtmampcode_check() to allow for other plugins
17
- * - Develop array's into strings as AMP GTM dose not allow custom js variables
18
- * - Better Client ID support (https://github.com/Automattic/amp-wp/issues/775)
19
- * - Update AMP cache on GTM changes (https://github.com/Automattic/amp-wp/pull/605)
20
- * - Supporting PWA with SuperPWA intergration with AMP and PWA for offline
21
- */
22
-
23
-
24
- /**
25
- * Check if we are running AMP
26
- *
27
- * @author Vincent Koc <https://github.com/koconder/>
28
- * @return bool Returns true if we are running on an AMP page
29
- */
30
- function gtm4wp_amp_running() {
31
- if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
32
- return true;
33
- }
34
-
35
- return false;
36
- }
37
-
38
- /**
39
- * Pre-injection Check AMP Project's AMP Analytics tag, using amp-wp hook
40
- *
41
- * @link https://github.com/Automattic/amp-wp/blob/develop/includes/amp-post-template-actions.php
42
- * @author Vincent Koc <https://github.com/koconder/>
43
- * @return array Returns AMP Analytics array used by amp-wp
44
- */
45
- function gtm4wp_amp_gtmampcode_check( $data ) {
46
- global $gtm4wp_amp_headerinjected;
47
-
48
- //run once
49
- if($gtm4wp_amp_headerinjected){
50
- return $data;
51
- }
52
-
53
- // AMP-WP Plugin
54
- if ( ! empty( $data['amp_analytics'] ) ) {
55
- // Inject into AMP Plugin to load
56
- $data['amp_component_scripts']['amp-analytics'] = 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js';
57
- $gtm4wp_amp_headerinjected = true;
58
-
59
- // Manually load into AMP-WP Plugin
60
- } else {
61
- // Inject manually based on AMP <head> hook
62
- add_action( 'amp_post_template_head', 'gtm4wp_amp_gtmampcode_injecthead' );
63
- }
64
-
65
- // Return the $data back to amp-wp hook
66
- return $data;
67
- }
68
-
69
- /**
70
- * Post-check AMP Project's AMP Analytics tag, using amp-wp hook
71
- *
72
- * @link https://github.com/Automattic/amp-wp/blob/develop/includes/amp-post-template-actions.php
73
- * @author Vincent Koc <https://github.com/koconder/>
74
- */
75
- function gtm4wp_amp_gtmampcode_injecthead() {
76
- global $gtm4wp_amp_headerinjected;
77
-
78
- $gtm4wp_amp_headerinjected = true;
79
- echo '<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>';
80
- }
81
-
82
- /**
83
- * Generate AMP ready Google Tag Manager code
84
- *
85
- * @author Vincent Koc <https://github.com/koconder/>
86
- * @return int Returns number of injected snippets, false if no injection
87
- */
88
- function gtm4wp_amp_gtmcode() {
89
- global $gtm4wp_datalayer_json, $gtm4wp_options, $gtm4wp_amp_bodyinjected;
90
-
91
- //run once
92
- if($gtm4wp_amp_bodyinjected){
93
- return false;
94
- }
95
-
96
- $gtm4wp_amp_bodyinjected = true;
97
- // Check dataLayer is loaded from the plugin
98
- if ( ! empty( $gtm4wp_datalayer_json ) ) {
99
-
100
- // Builds a list of GTM id's
101
- $gtm4wp_ampids = explode( ',', $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_AMPID ] );
102
- $gtm4wp_ampid_list = array();
103
-
104
- // Counter used for status return
105
- $x = 0;
106
-
107
- // Check we have more than one valid Google Tag Manager ID
108
- if ( count( $gtm4wp_ampids ) > 0 ) {
109
-
110
- // Loop through each GTM idea and build the AMP GTM code
111
- foreach ( $gtm4wp_ampids as $gtm4wp_oneampid ) {
112
-
113
- // Docs: https://developers.google.com/analytics/devguides/collection/amp-analytics/
114
- // Examples: from https://www.simoahava.com/analytics/accelerated-mobile-pages-via-google-tag-manager/
115
- // Inject the AMP GTM code
116
- // TODO: Use AMP classes to enable cross-compatibility with other future plugins
117
- echo '<!-- Google Tag Manager --><amp-analytics config="https://www.googletagmanager.com/amp.json?id=' . $gtm4wp_oneampid . '&gtm.url=SOURCE_URL" data-credentials="include"><script type="application/json">' . gtm4wp_amp_gtmvariables() . '</script></amp-analytics>';
118
-
119
- // Add to counter
120
- $x++;
121
- }
122
-
123
- // Check how many injections for return
124
- if ( $x > 0 ) {
125
- return $x;
126
- }
127
- }
128
- }
129
-
130
- // No injection has occured
131
- return false;
132
- }
133
-
134
- /**
135
- * Generate the AMP "vars" from the GTM dataLayer
136
- *
137
- * @author Vincent Koc <https://github.com/koconder/>
138
- * @return string Returns json dataLayer for AMP code
139
- */
140
- function gtm4wp_amp_gtmvariables() {
141
- global $gtm4wp_datalayer_json;
142
- return '{"vars":' . $gtm4wp_datalayer_json . ' }';
143
- }
144
-
145
-
146
- // Set Status at start
147
- $gtm4wp_amp_headerinjected = false;
148
- $gtm4wp_amp_bodyinjected = false;
149
-
150
- // Load AMP-Analytics tag into <head>
151
- add_action( 'amp_post_template_data', 'gtm4wp_amp_gtmampcode_check' );
152
-
153
- // Load the GTM code processing to gain the GTM DataLayer
154
- add_action( 'amp_post_template_head', 'gtm4wp_wp_header_begin' );
155
- add_action( 'amp_post_template_head', 'gtm4wp_wp_header_top', 1 );
156
-
157
- // Try amp_wp_body_open
158
- // (https://github.com/Automattic/amp-wp/pull/1143)
159
- add_action( 'amp_post_template_body_open', 'gtm4wp_amp_gtmcode');
160
- // Publish the GTM code and dataLayer to the footer
161
- add_action( 'amp_post_template_footer', 'gtm4wp_amp_gtmcode' );
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AMP (Accelerated Mobile Pages Support) for gtm4wp
4
+ *
5
+ * This intergration added AMP support when using amp-wp, the existing dataLayer used
6
+ * for Google Tag Manager on HTML is loaded and built into AMP compatible code.
7
+ *
8
+ * @author Vincent Koc <https://github.com/koconder/>
9
+ * @package GTM4WP
10
+ */
11
+
12
+ /**
13
+ *
14
+ * Todo's
15
+ *
16
+ * - Better handling of gtm4wp_amp_gtmampcode_check() to allow for other plugins
17
+ * - Develop array's into strings as AMP GTM does not allow custom js variables
18
+ * - Better Client ID support (https://github.com/Automattic/amp-wp/issues/775)
19
+ * - Update AMP cache on GTM changes (https://github.com/Automattic/amp-wp/pull/605)
20
+ * - Supporting PWA with SuperPWA intergration with AMP and PWA for offline
21
+ */
22
+
23
+ /**
24
+ * Check if we are running AMP
25
+ *
26
+ * @author Vincent Koc <https://github.com/koconder/>
27
+ * @return bool Returns true if we are running on an AMP page
28
+ */
29
+ function gtm4wp_amp_running() {
30
+ if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
31
+ return true;
32
+ }
33
+
34
+ return false;
35
+ }
36
+
37
+ /**
38
+ * Pre-injection Check AMP Project's AMP Analytics tag, using amp-wp hook
39
+ *
40
+ * @link https://github.com/Automattic/amp-wp/blob/develop/includes/amp-post-template-actions.php
41
+ * @author Vincent Koc <https://github.com/koconder/>
42
+ *
43
+ * @param array $data AMP Template data.
44
+ * @return array Returns AMP Analytics array used by amp-wp
45
+ */
46
+ function gtm4wp_amp_gtmampcode_check( $data ) {
47
+ global $gtm4wp_amp_headerinjected;
48
+
49
+ // run once.
50
+ if ( $gtm4wp_amp_headerinjected ) {
51
+ return $data;
52
+ }
53
+
54
+ // AMP-WP Plugin.
55
+ if ( ! empty( $data['amp_analytics'] ) ) {
56
+ // Inject into AMP Plugin to load.
57
+ $data['amp_component_scripts']['amp-analytics'] = 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js';
58
+ $gtm4wp_amp_headerinjected = true;
59
+
60
+ // Manually load into AMP-WP Plugin.
61
+ } else {
62
+ // Inject manually based on AMP <head> hook.
63
+ add_action( 'amp_post_template_head', 'gtm4wp_amp_gtmampcode_injecthead' );
64
+ }
65
+
66
+ // Return the $data back to amp-wp hook.
67
+ return $data;
68
+ }
69
+
70
+
71
+ /**
72
+ * Post-check AMP Project's AMP Analytics tag, using amp-wp hook
73
+ *
74
+ * @link https://github.com/Automattic/amp-wp/blob/develop/includes/amp-post-template-actions.php
75
+ * @author Vincent Koc <https://github.com/koconder/>
76
+ */
77
+ function gtm4wp_amp_gtmampcode_injecthead() {
78
+ global $gtm4wp_amp_headerinjected;
79
+
80
+ $gtm4wp_amp_headerinjected = true;
81
+
82
+ // phpcs ignore because wp_enqueue_script() can not handle special script element attributes needed here.
83
+ echo '<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>'; // phpcs:ignore
84
+ }
85
+
86
+ /**
87
+ * Generate AMP ready Google Tag Manager code
88
+ *
89
+ * @author Vincent Koc <https://github.com/koconder/>
90
+ * @return int Returns number of injected snippets, false if no injection
91
+ */
92
+ function gtm4wp_amp_gtmcode() {
93
+ global $gtm4wp_datalayer_json, $gtm4wp_options, $gtm4wp_amp_bodyinjected;
94
+
95
+ // run once.
96
+ if ( $gtm4wp_amp_bodyinjected ) {
97
+ return false;
98
+ }
99
+
100
+ $gtm4wp_amp_bodyinjected = true;
101
+ // Check dataLayer is loaded from the plugin.
102
+ if ( ! empty( $gtm4wp_datalayer_json ) ) {
103
+
104
+ // Builds a list of GTM id's.
105
+ $gtm4wp_ampids = explode( ',', $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_AMPID ] );
106
+ $gtm4wp_ampid_list = array();
107
+
108
+ // Counter used for status return.
109
+ $x = 0;
110
+
111
+ // Check we have more than one valid Google Tag Manager ID.
112
+ if ( count( $gtm4wp_ampids ) > 0 ) {
113
+
114
+ // Loop through each GTM idea and build the AMP GTM code.
115
+ foreach ( $gtm4wp_ampids as $gtm4wp_oneampid ) {
116
+
117
+ // Docs: https://developers.google.com/analytics/devguides/collection/amp-analytics/.
118
+ // Examples: from https://www.simoahava.com/analytics/accelerated-mobile-pages-via-google-tag-manager/.
119
+ // Inject the AMP GTM code.
120
+ // TODO: Use AMP classes to enable cross-compatibility with other future plugins.
121
+ echo '<!-- Google Tag Manager --><amp-analytics config="https://www.googletagmanager.com/amp.json?id=' . esc_url_raw( $gtm4wp_oneampid ) . '&gtm.url=SOURCE_URL" data-credentials="include"><script type="application/json">' . wp_json_encode( gtm4wp_amp_gtmvariables() ) . '</script></amp-analytics>';
122
+
123
+ // Add to counter.
124
+ $x++;
125
+ }
126
+
127
+ // Check how many injections for return.
128
+ if ( $x > 0 ) {
129
+ return $x;
130
+ }
131
+ }
132
+ }
133
+
134
+ // No injection has occured.
135
+ return false;
136
+ }
137
+
138
+ /**
139
+ * Generate the AMP "vars" from the GTM dataLayer
140
+ *
141
+ * @author Vincent Koc <https://github.com/koconder/>
142
+ * @return array Returns json dataLayer for AMP code
143
+ */
144
+ function gtm4wp_amp_gtmvariables() {
145
+ global $gtm4wp_datalayer_data;
146
+
147
+ return array(
148
+ 'vars' => $gtm4wp_datalayer_data,
149
+ );
150
+ }
151
+
152
+ // Set Status at start.
153
+ $gtm4wp_amp_headerinjected = false;
154
+ $gtm4wp_amp_bodyinjected = false;
155
+
156
+ // Load AMP-Analytics tag into <head>.
157
+ add_action( 'amp_post_template_data', 'gtm4wp_amp_gtmampcode_check' );
158
+
159
+ // Load the GTM code processing to gain the GTM DataLayer.
160
+ add_action( 'amp_post_template_head', 'gtm4wp_wp_header_begin' );
161
+ add_action( 'amp_post_template_head', 'gtm4wp_wp_header_top', 1 );
162
+
163
+ // Try amp_wp_body_open.
164
+ // (https://github.com/Automattic/amp-wp/pull/1143).
165
+ add_action( 'amp_post_template_body_open', 'gtm4wp_amp_gtmcode' );
166
+ // Publish the GTM code and dataLayer to the footer.
167
+ add_action( 'amp_post_template_footer', 'gtm4wp_amp_gtmcode' );
integration/google-optimize.php CHANGED
@@ -1,4 +1,21 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  function gtm4wp_go_pagehiding_snippet() {
3
  global $gtm4wp_options;
4
 
@@ -6,11 +23,11 @@ function gtm4wp_go_pagehiding_snippet() {
6
  $gtm4wp_goid_pagehiding_list = array();
7
  if ( count( $gtm4wp_goids ) > 0 ) {
8
  foreach ( $gtm4wp_goids as $gtm4wp_onegoid ) {
9
- $gtm4wp_goid_pagehiding_list[] = "'" . $gtm4wp_onegoid . "': true";
10
  }
11
 
12
  $gtm4wp_gotimeout = (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT ];
13
- if ( 0 == $gtm4wp_gotimeout ) {
14
  $gtm4wp_gotimeout = 4000;
15
  }
16
 
@@ -34,12 +51,19 @@ function gtm4wp_go_pagehiding_snippet() {
34
  h.end=null
35
  },c);
36
  h.timeout=c;
37
- })(window,document.documentElement,'async-hide','dataLayer'," . $gtm4wp_gotimeout . ",{" . implode( ", ", $gtm4wp_goid_pagehiding_list ) . "});
38
  </script>
39
- <!-- GTM4WP: End of Google Optimize Page Hiding snippet -->";
40
  }
41
  }
42
 
 
 
 
 
 
 
 
43
  function gtm4wp_go_snippet( $content ) {
44
  global $gtm4wp_options;
45
 
@@ -47,18 +71,27 @@ function gtm4wp_go_snippet( $content ) {
47
  $gtm4wp_goid_list = array();
48
  if ( count( $gtm4wp_goids ) > 0 ) {
49
  foreach ( $gtm4wp_goids as $gtm4wp_onegoid ) {
 
50
  $gtm4wp_goid_list[] = '
51
- <script src="https://www.googleoptimize.com/optimize.js?id=' . $gtm4wp_onegoid . '" onerror="dataLayer.hide.end && dataLayer.hide.end()"></script>';
52
  }
53
 
54
- $content .= "
55
  <!-- GTM4WP: Load Google Optimize containers -->
56
- " . implode("", $gtm4wp_goid_list) . "
57
- <!-- GTM4WP: End of Load Google Optimize containers -->";
58
  }
59
 
60
- return $content;
 
 
 
 
 
 
 
 
61
  }
62
 
63
  add_action( 'wp_head', 'gtm4wp_go_pagehiding_snippet', 1 );
64
- add_filter( GTM4WP_WPFILTER_AFTER_DATALAYER, 'gtm4wp_go_snippet' );
1
  <?php
2
+ /**
3
+ * Google Optimize integration related codes.
4
+ * Outputs the Google Optimize code and the anti flicker snippet on the fronend.
5
+ *
6
+ * @package GTM4WP
7
+ * @author Thomas Geiger
8
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
9
+ * @license GNU General Public License, version 3
10
+ */
11
+
12
+ /**
13
+ * WordPress hook to add the anti flicker snippet into the <head> section of the page.
14
+ *
15
+ * @see https://developer.wordpress.org/reference/functions/wp_head/
16
+ *
17
+ * @return void
18
+ */
19
  function gtm4wp_go_pagehiding_snippet() {
20
  global $gtm4wp_options;
21
 
23
  $gtm4wp_goid_pagehiding_list = array();
24
  if ( count( $gtm4wp_goids ) > 0 ) {
25
  foreach ( $gtm4wp_goids as $gtm4wp_onegoid ) {
26
+ $gtm4wp_goid_pagehiding_list[ esc_attr( $gtm4wp_onegoid ) ] = true;
27
  }
28
 
29
  $gtm4wp_gotimeout = (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZETIMEOUT ];
30
+ if ( 0 === $gtm4wp_gotimeout ) {
31
  $gtm4wp_gotimeout = 4000;
32
  }
33
 
51
  h.end=null
52
  },c);
53
  h.timeout=c;
54
+ })(window,document.documentElement,'async-hide','dataLayer'," . esc_js( $gtm4wp_gotimeout ) . ',' . wp_json_encode( $gtm4wp_goid_pagehiding_list, JSON_FORCE_OBJECT ) . ');
55
  </script>
56
+ <!-- GTM4WP: End of Google Optimize Page Hiding snippet -->';
57
  }
58
  }
59
 
60
+ /**
61
+ * Add the Google Optimize snippet after the initial dataLayer.push() so that
62
+ * Google Optimize can utilize content of the data layer.
63
+ *
64
+ * @param string $content The original content that can be extended by this hook.
65
+ * @return void
66
+ */
67
  function gtm4wp_go_snippet( $content ) {
68
  global $gtm4wp_options;
69
 
71
  $gtm4wp_goid_list = array();
72
  if ( count( $gtm4wp_goids ) > 0 ) {
73
  foreach ( $gtm4wp_goids as $gtm4wp_onegoid ) {
74
+ // phpcs ignore set due to wp_enqueue_script() can not handle custom script element attributes.
75
  $gtm4wp_goid_list[] = '
76
+ <script src="' . esc_url( 'https://www.googleoptimize.com/optimize.js?id=' . $gtm4wp_onegoid ) . '" onerror="dataLayer.hide.end && dataLayer.hide.end()"></script>'; // phpcs:ignore
77
  }
78
 
79
+ $content .= '
80
  <!-- GTM4WP: Load Google Optimize containers -->
81
+ ' . implode( '', $gtm4wp_goid_list ) . '
82
+ <!-- GTM4WP: End of Load Google Optimize containers -->';
83
  }
84
 
85
+ echo wp_kses(
86
+ $content,
87
+ array(
88
+ 'script' => array(
89
+ 'src' => array(),
90
+ 'onerror' => array(),
91
+ ),
92
+ )
93
+ );
94
  }
95
 
96
  add_action( 'wp_head', 'gtm4wp_go_pagehiding_snippet', 1 );
97
+ add_action( GTM4WP_WPACTION_AFTER_DATALAYER, 'gtm4wp_go_snippet' );
integration/soundcloud.php CHANGED
@@ -1,6 +1,16 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
2
  if ( ! is_admin() ) {
3
- $in_footer = apply_filters( 'gtm4wp_soundcloud', true );
4
  wp_enqueue_script( 'gtm4wp-soundcloud-api', 'https://w.soundcloud.com/player/api.js', array(), '1.0', $in_footer );
5
  wp_enqueue_script( 'gtm4wp-soundcloud', $gtp4wp_plugin_url . 'js/gtm4wp-soundcloud.js', array(), GTM4WP_VERSION, $in_footer );
6
  }
1
  <?php
2
+ /**
3
+ * Soundcloud integration related codes.
4
+ * Enqueues the official Soundcloud JS API script and the interaction tracking script of GTM4WP.
5
+ *
6
+ * @package GTM4WP
7
+ * @author Thomas Geiger
8
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
9
+ * @license GNU General Public License, version 3
10
+ */
11
+
12
  if ( ! is_admin() ) {
13
+ $in_footer = (bool) apply_filters( 'gtm4wp_soundcloud', true );
14
  wp_enqueue_script( 'gtm4wp-soundcloud-api', 'https://w.soundcloud.com/player/api.js', array(), '1.0', $in_footer );
15
  wp_enqueue_script( 'gtm4wp-soundcloud', $gtp4wp_plugin_url . 'js/gtm4wp-soundcloud.js', array(), GTM4WP_VERSION, $in_footer );
16
  }
integration/vimeo.php CHANGED
@@ -1,6 +1,16 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
2
  if ( ! is_admin() ) {
3
- $in_footer = apply_filters( 'gtm4wp_vimeo', true );
4
 
5
  wp_enqueue_script( 'gtm4wp-vimeo-api', 'https://player.vimeo.com/api/player.js', array(), '1.0', $in_footer );
6
  wp_enqueue_script( 'gtm4wp-vimeo', $gtp4wp_plugin_url . 'js/gtm4wp-vimeo.js', array(), GTM4WP_VERSION, $in_footer );
1
  <?php
2
+ /**
3
+ * Vimeo integration related codes.
4
+ * Enqueues the official Vimeo JS API script and the interaction tracking script of GTM4WP.
5
+ *
6
+ * @package GTM4WP
7
+ * @author Thomas Geiger
8
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
9
+ * @license GNU General Public License, version 3
10
+ */
11
+
12
  if ( ! is_admin() ) {
13
+ $in_footer = (bool) apply_filters( 'gtm4wp_vimeo', true );
14
 
15
  wp_enqueue_script( 'gtm4wp-vimeo-api', 'https://player.vimeo.com/api/player.js', array(), '1.0', $in_footer );
16
  wp_enqueue_script( 'gtm4wp-vimeo', $gtp4wp_plugin_url . 'js/gtm4wp-vimeo.js', array(), GTM4WP_VERSION, $in_footer );
integration/woocommerce.php CHANGED
@@ -1,4 +1,13 @@
1
  <?php
 
 
 
 
 
 
 
 
 
2
  define( 'GTM4WP_WPFILTER_EEC_PRODUCT_ARRAY', 'gtm4wp_eec_product_array' );
3
  define( 'GTM4WP_WPFILTER_EEC_CART_ITEM', 'gtm4wp_eec_cart_item' );
4
  define( 'GTM4WP_WPFILTER_EEC_ORDER_ITEM', 'gtm4wp_eec_order_item' );
@@ -10,52 +19,78 @@ if ( function_exists( 'WC' ) ) {
10
  } else {
11
  $GLOBALS['gtm4wp_is_woocommerce3_7'] = false;
12
  }
13
- $GLOBALS['gtm4wp_grouped_product_ix'] = 1;
14
  $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] = false;
15
 
16
- // from https://snippets.webaware.com.au/ramblings/php-really-doesnt-unicode/
 
 
 
 
 
 
 
17
  function gtm4wp_untexturize( $fancy ) {
18
- $fixes = false;
19
-
20
- if ( $fixes === false ) {
21
- $fixes = array(
22
- json_decode( '"\u201C"' ) => '"', // left double quotation mark
23
- json_decode( '"\u201D"' ) => '"', // right double quotation mark
24
- json_decode( '"\u2018"' ) => "'", // left single quotation mark
25
- json_decode( '"\u2019"' ) => "'", // right single quotation mark
26
- json_decode( '"\u2032"' ) => "'", // prime (minutes, feet)
27
- json_decode( '"\u2033"' ) => '"', // double prime (seconds, inches)
28
- json_decode( '"\u2013"' ) => '-', // en dash
29
- json_decode( '"\u2014"' ) => '--', // em dash
30
- );
31
- }
32
 
33
  $normal = strtr( $fancy, $fixes );
34
 
35
  return $normal;
36
  }
37
 
38
- function gtm4wp_woocommerce_html_entity_decode( $val ) {
39
- return gtm4wp_untexturize( html_entity_decode( $val, ENT_QUOTES, 'utf-8' ) );
40
- }
41
-
 
 
 
 
 
 
42
  function gtm4wp_prefix_productid( $product_id ) {
43
  global $gtm4wp_options;
44
 
45
- if ( '' != $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX ] ) {
46
  return $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX ] . $product_id;
47
  } else {
48
  return $product_id;
49
  }
50
  }
51
 
52
- // from https://stackoverflow.com/questions/1252693/using-str-replace-so-that-it-only-acts-on-the-first-match
53
- function gtm4wp_str_replace_first( $from, $to, $subject ) {
54
- $from = '/' . preg_quote( $from, '/' ) . '/';
55
-
56
- return preg_replace( $from, $to, $subject, 1 );
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
 
 
 
 
 
 
59
  function gtm4wp_get_product_category_hierarchy( $category_id ) {
60
  $cat_hierarchy = '';
61
 
@@ -77,10 +112,26 @@ function gtm4wp_get_product_category_hierarchy( $category_id ) {
77
  return $cat_hierarchy;
78
  }
79
 
 
 
 
 
 
 
 
 
80
  function gtm4wp_get_product_category( $product_id, $fullpath = false ) {
81
  $product_cat = '';
82
 
83
- $_product_cats = wp_get_post_terms( $product_id, 'product_cat', array( 'orderby' => 'parent', 'order' => 'ASC'));
 
 
 
 
 
 
 
 
84
  if ( ( is_array( $_product_cats ) ) && ( count( $_product_cats ) > 0 ) ) {
85
  $first_product_cat = array_pop( $_product_cats );
86
  if ( $fullpath ) {
@@ -93,15 +144,43 @@ function gtm4wp_get_product_category( $product_id, $fullpath = false ) {
93
  return $product_cat;
94
  }
95
 
 
 
 
 
 
 
 
 
 
96
  function gtm4wp_woocommerce_getproductterm( $product_id, $taxonomy ) {
97
- $gtm4wp_product_terms = wp_get_post_terms( $product_id, $taxonomy, array( 'orderby' => 'parent', 'order' => 'ASC'));
 
 
 
 
 
 
 
 
98
  if ( is_array( $gtm4wp_product_terms ) && ( count( $gtm4wp_product_terms ) > 0 ) ) {
99
  return $gtm4wp_product_terms[0]->name;
100
  }
101
 
102
- return "";
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
105
  function gtm4wp_process_product( $product, $additional_product_attributes, $attributes_used_for ) {
106
  global $gtm4wp_options;
107
 
@@ -118,14 +197,14 @@ function gtm4wp_process_product( $product, $additional_product_attributes, $attr
118
  $remarketing_id = $product_id;
119
  $product_sku = $product->get_sku();
120
 
121
- if ( 'variation' == $product_type ) {
122
  $parent_product_id = $product->get_parent_id();
123
  $product_cat = gtm4wp_get_product_category( $parent_product_id, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH ] );
124
  } else {
125
- $product_cat = gtm4wp_get_product_category( $product_id, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH ] );
126
  }
127
 
128
- if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSESKU ] && ( '' != $product_sku ) ) {
129
  $remarketing_id = $product_sku;
130
  }
131
 
@@ -134,21 +213,21 @@ function gtm4wp_process_product( $product, $additional_product_attributes, $attr
134
  'name' => $product->get_title(),
135
  'sku' => $product_sku ? $product_sku : $product_id,
136
  'category' => $product_cat,
137
- 'price' => round( (float) wc_get_price_to_display( $product ), 2),
138
- 'stocklevel' => $product->get_stock_quantity()
139
  );
140
 
141
- if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ] != "" ) {
142
- if ( isset( $parent_product_id ) && ( $parent_product_id !== 0 ) ) {
143
  $product_id_to_query = $parent_product_id;
144
  } else {
145
  $product_id_to_query = $product_id;
146
  }
147
 
148
- $_temp_productdata[ "brand" ] = gtm4wp_woocommerce_getproductterm( $product_id_to_query, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ] );
149
  }
150
 
151
- if ( 'variation' == $product_type ) {
152
  $_temp_productdata['variant'] = implode( ',', $product->get_variation_attributes() );
153
  }
154
 
@@ -157,80 +236,110 @@ function gtm4wp_process_product( $product, $additional_product_attributes, $attr
157
  return apply_filters( GTM4WP_WPFILTER_EEC_PRODUCT_ARRAY, $_temp_productdata, $attributes_used_for );
158
  }
159
 
 
 
 
 
 
 
 
160
  function gtm4wp_get_gads_product_id_variable_name( $vertical_id ) {
161
  global $gtm4wp_business_verticals_ids;
162
 
163
  if ( array_key_exists( $vertical_id, $gtm4wp_business_verticals_ids ) ) {
164
  return $gtm4wp_business_verticals_ids[ $vertical_id ];
165
  } else {
166
- return "id";
167
  }
168
  }
169
 
 
 
 
 
 
 
170
  function gtm4wp_map_eec_to_ga4( $productdata ) {
171
  global $gtm4wp_options;
172
 
173
- if ( !is_array($productdata) ) {
174
  return;
175
  }
176
 
177
- $category_path = array_key_exists( "category", $productdata ) ? $productdata[ "category" ] : "";
178
- $category_parts = explode('/', $category_path);
179
 
180
- // default, required parameters
181
  $ga4_product = array(
182
- 'item_id' => array_key_exists( "id", $productdata ) ? $productdata[ "id" ] : "",
183
- 'item_name' => array_key_exists( "name", $productdata ) ? $productdata[ "name" ] : "",
184
- 'item_brand' => array_key_exists( "brand", $productdata ) ? $productdata[ "brand" ] : "",
185
- 'price' => array_key_exists( "price", $productdata ) ? $productdata[ "price" ] : ""
186
  );
187
 
188
- // category, also handle category path
189
- if ( 1 == count($category_parts) ) {
190
- $ga4_product[ "item_category" ] = $category_parts[0];
191
- } else if ( count($category_parts) > 1 ) {
192
- $ga4_product[ "item_category" ] = $category_parts[0];
193
- for( $i=1; $i < min( 5, count( $category_parts ) ); $i++ ) {
194
- $ga4_product[ "item_category_" . (string)($i+1) ] = $category_parts[$i];
 
 
195
  }
196
  }
197
 
198
- // optional parameters which should not be included in the array if not set
199
- if ( array_key_exists( "variant", $productdata ) ) {
200
- $ga4_product[ "item_variant" ] = $productdata[ "variant" ];
201
  }
202
- if ( array_key_exists( "listname", $productdata ) ) {
203
- $ga4_product[ "item_list_name" ] = $productdata[ "listname" ];
204
  }
205
- if ( array_key_exists( "listposition", $productdata ) ) {
206
- $ga4_product[ "index" ] = $productdata[ "listposition" ];
207
  }
208
- if ( array_key_exists( "quantity", $productdata ) ) {
209
- $ga4_product[ "quantity" ] = $productdata[ "quantity" ];
210
  }
211
- if ( array_key_exists( "coupon", $productdata ) ) {
212
- $ga4_product[ "coupon" ] = $productdata[ "coupon" ];
213
  }
214
 
215
- $ga4_product[ "google_business_vertical" ] = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ];
216
- $ga4_product[ gtm4wp_get_gads_product_id_variable_name( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] ) ] = gtm4wp_prefix_productid( $ga4_product["item_id"] );
 
217
 
218
  return $ga4_product;
219
  }
220
 
 
 
 
 
 
 
 
 
 
 
221
  function gtm4wp_process_order_items( $order ) {
222
  global $gtm4wp_options;
223
 
224
  $return_data = array(
225
- 'products' => [],
226
- 'sumprice' => 0,
227
- 'product_ids' => []
228
  );
229
 
230
  if ( ! $order ) {
231
  return $return_data;
232
  }
233
 
 
 
 
 
234
  $order_items = $order->get_items();
235
 
236
  if ( $order_items ) {
@@ -239,17 +348,22 @@ function gtm4wp_process_order_items( $order ) {
239
  continue;
240
  }
241
 
242
- $product = $item->get_product();
243
- $inc_tax = ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) );
244
- $product_price = round( (float) $order->get_item_total( $item, $inc_tax ), 2);
245
- $eec_product_array = gtm4wp_process_product( $product, array(
246
- 'quantity' => $item->get_quantity(),
247
- 'price' => $product_price
248
- ), 'purchase' );
 
 
 
 
 
249
 
250
  if ( $eec_product_array ) {
251
  $return_data['products'][] = $eec_product_array;
252
- $return_data['sumprice'] += $product_price * $eec_product_array['quantity'];
253
  $return_data['product_ids'][] = gtm4wp_prefix_productid( $eec_product_array['id'] );
254
  }
255
  }
@@ -258,6 +372,12 @@ function gtm4wp_process_order_items( $order ) {
258
  return $return_data;
259
  }
260
 
 
 
 
 
 
 
261
  function gtm4wp_woocommerce_addglobalvars( $return ) {
262
  global $gtm4wp_options;
263
 
@@ -267,39 +387,49 @@ function gtm4wp_woocommerce_addglobalvars( $return ) {
267
  $gtm4wp_needs_shipping_address = false;
268
  }
269
 
270
- $return .= '
271
- const gtm4wp_use_sku_instead = ' . (int) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSESKU ] ) . ';
272
- const gtm4wp_id_prefix = \'' . esc_js( gtm4wp_prefix_productid( '' ) ) . '\';
273
- const gtm4wp_remarketing = ' . gtm4wp_escjs_boolean( (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) ) . ';
274
- const gtm4wp_eec = ' . gtm4wp_escjs_boolean( (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) . ';
275
- const gtm4wp_classicec = ' . gtm4wp_escjs_boolean( (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) ) . ';
276
- const gtm4wp_currency = \'' . esc_js( get_woocommerce_currency() ) . '\';
277
- const gtm4wp_product_per_impression = ' . (int) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION ] ) . ';
278
- const gtm4wp_needs_shipping_address = ' . gtm4wp_escjs_boolean( $gtm4wp_needs_shipping_address ) . ';
279
- const gtm4wp_business_vertical = \'' . esc_js( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] ) . '\';
280
- const gtm4wp_business_vertical_id = \'' . gtm4wp_get_gads_product_id_variable_name( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] ) . '\';';
281
 
282
  return $return;
283
  }
284
 
 
 
 
 
 
 
 
 
285
  function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
286
  global $gtm4wp_options, $gtm4wp_is_woocommerce3_7;
287
 
288
- $dataLayer = array();
289
 
290
  if ( $order instanceof WC_Order ) {
291
  $woo = WC();
292
 
293
- // variable for Google Smart Shopping campaign new customer reporting
294
- // https://support.google.com/google-ads/answer/9917012?hl=en-AU#zippy=%2Cinstall-with-google-tag-manager
 
 
 
295
  if ( $woo->customer instanceof WC_Customer ) {
296
- // we need to use this instead of $woo->customer as this will load proper total order number and value from the database instead of the session
297
- $woo_customer = new WC_Customer( $woo->customer->get_id() );
298
- $dataLayer['new_customer'] = $woo_customer->get_order_count() === 1;
299
  }
300
 
301
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEXCLUDETAX ] ) {
302
- $order_revenue = (float)( $order->get_total() - $order->get_total_tax() );
303
  } else {
304
  $order_revenue = (float) $order->get_total();
305
  }
@@ -313,18 +443,18 @@ function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
313
  $order_currency = $order->get_currency();
314
 
315
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
316
- $dataLayer['event'] = 'gtm4wp.orderCompleted';
317
- $dataLayer['transactionId'] = $order->get_order_number();
318
- $dataLayer['transactionAffiliation'] = '';
319
- $dataLayer['transactionTotal'] = $order_revenue;
320
- $dataLayer['transactionShipping'] = $order_shipping_cost;
321
- $dataLayer['transactionTax'] = (float) $order->get_total_tax();
322
- $dataLayer['transactionCurrency'] = $order_currency;
323
  }
324
 
325
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
326
- $dataLayer['event'] = 'gtm4wp.orderCompletedEEC';
327
- $dataLayer['ecommerce'] = array(
328
  'currencyCode' => $order_currency,
329
  'purchase' => array(
330
  'actionField' => array(
@@ -332,10 +462,10 @@ function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
332
  'affiliation' => '',
333
  'revenue' => $order_revenue,
334
  'tax' => (float) $order->get_total_tax(),
335
- 'shipping' => (float)( $order->get_shipping_total() ),
336
  'coupon' => implode( ', ', ( $gtm4wp_is_woocommerce3_7 ? $order->get_coupon_codes() : $order->get_used_coupons() ) ),
337
- )
338
- )
339
  );
340
  }
341
 
@@ -346,92 +476,109 @@ function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
346
  }
347
 
348
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
349
- $dataLayer['transactionProducts'] = $_order_items['products'];
350
  }
351
 
352
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
353
- $dataLayer['ecommerce']['purchase']['products'] = $_order_items['products'];
354
  }
355
 
356
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
357
- $dataLayer['ecomm_prodid'] = $_order_items['product_ids'];
358
- $dataLayer['ecomm_pagetype'] = 'purchase';
359
- $dataLayer['ecomm_totalvalue'] = (float) $_order_items['sumprice'];
360
  }
361
  }
362
 
363
- return $dataLayer;
364
  }
365
 
366
- function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
 
 
 
 
 
 
 
367
  global $gtm4wp_options, $wp_query, $gtm4wp_datalayer_name, $gtm4wp_product_counter, $gtm4wp_is_woocommerce3_7;
368
 
369
- if ( array_key_exists( "HTTP_X_REQUESTED_WITH", $_SERVER ) ) {
370
- return $dataLayer;
371
  }
372
 
373
  $woo = WC();
374
 
375
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCCUSTOMERDATA ] ) {
376
  if ( $woo->customer instanceof WC_Customer ) {
377
- // we need to use this instead of $woo->customer as this will load proper total order number and value from the database instead of the session
378
  $woo_customer = new WC_Customer( $woo->customer->get_id() );
379
 
380
- $dataLayer['customerTotalOrders'] = $woo_customer->get_order_count();
381
- $dataLayer['customerTotalOrderValue'] = $woo_customer->get_total_spent();
382
-
383
- $dataLayer['customerFirstName'] = $woo_customer->get_first_name();
384
- $dataLayer['customerLastName'] = $woo_customer->get_last_name();
385
-
386
- $dataLayer['customerBillingFirstName'] = $woo_customer->get_billing_first_name();
387
- $dataLayer['customerBillingLastName'] = $woo_customer->get_billing_last_name();
388
- $dataLayer['customerBillingCompany'] = $woo_customer->get_billing_company();
389
- $dataLayer['customerBillingAddress1'] = $woo_customer->get_billing_address_1();
390
- $dataLayer['customerBillingAddress2'] = $woo_customer->get_billing_address_2();
391
- $dataLayer['customerBillingCity'] = $woo_customer->get_billing_city();
392
- $dataLayer['customerBillingPostcode'] = $woo_customer->get_billing_postcode();
393
- $dataLayer['customerBillingCountry'] = $woo_customer->get_billing_country();
394
- $dataLayer['customerBillingEmail'] = $woo_customer->get_billing_email();
395
- $dataLayer['customerBillingEmailHash'] = hash( 'sha256', $woo_customer->get_billing_email() );
396
- $dataLayer['customerBillingPhone'] = $woo_customer->get_billing_phone();
397
-
398
- $dataLayer['customerShippingFirstName'] = $woo_customer->get_shipping_first_name();
399
- $dataLayer['customerShippingLastName'] = $woo_customer->get_shipping_last_name();
400
- $dataLayer['customerShippingCompany'] = $woo_customer->get_shipping_company();
401
- $dataLayer['customerShippingAddress1'] = $woo_customer->get_shipping_address_1();
402
- $dataLayer['customerShippingAddress2'] = $woo_customer->get_shipping_address_2();
403
- $dataLayer['customerShippingCity'] = $woo_customer->get_shipping_city();
404
- $dataLayer['customerShippingPostcode'] = $woo_customer->get_shipping_postcode();
405
- $dataLayer['customerShippingCountry'] = $woo_customer->get_shipping_country();
406
  }
407
  }
408
 
409
- if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEINCLUDECARTINDL ] && version_compare( $woo->version, "3.2", ">=" ) && isset($woo) && isset($woo->cart) ) {
 
 
 
 
 
410
  $current_cart = $woo->cart;
411
- $dataLayer["cartContent"] = array(
412
- "totals" => array(
413
- "applied_coupons" => $current_cart->get_applied_coupons(),
414
- "discount_total" => $current_cart->get_discount_total(),
415
- "subtotal" => $current_cart->get_subtotal(),
416
- "total" => $current_cart->get_cart_contents_total()
 
417
  ),
418
- "items" => array()
419
  );
420
 
421
- foreach( $current_cart->get_cart() as $cart_item_id => $cart_item_data) {
422
- $product = apply_filters( 'woocommerce_cart_item_product', $cart_item_data["data"], $cart_item_data, $cart_item_id );
423
  if (
424
- !apply_filters( GTM4WP_WPFILTER_EEC_CART_ITEM, true, $cart_item_data )
425
- || !apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item_data, $cart_item_id )
426
  ) {
427
  continue;
428
  }
429
 
430
- $eec_product_array = gtm4wp_process_product( $product, array(
431
- 'quantity' => $cart_item_data["quantity"]
432
- ), 'cart' );
 
 
 
 
433
 
434
- $dataLayer["cartContent"]["items"][] = $eec_product_array;
435
  }
436
  }
437
 
@@ -444,65 +591,66 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
444
  }
445
 
446
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
447
- $dataLayer['ecomm_prodid'] = array();
448
- $dataLayer['ecomm_pagetype'] = $ecomm_pagetype;
449
- $dataLayer['ecomm_totalvalue'] = 0;
450
  }
451
  } elseif ( is_product() ) {
452
  if (
453
  $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ]
454
  || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] )
455
  ) {
456
- $postid = get_the_ID();
457
- $product = wc_get_product( $postid );
458
 
459
- $eec_product_array = gtm4wp_process_product( $product, array(), 'productdetail' );
 
 
 
 
460
 
461
- $dataLayer['productRatingCounts'] = $product->get_rating_counts();
462
- $dataLayer['productAverageRating'] = (float) $product->get_average_rating();
463
- $dataLayer['productReviewCount'] = (int) $product->get_review_count();
464
- $dataLayer['productType'] = $product->get_type();
465
 
466
- switch ( $dataLayer['productType'] ) {
467
- case 'variable': {
468
- $dataLayer['productIsVariable'] = 1;
469
 
470
- $dataLayer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
471
- $dataLayer['ecomm_pagetype'] = 'product';
472
- $dataLayer['ecomm_totalvalue'] = $eec_product_array[ 'price' ];
473
 
474
  break;
475
- }
476
 
477
- case 'grouped': {
478
- $dataLayer['productIsVariable'] = 0;
479
 
480
  break;
481
- }
482
 
483
- default: {
484
- $dataLayer['productIsVariable'] = 0;
485
 
486
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
487
- $dataLayer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
488
- $dataLayer['ecomm_pagetype'] = 'product';
489
- $dataLayer['ecomm_totalvalue'] = $eec_product_array['price'];
490
  }
491
 
492
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
493
- $currencyCode = get_woocommerce_currency();
494
 
495
- $dataLayer['event'] = 'gtm4wp.changeDetailViewEEC';
496
- $dataLayer['ecommerce'] = array(
497
- 'currencyCode' => $currencyCode,
498
  'detail' => array(
499
  'products' => array(
500
- $eec_product_array
501
- )
502
- )
503
  );
504
  }
505
- }
506
  }
507
  }
508
  } elseif ( is_cart() ) {
@@ -523,54 +671,67 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
523
  continue;
524
  }
525
 
526
- $eec_product_array = gtm4wp_process_product( $product, array(
527
- 'quantity' => $cart_item_data['quantity']
528
- ), 'cart' );
 
 
 
 
529
 
530
- $gtm4wp_cart_products[] = $eec_product_array;
531
- $gtm4wp_cart_products_remarketing[] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
532
  }
533
 
534
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
535
- $dataLayer['ecomm_prodid'] = $gtm4wp_cart_products_remarketing;
536
- $dataLayer['ecomm_pagetype'] = 'cart';
537
  if ( ! $woo->cart->prices_include_tax ) {
538
  $cart_total = $woo->cart->cart_contents_total;
539
  } else {
540
  $cart_total = $woo->cart->cart_contents_total + $woo->cart->tax_total;
541
  }
542
- $dataLayer['ecomm_totalvalue'] = (float) $cart_total;
543
  }
544
 
545
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
546
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ] ) {
547
- $dataLayer['event'] = 'gtm4wp.checkoutStepEEC';
548
- $dataLayer['ecommerce'] = array(
549
  'currencyCode' => $gtm4wp_currency,
550
  'checkout' => array(
551
  'actionField' => array(
552
  'step' => 1,
553
  ),
554
  'products' => $gtm4wp_cart_products,
555
- )
556
  );
557
  } else {
558
- // add only ga4 products to populate view_cart event
559
- $dataLayer['ecommerce'] = array(
560
- 'cart' => $gtm4wp_cart_products
561
  );
562
  }
563
  }
564
  }
565
  } elseif ( is_order_received_page() ) {
566
  $do_not_flag_tracked_order = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG ] );
567
- $order_id = empty( $_GET['order'] ) ? ( $GLOBALS['wp']->query_vars['order-received'] ? $GLOBALS['wp']->query_vars['order-received'] : 0 ) : absint( $_GET['order'] );
568
- $order_id_filtered = apply_filters( 'woocommerce_thankyou_order_id', $order_id );
569
- if ( '' != $order_id_filtered ) {
 
 
 
 
 
 
 
570
  $order_id = $order_id_filtered;
571
  }
572
 
573
- $order_key = apply_filters( 'woocommerce_thankyou_order_key', empty( $_GET['key'] ) ? '' : wc_clean( $_GET['key'] ) );
 
 
574
 
575
  if ( $order_id > 0 ) {
576
  $order = wc_get_order( $order_id );
@@ -578,7 +739,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
578
  if ( $order instanceof WC_Order ) {
579
  $this_order_key = $order->get_order_key();
580
 
581
- if ( $this_order_key != $order_key ) {
582
  unset( $order );
583
  }
584
  } else {
@@ -586,49 +747,52 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
586
  }
587
  }
588
 
589
- // From this point if for any reason purchase data is not pushed
590
- // that is because for a specific reason.
591
- // In any other case woocommerce_thankyou hook will be the fallback if
592
- // is_order_received_page does not work
 
 
593
  $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] = true;
594
 
595
- if(isset($order) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ){
596
 
597
- $now = new DateTime();
598
- if($order->is_paid() && $order->get_date_paid()){
599
- $diff = $now->diff($order->get_date_paid());
600
- $minutes = ($diff->days * 24 * 60) + ($diff->h * 60) + $diff->i;
601
  } else {
602
- $diff = $now->diff($order->get_date_created());
603
- $minutes = ($diff->days * 24 * 60) + ($diff->h * 60) + $diff->i;
 
604
  }
605
 
606
- if($minutes > $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ]) {
607
- unset($order);
608
  }
609
  }
610
 
611
- $order_items = NULL;
612
- if ( isset($order) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERDATA ] ) {
613
  $order_items = gtm4wp_process_order_items( $order );
614
 
615
- $dataLayer['orderData'] = array(
616
  'attributes' => array(
617
- 'date' => $order->get_date_created()->date( 'c' ),
618
 
619
- 'order_number' => $order->get_order_number(),
620
- 'order_key' => $order->get_order_key(),
621
 
622
  'payment_method' => esc_js( $order->get_payment_method() ),
623
- 'payment_method_title' => esc_js( $order->get_payment_method_title() ),
624
 
625
- 'shipping_method' => esc_js( $order->get_shipping_method() ),
626
 
627
- 'status' => esc_js( $order->get_status() ),
628
 
629
- 'coupons' => implode( ', ', ( $gtm4wp_is_woocommerce3_7 ? $order->get_coupon_codes() : $order->get_used_coupons() ) )
630
  ),
631
- 'totals' => array(
632
  'currency' => esc_js( $order->get_currency() ),
633
  'discount_total' => esc_js( $order->get_discount_total() ),
634
  'discount_tax' => esc_js( $order->get_discount_tax() ),
@@ -639,12 +803,12 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
639
  'total_tax' => esc_js( $order->get_total_tax() ),
640
  'total_discount' => esc_js( $order->get_total_discount() ),
641
  'subtotal' => esc_js( $order->get_subtotal() ),
642
- 'tax_totals' => $order->get_tax_totals()
643
  ),
644
- 'customer' => array(
645
- 'id' => $order->get_customer_id(),
646
 
647
- 'billing' => array(
648
  'first_name' => esc_js( $order->get_billing_first_name() ),
649
  'last_name' => esc_js( $order->get_billing_last_name() ),
650
  'company' => esc_js( $order->get_billing_company() ),
@@ -656,7 +820,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
656
  'country' => esc_js( $order->get_billing_country() ),
657
  'email' => esc_js( $order->get_billing_email() ),
658
  'emailhash' => esc_js( hash( 'sha256', $order->get_billing_email() ) ),
659
- 'phone' => esc_js( $order->get_billing_phone() )
660
  ),
661
 
662
  'shipping' => array(
@@ -668,33 +832,33 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
668
  'city' => esc_js( $order->get_shipping_city() ),
669
  'state' => esc_js( $order->get_shipping_state() ),
670
  'postcode' => esc_js( $order->get_shipping_postcode() ),
671
- 'country' => esc_js( $order->get_shipping_country() )
672
- )
673
 
674
  ),
675
- 'items' => $order_items['products']
676
  );
677
  }
678
 
679
- if ( ( 1 == get_post_meta( $order_id, '_ga_tracked', true ) ) && ! $do_not_flag_tracked_order ) {
680
  unset( $order );
681
  }
682
 
683
- if ( isset( $_COOKIE[ 'gtm4wp_orderid_tracked' ] ) ) {
684
- $tracked_order_id = filter_var( $_COOKIE[ 'gtm4wp_orderid_tracked' ], FILTER_VALIDATE_INT );
685
 
686
- if ( $tracked_order_id && ( $tracked_order_id == $order_id ) && !$do_not_flag_tracked_order ) {
687
  unset( $order );
688
  }
689
  }
690
 
691
- if ( isset( $order ) && ( "failed" == $order->get_status() ) ) {
692
- // do not track order where payment failed
693
  unset( $order );
694
  }
695
 
696
  if ( isset( $order ) ) {
697
- $dataLayer = array_merge( $dataLayer, gtm4wp_get_purchase_datalayer( $order, $order_items ) );
698
 
699
  if ( ! $do_not_flag_tracked_order ) {
700
  update_post_meta( $order_id, '_ga_tracked', 1 );
@@ -716,46 +880,52 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
716
  continue;
717
  }
718
 
719
- $eec_product_array = gtm4wp_process_product( $product, array(
720
- 'quantity' => $cart_item_data['quantity']
721
- ), 'cart' );
 
 
 
 
722
 
723
  $gtm4wp_checkout_products[] = $eec_product_array;
724
 
725
- $gtm4wp_checkout_products_remarketing[] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
726
  $gtm4wp_totalvalue += $eec_product_array['quantity'] * $eec_product_array['price'];
727
  } // end foreach cart item
728
 
729
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
730
- $dataLayer['ecomm_prodid'] = $gtm4wp_checkout_products_remarketing;
731
- $dataLayer['ecomm_pagetype'] = 'cart';
732
- $dataLayer['ecomm_totalvalue'] = (float) $gtm4wp_totalvalue;
733
  }
734
 
735
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
736
- $currencyCode = get_woocommerce_currency();
737
 
738
  $ga4_products = array();
739
- $sum_value = 0;
740
- foreach( $gtm4wp_checkout_products as $oneproduct ) {
 
741
  $ga4_products[] = gtm4wp_map_eec_to_ga4( $oneproduct );
742
- $sum_value += $oneproduct["price"] * $oneproduct["quantity"];
743
  }
744
 
745
- $dataLayer['event'] = 'gtm4wp.checkoutStepEEC';
746
- $dataLayer['ecommerce'] = array(
747
- 'currencyCode' => $currencyCode,
748
  'checkout' => array(
749
  'actionField' => array(
750
  'step' => 1 + (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ],
751
  ),
752
  'products' => $gtm4wp_checkout_products,
753
- )
754
  );
755
 
756
- wc_enqueue_js('
757
- window.gtm4wp_checkout_products = ' . json_encode( $gtm4wp_checkout_products ) . ';
758
- window.gtm4wp_checkout_products_ga4 = ' . json_encode( $ga4_products ) . ';
 
759
  window.gtm4wp_checkout_value = ' . (float) $sum_value . ';
760
  window.gtm4wp_checkout_step_offset = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ] . ';'
761
  );
@@ -763,7 +933,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
763
  }
764
  } else {
765
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
766
- $dataLayer['ecomm_pagetype'] = 'other';
767
  }
768
  }
769
 
@@ -774,36 +944,50 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
774
  if ( ! empty( $cart_item ) ) {
775
  $product = $cart_item['data'];
776
 
777
- $eec_product_array = gtm4wp_process_product( $product, array(
778
- 'quantity' => $cart_item['quantity']
779
- ), 'readdedtocart' );
 
 
 
 
780
 
781
- $currencyCode = get_woocommerce_currency();
782
 
783
- $dataLayer['event'] = 'gtm4wp.addProductToCartEEC';
784
- $dataLayer['ecommerce'] = array(
785
- 'currencyCode' => $currencyCode,
786
- 'add' => array(
787
  'products' => array(
788
- $eec_product_array
789
- )
790
- )
791
  );
792
  }
793
 
794
- WC()->session->set( 'gtm4wp_product_readded_to_cart', NULL );
795
  }
796
  }
797
 
798
- return $dataLayer;
799
  }
800
 
 
 
 
 
 
 
 
 
801
  function gtm4wp_woocommerce_thankyou( $order_id ) {
802
  global $gtm4wp_options, $gtm4wp_datalayer_name;
803
 
804
- // if this flag is set to true, it means that the puchase event was fired
805
- // when capturing the is_order_received_page template tag therefore
806
- // no need to handle this here twice
 
 
807
  if ( $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] ) {
808
  return;
809
  }
@@ -812,49 +996,49 @@ function gtm4wp_woocommerce_thankyou( $order_id ) {
812
  $order = wc_get_order( $order_id );
813
  }
814
 
815
- if( isset( $order ) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
816
  $now = new DateTime();
817
- if( $order->is_paid() && $order->get_date_paid() ) {
818
- $diff = $now->diff( $order->get_date_paid() );
819
  $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
820
  } else {
821
- $diff = $now->diff( $order->get_date_created() );
822
  $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
823
  }
824
 
825
- if( $minutes > $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
826
  unset( $order );
827
  }
828
  }
829
 
830
  $do_not_flag_tracked_order = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG ] );
831
- if ( ( 1 == get_post_meta( $order_id, '_ga_tracked', true ) ) && ! $do_not_flag_tracked_order ) {
832
  unset( $order );
833
  }
834
 
835
- if ( isset( $_COOKIE[ 'gtm4wp_orderid_tracked' ] ) ) {
836
- $tracked_order_id = filter_var( $_COOKIE[ 'gtm4wp_orderid_tracked' ], FILTER_VALIDATE_INT );
837
 
838
- if ( $tracked_order_id && ( $tracked_order_id == $order_id ) && !$do_not_flag_tracked_order ) {
839
  unset( $order );
840
  }
841
  }
842
 
843
- if ( isset( $order ) && ( "failed" == $order->get_status() ) ) {
844
- // do not track order where payment failed
845
  unset( $order );
846
  }
847
 
848
  if ( isset( $order ) ) {
849
- $dataLayer = gtm4wp_get_purchase_datalayer( $order, NULL );
850
 
851
- $has_html5_support = current_theme_supports( 'html5' );
852
  $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
853
 
854
  echo '
855
  <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
856
- window.' . $gtm4wp_datalayer_name . ' = window.' . $gtm4wp_datalayer_name . ' || [];
857
- window.' . $gtm4wp_datalayer_name . '.push(' . json_encode( $dataLayer ) . ');
858
  </script>';
859
 
860
  if ( ! $do_not_flag_tracked_order ) {
@@ -863,34 +1047,75 @@ function gtm4wp_woocommerce_thankyou( $order_id ) {
863
  }
864
  }
865
 
 
 
 
 
 
866
  function gtm4wp_woocommerce_single_add_to_cart_tracking() {
867
  global $product, $gtm4wp_datalayer_name, $gtm4wp_options;
868
 
869
- // exit early if there is nothing to do
870
  if ( ( false === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) && ( false === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
871
  return;
872
  }
873
 
874
- $eec_product_array = gtm4wp_process_product( $product, array(), 'addtocartsingle' );
 
 
 
 
875
 
876
  foreach ( $eec_product_array as $eec_product_array_key => $eec_product_array_value ) {
877
  echo '<input type="hidden" name="gtm4wp_' . esc_attr( $eec_product_array_key ) . '" value="' . esc_attr( $eec_product_array_value ) . '" />' . "\n";
878
  }
879
  }
880
 
 
 
 
 
 
881
  $GLOBALS['gtm4wp_cart_item_proddata'] = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  function gtm4wp_woocommerce_cart_item_product_filter( $product, $cart_item = '', $cart_id = '' ) {
883
  global $gtm4wp_options;
884
 
885
- $eec_product_array = gtm4wp_process_product( $product, array(
886
- 'productlink' => apply_filters( 'the_permalink', get_permalink(), 0 )
887
- ), 'cart' );
 
 
 
 
888
 
889
  $GLOBALS['gtm4wp_cart_item_proddata'] = $eec_product_array;
890
 
891
  return $product;
892
  }
893
 
 
 
 
 
 
 
 
 
 
 
894
  function gtm4wp_woocommerce_cart_item_remove_link_filter( $remove_from_cart_link ) {
895
  if ( ! isset( $GLOBALS['gtm4wp_cart_item_proddata'] ) ) {
896
  return $remove_from_cart_link;
@@ -908,7 +1133,7 @@ function gtm4wp_woocommerce_cart_item_remove_link_filter( $remove_from_cart_link
908
  $GLOBALS['gtm4wp_cart_item_proddata']['brand'] = '';
909
  }
910
 
911
- $cartlink_with_data = sprintf(
912
  'data-gtm4wp_product_id="%s" data-gtm4wp_product_name="%s" data-gtm4wp_product_price="%s" data-gtm4wp_product_cat="%s" data-gtm4wp_product_url="%s" data-gtm4wp_product_variant="%s" data-gtm4wp_product_stocklevel="%s" data-gtm4wp_product_brand="%s" href="',
913
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['id'] ),
914
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['name'] ),
@@ -919,17 +1144,31 @@ function gtm4wp_woocommerce_cart_item_remove_link_filter( $remove_from_cart_link
919
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['stocklevel'] ),
920
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['brand'] )
921
  );
 
922
  $GLOBALS['gtm4wp_cart_item_proddata'] = '';
923
 
924
  return gtm4wp_str_replace_first( 'href="', $cartlink_with_data, $remove_from_cart_link );
925
  }
926
 
 
 
 
 
 
 
927
  function gtp4wp_woocommerce_reset_loop() {
928
  global $woocommerce_loop;
929
 
930
  $woocommerce_loop['listtype'] = '';
931
  }
932
 
 
 
 
 
 
 
 
933
  function gtm4wp_woocommerce_add_related_to_loop( $arg ) {
934
  global $woocommerce_loop;
935
 
@@ -938,6 +1177,13 @@ function gtm4wp_woocommerce_add_related_to_loop( $arg ) {
938
  return $arg;
939
  }
940
 
 
 
 
 
 
 
 
941
  function gtm4wp_woocommerce_add_cross_sell_to_loop( $arg ) {
942
  global $woocommerce_loop;
943
 
@@ -946,6 +1192,13 @@ function gtm4wp_woocommerce_add_cross_sell_to_loop( $arg ) {
946
  return $arg;
947
  }
948
 
 
 
 
 
 
 
 
949
  function gtm4wp_woocommerce_add_upsells_to_loop( $arg ) {
950
  global $woocommerce_loop;
951
 
@@ -954,25 +1207,46 @@ function gtm4wp_woocommerce_add_upsells_to_loop( $arg ) {
954
  return $arg;
955
  }
956
 
 
 
 
 
 
 
 
 
957
  function gtm4wp_woocommerce_before_template_part( $template_name ) {
958
  ob_start();
959
  }
960
 
 
 
 
 
 
 
 
 
 
961
  function gtm4wp_woocommerce_after_template_part( $template_name ) {
962
  global $product, $gtm4wp_product_counter, $gtm4wp_last_widget_title, $gtm4wp_options;
963
 
964
  $productitem = ob_get_contents();
965
  ob_end_clean();
966
 
967
- if ( 'content-widget-product.php' == $template_name ) {
968
- $eec_product_array = gtm4wp_process_product( $product, array(
969
- 'productlink' => apply_filters( 'the_permalink', get_permalink(), 0 ),
970
- 'listname' => $gtm4wp_last_widget_title,
971
- 'listposition' => $gtm4wp_product_counter
972
- ), 'widgetproduct' );
 
 
 
 
973
 
974
- if ( ! isset( $eec_product_array[ 'brand' ] ) ) {
975
- $eec_product_array[ 'brand' ] = '';
976
  }
977
 
978
  $productlink_with_data = sprintf(
@@ -985,7 +1259,7 @@ function gtm4wp_woocommerce_after_template_part( $template_name ) {
985
  esc_attr( $eec_product_array['listname'] ),
986
  esc_attr( $eec_product_array['listposition'] ),
987
  esc_attr( $eec_product_array['stocklevel'] ),
988
- esc_attr( $eec_product_array[ "brand" ] )
989
  );
990
 
991
  $gtm4wp_product_counter++;
@@ -993,9 +1267,25 @@ function gtm4wp_woocommerce_after_template_part( $template_name ) {
993
  $productitem = str_replace( 'href="', $productlink_with_data, $productitem );
994
  }
995
 
996
- echo $productitem;
 
 
 
 
 
 
997
  }
998
 
 
 
 
 
 
 
 
 
 
 
999
  function gtm4wp_widget_title_filter( $widget_title ) {
1000
  global $gtm4wp_product_counter, $gtm4wp_last_widget_title;
1001
 
@@ -1005,49 +1295,99 @@ function gtm4wp_widget_title_filter( $widget_title ) {
1005
  return $widget_title;
1006
  }
1007
 
 
 
 
 
 
 
1008
  function gtm4wp_before_recent_products_loop() {
1009
  global $woocommerce_loop;
1010
 
1011
  $woocommerce_loop['listtype'] = __( 'Recent Products', 'duracelltomi-google-tag-manager' );
1012
  }
1013
 
 
 
 
 
 
 
1014
  function gtm4wp_before_sale_products_loop() {
1015
  global $woocommerce_loop;
1016
 
1017
  $woocommerce_loop['listtype'] = __( 'Sale Products', 'duracelltomi-google-tag-manager' );
1018
  }
1019
 
 
 
 
 
 
 
1020
  function gtm4wp_before_best_selling_products_loop() {
1021
  global $woocommerce_loop;
1022
 
1023
  $woocommerce_loop['listtype'] = __( 'Best Selling Products', 'duracelltomi-google-tag-manager' );
1024
  }
1025
 
 
 
 
 
 
 
1026
  function gtm4wp_before_top_rated_products_loop() {
1027
  global $woocommerce_loop;
1028
 
1029
  $woocommerce_loop['listtype'] = __( 'Top Rated Products', 'duracelltomi-google-tag-manager' );
1030
  }
1031
 
 
 
 
 
 
 
1032
  function gtm4wp_before_featured_products_loop() {
1033
  global $woocommerce_loop;
1034
 
1035
  $woocommerce_loop['listtype'] = __( 'Featured Products', 'duracelltomi-google-tag-manager' );
1036
  }
1037
 
 
 
 
 
 
 
1038
  function gtm4wp_before_related_products_loop() {
1039
  global $woocommerce_loop;
1040
 
1041
  $woocommerce_loop['listtype'] = __( 'Related Products', 'duracelltomi-google-tag-manager' );
1042
  }
1043
 
1044
- function gtm4wp_woocommerce_get_product_list_item_extra_tag($product, $listtype, $itemix, $permalink) {
 
 
 
 
 
 
 
 
 
 
1045
  global $wp_query, $gtm4wp_options;
1046
 
1047
  if ( ! isset( $product ) ) {
1048
  return;
1049
  }
1050
 
 
 
 
 
1051
  $product_id = $product->get_id();
1052
 
1053
  $product_cat = '';
@@ -1065,7 +1405,7 @@ function gtm4wp_woocommerce_get_product_list_item_extra_tag($product, $listtype,
1065
 
1066
  if ( is_search() ) {
1067
  $list_name = __( 'Search Results', 'duracelltomi-google-tag-manager' );
1068
- } elseif ( $listtype != '' ) {
1069
  $list_name = $listtype;
1070
  } else {
1071
  $list_name = __( 'General Product List', 'duracelltomi-google-tag-manager' );
@@ -1077,14 +1417,18 @@ function gtm4wp_woocommerce_get_product_list_item_extra_tag($product, $listtype,
1077
  $posts_per_page = 1;
1078
  }
1079
 
1080
- $eec_product_array = gtm4wp_process_product( $product, array(
1081
- 'productlink' => $permalink,
1082
- 'listname' => $list_name,
1083
- 'listposition' => (int) $itemix + ( $posts_per_page * ( $paged - 1 ) )
1084
- ), 'productlist' );
 
 
 
 
1085
 
1086
- if ( ! isset( $eec_product_array[ 'brand' ] ) ) {
1087
- $eec_product_array[ 'brand' ] = '';
1088
  }
1089
 
1090
  return sprintf(
@@ -1097,110 +1441,158 @@ function gtm4wp_woocommerce_get_product_list_item_extra_tag($product, $listtype,
1097
  esc_attr( $eec_product_array['listposition'] ),
1098
  esc_attr( $eec_product_array['listname'] ),
1099
  esc_attr( $eec_product_array['stocklevel'] ),
1100
- esc_attr( $eec_product_array[ "brand" ] )
1101
  );
1102
  }
1103
 
 
 
 
 
 
 
 
1104
  function gtm4wp_woocommerce_after_shop_loop_item() {
1105
  global $product, $woocommerce_loop;
1106
 
1107
- $listtype = "";
1108
- if ( isset( $woocommerce_loop['listtype'] ) && ( $woocommerce_loop['listtype'] != '' ) ) {
1109
  $listtype = $woocommerce_loop['listtype'];
1110
  }
1111
 
1112
- $itemix = "";
1113
- if ( isset( $woocommerce_loop['loop'] ) && ( $woocommerce_loop['loop'] != '' ) ) {
1114
  $itemix = $woocommerce_loop['loop'];
1115
  }
1116
 
1117
- echo gtm4wp_woocommerce_get_product_list_item_extra_tag($product, $listtype, $itemix, apply_filters( 'the_permalink', get_permalink(), 0 ));
 
 
 
 
 
 
 
 
 
 
1118
  }
1119
 
 
 
 
 
 
 
 
 
1120
  function gtm4wp_woocommerce_cart_item_restored( $cart_item_key ) {
1121
  if ( function_exists( 'WC' ) && WC()->session ) {
1122
  WC()->session->set( 'gtm4wp_product_readded_to_cart', $cart_item_key );
1123
  }
1124
  }
1125
 
 
 
 
 
 
 
1126
  function gtm4wp_woocommerce_enqueue_scripts() {
1127
  global $gtm4wp_options, $gtp4wp_plugin_url;
1128
 
1129
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
1130
- $in_footer = apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC, false );
1131
  wp_enqueue_script( 'gtm4wp-woocommerce-classic', $gtp4wp_plugin_url . 'js/gtm4wp-woocommerce-classic.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
1132
  }
1133
 
1134
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
1135
- $in_footer = apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC, false );
1136
  wp_enqueue_script( 'gtm4wp-woocommerce-enhanced', $gtp4wp_plugin_url . 'js/gtm4wp-woocommerce-enhanced.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
1137
  }
1138
  }
1139
 
 
 
 
 
 
 
 
1140
  function gtm4wp_wc_quick_view_before_single_product() {
1141
  global $gtm4wp_options, $gtm4wp_datalayer_name;
1142
 
1143
- $dataLayer = array(
1144
  'event' => 'gtm4wp.changeDetailViewEEC',
1145
  );
1146
 
1147
  if ( ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
1148
- $postid = get_the_ID();
1149
- $product = wc_get_product( $postid );
1150
 
1151
- $eec_product_array = gtm4wp_process_product( $product, array(), 'productdetail' );
 
 
 
 
1152
 
1153
- $dataLayer['productRatingCounts'] = $product->get_rating_counts();
1154
- $dataLayer['productAverageRating'] = (float) $product->get_average_rating();
1155
- $dataLayer['productReviewCount'] = (int) $product->get_review_count();
1156
- $dataLayer['productType'] = $product->get_type();
1157
 
1158
- switch ( $dataLayer['productType'] ) {
1159
- case 'variable': {
1160
- $dataLayer['productIsVariable'] = 1;
1161
 
1162
- $dataLayer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
1163
- $dataLayer['ecomm_pagetype'] = 'product';
1164
- $dataLayer['ecomm_totalvalue'] = $eec_product_array[ 'price' ];
1165
 
1166
  break;
1167
- }
1168
 
1169
- case 'grouped': {
1170
- $dataLayer['productIsVariable'] = 0;
1171
 
1172
  break;
1173
- }
1174
 
1175
- default: {
1176
- $dataLayer['productIsVariable'] = 0;
1177
 
1178
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
1179
- $dataLayer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array[ 'id' ] );
1180
- $dataLayer['ecomm_pagetype'] = 'product';
1181
- $dataLayer['ecomm_totalvalue'] = $eec_product_array['price'];
1182
  }
1183
 
1184
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
1185
- $currencyCode = get_woocommerce_currency();
1186
 
1187
- $dataLayer['ecommerce'] = array(
1188
- 'currencyCode' => $currencyCode,
1189
  'detail' => array(
1190
  'products' => array(
1191
- $eec_product_array
1192
- )
1193
- )
1194
  );
1195
  }
1196
- }
1197
  }
1198
  }
1199
 
1200
  echo '
1201
- <span style="display: none;" id="gtm4wp_quickview_data" data-gtm4wp_datalayer="' . esc_attr(json_encode( $dataLayer )) . '"></span>';
1202
  }
1203
 
 
 
 
 
 
 
 
 
 
1204
  function gtm4wp_woocommerce_grouped_product_list_column_label( $labelvalue, $product ) {
1205
  global $gtm4wp_options, $gtm4wp_grouped_product_ix;
1206
 
@@ -1210,16 +1602,20 @@ function gtm4wp_woocommerce_grouped_product_list_column_label( $labelvalue, $pro
1210
 
1211
  $list_name = __( 'Grouped Product Detail Page', 'duracelltomi-google-tag-manager' );
1212
 
1213
- $eec_product_array = gtm4wp_process_product( $product, array(
1214
- 'productlink' => $product->get_permalink(),
1215
- 'listname' => $list_name,
1216
- 'listposition' => $gtm4wp_grouped_product_ix
1217
- ), 'groupedproductlist' );
 
 
 
 
1218
 
1219
  $gtm4wp_grouped_product_ix++;
1220
 
1221
- if ( ! isset( $eec_product_array[ 'brand' ] ) ) {
1222
- $eec_product_array[ 'brand' ] = '';
1223
  }
1224
 
1225
  $labelvalue .=
@@ -1240,19 +1636,38 @@ function gtm4wp_woocommerce_grouped_product_list_column_label( $labelvalue, $pro
1240
  return $labelvalue;
1241
  }
1242
 
1243
- function gtm4wp_add_productdata_to_wc_block($content, $data, $product) {
1244
- $product_data_tag = gtm4wp_woocommerce_get_product_list_item_extra_tag($product, "", 0, $data->permalink);
1245
-
1246
- return preg_replace('/<li.+class=("|"[^"]+)wc-block-grid__product("|[^"]+")[^<]*>/i', '$0' . $product_data_tag, $content);
 
 
 
 
 
 
 
 
 
 
 
1247
  }
1248
 
 
 
 
 
 
 
 
 
1249
  function gtm4wp_woocommerce_header_top() {
1250
  global $gtm4wp_options;
1251
 
1252
- $has_html5_support = current_theme_supports( 'html5' );
1253
  $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
1254
 
1255
- echo "<script" . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . ">
1256
  const gtm4wp_is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
1257
  if ( gtm4wp_is_safari ) {
1258
  window.addEventListener('pageshow', function(event) {
@@ -1267,7 +1682,7 @@ if ( gtm4wp_is_safari ) {
1267
  </script>";
1268
  }
1269
 
1270
- // do not add filter if someone enabled WooCommerce integration without an activated WooCommerce plugin
1271
  if ( function_exists( 'WC' ) ) {
1272
  add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, 'gtm4wp_woocommerce_datalayer_filter_items' );
1273
 
@@ -1275,11 +1690,10 @@ if ( function_exists( 'WC' ) ) {
1275
  add_action( 'woocommerce_after_shop_loop_item', 'gtm4wp_woocommerce_after_shop_loop_item' );
1276
  add_action( 'woocommerce_after_add_to_cart_button', 'gtm4wp_woocommerce_single_add_to_cart_tracking' );
1277
 
1278
- // add_action( "wp_footer", "gtm4wp_woocommerce_wp_footer" );
1279
  add_action( 'wp_enqueue_scripts', 'gtm4wp_woocommerce_enqueue_scripts' );
1280
- add_filter( GTM4WP_WPFILTER_ADDGLOBALVARS, 'gtm4wp_woocommerce_addglobalvars' );
1281
 
1282
- add_filter( 'woocommerce_blocks_product_grid_item_html', 'gtm4wp_add_productdata_to_wc_block', 10, 3);
1283
 
1284
  add_action( 'woocommerce_thankyou', 'gtm4wp_woocommerce_thankyou' );
1285
 
1
  <?php
2
+ /**
3
+ * GTM4WP WooCoommerce integration.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
  define( 'GTM4WP_WPFILTER_EEC_PRODUCT_ARRAY', 'gtm4wp_eec_product_array' );
12
  define( 'GTM4WP_WPFILTER_EEC_CART_ITEM', 'gtm4wp_eec_cart_item' );
13
  define( 'GTM4WP_WPFILTER_EEC_ORDER_ITEM', 'gtm4wp_eec_order_item' );
19
  } else {
20
  $GLOBALS['gtm4wp_is_woocommerce3_7'] = false;
21
  }
22
+ $GLOBALS['gtm4wp_grouped_product_ix'] = 1;
23
  $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] = false;
24
 
25
+ /**
26
+ * Convert special unicode quotation and dash characters to normal version.
27
+ *
28
+ * @see https://snippets.webaware.com.au/ramblings/php-really-doesnt-unicode/
29
+ *
30
+ * @param string $fancy Input string with special unicode quotes and dash characters.
31
+ * @return string All kind of quotes and dash characters replaced with normal version.
32
+ */
33
  function gtm4wp_untexturize( $fancy ) {
34
+ $fixes = array(
35
+ json_decode( '"\u201C"' ) => '"', // left double quotation mark.
36
+ json_decode( '"\u201D"' ) => '"', // right double quotation mark.
37
+ json_decode( '"\u2018"' ) => "'", // left single quotation mark.
38
+ json_decode( '"\u2019"' ) => "'", // right single quotation mark.
39
+ json_decode( '"\u2032"' ) => "'", // prime (minutes, feet).
40
+ json_decode( '"\u2033"' ) => '"', // double prime (seconds, inches).
41
+ json_decode( '"\u2013"' ) => '-', // en dash.
42
+ json_decode( '"\u2014"' ) => '--', // em dash.
43
+ );
 
 
 
 
44
 
45
  $normal = strtr( $fancy, $fixes );
46
 
47
  return $normal;
48
  }
49
 
50
+ /**
51
+ * Takes a product ID and returns a string that has a prefix appended.
52
+ * The prefix can be set on the GTM4WP options page under Integration->WooCommerce.
53
+ *
54
+ * This is needed in cases where the generated feed has IDs with some sort of constant prefix and
55
+ * tracking needs to align with this ID in order for dynamic remarketing to work properly.
56
+ *
57
+ * @param int|string $product_id A product ID that has to be prefixed.
58
+ * @return string. The product ID with the prefix strings.
59
+ */
60
  function gtm4wp_prefix_productid( $product_id ) {
61
  global $gtm4wp_options;
62
 
63
+ if ( '' !== $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX ] ) {
64
  return $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMPRODIDPREFIX ] . $product_id;
65
  } else {
66
  return $product_id;
67
  }
68
  }
69
 
70
+ /**
71
+ * Replace only the first occurrence of the search string with the replacement string.
72
+ *
73
+ * @see https://stackoverflow.com/questions/1252693/using-str-replace-so-that-it-only-acts-on-the-first-match
74
+ *
75
+ * TODO: replace regexp usage.
76
+ *
77
+ * @param string $search The value being searched for, otherwise known as the needle. Must be a string.
78
+ * @param string $replace The replacement value that replaces found search values. Must be a string.
79
+ * @param string $subject The string being searched and replaced on, otherwise known as the haystack.
80
+ * @return string This function returns a string with the replaced values.
81
+ */
82
+ function gtm4wp_str_replace_first( $search, $replace, $subject ) {
83
+ $search = '/' . preg_quote( $search, '/' ) . '/';
84
+
85
+ return preg_replace( $search, $replace, $subject, 1 );
86
  }
87
 
88
+ /**
89
+ * Given a WooCommerce category ID, this function returns the full path to this category separated with the / character.
90
+ *
91
+ * @param int $category_id The ID of the WooCommerce category that needs to be scanned for parents.
92
+ * @return string The category path. An example outout can be: Home / Clothing / Toddlers.
93
+ */
94
  function gtm4wp_get_product_category_hierarchy( $category_id ) {
95
  $cat_hierarchy = '';
96
 
112
  return $cat_hierarchy;
113
  }
114
 
115
+ /**
116
+ * Given a WooCommerce product ID, this function will return the first assigned category of the product.
117
+ * Currently, it does not take into account the "primary category" option of various SEO plugins.
118
+ *
119
+ * @param int $product_id A WooCommerce product ID whose first assigned category has to be returned.
120
+ * @param boolean $fullpath Set this to true of you need to query the full path including parent categories. Defaults to false.
121
+ * @return string The first category name of the product. Incluldes the name of parent categories if the $fullpath parameter is set to true.
122
+ */
123
  function gtm4wp_get_product_category( $product_id, $fullpath = false ) {
124
  $product_cat = '';
125
 
126
+ $_product_cats = wp_get_post_terms(
127
+ $product_id,
128
+ 'product_cat',
129
+ array(
130
+ 'orderby' => 'parent',
131
+ 'order' => 'ASC',
132
+ )
133
+ );
134
+
135
  if ( ( is_array( $_product_cats ) ) && ( count( $_product_cats ) > 0 ) ) {
136
  $first_product_cat = array_pop( $_product_cats );
137
  if ( $fullpath ) {
144
  return $product_cat;
145
  }
146
 
147
+ /**
148
+ * Given a WooCommerce product ID, this function returns the assigned value of a custom taxonomy like the brand name.
149
+ *
150
+ * @see https://developer.wordpress.org/reference/functions/wp_get_post_terms/
151
+ *
152
+ * @param int $product_id A WooCommerce product ID whose taxonomy assosiation needs to be queried.
153
+ * @param string $taxonomy The taxonomy slug for which to retrieve terms.
154
+ * @return string Returns the first assigned taxonomy value to the given WooCommerce product ID.
155
+ */
156
  function gtm4wp_woocommerce_getproductterm( $product_id, $taxonomy ) {
157
+ $gtm4wp_product_terms = wp_get_post_terms(
158
+ $product_id,
159
+ $taxonomy,
160
+ array(
161
+ 'orderby' => 'parent',
162
+ 'order' => 'ASC',
163
+ )
164
+ );
165
+
166
  if ( is_array( $gtm4wp_product_terms ) && ( count( $gtm4wp_product_terms ) > 0 ) ) {
167
  return $gtm4wp_product_terms[0]->name;
168
  }
169
 
170
+ return '';
171
  }
172
 
173
+ /**
174
+ * Given a WP_Product instane, this function returns an array of product attributes in the format of
175
+ * Google Analytics enhanced ecommerce product data.
176
+ *
177
+ * @see https://developers.google.com/analytics/devguides/collection/ua/gtm/enhanced-ecommerce
178
+ *
179
+ * @param WP_Product $product An instance of WP_Product that needs to be transformed into an enhanced ecommerce product object.
180
+ * @param array $additional_product_attributes Any key-value pair that needs to be added into the enhanced ecommerce product object.
181
+ * @param string $attributes_used_for The placement ID of the product that is passed to the apply_filters hook so that 3rd party code can be notified where this product data is being used.
182
+ * @return array The enhanced ecommerce product object of the WooCommerce product.
183
+ */
184
  function gtm4wp_process_product( $product, $additional_product_attributes, $attributes_used_for ) {
185
  global $gtm4wp_options;
186
 
197
  $remarketing_id = $product_id;
198
  $product_sku = $product->get_sku();
199
 
200
+ if ( 'variation' === $product_type ) {
201
  $parent_product_id = $product->get_parent_id();
202
  $product_cat = gtm4wp_get_product_category( $parent_product_id, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH ] );
203
  } else {
204
+ $product_cat = gtm4wp_get_product_category( $product_id, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSEFULLCATEGORYPATH ] );
205
  }
206
 
207
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSESKU ] && ( '' !== $product_sku ) ) {
208
  $remarketing_id = $product_sku;
209
  }
210
 
213
  'name' => $product->get_title(),
214
  'sku' => $product_sku ? $product_sku : $product_id,
215
  'category' => $product_cat,
216
+ 'price' => round( (float) wc_get_price_to_display( $product ), 2 ),
217
+ 'stocklevel' => $product->get_stock_quantity(),
218
  );
219
 
220
+ if ( '' !== $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ] ) {
221
+ if ( isset( $parent_product_id ) && ( 0 !== $parent_product_id ) ) {
222
  $product_id_to_query = $parent_product_id;
223
  } else {
224
  $product_id_to_query = $product_id;
225
  }
226
 
227
+ $_temp_productdata['brand'] = gtm4wp_woocommerce_getproductterm( $product_id_to_query, $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECBRANDTAXONOMY ] );
228
  }
229
 
230
+ if ( 'variation' === $product_type ) {
231
  $_temp_productdata['variant'] = implode( ',', $product->get_variation_attributes() );
232
  }
233
 
236
  return apply_filters( GTM4WP_WPFILTER_EEC_PRODUCT_ARRAY, $_temp_productdata, $attributes_used_for );
237
  }
238
 
239
+ /**
240
+ * Given a Google Business vertical ID, this function returns the name of the "ID" field in tagging Google Ads dynamic remarketing.
241
+ * This "id" in most cases, but sometimes "destination".
242
+ *
243
+ * @param string $vertical_id The Google Business vertical ID (like retail, flights, etc.).
244
+ * @return string The name of the "ID" field for tagging.
245
+ */
246
  function gtm4wp_get_gads_product_id_variable_name( $vertical_id ) {
247
  global $gtm4wp_business_verticals_ids;
248
 
249
  if ( array_key_exists( $vertical_id, $gtm4wp_business_verticals_ids ) ) {
250
  return $gtm4wp_business_verticals_ids[ $vertical_id ];
251
  } else {
252
+ return 'id';
253
  }
254
  }
255
 
256
+ /**
257
+ * Takes a GA3 style enhanced ecommerce product object and transforms it into a GA4 product object.
258
+ *
259
+ * @param array $productdata WooCommerce product data in GA3 enhanced ecommerce product object format.
260
+ * @return array WooCommerce product data in GA4 enhanced ecommerce product object format.
261
+ */
262
  function gtm4wp_map_eec_to_ga4( $productdata ) {
263
  global $gtm4wp_options;
264
 
265
+ if ( ! is_array( $productdata ) ) {
266
  return;
267
  }
268
 
269
+ $category_path = array_key_exists( 'category', $productdata ) ? $productdata['category'] : '';
270
+ $category_parts = explode( '/', $category_path );
271
 
272
+ // Default, required parameters.
273
  $ga4_product = array(
274
+ 'item_id' => array_key_exists( 'id', $productdata ) ? $productdata['id'] : '',
275
+ 'item_name' => array_key_exists( 'name', $productdata ) ? $productdata['name'] : '',
276
+ 'item_brand' => array_key_exists( 'brand', $productdata ) ? $productdata['brand'] : '',
277
+ 'price' => array_key_exists( 'price', $productdata ) ? $productdata['price'] : '',
278
  );
279
 
280
+ // Category, also handle category path.
281
+ if ( 1 === count( $category_parts ) ) {
282
+ $ga4_product['item_category'] = $category_parts[0];
283
+ } elseif ( count( $category_parts ) > 1 ) {
284
+ $ga4_product['item_category'] = $category_parts[0];
285
+
286
+ $num_category_parts = min( 5, count( $category_parts ) );
287
+ for ( $i = 1; $i < $num_category_parts; $i++ ) {
288
+ $ga4_product[ 'item_category_' . (string) ( $i + 1 ) ] = $category_parts[ $i ];
289
  }
290
  }
291
 
292
+ // Optional parameters which should not be included in the array if not set.
293
+ if ( array_key_exists( 'variant', $productdata ) ) {
294
+ $ga4_product['item_variant'] = $productdata['variant'];
295
  }
296
+ if ( array_key_exists( 'listname', $productdata ) ) {
297
+ $ga4_product['item_list_name'] = $productdata['listname'];
298
  }
299
+ if ( array_key_exists( 'listposition', $productdata ) ) {
300
+ $ga4_product['index'] = $productdata['listposition'];
301
  }
302
+ if ( array_key_exists( 'quantity', $productdata ) ) {
303
+ $ga4_product['quantity'] = $productdata['quantity'];
304
  }
305
+ if ( array_key_exists( 'coupon', $productdata ) ) {
306
+ $ga4_product['coupon'] = $productdata['coupon'];
307
  }
308
 
309
+ $ga4_product['google_business_vertical'] = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ];
310
+
311
+ $ga4_product[ gtm4wp_get_gads_product_id_variable_name( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] ) ] = gtm4wp_prefix_productid( $ga4_product['item_id'] );
312
 
313
  return $ga4_product;
314
  }
315
 
316
+ /**
317
+ * Takes a WooCommerce order and returns an associative array that can be used
318
+ * for enhanced ecommerce tracking and Google Ads dynamic remarketing (legacy version).
319
+ *
320
+ * @param WC_Order $order The order that needs to be processed.
321
+ * @return array An associative array with the keys:
322
+ * products - enhanced ecommerce (GA3) products
323
+ * sumprice - total order value based on item data
324
+ * product_ids - array of product IDs to be used in ecomm_prodid.
325
+ */
326
  function gtm4wp_process_order_items( $order ) {
327
  global $gtm4wp_options;
328
 
329
  $return_data = array(
330
+ 'products' => array(),
331
+ 'sumprice' => 0,
332
+ 'product_ids' => array(),
333
  );
334
 
335
  if ( ! $order ) {
336
  return $return_data;
337
  }
338
 
339
+ if ( ! ( $order instanceof WC_Order ) ) {
340
+ return $return_data;
341
+ }
342
+
343
  $order_items = $order->get_items();
344
 
345
  if ( $order_items ) {
348
  continue;
349
  }
350
 
351
+ $product = $item->get_product();
352
+ $inc_tax = ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) );
353
+ $product_price = round( (float) $order->get_item_total( $item, $inc_tax ), 2 );
354
+
355
+ $eec_product_array = gtm4wp_process_product(
356
+ $product,
357
+ array(
358
+ 'quantity' => $item->get_quantity(),
359
+ 'price' => $product_price,
360
+ ),
361
+ 'purchase'
362
+ );
363
 
364
  if ( $eec_product_array ) {
365
  $return_data['products'][] = $eec_product_array;
366
+ $return_data['sumprice'] += $product_price * $eec_product_array['quantity'];
367
  $return_data['product_ids'][] = gtm4wp_prefix_productid( $eec_product_array['id'] );
368
  }
369
  }
372
  return $return_data;
373
  }
374
 
375
+ /**
376
+ * Function to be called on the gtm4wp_add_global_vars hook to output WooCommerce related global JavaScript variables.
377
+ *
378
+ * @param array $return The already added variables as key-value pairs in an associative array.
379
+ * @return array The $return parameter with added global JavaScript variables as key-value pairs.
380
+ */
381
  function gtm4wp_woocommerce_addglobalvars( $return ) {
382
  global $gtm4wp_options;
383
 
387
  $gtm4wp_needs_shipping_address = false;
388
  }
389
 
390
+ $return['gtm4wp_use_sku_instead'] = (int) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCUSESKU ] );
391
+ $return['gtm4wp_id_prefix'] = gtm4wp_prefix_productid( '' );
392
+ $return['gtm4wp_remarketing'] = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] );
393
+ $return['gtm4wp_eec'] = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] );
394
+ $return['gtm4wp_classicec'] = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] );
395
+ $return['gtm4wp_currency'] = get_woocommerce_currency();
396
+ $return['gtm4wp_product_per_impression'] = (int) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCPRODPERIMPRESSION ] );
397
+ $return['gtm4wp_needs_shipping_address'] = (bool) $gtm4wp_needs_shipping_address;
398
+ $return['gtm4wp_business_vertical'] = esc_js( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] );
399
+ $return['gtm4wp_business_vertical_id'] = gtm4wp_get_gads_product_id_variable_name( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCBUSINESSVERTICAL ] );
 
400
 
401
  return $return;
402
  }
403
 
404
+ /**
405
+ * Takes a WooCommerce order and order items and generates the standard/classic and
406
+ * enhanced ecommerce version of the purchase data layer codes for Universal Analytics.
407
+ *
408
+ * @param WC_Order $order The WooCommerce order that needs to be transformed into an enhanced ecommerce data layer.
409
+ * @param array $order_items The array returned by gtm4wp_process_order_items(). It not set, then function will call gtm4wp_process_order_items().
410
+ * @return array The data layer content as an associative array that can be passed to json_encode() to product a JavaScript object used by GTM.
411
+ */
412
  function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
413
  global $gtm4wp_options, $gtm4wp_is_woocommerce3_7;
414
 
415
+ $data_layer = array();
416
 
417
  if ( $order instanceof WC_Order ) {
418
  $woo = WC();
419
 
420
+ /**
421
+ * Variable for Google Smart Shopping campaign new customer reporting.
422
+ *
423
+ * @see https://support.google.com/google-ads/answer/9917012?hl=en-AU#zippy=%2Cinstall-with-google-tag-manager
424
+ */
425
  if ( $woo->customer instanceof WC_Customer ) {
426
+ // we need to use this instead of $woo->customer as this will load proper total order number and value from the database instead of the session.
427
+ $woo_customer = new WC_Customer( $woo->customer->get_id() );
428
+ $data_layer['new_customer'] = $woo_customer->get_order_count() === 1;
429
  }
430
 
431
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEXCLUDETAX ] ) {
432
+ $order_revenue = (float) ( $order->get_total() - $order->get_total_tax() );
433
  } else {
434
  $order_revenue = (float) $order->get_total();
435
  }
443
  $order_currency = $order->get_currency();
444
 
445
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
446
+ $data_layer['event'] = 'gtm4wp.orderCompleted';
447
+ $data_layer['transactionId'] = $order->get_order_number();
448
+ $data_layer['transactionAffiliation'] = '';
449
+ $data_layer['transactionTotal'] = $order_revenue;
450
+ $data_layer['transactionShipping'] = $order_shipping_cost;
451
+ $data_layer['transactionTax'] = (float) $order->get_total_tax();
452
+ $data_layer['transactionCurrency'] = $order_currency;
453
  }
454
 
455
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
456
+ $data_layer['event'] = 'gtm4wp.orderCompletedEEC';
457
+ $data_layer['ecommerce'] = array(
458
  'currencyCode' => $order_currency,
459
  'purchase' => array(
460
  'actionField' => array(
462
  'affiliation' => '',
463
  'revenue' => $order_revenue,
464
  'tax' => (float) $order->get_total_tax(),
465
+ 'shipping' => (float) ( $order->get_shipping_total() ),
466
  'coupon' => implode( ', ', ( $gtm4wp_is_woocommerce3_7 ? $order->get_coupon_codes() : $order->get_used_coupons() ) ),
467
+ ),
468
+ ),
469
  );
470
  }
471
 
476
  }
477
 
478
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
479
+ $data_layer['transactionProducts'] = $_order_items['products'];
480
  }
481
 
482
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
483
+ $data_layer['ecommerce']['purchase']['products'] = $_order_items['products'];
484
  }
485
 
486
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
487
+ $data_layer['ecomm_prodid'] = $_order_items['product_ids'];
488
+ $data_layer['ecomm_pagetype'] = 'purchase';
489
+ $data_layer['ecomm_totalvalue'] = (float) $_order_items['sumprice'];
490
  }
491
  }
492
 
493
+ return $data_layer;
494
  }
495
 
496
+ /**
497
+ * Function executed when the main GTM4WP data layer generation happens.
498
+ * Hooks into gtm4wp_compile_datalayer.
499
+ *
500
+ * @param array $data_layer An array of key-value pairs that will be converted into a JavaScript object on the frontend for GTM.
501
+ * @return array Extended data layer content with WooCommerce data added.
502
+ */
503
+ function gtm4wp_woocommerce_datalayer_filter_items( $data_layer ) {
504
  global $gtm4wp_options, $wp_query, $gtm4wp_datalayer_name, $gtm4wp_product_counter, $gtm4wp_is_woocommerce3_7;
505
 
506
+ if ( array_key_exists( 'HTTP_X_REQUESTED_WITH', $_SERVER ) ) {
507
+ return $data_layer;
508
  }
509
 
510
  $woo = WC();
511
 
512
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCCUSTOMERDATA ] ) {
513
  if ( $woo->customer instanceof WC_Customer ) {
514
+ // we need to use this instead of $woo->customer as this will load proper total order number and value from the database instead of the session.
515
  $woo_customer = new WC_Customer( $woo->customer->get_id() );
516
 
517
+ $data_layer['customerTotalOrders'] = $woo_customer->get_order_count();
518
+ $data_layer['customerTotalOrderValue'] = $woo_customer->get_total_spent();
519
+
520
+ $data_layer['customerFirstName'] = $woo_customer->get_first_name();
521
+ $data_layer['customerLastName'] = $woo_customer->get_last_name();
522
+
523
+ $data_layer['customerBillingFirstName'] = $woo_customer->get_billing_first_name();
524
+ $data_layer['customerBillingLastName'] = $woo_customer->get_billing_last_name();
525
+ $data_layer['customerBillingCompany'] = $woo_customer->get_billing_company();
526
+ $data_layer['customerBillingAddress1'] = $woo_customer->get_billing_address_1();
527
+ $data_layer['customerBillingAddress2'] = $woo_customer->get_billing_address_2();
528
+ $data_layer['customerBillingCity'] = $woo_customer->get_billing_city();
529
+ $data_layer['customerBillingPostcode'] = $woo_customer->get_billing_postcode();
530
+ $data_layer['customerBillingCountry'] = $woo_customer->get_billing_country();
531
+ $data_layer['customerBillingEmail'] = $woo_customer->get_billing_email();
532
+ $data_layer['customerBillingEmailHash'] = hash( 'sha256', $woo_customer->get_billing_email() );
533
+ $data_layer['customerBillingPhone'] = $woo_customer->get_billing_phone();
534
+
535
+ $data_layer['customerShippingFirstName'] = $woo_customer->get_shipping_first_name();
536
+ $data_layer['customerShippingLastName'] = $woo_customer->get_shipping_last_name();
537
+ $data_layer['customerShippingCompany'] = $woo_customer->get_shipping_company();
538
+ $data_layer['customerShippingAddress1'] = $woo_customer->get_shipping_address_1();
539
+ $data_layer['customerShippingAddress2'] = $woo_customer->get_shipping_address_2();
540
+ $data_layer['customerShippingCity'] = $woo_customer->get_shipping_city();
541
+ $data_layer['customerShippingPostcode'] = $woo_customer->get_shipping_postcode();
542
+ $data_layer['customerShippingCountry'] = $woo_customer->get_shipping_country();
543
  }
544
  }
545
 
546
+ if (
547
+ $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEINCLUDECARTINDL ] &&
548
+ version_compare( $woo->version, '3.2', '>=' ) &&
549
+ isset( $woo ) &&
550
+ isset( $woo->cart )
551
+ ) {
552
  $current_cart = $woo->cart;
553
+
554
+ $data_layer['cartContent'] = array(
555
+ 'totals' => array(
556
+ 'applied_coupons' => $current_cart->get_applied_coupons(),
557
+ 'discount_total' => $current_cart->get_discount_total(),
558
+ 'subtotal' => $current_cart->get_subtotal(),
559
+ 'total' => $current_cart->get_cart_contents_total(),
560
  ),
561
+ 'items' => array(),
562
  );
563
 
564
+ foreach ( $current_cart->get_cart() as $cart_item_id => $cart_item_data ) {
565
+ $product = apply_filters( 'woocommerce_cart_item_product', $cart_item_data['data'], $cart_item_data, $cart_item_id );
566
  if (
567
+ ! apply_filters( GTM4WP_WPFILTER_EEC_CART_ITEM, true, $cart_item_data )
568
+ || ! apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item_data, $cart_item_id )
569
  ) {
570
  continue;
571
  }
572
 
573
+ $eec_product_array = gtm4wp_process_product(
574
+ $product,
575
+ array(
576
+ 'quantity' => $cart_item_data['quantity'],
577
+ ),
578
+ 'cart'
579
+ );
580
 
581
+ $data_layer['cartContent']['items'][] = $eec_product_array;
582
  }
583
  }
584
 
591
  }
592
 
593
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
594
+ $data_layer['ecomm_prodid'] = array();
595
+ $data_layer['ecomm_pagetype'] = $ecomm_pagetype;
596
+ $data_layer['ecomm_totalvalue'] = 0;
597
  }
598
  } elseif ( is_product() ) {
599
  if (
600
  $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ]
601
  || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] )
602
  ) {
603
+ $postid = get_the_ID();
604
+ $product = wc_get_product( $postid );
605
 
606
+ $eec_product_array = gtm4wp_process_product(
607
+ $product,
608
+ array(),
609
+ 'productdetail'
610
+ );
611
 
612
+ $data_layer['productRatingCounts'] = $product->get_rating_counts();
613
+ $data_layer['productAverageRating'] = (float) $product->get_average_rating();
614
+ $data_layer['productReviewCount'] = (int) $product->get_review_count();
615
+ $data_layer['productType'] = $product->get_type();
616
 
617
+ switch ( $data_layer['productType'] ) {
618
+ case 'variable':
619
+ $data_layer['productIsVariable'] = 1;
620
 
621
+ $data_layer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array['id'] );
622
+ $data_layer['ecomm_pagetype'] = 'product';
623
+ $data_layer['ecomm_totalvalue'] = $eec_product_array['price'];
624
 
625
  break;
 
626
 
627
+ case 'grouped':
628
+ $data_layer['productIsVariable'] = 0;
629
 
630
  break;
 
631
 
632
+ default:
633
+ $data_layer['productIsVariable'] = 0;
634
 
635
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
636
+ $data_layer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array['id'] );
637
+ $data_layer['ecomm_pagetype'] = 'product';
638
+ $data_layer['ecomm_totalvalue'] = $eec_product_array['price'];
639
  }
640
 
641
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
642
+ $currency_code = get_woocommerce_currency();
643
 
644
+ $data_layer['event'] = 'gtm4wp.changeDetailViewEEC';
645
+ $data_layer['ecommerce'] = array(
646
+ 'currencyCode' => $currency_code,
647
  'detail' => array(
648
  'products' => array(
649
+ $eec_product_array,
650
+ ),
651
+ ),
652
  );
653
  }
 
654
  }
655
  }
656
  } elseif ( is_cart() ) {
671
  continue;
672
  }
673
 
674
+ $eec_product_array = gtm4wp_process_product(
675
+ $product,
676
+ array(
677
+ 'quantity' => $cart_item_data['quantity'],
678
+ ),
679
+ 'cart'
680
+ );
681
 
682
+ $gtm4wp_cart_products[] = $eec_product_array;
683
+ $gtm4wp_cart_products_remarketing[] = gtm4wp_prefix_productid( $eec_product_array['id'] );
684
  }
685
 
686
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
687
+ $data_layer['ecomm_prodid'] = $gtm4wp_cart_products_remarketing;
688
+ $data_layer['ecomm_pagetype'] = 'cart';
689
  if ( ! $woo->cart->prices_include_tax ) {
690
  $cart_total = $woo->cart->cart_contents_total;
691
  } else {
692
  $cart_total = $woo->cart->cart_contents_total + $woo->cart->tax_total;
693
  }
694
+ $data_layer['ecomm_totalvalue'] = (float) $cart_total;
695
  }
696
 
697
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
698
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ] ) {
699
+ $data_layer['event'] = 'gtm4wp.checkoutStepEEC';
700
+ $data_layer['ecommerce'] = array(
701
  'currencyCode' => $gtm4wp_currency,
702
  'checkout' => array(
703
  'actionField' => array(
704
  'step' => 1,
705
  ),
706
  'products' => $gtm4wp_cart_products,
707
+ ),
708
  );
709
  } else {
710
+ // add only ga4 products to populate view_cart event.
711
+ $data_layer['ecommerce'] = array(
712
+ 'cart' => $gtm4wp_cart_products,
713
  );
714
  }
715
  }
716
  }
717
  } elseif ( is_order_received_page() ) {
718
  $do_not_flag_tracked_order = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG ] );
719
+
720
+ // Supressing 'Processing form data without nonce verification.' message as there is no nonce accesible in this case.
721
+ $order_id = filter_var( wp_unslash( isset( $_GET['order'] ) ? $_GET['order'] : '' ), FILTER_VALIDATE_INT ); // phpcs:ignore
722
+ if ( ! $order_id & isset( $GLOBALS['wp']->query_vars['order-received'] ) ) {
723
+ $order_id = $GLOBALS['wp']->query_vars['order-received'];
724
+ }
725
+ $order_id = absint( $order_id );
726
+
727
+ $order_id_filtered = apply_filters( 'woocommerce_thankyou_order_id', $order_id );
728
+ if ( '' !== $order_id_filtered ) {
729
  $order_id = $order_id_filtered;
730
  }
731
 
732
+ // Supressing 'Processing form data without nonce verification.' message as there is no nonce accesible in this case.
733
+ $order_key = isset( $_GET['key'] ) ? wc_clean( sanitize_text_field( wp_unslash( $_GET['key'] ) ) ) : ''; // phpcs:ignore
734
+ $order_key = apply_filters( 'woocommerce_thankyou_order_key', $order_key );
735
 
736
  if ( $order_id > 0 ) {
737
  $order = wc_get_order( $order_id );
739
  if ( $order instanceof WC_Order ) {
740
  $this_order_key = $order->get_order_key();
741
 
742
+ if ( $this_order_key !== $order_key ) {
743
  unset( $order );
744
  }
745
  } else {
747
  }
748
  }
749
 
750
+ /*
751
+ From this point if for any reason purchase data is not pushed
752
+ that is because for a specific reason.
753
+ In any other case woocommerce_thankyou hook will be the fallback if
754
+ is_order_received_page does not work.
755
+ */
756
  $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] = true;
757
 
758
+ if ( isset( $order ) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
759
 
760
+ if ( $order->is_paid() && $order->get_date_paid() ) {
761
+ $now = new DateTime( 'now', $order->get_date_paid()->getTimezone() );
762
+ $diff = $now->diff( $order->get_date_paid() );
763
+ $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
764
  } else {
765
+ $now = new DateTime( 'now', $order->get_date_created()->getTimezone() );
766
+ $diff = $now->diff( $order->get_date_created() );
767
+ $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
768
  }
769
 
770
+ if ( $minutes > $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
771
+ unset( $order );
772
  }
773
  }
774
 
775
+ $order_items = null;
776
+ if ( isset( $order ) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERDATA ] ) {
777
  $order_items = gtm4wp_process_order_items( $order );
778
 
779
+ $data_layer['orderData'] = array(
780
  'attributes' => array(
781
+ 'date' => $order->get_date_created()->date( 'c' ),
782
 
783
+ 'order_number' => $order->get_order_number(),
784
+ 'order_key' => $order->get_order_key(),
785
 
786
  'payment_method' => esc_js( $order->get_payment_method() ),
787
+ 'payment_method_title' => esc_js( $order->get_payment_method_title() ),
788
 
789
+ 'shipping_method' => esc_js( $order->get_shipping_method() ),
790
 
791
+ 'status' => esc_js( $order->get_status() ),
792
 
793
+ 'coupons' => implode( ', ', ( $gtm4wp_is_woocommerce3_7 ? $order->get_coupon_codes() : $order->get_used_coupons() ) ),
794
  ),
795
+ 'totals' => array(
796
  'currency' => esc_js( $order->get_currency() ),
797
  'discount_total' => esc_js( $order->get_discount_total() ),
798
  'discount_tax' => esc_js( $order->get_discount_tax() ),
803
  'total_tax' => esc_js( $order->get_total_tax() ),
804
  'total_discount' => esc_js( $order->get_total_discount() ),
805
  'subtotal' => esc_js( $order->get_subtotal() ),
806
+ 'tax_totals' => $order->get_tax_totals(),
807
  ),
808
+ 'customer' => array(
809
+ 'id' => $order->get_customer_id(),
810
 
811
+ 'billing' => array(
812
  'first_name' => esc_js( $order->get_billing_first_name() ),
813
  'last_name' => esc_js( $order->get_billing_last_name() ),
814
  'company' => esc_js( $order->get_billing_company() ),
820
  'country' => esc_js( $order->get_billing_country() ),
821
  'email' => esc_js( $order->get_billing_email() ),
822
  'emailhash' => esc_js( hash( 'sha256', $order->get_billing_email() ) ),
823
+ 'phone' => esc_js( $order->get_billing_phone() ),
824
  ),
825
 
826
  'shipping' => array(
832
  'city' => esc_js( $order->get_shipping_city() ),
833
  'state' => esc_js( $order->get_shipping_state() ),
834
  'postcode' => esc_js( $order->get_shipping_postcode() ),
835
+ 'country' => esc_js( $order->get_shipping_country() ),
836
+ ),
837
 
838
  ),
839
+ 'items' => $order_items['products'],
840
  );
841
  }
842
 
843
+ if ( ( 1 === get_post_meta( $order_id, '_ga_tracked', true ) ) && ! $do_not_flag_tracked_order ) {
844
  unset( $order );
845
  }
846
 
847
+ if ( isset( $_COOKIE['gtm4wp_orderid_tracked'] ) ) {
848
+ $tracked_order_id = filter_var( wp_unslash( $_COOKIE['gtm4wp_orderid_tracked'] ), FILTER_VALIDATE_INT );
849
 
850
+ if ( $tracked_order_id && ( $tracked_order_id === $order_id ) && ! $do_not_flag_tracked_order ) {
851
  unset( $order );
852
  }
853
  }
854
 
855
+ if ( isset( $order ) && ( 'failed' === $order->get_status() ) ) {
856
+ // do not track order where payment failed.
857
  unset( $order );
858
  }
859
 
860
  if ( isset( $order ) ) {
861
+ $data_layer = array_merge( $data_layer, gtm4wp_get_purchase_datalayer( $order, $order_items ) );
862
 
863
  if ( ! $do_not_flag_tracked_order ) {
864
  update_post_meta( $order_id, '_ga_tracked', 1 );
880
  continue;
881
  }
882
 
883
+ $eec_product_array = gtm4wp_process_product(
884
+ $product,
885
+ array(
886
+ 'quantity' => $cart_item_data['quantity'],
887
+ ),
888
+ 'cart'
889
+ );
890
 
891
  $gtm4wp_checkout_products[] = $eec_product_array;
892
 
893
+ $gtm4wp_checkout_products_remarketing[] = gtm4wp_prefix_productid( $eec_product_array['id'] );
894
  $gtm4wp_totalvalue += $eec_product_array['quantity'] * $eec_product_array['price'];
895
  } // end foreach cart item
896
 
897
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
898
+ $data_layer['ecomm_prodid'] = $gtm4wp_checkout_products_remarketing;
899
+ $data_layer['ecomm_pagetype'] = 'cart';
900
+ $data_layer['ecomm_totalvalue'] = (float) $gtm4wp_totalvalue;
901
  }
902
 
903
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
904
+ $currency_code = get_woocommerce_currency();
905
 
906
  $ga4_products = array();
907
+ $sum_value = 0;
908
+
909
+ foreach ( $gtm4wp_checkout_products as $oneproduct ) {
910
  $ga4_products[] = gtm4wp_map_eec_to_ga4( $oneproduct );
911
+ $sum_value += $oneproduct['price'] * $oneproduct['quantity'];
912
  }
913
 
914
+ $data_layer['event'] = 'gtm4wp.checkoutStepEEC';
915
+ $data_layer['ecommerce'] = array(
916
+ 'currencyCode' => $currency_code,
917
  'checkout' => array(
918
  'actionField' => array(
919
  'step' => 1 + (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ],
920
  ),
921
  'products' => $gtm4wp_checkout_products,
922
+ ),
923
  );
924
 
925
+ wc_enqueue_js(
926
+ '
927
+ window.gtm4wp_checkout_products = ' . wp_json_encode( $gtm4wp_checkout_products ) . ';
928
+ window.gtm4wp_checkout_products_ga4 = ' . wp_json_encode( $ga4_products ) . ';
929
  window.gtm4wp_checkout_value = ' . (float) $sum_value . ';
930
  window.gtm4wp_checkout_step_offset = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEECCARTASFIRSTSTEP ] . ';'
931
  );
933
  }
934
  } else {
935
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
936
+ $data_layer['ecomm_pagetype'] = 'other';
937
  }
938
  }
939
 
944
  if ( ! empty( $cart_item ) ) {
945
  $product = $cart_item['data'];
946
 
947
+ $eec_product_array = gtm4wp_process_product(
948
+ $product,
949
+ array(
950
+ 'quantity' => $cart_item['quantity'],
951
+ ),
952
+ 'readdedtocart'
953
+ );
954
 
955
+ $currency_code = get_woocommerce_currency();
956
 
957
+ $data_layer['event'] = 'gtm4wp.addProductToCartEEC';
958
+ $data_layer['ecommerce'] = array(
959
+ 'currencyCode' => $currency_code,
960
+ 'add' => array(
961
  'products' => array(
962
+ $eec_product_array,
963
+ ),
964
+ ),
965
  );
966
  }
967
 
968
+ WC()->session->set( 'gtm4wp_product_readded_to_cart', null );
969
  }
970
  }
971
 
972
+ return $data_layer;
973
  }
974
 
975
+ /**
976
+ * Executed during woocommerce_thankyou.
977
+ * This is a fallback function to output purchase data layer on customized order received pages where
978
+ * the is_order_received_page() template tag returns false for some reason.
979
+ *
980
+ * @param int $order_id The ID of the order placed by the user just recently.
981
+ * @return void
982
+ */
983
  function gtm4wp_woocommerce_thankyou( $order_id ) {
984
  global $gtm4wp_options, $gtm4wp_datalayer_name;
985
 
986
+ /*
987
+ If this flag is set to true, it means that the puchase event was fired
988
+ when capturing the is_order_received_page template tag therefore
989
+ no need to handle this here twice
990
+ */
991
  if ( $GLOBALS['gtm4wp_woocommerce_purchase_data_pushed'] ) {
992
  return;
993
  }
996
  $order = wc_get_order( $order_id );
997
  }
998
 
999
+ if ( isset( $order ) && $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
1000
  $now = new DateTime();
1001
+ if ( $order->is_paid() && $order->get_date_paid() ) {
1002
+ $diff = $now->diff( $order->get_date_paid() );
1003
  $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
1004
  } else {
1005
+ $diff = $now->diff( $order->get_date_created() );
1006
  $minutes = ( $diff->days * 24 * 60 ) + ( $diff->h * 60 ) + $diff->i;
1007
  }
1008
 
1009
+ if ( $minutes > $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCORDERMAXAGE ] ) {
1010
  unset( $order );
1011
  }
1012
  }
1013
 
1014
  $do_not_flag_tracked_order = (bool) ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCNOORDERTRACKEDFLAG ] );
1015
+ if ( ( 1 === get_post_meta( $order_id, '_ga_tracked', true ) ) && ! $do_not_flag_tracked_order ) {
1016
  unset( $order );
1017
  }
1018
 
1019
+ if ( isset( $_COOKIE['gtm4wp_orderid_tracked'] ) ) {
1020
+ $tracked_order_id = filter_var( wp_unslash( $_COOKIE['gtm4wp_orderid_tracked'] ), FILTER_VALIDATE_INT );
1021
 
1022
+ if ( $tracked_order_id && ( $tracked_order_id === $order_id ) && ! $do_not_flag_tracked_order ) {
1023
  unset( $order );
1024
  }
1025
  }
1026
 
1027
+ if ( isset( $order ) && ( 'failed' === $order->get_status() ) ) {
1028
+ // do not track order where payment failed.
1029
  unset( $order );
1030
  }
1031
 
1032
  if ( isset( $order ) ) {
1033
+ $data_layer = gtm4wp_get_purchase_datalayer( $order, null );
1034
 
1035
+ $has_html5_support = current_theme_supports( 'html5' );
1036
  $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
1037
 
1038
  echo '
1039
  <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
1040
+ window.' . esc_js( $gtm4wp_datalayer_name ) . ' = window.' . esc_js( $gtm4wp_datalayer_name ) . ' || [];
1041
+ window.' . esc_js( $gtm4wp_datalayer_name ) . '.push(' . wp_json_encode( $data_layer ) . ');
1042
  </script>';
1043
 
1044
  if ( ! $do_not_flag_tracked_order ) {
1047
  }
1048
  }
1049
 
1050
+ /**
1051
+ * Function executed with the woocommerce_after_add_to_cart_button hook.
1052
+ *
1053
+ * @return void
1054
+ */
1055
  function gtm4wp_woocommerce_single_add_to_cart_tracking() {
1056
  global $product, $gtm4wp_datalayer_name, $gtm4wp_options;
1057
 
1058
+ // exit early if there is nothing to do.
1059
  if ( ( false === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) && ( false === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
1060
  return;
1061
  }
1062
 
1063
+ $eec_product_array = gtm4wp_process_product(
1064
+ $product,
1065
+ array(),
1066
+ 'addtocartsingle'
1067
+ );
1068
 
1069
  foreach ( $eec_product_array as $eec_product_array_key => $eec_product_array_value ) {
1070
  echo '<input type="hidden" name="gtm4wp_' . esc_attr( $eec_product_array_key ) . '" value="' . esc_attr( $eec_product_array_value ) . '" />' . "\n";
1071
  }
1072
  }
1073
 
1074
+ /**
1075
+ * Universal Analytics enhanced ecommerce product array with the product that is currently shown in the cart.
1076
+ *
1077
+ * @var array
1078
+ */
1079
  $GLOBALS['gtm4wp_cart_item_proddata'] = '';
1080
+
1081
+ /**
1082
+ * Executed during woocommerce_cart_item_product for each product in the cart.
1083
+ * Stores the Universal Analytics enhanced ecommerce product data into a global variable
1084
+ * to be processed when the cart item is rendered.
1085
+ *
1086
+ * @see https://woocommerce.github.io/code-reference/files/woocommerce-templates-cart-cart.html#source-view.41
1087
+ *
1088
+ * @param WC_Product $product A WooCommerce product that is shown in the cart.
1089
+ * @param string $cart_item Not used by this hook.
1090
+ * @param string $cart_id Not used by this hook.
1091
+ * @return array Enhanced ecommerce product data in an associative array.
1092
+ */
1093
  function gtm4wp_woocommerce_cart_item_product_filter( $product, $cart_item = '', $cart_id = '' ) {
1094
  global $gtm4wp_options;
1095
 
1096
+ $eec_product_array = gtm4wp_process_product(
1097
+ $product,
1098
+ array(
1099
+ 'productlink' => apply_filters( 'the_permalink', get_permalink(), 0 ),
1100
+ ),
1101
+ 'cart'
1102
+ );
1103
 
1104
  $GLOBALS['gtm4wp_cart_item_proddata'] = $eec_product_array;
1105
 
1106
  return $product;
1107
  }
1108
 
1109
+ /**
1110
+ * Executed during woocommerce_cart_item_remove_link.
1111
+ * Adds additional product data into the remove product link of the cart table to be able to track
1112
+ * enhanced ecommerce remove_from_cart action with product data.
1113
+ *
1114
+ * @global gtm4wp_cart_item_proddata The previously stored product array in gtm4wp_woocommerce_cart_item_product_filter.
1115
+ *
1116
+ * @param string $remove_from_cart_link The HTML code of the remove from cart link element.
1117
+ * @return string The updated remove product from cart link with product data added in data attributes.
1118
+ */
1119
  function gtm4wp_woocommerce_cart_item_remove_link_filter( $remove_from_cart_link ) {
1120
  if ( ! isset( $GLOBALS['gtm4wp_cart_item_proddata'] ) ) {
1121
  return $remove_from_cart_link;
1133
  $GLOBALS['gtm4wp_cart_item_proddata']['brand'] = '';
1134
  }
1135
 
1136
+ $cartlink_with_data = sprintf(
1137
  'data-gtm4wp_product_id="%s" data-gtm4wp_product_name="%s" data-gtm4wp_product_price="%s" data-gtm4wp_product_cat="%s" data-gtm4wp_product_url="%s" data-gtm4wp_product_variant="%s" data-gtm4wp_product_stocklevel="%s" data-gtm4wp_product_brand="%s" href="',
1138
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['id'] ),
1139
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['name'] ),
1144
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['stocklevel'] ),
1145
  esc_attr( $GLOBALS['gtm4wp_cart_item_proddata']['brand'] )
1146
  );
1147
+
1148
  $GLOBALS['gtm4wp_cart_item_proddata'] = '';
1149
 
1150
  return gtm4wp_str_replace_first( 'href="', $cartlink_with_data, $remove_from_cart_link );
1151
  }
1152
 
1153
+ /**
1154
+ * Executed during loop_end.
1155
+ * Resets the product impression list name after a specific product list ended rendering.
1156
+ *
1157
+ * @return void
1158
+ */
1159
  function gtp4wp_woocommerce_reset_loop() {
1160
  global $woocommerce_loop;
1161
 
1162
  $woocommerce_loop['listtype'] = '';
1163
  }
1164
 
1165
+ /**
1166
+ * Executed during woocommerce_related_products_args.
1167
+ * Sets the currently rendered product list impression name to Related Products.
1168
+ *
1169
+ * @param array $arg Not used by this hook.
1170
+ * @return array
1171
+ */
1172
  function gtm4wp_woocommerce_add_related_to_loop( $arg ) {
1173
  global $woocommerce_loop;
1174
 
1177
  return $arg;
1178
  }
1179
 
1180
+ /**
1181
+ * Executed during woocommerce_cross_sells_columns.
1182
+ * Sets the currently rendered product list impression name to Cross-Sell Products.
1183
+ *
1184
+ * @param array $arg Not used by this hook.
1185
+ * @return array
1186
+ */
1187
  function gtm4wp_woocommerce_add_cross_sell_to_loop( $arg ) {
1188
  global $woocommerce_loop;
1189
 
1192
  return $arg;
1193
  }
1194
 
1195
+ /**
1196
+ * Executed during woocommerce_upsells_columns.
1197
+ * Sets the currently rendered product list impression name to Upsell Products.
1198
+ *
1199
+ * @param array $arg Not used by this hook.
1200
+ * @return array
1201
+ */
1202
  function gtm4wp_woocommerce_add_upsells_to_loop( $arg ) {
1203
  global $woocommerce_loop;
1204
 
1207
  return $arg;
1208
  }
1209
 
1210
+ /**
1211
+ * Executed during woocommerce_before_template_part.
1212
+ * Starts output buffering in order to be able to add product data attributes to the link element
1213
+ * of a product list (classic) widget.
1214
+ *
1215
+ * @param string $template_name The template part that is being rendered.
1216
+ * @return void
1217
+ */
1218
  function gtm4wp_woocommerce_before_template_part( $template_name ) {
1219
  ob_start();
1220
  }
1221
 
1222
+ /**
1223
+ * Executed during woocommerce_after_template_part.
1224
+ * Stops output buffering and gets the generated content since woocommerce_before_template_part.
1225
+ * Adds data attributes into the product link to be able to track product list impression and
1226
+ * click actions with Google Tag Manager.
1227
+ *
1228
+ * @param string $template_name The template part that is being rendered. This functions looks for content-widget-product.php.
1229
+ * @return void
1230
+ */
1231
  function gtm4wp_woocommerce_after_template_part( $template_name ) {
1232
  global $product, $gtm4wp_product_counter, $gtm4wp_last_widget_title, $gtm4wp_options;
1233
 
1234
  $productitem = ob_get_contents();
1235
  ob_end_clean();
1236
 
1237
+ if ( 'content-widget-product.php' === $template_name ) {
1238
+ $eec_product_array = gtm4wp_process_product(
1239
+ $product,
1240
+ array(
1241
+ 'productlink' => apply_filters( 'the_permalink', get_permalink(), 0 ),
1242
+ 'listname' => $gtm4wp_last_widget_title,
1243
+ 'listposition' => $gtm4wp_product_counter,
1244
+ ),
1245
+ 'widgetproduct'
1246
+ );
1247
 
1248
+ if ( ! isset( $eec_product_array['brand'] ) ) {
1249
+ $eec_product_array['brand'] = '';
1250
  }
1251
 
1252
  $productlink_with_data = sprintf(
1259
  esc_attr( $eec_product_array['listname'] ),
1260
  esc_attr( $eec_product_array['listposition'] ),
1261
  esc_attr( $eec_product_array['stocklevel'] ),
1262
+ esc_attr( $eec_product_array['brand'] )
1263
  );
1264
 
1265
  $gtm4wp_product_counter++;
1267
  $productitem = str_replace( 'href="', $productlink_with_data, $productitem );
1268
  }
1269
 
1270
+ /*
1271
+ $productitem is initialized as the template itself outputs a product item.
1272
+ Therefore I can not pass this to wp_kses() as it can include eventually any HTML.
1273
+ This filter function only adds additional attributes to the link element that points
1274
+ to a product detail page. Attribute values are escaped above.
1275
+ */
1276
+ echo $productitem; // phpcs:ignore
1277
  }
1278
 
1279
+ /**
1280
+ * Executed during widget_title.
1281
+ * This hook is used for any custom (classic) product list widget with custom title.
1282
+ * The widget title will be used to report a custom product list name into Google Analytics.
1283
+ * This function also resets the $gtm4wp_product_counter global variable to report the first
1284
+ * product in the widget in the proper position.
1285
+ *
1286
+ * @param string $widget_title The title of the widget being rendered.
1287
+ * @return string The updated widget title which is not changed by this function.
1288
+ */
1289
  function gtm4wp_widget_title_filter( $widget_title ) {
1290
  global $gtm4wp_product_counter, $gtm4wp_last_widget_title;
1291
 
1295
  return $widget_title;
1296
  }
1297
 
1298
+ /**
1299
+ * Executed during woocommerce_shortcode_before_recent_products_loop.
1300
+ * Sets the product list title for product list impression reporting.
1301
+ *
1302
+ * @return void
1303
+ */
1304
  function gtm4wp_before_recent_products_loop() {
1305
  global $woocommerce_loop;
1306
 
1307
  $woocommerce_loop['listtype'] = __( 'Recent Products', 'duracelltomi-google-tag-manager' );
1308
  }
1309
 
1310
+ /**
1311
+ * Executed during woocommerce_shortcode_before_sale_products_loop.
1312
+ * Sets the product list title for product list impression reporting.
1313
+ *
1314
+ * @return void
1315
+ */
1316
  function gtm4wp_before_sale_products_loop() {
1317
  global $woocommerce_loop;
1318
 
1319
  $woocommerce_loop['listtype'] = __( 'Sale Products', 'duracelltomi-google-tag-manager' );
1320
  }
1321
 
1322
+ /**
1323
+ * Executed during woocommerce_shortcode_before_best_selling_products_loop.
1324
+ * Sets the product list title for product list impression reporting.
1325
+ *
1326
+ * @return void
1327
+ */
1328
  function gtm4wp_before_best_selling_products_loop() {
1329
  global $woocommerce_loop;
1330
 
1331
  $woocommerce_loop['listtype'] = __( 'Best Selling Products', 'duracelltomi-google-tag-manager' );
1332
  }
1333
 
1334
+ /**
1335
+ * Executed during woocommerce_shortcode_before_top_rated_products_loop.
1336
+ * Sets the product list title for product list impression reporting.
1337
+ *
1338
+ * @return void
1339
+ */
1340
  function gtm4wp_before_top_rated_products_loop() {
1341
  global $woocommerce_loop;
1342
 
1343
  $woocommerce_loop['listtype'] = __( 'Top Rated Products', 'duracelltomi-google-tag-manager' );
1344
  }
1345
 
1346
+ /**
1347
+ * Executed during woocommerce_shortcode_before_featured_products_loop.
1348
+ * Sets the product list title for product list impression reporting.
1349
+ *
1350
+ * @return void
1351
+ */
1352
  function gtm4wp_before_featured_products_loop() {
1353
  global $woocommerce_loop;
1354
 
1355
  $woocommerce_loop['listtype'] = __( 'Featured Products', 'duracelltomi-google-tag-manager' );
1356
  }
1357
 
1358
+ /**
1359
+ * Executed during woocommerce_shortcode_before_related_products_loop.
1360
+ * Sets the product list title for product list impression reporting.
1361
+ *
1362
+ * @return void
1363
+ */
1364
  function gtm4wp_before_related_products_loop() {
1365
  global $woocommerce_loop;
1366
 
1367
  $woocommerce_loop['listtype'] = __( 'Related Products', 'duracelltomi-google-tag-manager' );
1368
  }
1369
 
1370
+ /**
1371
+ * Generates a <span> element that can be used as a hidden addition to the DOM to be able to report
1372
+ * product list impressions and clicks on list pages like product category or tag pages.
1373
+ *
1374
+ * @param WC_Product $product A WooCommerce product object.
1375
+ * @param string $listtype The name of the product list where the product is currently shown.
1376
+ * @param string $itemix The index of the product in the product list. The first product should have the index no. 1.
1377
+ * @param string $permalink The link where the click should land when a users clicks on this product element.
1378
+ * @return string A hidden <span> element that includes all product data needed for enhanced ecommerce reporting in product lists.
1379
+ */
1380
+ function gtm4wp_woocommerce_get_product_list_item_extra_tag( $product, $listtype, $itemix, $permalink ) {
1381
  global $wp_query, $gtm4wp_options;
1382
 
1383
  if ( ! isset( $product ) ) {
1384
  return;
1385
  }
1386
 
1387
+ if ( ! ( $product instanceof WC_Product ) ) {
1388
+ return false;
1389
+ }
1390
+
1391
  $product_id = $product->get_id();
1392
 
1393
  $product_cat = '';
1405
 
1406
  if ( is_search() ) {
1407
  $list_name = __( 'Search Results', 'duracelltomi-google-tag-manager' );
1408
+ } elseif ( '' !== $listtype ) {
1409
  $list_name = $listtype;
1410
  } else {
1411
  $list_name = __( 'General Product List', 'duracelltomi-google-tag-manager' );
1417
  $posts_per_page = 1;
1418
  }
1419
 
1420
+ $eec_product_array = gtm4wp_process_product(
1421
+ $product,
1422
+ array(
1423
+ 'productlink' => $permalink,
1424
+ 'listname' => $list_name,
1425
+ 'listposition' => (int) $itemix + ( $posts_per_page * ( $paged - 1 ) ),
1426
+ ),
1427
+ 'productlist'
1428
+ );
1429
 
1430
+ if ( ! isset( $eec_product_array['brand'] ) ) {
1431
+ $eec_product_array['brand'] = '';
1432
  }
1433
 
1434
  return sprintf(
1441
  esc_attr( $eec_product_array['listposition'] ),
1442
  esc_attr( $eec_product_array['listname'] ),
1443
  esc_attr( $eec_product_array['stocklevel'] ),
1444
+ esc_attr( $eec_product_array['brand'] )
1445
  );
1446
  }
1447
 
1448
+ /**
1449
+ * Executed during woocommerce_after_shop_loop_item.
1450
+ * Shows a hidden <span> element with product data to report enhanced ecommerce
1451
+ * product impression and click actions in product lists.
1452
+ *
1453
+ * @return void
1454
+ */
1455
  function gtm4wp_woocommerce_after_shop_loop_item() {
1456
  global $product, $woocommerce_loop;
1457
 
1458
+ $listtype = '';
1459
+ if ( isset( $woocommerce_loop['listtype'] ) && ( '' !== $woocommerce_loop['listtype'] ) ) {
1460
  $listtype = $woocommerce_loop['listtype'];
1461
  }
1462
 
1463
+ $itemix = '';
1464
+ if ( isset( $woocommerce_loop['loop'] ) && ( '' !== $woocommerce_loop['loop'] ) ) {
1465
  $itemix = $woocommerce_loop['loop'];
1466
  }
1467
 
1468
+ // no need to escape here as everthing is handled within the function call with esc_attr() and esc_url().
1469
+ echo gtm4wp_woocommerce_get_product_list_item_extra_tag( //phpcs:ignore
1470
+ $product,
1471
+ $listtype,
1472
+ $itemix,
1473
+ apply_filters(
1474
+ 'the_permalink',
1475
+ get_permalink(),
1476
+ 0
1477
+ )
1478
+ );
1479
  }
1480
 
1481
+ /**
1482
+ * Executed during woocommerce_cart_item_restored.
1483
+ * When the user restores the just removed cart item, this function stores the cart item key to
1484
+ * be able to generate an add_to_cart event after restoration completes.
1485
+ *
1486
+ * @param string $cart_item_key A unique cart item key.
1487
+ * @return void
1488
+ */
1489
  function gtm4wp_woocommerce_cart_item_restored( $cart_item_key ) {
1490
  if ( function_exists( 'WC' ) && WC()->session ) {
1491
  WC()->session->set( 'gtm4wp_product_readded_to_cart', $cart_item_key );
1492
  }
1493
  }
1494
 
1495
+ /**
1496
+ * Executes during wp_enqueue_scripts.
1497
+ * Loads classic/standard and enhanced ecommerce frontend JavaScript codes to track on site events and interactions.
1498
+ *
1499
+ * @return void
1500
+ */
1501
  function gtm4wp_woocommerce_enqueue_scripts() {
1502
  global $gtm4wp_options, $gtp4wp_plugin_url;
1503
 
1504
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
1505
+ $in_footer = (bool) apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC, false );
1506
  wp_enqueue_script( 'gtm4wp-woocommerce-classic', $gtp4wp_plugin_url . 'js/gtm4wp-woocommerce-classic.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
1507
  }
1508
 
1509
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
1510
+ $in_footer = (bool) apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC, false );
1511
  wp_enqueue_script( 'gtm4wp-woocommerce-enhanced', $gtp4wp_plugin_url . 'js/gtm4wp-woocommerce-enhanced.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
1512
  }
1513
  }
1514
 
1515
+ /**
1516
+ * Executed during wc_quick_view_before_single_product.
1517
+ * This function makes GTM4WP compatible with the WooCommerce Quick View plugin.
1518
+ * It allows GTM4WP to fire product detail action when quick view is opened.
1519
+ *
1520
+ * @return void
1521
+ */
1522
  function gtm4wp_wc_quick_view_before_single_product() {
1523
  global $gtm4wp_options, $gtm4wp_datalayer_name;
1524
 
1525
+ $data_layer = array(
1526
  'event' => 'gtm4wp.changeDetailViewEEC',
1527
  );
1528
 
1529
  if ( ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
1530
+ $postid = get_the_ID();
1531
+ $product = wc_get_product( $postid );
1532
 
1533
+ $eec_product_array = gtm4wp_process_product(
1534
+ $product,
1535
+ array(),
1536
+ 'productdetail'
1537
+ );
1538
 
1539
+ $data_layer['productRatingCounts'] = $product->get_rating_counts();
1540
+ $data_layer['productAverageRating'] = (float) $product->get_average_rating();
1541
+ $data_layer['productReviewCount'] = (int) $product->get_review_count();
1542
+ $data_layer['productType'] = $product->get_type();
1543
 
1544
+ switch ( $data_layer['productType'] ) {
1545
+ case 'variable':
1546
+ $data_layer['productIsVariable'] = 1;
1547
 
1548
+ $data_layer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array['id'] );
1549
+ $data_layer['ecomm_pagetype'] = 'product';
1550
+ $data_layer['ecomm_totalvalue'] = $eec_product_array['price'];
1551
 
1552
  break;
 
1553
 
1554
+ case 'grouped':
1555
+ $data_layer['productIsVariable'] = 0;
1556
 
1557
  break;
 
1558
 
1559
+ default:
1560
+ $data_layer['productIsVariable'] = 0;
1561
 
1562
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
1563
+ $data_layer['ecomm_prodid'] = gtm4wp_prefix_productid( $eec_product_array['id'] );
1564
+ $data_layer['ecomm_pagetype'] = 'product';
1565
+ $data_layer['ecomm_totalvalue'] = $eec_product_array['price'];
1566
  }
1567
 
1568
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
1569
+ $currency_code = get_woocommerce_currency();
1570
 
1571
+ $data_layer['ecommerce'] = array(
1572
+ 'currencyCode' => $currency_code,
1573
  'detail' => array(
1574
  'products' => array(
1575
+ $eec_product_array,
1576
+ ),
1577
+ ),
1578
  );
1579
  }
 
1580
  }
1581
  }
1582
 
1583
  echo '
1584
+ <span style="display: none;" id="gtm4wp_quickview_data" data-gtm4wp_datalayer="' . esc_attr( wp_json_encode( $data_layer ) ) . '"></span>';
1585
  }
1586
 
1587
+ /**
1588
+ * Executed during woocommerce_grouped_product_list_column_label.
1589
+ * Adds product list impression info into every product listed on a grouped product detail page to
1590
+ * track product list impression and click interactions for individual products in the grouped product.
1591
+ *
1592
+ * @param string $labelvalue Not used by this function, returns the value without modifying it.
1593
+ * @param WC_Product $product The WooCommerce product object being shown.
1594
+ * @return string The string that has been passed to the $labelvalue parameter without any modification.
1595
+ */
1596
  function gtm4wp_woocommerce_grouped_product_list_column_label( $labelvalue, $product ) {
1597
  global $gtm4wp_options, $gtm4wp_grouped_product_ix;
1598
 
1602
 
1603
  $list_name = __( 'Grouped Product Detail Page', 'duracelltomi-google-tag-manager' );
1604
 
1605
+ $eec_product_array = gtm4wp_process_product(
1606
+ $product,
1607
+ array(
1608
+ 'productlink' => $product->get_permalink(),
1609
+ 'listname' => $list_name,
1610
+ 'listposition' => $gtm4wp_grouped_product_ix,
1611
+ ),
1612
+ 'groupedproductlist'
1613
+ );
1614
 
1615
  $gtm4wp_grouped_product_ix++;
1616
 
1617
+ if ( ! isset( $eec_product_array['brand'] ) ) {
1618
+ $eec_product_array['brand'] = '';
1619
  }
1620
 
1621
  $labelvalue .=
1636
  return $labelvalue;
1637
  }
1638
 
1639
+ /**
1640
+ * Executed during woocommerce_blocks_product_grid_item_html.
1641
+ * Adds product list impression data into a product list that has been generated using the block templates
1642
+ * provided by WooCommerce. This allows proper tracking ot WooCommerce Blocks with product list
1643
+ * impression and click actions.
1644
+ *
1645
+ * @param string $content Product grid item HTML.
1646
+ * @param object $data Product data passed to the template.
1647
+ * @param WC_Product $product Product object.
1648
+ * @return string The product grid item HTML with added hidden <span> element for ecommerce tracking.
1649
+ */
1650
+ function gtm4wp_add_productdata_to_wc_block( $content, $data, $product ) {
1651
+ $product_data_tag = gtm4wp_woocommerce_get_product_list_item_extra_tag( $product, '', 0, $data->permalink );
1652
+
1653
+ return preg_replace( '/<li.+class=("|"[^"]+)wc-block-grid__product("|[^"]+")[^<]*>/i', '$0' . $product_data_tag, $content );
1654
  }
1655
 
1656
+ /**
1657
+ * Executed during wp_head with high priority.
1658
+ * Adds a JavaScript code that reloads the page in Safari if the user landed on the page using the back button of the browser.
1659
+ * Since the back button loads the previous state of the page instead of a new, clean state, some double tracking preventions
1660
+ * break the functionality of GTM4WP.
1661
+ *
1662
+ * @return void
1663
+ */
1664
  function gtm4wp_woocommerce_header_top() {
1665
  global $gtm4wp_options;
1666
 
1667
+ $has_html5_support = current_theme_supports( 'html5' );
1668
  $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
1669
 
1670
+ echo '<script' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . ">
1671
  const gtm4wp_is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
1672
  if ( gtm4wp_is_safari ) {
1673
  window.addEventListener('pageshow', function(event) {
1682
  </script>";
1683
  }
1684
 
1685
+ // do not add filter if someone enabled WooCommerce integration without an activated WooCommerce plugin.
1686
  if ( function_exists( 'WC' ) ) {
1687
  add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, 'gtm4wp_woocommerce_datalayer_filter_items' );
1688
 
1690
  add_action( 'woocommerce_after_shop_loop_item', 'gtm4wp_woocommerce_after_shop_loop_item' );
1691
  add_action( 'woocommerce_after_add_to_cart_button', 'gtm4wp_woocommerce_single_add_to_cart_tracking' );
1692
 
 
1693
  add_action( 'wp_enqueue_scripts', 'gtm4wp_woocommerce_enqueue_scripts' );
1694
+ add_filter( GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY, 'gtm4wp_woocommerce_addglobalvars' );
1695
 
1696
+ add_filter( 'woocommerce_blocks_product_grid_item_html', 'gtm4wp_add_productdata_to_wc_block', 10, 3 );
1697
 
1698
  add_action( 'woocommerce_thankyou', 'gtm4wp_woocommerce_thankyou' );
1699
 
integration/youtube.php CHANGED
@@ -1,7 +1,27 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  function gtm4wp_youtube( $return, $url, $data ) {
3
  $site_url = site_url();
4
- $site_url_parts = parse_url( $site_url );
5
 
6
  if ( false !== strpos( $return, 'youtube.com' ) ) {
7
  return str_replace( 'feature=oembed', 'feature=oembed&enablejsapi=1&origin=' . $site_url_parts['scheme'] . '://' . $site_url_parts['host'], $return );
@@ -13,6 +33,6 @@ function gtm4wp_youtube( $return, $url, $data ) {
13
  add_filter( 'oembed_result', 'gtm4wp_youtube', 10, 3 );
14
 
15
  if ( ! is_admin() ) {
16
- $in_footer = apply_filters( 'gtm4wp_youtube', true );
17
  wp_enqueue_script( 'gtm4wp-youtube', $gtp4wp_plugin_url . 'js/gtm4wp-youtube.js', array(), GTM4WP_VERSION, $in_footer );
18
  }
1
  <?php
2
+ /**
3
+ * Vimeo integration related codes.
4
+ * Enabled JS API in YouTube embed codes and loads the interaction tracking script of GTM4WP.
5
+ *
6
+ * @package GTM4WP
7
+ * @author Thomas Geiger
8
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
9
+ * @license GNU General Public License, version 3
10
+ */
11
+
12
+ /**
13
+ * Adds loading of the JS API of the YouTube player into the embed codes.
14
+ *
15
+ * @see https://developer.wordpress.org/reference/hooks/oembed_result/
16
+ *
17
+ * @param string|false $return The returned oEmbed HTML (false if unsafe).
18
+ * @param string $url URL of the content to be embedded.
19
+ * @param string|array $data Additional arguments for retrieving embed HTML. See wp_oembed_get() for accepted arguments. Default empty.
20
+ * @return string
21
+ */
22
  function gtm4wp_youtube( $return, $url, $data ) {
23
  $site_url = site_url();
24
+ $site_url_parts = wp_parse_url( $site_url );
25
 
26
  if ( false !== strpos( $return, 'youtube.com' ) ) {
27
  return str_replace( 'feature=oembed', 'feature=oembed&enablejsapi=1&origin=' . $site_url_parts['scheme'] . '://' . $site_url_parts['host'], $return );
33
  add_filter( 'oembed_result', 'gtm4wp_youtube', 10, 3 );
34
 
35
  if ( ! is_admin() ) {
36
+ $in_footer = (bool) apply_filters( 'gtm4wp_youtube', true );
37
  wp_enqueue_script( 'gtm4wp-youtube', $gtp4wp_plugin_url . 'js/gtm4wp-youtube.js', array(), GTM4WP_VERSION, $in_footer );
38
  }
js/admin-subtabs.js CHANGED
@@ -14,10 +14,6 @@ var adminsubtabs = {
14
  tabtext: gtm4wp.visitortabtitle,
15
  numitems: 7
16
  },
17
- "adwords": {
18
- tabtext: gtm4wp.adwordstabtitle,
19
- numitems: 1
20
- },
21
  "whichbrowser": {
22
  tabtext: gtm4wp.browsertabtitle,
23
  numitems: 3
14
  tabtext: gtm4wp.visitortabtitle,
15
  numitems: 7
16
  },
 
 
 
 
17
  "whichbrowser": {
18
  tabtext: gtm4wp.browsertabtitle,
19
  numitems: 3
public/frontend.php CHANGED
@@ -1,1026 +1,1255 @@
1
- <?php
2
- define( 'GTM4WP_WPFILTER_COMPILE_DATALAYER', 'gtm4wp_compile_datalayer' );
3
- define( 'GTM4WP_WPFILTER_AFTER_DATALAYER', 'gtm4wp_after_datalayer' );
4
- define( 'GTM4WP_WPFILTER_GETTHEGTMTAG', 'gtm4wp_get_the_gtm_tag' );
5
- define( 'GTM4WP_WPFILTER_ADDGLOBALVARS', 'gtm4wp_add_global_vars' );
6
-
7
- $GLOBALS['gtm4wp_container_code_written'] = false;
8
-
9
- // check for empty is needed to prevent error in WP CLI
10
- // bugfix by Patrick Holberg Hesselberg
11
- if ( empty( $GLOBALS['gtm4wp_options'] ) || ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_DATALAYER_NAME ] == '' ) ) {
12
- $GLOBALS['gtm4wp_datalayer_name'] = 'dataLayer';
13
- } else {
14
- $GLOBALS['gtm4wp_datalayer_name'] = $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_DATALAYER_NAME ];
15
- }
16
-
17
- // Setting Global Variable to Store JSON based Datalayer for Intergrations
18
- $GLOBALS['gtm4wp_datalayer_json'] = '';
19
- $GLOBALS['gtm4wp_datalayer_globalvars'] = '';
20
-
21
- // Moving include to top due to hierarchy of includes
22
- if ( isset( $GLOBALS['gtm4wp_options'] ) && ( '' != $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_AMPID ] ) ) {
23
- require_once dirname( __FILE__ ) . '/../integration/amp.php';
24
- }
25
- if ( ! function_exists( 'gtm4wp_amp_running' ) ) {
26
- function gtm4wp_amp_running() {
27
- return false;
28
- }
29
- }
30
-
31
- /**
32
- * Converts bool false to string false for JS
33
- *
34
- * @author Vincent Koc <https://github.com/koconder/>
35
- * @return mixed Returs object or string false
36
- */
37
- function gtm4wp_escjs_boolean( $obj ) {
38
- if ( empty( $obj ) || is_null( $obj ) || ! $obj ) {
39
- return 'false';
40
- } else {
41
- return $obj;
42
- }
43
- }
44
-
45
- /**
46
- * Original copyright:
47
- * By Grant Burton @ BURTONTECH.COM
48
- *
49
- * Code improved by Thomas Geiger
50
- */
51
- function gtm4wp_get_user_ip() {
52
- if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
53
- foreach ( explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ) as $ip ) {
54
- if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
55
- return $ip;
56
- }
57
- }
58
- }
59
-
60
- $possible_ip_variables = array(
61
- 'HTTP_CLIENT_IP',
62
- 'HTTP_X_FORWARDED',
63
- 'HTTP_X_CLUSTER_CLIENT_IP',
64
- 'HTTP_FORWARDED_FOR',
65
- 'HTTP_FORWARDED',
66
- 'REMOTE_ADDR'
67
- );
68
-
69
- foreach( $possible_ip_variables as $one_ip_variable ) {
70
- if (
71
- ! empty( $_SERVER[ $one_ip_variable ] )
72
- && ( filter_var( $_SERVER[ $one_ip_variable ], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false )
73
- ) {
74
- return $_SERVER[ $one_ip_variable ];
75
- }
76
- }
77
-
78
- return '';
79
- }
80
-
81
- if ( ! function_exists( 'getallheaders' ) ) {
82
- function getallheaders() {
83
- $headers = array();
84
- foreach ( $_SERVER as $name => $value ) {
85
- if ( substr( $name, 0, 5 ) == 'HTTP_' ) {
86
- $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
87
- }
88
- }
89
-
90
- return $headers;
91
- }
92
- }
93
-
94
- function gtm4wp_add_basic_datalayer_data( $dataLayer ) {
95
- global $wp_query, $gtm4wp_options, $gtm4wp_entity_ids;
96
-
97
- if ( $gtm4wp_options[ GTM4WP_OPTION_DONOTTRACK ] ) {
98
- if ( ! empty( $_SERVER['HTTP_DNT'] ) ) {
99
- $dataLayer['visitorDoNotTrack'] = (int) ( $_SERVER['HTTP_DNT'] );
100
- } else {
101
- $dataLayer['visitorDoNotTrack'] = 0;
102
- }
103
- }
104
-
105
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_SITEID ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_SITENAME ] ) {
106
- $dataLayer['siteID'] = 0;
107
- $dataLayer['siteName'] = '';
108
-
109
- if ( function_exists( 'get_blog_details' ) ) {
110
- $gtm4wp_blogdetails = get_blog_details();
111
-
112
- $dataLayer['siteID'] = $gtm4wp_blogdetails->blog_id;
113
- $dataLayer['siteName'] = $gtm4wp_blogdetails->blogname;
114
- }
115
- }
116
-
117
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_LOGGEDIN ] ) {
118
- if ( is_user_logged_in() ) {
119
- $dataLayer['visitorLoginState'] = 'logged-in';
120
- } else {
121
- $dataLayer['visitorLoginState'] = 'logged-out';
122
- }
123
- }
124
-
125
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERROLE ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USEREMAIL ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERREGDATE ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERNAME ] ) {
126
- $current_user = wp_get_current_user();
127
-
128
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERROLE ] ) {
129
- $dataLayer['visitorType'] = ( $current_user->ID == 0 ? 'visitor-logged-out' : implode(",", $current_user->roles) );
130
- }
131
-
132
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USEREMAIL ] ) {
133
- $dataLayer['visitorEmail'] = ( empty( $current_user->user_email ) ? '' : $current_user->user_email );
134
- $dataLayer['visitorEmailHash'] = ( empty( $current_user->user_email ) ? '' : hash( 'sha256', $current_user->user_email ) );
135
- }
136
-
137
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERREGDATE ] ) {
138
- $dataLayer['visitorRegistrationDate'] = ( empty( $current_user->user_registered ) ? '' : strtotime( $current_user->user_registered ) );
139
- }
140
-
141
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERNAME ] ) {
142
- $dataLayer['visitorUsername'] = ( empty( $current_user->user_login ) ? '' : $current_user->user_login );
143
- }
144
- }
145
-
146
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERID ] ) {
147
- $_gtm4wp_userid = get_current_user_id();
148
- if ( $_gtm4wp_userid > 0 ) {
149
- $dataLayer['visitorId'] = $_gtm4wp_userid;
150
- }
151
- }
152
-
153
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_VISITOR_IP ] ) {
154
- $dataLayer['visitorIP'] = esc_js( gtm4wp_get_user_ip() );
155
- }
156
-
157
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTITLE ] ) {
158
- $dataLayer['pageTitle'] = strip_tags( wp_title( '|', false, 'right' ) );
159
- }
160
-
161
- if ( is_singular() ) {
162
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
163
- $dataLayer['pagePostType'] = get_post_type();
164
- $dataLayer['pagePostType2'] = 'single-' . get_post_type();
165
- }
166
-
167
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_CATEGORIES ] ) {
168
- $_post_cats = get_the_category();
169
- if ( $_post_cats ) {
170
- $dataLayer['pageCategory'] = array();
171
- foreach ( $_post_cats as $_one_cat ) {
172
- $dataLayer['pageCategory'][] = $_one_cat->slug;
173
- }
174
- }
175
- }
176
-
177
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_TAGS ] ) {
178
- $_post_tags = get_the_tags();
179
- if ( $_post_tags ) {
180
- $dataLayer['pageAttributes'] = array();
181
- foreach ( $_post_tags as $_one_tag ) {
182
- $dataLayer['pageAttributes'][] = $_one_tag->slug;
183
- }
184
- }
185
- }
186
-
187
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) {
188
- $postuser = get_userdata( $GLOBALS['post']->post_author );
189
-
190
- if ( false !== $postuser ) {
191
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] ) {
192
- $dataLayer['pagePostAuthorID'] = $postuser->ID;
193
- }
194
-
195
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) {
196
- $dataLayer['pagePostAuthor'] = $postuser->display_name;
197
- }
198
- }
199
- }
200
-
201
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
202
- $dataLayer['pagePostDate'] = get_the_date();
203
- $dataLayer['pagePostDateYear'] = get_the_date( 'Y' );
204
- $dataLayer['pagePostDateMonth'] = get_the_date( 'm' );
205
- $dataLayer['pagePostDateDay'] = get_the_date( 'd' );
206
- $dataLayer['pagePostDateDayName'] = get_the_date( 'l' );
207
- $dataLayer['pagePostDateHour'] = get_the_date( 'H' );
208
- $dataLayer['pagePostDateMinute'] = get_the_date( 'i' );
209
- $dataLayer['pagePostDateIso'] = get_the_date( 'c' );
210
- $dataLayer['pagePostDateUnix'] = get_the_date( 'U' );
211
- }
212
-
213
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTERMLIST ] ) {
214
- $dataLayer["pagePostTerms"] = array();
215
- $object_taxonomies = get_object_taxonomies( get_post_type() );
216
- foreach( $object_taxonomies as $one_object_taxonomy ) {
217
- $post_taxonomy_values = get_the_terms( $GLOBALS[ "post" ]->ID, $one_object_taxonomy );
218
- if ( is_array( $post_taxonomy_values ) ) {
219
- $dataLayer["pagePostTerms"][$one_object_taxonomy] = array();
220
- foreach( $post_taxonomy_values as $one_taxonomy_value ) {
221
- $dataLayer["pagePostTerms"][$one_object_taxonomy][] = $one_taxonomy_value->name;
222
- }
223
- }
224
- }
225
- }
226
- }
227
-
228
- if ( is_archive() || is_post_type_archive() ) {
229
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
230
- $dataLayer['pagePostType'] = get_post_type();
231
-
232
- if ( is_category() ) {
233
- $dataLayer['pagePostType2'] = 'category-' . get_post_type();
234
- } elseif ( is_tag() ) {
235
- $dataLayer['pagePostType2'] = 'tag-' . get_post_type();
236
- } elseif ( is_tax() ) {
237
- $dataLayer['pagePostType2'] = 'tax-' . get_post_type();
238
- } elseif ( is_author() ) {
239
- $dataLayer['pagePostType2'] = 'author-' . get_post_type();
240
- } elseif ( is_year() ) {
241
- $dataLayer['pagePostType2'] = 'year-' . get_post_type();
242
-
243
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
244
- $dataLayer['pagePostDateYear'] = get_the_date( 'Y' );
245
- }
246
- } elseif ( is_month() ) {
247
- $dataLayer['pagePostType2'] = 'month-' . get_post_type();
248
-
249
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
250
- $dataLayer['pagePostDateYear'] = get_the_date( 'Y' );
251
- $dataLayer['pagePostDateMonth'] = get_the_date( 'm' );
252
- }
253
- } elseif ( is_day() ) {
254
- $dataLayer['pagePostType2'] = 'day-' . get_post_type();
255
-
256
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
257
- $dataLayer['pagePostDate'] = get_the_date();
258
- $dataLayer['pagePostDateYear'] = get_the_date( 'Y' );
259
- $dataLayer['pagePostDateMonth'] = get_the_date( 'm' );
260
- $dataLayer['pagePostDateDay'] = get_the_date( 'd' );
261
- }
262
- } elseif ( is_time() ) {
263
- $dataLayer['pagePostType2'] = 'time-' . get_post_type();
264
- } elseif ( is_date() ) {
265
- $dataLayer['pagePostType2'] = 'date-' . get_post_type();
266
-
267
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
268
- $dataLayer['pagePostDate'] = get_the_date();
269
- $dataLayer['pagePostDateYear'] = get_the_date( 'Y' );
270
- $dataLayer['pagePostDateMonth'] = get_the_date( 'm' );
271
- $dataLayer['pagePostDateDay'] = get_the_date( 'd' );
272
- }
273
- }
274
- }
275
-
276
- if ( ( is_tax() || is_category() ) && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_CATEGORIES ] ) {
277
- $_post_cats = get_the_category();
278
- $dataLayer['pageCategory'] = array();
279
- foreach ( $_post_cats as $_one_cat ) {
280
- $dataLayer['pageCategory'][] = $_one_cat->slug;
281
- }
282
- }
283
-
284
- if ( ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] ) && ( is_author() ) ) {
285
- global $authordata;
286
- $dataLayer['pagePostAuthorID'] = isset( $authordata->ID ) ? $authordata->ID : 0;
287
- }
288
-
289
- if ( ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) && ( is_author() ) ) {
290
- $dataLayer['pagePostAuthor'] = get_the_author();
291
- }
292
- }
293
-
294
- if ( is_search() ) {
295
- $dataLayer['pagePostType'] = 'search-results';
296
-
297
- $dataLayer['siteSearchTerm'] = get_search_query();
298
- $dataLayer['siteSearchFrom'] = '';
299
- if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
300
- $referer_url_parts = explode( '?', $_SERVER['HTTP_REFERER'] );
301
- if ( count( $referer_url_parts ) > 1 ) {
302
- $dataLayer['siteSearchFrom'] = $referer_url_parts[0] . '?' . rawurlencode( $referer_url_parts[1] );
303
- } else {
304
- $dataLayer['siteSearchFrom'] = $referer_url_parts[0];
305
- }
306
- }
307
- $dataLayer['siteSearchResults'] = $wp_query->post_count;
308
- }
309
-
310
- if ( is_front_page() && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
311
- $dataLayer['pagePostType'] = 'frontpage';
312
- }
313
-
314
- if ( ! is_front_page() && is_home() && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
315
- $dataLayer['pagePostType'] = 'bloghome';
316
- }
317
-
318
- if ( is_404() ) {
319
- $dataLayer['pagePostType'] = '404-error';
320
- }
321
-
322
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_BROWSERDATA ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_OSDATA ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_DEVICEDATA ] ) {
323
- spl_autoload_register(
324
- function( $class ) {
325
- $class_parts = explode( '\\', $class );
326
- if ( 'WhichBrowser' == $class_parts[0] ) {
327
- include dirname( __FILE__ ) . '/../integration/whichbrowser/' . str_replace( array( 'WhichBrowser', '\\' ), array( 'src', '/' ), $class ) . '.php';
328
- }
329
- }
330
- );
331
-
332
- require_once dirname( __FILE__ ) . '/../integration/whichbrowser/src/Parser.php';
333
-
334
- $gtp4wp_headers = getallheaders();
335
- if ( ( false === $gtp4wp_headers ) && isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
336
- $gtp4wp_headers = $_SERVER['HTTP_USER_AGENT'];
337
- }
338
- if ( false !== $gtp4wp_headers ) {
339
- $detected = new WhichBrowser\Parser( $gtp4wp_headers );
340
-
341
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_BROWSERDATA ] ) {
342
- $dataLayer['browserName'] = isset( $detected->browser->name ) ? $detected->browser->name : '';
343
- $dataLayer['browserVersion'] = isset( $detected->browser->version->value ) ? $detected->browser->version->value : '';
344
-
345
- $dataLayer['browserEngineName'] = isset( $detected->engine->name ) ? $detected->engine->name : '';
346
- $dataLayer['browserEngineVersion'] = isset( $detected->engine->version->value ) ? $detected->engine->version->value : '';
347
- }
348
-
349
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_OSDATA ] ) {
350
- $dataLayer['osName'] = isset( $detected->os->name ) ? $detected->os->name : '';
351
- $dataLayer['osVersion'] = isset( $detected->os->version->value ) ? $detected->os->version->value : '';
352
- }
353
-
354
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_DEVICEDATA ] ) {
355
- $dataLayer['deviceType'] = isset( $detected->device->type ) ? $detected->device->type : '';
356
- $dataLayer['deviceManufacturer'] = isset( $detected->device->manufacturer ) ? $detected->device->manufacturer : '';
357
- $dataLayer['deviceModel'] = isset( $detected->device->model ) ? $detected->device->model : '';
358
- }
359
- }
360
- }
361
-
362
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTCOUNT ] ) {
363
- $dataLayer['postCountOnPage'] = (int) $wp_query->post_count;
364
- $dataLayer['postCountTotal'] = (int) $wp_query->found_posts;
365
- }
366
-
367
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTID ] && is_singular() === true ) {
368
- $dataLayer['postID'] = (int) get_the_ID();
369
- }
370
-
371
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTFORMAT ] && is_singular() === true ) {
372
- $dataLayer['postFormat'] = get_post_format() ?: 'standard';
373
- }
374
-
375
- if ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] > 0 ) {
376
- $_gtmrestrictlistitems = array();
377
-
378
- // because of security reasons, we loop through each stored entity in the options and validate them
379
- // to make sure nobody has entered some 'funny' item manually
380
- $valid_entity_ids = array_merge(
381
- array_keys( $gtm4wp_entity_ids[ 'tags' ] ),
382
- array_keys( $gtm4wp_entity_ids[ 'triggers' ] ),
383
- array_keys( $gtm4wp_entity_ids[ 'variables' ] )
384
- );
385
- foreach( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] as $listed_entity ) {
386
- if ( in_array( $listed_entity, $valid_entity_ids ) ) {
387
- $_gtmrestrictlistitems[] = $listed_entity;
388
- }
389
- }
390
-
391
- $_gtmwhitelist = array();
392
- $_gtmblacklist = array();
393
- if ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] == 1 ) {
394
- $_gtmblacklist = array_merge( $_gtmblacklist, $_gtmrestrictlistitems );
395
- } else {
396
- $_gtmwhitelist = array_merge( $_gtmwhitelist, $_gtmrestrictlistitems );
397
- }
398
-
399
- $dataLayer['gtm.whitelist'] = $_gtmwhitelist;
400
- $dataLayer['gtm.blacklist'] = $_gtmblacklist;
401
- }
402
-
403
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEOCF ] && isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
404
- $dataLayer['geoCloudflareCountryCode'] = esc_js( $_SERVER['HTTP_CF_IPCOUNTRY'] );
405
- }
406
-
407
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
408
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] ) {
409
- $dataLayer['weatherCategory'] = __( '(no weather data available)', 'duracelltomi-google-tag-manager' );
410
- $dataLayer['weatherDescription'] = __( '(no weather data available)', 'duracelltomi-google-tag-manager' );
411
- $dataLayer['weatherTemp'] = 0;
412
- $dataLayer['weatherPressure'] = 0;
413
- $dataLayer['weatherWindSpeed'] = 0;
414
- $dataLayer['weatherWindDeg'] = 0;
415
- $dataLayer['weatherDataStatus'] = 'Initialized with empty data';
416
- }
417
-
418
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
419
- $dataLayer['geoCountryCode'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
420
- $dataLayer['geoCountryName'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
421
- $dataLayer['geoRegionCode'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
422
- $dataLayer['geoRegionName'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
423
- $dataLayer['geoCity'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
424
- $dataLayer['geoZipcode'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
425
- $dataLayer['geoLatitude'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
426
- $dataLayer['geoLongitude'] = __( '(no geo data available)', 'duracelltomi-google-tag-manager' );
427
- }
428
-
429
- $client_ip = gtm4wp_get_user_ip();
430
-
431
- if ( '' !== $client_ip ) {
432
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] ) {
433
- $weatherdata = get_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ) );
434
-
435
- if ( false !== $weatherdata ) {
436
- $dataLayer['weatherCategory'] = $weatherdata->weather[0]->main;
437
- $dataLayer['weatherDescription'] = $weatherdata->weather[0]->description;
438
- $dataLayer['weatherTemp'] = $weatherdata->main->temp;
439
- $dataLayer['weatherPressure'] = $weatherdata->main->pressure;
440
- $dataLayer['weatherWindSpeed'] = $weatherdata->wind->speed;
441
- $dataLayer['weatherWindDeg'] = ( isset($weatherdata->wind->deg) ? $weatherdata->wind->deg : '' );
442
- $dataLayer['weatherFullWeatherData'] = $weatherdata;
443
- $dataLayer['weatherDataStatus'] = 'Read from cache';
444
- } else {
445
- $dataLayer['weatherDataStatus'] = 'No weather data in cache (' . esc_attr( $client_ip ) . ')';
446
- }
447
- }
448
-
449
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
450
- $geodata = get_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ) );
451
-
452
- if ( false !== $geodata ) {
453
- $dataLayer['geoFullGeoData'] = $geodata;
454
-
455
- if ( isset( $geodata->latitude ) ) {
456
- $dataLayer['geoCountryCode'] = $geodata->country_code;
457
- $dataLayer['geoCountryName'] = $geodata->country_name;
458
- $dataLayer['geoRegionCode'] = $geodata->region_code;
459
- $dataLayer['geoRegionName'] = $geodata->region_name;
460
- $dataLayer['geoCity'] = $geodata->city;
461
- $dataLayer['geoZipcode'] = $geodata->zip;
462
- $dataLayer['geoLatitude'] = $geodata->latitude;
463
- $dataLayer['geoLongitude'] = $geodata->longitude;
464
- }
465
- }
466
- }
467
- }
468
- }
469
-
470
- return $dataLayer;
471
- }
472
-
473
- function gtm4wp_wp_loaded() {
474
- global $gtm4wp_options;
475
-
476
- if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
477
- $client_ip = gtm4wp_get_user_ip();
478
- $geodata = get_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ) );
479
-
480
- if ( false === $geodata ) {
481
- $gtm4wp_geodata = @wp_remote_get( sprintf( 'http://api.ipstack.com/%s?access_key=%s&format=1', urlencode( $client_ip ), $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEOAPI ] ) );
482
-
483
- if ( is_array( $gtm4wp_geodata ) && ( 200 == $gtm4wp_geodata['response']['code'] ) ) {
484
- $gtm4wp_geodata = @json_decode( $gtm4wp_geodata['body'] );
485
-
486
- if ( is_object( $gtm4wp_geodata ) ) {
487
- set_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ), $gtm4wp_geodata, 60 * 60 );
488
-
489
- $weatherdata = get_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ) );
490
- if ( false === $weatherdata && isset( $gtm4wp_geodata->latitude ) ) {
491
-
492
- $weatherdata = wp_remote_get( 'http://api.openweathermap.org/data/2.5/weather?appid=' . $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHEROWMAPI ] . '&lat=' . $gtm4wp_geodata->latitude . '&lon=' . $gtm4wp_geodata->longitude . '&units=' . ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] == 0 ? 'metric' : 'imperial' ) );
493
-
494
- if ( is_array( $weatherdata ) && ( 200 == $weatherdata['response']['code'] ) ) {
495
- $weatherdata = @json_decode( $weatherdata['body'] );
496
-
497
- if ( is_object( $weatherdata ) ) {
498
- set_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ), $weatherdata, 60 * 60 );
499
- setcookie( 'gtm4wp_last_weatherstatus', 'Weather data loaded.', 0, "/", "", false, true );
500
- } else {
501
- setcookie( 'gtm4wp_last_weatherstatus', 'Openweathermap.org did not return processable data: ' . var_export( $weatherdata, true ), 0, "/", "", false, true );
502
- }
503
- } else {
504
- if ( is_wp_error( $weatherdata ) ) {
505
- setcookie( 'gtm4wp_last_weatherstatus', 'Openweathermap.org request error: ' . $weatherdata->get_error_message(), 0, "/", "", false, true );
506
- } else {
507
- setcookie( 'gtm4wp_last_weatherstatus', 'Openweathermap.org returned status code: ' . $weatherdata['response']['code'], 0, "/", "", false, true );
508
- }
509
- }
510
- }
511
- } else {
512
- setcookie( 'gtm4wp_last_weatherstatus', 'ipstack.com did not return lat-lng data: ' . var_export( $gtm4wp_geodata, true ), 0, "/", "", false, true );
513
- }
514
- } else {
515
- if ( is_wp_error( $gtm4wp_geodata ) ) {
516
- setcookie( 'gtm4wp_last_weatherstatus', 'ipstack.com request error: ' . $gtm4wp_geodata->get_error_message(), 0, "/", "", false, true );
517
- } else {
518
- setcookie( 'gtm4wp_last_weatherstatus', 'ipstack.com returned status code: ' . $gtm4wp_geodata['response']['code'], 0, "/", "", false, true );
519
- }
520
- }
521
- }
522
- }
523
- }
524
-
525
- function gtm4wp_get_container_placement_string() {
526
- global $gtm4wp_options;
527
-
528
- switch($gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ]) {
529
- case GTM4WP_PLACEMENT_FOOTER: return "footer";
530
- case GTM4WP_PLACEMENT_BODYOPEN: return "manual";
531
- case GTM4WP_PLACEMENT_BODYOPEN_AUTO: return "automatic";
532
- case GTM4WP_PLACEMENT_OFF: return "off";
533
- default: return "unknown (" . $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] . ")";
534
- }
535
- }
536
-
537
- function gtm4wp_get_the_gtm_tag() {
538
- global $gtm4wp_options, $gtm4wp_datalayer_name, $gtm4wp_container_code_written;
539
-
540
- $has_html5_support = current_theme_supports( 'html5' );
541
- $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
542
-
543
- $_gtm_tag = '
544
- <!-- GTM Container placement set to ' . gtm4wp_get_container_placement_string() . ' -->
545
- <!-- Google Tag Manager (noscript) -->';
546
-
547
- if ( GTM4WP_PLACEMENT_OFF == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
548
- $gtm4wp_container_code_written = true;
549
-
550
- $_gtm_tag .= '
551
- <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
552
- console.warn && console.warn("[GTM4WP] Google Tag Manager container code placement set to OFF !!!");
553
- console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container must be loaded using custom coding !!!");
554
- </script>';
555
- }
556
-
557
- if ( ( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] != '' ) && ( ! $gtm4wp_container_code_written ) ) {
558
- $_gtm_codes = explode( ',', str_replace( array( ';', ' ' ), array( ',', '' ), $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) );
559
-
560
- if ( ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) ) {
561
- $_gtm_env = '&gtm_auth=' . $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] . '&gtm_preview=' . $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] . '&gtm_cookies_win=x';
562
- } else {
563
- $_gtm_env = '';
564
- }
565
-
566
- $_gtm_domain_name = 'www.googletagmanager.com';
567
- if ( $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] != '' ) {
568
- $_gtm_domain_name = $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ];
569
- }
570
-
571
- foreach ( $_gtm_codes as $one_gtm_code ) {
572
- $_gtm_tag .= '
573
- <noscript><iframe src="https://' . $_gtm_domain_name . '/ns.html?id=' . $one_gtm_code . $_gtm_env . '"
574
- height="0" width="0" style="display:none;visibility:hidden" aria-hidden="true"></iframe></noscript>';
575
- }
576
-
577
- $_gtm_tag .= '
578
- <!-- End Google Tag Manager (noscript) -->';
579
-
580
- $_gtm_tag = apply_filters( GTM4WP_WPFILTER_GETTHEGTMTAG, $_gtm_tag );
581
- $gtm4wp_container_code_written = true;
582
- }
583
-
584
- return $_gtm_tag;
585
- }
586
-
587
- function gtm4wp_the_gtm_tag() {
588
- echo gtm4wp_get_the_gtm_tag();
589
- }
590
-
591
- function gtm4wp_enqueue_scripts() {
592
- global $gtm4wp_options, $gtp4wp_plugin_url;
593
-
594
- if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WPCF7 ] ) {
595
- $in_footer = apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WPCF7, true );
596
- wp_enqueue_script( 'gtm4wp-contact-form-7-tracker', $gtp4wp_plugin_url . 'js/gtm4wp-contact-form-7-tracker.js', array(), GTM4WP_VERSION, $in_footer );
597
- }
598
-
599
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_FORMMOVE ] ) {
600
- $in_footer = apply_filters( 'gtm4wp_' . GTM4WP_OPTION_EVENTS_FORMMOVE, true );
601
- wp_enqueue_script( 'gtm4wp-form-move-tracker', $gtp4wp_plugin_url . 'js/gtm4wp-form-move-tracker.js', array(), GTM4WP_VERSION, $in_footer );
602
- }
603
-
604
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_YOUTUBE ] ) {
605
- require_once dirname( __FILE__ ) . '/../integration/youtube.php';
606
- }
607
-
608
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_VIMEO ] ) {
609
- require_once dirname( __FILE__ ) . '/../integration/vimeo.php';
610
- }
611
-
612
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_SOUNDCLOUD ] ) {
613
- require_once dirname( __FILE__ ) . '/../integration/soundcloud.php';
614
- }
615
-
616
- if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
617
- $in_footer = apply_filters( 'gtm4wp_' . GTM4WP_OPTION_SCROLLER_ENABLED, false );
618
- wp_enqueue_script( 'gtm4wp-scroll-tracking', $gtp4wp_plugin_url . 'js/analytics-talk-content-tracking.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
619
- }
620
- }
621
-
622
- function gtm4wp_wp_footer() {
623
- global $gtm4wp_options, $gtm4wp_datalayer_name;
624
-
625
- if ( GTM4WP_PLACEMENT_FOOTER == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
626
- gtm4wp_the_gtm_tag();
627
- }
628
-
629
- $has_html5_support = current_theme_supports( 'html5' );
630
- $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
631
-
632
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_NEWUSERREG ] ) {
633
- $user_logged_in = array_key_exists( "gtm4wp_user_logged_in", $_COOKIE ) ?
634
- filter_var( $_COOKIE[ "gtm4wp_user_logged_in" ], FILTER_VALIDATE_INT )
635
- : 0;
636
-
637
- if ( $user_logged_in ) {
638
- echo "
639
- <script" . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . ">
640
- if ( window." . $gtm4wp_datalayer_name . " ) {
641
- window." . $gtm4wp_datalayer_name . ".push({
642
- 'event': 'gtm4wp.userLoggedIn'
643
- });
644
- }
645
- </script>";
646
-
647
- unset( $_COOKIE[ "gtm4wp_user_logged_in" ] );
648
- }
649
- }
650
-
651
- if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_USERLOGIN ] ) {
652
- $user_registered = array_key_exists( "gtm4wp_user_registered", $_COOKIE ) ?
653
- filter_var( $_COOKIE[ "gtm4wp_user_registered" ], FILTER_VALIDATE_INT )
654
- : 0;
655
-
656
- if ( $user_registered ) {
657
- echo "
658
- <script" . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . ">
659
- if ( window." . $gtm4wp_datalayer_name . " ) {
660
- window." . $gtm4wp_datalayer_name . ".push({
661
- 'event': 'gtm4wp.userRegistered'
662
- });
663
- }
664
- </script>";
665
-
666
- unset( $_COOKIE[ "gtm4wp_user_registered" ] );
667
- }
668
- }
669
- }
670
-
671
- function gtm4wp_wp_body_open() {
672
- global $gtm4wp_options;
673
-
674
- if ( ( GTM4WP_PLACEMENT_BODYOPEN == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) || ( GTM4WP_PLACEMENT_BODYOPEN_AUTO == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) ) {
675
- gtm4wp_the_gtm_tag();
676
- }
677
- }
678
-
679
- /**
680
- * GTM4WP global JS variables WordPress filter
681
- *
682
- * @author Vincent Koc <https://github.com/koconder/>
683
- * @link https://github.com/duracelltomi/gtm4wp/issues/34
684
- * @return mixed returns the
685
- */
686
- function gtm4wp_add_global_vars( $vars, $return = false ) {
687
- if ( ! $return ) {
688
- if ( function_exists( $vars ) ) {
689
- $vars = $vars();
690
- }
691
- $GLOBALS['gtm4wp_datalayer_globalvars'] = $GLOBALS['gtm4wp_datalayer_globalvars'] . ' ' . $vars;
692
- }
693
- return $GLOBALS['gtm4wp_datalayer_globalvars'];
694
- }
695
-
696
- function gtm4wp_wp_header_top( $echo = true ) {
697
- global $gtm4wp_options, $gtm4wp_datalayer_name;
698
-
699
- $has_html5_support = current_theme_supports( 'html5' );
700
- $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
701
-
702
- $_gtm_top_content = '
703
- <!-- Google Tag Manager for WordPress by gtm4wp.com -->
704
- <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
705
- var gtm4wp_datalayer_name = "' . $gtm4wp_datalayer_name . '";
706
- var ' . $gtm4wp_datalayer_name . ' = ' . $gtm4wp_datalayer_name . ' || [];';
707
-
708
- // Load in the global variables from gtm4wp_add_global_vars / GTM4WP_WPFILTER_ADDGLOBALVARS filter
709
- $_gtm_top_content .= apply_filters( GTM4WP_WPFILTER_ADDGLOBALVARS, '' );
710
-
711
- if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
712
- $_gtm_top_content .= '
713
-
714
- var gtm4wp_scrollerscript_debugmode = ' . ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_DEBUGMODE ] ? 'true' : 'false' ) . ';
715
- var gtm4wp_scrollerscript_callbacktime = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_CALLBACKTIME ] . ';
716
- var gtm4wp_scrollerscript_readerlocation = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_DISTANCE ] . ';
717
- var gtm4wp_scrollerscript_contentelementid = "' . esc_js( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_CONTENTID ] ) . '";
718
- var gtm4wp_scrollerscript_scannertime = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_READERTIME ] . ';';
719
- }
720
-
721
- $_gtm_top_content .= '
722
- </script>
723
- <!-- End Google Tag Manager for WordPress by gtm4wp.com -->';
724
-
725
- if ( ! gtm4wp_amp_running() ) {
726
- if ( $echo ) {
727
- echo $_gtm_top_content;
728
- } else {
729
- return $_gtm_top_content;
730
- }
731
- }
732
- }
733
-
734
- function gtm4wp_wp_header_begin( $echo = true ) {
735
- global $gtm4wp_datalayer_name, $gtm4wp_datalayer_json, $gtm4wp_options, $woocommerce;
736
-
737
- $has_html5_support = current_theme_supports( 'html5' );
738
- $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
739
-
740
- $_gtm_header_content = '
741
- <!-- Google Tag Manager for WordPress by gtm4wp.com -->
742
- <!-- GTM Container placement set to ' . gtm4wp_get_container_placement_string() . ' -->
743
- <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>';
744
-
745
- if ( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] != '' ) {
746
- $gtm4wp_datalayer_data = array();
747
- $gtm4wp_datalayer_data = (array) apply_filters( GTM4WP_WPFILTER_COMPILE_DATALAYER, $gtm4wp_datalayer_data );
748
-
749
- if ( version_compare( PHP_VERSION, '5.4.0' ) >= 0 ) {
750
- $gtm4wp_datalayer_json = json_encode( $gtm4wp_datalayer_data, JSON_UNESCAPED_UNICODE );
751
- } else {
752
- $gtm4wp_datalayer_json = json_encode( $gtm4wp_datalayer_data );
753
- }
754
-
755
- $_gtm_header_content .= '
756
- var dataLayer_content = ' . $gtm4wp_datalayer_json . ';';
757
-
758
- // fire WooCommerce order double tracking protection only if WooCommerce is active and user is on the order received page
759
- if ( isset( $gtm4wp_options ) && ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] || $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) && isset( $woocommerce ) && is_order_received_page() ) {
760
-
761
- $_gtm_header_content .= '
762
- // if dataLayer contains ecommerce purchase data, check whether it has been already tracked
763
- if ( dataLayer_content.transactionId || ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.purchase ) ) {
764
- // read order id already tracked from cookies
765
- var gtm4wp_orderid_tracked = "";
766
-
767
- if ( !window.localStorage ) {
768
- var gtm4wp_cookie = "; " + document.cookie;
769
- var gtm4wp_cookie_parts = gtm4wp_cookie.split( "; gtm4wp_orderid_tracked=" );
770
- if ( gtm4wp_cookie_parts.length == 2 ) {
771
- gtm4wp_orderid_tracked = gtm4wp_cookie_parts.pop().split(";").shift();
772
- }
773
- } else {
774
- gtm4wp_orderid_tracked = window.localStorage.getItem( "gtm4wp_orderid_tracked" );
775
- }
776
-
777
- // check enhanced ecommerce
778
- if ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.purchase ) {
779
- if ( gtm4wp_orderid_tracked && ( dataLayer_content.ecommerce.purchase.actionField.id == gtm4wp_orderid_tracked ) ) {
780
- delete dataLayer_content.ecommerce.purchase;
781
- } else {
782
- gtm4wp_orderid_tracked = dataLayer_content.ecommerce.purchase.actionField.id;
783
- }
784
- }
785
-
786
- // check app+web ecommerce
787
- if ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.items ) {
788
- if ( gtm4wp_orderid_tracked && ( dataLayer_content.ecommerce.transaction_id == gtm4wp_orderid_tracked ) ) {
789
- delete dataLayer_content.ecommerce.affiliation;
790
- delete dataLayer_content.ecommerce.value;
791
- delete dataLayer_content.ecommerce.currency;
792
- delete dataLayer_content.ecommerce.tax;
793
- delete dataLayer_content.ecommerce.shipping;
794
- delete dataLayer_content.ecommerce.transaction_id;
795
-
796
- delete dataLayer_content.ecommerce.items;
797
- } else {
798
- gtm4wp_orderid_tracked = dataLayer_content.ecommerce.purchase.actionField.id;
799
- }
800
- }
801
-
802
- // check standard ecommerce
803
- if ( dataLayer_content.transactionId ) {
804
- if ( gtm4wp_orderid_tracked && ( dataLayer_content.transactionId == gtm4wp_orderid_tracked ) ) {
805
- delete dataLayer_content.transactionId;
806
- delete dataLayer_content.transactionDate;
807
- delete dataLayer_content.transactionType;
808
- delete dataLayer_content.transactionAffiliation;
809
- delete dataLayer_content.transactionTotal;
810
- delete dataLayer_content.transactionShipping;
811
- delete dataLayer_content.transactionTax;
812
- delete dataLayer_content.transactionPaymentType;
813
- delete dataLayer_content.transactionCurrency;
814
- delete dataLayer_content.transactionShippingMethod;
815
- delete dataLayer_content.transactionPromoCode;
816
- delete dataLayer_content.transactionProducts;
817
- } else {
818
- gtm4wp_orderid_tracked = dataLayer_content.transactionId;
819
- }
820
- }
821
-
822
- if ( gtm4wp_orderid_tracked ) {
823
- if ( !window.localStorage ) {
824
- var gtm4wp_orderid_cookie_expire = new Date();
825
- gtm4wp_orderid_cookie_expire.setTime( gtm4wp_orderid_cookie_expire.getTime() + (365*24*60*60*1000) );
826
- var gtm4wp_orderid_cookie_expires_part = "expires=" + gtm4wp_orderid_cookie_expire.toUTCString();
827
- document.cookie = "gtm4wp_orderid_tracked=" + gtm4wp_orderid_tracked + ";" + gtm4wp_orderid_cookie_expires_part + ";path=/";
828
- } else {
829
- window.localStorage.setItem( "gtm4wp_orderid_tracked", gtm4wp_orderid_tracked );
830
- }
831
- }
832
-
833
- }';
834
- }
835
-
836
- $_gtm_header_content .= '
837
- ' . $gtm4wp_datalayer_name . '.push( dataLayer_content );';
838
- }
839
-
840
- $_gtm_header_content .= '
841
- </script>';
842
-
843
- $_gtm_header_content .= apply_filters( GTM4WP_WPFILTER_AFTER_DATALAYER, '' );
844
-
845
- $output_container_code = true;
846
- if ( GTM4WP_PLACEMENT_OFF == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
847
- $output_container_code = false;
848
-
849
- $_gtm_header_content .= '
850
- <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
851
- console.warn && console.warn("[GTM4WP] Google Tag Manager container code placement set to OFF !!!");
852
- console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container must be loaded using custom coding !!!");
853
- </script>';
854
- }
855
-
856
- $disabled_roles = explode( ',', $gtm4wp_options[ GTM4WP_OPTION_NOGTMFORLOGGEDIN ] );
857
- if (count($disabled_roles) > 0 ) {
858
- $current_user = wp_get_current_user();
859
- foreach( $current_user->roles as $user_role ) {
860
- if ( in_array( $user_role, $disabled_roles ) ) {
861
- $output_container_code = false;
862
-
863
- $_gtm_header_content .= '
864
- <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
865
- console.warn && console.warn("[GTM4WP] Google Tag Manager container code was disabled for this user role: ' . $user_role . ' !!!");
866
- console.warn && console.warn("[GTM4WP] Logout or login with a user having a different user role!");
867
- console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container code is omitted !!!");
868
- </script>';
869
-
870
- break;
871
- }
872
- }
873
- }
874
-
875
- if ( ( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] != '' ) && $output_container_code ) {
876
- $_gtm_codes = explode( ',', str_replace( array( ';', ' ' ), array( ',', '' ), $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) );
877
- $add_cookiebot_ignore = $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
878
-
879
- $_gtm_tag = '';
880
- foreach ( $_gtm_codes as $one_gtm_code ) {
881
- if ( ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' != $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) ) {
882
- $_gtm_env = "+'&gtm_auth=" . $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] . '&gtm_preview=' . $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] . "&gtm_cookies_win=x'";
883
- } else {
884
- $_gtm_env = '';
885
- }
886
-
887
- $_gtm_domain_name = 'www.googletagmanager.com';
888
- if ( $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] != '' ) {
889
- $_gtm_domain_name = $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ];
890
- }
891
-
892
- $_gtm_tag .= '
893
- <script data-cfasync="false"' . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
894
- (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\'gtm.start\':
895
- new Date().getTime(),event:\'gtm.js\'});var f=d.getElementsByTagName(s)[0],
896
- j=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=
897
- \'//' . $_gtm_domain_name . '/gtm.\'' . '+\'js?id=\'+i+dl' . $_gtm_env . ';f.parentNode.insertBefore(j,f);
898
- })(window,document,\'script\',\'' . $gtm4wp_datalayer_name . '\',\'' . $one_gtm_code . '\');
899
- </script>';
900
- }
901
-
902
- $_gtm_tag .= '
903
- <!-- End Google Tag Manager -->';
904
-
905
- $_gtm_tag = apply_filters( GTM4WP_WPFILTER_GETTHEGTMTAG, $_gtm_tag );
906
- $_gtm_header_content .= $_gtm_tag;
907
- }
908
-
909
- $_gtm_header_content .= '
910
- <!-- End Google Tag Manager for WordPress by gtm4wp.com -->';
911
-
912
- if ( ! gtm4wp_amp_running() ) {
913
- if ( $echo ) {
914
- echo $_gtm_header_content;
915
- } else {
916
- return $_gtm_header_content;
917
- }
918
- }
919
- }
920
-
921
- function gtm4wp_wp_login() {
922
- setcookie(
923
- "gtm4wp_user_logged_in",
924
- "1",
925
- 0,
926
- "/",
927
- "",
928
- (false !== strstr( get_option( 'home' ), 'https:' )) && is_ssl(),
929
- true
930
- );
931
- }
932
-
933
- function gtm4wp_user_register() {
934
- setcookie(
935
- "gtm4wp_user_registered",
936
- "1",
937
- 0,
938
- "/",
939
- "",
940
- (false !== strstr( get_option( 'home' ), 'https:' )) && is_ssl(),
941
- true
942
- );
943
- }
944
-
945
- function gtm4wp_rocket_excluded_inline_js_content( $pattern ) {
946
- $pattern[] = 'dataLayer';
947
- $pattern[] = 'gtm4wp';
948
-
949
- return $pattern;
950
- }
951
-
952
- function gtm4wp_wp_init() {
953
- if ( array_key_exists( "gtm4wp_user_logged_in", $_COOKIE ) ) {
954
- setcookie(
955
- "gtm4wp_user_logged_in",
956
- "",
957
- -10000,
958
- "/",
959
- "",
960
- (false !== strstr( get_option( 'home' ), 'https:' )) && is_ssl(),
961
- true
962
- );
963
- }
964
-
965
- if ( array_key_exists( "gtm4wp_user_registered", $_COOKIE ) ) {
966
- setcookie(
967
- "gtm4wp_user_registered",
968
- "",
969
- -10000,
970
- "/",
971
- "",
972
- (false !== strstr( get_option( 'home' ), 'https:' )) && is_ssl(),
973
- true
974
- );
975
- }
976
- }
977
-
978
- add_action( 'wp_enqueue_scripts', 'gtm4wp_enqueue_scripts' );
979
- $gtm4wp_header_begin_prior = 10;
980
- if ( isset( $GLOBALS['gtm4wp_options'] ) && $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_LOADEARLY ] ) {
981
- $gtm4wp_header_begin_prior = 2;
982
- }
983
- add_action( 'wp_head', 'gtm4wp_wp_header_begin', $gtm4wp_header_begin_prior, 0 );
984
- add_action( 'wp_head', 'gtm4wp_wp_header_top', 1, 0 );
985
- add_action( 'wp_footer', 'gtm4wp_wp_footer' );
986
- add_action( 'wp_loaded', 'gtm4wp_wp_loaded' );
987
- add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, 'gtm4wp_add_basic_datalayer_data' );
988
- add_action( 'init', 'gtm4wp_wp_init' );
989
-
990
- // to be able to easily migrate from other Google Tag Manager plugins
991
- add_action( 'body_open', 'gtm4wp_wp_body_open' );
992
-
993
- // compatibility with existing themes that natively support code injection after opening body tag
994
- add_action( 'genesis_before', 'gtm4wp_wp_body_open' ); // Genisis theme
995
- add_action( 'generate_before_header', 'gtm4wp_wp_body_open', 0 ); // GeneratePress theme
996
- add_action( 'elementor/page_templates/canvas/before_content', 'gtm4wp_wp_body_open' ); // Elementor
997
- add_action( 'ct_before_builder', 'gtm4wp_wp_body_open', 0 ); // Oxygen Builder
998
- add_action( 'fl_before_builder', 'gtm4wp_wp_body_open', 0 ); // Beaver Builder Theme
999
-
1000
- // standard WP theme support for body open tags
1001
- add_action( 'wp_body_open', 'gtm4wp_wp_body_open' );
1002
-
1003
- add_filter( 'rocket_excluded_inline_js_content', 'gtm4wp_rocket_excluded_inline_js_content' ); // WP Rocket
1004
- if (
1005
- isset( $GLOBALS['gtm4wp_options'] )
1006
- && (
1007
- $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ]
1008
- || $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ]
1009
- )
1010
- && isset( $GLOBALS['woocommerce'] )
1011
- && version_compare( WC()->version, '3.2', '>=' ) // only activate WooCommerce integration for minimum supported WooCommerce version
1012
- ) {
1013
- require_once dirname( __FILE__ ) . '/../integration/woocommerce.php';
1014
- }
1015
-
1016
- if ( isset( $GLOBALS['gtm4wp_options'] ) && ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS ] != '' ) ) {
1017
- require_once dirname( __FILE__ ) . '/../integration/google-optimize.php';
1018
- }
1019
-
1020
- if ( isset( $GLOBALS['gtm4wp_options'] ) && ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_EVENTS_USERLOGIN ] ) ) {
1021
- add_action( 'wp_login', 'gtm4wp_wp_login' );
1022
- }
1023
-
1024
- if ( isset( $GLOBALS['gtm4wp_options'] ) && ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_EVENTS_NEWUSERREG ] ) ) {
1025
- add_action( 'user_register', 'gtm4wp_user_register' );
1026
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Load Google Tag Manager container code on frontend and output data layer variables.
4
+ *
5
+ * @package GTM4WP
6
+ * @author Thomas Geiger
7
+ * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
8
+ * @license GNU General Public License, version 3
9
+ */
10
+
11
+ /**
12
+ * Constant used by GTM4WP as a WordPress filter to allow itself 3rd party plugins
13
+ * to add their own data into the data layer.
14
+ */
15
+ define( 'GTM4WP_WPFILTER_COMPILE_DATALAYER', 'gtm4wp_compile_datalayer' );
16
+
17
+ /**
18
+ * Constant used by GTM4WP as a WordPress filter to allow itself and 3rd party plugins
19
+ * to add their own code after the first data layer push command.
20
+ *
21
+ * @deprecated 1.16 Use GTM4WP_WPACTION_AFTER_DATALAYER/gtm4wp_output_after_datalayer instead.
22
+ */
23
+ define( 'GTM4WP_WPFILTER_AFTER_DATALAYER', 'gtm4wp_after_datalayer' );
24
+
25
+ /**
26
+ * Constant used by GTM4WP as a WordPress action to allow itself and 3rd party plugins
27
+ * to add their own code after the first data layer push command.
28
+ */
29
+ define( 'GTM4WP_WPACTION_AFTER_DATALAYER', 'gtm4wp_output_after_datalayer' );
30
+
31
+ /**
32
+ * Constant that 3rd party plugins can use as a WordPress filter to alter the generated
33
+ * Google Tag Manager container code (both the regular and iframe/noscript code).
34
+ *
35
+ * @deprecated 1.16 Instead of manipulating the GTM container code through this filter,
36
+ * turn off the container code in plugin options and add your modified code manually.
37
+ */
38
+ define( 'GTM4WP_WPFILTER_GETTHEGTMTAG', 'gtm4wp_get_the_gtm_tag' );
39
+
40
+ // TODO: change this hook to use an associative array instead of full script content.
41
+ /**
42
+ * Constant that GTM4WP itself and 3rd party plugins can use to add JavaScript
43
+ * variable declarations above the first data layer push command.
44
+ *
45
+ * This hook was used by allowing any HTML/JS content to be added in hook functions.
46
+ * The new GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY / gtm4wp_add_global_vars_array is now
47
+ * required which is a safer way to add code.
48
+ *
49
+ * @deprecated 1.16
50
+ */
51
+ define( 'GTM4WP_WPFILTER_ADDGLOBALVARS', 'gtm4wp_add_global_vars' );
52
+
53
+ /**
54
+ * Constant that GTM4WP itself and 3rd party plugins can use to add JavaScript
55
+ * variable declarations above the first data layer push command.
56
+ */
57
+ define( 'GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY', 'gtm4wp_add_global_vars_array' );
58
+
59
+ /**
60
+ * Stores whether the container code has been outputed or not.
61
+ * Helps preventing double output of the GTM container code if the WordPress
62
+ * theme is using the gtm4wp_get_the_gtm_tag() function in a wrong way
63
+ * (next to the automatic code injection using wp_body_open)
64
+ *
65
+ * @var bool
66
+ */
67
+ $GLOBALS['gtm4wp_container_code_written'] = false;
68
+
69
+ /**
70
+ * Check for empty is needed to prevent error in WP CLI
71
+ * bugfix by Patrick Holberg Hesselberg
72
+ *
73
+ * @since 1.2
74
+ */
75
+ if ( empty( $GLOBALS['gtm4wp_options'] ) || ( '' === $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_DATALAYER_NAME ] ) ) {
76
+ $GLOBALS['gtm4wp_datalayer_name'] = 'dataLayer';
77
+ } else {
78
+ $GLOBALS['gtm4wp_datalayer_name'] = $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_DATALAYER_NAME ];
79
+ }
80
+
81
+ /**
82
+ * Stores the data layer content to give access to this data
83
+ * for the AMP integration.
84
+ *
85
+ * @var string
86
+ */
87
+ $GLOBALS['gtm4wp_datalayer_data'] = array();
88
+
89
+ /**
90
+ * Include AMP integration
91
+ */
92
+ if ( isset( $GLOBALS['gtm4wp_options'] ) && ( '' !== $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_AMPID ] ) ) {
93
+ require_once dirname( __FILE__ ) . '/../integration/amp.php';
94
+ }
95
+ if ( ! function_exists( 'gtm4wp_amp_running' ) ) {
96
+ /**
97
+ * Declare function if AMP is not runnig to allow gtm4wp_wp_header_top() to function properly.
98
+ *
99
+ * @return bool
100
+ */
101
+ function gtm4wp_amp_running() {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Original copyright:
108
+ * By Grant Burton @ BURTONTECH.COM
109
+ *
110
+ * Code improved by Thomas Geiger
111
+ */
112
+ function gtm4wp_get_user_ip() {
113
+ if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
114
+ foreach ( explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) as $ip ) {
115
+ if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
116
+ return $ip;
117
+ }
118
+ }
119
+ }
120
+
121
+ $possible_ip_variables = array(
122
+ 'HTTP_CLIENT_IP',
123
+ 'HTTP_X_FORWARDED',
124
+ 'HTTP_X_CLUSTER_CLIENT_IP',
125
+ 'HTTP_FORWARDED_FOR',
126
+ 'HTTP_FORWARDED',
127
+ 'REMOTE_ADDR',
128
+ );
129
+
130
+ foreach ( $possible_ip_variables as $one_ip_variable ) {
131
+ if ( ! empty( $_SERVER[ $one_ip_variable ] ) ) {
132
+ $ip = filter_var( wp_unslash( $_SERVER[ $one_ip_variable ] ), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE );
133
+ if ( false !== $ip ) {
134
+ return $ip;
135
+ }
136
+ }
137
+ }
138
+
139
+ return '';
140
+ }
141
+
142
+ if ( ! function_exists( 'getallheaders' ) ) {
143
+ /**
144
+ * Fetches all HTTP headers from the current request.
145
+ * Fallback function for nginx servers where this function is not available.
146
+ *
147
+ * @return array An associative array of all the HTTP headers in the current request, or false on failure.
148
+ */
149
+ function getallheaders() {
150
+ $headers = array();
151
+ foreach ( $_SERVER as $name => $value ) {
152
+ if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
153
+ $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
154
+ }
155
+ }
156
+
157
+ return $headers;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Populate main data layer outputted in the <head> before the GTM container snippet.
163
+ *
164
+ * @param array $data_layer Array of key-value pairs that will be outputed as a JSON object into the dataLayer global JavaScript variable.
165
+ * @return array
166
+ */
167
+ function gtm4wp_add_basic_datalayer_data( $data_layer ) {
168
+ global $wp_query, $gtm4wp_options, $gtm4wp_entity_ids;
169
+
170
+ if ( $gtm4wp_options[ GTM4WP_OPTION_DONOTTRACK ] ) {
171
+ if ( ! empty( $_SERVER['HTTP_DNT'] ) ) {
172
+ $data_layer['visitorDoNotTrack'] = (int) ( sanitize_text_field( wp_unslash( $_SERVER['HTTP_DNT'] ) ) );
173
+ } else {
174
+ $data_layer['visitorDoNotTrack'] = 0;
175
+ }
176
+ }
177
+
178
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_SITEID ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_SITENAME ] ) {
179
+ $data_layer['siteID'] = 0;
180
+ $data_layer['siteName'] = '';
181
+
182
+ if ( function_exists( 'get_blog_details' ) ) {
183
+ $gtm4wp_blogdetails = get_blog_details();
184
+
185
+ $data_layer['siteID'] = $gtm4wp_blogdetails->blog_id;
186
+ $data_layer['siteName'] = $gtm4wp_blogdetails->blogname;
187
+ }
188
+ }
189
+
190
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_LOGGEDIN ] ) {
191
+ if ( is_user_logged_in() ) {
192
+ $data_layer['visitorLoginState'] = 'logged-in';
193
+ } else {
194
+ $data_layer['visitorLoginState'] = 'logged-out';
195
+ }
196
+ }
197
+
198
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERROLE ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USEREMAIL ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERREGDATE ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERNAME ] ) {
199
+ $current_user = wp_get_current_user();
200
+
201
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERROLE ] ) {
202
+ $data_layer['visitorType'] = ( 0 === $current_user->ID ? 'visitor-logged-out' : implode( ',', $current_user->roles ) );
203
+ }
204
+
205
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USEREMAIL ] ) {
206
+ $data_layer['visitorEmail'] = ( empty( $current_user->user_email ) ? '' : $current_user->user_email );
207
+ $data_layer['visitorEmailHash'] = ( empty( $current_user->user_email ) ? '' : hash( 'sha256', $current_user->user_email ) );
208
+ }
209
+
210
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERREGDATE ] ) {
211
+ $data_layer['visitorRegistrationDate'] = ( empty( $current_user->user_registered ) ? '' : strtotime( $current_user->user_registered ) );
212
+ }
213
+
214
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERNAME ] ) {
215
+ $data_layer['visitorUsername'] = ( empty( $current_user->user_login ) ? '' : $current_user->user_login );
216
+ }
217
+ }
218
+
219
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_USERID ] ) {
220
+ $_gtm4wp_userid = get_current_user_id();
221
+ if ( $_gtm4wp_userid > 0 ) {
222
+ $data_layer['visitorId'] = $_gtm4wp_userid;
223
+ }
224
+ }
225
+
226
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_VISITOR_IP ] ) {
227
+ $data_layer['visitorIP'] = esc_js( gtm4wp_get_user_ip() );
228
+ }
229
+
230
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTITLE ] ) {
231
+ $data_layer['pageTitle'] = wp_strip_all_tags( wp_title( '|', false, 'right' ) );
232
+ }
233
+
234
+ if ( is_singular() ) {
235
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
236
+ $data_layer['pagePostType'] = get_post_type();
237
+ $data_layer['pagePostType2'] = 'single-' . get_post_type();
238
+ }
239
+
240
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_CATEGORIES ] ) {
241
+ $_post_cats = get_the_category();
242
+ if ( $_post_cats ) {
243
+ $data_layer['pageCategory'] = array();
244
+ foreach ( $_post_cats as $_one_cat ) {
245
+ $data_layer['pageCategory'][] = $_one_cat->slug;
246
+ }
247
+ }
248
+ }
249
+
250
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_TAGS ] ) {
251
+ $_post_tags = get_the_tags();
252
+ if ( $_post_tags ) {
253
+ $data_layer['pageAttributes'] = array();
254
+ foreach ( $_post_tags as $_one_tag ) {
255
+ $data_layer['pageAttributes'][] = $_one_tag->slug;
256
+ }
257
+ }
258
+ }
259
+
260
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) {
261
+ $postuser = get_userdata( $GLOBALS['post']->post_author );
262
+
263
+ if ( false !== $postuser ) {
264
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] ) {
265
+ $data_layer['pagePostAuthorID'] = $postuser->ID;
266
+ }
267
+
268
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) {
269
+ $data_layer['pagePostAuthor'] = $postuser->display_name;
270
+ }
271
+ }
272
+ }
273
+
274
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
275
+ $data_layer['pagePostDate'] = get_the_date();
276
+ $data_layer['pagePostDateYear'] = get_the_date( 'Y' );
277
+ $data_layer['pagePostDateMonth'] = get_the_date( 'm' );
278
+ $data_layer['pagePostDateDay'] = get_the_date( 'd' );
279
+ $data_layer['pagePostDateDayName'] = get_the_date( 'l' );
280
+ $data_layer['pagePostDateHour'] = get_the_date( 'H' );
281
+ $data_layer['pagePostDateMinute'] = get_the_date( 'i' );
282
+ $data_layer['pagePostDateIso'] = get_the_date( 'c' );
283
+ $data_layer['pagePostDateUnix'] = get_the_date( 'U' );
284
+ }
285
+
286
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTERMLIST ] ) {
287
+ $data_layer['pagePostTerms'] = array();
288
+
289
+ $object_taxonomies = get_object_taxonomies( get_post_type() );
290
+
291
+ foreach ( $object_taxonomies as $one_object_taxonomy ) {
292
+ $post_taxonomy_values = get_the_terms( $GLOBALS['post']->ID, $one_object_taxonomy );
293
+ if ( is_array( $post_taxonomy_values ) ) {
294
+ $data_layer['pagePostTerms'][ $one_object_taxonomy ] = array();
295
+ foreach ( $post_taxonomy_values as $one_taxonomy_value ) {
296
+ $data_layer['pagePostTerms'][ $one_object_taxonomy ][] = $one_taxonomy_value->name;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ if ( is_archive() || is_post_type_archive() ) {
304
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
305
+ $data_layer['pagePostType'] = get_post_type();
306
+
307
+ if ( is_category() ) {
308
+ $data_layer['pagePostType2'] = 'category-' . get_post_type();
309
+ } elseif ( is_tag() ) {
310
+ $data_layer['pagePostType2'] = 'tag-' . get_post_type();
311
+ } elseif ( is_tax() ) {
312
+ $data_layer['pagePostType2'] = 'tax-' . get_post_type();
313
+ } elseif ( is_author() ) {
314
+ $data_layer['pagePostType2'] = 'author-' . get_post_type();
315
+ } elseif ( is_year() ) {
316
+ $data_layer['pagePostType2'] = 'year-' . get_post_type();
317
+
318
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
319
+ $data_layer['pagePostDateYear'] = get_the_date( 'Y' );
320
+ }
321
+ } elseif ( is_month() ) {
322
+ $data_layer['pagePostType2'] = 'month-' . get_post_type();
323
+
324
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
325
+ $data_layer['pagePostDateYear'] = get_the_date( 'Y' );
326
+ $data_layer['pagePostDateMonth'] = get_the_date( 'm' );
327
+ }
328
+ } elseif ( is_day() ) {
329
+ $data_layer['pagePostType2'] = 'day-' . get_post_type();
330
+
331
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
332
+ $data_layer['pagePostDate'] = get_the_date();
333
+ $data_layer['pagePostDateYear'] = get_the_date( 'Y' );
334
+ $data_layer['pagePostDateMonth'] = get_the_date( 'm' );
335
+ $data_layer['pagePostDateDay'] = get_the_date( 'd' );
336
+ }
337
+ } elseif ( is_time() ) {
338
+ $data_layer['pagePostType2'] = 'time-' . get_post_type();
339
+ } elseif ( is_date() ) {
340
+ $data_layer['pagePostType2'] = 'date-' . get_post_type();
341
+
342
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTDATE ] ) {
343
+ $data_layer['pagePostDate'] = get_the_date();
344
+ $data_layer['pagePostDateYear'] = get_the_date( 'Y' );
345
+ $data_layer['pagePostDateMonth'] = get_the_date( 'm' );
346
+ $data_layer['pagePostDateDay'] = get_the_date( 'd' );
347
+ }
348
+ }
349
+ }
350
+
351
+ if ( ( is_tax() || is_category() ) && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_CATEGORIES ] ) {
352
+ $_post_cats = get_the_category();
353
+ $data_layer['pageCategory'] = array();
354
+ foreach ( $_post_cats as $_one_cat ) {
355
+ $data_layer['pageCategory'][] = $_one_cat->slug;
356
+ }
357
+ }
358
+
359
+ if ( ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHORID ] ) && ( is_author() ) ) {
360
+ global $authordata;
361
+ $data_layer['pagePostAuthorID'] = isset( $authordata->ID ) ? $authordata->ID : 0;
362
+ }
363
+
364
+ if ( ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_AUTHOR ] ) && ( is_author() ) ) {
365
+ $data_layer['pagePostAuthor'] = get_the_author();
366
+ }
367
+ }
368
+
369
+ if ( is_search() ) {
370
+ $data_layer['pagePostType'] = 'search-results';
371
+
372
+ $data_layer['siteSearchTerm'] = get_search_query();
373
+ $data_layer['siteSearchFrom'] = '';
374
+ if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
375
+ $referer_url_parts = explode( '?', esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) );
376
+ if ( count( $referer_url_parts ) > 1 ) {
377
+ $data_layer['siteSearchFrom'] = $referer_url_parts[0] . '?' . rawurlencode( $referer_url_parts[1] );
378
+ } else {
379
+ $data_layer['siteSearchFrom'] = $referer_url_parts[0];
380
+ }
381
+ }
382
+ $data_layer['siteSearchResults'] = $wp_query->post_count;
383
+ }
384
+
385
+ if ( is_front_page() && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
386
+ $data_layer['pagePostType'] = 'frontpage';
387
+ }
388
+
389
+ if ( ! is_front_page() && is_home() && $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTTYPE ] ) {
390
+ $data_layer['pagePostType'] = 'bloghome';
391
+ }
392
+
393
+ if ( is_404() ) {
394
+ $data_layer['pagePostType'] = '404-error';
395
+ }
396
+
397
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_BROWSERDATA ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_OSDATA ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_DEVICEDATA ] ) {
398
+ spl_autoload_register(
399
+ function( $class ) {
400
+ $class_parts = explode( '\\', $class );
401
+ if ( 'WhichBrowser' === $class_parts[0] ) {
402
+ include dirname( __FILE__ ) . '/../integration/whichbrowser/' . str_replace( array( 'WhichBrowser', '\\' ), array( 'src', '/' ), $class ) . '.php';
403
+ }
404
+ }
405
+ );
406
+
407
+ require_once dirname( __FILE__ ) . '/../integration/whichbrowser/src/Parser.php';
408
+
409
+ $gtp4wp_headers = getallheaders();
410
+ if ( ( false === $gtp4wp_headers ) && isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
411
+ $gtp4wp_headers = wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
412
+ }
413
+ if ( false !== $gtp4wp_headers ) {
414
+ $detected = new WhichBrowser\Parser( $gtp4wp_headers );
415
+
416
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_BROWSERDATA ] ) {
417
+ $data_layer['browserName'] = isset( $detected->browser->name ) ? $detected->browser->name : '';
418
+ $data_layer['browserVersion'] = isset( $detected->browser->version->value ) ? $detected->browser->version->value : '';
419
+
420
+ $data_layer['browserEngineName'] = isset( $detected->engine->name ) ? $detected->engine->name : '';
421
+ $data_layer['browserEngineVersion'] = isset( $detected->engine->version->value ) ? $detected->engine->version->value : '';
422
+ }
423
+
424
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_OSDATA ] ) {
425
+ $data_layer['osName'] = isset( $detected->os->name ) ? $detected->os->name : '';
426
+ $data_layer['osVersion'] = isset( $detected->os->version->value ) ? $detected->os->version->value : '';
427
+ }
428
+
429
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_DEVICEDATA ] ) {
430
+ $data_layer['deviceType'] = isset( $detected->device->type ) ? $detected->device->type : '';
431
+ $data_layer['deviceManufacturer'] = isset( $detected->device->manufacturer ) ? $detected->device->manufacturer : '';
432
+ $data_layer['deviceModel'] = isset( $detected->device->model ) ? $detected->device->model : '';
433
+ }
434
+ }
435
+ }
436
+
437
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTCOUNT ] ) {
438
+ $data_layer['postCountOnPage'] = (int) $wp_query->post_count;
439
+ $data_layer['postCountTotal'] = (int) $wp_query->found_posts;
440
+ }
441
+
442
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTID ] && is_singular() === true ) {
443
+ $data_layer['postID'] = (int) get_the_ID();
444
+ }
445
+
446
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_POSTFORMAT ] && is_singular() === true ) {
447
+ $data_layer['postFormat'] = get_post_format() ? '' : 'standard';
448
+ }
449
+
450
+ if ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] > 0 ) {
451
+ $_gtmrestrictlistitems = array();
452
+
453
+ // because of security reasons, we loop through each stored entity in the options and validate them
454
+ // to make sure nobody has entered some 'funny' item manually.
455
+ $valid_entity_ids = array_merge(
456
+ array_keys( $gtm4wp_entity_ids['tags'] ),
457
+ array_keys( $gtm4wp_entity_ids['triggers'] ),
458
+ array_keys( $gtm4wp_entity_ids['variables'] )
459
+ );
460
+ foreach ( $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_STATUS ] as $listed_entity ) {
461
+ if ( in_array( $listed_entity, $valid_entity_ids, true ) ) {
462
+ $_gtmrestrictlistitems[] = $listed_entity;
463
+ }
464
+ }
465
+
466
+ $_gtmwhitelist = array();
467
+ $_gtmblacklist = array();
468
+ if ( 1 === $gtm4wp_options[ GTM4WP_OPTION_BLACKLIST_ENABLE ] ) {
469
+ $_gtmblacklist = array_merge( $_gtmblacklist, $_gtmrestrictlistitems );
470
+ } else {
471
+ $_gtmwhitelist = array_merge( $_gtmwhitelist, $_gtmrestrictlistitems );
472
+ }
473
+
474
+ $data_layer['gtm.whitelist'] = $_gtmwhitelist;
475
+ $data_layer['gtm.blacklist'] = $_gtmblacklist;
476
+ }
477
+
478
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEOCF ] && isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
479
+ $data_layer['geoCloudflareCountryCode'] = esc_js( sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) );
480
+ }
481
+
482
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
483
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] ) {
484
+ $data_layer['weatherCategory'] = esc_js( __( '(no weather data available)', 'duracelltomi-google-tag-manager' ) );
485
+ $data_layer['weatherDescription'] = esc_js( __( '(no weather data available)', 'duracelltomi-google-tag-manager' ) );
486
+ $data_layer['weatherTemp'] = 0;
487
+ $data_layer['weatherPressure'] = 0;
488
+ $data_layer['weatherWindSpeed'] = 0;
489
+ $data_layer['weatherWindDeg'] = 0;
490
+ $data_layer['weatherDataStatus'] = 'Initialized with empty data';
491
+ }
492
+
493
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
494
+ $data_layer['geoCountryCode'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
495
+ $data_layer['geoCountryName'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
496
+ $data_layer['geoRegionCode'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
497
+ $data_layer['geoRegionName'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
498
+ $data_layer['geoCity'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
499
+ $data_layer['geoZipcode'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
500
+ $data_layer['geoLatitude'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
501
+ $data_layer['geoLongitude'] = esc_js( __( '(no geo data available)', 'duracelltomi-google-tag-manager' ) );
502
+ }
503
+
504
+ $client_ip = gtm4wp_get_user_ip();
505
+
506
+ if ( '' !== $client_ip ) {
507
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] ) {
508
+ $weatherdata = get_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ) );
509
+
510
+ if ( false !== $weatherdata ) {
511
+ $data_layer['weatherCategory'] = $weatherdata->weather[0]->main;
512
+ $data_layer['weatherDescription'] = $weatherdata->weather[0]->description;
513
+ $data_layer['weatherTemp'] = $weatherdata->main->temp;
514
+ $data_layer['weatherPressure'] = $weatherdata->main->pressure;
515
+ $data_layer['weatherWindSpeed'] = $weatherdata->wind->speed;
516
+ $data_layer['weatherWindDeg'] = ( isset( $weatherdata->wind->deg ) ? $weatherdata->wind->deg : '' );
517
+ $data_layer['weatherFullWeatherData'] = $weatherdata;
518
+ $data_layer['weatherDataStatus'] = 'Read from cache';
519
+ } else {
520
+ $data_layer['weatherDataStatus'] = 'No weather data in cache (' . esc_attr( $client_ip ) . ')';
521
+ }
522
+ }
523
+
524
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
525
+ $geodata = get_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ) );
526
+
527
+ if ( false !== $geodata ) {
528
+ $data_layer['geoFullGeoData'] = $geodata;
529
+
530
+ if ( isset( $geodata->latitude ) ) {
531
+ $data_layer['geoCountryCode'] = $geodata->country_code;
532
+ $data_layer['geoCountryName'] = $geodata->country_name;
533
+ $data_layer['geoRegionCode'] = $geodata->region_code;
534
+ $data_layer['geoRegionName'] = $geodata->region_name;
535
+ $data_layer['geoCity'] = $geodata->city;
536
+ $data_layer['geoZipcode'] = $geodata->zip;
537
+ $data_layer['geoLatitude'] = $geodata->latitude;
538
+ $data_layer['geoLongitude'] = $geodata->longitude;
539
+ }
540
+ }
541
+ }
542
+ }
543
+ }
544
+
545
+ return $data_layer;
546
+ }
547
+
548
+ /**
549
+ * Function executed during wp_loaded.
550
+ * Loads geo and weather data to be processed later.
551
+ *
552
+ * @see https://developer.wordpress.org/reference/hooks/wp_loaded/
553
+ *
554
+ * @return void
555
+ */
556
+ function gtm4wp_wp_loaded() {
557
+ global $gtm4wp_options;
558
+
559
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHER ] || $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEO ] ) {
560
+ $client_ip = gtm4wp_get_user_ip();
561
+ $geodata = get_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ) );
562
+
563
+ if ( false === $geodata ) {
564
+ $gtm4wp_geodata = wp_remote_get(
565
+ esc_url(
566
+ 'http://api.ipstack.com/' .
567
+ rawurlencode( $client_ip ) .
568
+ '?access_key=' . $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_MISCGEOAPI ] .
569
+ '&format=1'
570
+ )
571
+ );
572
+
573
+ if ( is_array( $gtm4wp_geodata ) && ( 200 === $gtm4wp_geodata['response']['code'] ) ) {
574
+ $gtm4wp_geodata = json_decode( $gtm4wp_geodata['body'] );
575
+
576
+ if ( is_object( $gtm4wp_geodata ) ) {
577
+ set_transient( 'gtm4wp-geodata-' . esc_attr( $client_ip ), $gtm4wp_geodata, 60 * 60 );
578
+
579
+ $weatherdata = get_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ) );
580
+ if ( false === $weatherdata && isset( $gtm4wp_geodata->latitude ) ) {
581
+
582
+ $weatherdata = wp_remote_get(
583
+ esc_url(
584
+ 'http://api.openweathermap.org/data/2.5/weather?appid=' .
585
+ $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHEROWMAPI ] .
586
+ '&lat=' . $gtm4wp_geodata->latitude .
587
+ '&lon=' . $gtm4wp_geodata->longitude .
588
+ '&units=' . ( 0 === $gtm4wp_options[ GTM4WP_OPTION_INCLUDE_WEATHERUNITS ] ? 'metric' : 'imperial' )
589
+ )
590
+ );
591
+
592
+ if ( is_array( $weatherdata ) && ( 200 === $weatherdata['response']['code'] ) ) {
593
+ $weatherdata = json_decode( $weatherdata['body'] );
594
+
595
+ if ( is_object( $weatherdata ) ) {
596
+ set_transient( 'gtm4wp-weatherdata-' . esc_attr( $client_ip ), $weatherdata, 60 * 60 );
597
+ setcookie( 'gtm4wp_last_weatherstatus', 'Weather data loaded.', 0, '/', '', false, true );
598
+ }
599
+ } else {
600
+ if ( is_wp_error( $weatherdata ) ) {
601
+ setcookie( 'gtm4wp_last_weatherstatus', 'Openweathermap.org request error: ' . $weatherdata->get_error_message(), 0, '/', '', false, true );
602
+ } else {
603
+ setcookie( 'gtm4wp_last_weatherstatus', 'Openweathermap.org returned status code: ' . $weatherdata['response']['code'], 0, '/', '', false, true );
604
+ }
605
+ }
606
+ }
607
+ }
608
+ } else {
609
+ if ( is_wp_error( $gtm4wp_geodata ) ) {
610
+ setcookie( 'gtm4wp_last_weatherstatus', 'ipstack.com request error: ' . $gtm4wp_geodata->get_error_message(), 0, '/', '', false, true );
611
+ } else {
612
+ setcookie( 'gtm4wp_last_weatherstatus', 'ipstack.com returned status code: ' . $gtm4wp_geodata['response']['code'], 0, '/', '', false, true );
613
+ }
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Helper function to translate GTM container code placement value into readable string.
621
+ *
622
+ * @return string Readable form of a GTM container code placement option.
623
+ */
624
+ function gtm4wp_get_container_placement_string() {
625
+ global $gtm4wp_options;
626
+
627
+ switch ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
628
+ case GTM4WP_PLACEMENT_FOOTER:
629
+ return 'footer';
630
+
631
+ case GTM4WP_PLACEMENT_BODYOPEN:
632
+ return 'manual';
633
+
634
+ case GTM4WP_PLACEMENT_BODYOPEN_AUTO:
635
+ return 'automatic';
636
+
637
+ case GTM4WP_PLACEMENT_OFF:
638
+ return 'off';
639
+
640
+ default:
641
+ return 'unknown (' . $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] . ')';
642
+ }
643
+ }
644
+
645
+ /**
646
+ * Returns a HTML code that includes the noscript/iframe part of the Google Tag Manager container.
647
+ * Can be used to manually place the snippet next to the opening body tag if the installed template
648
+ * does not support the wp_body_open hook.
649
+ *
650
+ * @see https://developer.wordpress.org/reference/functions/wp_body_open/
651
+ *
652
+ * @return string The HTML code that includes the noscript/iframe part of the GTM container code.
653
+ */
654
+ function gtm4wp_get_the_gtm_tag() {
655
+ global $gtm4wp_options, $gtm4wp_container_code_written;
656
+
657
+ $has_html5_support = current_theme_supports( 'html5' );
658
+ $add_cookiebot_ignore = (bool) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
659
+
660
+ $_gtm_tag = '
661
+ <!-- GTM Container placement set to ' . esc_html( gtm4wp_get_container_placement_string() ) . ' -->
662
+ <!-- Google Tag Manager (noscript) -->';
663
+
664
+ if ( GTM4WP_PLACEMENT_OFF === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
665
+ $gtm4wp_container_code_written = true;
666
+
667
+ $_gtm_tag .= '
668
+ <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
669
+ console.warn && console.warn("[GTM4WP] Google Tag Manager container code placement set to OFF !!!");
670
+ console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container must be loaded using custom coding !!!");
671
+ </script>';
672
+ }
673
+
674
+ if ( ( '' !== $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) && ( ! $gtm4wp_container_code_written ) ) {
675
+ $_gtm_codes = explode( ',', str_replace( array( ';', ' ' ), array( ',', '' ), $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) );
676
+
677
+ if ( ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) ) {
678
+ $_gtm_env = '&gtm_auth=' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) . '&gtm_preview=' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) . '&gtm_cookies_win=x';
679
+ } else {
680
+ $_gtm_env = '';
681
+ }
682
+
683
+ $_gtm_domain_name = 'www.googletagmanager.com';
684
+ if (
685
+ ( '' !== $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] ) &&
686
+ ( preg_match( '/^[a-z0-9\.:]+$/', strtolower( $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] ) ) )
687
+ ) {
688
+ $_gtm_domain_name = $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ];
689
+ }
690
+
691
+ foreach ( $_gtm_codes as $one_gtm_id ) {
692
+ $gtm_id = preg_match( '/^GTM-[A-Z0-9]+$/', $one_gtm_id );
693
+
694
+ $_gtm_tag .= '
695
+ <noscript><iframe src="https://' . $_gtm_domain_name . '/ns.html?id=' . $gtm_id . $_gtm_env . '"
696
+ height="0" width="0" style="display:none;visibility:hidden" aria-hidden="true"></iframe></noscript>';
697
+ }
698
+
699
+ $_gtm_tag .= '
700
+ <!-- End Google Tag Manager (noscript) -->';
701
+
702
+ $_gtm_tag = apply_filters( GTM4WP_WPFILTER_GETTHEGTMTAG, $_gtm_tag );
703
+ $gtm4wp_container_code_written = true;
704
+ }
705
+
706
+ return $_gtm_tag;
707
+ }
708
+
709
+ add_filter(
710
+ 'safe_style_css',
711
+ function( $styles ) {
712
+ $styles[] = 'display';
713
+ $styles[] = 'visibility';
714
+ return $styles;
715
+ }
716
+ );
717
+
718
+ /**
719
+ * Outputs a HTML code that includes the noscript/iframe part of the Google Tag Manager container.
720
+ * Can be used to manually place the snippet next to the opening body tag if the installed template
721
+ * does not support the wp_body_open hook.
722
+ *
723
+ * @see https://developer.wordpress.org/reference/functions/wp_body_open/
724
+ *
725
+ * @return void
726
+ */
727
+ function gtm4wp_the_gtm_tag() {
728
+ echo wp_kses(
729
+ gtm4wp_get_the_gtm_tag(),
730
+ array(
731
+ 'noscript' => array(),
732
+ 'iframe' => array(
733
+ 'src' => array(),
734
+ 'height' => array(),
735
+ 'width' => array(),
736
+ 'style' => array(),
737
+ 'aria-hidden' => array(),
738
+ ),
739
+ )
740
+ );
741
+ }
742
+
743
+ /**
744
+ * Function executed during wp_enqueue_scripts.
745
+ * Loads JavaScript files based on plugin options that are turned on to prevent bloating the frontend.
746
+ *
747
+ * @see https://developer.wordpress.org/reference/hooks/wp_enqueue_scripts/
748
+ *
749
+ * @return void
750
+ */
751
+ function gtm4wp_enqueue_scripts() {
752
+ global $gtm4wp_options, $gtp4wp_plugin_url;
753
+
754
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WPCF7 ] ) {
755
+ $in_footer = (bool) apply_filters( 'gtm4wp_' . GTM4WP_OPTION_INTEGRATE_WPCF7, true );
756
+ wp_enqueue_script( 'gtm4wp-contact-form-7-tracker', $gtp4wp_plugin_url . 'js/gtm4wp-contact-form-7-tracker.js', array(), GTM4WP_VERSION, $in_footer );
757
+ }
758
+
759
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_FORMMOVE ] ) {
760
+ $in_footer = (bool) apply_filters( 'gtm4wp_' . GTM4WP_OPTION_EVENTS_FORMMOVE, true );
761
+ wp_enqueue_script( 'gtm4wp-form-move-tracker', $gtp4wp_plugin_url . 'js/gtm4wp-form-move-tracker.js', array(), GTM4WP_VERSION, $in_footer );
762
+ }
763
+
764
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_YOUTUBE ] ) {
765
+ require_once dirname( __FILE__ ) . '/../integration/youtube.php';
766
+ }
767
+
768
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_VIMEO ] ) {
769
+ require_once dirname( __FILE__ ) . '/../integration/vimeo.php';
770
+ }
771
+
772
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_SOUNDCLOUD ] ) {
773
+ require_once dirname( __FILE__ ) . '/../integration/soundcloud.php';
774
+ }
775
+
776
+ if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
777
+ $in_footer = (bool) apply_filters( 'gtm4wp_' . GTM4WP_OPTION_SCROLLER_ENABLED, false );
778
+ wp_enqueue_script( 'gtm4wp-scroll-tracking', $gtp4wp_plugin_url . 'js/analytics-talk-content-tracking.js', array( 'jquery' ), GTM4WP_VERSION, $in_footer );
779
+ }
780
+ }
781
+
782
+ /**
783
+ * Function executed during wp_footer.
784
+ * Inserts the GTM noscript/iframe code if code placement set to Footer.
785
+ * Outputs scripts that fire GTM tags on new user registration and login.
786
+ *
787
+ * @see https://developer.wordpress.org/reference/hooks/wp_footer/
788
+ *
789
+ * @return void
790
+ */
791
+ function gtm4wp_wp_footer() {
792
+ global $gtm4wp_options, $gtm4wp_datalayer_name;
793
+
794
+ if ( GTM4WP_PLACEMENT_FOOTER === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
795
+ gtm4wp_the_gtm_tag();
796
+ }
797
+
798
+ $has_html5_support = current_theme_supports( 'html5' );
799
+ $add_cookiebot_ignore = (bool) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
800
+
801
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_NEWUSERREG ] ) {
802
+ $user_logged_in = array_key_exists( 'gtm4wp_user_logged_in', $_COOKIE ) ?
803
+ filter_var( wp_unslash( $_COOKIE['gtm4wp_user_logged_in'] ), FILTER_VALIDATE_INT )
804
+ : 0;
805
+
806
+ if ( $user_logged_in ) {
807
+ echo '
808
+ <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
809
+ if ( window.' . esc_js( $gtm4wp_datalayer_name ) . ' ) {
810
+ window.' . esc_js( $gtm4wp_datalayer_name ) . ".push({
811
+ 'event': 'gtm4wp.userLoggedIn'
812
+ });
813
+ }
814
+ </script>";
815
+
816
+ unset( $_COOKIE['gtm4wp_user_logged_in'] );
817
+ }
818
+ }
819
+
820
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_USERLOGIN ] ) {
821
+ $user_registered = array_key_exists( 'gtm4wp_user_registered', $_COOKIE ) ?
822
+ filter_var( wp_unslash( $_COOKIE['gtm4wp_user_registered'] ), FILTER_VALIDATE_INT )
823
+ : 0;
824
+
825
+ if ( $user_registered ) {
826
+ echo '
827
+ <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
828
+ if ( window.' . esc_js( $gtm4wp_datalayer_name ) . ' ) {
829
+ window.' . esc_js( $gtm4wp_datalayer_name ) . ".push({
830
+ 'event': 'gtm4wp.userRegistered'
831
+ });
832
+ }
833
+ </script>";
834
+
835
+ unset( $_COOKIE['gtm4wp_user_registered'] );
836
+ }
837
+ }
838
+ }
839
+
840
+ /**
841
+ * Function executed during wp_body_open, genesis_before, generate_before_header,
842
+ * elementor/page_templates/canvas/before_content, ct_before_builder or fl_before_builder.
843
+ *
844
+ * Outputs the noscript/iframe container code.
845
+ *
846
+ * @see https://developer.wordpress.org/reference/functions/wp_body_open/
847
+ * @see https://docs.generatepress.com/article/generate_before_header/
848
+ *
849
+ * @return void
850
+ */
851
+ function gtm4wp_wp_body_open() {
852
+ global $gtm4wp_options;
853
+
854
+ if ( ( GTM4WP_PLACEMENT_BODYOPEN === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) || ( GTM4WP_PLACEMENT_BODYOPEN_AUTO === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) ) {
855
+ gtm4wp_the_gtm_tag();
856
+ }
857
+ }
858
+
859
+ /**
860
+ * Function executed during wp_head with high priority.
861
+ * Outputs some global JavaScript variables that needs to be accessable by other parts of the plugin.
862
+ *
863
+ * @see https://developer.wordpress.org/reference/functions/wp_head/
864
+ *
865
+ * @param boolean $echo If set to true and AMP is currently generating the page content, the HTML is outputed immediately.
866
+ * @return string|void Returns the HTML if the $echo parameter is set to false or when not AMP page generation is running.
867
+ */
868
+ function gtm4wp_wp_header_top( $echo = true ) {
869
+ global $gtm4wp_options, $gtm4wp_datalayer_name;
870
+
871
+ $has_html5_support = current_theme_supports( 'html5' );
872
+ $add_cookiebot_ignore = (bool) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
873
+
874
+ // the data layer initialization has to use 'var' instead of 'let' since 'let' can break related browser extension and 3rd party script.
875
+ $_gtm_top_content = '
876
+ <!-- Google Tag Manager for WordPress by gtm4wp.com -->
877
+ <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
878
+ var gtm4wp_datalayer_name = "' . esc_js( $gtm4wp_datalayer_name ) . '";
879
+ var ' . esc_js( $gtm4wp_datalayer_name ) . ' = ' . esc_js( $gtm4wp_datalayer_name ) . ' || [];';
880
+
881
+ // Load in the global variables from gtm4wp_add_global_vars_array / GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY filter.
882
+ $added_global_js_vars = (array) apply_filters( GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY, array() );
883
+ foreach ( $added_global_js_vars as $js_var_name => $js_var_value ) {
884
+ if ( is_string( $js_var_value ) ) {
885
+ $js_var_value = "'" . esc_js( $js_var_value ) . "'";
886
+ }
887
+
888
+ if ( is_bool( $js_var_value ) || empty( $js_var_value ) ) {
889
+ $js_var_value = $js_var_value ? 'true' : 'false';
890
+ }
891
+
892
+ if ( is_array( $js_var_value ) ) {
893
+ $js_var_value = wp_json_encode( $js_var_value );
894
+ }
895
+
896
+ if ( is_null( $js_var_value ) ) {
897
+ $js_var_value = 'null';
898
+ }
899
+
900
+ $_gtm_top_content .= '
901
+ const ' . esc_js( $js_var_name ) . ' = ' . $js_var_value . ';';
902
+ }
903
+
904
+ if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
905
+ $_gtm_top_content .= '
906
+
907
+ const gtm4wp_scrollerscript_debugmode = ' . ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_DEBUGMODE ] ? 'true' : 'false' ) . ';
908
+ const gtm4wp_scrollerscript_callbacktime = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_CALLBACKTIME ] . ';
909
+ const gtm4wp_scrollerscript_readerlocation = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_DISTANCE ] . ';
910
+ const gtm4wp_scrollerscript_contentelementid = "' . esc_js( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_CONTENTID ] ) . '";
911
+ const gtm4wp_scrollerscript_scannertime = ' . (int) $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_READERTIME ] . ';';
912
+ }
913
+
914
+ $_gtm_top_content .= '
915
+ </script>
916
+ <!-- End Google Tag Manager for WordPress by gtm4wp.com -->';
917
+
918
+ if ( ! gtm4wp_amp_running() ) {
919
+ if ( $echo ) {
920
+ echo wp_kses(
921
+ $_gtm_top_content,
922
+ array(
923
+ 'script' => array(
924
+ 'data-cfasync' => array(),
925
+ 'data-pagespeed-no-defer' => array(),
926
+ 'data-cookieconsent' => array(),
927
+ ),
928
+ )
929
+ );
930
+ } else {
931
+ return $_gtm_top_content;
932
+ }
933
+ }
934
+ }
935
+
936
+ /**
937
+ * Function executed during wp_head.
938
+ * Outputs the main Google Tag Manager container code and if WooCommerce is active, it removes the
939
+ * purchase data from the data layer if the order ID has been already tracked before and
940
+ * double tracking prevention option is active.
941
+ *
942
+ * @see https://developer.wordpress.org/reference/functions/wp_head/
943
+ *
944
+ * @param boolean $echo If set to true and AMP is currently generating the page content, the HTML is outputed immediately.
945
+ * @return string|void Returns the HTML if the $echo parameter is set to false or when not AMP page generation is running.
946
+ */
947
+ function gtm4wp_wp_header_begin( $echo = true ) {
948
+ global $gtm4wp_datalayer_name, $gtm4wp_datalayer_data, $gtm4wp_options, $woocommerce;
949
+
950
+ $has_html5_support = current_theme_supports( 'html5' );
951
+ $add_cookiebot_ignore = (bool) $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_COOKIEBOT ];
952
+
953
+ echo '
954
+ <!-- Google Tag Manager for WordPress by gtm4wp.com -->
955
+ <!-- GTM Container placement set to ' . esc_html( gtm4wp_get_container_placement_string() ) . ' -->
956
+ <script data-cfasync="false" data-pagespeed-no-defer' . ( $has_html5_support ? ' type="text/javascript"' : '' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>';
957
+
958
+ if ( '' !== $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) {
959
+ $gtm4wp_datalayer_data = array();
960
+ $gtm4wp_datalayer_data = (array) apply_filters( GTM4WP_WPFILTER_COMPILE_DATALAYER, $gtm4wp_datalayer_data );
961
+
962
+ echo '
963
+ var dataLayer_content = ' . wp_json_encode( $gtm4wp_datalayer_data, JSON_UNESCAPED_UNICODE ) . ';';
964
+
965
+ // fire WooCommerce order double tracking protection only if WooCommerce is active and user is on the order received page.
966
+ if ( isset( $gtm4wp_options ) && ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] || $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) && isset( $woocommerce ) && is_order_received_page() ) {
967
+ echo '
968
+ // if dataLayer contains ecommerce purchase data, check whether it has been already tracked
969
+ if ( dataLayer_content.transactionId || ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.purchase ) ) {
970
+ // read order id already tracked from cookies
971
+ var gtm4wp_orderid_tracked = "";
972
+
973
+ if ( !window.localStorage ) {
974
+ var gtm4wp_cookie = "; " + document.cookie;
975
+ var gtm4wp_cookie_parts = gtm4wp_cookie.split( "; gtm4wp_orderid_tracked=" );
976
+ if ( gtm4wp_cookie_parts.length == 2 ) {
977
+ gtm4wp_orderid_tracked = gtm4wp_cookie_parts.pop().split(";").shift();
978
+ }
979
+ } else {
980
+ gtm4wp_orderid_tracked = window.localStorage.getItem( "gtm4wp_orderid_tracked" );
981
+ }
982
+
983
+ // check enhanced ecommerce
984
+ if ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.purchase ) {
985
+ if ( gtm4wp_orderid_tracked && ( dataLayer_content.ecommerce.purchase.actionField.id == gtm4wp_orderid_tracked ) ) {
986
+ delete dataLayer_content.ecommerce.purchase;
987
+ } else {
988
+ gtm4wp_orderid_tracked = dataLayer_content.ecommerce.purchase.actionField.id;
989
+ }
990
+ }
991
+
992
+ // check app+web ecommerce
993
+ if ( dataLayer_content.ecommerce && dataLayer_content.ecommerce.items ) {
994
+ if ( gtm4wp_orderid_tracked && ( dataLayer_content.ecommerce.transaction_id == gtm4wp_orderid_tracked ) ) {
995
+ delete dataLayer_content.ecommerce.affiliation;
996
+ delete dataLayer_content.ecommerce.value;
997
+ delete dataLayer_content.ecommerce.currency;
998
+ delete dataLayer_content.ecommerce.tax;
999
+ delete dataLayer_content.ecommerce.shipping;
1000
+ delete dataLayer_content.ecommerce.transaction_id;
1001
+
1002
+ delete dataLayer_content.ecommerce.items;
1003
+ } else {
1004
+ gtm4wp_orderid_tracked = dataLayer_content.ecommerce.purchase.actionField.id;
1005
+ }
1006
+ }
1007
+
1008
+ // check standard ecommerce
1009
+ if ( dataLayer_content.transactionId ) {
1010
+ if ( gtm4wp_orderid_tracked && ( dataLayer_content.transactionId == gtm4wp_orderid_tracked ) ) {
1011
+ delete dataLayer_content.transactionId;
1012
+ delete dataLayer_content.transactionDate;
1013
+ delete dataLayer_content.transactionType;
1014
+ delete dataLayer_content.transactionAffiliation;
1015
+ delete dataLayer_content.transactionTotal;
1016
+ delete dataLayer_content.transactionShipping;
1017
+ delete dataLayer_content.transactionTax;
1018
+ delete dataLayer_content.transactionPaymentType;
1019
+ delete dataLayer_content.transactionCurrency;
1020
+ delete dataLayer_content.transactionShippingMethod;
1021
+ delete dataLayer_content.transactionPromoCode;
1022
+ delete dataLayer_content.transactionProducts;
1023
+ } else {
1024
+ gtm4wp_orderid_tracked = dataLayer_content.transactionId;
1025
+ }
1026
+ }
1027
+
1028
+ if ( gtm4wp_orderid_tracked ) {
1029
+ if ( !window.localStorage ) {
1030
+ var gtm4wp_orderid_cookie_expire = new Date();
1031
+ gtm4wp_orderid_cookie_expire.setTime( gtm4wp_orderid_cookie_expire.getTime() + (365*24*60*60*1000) );
1032
+ var gtm4wp_orderid_cookie_expires_part = "expires=" + gtm4wp_orderid_cookie_expire.toUTCString();
1033
+ document.cookie = "gtm4wp_orderid_tracked=" + gtm4wp_orderid_tracked + ";" + gtm4wp_orderid_cookie_expires_part + ";path=/";
1034
+ } else {
1035
+ window.localStorage.setItem( "gtm4wp_orderid_tracked", gtm4wp_orderid_tracked );
1036
+ }
1037
+ }
1038
+
1039
+ }';
1040
+ }
1041
+
1042
+ echo '
1043
+ ' . esc_js( $gtm4wp_datalayer_name ) . '.push( dataLayer_content );';
1044
+ }
1045
+
1046
+ echo '
1047
+ </script>';
1048
+
1049
+ do_action( GTM4WP_WPACTION_AFTER_DATALAYER );
1050
+
1051
+ $output_container_code = true;
1052
+ if ( GTM4WP_PLACEMENT_OFF === $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
1053
+ $output_container_code = false;
1054
+
1055
+ echo '
1056
+ <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
1057
+ console.warn && console.warn("[GTM4WP] Google Tag Manager container code placement set to OFF !!!");
1058
+ console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container must be loaded using custom coding !!!");
1059
+ </script>';
1060
+ }
1061
+
1062
+ $disabled_roles = explode( ',', (string) $gtm4wp_options[ GTM4WP_OPTION_NOGTMFORLOGGEDIN ] );
1063
+ if ( count( $disabled_roles ) > 0 ) {
1064
+ $current_user = wp_get_current_user();
1065
+ foreach ( $current_user->roles as $user_role ) {
1066
+ if ( in_array( $user_role, $disabled_roles, true ) ) {
1067
+ $output_container_code = false;
1068
+
1069
+ echo '
1070
+ <script' . ( $has_html5_support ? '' : ' type="text/javascript"' ) . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
1071
+ console.warn && console.warn("[GTM4WP] Google Tag Manager container code was disabled for this user role: ' . esc_js( $user_role ) . ' !!!");
1072
+ console.warn && console.warn("[GTM4WP] Logout or login with a user having a different user role!");
1073
+ console.warn && console.warn("[GTM4WP] Data layer codes are active but GTM container code is omitted !!!");
1074
+ </script>';
1075
+
1076
+ break;
1077
+ }
1078
+ }
1079
+ }
1080
+
1081
+ if ( ( '' !== $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) && $output_container_code ) {
1082
+ $_gtm_codes = explode( ',', str_replace( array( ';', ' ' ), array( ',', '' ), $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] ) );
1083
+
1084
+ $_gtm_tag = '';
1085
+ foreach ( $_gtm_codes as $one_gtm_id ) {
1086
+ if ( ! preg_match( '/^GTM-[A-Z0-9]+$/', $one_gtm_id ) ) {
1087
+ continue;
1088
+ }
1089
+
1090
+ $_gtm_domain_name = 'www.googletagmanager.com';
1091
+ if (
1092
+ ( '' !== $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] ) &&
1093
+ ( preg_match( '/^[a-z0-9\.:]+$/', strtolower( $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ] ) ) )
1094
+ ) {
1095
+ $_gtm_domain_name = $gtm4wp_options[ GTM4WP_OPTION_GTMDOMAIN ];
1096
+ }
1097
+
1098
+ echo '
1099
+ <script data-cfasync="false"' . ( $add_cookiebot_ignore ? ' data-cookieconsent="ignore"' : '' ) . '>
1100
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\'gtm.start\':
1101
+ new Date().getTime(),event:\'gtm.js\'});var f=d.getElementsByTagName(s)[0],
1102
+ j=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=
1103
+ \'//' . esc_js( $_gtm_domain_name ) . '/gtm.\'+\'js?id=\'+i+dl' .
1104
+ ( ( ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) && ( '' !== $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) ) ? "+'&gtm_auth=" . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_AUTH ] ) . '&gtm_preview=' . esc_attr( $gtm4wp_options[ GTM4WP_OPTION_ENV_GTM_PREVIEW ] ) . "&gtm_cookies_win=x'" : '' ) . ';f.parentNode.insertBefore(j,f);
1105
+ })(window,document,\'script\',\'' . esc_js( $gtm4wp_datalayer_name ) . '\',\'' . esc_js( $one_gtm_id ) . '\');
1106
+ </script>';
1107
+ }
1108
+
1109
+ echo '
1110
+ <!-- End Google Tag Manager -->';
1111
+ }
1112
+
1113
+ echo '
1114
+ <!-- End Google Tag Manager for WordPress by gtm4wp.com -->';
1115
+ }
1116
+
1117
+ /**
1118
+ * Function executed during wp_login.
1119
+ * Sets a cookie so that the next page load can fire a GTM event after a user has been logged in.
1120
+ *
1121
+ * @see https://developer.wordpress.org/reference/hooks/wp_login/
1122
+ *
1123
+ * @return void
1124
+ */
1125
+ function gtm4wp_wp_login() {
1126
+ setcookie(
1127
+ 'gtm4wp_user_logged_in',
1128
+ '1',
1129
+ 0,
1130
+ '/',
1131
+ '',
1132
+ ( false !== strstr( get_option( 'home' ), 'https:' ) ) && is_ssl(),
1133
+ true
1134
+ );
1135
+ }
1136
+
1137
+ /**
1138
+ * Function executed during user_register.
1139
+ * Sets a cookie so that the next page load can fire a GTM event after a new user has been registered.
1140
+ *
1141
+ * @see https://developer.wordpress.org/reference/hooks/user_register/
1142
+ *
1143
+ * @return void
1144
+ */
1145
+ function gtm4wp_user_register() {
1146
+ setcookie(
1147
+ 'gtm4wp_user_registered',
1148
+ '1',
1149
+ 0,
1150
+ '/',
1151
+ '',
1152
+ ( false !== strstr( get_option( 'home' ), 'https:' ) ) && is_ssl(),
1153
+ true
1154
+ );
1155
+ }
1156
+
1157
+ /**
1158
+ * Function executed during rocket_excluded_inline_js_content if WP-Rocket is active.
1159
+ * Excludes the dataLayer variable and gtm4wp* variables from being combined into the minified JS file.
1160
+ *
1161
+ * @param array $pattern Patterns to match in inline JS content.
1162
+ * @return array Patterns to match in inline JS content extended with additional items.
1163
+ */
1164
+ function gtm4wp_rocket_excluded_inline_js_content( $pattern ) {
1165
+ $pattern[] = 'dataLayer';
1166
+ $pattern[] = 'gtm4wp';
1167
+
1168
+ return $pattern;
1169
+ }
1170
+
1171
+ /**
1172
+ * Function executed during init.
1173
+ * Removes the cookies set to fire GTM events for new user registration and user login.
1174
+ *
1175
+ * @see https://developer.wordpress.org/reference/hooks/init/
1176
+ *
1177
+ * @return void
1178
+ */
1179
+ function gtm4wp_wp_init() {
1180
+ if ( array_key_exists( 'gtm4wp_user_logged_in', $_COOKIE ) ) {
1181
+ setcookie(
1182
+ 'gtm4wp_user_logged_in',
1183
+ '',
1184
+ -10000,
1185
+ '/',
1186
+ '',
1187
+ ( false !== strstr( get_option( 'home' ), 'https:' ) ) && is_ssl(),
1188
+ true
1189
+ );
1190
+ }
1191
+
1192
+ if ( array_key_exists( 'gtm4wp_user_registered', $_COOKIE ) ) {
1193
+ setcookie(
1194
+ 'gtm4wp_user_registered',
1195
+ '',
1196
+ -10000,
1197
+ '/',
1198
+ '',
1199
+ ( false !== strstr( get_option( 'home' ), 'https:' ) ) && is_ssl(),
1200
+ true
1201
+ );
1202
+ }
1203
+ }
1204
+
1205
+ add_action( 'wp_enqueue_scripts', 'gtm4wp_enqueue_scripts' );
1206
+ $gtm4wp_header_begin_prior = 10;
1207
+ if ( isset( $GLOBALS['gtm4wp_options'] ) && $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_LOADEARLY ] ) {
1208
+ $gtm4wp_header_begin_prior = 2;
1209
+ }
1210
+ add_action( 'wp_head', 'gtm4wp_wp_header_begin', $gtm4wp_header_begin_prior, 0 );
1211
+ add_action( 'wp_head', 'gtm4wp_wp_header_top', 1, 0 );
1212
+ add_action( 'wp_footer', 'gtm4wp_wp_footer' );
1213
+ add_action( 'wp_loaded', 'gtm4wp_wp_loaded' );
1214
+ add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, 'gtm4wp_add_basic_datalayer_data' );
1215
+ add_action( 'init', 'gtm4wp_wp_init' );
1216
+
1217
+ // to be able to easily migrate from other Google Tag Manager plugins.
1218
+ add_action( 'body_open', 'gtm4wp_wp_body_open' );
1219
+
1220
+ // compatibility with existing themes that natively support code injection after opening body tag.
1221
+ add_action( 'genesis_before', 'gtm4wp_wp_body_open' ); // Genisis theme.
1222
+ add_action( 'generate_before_header', 'gtm4wp_wp_body_open', 0 ); // GeneratePress theme.
1223
+ add_action( 'elementor/page_templates/canvas/before_content', 'gtm4wp_wp_body_open' ); // Elementor.
1224
+ add_action( 'ct_before_builder', 'gtm4wp_wp_body_open', 0 ); // Oxygen Builder.
1225
+ add_action( 'fl_before_builder', 'gtm4wp_wp_body_open', 0 ); // Beaver Builder Theme.
1226
+
1227
+ // standard WP theme support for body open tags.
1228
+ add_action( 'wp_body_open', 'gtm4wp_wp_body_open' );
1229
+
1230
+ add_filter( 'rocket_excluded_inline_js_content', 'gtm4wp_rocket_excluded_inline_js_content' ); // WP Rocket.
1231
+
1232
+ // only activate WooCommerce integration for minimum supported WooCommerce version.
1233
+ if (
1234
+ isset( $GLOBALS['gtm4wp_options'] ) &&
1235
+ (
1236
+ $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ||
1237
+ $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ]
1238
+ ) &&
1239
+ isset( $GLOBALS['woocommerce'] ) &&
1240
+ version_compare( WC()->version, '3.2', '>=' )
1241
+ ) {
1242
+ require_once dirname( __FILE__ ) . '/../integration/woocommerce.php';
1243
+ }
1244
+
1245
+ if ( isset( $GLOBALS['gtm4wp_options'] ) && ( '' !== $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_INTEGRATE_GOOGLEOPTIMIZEIDS ] ) ) {
1246
+ require_once dirname( __FILE__ ) . '/../integration/google-optimize.php';
1247
+ }
1248
+
1249
+ if ( isset( $GLOBALS['gtm4wp_options'] ) && ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_EVENTS_USERLOGIN ] ) ) {
1250
+ add_action( 'wp_login', 'gtm4wp_wp_login' );
1251
+ }
1252
+
1253
+ if ( isset( $GLOBALS['gtm4wp_options'] ) && ( $GLOBALS['gtm4wp_options'][ GTM4WP_OPTION_EVENTS_NEWUSERREG ] ) ) {
1254
+ add_action( 'user_register', 'gtm4wp_user_register' );
1255
+ }
readme.txt CHANGED
@@ -1,1009 +1,1034 @@
1
- === GTM4WP ===
2
- Contributors: duracelltomi
3
- Donate link: https://gtm4wp.com/
4
- Tags: google tag manager, tag manager, gtm, google, adwords, google adwords, google ads, adwords remarketing, google ads remarketing, remarketing, google analytics, analytics, facebook ads, facebook remarketing, facebook pixel, google optimize, personalisation
5
- Requires at least: 3.4.0
6
- Requires PHP: 5.6
7
- Tested up to: 6.0.0
8
- Stable tag: 1.15.2
9
- License: GPLv3
10
- License URI: http://www.gnu.org/licenses/gpl.html
11
-
12
- Advanced measurement/advertising tag management and site personalisation for WordPress with Google Tag Manager and Google Optimize
13
-
14
- == Description ==
15
-
16
- Google Tag Manager (GTM) is Google's free tool for everyone to manage and deploy analytics and marketing tags as well as other code snippets
17
- using an intuitive web UI. To learn more about this tool, visit the [official website](https://www.google.com/analytics/tag-manager/).
18
-
19
- This plugin places the GTM container code snippets onto your wordpress website so that you do not need to add this manually.
20
- Multiple containers are also supported!
21
-
22
- The plugin complements your GTM setup by pushing page meta data and user information into the so called data layer.
23
- Google's official help pages includes [more details about the data layer](https://developers.google.com/tag-manager/devguide#datalayer).
24
-
25
- You can also add your Google Optimize container with the [recommended code setup](https://support.google.com/optimize/answer/7359264?hl=en)
26
-
27
- **Some parts of the plugin require PHP 5.6 newer.
28
- PHP 7.0 or newer is recommended.**
29
-
30
- Please note that PHP versions 7.2 or older already reached their end of life cycle thus it is recommended to upgrade. If you are not sure which version you are using, please contact your hosting provider for support.
31
-
32
- = GTM container code placement =
33
-
34
- The original GTM container code is divided into two parts:
35
-
36
- The first part is a javascript code snippet that is added to the `<head>` section of every page of the website.
37
- This part is critical to enable all features of GTM, and this plugin helps to place this part
38
- correctly on your site.
39
-
40
- The second part is an iframe snippet that acts as a failsafe/fallback should users' JavaScript be disabled.
41
- Google recommends – for best performance – to place this code snippet directly after the opening `<body>` tag on each page.
42
-
43
- Albeit not ideal, it will work when placed lower in the code. This plugin provides a code placement option for the second code snippet.
44
-
45
- If your WordPress theme is compatible with the additions of WordPress 5.2 then this plugin will place this second code to the right place.
46
- Users of the Genisis theme, GeneratePress theme, Elementor, Oxygen Builder and Beaver Builder Theme will also have this placed correctly.
47
- To utilize this, use the "Codeless" placement option.
48
-
49
- All other users can place this second code snippet using a custom PHP code ("Custom" placement option) or select the so called "Footer" option to
50
- add the code lower in the code (it is not the recommended way but will work)
51
-
52
-
53
- = Basic data included =
54
-
55
- * post/page titles
56
- * post/page dates
57
- * post/page category names
58
- * post/page tag names
59
- * post/page author ID and name
60
- * post/page ID
61
- * post types
62
- * post count on the current page + in the current category/tag/taxonomy
63
- * custom terms associated with any post type
64
- * logged in status
65
- * logged in user role
66
- * logged in user ID (to track cross device behaviour in Google Analytics)
67
- * logged in user email address (to comply with [GTM terms of service](https://www.google.com/analytics/tag-manager/use-policy/) do not pass this on to Google tags)
68
- * logger in user creation date
69
- * site search data
70
- * site name and id (for WordPress multisite instances)
71
- * IP address of the visitor (please use the explicit consent of the visitor to utilize this)
72
-
73
- = Browser / OS / Device data =
74
-
75
- * browser data (name, version, engine)
76
- * OS data (name, version)
77
- * device data (type, manufacturer, model)
78
-
79
- Data is provided using the WhichBrowser library: http://whichbrowser.net/
80
-
81
- = Weather data =
82
-
83
- (beta)
84
-
85
- Push data about users' current weather conditions into the dataLayer. This can be used to generate weather-related
86
- audience/remarketing lists on ad platforms and allows for user segmentation in your web analytics solutions:
87
-
88
- * weather category (clouds, rain, snow, etc.)
89
- * weather description: more detailed data
90
- * temperature in Celsius or Fahrenheit
91
- * air pressure
92
- * wind speed and degrees
93
-
94
- Weather data is queried from Open Weather Map. Depending on your websites traffic, additional fees may apply:
95
- http://openweathermap.org/price
96
-
97
- An (free) API key from OpenWeatherMap is required for this feature to work.
98
-
99
- ipstack.com is used to determine the site visitor's location. A (free) API key from IPStack.com is required for this feature to work:
100
- https://ipstack.com/product
101
-
102
- = Media player events =
103
-
104
- (experimental)
105
-
106
- Track users' interaction with any embedded media:
107
-
108
- * YouTube
109
- * Vimeo
110
- * Soundcloud
111
-
112
- DataLayer events can be chosen to fire upon media player load, media is being played, paused/stopped and optionally when
113
- the user reaches 10, 20, 30, ..., 90, 100% of the media duration.
114
-
115
- Tracking is supported for embedded media using the built-in oEmbed feature of WordPress as well as most other media plugins
116
- and copy/pasted codes. Players injected into the website after page load are not currently supported.
117
-
118
- = Scroll tracking =
119
-
120
- Fire tags based on how the visitor scrolls from the top to the bottom of a page.
121
- An example would be to separate "readers" (who spend a specified amount of time on a page) from "scrollers"
122
- (who only scroll through within seconds). You can use these events to fire Analytics tags and/or remarketing/conversion tags
123
- (for micro conversions).
124
-
125
- Scroll tracking is based on the solution originally created by
126
-
127
- * Nick Mihailovski
128
- * Thomas Baekdal
129
- * Avinash Kaushik
130
- * Joost de Valk
131
- * Eivind Savio
132
- * Justin Cutroni
133
-
134
- Original script:
135
- http://cutroni.com/blog/2012/02/21/advanced-content-tracking-with-google-analytics-part-1/
136
-
137
- = Google Ads remarketing =
138
-
139
- GTM4WP can add each dataLayer variable as a Google Ads remarketing custom parameter list.
140
- This enables you to build sophisticated remarketing lists.
141
-
142
- = Blacklist & Whitelist Tag Manager tags, triggers and variables =
143
-
144
- To increase website security, you have the option to white- and blacklist tags/triggers/variables.
145
- You can prevent specific tags from firing or the use of certain variable types regardless of your GTM setup.
146
-
147
- If the Google account associated with your GTM account is being hacked, an attacker could easily
148
- execute malware on your website without accessing its code on your hosting server. By blacklisting custom HTML tags
149
- and/or custom JavaScript variables you can secure the Tag Manager container.
150
-
151
- = Integration =
152
-
153
- GTM4WP integrates with several popular plugins. More integration to come!
154
-
155
- * Contact Form 7: fire an event when a Contact Form 7 form was submitted with any result (mail sent, mail failed, spam detected, invalid input)
156
- * WooCommerce:
157
- * Classic e-commerce (deprecated):
158
- * fire an event when visitors add products to their cart
159
- * capture transaction data to be passed to your ad platforms and/or Analytics
160
- * capture necessary remarketing parameters for Google Ads Dynamic Remarketing
161
- * Enhanced e-commerce:
162
- * implementation of [Enhanced E-commerce GA3](https://developers.google.com/tag-manager/enhanced-ecommerce)
163
- * implementation of [Enhanced E-commerce GA4](https://developers.google.com/tag-manager/ecommerce-ga4)
164
- * Does not support promotions since WooCommerce does not have such a feature (yet)
165
- * Does not support refunds
166
- * Google Optimize: load your Google Optimize container directly from your website with the ability to use the data layer variables provided during page load
167
- * AMP: load your AMP container on the AMP version of your pages
168
-
169
- = Server side containers =
170
-
171
- If you are using a [server side container](https://developers.google.com/tag-manager/serverside/send-data#update_the_gtmjs_source_domain)
172
- you can enter your custom domain name to load gtm.js from your there.
173
-
174
- == Installation ==
175
-
176
- 1. Upload `duracelltomi-google-tag-manager-for-wordpress` to the `/wp-content/plugins/` directory
177
- 1. Activate the plugin through the 'Plugins' menu in WordPress
178
- 1. Go to Settings / Google Tag Manager and enter your Google Tag Manager container ID and set additional options
179
-
180
- == Frequently Asked Questions ==
181
-
182
- = How can I ... =
183
-
184
- Tutorials for various Google Tag Manager settings and implementation are available on my website:
185
- https://gtm4wp.com/how-to-articles/
186
-
187
- = PayPal / 3rd party payment gateway transactions in WooCommerce are not being tracked in Google Analytics =
188
-
189
- PayPal and some other 3rd party payment gateways do not redirect users back to your website upon successful transaction by default.
190
- It offers the route back for your customer but it can happen that users close the browser before arriving at your thankyou page
191
- (aka. order received page). This means that neither Google Analytics tags or any other tags have the chance to fire.
192
-
193
- Enable auto-return in your payment gateway settings. This will instruct them to show a quick info page after payment
194
- and redirect the user back to your site. This will improve the accuracy and frequency of tracked transactions.
195
-
196
- = Why isn't there an option to blacklist tag/variable classes =
197
-
198
- Although Google recommends to blacklist tags and variables using classes, people struggle to know
199
- which tags/variables gets affected. Therefore I opted for individual tags and variables rather than classes
200
- on the blacklist tabs.
201
-
202
- Regarding variables; ensure they are not part of any critical tags as blacklisting such variables will render said tags useless.
203
-
204
- = How can I track scroll events in Google Tag Manager? =
205
-
206
- Google Tag Manager supports basic scroll depth tracking based on percentage or pixels natively. This plugin adds
207
- additional scroll tracking events, more focused on capturing the users' intent and/or engagement.
208
-
209
- There are five dataLayer events you can use in your rule definitions:
210
-
211
- * gtm4wp.reading.articleLoaded: the content has been loaded
212
- * gtm4wp.reading.startReading: the visitor started to scroll. The `timeToScroll` dataLayer variable stores duration since the article loaded (in seconds)
213
- * gtm4wp.reading.contentBottom: the visitor reached the end of the content (not the page!). `timeToScroll` dataLayer variable updated
214
- * gtm4wp.reading.pagebottom: the visitor reached the end of the page. `timeToScroll` dataLayer variable updated
215
- * gtm4wp.reading.readerType: based on time spent since article loaded we determine whether the user is a 'scanner' or 'reader' and store this in the `readerType` dataLayer variable
216
-
217
- Example use cases: using these events as triggers, you can fire Google Universal Analytics and/or Google Ads remarketing/conversion tags
218
- to report micro conversions and/or to serve ads only to visitors who spend more time reading your content.
219
-
220
- = Can I exclude certain user roles from being tracked? =
221
-
222
- This is easily managed through GTM itself. If you want to exclude logged in users or users with certain user roles,
223
- use the corresponding dataLayer variable (visitorType) and an exclude filter in Google Tag Manager.
224
-
225
- https://gtm4wp.com/how-to-articles/how-to-exclude-admin-users-from-being-tracked/
226
-
227
- == Screenshots ==
228
-
229
- 1. Admin panel
230
- 2. Basic settings
231
- 3. Events
232
- 4. Integration panel
233
- 5. Advanced settings
234
- 6. Scroll tracking
235
-
236
- == Changelog ==
237
-
238
- = 1.15.2 =
239
-
240
- * Fixed: Stored XSS when using the scroll tracking feature and an admin changes the content element ID into a JavaScript code.
241
-
242
- Full scan of the plugin is also in works to fix any other possible XSS issues.
243
-
244
- = 1.15.1 =
245
-
246
- * Fixed: JavaScript error with the newly added console logging to debug code placement issues
247
- * Fixed: possible XSS Vulnerability if Cloudflare country code option enabled. Thanks [Guillaume Fortier](https://www.linkedin.com/in/guillaume-f-a728711b0/)
248
- * Fixed: proven XSS Vulnerability if adding site search into the data layer was enabled. Original report by [not_stoppable](https://hackerone.com/not_stoppable?type=user). Root cause analysis by [Cory Buecker](https://www.linkedin.com/in/corybuecker/).
249
- * Removed: deprecated feature Google Ads remarketing. This is the outdated, classic way using the google_tag_params variable.
250
- * Dev: removed PHP constant GTM4WP_WPFILTER_COMPILE_REMARKTING (related to removed Google Ads remarketing feature)
251
- * Dev: removed gtm4wp_compile_remarkering WordPress filter (related to removed Google Ads remarketing feature)
252
-
253
- Note to plugin users: I sincerely appologize for the vulnerabilities. To make sure, such cases do not happen again,
254
- the next version will be fully dedicacted to go through every peace of code and make sure proper data processing is happening in GTM4WP.
255
-
256
- = 1.15 =
257
-
258
- * Added: pagePostType data layer variable will now return 404-error on 404 pages and search-results on search result pages
259
- * Added: Google Tag Manager container code can be disabled for specific WordPress user roles under Advanced plugin options. A browser console warning will be shown in such cases to prevent confusion
260
- * Added: support for all Contact Form 7 events for more granual tracking: gtm4wp.contactForm7MailSent, gtm4wp.contactForm7MailFailed, gtm4wp.contactForm7SpamDetected, gtm4wp.contactForm7InvalidInput
261
- * Added: additional data layer variables for date attributes: pagePostDateDayName, pagePostDateHour, pagePostDateMinute, pagePostDateIso, pagePostDateUnix - by [ajtatum](https://github.com/ajtatum)
262
- * Fixed: unclickable products in WooCommerce product lists in Firefox when visiting site in Strict privacy mode or using private browsing
263
- * Fixed: tracking step 2 on WooCommerce checkout page was broken
264
- * Updated: removed CDATA blocks as they are not required in simple HTML and they break some cases where code optimizer is being used
265
- * Updated: products per impression in WooCommerce integration now defaults to 10 instead of 0. This allows view_item_list event to fire on new sites as well
266
- * Updated: code placement options. Separated container on/off option and replaced code placement with the new terminology: compatibility mode
267
- * Updated: removed optional chaining operator usage (?.) in JavaScript codes for better compatibility with outdated browsers
268
- * Updated: changed 'Do not flag orders as being tracked' description to be more precise about what happens if turned on or left off
269
- * Updated: if you enter your custom domain name for server side tagging with the https:// prefix, it will be removed before domain name validation
270
- * Updated: all script blocks to be ignored by Cookiebot if this integration is enabled
271
- * Updated: do not track WooCommerce order where payment failed
272
-
273
- = 1.14.2 =
274
-
275
- * Fixed: undefined google_business_vertical
276
- * Fixed: missing product price in product impression data
277
- * Fixed: better compatibility with cache plugins and lazy load functionalities
278
- * Fixed: Added optional chaining operator to form move tracker code
279
-
280
- = 1.14.1 =
281
-
282
- * Bugfixes
283
-
284
- = 1.14 =
285
-
286
- * Added: support for tracking WooCommerce Block based product lists, except the "All Products" block
287
- * Added: support for [new_customer parameter](https://support.google.com/google-ads/answer/9917012?hl=en-AU#zippy=%2Cinstall-with-google-tag-manager) for Google Smart Shopping campaigns
288
- * Added: SHA256 hashed versions of data layer variables containing email addresses: customerBillingEmailHash on WooCommerce order received pages and visitorEmailHash on generic uses cases
289
- * Added: WooCommerce - if for some reason is_order_received_page() reports false on the order received page, woocommerce_thankyou hook will be used as backup
290
- * Updated: removed jQuery dependency from plugin modules: contact form 7 integration, form move tracker, Vimeo, YouTube, Soundcloud, partly WooCommerce
291
- * Updated: moved the hidden helper span element in products lists to the end of the product box to make more compatible with themes
292
- * Updated: more consistent retrieval of product categories - by [Dekadinious](https://github.com/Dekadinious)
293
- * Updated: gtm4wp_product_readded_to_cart cookie replaced with a WooCommerce session variable to use fewer cookies in this plugin
294
- * Updated: gtm4wp_user_logged_in, gtm4wp_user_registered and gtm4wp_last_weatherstatus cookies are now HTTP only cookies
295
- * Updated: replaced deprecated jQuery method and event usage in WP admin
296
- * Updated: added rel="noopener" to links pointing to external sites on WP admin page
297
- * Updated: Hiding the iframe tag from assistive technologies as it provides no functionality for the end user. This will also alleviate a11y audit warnings.
298
- * Fixed: proper values for visitorType data layer variable
299
- * Fixed: replaced unsafe usage of eval() in WooCommerce QuickView plugin integration
300
- * Fixed: type check of the order ID obtained from a cookie before using the value
301
- * Fixed: navigation issues in Safari if browser loads previous page from cache. GTM4WP will now force Safari to always reload pages.
302
- * Fixed: Do not trigger browser change event in WooCommerce checkout page submit event handler. It caused issues with other 3rd party plugins.
303
- * Fixed: HTML5 detection. - by [Sjoerd](https://github.com/sjoerdkoelewijn)
304
- * Fixed: Username not included in datalayer if no other user attribute is included - by [StaymanHou](https://github.com/StaymanHou)
305
-
306
- = 1.13.1 =
307
-
308
- * Fix: better PHP8 compatibility
309
- * Fix: PHP notice on admin page
310
-
311
- = 1.13 =
312
-
313
- WARNING!
314
- If you are using the geo or weather options of this plugin, make sure your hosting is using PHP 7.0 or newer!
315
-
316
- If you are using the WooCommerce integration with enhanced ecommerce, once again you will need to update your GTM container.
317
- Please check the [setup article](https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking) to see what needs to be changed.
318
- The goal is to keep this setup in the upcoming versions.
319
-
320
- * Added: Google Business Vertical option to populate Google Ads dynamic remarketing
321
- * Added: Make automatic cookie blocking mode of Cookiebot to work with your Google Tag Manager container (new option in the Integration tab)
322
- * Added: support for [server side containers with custom domains](https://developers.google.com/tag-manager/serverside/send-data#update_the_gtmjs_source_domain)
323
- * Added: improved duplacate WooCommerce order tracking prevention by also checking the age of the order. You can adjust the value in minutes on the plugin options page - by [Code-Craze](https://github.com/Code-Craze)
324
- * Fixed: prices rounded to 2 decimals in the data layer (WooCommerce integration)
325
- * Fixed: select_item event fired multiple times (at least twice)
326
- * Fixed: frontend protection to preveent double tracking transactions had a bug
327
- * Fixed: missing product detail and first checkout step tracking in WooCommerce shops with Enhanced Ecommerce
328
- * Fixed: JavaScript type error if product category is a number
329
- * Updated: WhichBrowser v2.1.1 (requires PHP 7.0 or newer)
330
- * Updated: descriptions of WooCommerce tracking methods to emphasize the importance to migrate from standard to enhanced ecommerce
331
- * Updated: added links to enhanced ecommerce setup guides (GA3 and GA4)
332
-
333
- = 1.12.3 =
334
-
335
- No new or updated functionality, but updated WooCommerce compatibility.
336
-
337
- IMPORTANT!
338
- If you are using the WooCommerce integration and enhanced ecommerce, please update your ecommerce event trigger to include gtm4wp.orderCompletedEEC as well.
339
- https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking
340
-
341
- = 1.12.2 =
342
-
343
- If you are upgrading from 1.11.x, please read the previous changelog entry for v1.12
344
-
345
- * Fixed: error message in WooCommerce integration on PHP 8 hosts
346
- * Fixed: PHP notice about non existing blacklist-enable array key
347
-
348
- = 1.12.1 =
349
-
350
- If you are upgrading from 1.11.x, please read the previous changelog entry for v1.12
351
-
352
- * Fixed: PHP notice while saving admin options
353
-
354
- = 1.12 =
355
-
356
- WARNING!
357
- If you are using the blacklist/whitelist feature of the plugin, review these options after upgrade as they could break because of a fundamental rework of this feature.
358
-
359
- * Added: support for Google Analytics 4 version of ecommerce data layer
360
- * Fixed: safer IP address validation in geo and weather data features
361
- * Updated: removed deprecated events: download links, email links, social links -> such can be now tracked with native Google Tag Manager triggers
362
- * Updated: removed support for WooCommerce versions before v3.2
363
- * Updated: 'Blacklist tags' tab renamed to 'Security'
364
- * Updated: complete rework of blacklist/whitelist feature to use the latest tag/trigger/variable list of Google
365
- * Deprecated: classic ecommerce tracking will be removed later this year, please upgrade to enhanced ecommerce tracking
366
- * Deprecated: standard Google Ads remarketing variable will be removed soon as the Google Ads remarketing tag template can easily use any of your Google Tag Manager variables
367
- * Deprecated: the old fashioned way of using Google Ads remarketing with the ecomm_ prefixed data layer variables will be removed soon. Instructions for upgrade will be published on gtm4wp.com once this feature gets updated in an upcoming plugin version
368
-
369
- = 1.11.6 =
370
-
371
- * Fixed: do not track hidden products in the cart in WooCommerce shops
372
- * Fixed: do not fire add to cart event if button is in a disabled state
373
- * Fixed: fixed translation of an admin text
374
- * Fixed: needs_shipping_address() calls were sometimes broken in WooCommerce shops, added additional checks to prevent
375
- * Updated: removed the body_class method of adding the iframe/noscript container code (page builders and the standard wp_body_open hook remains supported)
376
-
377
- = 1.11.5 =
378
-
379
- * Fixed: new Google Optimize container ID format accepted now
380
-
381
- = 1.11.4 =
382
-
383
- * Fixed: fire gtm4wp.checkoutStepEEC and gtm4wp.checkoutOptionEEC events if there is only one shipping method available and it is hidden from the user
384
- * Fixed: decrease checkout step numbers 3 and 4 if shipping destination is set to 'Force shipping to the customer billing address' in WooCommerce. This way there will be no gap in Checkout behaviour report in Google Analytics.
385
- * Updated: tested version number for WooCommerce
386
-
387
- = 1.11.3 =
388
-
389
- * Fixed: use var_export instead of var_dump in some debug code,
390
- * Fixed: apply WooCommerce option for tax inclusion on the order received page as well
391
- * Fixed: JavaScript errors in Vimeo player
392
- * Updated: do not add type attribute to script elements if theme suppors HTML5
393
- * Updated: tested version numbers for WordPress and WooCommerce
394
-
395
- = 1.11.2 =
396
-
397
- WARNING!
398
- If you are upgrading directly from v1.10.x, please read the changelog of v1.11 since it includes many important notices!
399
-
400
- No change in plugin code, WP.org deployment of v1.11.1 included wrong directories
401
-
402
- = 1.11.1 =
403
-
404
- WARNING!
405
- If you are upgrading directly from v1.10.x, please read the changelog of v1.11 since it includes many important notices!
406
-
407
- * Fixed: PHP notice about undefined order_items variable if the new 'Order data in data layer' is turned off
408
- * Fixed: PHP notice about missing brand array key if no brand taxonomy is selected in GTM4WP options
409
-
410
- = 1.11 =
411
-
412
- WARNING!
413
- Please read the changelog very carefully as there are many important changes and removed features which could need your attention before updating!
414
-
415
- * Added Oxygen Builder and Beaver Builder Theme support - you can now use the codeless placement option without issues
416
- * Added ability to fix the Google Tag Manager ID and GTM Environment parameters in wp-config.php. To use it, create PHP constants with the names
417
- ** GTM4WP_HARDCODED_GTM_ID
418
- ** GTM4WP_HARDCODED_GTM_ENV_AUTH
419
- ** GTM4WP_HARDCODED_GTM_ENV_PREVIEW
420
- * Added support for WooCommerce Grouped Products
421
- * Added new WooCommerce option to add all order data into the data layer on the order reveived page
422
- ** This includes personal data of the customer -> you need to ensure this is used in a privacy friendly and compliant way!
423
- ** This order data will be always present on the order received page, even if the page is reloaded or later revisited!
424
- * Removed several unofficial data layer variables on the WooCommerce order received page as they can be read using the new order data option
425
- ** transactionDate
426
- ** transactionType
427
- ** transactionPaymentType
428
- ** transactionShippingMethod
429
- ** transactionPromoCode
430
- * Improved: price reporting with the WooCommerce enhanced ecommerce integration now follows the option set with the 'Display prices in the shop' option of WooCommerce
431
- * Improved: from WooCommerce 3.7 WC_Abstract_Order::get_used_coupons() was replaced with WC_Abstract_Order::get_coupon_codes() which is now used if WC 3.7+ is detected
432
- * Improved: use localStorage for WooCommerce duplicate transaction tracking prevention if available. Should be work with Safari at least for now.
433
- * Fixed: WooCommerce duplicate transaction tracking prevention's cookie was set to expire on session end, now adds 1 year.
434
- * Deprecated data layer variable productIsVariable. Use the new productType data layer variable which will equal to simple, variable, grouped or external depending on the type of the product shown
435
- * Fixed: Wrong lookup for product brand name if Use SKU instead of product ID option was turned on
436
- * Fixed: Wrong lookup for product brand name for variable products
437
- * Fixed: check if $woo->customer is initialized
438
- * Fixed: no checkout step reported on WooCommerce checkout page if the user has accepted the default selection of the payment and shipping methods
439
-
440
- ! Planned deprecation of support for WooCommerce 2.x-3.1.x with next plugin version !
441
- ! Planned deprecation of support for WordPress 4.x with next plugin version !
442
-
443
- = 1.10.1 =
444
-
445
- * Fixed: wrong cookie name was used with the newly introduced double transaction tracking protection while setting the cookie
446
- * Fixed: double transaction tracking JavaScript code is now only included on the order received page
447
- * Fixed: product impressions not properly reported if Products per impressions were set to 0
448
- * Fixed: replaced all references to AdWords to Google Ads
449
-
450
- = 1.10 =
451
-
452
- * Added: Automatically add the noscript part of the container code after the opening body tag for WordPress 5.2+ sites where themes support the new wp_body_open action
453
- * Added: add associated taxonomy values for post type
454
- * Added: select brand taxonomy for WooCommerce products to populate "Product brand" dimension in enhanced ecommerce
455
- * Added: add cart content into data layer so that you can personalize your site experience using Google Optimize
456
- * Added: option to remove shipping costs from revenue data on order received page of WooCommerce
457
- * Added: if you enable either enhanced ecommerce or just Google Ads remarketing variables, 3 new data layer variables will be also available about the product on a detail page
458
- * Product rating details (productRatingCounts)
459
- * Average product rating (productAverageRating)
460
- * Review count (productReviewCount)
461
- * Added: if you are using Cloudflare, you can now add the country code HTTP header value into the data layer and read from it with the geoCloudflareCountryCode variable name
462
- * Fixed: better compatibility with Google's mod_pagespeed
463
- * Fixed: missing product quantity while adding a variable product into the cart
464
- * Fixed: prevent multiple tracking of WooCommerce orders on mobile devices where the mobile browser reloads the order received page from local cache executing GTM tracking again
465
-
466
- = 1.9.2 =
467
-
468
- * Fixed: possible PHP warning if geo data or weather data feature is turned on
469
-
470
- = 1.9.1 =
471
-
472
- * Fixed: handle out of quota cases with ipstack queries properly
473
- * Fixed: proper YouTube tracking for WordPress sites and WordPress multisites installed in a subdirectory
474
- * Fixed: properly detect client IP address and also properly escape this data while using it
475
- * Fixed: WooCommerce checkout steps after page load did not include products in the cart
476
- * Fixed: checkout step events for payment mode and shipping type not always fired
477
- * Fixed: the CMD on Mac will be treated just like the Ctrl key on Windows while processing the product click event in the WooCommerce integration (thy for luzinis)
478
- * Fixed: add currencyCode to every ecommerce action in WooCommerce integration
479
- * Fixed: better WooCommere Quick View integration
480
- * Fixed: possible cross site scripting vulnerability if site search tracking was enabled due to not properly escaped referrer url tracking
481
- * Changed: code cleanup in WooCommerce integration
482
-
483
- = 1.9 =
484
-
485
- * Added: initial support for AMP plugin from Automattic (thx koconder for the contribution!)
486
- * Added: option to remove tax from revenue data on order received page of WooCommerce
487
- * Added: WooCommerce enhanced ecommerce datasets now include stock levels
488
- * Added: new productIsVariable data layer variable is set to 1 on variable WooCommerce product pages
489
- * Added: product impressions can now be split into multiple chunks to prevent data loss on large product category and site home pages (thx Tim Zook for the contribution!)
490
- * IMPORTANT! You will need to update your GTM setup, please read the new Step 9 section of the [setup tutorial page](https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking).
491
- * Added: you can now disable flagging of WooCommerce orders as being already tracked once. In same cases (with iDeal for example) you may need this to make purchase tracking to work.
492
- * Added: uninstalling the plugin will now remove configured plugin options from database
493
- * Added: new advanced plugin option: data layer variable visitorDoNotTrack will include 1 if the user has set the [do not track flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT) in his/her browser
494
- * Added: new data layer event when a user has logged in on the frontend: gtm4wp.userLoggedIn
495
- * Added: new data layer event when a new user has registered on the frontend: gtm4wp.userRegistered
496
- * Added: new advanced plugin option: move data layer declaration and Google Tag Manager container as close as possible to the beginning of the HTML document
497
- * Added: better WP Rocket support
498
- * Updated: Full Google Optimize support. Now the plugin can load your Google Optimize container with the [recommended code placement](https://support.google.com/optimize/answer/7359264?hl=en)
499
- * Updated: moved most of the inline JavaScript codes into separate .js files which should help cache plugins to do their job much better when my plugin is active
500
- * Fixed: wrong ecomm_pagetype on product search result pages
501
- * Fixed: PHP notice in some cases when geo data was not loaded properly
502
- * Fixed / Added: freegeoip.net was rebranded to ipstack.com and an API key is needed now even for free usage. You can now add your API key so that weather data and geo data can be added into the data layer
503
- * Warning: some plugin features will be remove from v1.10, most of them can be tracked now using pure Google Tag Manager triggers:
504
- * Social actions
505
- * Outbound link click events
506
- * Download click events
507
- * Email click events
508
- * Warning: PHP 5.6 is now the minimum recommended version to use this plugin. I advise to move to PHP 7.x
509
-
510
- = 1.8.1 =
511
-
512
- * Added: new visitorIP data layer variable to support post-GDPR implementations where for example internal traffic exclusion has to be made inside the browser
513
- * Fixed: JavaScript error around the variable gtm4wp_use_sku_instead
514
- * Fixed: added _ as a valid character for gtm_auth GTM environment variable
515
- * Fixed: corrected typo - gtm4wp.checkoutStepE**E**C
516
- * Fixed: two strings were not recognized by WordPress Translate on the admin page
517
- * Fixed: some other plugins call found_variation event of WooCommerce without product variation data being included
518
- * Fixed: product name included variation name on order received page which broke GA product reports
519
- * Fixed: in some cases, no contact form 7 data was being passed to the gtm4wp.contactForm7Submitted event
520
- * Updated: added CDATA markup around container code for better DOM compatibility
521
- * Updated: removed 'SKU:' prefix text from classic ecommerce dimension as it broke some enhanced ecommerce reports
522
-
523
- = 1.8 =
524
-
525
- * Fixed: weather data tracking codes could result in fatal PHP error
526
- * Fixed: cart events did to fire while user pressed the Enter key in quantity fields
527
- * Fixed: contact form 7 changed some code which prevented successful form submission tracking
528
- * Changed: links to plugin website updated
529
- * Changed: gtm4wp.cf7formid data layer variable now includes the ID of the form in WordPress
530
- * Added: gtm4wp.cf7inputs includes data that has been filled in the form
531
- * Added: [WooCommerce compatibility headers](https://docs.woocommerce.com/document/create-a-plugin/#section-10)
532
- * Added: admin warning for WooCommerce 2.x users. This plugin will drop support for WooCommerce 2.x soon
533
- * Added: postFormat data layer variable on singular pages
534
- * Added: customer* data layer variables with stored billing and shipping data, total number of orders and total value of those orders (needs WooCommerce 3.x)
535
- * Added: geo* data layer variables to get country, city, lat-lon coordinates of the visitor
536
- * Added: visitorUsername data layer variable with the username of the logged in user
537
- * Added: more detailed checkout reporting for WooCommerce sites
538
- * Add gtm4wp.checkoutStepEEC to your Ecommerce Helper trigger
539
- * Change a typo: gtm4wp.checkoutOptionE**C**C => gtm4wp.checkoutOptionE**E**C
540
- * Added: option to include full product category path in enhanced ecommerce reporting (can cause performance issues on large sites!)
541
- * Added: initial support for [Google Tag Manager Environments](https://support.google.com/tagmanager/answer/6311518?hl=en)
542
- * Added: support for [WooCommerce Quick View plugin](https://woocommerce.com/products/woocommerce-quick-view/)
543
- * Updated: description of code placement options to clarify what this option does
544
- * Updated: cleanup of readme.txt, spelling and grammar improvements
545
- * Updated: bundled WhichBrowser lib v2.0.32
546
-
547
-
548
- = 1.7.2 =
549
-
550
- * Fixed: in some cases, the remove item from cart link in a WooCommerce cart was not altered properly with additional tracking codes
551
- * Fixed: product categories were empty in the cart, on the checkout pages and on the order received page for product variations
552
- * Fixed: checkout option data included checkout step #1 if cart page was setup to be the first
553
- * Fixed: even more WooCommerce 3.x compatibility
554
- * Added: registration date of the logged in user can be added to the data layer
555
- * Updated: geoplugin.net has been replaced by freegeoip.net for weather tracking which has far better quota for free usage
556
- * Updated: Google Ads dynamic remarketing data layer items on a WooCommerce product page will be shown for the root product as well on variable product pages
557
- * Updated: Selecting a product variation will include the price of the product in Google Ads dynamic remarketing data layer items
558
- * Updated: minor code cleanup
559
-
560
- = 1.7.1 =
561
-
562
- * Fixed: PHP 5.3 compatible syntax in frontend.php
563
- * Fixed: PHP error using classic ecommerce with WooCommerce 2.6.x
564
- * Updated: Added data-cfasync='false' to all <script> elements to prevent CloudFlare to load scripts async
565
- * Added: Warning for users of PHP 5.4 or older to consider upgrade (FYI: PHP 5.5 and older versions do not get even security fixes)
566
-
567
- = 1.7 =
568
-
569
- * Updated: even better WooCommerce 3.0 compatibility (WooCommerce 2.6 still supported but this support ends with the next plugin version)
570
- * Fixed: properly escaping product category name on variable product detail pages
571
- * Fixed: proper data layer stucture in the gtm4wp.changeDetailViewEEC event
572
- * Added: Google Optimize page hiding snippet under Integrations tab
573
- * Added: add to cart data for WooCommerce enhanced ecommerce tracking if user undos a cart item removal (no need to update GTM tags)
574
- * Added: you can now enter a product ID prefix so that IDs can match with IDs in some product feeds generated by other plugins
575
- * Added: option to track cart page as step 1 in enhanced ecommerce checkout funnel
576
-
577
- = 1.6.1 =
578
-
579
- * Fixed: PHP warning message on WooCommerce cart page
580
- * Fixed: Better compatibility with WooCommerce 2.6.x :-)
581
-
582
- = 1.6 =
583
-
584
- * Fixed: do not block product list item clicks if ad blocker is enabled
585
- * Fixed: only track product clicks in product lists if link points to the product detail page URL
586
- * Fixed: PHP warning in backlogs 'Undefined variable: gtm4wp_options'
587
- * Added: product variation support in WooCommerce integration (enhanced ecommerce implementations should add the GTM event gtm4wp.changeDetailViewEEC to the ecommerce event trigger)
588
- * Updated: better WooCommerce 3.0 compatibility
589
-
590
- = 1.5.1 =
591
-
592
- * Fixed: clicks on products in product list pages redirected to undefined URLs with some themes.
593
-
594
- = 1.5 =
595
-
596
- Lots of WooCommerce ecommerce codes has been changed and extended, please double check your measurement after upgrading to this version!
597
-
598
- * Added: warning message if you are using PHP 5.3 or older. Browser/OS/Device tracking needs 5.4 or newer
599
- * Added: Email address of the logged in user into the visitorEmail dataLayer variable. Remember: to comply with GTM TOS you are not allowed to pass this data towards any Google tag but you can use this in any other 3rd party tag.
600
- * Added: gtm4wp_eec_product_array WordPress filter so that plugin and theme authors can add their own data for enhanced ecommere product arrays
601
- * Fixed: JavaScript error in WooCommerce stores when enhanced ecommerce enabled and a product being clicked in a widget area
602
- * Fixed: Order data not present in some cases on the order received page
603
- * Changed: Extended "User SKUs instead of IDs for remarketing" option to be also applied to ecommerce product data arrays
604
- * Changed: Use wc_clean instead of the deprecated function woocommerce_clean
605
- * Changed: New, divided GTM container implemented - a fixed part in the <head> and an iframe part placed using the container placement option you've set earlier
606
-
607
- = 1.4 =
608
-
609
- * Fixed: WP CLI error message
610
- * Fixed: wrong dynamic remarketing tagging on cart and checkout pages
611
- * Updated: WhichBrowser library to 2.0.22
612
- * Updated: slightly changed container code snippet to prevent W3 Total Cache to alter the code which breaks proper code execution
613
- * Updated: replaced file_get_contents() usage in weather tracking to wp_remote_get() so that it is more compatible with several WP instances
614
- * Updated: YouTube/Video/Soundcloud tracking now tracks videos not embedded using oEmbed (like videos in a widget area)
615
- * Updated: new Vimeo Player API implemented which should solve several issues
616
- * Changed: adapted W3C HTML5 media player event names which changes some events (needs updating your existing GTM setup):
617
- * Soundcloud: finish => ended, seek => seeked
618
- * YouTube: playing => play, paused => pause, playback-rate-change => ratechange
619
- * Vimeo: seek => seeked
620
- * Added: new placement option - 'off'. This will only generate the data layer but you will need to add the proper GTM container code snippet by hand
621
- * Added: new data layer variable: authorID
622
- * Added: new data layer variable: siteID to be able to track based on blog ID in a multisite environment
623
- * Added: new data layer variable: siteName to be able to track in a multisite environment
624
-
625
- = 1.3.2 =
626
-
627
- * Fixed: remove cart event not fired in WooCommerce 2.6
628
- * Fixed: ecomm_prodid.push error message on product detail pages
629
- * Fixed: proper tracking of cart actions on the cart page for WooCommerce 2.6
630
- * Fixed: 'Illegal string offset' errors in some cases in the cart
631
- * Fixed: OpenWeatherMap requires a (free) API key now, you can now enter this to use weather data in data layer
632
-
633
- = 1.3.1 =
634
-
635
- * Fixed: "json_encode() expects parameter 2 to be long, string given" on PHP 5.3 instances
636
- * Fixed: Fatal PHP error in cart if you enabled taxes to be included in your cart
637
-
638
- = 1.3 =
639
-
640
- Major changes to the Enhanced Ecommerce implementation of the WooCommerce integration!
641
-
642
- * Fixed: proper tracking of list positions
643
- * Fixed: opening product detail page in a new window/tab when user pressed the CTRL key
644
- * Fixed: ecomm_totalvalue included the total price of the cart without taxes
645
- * Fixed: ecomm_totalvalue does not take into account the quantity of ordered products on the order received page
646
- * Fixed: php error message on product lists when Google Ads dynamic remarketing was enabled on WooCommerce 2.6
647
- * Fixed: added data-cfasync="false" to the GTM container code for better compatibility with CloudFlare
648
- * Added: introducing tracking of list names (general product list, recent products list, featured products list, etc.)
649
- * Some list names (like cross-sells) will be shown as 'General Product List'. A proposed change in WooCommerce 2.6 will solve that issue
650
- * Added: tracking product lists in widgets
651
- * Added: tracking checkout options (payment and shipment)
652
- * Updated: better add-to-cart / remove-from-cart management in mini cart and while updating cart content
653
- * Updated: added currency code to each enhanced ecommerce call so that currency reporting is OK for multi currency sites
654
- * Updated: replaced usage of get_currentuser() to keep compatibility with WordPress 4.5
655
-
656
- = 1.2 =
657
-
658
- * Fixed: subtabs on admin page now showing in certain cases
659
- * Fixed: error message when running the site using WP CLI (thanks Patrick Holberg Hesselberg)
660
- * Fixed: some typos on admin page
661
- * Fixed: dismissable notices did not disappear in some cases
662
- * Fixed: tracking of Twitter event cased sometimes JS errors
663
- * Fixed: site search tracking caused sometimes PHP errors when HTTP_REFERER was not set
664
- * Updated: preparation for translate.wordpress.org
665
- * Added: support for multiple container IDs
666
- * Added: added form ID when sending a Contact Form 7 form. Variable name: gtm4wp.cf7formid
667
-
668
- = 1.1.1 =
669
-
670
- * Fixed: PHP errors in frontend.php and admin.php
671
-
672
- = 1.1 =
673
-
674
- * Added: track embedded YouTube/Vimeo/Soundcloud videos (experimental)
675
- * Added: new checkbox - use product SKU for Google Ads Dynamic Remarketing variables instead of product ID (experimental)
676
- * Added: place your container code after the opening body tag without modifying your theme files (thx Yaniv Friedensohn)
677
- * Added: automatic codeless container code injection for Genesis framework users
678
- * Fixed: Possible PHP error with custom payment gateway (QuickPay) on the checkout page (thx Damiel for findig this)
679
-
680
- = 1.0 =
681
-
682
- The plugin itself is now declared as stable. This means that it should work with most WordPress instances.
683
- From now on each version will include features labeled as:
684
-
685
- * Beta: the feature has been proven to work for several users but it can still have some bugs
686
- * Experimental: new feature that needs proper testing with more users
687
- * Deprecated: this feature will be removed in a future version
688
-
689
- If you see any issue with beta or experimental functions just disable the checkbox. Using this error messages should disappear.
690
- Please report all bugs found in my plugin using the [contact form on my website](https://gtm4wp.com/contact).
691
-
692
- * Fixed: wrong GTM container code when renaming default dataLayer variable name (thx Vassilis Papavassiliou)
693
- * Fixed: Enhanced Ecommerce product click data was "undefined" in some cases (thx Sergio Alen)
694
- * Fixed: wrong user role detection while adding visitorType to the dataLayer (thx Philippe Vachon-Rivard)
695
- * Changed: only add visitorId to the dataLayer if there is a logged in user
696
- * Added: feature labels so that you can see beta, experimental and deprecated features
697
- * Deprecated: outbound click, email click and download click events. You should use GTM trigger events instead
698
-
699
- = 0.9.1 =
700
-
701
- * Fixed: PHP error message: missing get_shipping function using WooCommerce 2.3.x
702
-
703
- = 0.9 =
704
-
705
- * Added: visitorId dataLayer variable with the ID of the currently logged in user to track userID in Google Analytics
706
- * Added: WordPress filter hook so that other templates and plugins can get access to the GTM container code before outputting it
707
- * Fixed: 'variation incorrect' issue by Sharken03
708
- * Fixed: error messages in WooCommerce integration when product has no categories
709
- * Fixed: add_inline_js errors in newer versions of WooCommerce
710
- * Fixed: error message when some device/browser/OS data could not be set
711
- * Fixed: tracking Twitter events was broken
712
-
713
- = 0.8.2 =
714
-
715
- * Fixed: broken links when listing subcategories instead of products (thanks Jon)
716
- * Fixed: wheather/weather typo (thanks John Hockaday)
717
- * Fixed: wrong usage of get_the_permalink() instead of get_permalink() (thanks Szepe Viktor)
718
-
719
- = 0.8.1 =
720
-
721
- * Fixed: PHP error in enhanced ecommerce implementation when using layered nav widget
722
-
723
- = 0.8 =
724
-
725
- * Updated: Added subtabs to the admin UI to make room for new features :-)
726
- * Updated: WhichBrowser library to the latest version
727
- * Added: You can now dismiss plugin notices permanently for each user
728
- * Added: weather data. See updated plugin description for details
729
- * Added: Enhanced E-commerce for WooCommerce (experimental!)
730
- * Fixed: PHP notice in frontend.php script. Credit to Daniel Sousa
731
-
732
- = 0.7.1 =
733
-
734
- * Fixed: WooCommerce 2.1.x compatibility
735
-
736
- = 0.7 =
737
-
738
- * Updated/Fixed: dataLayer variables are now populated at the end of the head section. Using this the container code can appear just after the opening body tag, thus Webmaster Tools verification using Tag Manager option will work
739
- * Added: blacklist or whitelist tags and macros to increase security of your Tag Manager setup
740
-
741
-
742
- = 0.6 =
743
-
744
- * Updated: better add-to-cart events for WooCommerce, it includes now product name, SKU and ID
745
- * Added: browser, OS and device data to dataLayer variables
746
- * Added: postCountOnPage and postCountTotal dataLayer variables to track empty categories/tags/taxonomies
747
-
748
- = 0.5.1 =
749
-
750
- * Fixed: WooCommerce integration did not work on some environments
751
-
752
- = 0.5 =
753
- * Added: scroll tracking
754
- * Fixed: social tracking option on the admin panel was being shown as an edit box instead of a checkbox
755
- * Fixed: WooCommerce transaction data was not included in the dataLayer if you selected "Custom" code placement
756
- * Fixed: do not do anything if you enabled WooCommerce integration but did not activate WooCommerce plugin itself
757
- * Updated: do not re-declare dataLayer variable if it already exists (because another script already created it before my plugin was run)
758
-
759
- = 0.4 =
760
- * Added: you can now select container code placement. This way you can insert the code snippet after the opening body tag. Please read FAQ for details
761
- * Added: initial support for social event tracking for Facebook and Twitter buttons. Please read FAQ for details
762
- * Updated: event name on successful WooCommerce transaction: OrderCompleted -> gtm4wp.orderCompleted
763
- * Fixed: frontend JS codes did not load on some WordPress installs
764
-
765
- = 0.3 =
766
- * Updated: admin page does not show an alert box if Tag Manager ID or dataLayer variable name is incorrect. Instead it shows a warning line below the input field.
767
- * Updated: rewritten the code for WooCommerce dynamic remarketing. Added tag for homepage and order completed page.
768
-
769
- = 0.2 =
770
- * ! BACKWARD INCOMPATIBLE CHANGE ! - Names of Tag Manager click events has been changed to comply with naming conventions:
771
- * ContactFormSubmitted -> gtm4wp.contactForm7Submitted
772
- * DownloadClick -> gtm4wp.downloadClick
773
- * EmailClick -> gtm4wp.emailClick
774
- * OutboundClick -> gtm4wp.outboundClick
775
- * AddProductToCart -> gtm4wp.addProductToCart
776
- * Updated: click events are now disabled by default to reflect recently released Tag Manager auto events. I do not plan to remove this functionality. You can decide which solution you would like to use :-)
777
- * Updated: language template (pot) file and Hungarian translation
778
- * Added: new form move events to track how visitors interact with your (comment, contact, etc.) forms
779
- * Added: event names to admin options page so that you know what events to use in Google Tag Manager
780
- * Added: Google Tag Manager icon to admin settings page
781
- * Added: Settings link to admin plugins page
782
- * Fixed: null value in visitorType dataLayer variable if no logged in user exists (now 'visitor-logged-out')
783
-
784
- = 0.1 =
785
- * First beta release
786
-
787
- == Upgrade Notice ==
788
-
789
- = 1.15.2 =
790
-
791
- Bugfix release
792
-
793
- = 1.15.1 =
794
-
795
- Bugfix release
796
-
797
- = 1.15 =
798
-
799
- Various updates, fixes and improvements
800
-
801
- = 1.14.2 =
802
-
803
- Bugfix release
804
-
805
- = 1.14.1 =
806
-
807
- Bugfix release
808
-
809
- = 1.14 =
810
-
811
- Initial support for WooCommerce Blocks, removed jQuery usage in JavaScript codes (except for WooCommerce related codes)
812
-
813
- = 1.13.1 =
814
-
815
- Bugfix release
816
-
817
- = 1.13 =
818
-
819
- Updated support for Google Ads dynamic remarketing with WooCommerce, added support for server side containers and Cookiebot + fixes
820
-
821
- = 1.12.3 =
822
-
823
- Updated compatible WooCommerce version and added an important message to the changelog for WooCommerce users
824
-
825
- = 1.12.2 =
826
-
827
- Bugfix release + read changelog for important v1.12.x changes
828
-
829
- = 1.12.1 =
830
-
831
- Bugfix release + read changelog for important v1.12.x changes
832
-
833
- = 1.12 =
834
-
835
- Removed several deprecated features, dropped support for WooCommerce versions before 3.2, introduced GA4 data layer variables, deprecated classic ecommerce and Google Ads remarketing varibale
836
-
837
- = 1.11.6 =
838
-
839
- Bugfix release
840
-
841
- = 1.11.5 =
842
-
843
- Fixed: new Google Optimize container ID format accepted now
844
-
845
- = 1.11.4 =
846
-
847
- Bugfix and updated tested version number for WooCommerce
848
-
849
- = 1.11.3 =
850
-
851
- Some bugfixes and adding support for the latest WordPress and WooCommerce version
852
-
853
- = 1.11.2 =
854
-
855
- Replaces v1.11.1
856
-
857
- = 1.11.1 =
858
-
859
- WooCommerce integration related fixes.
860
-
861
- = 1.11 =
862
-
863
- Please read the changelog very carefully as there are many important changes and removed features which could need your attention before updating!
864
-
865
- = 1.10.1 =
866
-
867
- Bugfix release
868
-
869
- = 1.10 =
870
-
871
- Better WordPress 5.2 integration, support for brands in WooCommerce, access cart content in data layer, more stable double transaction tracking prevention on mobiles and more!
872
-
873
- = 1.9.2 =
874
-
875
- Fixed possible PHP warning if geo data or weather data feature is turned on
876
-
877
- = 1.9.1 =
878
-
879
- Bugfix version
880
-
881
- = 1.9 =
882
-
883
- New AMP GTM support, full Google Optimize support, lots of WooCommerce tracking improvements.
884
-
885
- = 1.8.1 =
886
-
887
- Bugfix version fixing some issues around WooCommerce tracking and GTM environments. Also adds IP address into the data layer.
888
-
889
- = 1.8 =
890
-
891
- Lots of new features and some important changes, please read the changelog to ensure your measurement does not break
892
-
893
- = 1.7.2 =
894
-
895
- Bugfix release: many little fixes, event better WooCommerce 3.x compatibility
896
-
897
- = 1.7.1 =
898
-
899
- Bugfix release: better PHP 5.3 and WooCommerce 2.6.x compatibility
900
-
901
- = 1.7 =
902
-
903
- Better WooCommerce 3.x compatibility and new features
904
-
905
- = 1.6.1 =
906
-
907
- Bugfix release.
908
-
909
- = 1.6 =
910
-
911
- If you are using WooCommerce and enhanced ecommerce, please add gtm4wp.changeDetailViewEEC to the ecommerce helper trigger
912
-
913
- = 1.5.1 =
914
-
915
- Fixed: clicks on products in product list pages redirected to undefined URLs with some themes.
916
-
917
- = 1.5 =
918
-
919
- Lots of WooCommerce ecommerce codes has been changed and extended, please double check your measurement after upgrading to this version!
920
-
921
- = 1.4 =
922
-
923
- Several additions and fixes, breaking changes on media player tracking, please read changelog before upgrade
924
-
925
- = 1.3.2 =
926
-
927
- Quickfix release for 1.3.x: major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
928
-
929
- = 1.3.1 =
930
-
931
- Quickfix release for 1.3: major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
932
-
933
- = 1.3 =
934
-
935
- Major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
936
-
937
- = 1.2 =
938
-
939
- New release with lots of fixes from the past months and new features like multiple container support!
940
-
941
- = 1.1.1 =
942
-
943
- This is a bugfix release to address some issues with v1.1
944
-
945
- = 1.1 =
946
-
947
- New! Track popular media players embedded into your website!
948
-
949
- = 1.0 =
950
-
951
- First stable release, please read changelog for details!
952
-
953
- = 0.9.1 =
954
-
955
- Bugfix release for WooCommerce users with ecommerce tracking enabled
956
-
957
- = 0.9 =
958
-
959
- Many bug fixes, important fixes for WooCommerce users
960
-
961
- = 0.8.2 =
962
-
963
- Another bugfix release for WooCommerce users with Enhanced Ecommerce enabled
964
-
965
- = 0.8.1 =
966
-
967
- Bugfix release for WooCommerce users with Enhanced Ecommerce enabled
968
-
969
- = 0.8 =
970
-
971
- This version introduces Enhanced E-commerce implementation for WooCommerce. Please note that this
972
- feature of the plugin is still experimental and the feature of Google Analytics is still in beta.
973
- Read the plugin FAQ for details.
974
-
975
- = 0.7.1 =
976
-
977
- If you are using WooCommerce and updated to 2.1.x you SHOULD update immediately.
978
- This release includes a fix so that transaction data can be passed to GTM.
979
-
980
- = 0.7 =
981
-
982
- Improved code so that Webmaster Tools verification can work using your GTM container tag.
983
- Blacklist or whitelist tags and macros to increase security of your Tag Manager setup.
984
- Fixed: WhichBrowser library was missing from 0.6
985
-
986
- = 0.6 =
987
-
988
- Improved add-to-cart events for WooCommerce, added browser/OS/device infos and post count infos.
989
-
990
- = 0.5.1 =
991
-
992
- Bug fix release for WooCommerce users.
993
-
994
- = 0.5 =
995
- Besides of some fixes this version includes scroll tracking events for Google Tag Manager.
996
-
997
- = 0.4 =
998
- Important change: Tag Manager event name of a WooCommerce successful order has been changed.
999
- See changelog for details.
1000
-
1001
- = 0.3 =
1002
- This is a minor release. If you are using WooCommerce you should update to include more accurate Google Ads dynamic remarketing feature.
1003
-
1004
- = 0.2 =
1005
- BACKWARD INCOMPATIBLE CHANGE: Names of Tag Manager click events has been changed to comply with naming conventions.
1006
- See changelog for details. Do not forget to update your Tag Manager container setup after upgrading this plugin!
1007
-
1008
- = 0.1 =
1009
- This is the first public beta, no upgrade is needed.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === GTM4WP ===
2
+ Contributors: duracelltomi
3
+ Donate link: https://gtm4wp.com/
4
+ Tags: google tag manager, tag manager, gtm, google, adwords, google adwords, google ads, adwords remarketing, google ads remarketing, remarketing, google analytics, analytics, facebook ads, facebook remarketing, facebook pixel, google optimize, personalisation
5
+ Requires at least: 3.4.0
6
+ Requires PHP: 5.6
7
+ Tested up to: 6.0.0
8
+ Stable tag: 1.16
9
+ License: GPLv3
10
+ License URI: http://www.gnu.org/licenses/gpl.html
11
+
12
+ Advanced measurement/advertising tag management and site personalisation for WordPress with Google Tag Manager and Google Optimize
13
+
14
+ == Description ==
15
+
16
+ Google Tag Manager (GTM) is Google's free tool for everyone to manage and deploy analytics and marketing tags as well as other code snippets
17
+ using an intuitive web UI. To learn more about this tool, visit the [official website](https://www.google.com/analytics/tag-manager/).
18
+
19
+ This plugin places the GTM container code snippets onto your wordpress website so that you do not need to add this manually.
20
+ Multiple containers are also supported!
21
+
22
+ The plugin complements your GTM setup by pushing page meta data and user information into the so called data layer.
23
+ Google's official help pages includes [more details about the data layer](https://developers.google.com/tag-manager/devguide#datalayer).
24
+
25
+ You can also add your Google Optimize container with the [recommended code setup](https://support.google.com/optimize/answer/7359264?hl=en)
26
+
27
+ **Some parts of the plugin require PHP 5.6 newer.
28
+ PHP 7.0 or newer is recommended.**
29
+
30
+ Please note that PHP versions 7.2 or older already reached their end of life cycle thus it is recommended to upgrade. If you are not sure which version you are using, please contact your hosting provider for support.
31
+
32
+ = GTM container code placement =
33
+
34
+ The original GTM container code is divided into two parts:
35
+
36
+ The first part is a javascript code snippet that is added to the `<head>` section of every page of the website.
37
+ This part is critical to enable all features of GTM, and this plugin helps to place this part
38
+ correctly on your site.
39
+
40
+ The second part is an iframe snippet that acts as a failsafe/fallback should users' JavaScript be disabled.
41
+ Google recommends – for best performance – to place this code snippet directly after the opening `<body>` tag on each page.
42
+
43
+ Albeit not ideal, it will work when placed lower in the code. This plugin provides a code placement option for the second code snippet.
44
+
45
+ If your WordPress theme is compatible with the additions of WordPress 5.2 then this plugin will place this second code to the right place.
46
+ Users of the Genisis theme, GeneratePress theme, Elementor, Oxygen Builder and Beaver Builder Theme will also have this placed correctly.
47
+ To utilize this, use the "Codeless" placement option.
48
+
49
+ All other users can place this second code snippet using a custom PHP code ("Custom" placement option) or select the so called "Footer" option to
50
+ add the code lower in the code (it is not the recommended way but will work)
51
+
52
+
53
+ = Basic data included =
54
+
55
+ * post/page titles
56
+ * post/page dates
57
+ * post/page category names
58
+ * post/page tag names
59
+ * post/page author ID and name
60
+ * post/page ID
61
+ * post types
62
+ * post count on the current page + in the current category/tag/taxonomy
63
+ * custom terms associated with any post type
64
+ * logged in status
65
+ * logged in user role
66
+ * logged in user ID (to track cross device behaviour in Google Analytics)
67
+ * logged in user email address (to comply with [GTM terms of service](https://www.google.com/analytics/tag-manager/use-policy/) do not pass this on to Google tags)
68
+ * logger in user creation date
69
+ * site search data
70
+ * site name and id (for WordPress multisite instances)
71
+ * IP address of the visitor (please use the explicit consent of the visitor to utilize this)
72
+
73
+ = Browser / OS / Device data =
74
+
75
+ * browser data (name, version, engine)
76
+ * OS data (name, version)
77
+ * device data (type, manufacturer, model)
78
+
79
+ Data is provided using the WhichBrowser library: http://whichbrowser.net/
80
+
81
+ = Weather data =
82
+
83
+ (beta)
84
+
85
+ Push data about users' current weather conditions into the dataLayer. This can be used to generate weather-related
86
+ audience/remarketing lists on ad platforms and allows for user segmentation in your web analytics solutions:
87
+
88
+ * weather category (clouds, rain, snow, etc.)
89
+ * weather description: more detailed data
90
+ * temperature in Celsius or Fahrenheit
91
+ * air pressure
92
+ * wind speed and degrees
93
+
94
+ Weather data is queried from Open Weather Map. Depending on your websites traffic, additional fees may apply:
95
+ http://openweathermap.org/price
96
+
97
+ An (free) API key from OpenWeatherMap is required for this feature to work.
98
+
99
+ ipstack.com is used to determine the site visitor's location. A (free) API key from IPStack.com is required for this feature to work:
100
+ https://ipstack.com/product
101
+
102
+ = Media player events =
103
+
104
+ (experimental)
105
+
106
+ Track users' interaction with any embedded media:
107
+
108
+ * YouTube
109
+ * Vimeo
110
+ * Soundcloud
111
+
112
+ DataLayer events can be chosen to fire upon media player load, media is being played, paused/stopped and optionally when
113
+ the user reaches 10, 20, 30, ..., 90, 100% of the media duration.
114
+
115
+ Tracking is supported for embedded media using the built-in oEmbed feature of WordPress as well as most other media plugins
116
+ and copy/pasted codes. Players injected into the website after page load are not currently supported.
117
+
118
+ = Scroll tracking =
119
+
120
+ Fire tags based on how the visitor scrolls from the top to the bottom of a page.
121
+ An example would be to separate "readers" (who spend a specified amount of time on a page) from "scrollers"
122
+ (who only scroll through within seconds). You can use these events to fire Analytics tags and/or remarketing/conversion tags
123
+ (for micro conversions).
124
+
125
+ Scroll tracking is based on the solution originally created by
126
+
127
+ * Nick Mihailovski
128
+ * Thomas Baekdal
129
+ * Avinash Kaushik
130
+ * Joost de Valk
131
+ * Eivind Savio
132
+ * Justin Cutroni
133
+
134
+ Original script:
135
+ http://cutroni.com/blog/2012/02/21/advanced-content-tracking-with-google-analytics-part-1/
136
+
137
+ = Google Ads remarketing =
138
+
139
+ Google Tag Manager for WordPress can add each dataLayer variable as a Google Ads remarketing custom parameter list.
140
+ This enables you to build sophisticated remarketing lists.
141
+
142
+ = Blacklist & Whitelist Tag Manager tags, triggers and variables =
143
+
144
+ To increase website security, you have the option to white- and blacklist tags/triggers/variables.
145
+ You can prevent specific tags from firing or the use of certain variable types regardless of your GTM setup.
146
+
147
+ If the Google account associated with your GTM account is being hacked, an attacker could easily
148
+ execute malware on your website without accessing its code on your hosting server. By blacklisting custom HTML tags
149
+ and/or custom JavaScript variables you can secure the Tag Manager container.
150
+
151
+ = Integration =
152
+
153
+ Google Tag Manager for WordPress integrates with several popular plugins. More integration to come!
154
+
155
+ * Contact Form 7: fire an event when a Contact Form 7 form was submitted with any result (mail sent, mail failed, spam detected, invalid input)
156
+ * WooCommerce:
157
+ * Classic e-commerce (deprecated):
158
+ * fire an event when visitors add products to their cart
159
+ * capture transaction data to be passed to your ad platforms and/or Analytics
160
+ * capture necessary remarketing parameters for Google Ads Dynamic Remarketing
161
+ * Enhanced e-commerce:
162
+ * implementation of [Enhanced E-commerce GA3](https://developers.google.com/tag-manager/enhanced-ecommerce)
163
+ * implementation of [Enhanced E-commerce GA4](https://developers.google.com/tag-manager/ecommerce-ga4)
164
+ * Does not support promotions since WooCommerce does not have such a feature (yet)
165
+ * Does not support refunds
166
+ * Google Optimize: load your Google Optimize container directly from your website with the ability to use the data layer variables provided during page load
167
+ * AMP: load your AMP container on the AMP version of your pages
168
+
169
+ = Server side containers =
170
+
171
+ If you are using a [server side container](https://developers.google.com/tag-manager/serverside/send-data#update_the_gtmjs_source_domain)
172
+ you can enter your custom domain name to load gtm.js from your there.
173
+
174
+ == Installation ==
175
+
176
+ 1. Upload `duracelltomi-google-tag-manager-for-wordpress` to the `/wp-content/plugins/` directory
177
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
178
+ 1. Go to Settings / Google Tag Manager and enter your Google Tag Manager container ID and set additional options
179
+
180
+ == Frequently Asked Questions ==
181
+
182
+ = How can I ... =
183
+
184
+ Tutorials for various Google Tag Manager settings and implementation are available on my website:
185
+ https://gtm4wp.com/how-to-articles/
186
+
187
+ = PayPal / 3rd party payment gateway transactions in WooCommerce are not being tracked in Google Analytics =
188
+
189
+ PayPal and some other 3rd party payment gateways do not redirect users back to your website upon successful transaction by default.
190
+ It offers the route back for your customer but it can happen that users close the browser before arriving at your thankyou page
191
+ (aka. order received page). This means that neither Google Analytics tags or any other tags have the chance to fire.
192
+
193
+ Enable auto-return in your payment gateway settings. This will instruct them to show a quick info page after payment
194
+ and redirect the user back to your site. This will improve the accuracy and frequency of tracked transactions.
195
+
196
+ = Why isn't there an option to blacklist tag/variable classes =
197
+
198
+ Although Google recommends to blacklist tags and variables using classes, people struggle to know
199
+ which tags/variables gets affected. Therefore I opted for individual tags and variables rather than classes
200
+ on the blacklist tabs.
201
+
202
+ Regarding variables; ensure they are not part of any critical tags as blacklisting such variables will render said tags useless.
203
+
204
+ = How can I track scroll events in Google Tag Manager? =
205
+
206
+ Google Tag Manager supports basic scroll depth tracking based on percentage or pixels natively. This plugin adds
207
+ additional scroll tracking events, more focused on capturing the users' intent and/or engagement.
208
+
209
+ There are five dataLayer events you can use in your rule definitions:
210
+
211
+ * gtm4wp.reading.articleLoaded: the content has been loaded
212
+ * gtm4wp.reading.startReading: the visitor started to scroll. The `timeToScroll` dataLayer variable stores duration since the article loaded (in seconds)
213
+ * gtm4wp.reading.contentBottom: the visitor reached the end of the content (not the page!). `timeToScroll` dataLayer variable updated
214
+ * gtm4wp.reading.pagebottom: the visitor reached the end of the page. `timeToScroll` dataLayer variable updated
215
+ * gtm4wp.reading.readerType: based on time spent since article loaded we determine whether the user is a 'scanner' or 'reader' and store this in the `readerType` dataLayer variable
216
+
217
+ Example use cases: using these events as triggers, you can fire Google Universal Analytics and/or Google Ads remarketing/conversion tags
218
+ to report micro conversions and/or to serve ads only to visitors who spend more time reading your content.
219
+
220
+ = Can I exclude certain user roles from being tracked? =
221
+
222
+ This is easily managed through GTM itself. If you want to exclude logged in users or users with certain user roles,
223
+ use the corresponding dataLayer variable (visitorType) and an exclude filter in Google Tag Manager.
224
+
225
+ https://gtm4wp.com/how-to-articles/how-to-exclude-admin-users-from-being-tracked/
226
+
227
+ == Screenshots ==
228
+
229
+ 1. Admin panel
230
+ 2. Basic settings
231
+ 3. Events
232
+ 4. Integration panel
233
+ 5. Advanced settings
234
+ 6. Scroll tracking
235
+
236
+ == Changelog ==
237
+
238
+ = 1.16 =
239
+
240
+ This plugin version does not add or update any functionality.
241
+ After recent events, the code of the plugin has been checked line by line to see where additional security checks can be added.
242
+ The code has been formatted to better support readability for other programmers.
243
+
244
+ Deprecated:
245
+ * gtm4wp_get_the_gtm_tag hook and the corresponding GTM4WP_WPFILTER_GETTHEGTMTAG PHP constant.
246
+ * gtm4wp_add_global_vars hook and the corresponding GTM4WP_WPFILTER_ADDGLOBALVARS PHP constant. Use gtm4wp_add_global_vars_array / GTM4WP_WPFILTER_ADDGLOBALVARS_ARRAY instead.
247
+ * gtm4wp_after_datalayer hook and the corresponding GTM4WP_WPACTION_AFTER_DATALAYER PHP constant. Use gtm4wp_output_after_datalayer / GTM4WP_WPACTION_AFTER_DATALAYER instead witch can be used in the same way but it is an action instead of a filter.
248
+
249
+ Upcoming version will come with important changes:
250
+ * Minimum PHP version will be raised to 7.4: this will allow me to add even more safety measures
251
+ * Minimum supported WooCommerce version will be raised to WooCommerce 5.0: with this I can remove some very old compatibility code
252
+ * Deprecated features will be removed (aims to simplify code for better maintenance):
253
+ * Do not track flag of the browser added into data layer
254
+ * Legacy version of WooCommerce dynamic remarketing (using ecomm_ parameters)
255
+
256
+ The goal of all these changes aim to keep the plugin code clean and free from legacy solutions.
257
+
258
+ = 1.15.2 =
259
+
260
+ * Fixed: Stored XSS when using the scroll tracking feature and an admin changes the content element ID into a JavaScript code.
261
+ * Deprecated option: 'do not track' flag of the browser. This browser feature itself [is now deprecated](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT)
262
+
263
+ Full scan of the plugin is also in works to fix any other possible XSS issue.
264
+
265
+ = 1.15.1 =
266
+
267
+ * Fixed: JavaScript error with the newly added console logging to debug code placement issues
268
+ * Fixed: possible XSS Vulnerability if Cloudflare country code option enabled. Thanks [Guillaume Fortier](https://www.linkedin.com/in/guillaume-f-a728711b0/)
269
+ * Fixed: proven XSS Vulnerability if adding site search into the data layer was enabled. Original report by [not_stoppable](https://hackerone.com/not_stoppable?type=user). Root cause analysis by [Cory Buecker](https://www.linkedin.com/in/corybuecker/).
270
+ * Removed: deprecated feature Google Ads remarketing. This is the outdated, classic way using the google_tag_params variable.
271
+ * Dev: removed PHP constant GTM4WP_WPFILTER_COMPILE_REMARKTING (related to removed Google Ads remarketing feature)
272
+ * Dev: removed gtm4wp_compile_remarkering WordPress filter (related to removed Google Ads remarketing feature)
273
+
274
+ Note to plugin users: I sincerely appologize for the vulnerabilities. To make sure, such cases do not happen again,
275
+ the next version will be fully dedicacted to go through every peace of code and make sure proper data processing is happening in GTM4WP.
276
+
277
+ = 1.15 =
278
+
279
+ * Added: pagePostType data layer variable will now return 404-error on 404 pages and search-results on search result pages
280
+ * Added: Google Tag Manager container code can be disabled for specific WordPress user roles under Advanced plugin options. A browser console warning will be shown in such cases to prevent confusion
281
+ * Added: support for all Contact Form 7 events for more granual tracking: gtm4wp.contactForm7MailSent, gtm4wp.contactForm7MailFailed, gtm4wp.contactForm7SpamDetected, gtm4wp.contactForm7InvalidInput
282
+ * Added: additional data layer variables for date attributes: pagePostDateDayName, pagePostDateHour, pagePostDateMinute, pagePostDateIso, pagePostDateUnix - by [ajtatum](https://github.com/ajtatum)
283
+ * Fixed: unclickable products in WooCommerce product lists in Firefox when visiting site in Strict privacy mode or using private browsing
284
+ * Fixed: tracking step 2 on WooCommerce checkout page was broken
285
+ * Updated: removed CDATA blocks as they are not required in simple HTML and they break some cases where code optimizer is being used
286
+ * Updated: products per impression in WooCommerce integration now defaults to 10 instead of 0. This allows view_item_list event to fire on new sites as well
287
+ * Updated: code placement options. Separated container on/off option and replaced code placement with the new terminology: compatibility mode
288
+ * Updated: removed optional chaining operator usage (?.) in JavaScript codes for better compatibility with outdated browsers
289
+ * Updated: changed 'Do not flag orders as being tracked' description to be more precise about what happens if turned on or left off
290
+ * Updated: if you enter your custom domain name for server side tagging with the https:// prefix, it will be removed before domain name validation
291
+ * Updated: all script blocks to be ignored by Cookiebot if this integration is enabled
292
+ * Updated: do not track WooCommerce order where payment failed
293
+
294
+ = 1.14.2 =
295
+
296
+ * Fixed: undefined google_business_vertical
297
+ * Fixed: missing product price in product impression data
298
+ * Fixed: better compatibility with cache plugins and lazy load functionalities
299
+ * Fixed: Added optional chaining operator to form move tracker code
300
+
301
+ = 1.14.1 =
302
+
303
+ * Bugfixes
304
+
305
+ = 1.14 =
306
+
307
+ * Added: support for tracking WooCommerce Block based product lists, except the "All Products" block
308
+ * Added: support for [new_customer parameter](https://support.google.com/google-ads/answer/9917012?hl=en-AU#zippy=%2Cinstall-with-google-tag-manager) for Google Smart Shopping campaigns
309
+ * Added: SHA256 hashed versions of data layer variables containing email addresses: customerBillingEmailHash on WooCommerce order received pages and visitorEmailHash on generic uses cases
310
+ * Added: WooCommerce - if for some reason is_order_received_page() reports false on the order received page, woocommerce_thankyou hook will be used as backup
311
+ * Updated: removed jQuery dependency from plugin modules: contact form 7 integration, form move tracker, Vimeo, YouTube, Soundcloud, partly WooCommerce
312
+ * Updated: moved the hidden helper span element in products lists to the end of the product box to make more compatible with themes
313
+ * Updated: more consistent retrieval of product categories - by [Dekadinious](https://github.com/Dekadinious)
314
+ * Updated: gtm4wp_product_readded_to_cart cookie replaced with a WooCommerce session variable to use fewer cookies in this plugin
315
+ * Updated: gtm4wp_user_logged_in, gtm4wp_user_registered and gtm4wp_last_weatherstatus cookies are now HTTP only cookies
316
+ * Updated: replaced deprecated jQuery method and event usage in WP admin
317
+ * Updated: added rel="noopener" to links pointing to external sites on WP admin page
318
+ * Updated: Hiding the iframe tag from assistive technologies as it provides no functionality for the end user. This will also alleviate a11y audit warnings.
319
+ * Fixed: proper values for visitorType data layer variable
320
+ * Fixed: replaced unsafe usage of eval() in WooCommerce QuickView plugin integration
321
+ * Fixed: type check of the order ID obtained from a cookie before using the value
322
+ * Fixed: navigation issues in Safari if browser loads previous page from cache. GTM4WP will now force Safari to always reload pages.
323
+ * Fixed: Do not trigger browser change event in WooCommerce checkout page submit event handler. It caused issues with other 3rd party plugins.
324
+ * Fixed: HTML5 detection. - by [Sjoerd](https://github.com/sjoerdkoelewijn)
325
+ * Fixed: Username not included in datalayer if no other user attribute is included - by [StaymanHou](https://github.com/StaymanHou)
326
+
327
+ = 1.13.1 =
328
+
329
+ * Fix: better PHP8 compatibility
330
+ * Fix: PHP notice on admin page
331
+
332
+ = 1.13 =
333
+
334
+ WARNING!
335
+ If you are using the geo or weather options of this plugin, make sure your hosting is using PHP 7.0 or newer!
336
+
337
+ If you are using the WooCommerce integration with enhanced ecommerce, once again you will need to update your GTM container.
338
+ Please check the [setup article](https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking) to see what needs to be changed.
339
+ The goal is to keep this setup in the upcoming versions.
340
+
341
+ * Added: Google Business Vertical option to populate Google Ads dynamic remarketing
342
+ * Added: Make automatic cookie blocking mode of Cookiebot to work with your Google Tag Manager container (new option in the Integration tab)
343
+ * Added: support for [server side containers with custom domains](https://developers.google.com/tag-manager/serverside/send-data#update_the_gtmjs_source_domain)
344
+ * Added: improved duplacate WooCommerce order tracking prevention by also checking the age of the order. You can adjust the value in minutes on the plugin options page - by [Code-Craze](https://github.com/Code-Craze)
345
+ * Fixed: prices rounded to 2 decimals in the data layer (WooCommerce integration)
346
+ * Fixed: select_item event fired multiple times (at least twice)
347
+ * Fixed: frontend protection to preveent double tracking transactions had a bug
348
+ * Fixed: missing product detail and first checkout step tracking in WooCommerce shops with Enhanced Ecommerce
349
+ * Fixed: JavaScript type error if product category is a number
350
+ * Updated: WhichBrowser v2.1.1 (requires PHP 7.0 or newer)
351
+ * Updated: descriptions of WooCommerce tracking methods to emphasize the importance to migrate from standard to enhanced ecommerce
352
+ * Updated: added links to enhanced ecommerce setup guides (GA3 and GA4)
353
+
354
+ = 1.12.3 =
355
+
356
+ No new or updated functionality, but updated WooCommerce compatibility.
357
+
358
+ IMPORTANT!
359
+ If you are using the WooCommerce integration and enhanced ecommerce, please update your ecommerce event trigger to include gtm4wp.orderCompletedEEC as well.
360
+ https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking
361
+
362
+ = 1.12.2 =
363
+
364
+ If you are upgrading from 1.11.x, please read the previous changelog entry for v1.12
365
+
366
+ * Fixed: error message in WooCommerce integration on PHP 8 hosts
367
+ * Fixed: PHP notice about non existing blacklist-enable array key
368
+
369
+ = 1.12.1 =
370
+
371
+ If you are upgrading from 1.11.x, please read the previous changelog entry for v1.12
372
+
373
+ * Fixed: PHP notice while saving admin options
374
+
375
+ = 1.12 =
376
+
377
+ WARNING!
378
+ If you are using the blacklist/whitelist feature of the plugin, review these options after upgrade as they could break because of a fundamental rework of this feature.
379
+
380
+ * Added: support for Google Analytics 4 version of ecommerce data layer
381
+ * Fixed: safer IP address validation in geo and weather data features
382
+ * Updated: removed deprecated events: download links, email links, social links -> such can be now tracked with native Google Tag Manager triggers
383
+ * Updated: removed support for WooCommerce versions before v3.2
384
+ * Updated: 'Blacklist tags' tab renamed to 'Security'
385
+ * Updated: complete rework of blacklist/whitelist feature to use the latest tag/trigger/variable list of Google
386
+ * Deprecated: classic ecommerce tracking will be removed later this year, please upgrade to enhanced ecommerce tracking
387
+ * Deprecated: standard Google Ads remarketing variable will be removed soon as the Google Ads remarketing tag template can easily use any of your Google Tag Manager variables
388
+ * Deprecated: the old fashioned way of using Google Ads remarketing with the ecomm_ prefixed data layer variables will be removed soon. Instructions for upgrade will be published on gtm4wp.com once this feature gets updated in an upcoming plugin version
389
+
390
+ = 1.11.6 =
391
+
392
+ * Fixed: do not track hidden products in the cart in WooCommerce shops
393
+ * Fixed: do not fire add to cart event if button is in a disabled state
394
+ * Fixed: fixed translation of an admin text
395
+ * Fixed: needs_shipping_address() calls were sometimes broken in WooCommerce shops, added additional checks to prevent
396
+ * Updated: removed the body_class method of adding the iframe/noscript container code (page builders and the standard wp_body_open hook remains supported)
397
+
398
+ = 1.11.5 =
399
+
400
+ * Fixed: new Google Optimize container ID format accepted now
401
+
402
+ = 1.11.4 =
403
+
404
+ * Fixed: fire gtm4wp.checkoutStepEEC and gtm4wp.checkoutOptionEEC events if there is only one shipping method available and it is hidden from the user
405
+ * Fixed: decrease checkout step numbers 3 and 4 if shipping destination is set to 'Force shipping to the customer billing address' in WooCommerce. This way there will be no gap in Checkout behaviour report in Google Analytics.
406
+ * Updated: tested version number for WooCommerce
407
+
408
+ = 1.11.3 =
409
+
410
+ * Fixed: use var_export instead of var_dump in some debug code,
411
+ * Fixed: apply WooCommerce option for tax inclusion on the order received page as well
412
+ * Fixed: JavaScript errors in Vimeo player
413
+ * Updated: do not add type attribute to script elements if theme suppors HTML5
414
+ * Updated: tested version numbers for WordPress and WooCommerce
415
+
416
+ = 1.11.2 =
417
+
418
+ WARNING!
419
+ If you are upgrading directly from v1.10.x, please read the changelog of v1.11 since it includes many important notices!
420
+
421
+ No change in plugin code, WP.org deployment of v1.11.1 included wrong directories
422
+
423
+ = 1.11.1 =
424
+
425
+ WARNING!
426
+ If you are upgrading directly from v1.10.x, please read the changelog of v1.11 since it includes many important notices!
427
+
428
+ * Fixed: PHP notice about undefined order_items variable if the new 'Order data in data layer' is turned off
429
+ * Fixed: PHP notice about missing brand array key if no brand taxonomy is selected in GTM4WP options
430
+
431
+ = 1.11 =
432
+
433
+ WARNING!
434
+ Please read the changelog very carefully as there are many important changes and removed features which could need your attention before updating!
435
+
436
+ * Added Oxygen Builder and Beaver Builder Theme support - you can now use the codeless placement option without issues
437
+ * Added ability to fix the Google Tag Manager ID and GTM Environment parameters in wp-config.php. To use it, create PHP constants with the names
438
+ ** GTM4WP_HARDCODED_GTM_ID
439
+ ** GTM4WP_HARDCODED_GTM_ENV_AUTH
440
+ ** GTM4WP_HARDCODED_GTM_ENV_PREVIEW
441
+ * Added support for WooCommerce Grouped Products
442
+ * Added new WooCommerce option to add all order data into the data layer on the order reveived page
443
+ ** This includes personal data of the customer -> you need to ensure this is used in a privacy friendly and compliant way!
444
+ ** This order data will be always present on the order received page, even if the page is reloaded or later revisited!
445
+ * Removed several unofficial data layer variables on the WooCommerce order received page as they can be read using the new order data option
446
+ ** transactionDate
447
+ ** transactionType
448
+ ** transactionPaymentType
449
+ ** transactionShippingMethod
450
+ ** transactionPromoCode
451
+ * Improved: price reporting with the WooCommerce enhanced ecommerce integration now follows the option set with the 'Display prices in the shop' option of WooCommerce
452
+ * Improved: from WooCommerce 3.7 WC_Abstract_Order::get_used_coupons() was replaced with WC_Abstract_Order::get_coupon_codes() which is now used if WC 3.7+ is detected
453
+ * Improved: use localStorage for WooCommerce duplicate transaction tracking prevention if available. Should be work with Safari at least for now.
454
+ * Fixed: WooCommerce duplicate transaction tracking prevention's cookie was set to expire on session end, now adds 1 year.
455
+ * Deprecated data layer variable productIsVariable. Use the new productType data layer variable which will equal to simple, variable, grouped or external depending on the type of the product shown
456
+ * Fixed: Wrong lookup for product brand name if Use SKU instead of product ID option was turned on
457
+ * Fixed: Wrong lookup for product brand name for variable products
458
+ * Fixed: check if $woo->customer is initialized
459
+ * Fixed: no checkout step reported on WooCommerce checkout page if the user has accepted the default selection of the payment and shipping methods
460
+
461
+ ! Planned deprecation of support for WooCommerce 2.x-3.1.x with next plugin version !
462
+ ! Planned deprecation of support for WordPress 4.x with next plugin version !
463
+
464
+ = 1.10.1 =
465
+
466
+ * Fixed: wrong cookie name was used with the newly introduced double transaction tracking protection while setting the cookie
467
+ * Fixed: double transaction tracking JavaScript code is now only included on the order received page
468
+ * Fixed: product impressions not properly reported if Products per impressions were set to 0
469
+ * Fixed: replaced all references to AdWords to Google Ads
470
+
471
+ = 1.10 =
472
+
473
+ * Added: Automatically add the noscript part of the container code after the opening body tag for WordPress 5.2+ sites where themes support the new wp_body_open action
474
+ * Added: add associated taxonomy values for post type
475
+ * Added: select brand taxonomy for WooCommerce products to populate "Product brand" dimension in enhanced ecommerce
476
+ * Added: add cart content into data layer so that you can personalize your site experience using Google Optimize
477
+ * Added: option to remove shipping costs from revenue data on order received page of WooCommerce
478
+ * Added: if you enable either enhanced ecommerce or just Google Ads remarketing variables, 3 new data layer variables will be also available about the product on a detail page
479
+ * Product rating details (productRatingCounts)
480
+ * Average product rating (productAverageRating)
481
+ * Review count (productReviewCount)
482
+ * Added: if you are using Cloudflare, you can now add the country code HTTP header value into the data layer and read from it with the geoCloudflareCountryCode variable name
483
+ * Fixed: better compatibility with Google's mod_pagespeed
484
+ * Fixed: missing product quantity while adding a variable product into the cart
485
+ * Fixed: prevent multiple tracking of WooCommerce orders on mobile devices where the mobile browser reloads the order received page from local cache executing GTM tracking again
486
+
487
+ = 1.9.2 =
488
+
489
+ * Fixed: possible PHP warning if geo data or weather data feature is turned on
490
+
491
+ = 1.9.1 =
492
+
493
+ * Fixed: handle out of quota cases with ipstack queries properly
494
+ * Fixed: proper YouTube tracking for WordPress sites and WordPress multisites installed in a subdirectory
495
+ * Fixed: properly detect client IP address and also properly escape this data while using it
496
+ * Fixed: WooCommerce checkout steps after page load did not include products in the cart
497
+ * Fixed: checkout step events for payment mode and shipping type not always fired
498
+ * Fixed: the CMD on Mac will be treated just like the Ctrl key on Windows while processing the product click event in the WooCommerce integration (thy for luzinis)
499
+ * Fixed: add currencyCode to every ecommerce action in WooCommerce integration
500
+ * Fixed: better WooCommere Quick View integration
501
+ * Fixed: possible cross site scripting vulnerability if site search tracking was enabled due to not properly escaped referrer url tracking
502
+ * Changed: code cleanup in WooCommerce integration
503
+
504
+ = 1.9 =
505
+
506
+ * Added: initial support for AMP plugin from Automattic (thx koconder for the contribution!)
507
+ * Added: option to remove tax from revenue data on order received page of WooCommerce
508
+ * Added: WooCommerce enhanced ecommerce datasets now include stock levels
509
+ * Added: new productIsVariable data layer variable is set to 1 on variable WooCommerce product pages
510
+ * Added: product impressions can now be split into multiple chunks to prevent data loss on large product category and site home pages (thx Tim Zook for the contribution!)
511
+ * IMPORTANT! You will need to update your GTM setup, please read the new Step 9 section of the [setup tutorial page](https://gtm4wp.com/how-to-articles/how-to-setup-enhanced-ecommerce-tracking).
512
+ * Added: you can now disable flagging of WooCommerce orders as being already tracked once. In same cases (with iDeal for example) you may need this to make purchase tracking to work.
513
+ * Added: uninstalling the plugin will now remove configured plugin options from database
514
+ * Added: new advanced plugin option: data layer variable visitorDoNotTrack will include 1 if the user has set the [do not track flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT) in his/her browser
515
+ * Added: new data layer event when a user has logged in on the frontend: gtm4wp.userLoggedIn
516
+ * Added: new data layer event when a new user has registered on the frontend: gtm4wp.userRegistered
517
+ * Added: new advanced plugin option: move data layer declaration and Google Tag Manager container as close as possible to the beginning of the HTML document
518
+ * Added: better WP Rocket support
519
+ * Updated: Full Google Optimize support. Now the plugin can load your Google Optimize container with the [recommended code placement](https://support.google.com/optimize/answer/7359264?hl=en)
520
+ * Updated: moved most of the inline JavaScript codes into separate .js files which should help cache plugins to do their job much better when my plugin is active
521
+ * Fixed: wrong ecomm_pagetype on product search result pages
522
+ * Fixed: PHP notice in some cases when geo data was not loaded properly
523
+ * Fixed / Added: freegeoip.net was rebranded to ipstack.com and an API key is needed now even for free usage. You can now add your API key so that weather data and geo data can be added into the data layer
524
+ * Warning: some plugin features will be remove from v1.10, most of them can be tracked now using pure Google Tag Manager triggers:
525
+ * Social actions
526
+ * Outbound link click events
527
+ * Download click events
528
+ * Email click events
529
+ * Warning: PHP 5.6 is now the minimum recommended version to use this plugin. I advise to move to PHP 7.x
530
+
531
+ = 1.8.1 =
532
+
533
+ * Added: new visitorIP data layer variable to support post-GDPR implementations where for example internal traffic exclusion has to be made inside the browser
534
+ * Fixed: JavaScript error around the variable gtm4wp_use_sku_instead
535
+ * Fixed: added _ as a valid character for gtm_auth GTM environment variable
536
+ * Fixed: corrected typo - gtm4wp.checkoutStepE**E**C
537
+ * Fixed: two strings were not recognized by WordPress Translate on the admin page
538
+ * Fixed: some other plugins call found_variation event of WooCommerce without product variation data being included
539
+ * Fixed: product name included variation name on order received page which broke GA product reports
540
+ * Fixed: in some cases, no contact form 7 data was being passed to the gtm4wp.contactForm7Submitted event
541
+ * Updated: added CDATA markup around container code for better DOM compatibility
542
+ * Updated: removed 'SKU:' prefix text from classic ecommerce dimension as it broke some enhanced ecommerce reports
543
+
544
+ = 1.8 =
545
+
546
+ * Fixed: weather data tracking codes could result in fatal PHP error
547
+ * Fixed: cart events did to fire while user pressed the Enter key in quantity fields
548
+ * Fixed: contact form 7 changed some code which prevented successful form submission tracking
549
+ * Changed: links to plugin website updated
550
+ * Changed: gtm4wp.cf7formid data layer variable now includes the ID of the form in WordPress
551
+ * Added: gtm4wp.cf7inputs includes data that has been filled in the form
552
+ * Added: [WooCommerce compatibility headers](https://docs.woocommerce.com/document/create-a-plugin/#section-10)
553
+ * Added: admin warning for WooCommerce 2.x users. This plugin will drop support for WooCommerce 2.x soon
554
+ * Added: postFormat data layer variable on singular pages
555
+ * Added: customer* data layer variables with stored billing and shipping data, total number of orders and total value of those orders (needs WooCommerce 3.x)
556
+ * Added: geo* data layer variables to get country, city, lat-lon coordinates of the visitor
557
+ * Added: visitorUsername data layer variable with the username of the logged in user
558
+ * Added: more detailed checkout reporting for WooCommerce sites
559
+ * Add gtm4wp.checkoutStepEEC to your Ecommerce Helper trigger
560
+ * Change a typo: gtm4wp.checkoutOptionE**C**C => gtm4wp.checkoutOptionE**E**C
561
+ * Added: option to include full product category path in enhanced ecommerce reporting (can cause performance issues on large sites!)
562
+ * Added: initial support for [Google Tag Manager Environments](https://support.google.com/tagmanager/answer/6311518?hl=en)
563
+ * Added: support for [WooCommerce Quick View plugin](https://woocommerce.com/products/woocommerce-quick-view/)
564
+ * Updated: description of code placement options to clarify what this option does
565
+ * Updated: cleanup of readme.txt, spelling and grammar improvements
566
+ * Updated: bundled WhichBrowser lib v2.0.32
567
+
568
+
569
+ = 1.7.2 =
570
+
571
+ * Fixed: in some cases, the remove item from cart link in a WooCommerce cart was not altered properly with additional tracking codes
572
+ * Fixed: product categories were empty in the cart, on the checkout pages and on the order received page for product variations
573
+ * Fixed: checkout option data included checkout step #1 if cart page was setup to be the first
574
+ * Fixed: even more WooCommerce 3.x compatibility
575
+ * Added: registration date of the logged in user can be added to the data layer
576
+ * Updated: geoplugin.net has been replaced by freegeoip.net for weather tracking which has far better quota for free usage
577
+ * Updated: Google Ads dynamic remarketing data layer items on a WooCommerce product page will be shown for the root product as well on variable product pages
578
+ * Updated: Selecting a product variation will include the price of the product in Google Ads dynamic remarketing data layer items
579
+ * Updated: minor code cleanup
580
+
581
+ = 1.7.1 =
582
+
583
+ * Fixed: PHP 5.3 compatible syntax in frontend.php
584
+ * Fixed: PHP error using classic ecommerce with WooCommerce 2.6.x
585
+ * Updated: Added data-cfasync='false' to all <script> elements to prevent CloudFlare to load scripts async
586
+ * Added: Warning for users of PHP 5.4 or older to consider upgrade (FYI: PHP 5.5 and older versions do not get even security fixes)
587
+
588
+ = 1.7 =
589
+
590
+ * Updated: even better WooCommerce 3.0 compatibility (WooCommerce 2.6 still supported but this support ends with the next plugin version)
591
+ * Fixed: properly escaping product category name on variable product detail pages
592
+ * Fixed: proper data layer stucture in the gtm4wp.changeDetailViewEEC event
593
+ * Added: Google Optimize page hiding snippet under Integrations tab
594
+ * Added: add to cart data for WooCommerce enhanced ecommerce tracking if user undos a cart item removal (no need to update GTM tags)
595
+ * Added: you can now enter a product ID prefix so that IDs can match with IDs in some product feeds generated by other plugins
596
+ * Added: option to track cart page as step 1 in enhanced ecommerce checkout funnel
597
+
598
+ = 1.6.1 =
599
+
600
+ * Fixed: PHP warning message on WooCommerce cart page
601
+ * Fixed: Better compatibility with WooCommerce 2.6.x :-)
602
+
603
+ = 1.6 =
604
+
605
+ * Fixed: do not block product list item clicks if ad blocker is enabled
606
+ * Fixed: only track product clicks in product lists if link points to the product detail page URL
607
+ * Fixed: PHP warning in backlogs 'Undefined variable: gtm4wp_options'
608
+ * Added: product variation support in WooCommerce integration (enhanced ecommerce implementations should add the GTM event gtm4wp.changeDetailViewEEC to the ecommerce event trigger)
609
+ * Updated: better WooCommerce 3.0 compatibility
610
+
611
+ = 1.5.1 =
612
+
613
+ * Fixed: clicks on products in product list pages redirected to undefined URLs with some themes.
614
+
615
+ = 1.5 =
616
+
617
+ Lots of WooCommerce ecommerce codes has been changed and extended, please double check your measurement after upgrading to this version!
618
+
619
+ * Added: warning message if you are using PHP 5.3 or older. Browser/OS/Device tracking needs 5.4 or newer
620
+ * Added: Email address of the logged in user into the visitorEmail dataLayer variable. Remember: to comply with GTM TOS you are not allowed to pass this data towards any Google tag but you can use this in any other 3rd party tag.
621
+ * Added: gtm4wp_eec_product_array WordPress filter so that plugin and theme authors can add their own data for enhanced ecommere product arrays
622
+ * Fixed: JavaScript error in WooCommerce stores when enhanced ecommerce enabled and a product being clicked in a widget area
623
+ * Fixed: Order data not present in some cases on the order received page
624
+ * Changed: Extended "User SKUs instead of IDs for remarketing" option to be also applied to ecommerce product data arrays
625
+ * Changed: Use wc_clean instead of the deprecated function woocommerce_clean
626
+ * Changed: New, divided GTM container implemented - a fixed part in the <head> and an iframe part placed using the container placement option you've set earlier
627
+
628
+ = 1.4 =
629
+
630
+ * Fixed: WP CLI error message
631
+ * Fixed: wrong dynamic remarketing tagging on cart and checkout pages
632
+ * Updated: WhichBrowser library to 2.0.22
633
+ * Updated: slightly changed container code snippet to prevent W3 Total Cache to alter the code which breaks proper code execution
634
+ * Updated: replaced file_get_contents() usage in weather tracking to wp_remote_get() so that it is more compatible with several WP instances
635
+ * Updated: YouTube/Video/Soundcloud tracking now tracks videos not embedded using oEmbed (like videos in a widget area)
636
+ * Updated: new Vimeo Player API implemented which should solve several issues
637
+ * Changed: adapted W3C HTML5 media player event names which changes some events (needs updating your existing GTM setup):
638
+ * Soundcloud: finish => ended, seek => seeked
639
+ * YouTube: playing => play, paused => pause, playback-rate-change => ratechange
640
+ * Vimeo: seek => seeked
641
+ * Added: new placement option - 'off'. This will only generate the data layer but you will need to add the proper GTM container code snippet by hand
642
+ * Added: new data layer variable: authorID
643
+ * Added: new data layer variable: siteID to be able to track based on blog ID in a multisite environment
644
+ * Added: new data layer variable: siteName to be able to track in a multisite environment
645
+
646
+ = 1.3.2 =
647
+
648
+ * Fixed: remove cart event not fired in WooCommerce 2.6
649
+ * Fixed: ecomm_prodid.push error message on product detail pages
650
+ * Fixed: proper tracking of cart actions on the cart page for WooCommerce 2.6
651
+ * Fixed: 'Illegal string offset' errors in some cases in the cart
652
+ * Fixed: OpenWeatherMap requires a (free) API key now, you can now enter this to use weather data in data layer
653
+
654
+ = 1.3.1 =
655
+
656
+ * Fixed: "json_encode() expects parameter 2 to be long, string given" on PHP 5.3 instances
657
+ * Fixed: Fatal PHP error in cart if you enabled taxes to be included in your cart
658
+
659
+ = 1.3 =
660
+
661
+ Major changes to the Enhanced Ecommerce implementation of the WooCommerce integration!
662
+
663
+ * Fixed: proper tracking of list positions
664
+ * Fixed: opening product detail page in a new window/tab when user pressed the CTRL key
665
+ * Fixed: ecomm_totalvalue included the total price of the cart without taxes
666
+ * Fixed: ecomm_totalvalue does not take into account the quantity of ordered products on the order received page
667
+ * Fixed: php error message on product lists when Google Ads dynamic remarketing was enabled on WooCommerce 2.6
668
+ * Fixed: added data-cfasync="false" to the GTM container code for better compatibility with CloudFlare
669
+ * Added: introducing tracking of list names (general product list, recent products list, featured products list, etc.)
670
+ * Some list names (like cross-sells) will be shown as 'General Product List'. A proposed change in WooCommerce 2.6 will solve that issue
671
+ * Added: tracking product lists in widgets
672
+ * Added: tracking checkout options (payment and shipment)
673
+ * Updated: better add-to-cart / remove-from-cart management in mini cart and while updating cart content
674
+ * Updated: added currency code to each enhanced ecommerce call so that currency reporting is OK for multi currency sites
675
+ * Updated: replaced usage of get_currentuser() to keep compatibility with WordPress 4.5
676
+
677
+ = 1.2 =
678
+
679
+ * Fixed: subtabs on admin page now showing in certain cases
680
+ * Fixed: error message when running the site using WP CLI (thanks Patrick Holberg Hesselberg)
681
+ * Fixed: some typos on admin page
682
+ * Fixed: dismissable notices did not disappear in some cases
683
+ * Fixed: tracking of Twitter event cased sometimes JS errors
684
+ * Fixed: site search tracking caused sometimes PHP errors when HTTP_REFERER was not set
685
+ * Updated: preparation for translate.wordpress.org
686
+ * Added: support for multiple container IDs
687
+ * Added: added form ID when sending a Contact Form 7 form. Variable name: gtm4wp.cf7formid
688
+
689
+ = 1.1.1 =
690
+
691
+ * Fixed: PHP errors in frontend.php and admin.php
692
+
693
+ = 1.1 =
694
+
695
+ * Added: track embedded YouTube/Vimeo/Soundcloud videos (experimental)
696
+ * Added: new checkbox - use product SKU for Google Ads Dynamic Remarketing variables instead of product ID (experimental)
697
+ * Added: place your container code after the opening body tag without modifying your theme files (thx Yaniv Friedensohn)
698
+ * Added: automatic codeless container code injection for Genesis framework users
699
+ * Fixed: Possible PHP error with custom payment gateway (QuickPay) on the checkout page (thx Damiel for findig this)
700
+
701
+ = 1.0 =
702
+
703
+ The plugin itself is now declared as stable. This means that it should work with most WordPress instances.
704
+ From now on each version will include features labeled as:
705
+
706
+ * Beta: the feature has been proven to work for several users but it can still have some bugs
707
+ * Experimental: new feature that needs proper testing with more users
708
+ * Deprecated: this feature will be removed in a future version
709
+
710
+ If you see any issue with beta or experimental functions just disable the checkbox. Using this error messages should disappear.
711
+ Please report all bugs found in my plugin using the [contact form on my website](https://gtm4wp.com/contact).
712
+
713
+ * Fixed: wrong GTM container code when renaming default dataLayer variable name (thx Vassilis Papavassiliou)
714
+ * Fixed: Enhanced Ecommerce product click data was "undefined" in some cases (thx Sergio Alen)
715
+ * Fixed: wrong user role detection while adding visitorType to the dataLayer (thx Philippe Vachon-Rivard)
716
+ * Changed: only add visitorId to the dataLayer if there is a logged in user
717
+ * Added: feature labels so that you can see beta, experimental and deprecated features
718
+ * Deprecated: outbound click, email click and download click events. You should use GTM trigger events instead
719
+
720
+ = 0.9.1 =
721
+
722
+ * Fixed: PHP error message: missing get_shipping function using WooCommerce 2.3.x
723
+
724
+ = 0.9 =
725
+
726
+ * Added: visitorId dataLayer variable with the ID of the currently logged in user to track userID in Google Analytics
727
+ * Added: WordPress filter hook so that other templates and plugins can get access to the GTM container code before outputting it
728
+ * Fixed: 'variation incorrect' issue by Sharken03
729
+ * Fixed: error messages in WooCommerce integration when product has no categories
730
+ * Fixed: add_inline_js errors in newer versions of WooCommerce
731
+ * Fixed: error message when some device/browser/OS data could not be set
732
+ * Fixed: tracking Twitter events was broken
733
+
734
+ = 0.8.2 =
735
+
736
+ * Fixed: broken links when listing subcategories instead of products (thanks Jon)
737
+ * Fixed: wheather/weather typo (thanks John Hockaday)
738
+ * Fixed: wrong usage of get_the_permalink() instead of get_permalink() (thanks Szepe Viktor)
739
+
740
+ = 0.8.1 =
741
+
742
+ * Fixed: PHP error in enhanced ecommerce implementation when using layered nav widget
743
+
744
+ = 0.8 =
745
+
746
+ * Updated: Added subtabs to the admin UI to make room for new features :-)
747
+ * Updated: WhichBrowser library to the latest version
748
+ * Added: You can now dismiss plugin notices permanently for each user
749
+ * Added: weather data. See updated plugin description for details
750
+ * Added: Enhanced E-commerce for WooCommerce (experimental!)
751
+ * Fixed: PHP notice in frontend.php script. Credit to Daniel Sousa
752
+
753
+ = 0.7.1 =
754
+
755
+ * Fixed: WooCommerce 2.1.x compatibility
756
+
757
+ = 0.7 =
758
+
759
+ * Updated/Fixed: dataLayer variables are now populated at the end of the head section. Using this the container code can appear just after the opening body tag, thus Webmaster Tools verification using Tag Manager option will work
760
+ * Added: blacklist or whitelist tags and macros to increase security of your Tag Manager setup
761
+
762
+
763
+ = 0.6 =
764
+
765
+ * Updated: better add-to-cart events for WooCommerce, it includes now product name, SKU and ID
766
+ * Added: browser, OS and device data to dataLayer variables
767
+ * Added: postCountOnPage and postCountTotal dataLayer variables to track empty categories/tags/taxonomies
768
+
769
+ = 0.5.1 =
770
+
771
+ * Fixed: WooCommerce integration did not work on some environments
772
+
773
+ = 0.5 =
774
+ * Added: scroll tracking
775
+ * Fixed: social tracking option on the admin panel was being shown as an edit box instead of a checkbox
776
+ * Fixed: WooCommerce transaction data was not included in the dataLayer if you selected "Custom" code placement
777
+ * Fixed: do not do anything if you enabled WooCommerce integration but did not activate WooCommerce plugin itself
778
+ * Updated: do not re-declare dataLayer variable if it already exists (because another script already created it before my plugin was run)
779
+
780
+ = 0.4 =
781
+ * Added: you can now select container code placement. This way you can insert the code snippet after the opening body tag. Please read FAQ for details
782
+ * Added: initial support for social event tracking for Facebook and Twitter buttons. Please read FAQ for details
783
+ * Updated: event name on successful WooCommerce transaction: OrderCompleted -> gtm4wp.orderCompleted
784
+ * Fixed: frontend JS codes did not load on some WordPress installs
785
+
786
+ = 0.3 =
787
+ * Updated: admin page does not show an alert box if Tag Manager ID or dataLayer variable name is incorrect. Instead it shows a warning line below the input field.
788
+ * Updated: rewritten the code for WooCommerce dynamic remarketing. Added tag for homepage and order completed page.
789
+
790
+ = 0.2 =
791
+ * ! BACKWARD INCOMPATIBLE CHANGE ! - Names of Tag Manager click events has been changed to comply with naming conventions:
792
+ * ContactFormSubmitted -> gtm4wp.contactForm7Submitted
793
+ * DownloadClick -> gtm4wp.downloadClick
794
+ * EmailClick -> gtm4wp.emailClick
795
+ * OutboundClick -> gtm4wp.outboundClick
796
+ * AddProductToCart -> gtm4wp.addProductToCart
797
+ * Updated: click events are now disabled by default to reflect recently released Tag Manager auto events. I do not plan to remove this functionality. You can decide which solution you would like to use :-)
798
+ * Updated: language template (pot) file and Hungarian translation
799
+ * Added: new form move events to track how visitors interact with your (comment, contact, etc.) forms
800
+ * Added: event names to admin options page so that you know what events to use in Google Tag Manager
801
+ * Added: Google Tag Manager icon to admin settings page
802
+ * Added: Settings link to admin plugins page
803
+ * Fixed: null value in visitorType dataLayer variable if no logged in user exists (now 'visitor-logged-out')
804
+
805
+ = 0.1 =
806
+ * First beta release
807
+
808
+ == Upgrade Notice ==
809
+
810
+ = 1.16 =
811
+
812
+ Maintenance release with lots of code updates without adding functionality.
813
+
814
+ = 1.15.2 =
815
+
816
+ Bugfix release
817
+
818
+ = 1.15.1 =
819
+
820
+ Bugfix release
821
+
822
+ = 1.15 =
823
+
824
+ Various updates, fixes and improvements
825
+
826
+ = 1.14.2 =
827
+
828
+ Bugfix release
829
+
830
+ = 1.14.1 =
831
+
832
+ Bugfix release
833
+
834
+ = 1.14 =
835
+
836
+ Initial support for WooCommerce Blocks, removed jQuery usage in JavaScript codes (except for WooCommerce related codes)
837
+
838
+ = 1.13.1 =
839
+
840
+ Bugfix release
841
+
842
+ = 1.13 =
843
+
844
+ Updated support for Google Ads dynamic remarketing with WooCommerce, added support for server side containers and Cookiebot + fixes
845
+
846
+ = 1.12.3 =
847
+
848
+ Updated compatible WooCommerce version and added an important message to the changelog for WooCommerce users
849
+
850
+ = 1.12.2 =
851
+
852
+ Bugfix release + read changelog for important v1.12.x changes
853
+
854
+ = 1.12.1 =
855
+
856
+ Bugfix release + read changelog for important v1.12.x changes
857
+
858
+ = 1.12 =
859
+
860
+ Removed several deprecated features, dropped support for WooCommerce versions before 3.2, introduced GA4 data layer variables, deprecated classic ecommerce and Google Ads remarketing varibale
861
+
862
+ = 1.11.6 =
863
+
864
+ Bugfix release
865
+
866
+ = 1.11.5 =
867
+
868
+ Fixed: new Google Optimize container ID format accepted now
869
+
870
+ = 1.11.4 =
871
+
872
+ Bugfix and updated tested version number for WooCommerce
873
+
874
+ = 1.11.3 =
875
+
876
+ Some bugfixes and adding support for the latest WordPress and WooCommerce version
877
+
878
+ = 1.11.2 =
879
+
880
+ Replaces v1.11.1
881
+
882
+ = 1.11.1 =
883
+
884
+ WooCommerce integration related fixes.
885
+
886
+ = 1.11 =
887
+
888
+ Please read the changelog very carefully as there are many important changes and removed features which could need your attention before updating!
889
+
890
+ = 1.10.1 =
891
+
892
+ Bugfix release
893
+
894
+ = 1.10 =
895
+
896
+ Better WordPress 5.2 integration, support for brands in WooCommerce, access cart content in data layer, more stable double transaction tracking prevention on mobiles and more!
897
+
898
+ = 1.9.2 =
899
+
900
+ Fixed possible PHP warning if geo data or weather data feature is turned on
901
+
902
+ = 1.9.1 =
903
+
904
+ Bugfix version
905
+
906
+ = 1.9 =
907
+
908
+ New AMP GTM support, full Google Optimize support, lots of WooCommerce tracking improvements.
909
+
910
+ = 1.8.1 =
911
+
912
+ Bugfix version fixing some issues around WooCommerce tracking and GTM environments. Also adds IP address into the data layer.
913
+
914
+ = 1.8 =
915
+
916
+ Lots of new features and some important changes, please read the changelog to ensure your measurement does not break
917
+
918
+ = 1.7.2 =
919
+
920
+ Bugfix release: many little fixes, event better WooCommerce 3.x compatibility
921
+
922
+ = 1.7.1 =
923
+
924
+ Bugfix release: better PHP 5.3 and WooCommerce 2.6.x compatibility
925
+
926
+ = 1.7 =
927
+
928
+ Better WooCommerce 3.x compatibility and new features
929
+
930
+ = 1.6.1 =
931
+
932
+ Bugfix release.
933
+
934
+ = 1.6 =
935
+
936
+ If you are using WooCommerce and enhanced ecommerce, please add gtm4wp.changeDetailViewEEC to the ecommerce helper trigger
937
+
938
+ = 1.5.1 =
939
+
940
+ Fixed: clicks on products in product list pages redirected to undefined URLs with some themes.
941
+
942
+ = 1.5 =
943
+
944
+ Lots of WooCommerce ecommerce codes has been changed and extended, please double check your measurement after upgrading to this version!
945
+
946
+ = 1.4 =
947
+
948
+ Several additions and fixes, breaking changes on media player tracking, please read changelog before upgrade
949
+
950
+ = 1.3.2 =
951
+
952
+ Quickfix release for 1.3.x: major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
953
+
954
+ = 1.3.1 =
955
+
956
+ Quickfix release for 1.3: major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
957
+
958
+ = 1.3 =
959
+
960
+ Major changes and improvements in the enhanced ecommerce implementation for WooCommerce. If you are already using this beta feature, please read the changelog before upgrading!
961
+
962
+ = 1.2 =
963
+
964
+ New release with lots of fixes from the past months and new features like multiple container support!
965
+
966
+ = 1.1.1 =
967
+
968
+ This is a bugfix release to address some issues with v1.1
969
+
970
+ = 1.1 =
971
+
972
+ New! Track popular media players embedded into your website!
973
+
974
+ = 1.0 =
975
+
976
+ First stable release, please read changelog for details!
977
+
978
+ = 0.9.1 =
979
+
980
+ Bugfix release for WooCommerce users with ecommerce tracking enabled
981
+
982
+ = 0.9 =
983
+
984
+ Many bug fixes, important fixes for WooCommerce users
985
+
986
+ = 0.8.2 =
987
+
988
+ Another bugfix release for WooCommerce users with Enhanced Ecommerce enabled
989
+
990
+ = 0.8.1 =
991
+
992
+ Bugfix release for WooCommerce users with Enhanced Ecommerce enabled
993
+
994
+ = 0.8 =
995
+
996
+ This version introduces Enhanced E-commerce implementation for WooCommerce. Please note that this
997
+ feature of the plugin is still experimental and the feature of Google Analytics is still in beta.
998
+ Read the plugin FAQ for details.
999
+
1000
+ = 0.7.1 =
1001
+
1002
+ If you are using WooCommerce and updated to 2.1.x you SHOULD update immediately.
1003
+ This release includes a fix so that transaction data can be passed to GTM.
1004
+
1005
+ = 0.7 =
1006
+
1007
+ Improved code so that Webmaster Tools verification can work using your GTM container tag.
1008
+ Blacklist or whitelist tags and macros to increase security of your Tag Manager setup.
1009
+ Fixed: WhichBrowser library was missing from 0.6
1010
+
1011
+ = 0.6 =
1012
+
1013
+ Improved add-to-cart events for WooCommerce, added browser/OS/device infos and post count infos.
1014
+
1015
+ = 0.5.1 =
1016
+
1017
+ Bug fix release for WooCommerce users.
1018
+
1019
+ = 0.5 =
1020
+ Besides of some fixes this version includes scroll tracking events for Google Tag Manager.
1021
+
1022
+ = 0.4 =
1023
+ Important change: Tag Manager event name of a WooCommerce successful order has been changed.
1024
+ See changelog for details.
1025
+
1026
+ = 0.3 =
1027
+ This is a minor release. If you are using WooCommerce you should update to include more accurate Google Ads dynamic remarketing feature.
1028
+
1029
+ = 0.2 =
1030
+ BACKWARD INCOMPATIBLE CHANGE: Names of Tag Manager click events has been changed to comply with naming conventions.
1031
+ See changelog for details. Do not forget to update your Tag Manager container setup after upgrading this plugin!
1032
+
1033
+ = 0.1 =
1034
+ This is the first public beta, no upgrade is needed.