Changeset 3753857
- Timestamp:
- 10/19/2026 03:06:05 AM (2 weeks ago)
- Location:
- choice-universal-form-tracker
- Files:
-
- 2 edited
-
tags/3.30.1/readme.txt (modified) (7 diffs)
-
trunk/readme.txt (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
choice-universal-form-tracker/tags/3.30.1/readme.txt
r3752395 r3753857 13 13 == Description == 14 14 15 Five form plugins, one event shape. Each form plugin fires its submissions 16 differently, so a site running more than one ends up with a different tag, a 17 different trigger, and a different set of field names for each. This plugin 18 detects whichever form plugins are active and emits a single, consistent 19 `form_submit` event for all of them, so one tag in Google Tag Manager covers 20 the whole site. 21 22 Campaign attribution rides along. UTM parameters and advertising click IDs are 23 captured on the visitor's first landing and stored for 30 days, so a form 24 submitted a week after the ad click still reports the campaign that produced 25 it. 15 Five form plugins, one event shape. Each form plugin fires its submissions differently, so a site running more than one ends up with a different tag, a different trigger, and a different set of field names for each. This plugin detects whichever form plugins are active and emits a single, consistent `form_submit` event for all of them, so one tag in Google Tag Manager covers the whole site. 16 17 Campaign attribution rides along. UTM parameters and advertising click IDs are captured on the visitor's first landing and stored for 30 days, so a form submitted a week after the ad click still reports the campaign that produced it. 26 18 27 19 = Supported form plugins = 28 20 29 Detection is automatic. No configuration is needed beyond activating the 30 plugin, and nothing is loaded for a form plugin that is not installed. 21 Detection is automatic. No configuration is needed beyond activating the plugin, and nothing is loaded for a form plugin that is not installed. 31 22 32 23 * Contact Form 7 … … 36 27 * Avada / Fusion Builder forms 37 28 38 These are third-party products, listed here to describe compatibility. This 39 plugin is not affiliated with, endorsed by, or supported by their makers. 29 These are third-party products, listed here to describe compatibility. This plugin is not affiliated with, endorsed by, or supported by their makers. 40 30 41 31 = What gets tracked = 42 32 43 * `form_submit` on every detected submission, carrying the form type, form ID, 44 and the email address and phone number the visitor entered 45 * `generate_lead` when a submission includes a valid email address and the 46 Generate Lead Events setting is on (off by default on new installs) 47 * `qualify_lead` when a submission carries an email address, a phone number, 48 and an advertising click ID 49 * `phone_click` when a visitor clicks a `tel:` link, and `email_click` for a 50 `mailto:` link, except links and numbers you exclude (see the FAQ) 33 * `form_submit` on every detected submission, carrying the form type, form ID, and the email address and phone number the visitor entered 34 * `generate_lead` when a submission includes a valid email address and the Generate Lead Events setting is on (off by default on new installs) 35 * `qualify_lead` when a submission carries an email address, a phone number, and an advertising click ID 36 * `phone_click` when a visitor clicks a `tel:` link, and `email_click` for a `mailto:` link, except links and numbers you exclude (see the FAQ) 51 37 * UTM parameters (source, medium, campaign, term, content) on all of the above 52 * Advertising click IDs: `gclid`, `gbraid`, `wbraid`, `fbclid`, `msclkid`, 53 `ttclid`, `twclid`, `rdt_cid`, `li_fat_id`, `snap_click_id`, and `pclid` 54 55 Every event carries `cuft_tracked: true`, which gives you one reliable 56 condition to build triggers on. 38 * Advertising click IDs: `gclid`, `gbraid`, `wbraid`, `fbclid`, `msclkid`, `ttclid`, `twclid`, `rdt_cid`, `li_fat_id`, `snap_click_id`, and `pclid` 39 40 Every event carries `cuft_tracked: true`, which gives you one reliable condition to build triggers on. 57 41 58 42 = Send it to GA4, Google Ads and Meta = 59 43 60 One trigger in Google Tag Manager covers every form on the site, and a form 61 plugin added later needs no new tag: 62 63 1. Add Data Layer Variables for `cuft_tracked` and for the fields you need, 64 such as `form_type`, `form_id`, `utm_campaign`, `gclid` and `lead_id`. 65 2. Create a Custom Event trigger for `form_submit` with the condition 66 `cuft_tracked` equals `true`. 67 3. Fire your GA4 event tag, Google Ads conversion tag or Meta Pixel `Lead` 68 event on that trigger. For Google Ads enhanced conversions, map 69 `user_email` into a User-Provided Data variable. `user_phone` holds the 70 number as the visitor typed it, so convert it to E.164 form (such as 71 `+17805550173`) before mapping it. For GA4, send `lead_id` and leave the 72 contact details out. 44 One trigger in Google Tag Manager covers every form on the site, and a form plugin added later needs no new tag: 45 46 1. Add Data Layer Variables for `cuft_tracked` and for the fields you need, such as `form_type`, `form_id`, `utm_campaign`, `gclid` and `lead_id`. 47 2. Create a Custom Event trigger for `form_submit` with the condition `cuft_tracked` equals `true`. 48 3. Fire your GA4 event tag, Google Ads conversion tag or Meta Pixel `Lead` event on that trigger. For Google Ads enhanced conversions, map `user_email` into a User-Provided Data variable. `user_phone` holds the number as the visitor typed it, so convert it to E.164 form (such as `+17805550173`) before mapping it. For GA4, send `lead_id` and leave the contact details out. 73 49 74 50 = Shared lead identity = 75 51 76 Each event can carry a `lead_id`: the SHA-256 hash of the visitor's email 77 address, lowercased and trimmed, or of their phone number in E.164 form when 78 no email was given. The same hash is computed server-side, so an analytics 79 event and a downstream CRM record derived from the same person produce the 80 same identifier. 81 82 The `form_submit` event also carries the raw email address and phone number. 83 Send `lead_id` instead of those fields wherever raw personal data must not 84 go; Google Analytics, for one, forbids sending it email addresses or phone 85 numbers. 86 87 The normalisation matches what Google Enhanced Conversions and Meta CAPI 88 expect, so the value doubles as a match key for offline conversion imports. 52 Each event can carry a `lead_id`: the SHA-256 hash of the visitor's email address, lowercased and trimmed, or of their phone number in E.164 form when no email was given. The same hash is computed server-side, so an analytics event and a downstream CRM record derived from the same person produce the same identifier. 53 54 The `form_submit` event also carries the raw email address and phone number. Send `lead_id` instead of those fields wherever raw personal data must not go; Google Analytics, for one, forbids sending it email addresses or phone numbers. 55 56 The normalisation matches what Google Enhanced Conversions and Meta CAPI expect, so the value doubles as a match key for offline conversion imports. 89 57 90 58 = Built to stay out of the way = … … 97 65 = Who maintains this = 98 66 99 [Choice OMG](https://choice.marketing/) maintains this plugin. We are a 100 digital marketing and web agency in Edmonton, Alberta, and we built it because 101 our clients' sites run several form plugins at once and each one needed its own 102 analytics plumbing. The plugin's overview page is 103 [Choice Universal Form Tracker for WordPress](https://choice.marketing/tools/choice-uft/). 104 If you would rather have GTM, GA4 and Google Ads conversion tracking set up for 105 you, [Choice OMG does that for clients](https://choice.marketing/services/conversion-optimization/). 67 [Choice OMG](https://choice.marketing/) maintains this plugin. We are a digital marketing and web agency in Edmonton, Alberta, and we built it because our clients' sites run several form plugins at once and each one needed its own analytics plumbing. The plugin's overview page is [Choice Universal Form Tracker for WordPress](https://choice.marketing/tools/choice-uft/). If you would rather have GTM, GA4 and Google Ads conversion tracking set up for you, [Choice OMG does that for clients](https://choice.marketing/services/conversion-optimization/). 106 68 107 69 == External services == 108 70 109 This plugin can connect to the external services below. Every one of them is 110 off on a new install and stays off until an administrator configures it on 111 the settings screen. With none configured, the plugin only pushes events to 112 the `window.dataLayer` array in the visitor's own browser and sends nothing 113 anywhere. 71 This plugin can connect to the external services below. Every one of them is off on a new install and stays off until an administrator configures it on the settings screen. With none configured, the plugin only pushes events to the `window.dataLayer` array in the visitor's own browser and sends nothing anywhere. 114 72 115 73 = Google Tag Manager = 116 74 117 Used when you enter a GTM container ID on the settings screen. Leave that 118 field empty and no request is made; the plugin then pushes events to a 119 dataLayer supplied by your own GTM installation instead. 120 121 *What is sent:* the visitor's browser requests the container script from 122 `https://www.googletagmanager.com/gtm.js` (and, for visitors without 123 JavaScript, the `ns.html` frame). As with any GTM installation, Google 124 receives the request, the visitor's IP address and browser details, and 125 whatever your own container tags choose to send, which will include the form 126 and campaign data described above if you configure tags to forward it. 75 Used when you enter a GTM container ID on the settings screen. Leave that field empty and no request is made; the plugin then pushes events to a dataLayer supplied by your own GTM installation instead. 76 77 *What is sent:* the visitor's browser requests the container script from `https://www.googletagmanager.com/gtm.js` (and, for visitors without JavaScript, the `ns.html` frame). As with any GTM installation, Google receives the request, the visitor's IP address and browser details, and whatever your own container tags choose to send, which will include the form and campaign data described above if you configure tags to forward it. 127 78 128 79 *When:* on every front-end page load, once a container ID is set. … … 134 85 = Server-side Google Tag Manager (optional custom host) = 135 86 136 Used only when you enable server-side GTM and enter your own tagging server 137 hostname. Off by default. The setting accepts a bare hostname only (for 138 example `gtm.example.com`); the plugin builds the script address itself as 139 `https://<host>/gtm.js?id=<container ID>`. 140 141 *What is sent:* the container script is requested from the host you entered 142 instead of from Google, so that host receives the same page-load request 143 described above. To decide whether the host is healthy, the plugin also 144 requests `gtm.js` and `ns.html` from it, server to server, when you press 145 the test button and on a six-hour schedule, with a recheck 15 minutes after a 146 failed check. Those checks carry the container ID and no visitor data. When 147 the custom host fails two checks in a row, the plugin falls back to 148 `www.googletagmanager.com`, and returns to the host after three passes in a 149 row. 150 151 *When:* on every front-end page load, and on the six-hour health check, while 152 server-side GTM is enabled. 153 154 You choose the host, so you decide who receives the request and which terms 155 apply. 87 Used only when you enable server-side GTM and enter your own tagging server hostname. Off by default. The setting accepts a bare hostname only (for example `gtm.example.com`); the plugin builds the script address itself as `https://<host>/gtm.js?id=<container ID>`. 88 89 *What is sent:* the container script is requested from the host you entered instead of from Google, so that host receives the same page-load request described above. To decide whether the host is healthy, the plugin also requests `gtm.js` and `ns.html` from it, server to server, when you press the test button and on a six-hour schedule, with a recheck 15 minutes after a failed check. Those checks carry the container ID and no visitor data. When the custom host fails two checks in a row, the plugin falls back to `www.googletagmanager.com`, and returns to the host after three passes in a row. 90 91 *When:* on every front-end page load, and on the six-hour health check, while server-side GTM is enabled. 92 93 You choose the host, so you decide who receives the request and which terms apply. 156 94 157 95 = Google Analytics Measurement Protocol = 158 96 159 Used only when you supply both a GA4 Measurement ID and an API secret on the 160 settings screen. Without both, no request is made. 161 162 *What is sent:* a server-side event to 163 `https://www.google-analytics.com/mp/collect`, containing the GA client ID 164 read from the visitor's `_ga` cookie, the event name, the configured lead 165 value and currency, and the event parameters for that submission. 166 167 *When:* when a lead's status is updated through the plugin's webhook (for 168 example, a lead marked qualified). 97 Used only when you supply both a GA4 Measurement ID and an API secret on the settings screen. Without both, no request is made. 98 99 *What is sent:* a server-side event to `https://www.google-analytics.com/mp/collect`, containing the GA client ID read from the visitor's `_ga` cookie, the event name, the configured lead value and currency, and the event parameters for that submission. 100 101 *When:* when a lead's status is updated through the plugin's webhook (for example, a lead marked qualified). 169 102 170 103 * Service: [Google Analytics](https://marketingplatform.google.com/about/analytics/) … … 174 107 = Click collector (optional custom host) = 175 108 176 Used only when you enter a Click Collector Host on the settings screen. Empty 177 by default, in which case nothing is sent. 178 179 *What is sent:* the visitor's browser sends a POST (`navigator.sendBeacon`) 180 to `https://<the host you entered>/p` carrying the advertising click ID and 181 its platform, the five UTM parameters, the referring URL, the landing page 182 URL, and the GA client ID from the `_ga` cookie. 183 184 *When:* on page load, only when a click ID is present in the URL or in the 185 plugin's own click cookie. 186 187 You choose the host. Point it at your own collector, or at the one Choice OMG 188 operates for its clients, in which case Choice OMG receives the data above 189 under the terms and privacy policy linked in the next section. 109 Used only when you enter a Click Collector Host on the settings screen. Empty by default, in which case nothing is sent. 110 111 *What is sent:* the visitor's browser sends a POST (`navigator.sendBeacon`) to `https://<the host you entered>/p` carrying the advertising click ID and its platform, the five UTM parameters, the referring URL, the landing page URL, and the GA client ID from the `_ga` cookie. 112 113 *When:* on page load, only when a click ID is present in the URL or in the plugin's own click cookie. 114 115 You choose the host. Point it at your own collector, or at the one Choice OMG operates for its clients, in which case Choice OMG receives the data above under the terms and privacy policy linked in the next section. 190 116 191 117 = Choice OMG phone validation service = 192 118 193 Used only when an administrator switches phone validation on and registers 194 the site with a registration secret that Choice OMG issues to its clients. 195 Both are off and empty by default, so this service is never contacted on a 196 standard install. 197 198 *What is sent:* registering the site (the Register Site button) sends the 199 site's domain name and the registration secret to 200 `https://phone-validator.choice.zone`. After that, each phone number to be 201 checked is sent from your server to 202 `https://phone-validator.choice.zone/validate-phone` together with the 203 site's registration token. Choice OMG's service looks the number up with its 204 own providers and returns whether the number is valid, its line type, and a 205 quality score. The plugin itself never contacts those providers. 206 207 *When:* when a submitted phone number is validated. Results are cached on your 208 site for 24 hours per number, so a repeated number is not sent again within 209 that window. 119 Used only when an administrator switches phone validation on and registers the site with a registration secret that Choice OMG issues to its clients. Both are off and empty by default, so this service is never contacted on a standard install. 120 121 *What is sent:* registering the site (the Register Site button) sends the site's domain name and the registration secret to `https://phone-validator.choice.zone`. After that, each phone number to be checked is sent from your server to `https://phone-validator.choice.zone/validate-phone` together with the site's registration token. Choice OMG's service looks the number up with its own providers and returns whether the number is valid, its line type, and a quality score. The plugin itself never contacts those providers. 122 123 *When:* when a submitted phone number is validated. Results are cached on your site for 24 hours per number, so a repeated number is not sent again within that window. 210 124 211 125 * Service: [Choice OMG](https://choice.marketing/tools/choice-uft/) … … 213 127 * Privacy policy: [https://choice.marketing/privacy-policy/](https://choice.marketing/privacy-policy/) 214 128 215 Because form submissions can carry personal data, review your own privacy 216 policy and any applicable data-protection obligations (GDPR, CCPA, PIPEDA and 217 similar) before enabling any of these services. 129 Because form submissions can carry personal data, review your own privacy policy and any applicable data-protection obligations (GDPR, CCPA, PIPEDA and similar) before enabling any of these services. 218 130 219 131 == Installation == … … 231 143 = Do I need Google Tag Manager? = 232 144 233 No. The plugin pushes its events to `window.dataLayer` whether or not it 234 injects a container for you. If you already load GTM through a theme or 235 another plugin, leave the container ID empty and the events will reach your 236 existing container. 145 No. The plugin pushes its events to `window.dataLayer` whether or not it injects a container for you. If you already load GTM through a theme or another plugin, leave the container ID empty and the events will reach your existing container. 237 146 238 147 = Can I use it alongside GTM4WP, GTM Kit or Site Kit? = 239 148 240 Yes. When another plugin already loads your container, leave this plugin's 241 container ID empty. Its events go into the same `window.dataLayer`, so your 242 existing container receives them, provided that plugin keeps the default 243 `dataLayer` name. Those plugins are separate products, and this one is not 244 affiliated with their makers. 149 Yes. When another plugin already loads your container, leave this plugin's container ID empty. Its events go into the same `window.dataLayer`, so your existing container receives them, provided that plugin keeps the default `dataLayer` name. Those plugins are separate products, and this one is not affiliated with their makers. 245 150 246 151 = Is there a paid version? = 247 152 248 No. There is no paid tier and no upgrade prompt. Two optional features can 249 use services Choice OMG runs for its agency clients: phone validation, which 250 needs a registration secret Choice OMG issues to those clients, and the click 251 collector, which can point at a collector of your own instead. Both are off by 252 default and are described under External services. 153 No. There is no paid tier and no upgrade prompt. Two optional features can use services Choice OMG runs for its agency clients: phone validation, which needs a registration secret Choice OMG issues to those clients, and the click collector, which can point at a collector of your own instead. Both are off by default and are described under External services. 253 154 254 155 = Which form plugins does it support? = 255 156 256 Contact Form 7, Gravity Forms, Ninja Forms, Elementor Pro forms, and Avada / 257 Fusion Builder forms. Detection is automatic; scripts for a form plugin that 258 is not installed are never loaded. 157 Contact Form 7, Gravity Forms, Ninja Forms, Elementor Pro forms, and Avada / Fusion Builder forms. Detection is automatic; scripts for a form plugin that is not installed are never loaded. 259 158 260 159 = What data does it collect, and where does it go? = 261 160 262 Form submissions produce dataLayer events containing the email address and 263 phone number entered, the form's identity, and the campaign parameters 264 captured when the visitor arrived. Those events go to your own analytics 265 setup. Nothing leaves your site unless you configure one of the services 266 listed under External services. 161 Form submissions produce dataLayer events containing the email address and phone number entered, the form's identity, and the campaign parameters captured when the visitor arrived. Those events go to your own analytics setup. Nothing leaves your site unless you configure one of the services listed under External services. 267 162 268 163 = How long is campaign attribution kept? = 269 164 270 UTM parameters and click IDs are stored in the visitor's browser for 30 days 271 from their first visit carrying them, so attribution survives a gap between 272 the ad click and the form submission. First-touch attribution is kept for a 273 year in a separate cookie. 274 275 On the server, each tracked click is a row in the plugin's click tracking 276 table: the click ID and its ad platform, the campaign parameters, a keyed 277 hash of the visitor's IP address, the browser's user agent, the referring 278 page, the landing page address, the Google Analytics client ID when the 279 visitor has one, and the tracked events with their times. A click on a phone 280 or email link by a visitor who did not arrive from an ad also creates a row, 281 under a temporary ID. Rows older than the Click Data Retention setting (365 282 days by default) are deleted once a day. The IP address itself is never 283 stored, and the hash is keyed with the authentication keys in wp-config.php, 284 so a copy of the database alone cannot reverse it. 165 UTM parameters and click IDs are stored in the visitor's browser for 30 days from their first visit carrying them, so attribution survives a gap between the ad click and the form submission. First-touch attribution is kept for a year in a separate cookie. 166 167 On the server, each tracked click is a row in the plugin's click tracking table: the click ID and its ad platform, the campaign parameters, a keyed hash of the visitor's IP address, the browser's user agent, the referring page, the landing page address, the Google Analytics client ID when the visitor has one, and the tracked events with their times. A click on a phone or email link by a visitor who did not arrive from an ad also creates a row, under a temporary ID. Rows older than the Click Data Retention setting (365 days by default) are deleted once a day. The IP address itself is never stored, and the hash is keyed with the authentication keys in wp-config.php, so a copy of the database alone cannot reverse it. 285 168 286 169 = Is it GDPR compliant? = 287 170 288 That depends on how you configure it. The plugin writes campaign parameters to 289 the visitor's browser and puts submitted contact details into your dataLayer, 290 so it processes personal data and belongs in your privacy policy and your 291 consent flow. Review the External services section for anything that leaves 292 your site. 171 That depends on how you configure it. The plugin writes campaign parameters to the visitor's browser and puts submitted contact details into your dataLayer, so it processes personal data and belongs in your privacy policy and your consent flow. Review the External services section for anything that leaves your site. 293 172 294 173 = Does it slow the site down? = 295 174 296 The tracking scripts are plain JavaScript with no jQuery dependency, and each 297 form plugin's handler loads only when that form plugin is present. Tracking 298 errors are caught so a failure cannot block a form submission. 175 The tracking scripts are plain JavaScript with no jQuery dependency, and each form plugin's handler loads only when that form plugin is present. Tracking errors are caught so a failure cannot block a form submission. 299 176 300 177 = Can my CRM or email platform update a lead's status? = 301 178 302 Yes. The Click Tracking screen shows a webhook URL that marks a tracked click 303 as qualified, scores it, or records a lifecycle status such as `qualify_lead`. 304 Every request must carry the site's secret webhook key, which the same screen 305 displays and can regenerate. Requests without the right key are rejected with 306 HTTP 403 and change nothing. 179 Yes. The Click Tracking screen shows a webhook URL that marks a tracked click as qualified, scores it, or records a lifecycle status such as `qualify_lead`. Every request must carry the site's secret webhook key, which the same screen displays and can regenerate. Requests without the right key are rejected with HTTP 403 and change nothing. 307 180 308 181 = Can I change what is tracked? = 309 182 310 Yes. Filters cover the computed `lead_id` (`cuft_lead_id`), the phone country 311 code used to normalise it (`cuft_lead_id_phone_country`), the attribution 312 payload (`cuft_form_attribution_payload`), and whether the plugin runs at all 313 (`cuft_enabled`). Since 3.30.0, `cuft_ignore_phone_numbers` adds numbers that 314 are never tracked, `cuft_record_event` decides whether an event reported by 315 the browser is recorded on its click, and `cuft_click_retention_days` sets how 316 long click rows are kept. 183 Yes. Filters cover the computed `lead_id` (`cuft_lead_id`), the phone country code used to normalise it (`cuft_lead_id_phone_country`), the attribution payload (`cuft_form_attribution_payload`), and whether the plugin runs at all (`cuft_enabled`). Since 3.30.0, `cuft_ignore_phone_numbers` adds numbers that are never tracked, `cuft_record_event` decides whether an event reported by the browser is recorded on its click, and `cuft_click_retention_days` sets how long click rows are kept. 317 184 318 185 = Can I keep one phone number or link out of tracking? = 319 186 320 Yes. List the number under Settings > Ignored Phone Numbers, and clicks on any 321 `tel:` link to it produce no `phone_click` in the dataLayer and nothing on the 322 click record. Numbers match on their last 10 digits, so every formatting of the 323 same number is covered. To exclude a single link of any kind, add the attribute 324 `data-cuft-ignore` or the CSS class `cuft-ignore` to the link or to an element 325 around it; in Elementor the class goes under Advanced > CSS Classes. 187 Yes. List the number under Settings > Ignored Phone Numbers, and clicks on any `tel:` link to it produce no `phone_click` in the dataLayer and nothing on the click record. Numbers match on their last 10 digits, so every formatting of the same number is covered. To exclude a single link of any kind, add the attribute `data-cuft-ignore` or the CSS class `cuft-ignore` to the link or to an element around it; in Elementor the class goes under Advanced > CSS Classes. 326 188 327 189 == Screenshots == -
choice-universal-form-tracker/trunk/readme.txt
r3752395 r3753857 13 13 == Description == 14 14 15 Five form plugins, one event shape. Each form plugin fires its submissions 16 differently, so a site running more than one ends up with a different tag, a 17 different trigger, and a different set of field names for each. This plugin 18 detects whichever form plugins are active and emits a single, consistent 19 `form_submit` event for all of them, so one tag in Google Tag Manager covers 20 the whole site. 21 22 Campaign attribution rides along. UTM parameters and advertising click IDs are 23 captured on the visitor's first landing and stored for 30 days, so a form 24 submitted a week after the ad click still reports the campaign that produced 25 it. 15 Five form plugins, one event shape. Each form plugin fires its submissions differently, so a site running more than one ends up with a different tag, a different trigger, and a different set of field names for each. This plugin detects whichever form plugins are active and emits a single, consistent `form_submit` event for all of them, so one tag in Google Tag Manager covers the whole site. 16 17 Campaign attribution rides along. UTM parameters and advertising click IDs are captured on the visitor's first landing and stored for 30 days, so a form submitted a week after the ad click still reports the campaign that produced it. 26 18 27 19 = Supported form plugins = 28 20 29 Detection is automatic. No configuration is needed beyond activating the 30 plugin, and nothing is loaded for a form plugin that is not installed. 21 Detection is automatic. No configuration is needed beyond activating the plugin, and nothing is loaded for a form plugin that is not installed. 31 22 32 23 * Contact Form 7 … … 36 27 * Avada / Fusion Builder forms 37 28 38 These are third-party products, listed here to describe compatibility. This 39 plugin is not affiliated with, endorsed by, or supported by their makers. 29 These are third-party products, listed here to describe compatibility. This plugin is not affiliated with, endorsed by, or supported by their makers. 40 30 41 31 = What gets tracked = 42 32 43 * `form_submit` on every detected submission, carrying the form type, form ID, 44 and the email address and phone number the visitor entered 45 * `generate_lead` when a submission includes a valid email address and the 46 Generate Lead Events setting is on (off by default on new installs) 47 * `qualify_lead` when a submission carries an email address, a phone number, 48 and an advertising click ID 49 * `phone_click` when a visitor clicks a `tel:` link, and `email_click` for a 50 `mailto:` link, except links and numbers you exclude (see the FAQ) 33 * `form_submit` on every detected submission, carrying the form type, form ID, and the email address and phone number the visitor entered 34 * `generate_lead` when a submission includes a valid email address and the Generate Lead Events setting is on (off by default on new installs) 35 * `qualify_lead` when a submission carries an email address, a phone number, and an advertising click ID 36 * `phone_click` when a visitor clicks a `tel:` link, and `email_click` for a `mailto:` link, except links and numbers you exclude (see the FAQ) 51 37 * UTM parameters (source, medium, campaign, term, content) on all of the above 52 * Advertising click IDs: `gclid`, `gbraid`, `wbraid`, `fbclid`, `msclkid`, 53 `ttclid`, `twclid`, `rdt_cid`, `li_fat_id`, `snap_click_id`, and `pclid` 54 55 Every event carries `cuft_tracked: true`, which gives you one reliable 56 condition to build triggers on. 38 * Advertising click IDs: `gclid`, `gbraid`, `wbraid`, `fbclid`, `msclkid`, `ttclid`, `twclid`, `rdt_cid`, `li_fat_id`, `snap_click_id`, and `pclid` 39 40 Every event carries `cuft_tracked: true`, which gives you one reliable condition to build triggers on. 57 41 58 42 = Send it to GA4, Google Ads and Meta = 59 43 60 One trigger in Google Tag Manager covers every form on the site, and a form 61 plugin added later needs no new tag: 62 63 1. Add Data Layer Variables for `cuft_tracked` and for the fields you need, 64 such as `form_type`, `form_id`, `utm_campaign`, `gclid` and `lead_id`. 65 2. Create a Custom Event trigger for `form_submit` with the condition 66 `cuft_tracked` equals `true`. 67 3. Fire your GA4 event tag, Google Ads conversion tag or Meta Pixel `Lead` 68 event on that trigger. For Google Ads enhanced conversions, map 69 `user_email` into a User-Provided Data variable. `user_phone` holds the 70 number as the visitor typed it, so convert it to E.164 form (such as 71 `+17805550173`) before mapping it. For GA4, send `lead_id` and leave the 72 contact details out. 44 One trigger in Google Tag Manager covers every form on the site, and a form plugin added later needs no new tag: 45 46 1. Add Data Layer Variables for `cuft_tracked` and for the fields you need, such as `form_type`, `form_id`, `utm_campaign`, `gclid` and `lead_id`. 47 2. Create a Custom Event trigger for `form_submit` with the condition `cuft_tracked` equals `true`. 48 3. Fire your GA4 event tag, Google Ads conversion tag or Meta Pixel `Lead` event on that trigger. For Google Ads enhanced conversions, map `user_email` into a User-Provided Data variable. `user_phone` holds the number as the visitor typed it, so convert it to E.164 form (such as `+17805550173`) before mapping it. For GA4, send `lead_id` and leave the contact details out. 73 49 74 50 = Shared lead identity = 75 51 76 Each event can carry a `lead_id`: the SHA-256 hash of the visitor's email 77 address, lowercased and trimmed, or of their phone number in E.164 form when 78 no email was given. The same hash is computed server-side, so an analytics 79 event and a downstream CRM record derived from the same person produce the 80 same identifier. 81 82 The `form_submit` event also carries the raw email address and phone number. 83 Send `lead_id` instead of those fields wherever raw personal data must not 84 go; Google Analytics, for one, forbids sending it email addresses or phone 85 numbers. 86 87 The normalisation matches what Google Enhanced Conversions and Meta CAPI 88 expect, so the value doubles as a match key for offline conversion imports. 52 Each event can carry a `lead_id`: the SHA-256 hash of the visitor's email address, lowercased and trimmed, or of their phone number in E.164 form when no email was given. The same hash is computed server-side, so an analytics event and a downstream CRM record derived from the same person produce the same identifier. 53 54 The `form_submit` event also carries the raw email address and phone number. Send `lead_id` instead of those fields wherever raw personal data must not go; Google Analytics, for one, forbids sending it email addresses or phone numbers. 55 56 The normalisation matches what Google Enhanced Conversions and Meta CAPI expect, so the value doubles as a match key for offline conversion imports. 89 57 90 58 = Built to stay out of the way = … … 97 65 = Who maintains this = 98 66 99 [Choice OMG](https://choice.marketing/) maintains this plugin. We are a 100 digital marketing and web agency in Edmonton, Alberta, and we built it because 101 our clients' sites run several form plugins at once and each one needed its own 102 analytics plumbing. The plugin's overview page is 103 [Choice Universal Form Tracker for WordPress](https://choice.marketing/tools/choice-uft/). 104 If you would rather have GTM, GA4 and Google Ads conversion tracking set up for 105 you, [Choice OMG does that for clients](https://choice.marketing/services/conversion-optimization/). 67 [Choice OMG](https://choice.marketing/) maintains this plugin. We are a digital marketing and web agency in Edmonton, Alberta, and we built it because our clients' sites run several form plugins at once and each one needed its own analytics plumbing. The plugin's overview page is [Choice Universal Form Tracker for WordPress](https://choice.marketing/tools/choice-uft/). If you would rather have GTM, GA4 and Google Ads conversion tracking set up for you, [Choice OMG does that for clients](https://choice.marketing/services/conversion-optimization/). 106 68 107 69 == External services == 108 70 109 This plugin can connect to the external services below. Every one of them is 110 off on a new install and stays off until an administrator configures it on 111 the settings screen. With none configured, the plugin only pushes events to 112 the `window.dataLayer` array in the visitor's own browser and sends nothing 113 anywhere. 71 This plugin can connect to the external services below. Every one of them is off on a new install and stays off until an administrator configures it on the settings screen. With none configured, the plugin only pushes events to the `window.dataLayer` array in the visitor's own browser and sends nothing anywhere. 114 72 115 73 = Google Tag Manager = 116 74 117 Used when you enter a GTM container ID on the settings screen. Leave that 118 field empty and no request is made; the plugin then pushes events to a 119 dataLayer supplied by your own GTM installation instead. 120 121 *What is sent:* the visitor's browser requests the container script from 122 `https://www.googletagmanager.com/gtm.js` (and, for visitors without 123 JavaScript, the `ns.html` frame). As with any GTM installation, Google 124 receives the request, the visitor's IP address and browser details, and 125 whatever your own container tags choose to send, which will include the form 126 and campaign data described above if you configure tags to forward it. 75 Used when you enter a GTM container ID on the settings screen. Leave that field empty and no request is made; the plugin then pushes events to a dataLayer supplied by your own GTM installation instead. 76 77 *What is sent:* the visitor's browser requests the container script from `https://www.googletagmanager.com/gtm.js` (and, for visitors without JavaScript, the `ns.html` frame). As with any GTM installation, Google receives the request, the visitor's IP address and browser details, and whatever your own container tags choose to send, which will include the form and campaign data described above if you configure tags to forward it. 127 78 128 79 *When:* on every front-end page load, once a container ID is set. … … 134 85 = Server-side Google Tag Manager (optional custom host) = 135 86 136 Used only when you enable server-side GTM and enter your own tagging server 137 hostname. Off by default. The setting accepts a bare hostname only (for 138 example `gtm.example.com`); the plugin builds the script address itself as 139 `https://<host>/gtm.js?id=<container ID>`. 140 141 *What is sent:* the container script is requested from the host you entered 142 instead of from Google, so that host receives the same page-load request 143 described above. To decide whether the host is healthy, the plugin also 144 requests `gtm.js` and `ns.html` from it, server to server, when you press 145 the test button and on a six-hour schedule, with a recheck 15 minutes after a 146 failed check. Those checks carry the container ID and no visitor data. When 147 the custom host fails two checks in a row, the plugin falls back to 148 `www.googletagmanager.com`, and returns to the host after three passes in a 149 row. 150 151 *When:* on every front-end page load, and on the six-hour health check, while 152 server-side GTM is enabled. 153 154 You choose the host, so you decide who receives the request and which terms 155 apply. 87 Used only when you enable server-side GTM and enter your own tagging server hostname. Off by default. The setting accepts a bare hostname only (for example `gtm.example.com`); the plugin builds the script address itself as `https://<host>/gtm.js?id=<container ID>`. 88 89 *What is sent:* the container script is requested from the host you entered instead of from Google, so that host receives the same page-load request described above. To decide whether the host is healthy, the plugin also requests `gtm.js` and `ns.html` from it, server to server, when you press the test button and on a six-hour schedule, with a recheck 15 minutes after a failed check. Those checks carry the container ID and no visitor data. When the custom host fails two checks in a row, the plugin falls back to `www.googletagmanager.com`, and returns to the host after three passes in a row. 90 91 *When:* on every front-end page load, and on the six-hour health check, while server-side GTM is enabled. 92 93 You choose the host, so you decide who receives the request and which terms apply. 156 94 157 95 = Google Analytics Measurement Protocol = 158 96 159 Used only when you supply both a GA4 Measurement ID and an API secret on the 160 settings screen. Without both, no request is made. 161 162 *What is sent:* a server-side event to 163 `https://www.google-analytics.com/mp/collect`, containing the GA client ID 164 read from the visitor's `_ga` cookie, the event name, the configured lead 165 value and currency, and the event parameters for that submission. 166 167 *When:* when a lead's status is updated through the plugin's webhook (for 168 example, a lead marked qualified). 97 Used only when you supply both a GA4 Measurement ID and an API secret on the settings screen. Without both, no request is made. 98 99 *What is sent:* a server-side event to `https://www.google-analytics.com/mp/collect`, containing the GA client ID read from the visitor's `_ga` cookie, the event name, the configured lead value and currency, and the event parameters for that submission. 100 101 *When:* when a lead's status is updated through the plugin's webhook (for example, a lead marked qualified). 169 102 170 103 * Service: [Google Analytics](https://marketingplatform.google.com/about/analytics/) … … 174 107 = Click collector (optional custom host) = 175 108 176 Used only when you enter a Click Collector Host on the settings screen. Empty 177 by default, in which case nothing is sent. 178 179 *What is sent:* the visitor's browser sends a POST (`navigator.sendBeacon`) 180 to `https://<the host you entered>/p` carrying the advertising click ID and 181 its platform, the five UTM parameters, the referring URL, the landing page 182 URL, and the GA client ID from the `_ga` cookie. 183 184 *When:* on page load, only when a click ID is present in the URL or in the 185 plugin's own click cookie. 186 187 You choose the host. Point it at your own collector, or at the one Choice OMG 188 operates for its clients, in which case Choice OMG receives the data above 189 under the terms and privacy policy linked in the next section. 109 Used only when you enter a Click Collector Host on the settings screen. Empty by default, in which case nothing is sent. 110 111 *What is sent:* the visitor's browser sends a POST (`navigator.sendBeacon`) to `https://<the host you entered>/p` carrying the advertising click ID and its platform, the five UTM parameters, the referring URL, the landing page URL, and the GA client ID from the `_ga` cookie. 112 113 *When:* on page load, only when a click ID is present in the URL or in the plugin's own click cookie. 114 115 You choose the host. Point it at your own collector, or at the one Choice OMG operates for its clients, in which case Choice OMG receives the data above under the terms and privacy policy linked in the next section. 190 116 191 117 = Choice OMG phone validation service = 192 118 193 Used only when an administrator switches phone validation on and registers 194 the site with a registration secret that Choice OMG issues to its clients. 195 Both are off and empty by default, so this service is never contacted on a 196 standard install. 197 198 *What is sent:* registering the site (the Register Site button) sends the 199 site's domain name and the registration secret to 200 `https://phone-validator.choice.zone`. After that, each phone number to be 201 checked is sent from your server to 202 `https://phone-validator.choice.zone/validate-phone` together with the 203 site's registration token. Choice OMG's service looks the number up with its 204 own providers and returns whether the number is valid, its line type, and a 205 quality score. The plugin itself never contacts those providers. 206 207 *When:* when a submitted phone number is validated. Results are cached on your 208 site for 24 hours per number, so a repeated number is not sent again within 209 that window. 119 Used only when an administrator switches phone validation on and registers the site with a registration secret that Choice OMG issues to its clients. Both are off and empty by default, so this service is never contacted on a standard install. 120 121 *What is sent:* registering the site (the Register Site button) sends the site's domain name and the registration secret to `https://phone-validator.choice.zone`. After that, each phone number to be checked is sent from your server to `https://phone-validator.choice.zone/validate-phone` together with the site's registration token. Choice OMG's service looks the number up with its own providers and returns whether the number is valid, its line type, and a quality score. The plugin itself never contacts those providers. 122 123 *When:* when a submitted phone number is validated. Results are cached on your site for 24 hours per number, so a repeated number is not sent again within that window. 210 124 211 125 * Service: [Choice OMG](https://choice.marketing/tools/choice-uft/) … … 213 127 * Privacy policy: [https://choice.marketing/privacy-policy/](https://choice.marketing/privacy-policy/) 214 128 215 Because form submissions can carry personal data, review your own privacy 216 policy and any applicable data-protection obligations (GDPR, CCPA, PIPEDA and 217 similar) before enabling any of these services. 129 Because form submissions can carry personal data, review your own privacy policy and any applicable data-protection obligations (GDPR, CCPA, PIPEDA and similar) before enabling any of these services. 218 130 219 131 == Installation == … … 231 143 = Do I need Google Tag Manager? = 232 144 233 No. The plugin pushes its events to `window.dataLayer` whether or not it 234 injects a container for you. If you already load GTM through a theme or 235 another plugin, leave the container ID empty and the events will reach your 236 existing container. 145 No. The plugin pushes its events to `window.dataLayer` whether or not it injects a container for you. If you already load GTM through a theme or another plugin, leave the container ID empty and the events will reach your existing container. 237 146 238 147 = Can I use it alongside GTM4WP, GTM Kit or Site Kit? = 239 148 240 Yes. When another plugin already loads your container, leave this plugin's 241 container ID empty. Its events go into the same `window.dataLayer`, so your 242 existing container receives them, provided that plugin keeps the default 243 `dataLayer` name. Those plugins are separate products, and this one is not 244 affiliated with their makers. 149 Yes. When another plugin already loads your container, leave this plugin's container ID empty. Its events go into the same `window.dataLayer`, so your existing container receives them, provided that plugin keeps the default `dataLayer` name. Those plugins are separate products, and this one is not affiliated with their makers. 245 150 246 151 = Is there a paid version? = 247 152 248 No. There is no paid tier and no upgrade prompt. Two optional features can 249 use services Choice OMG runs for its agency clients: phone validation, which 250 needs a registration secret Choice OMG issues to those clients, and the click 251 collector, which can point at a collector of your own instead. Both are off by 252 default and are described under External services. 153 No. There is no paid tier and no upgrade prompt. Two optional features can use services Choice OMG runs for its agency clients: phone validation, which needs a registration secret Choice OMG issues to those clients, and the click collector, which can point at a collector of your own instead. Both are off by default and are described under External services. 253 154 254 155 = Which form plugins does it support? = 255 156 256 Contact Form 7, Gravity Forms, Ninja Forms, Elementor Pro forms, and Avada / 257 Fusion Builder forms. Detection is automatic; scripts for a form plugin that 258 is not installed are never loaded. 157 Contact Form 7, Gravity Forms, Ninja Forms, Elementor Pro forms, and Avada / Fusion Builder forms. Detection is automatic; scripts for a form plugin that is not installed are never loaded. 259 158 260 159 = What data does it collect, and where does it go? = 261 160 262 Form submissions produce dataLayer events containing the email address and 263 phone number entered, the form's identity, and the campaign parameters 264 captured when the visitor arrived. Those events go to your own analytics 265 setup. Nothing leaves your site unless you configure one of the services 266 listed under External services. 161 Form submissions produce dataLayer events containing the email address and phone number entered, the form's identity, and the campaign parameters captured when the visitor arrived. Those events go to your own analytics setup. Nothing leaves your site unless you configure one of the services listed under External services. 267 162 268 163 = How long is campaign attribution kept? = 269 164 270 UTM parameters and click IDs are stored in the visitor's browser for 30 days 271 from their first visit carrying them, so attribution survives a gap between 272 the ad click and the form submission. First-touch attribution is kept for a 273 year in a separate cookie. 274 275 On the server, each tracked click is a row in the plugin's click tracking 276 table: the click ID and its ad platform, the campaign parameters, a keyed 277 hash of the visitor's IP address, the browser's user agent, the referring 278 page, the landing page address, the Google Analytics client ID when the 279 visitor has one, and the tracked events with their times. A click on a phone 280 or email link by a visitor who did not arrive from an ad also creates a row, 281 under a temporary ID. Rows older than the Click Data Retention setting (365 282 days by default) are deleted once a day. The IP address itself is never 283 stored, and the hash is keyed with the authentication keys in wp-config.php, 284 so a copy of the database alone cannot reverse it. 165 UTM parameters and click IDs are stored in the visitor's browser for 30 days from their first visit carrying them, so attribution survives a gap between the ad click and the form submission. First-touch attribution is kept for a year in a separate cookie. 166 167 On the server, each tracked click is a row in the plugin's click tracking table: the click ID and its ad platform, the campaign parameters, a keyed hash of the visitor's IP address, the browser's user agent, the referring page, the landing page address, the Google Analytics client ID when the visitor has one, and the tracked events with their times. A click on a phone or email link by a visitor who did not arrive from an ad also creates a row, under a temporary ID. Rows older than the Click Data Retention setting (365 days by default) are deleted once a day. The IP address itself is never stored, and the hash is keyed with the authentication keys in wp-config.php, so a copy of the database alone cannot reverse it. 285 168 286 169 = Is it GDPR compliant? = 287 170 288 That depends on how you configure it. The plugin writes campaign parameters to 289 the visitor's browser and puts submitted contact details into your dataLayer, 290 so it processes personal data and belongs in your privacy policy and your 291 consent flow. Review the External services section for anything that leaves 292 your site. 171 That depends on how you configure it. The plugin writes campaign parameters to the visitor's browser and puts submitted contact details into your dataLayer, so it processes personal data and belongs in your privacy policy and your consent flow. Review the External services section for anything that leaves your site. 293 172 294 173 = Does it slow the site down? = 295 174 296 The tracking scripts are plain JavaScript with no jQuery dependency, and each 297 form plugin's handler loads only when that form plugin is present. Tracking 298 errors are caught so a failure cannot block a form submission. 175 The tracking scripts are plain JavaScript with no jQuery dependency, and each form plugin's handler loads only when that form plugin is present. Tracking errors are caught so a failure cannot block a form submission. 299 176 300 177 = Can my CRM or email platform update a lead's status? = 301 178 302 Yes. The Click Tracking screen shows a webhook URL that marks a tracked click 303 as qualified, scores it, or records a lifecycle status such as `qualify_lead`. 304 Every request must carry the site's secret webhook key, which the same screen 305 displays and can regenerate. Requests without the right key are rejected with 306 HTTP 403 and change nothing. 179 Yes. The Click Tracking screen shows a webhook URL that marks a tracked click as qualified, scores it, or records a lifecycle status such as `qualify_lead`. Every request must carry the site's secret webhook key, which the same screen displays and can regenerate. Requests without the right key are rejected with HTTP 403 and change nothing. 307 180 308 181 = Can I change what is tracked? = 309 182 310 Yes. Filters cover the computed `lead_id` (`cuft_lead_id`), the phone country 311 code used to normalise it (`cuft_lead_id_phone_country`), the attribution 312 payload (`cuft_form_attribution_payload`), and whether the plugin runs at all 313 (`cuft_enabled`). Since 3.30.0, `cuft_ignore_phone_numbers` adds numbers that 314 are never tracked, `cuft_record_event` decides whether an event reported by 315 the browser is recorded on its click, and `cuft_click_retention_days` sets how 316 long click rows are kept. 183 Yes. Filters cover the computed `lead_id` (`cuft_lead_id`), the phone country code used to normalise it (`cuft_lead_id_phone_country`), the attribution payload (`cuft_form_attribution_payload`), and whether the plugin runs at all (`cuft_enabled`). Since 3.30.0, `cuft_ignore_phone_numbers` adds numbers that are never tracked, `cuft_record_event` decides whether an event reported by the browser is recorded on its click, and `cuft_click_retention_days` sets how long click rows are kept. 317 184 318 185 = Can I keep one phone number or link out of tracking? = 319 186 320 Yes. List the number under Settings > Ignored Phone Numbers, and clicks on any 321 `tel:` link to it produce no `phone_click` in the dataLayer and nothing on the 322 click record. Numbers match on their last 10 digits, so every formatting of the 323 same number is covered. To exclude a single link of any kind, add the attribute 324 `data-cuft-ignore` or the CSS class `cuft-ignore` to the link or to an element 325 around it; in Elementor the class goes under Advanced > CSS Classes. 187 Yes. List the number under Settings > Ignored Phone Numbers, and clicks on any `tel:` link to it produce no `phone_click` in the dataLayer and nothing on the click record. Numbers match on their last 10 digits, so every formatting of the same number is covered. To exclude a single link of any kind, add the attribute `data-cuft-ignore` or the CSS class `cuft-ignore` to the link or to an element around it; in Elementor the class goes under Advanced > CSS Classes. 326 188 327 189 == Screenshots ==
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)