Plugin Directory

Changeset 3397405


Ignore:
Timestamp:
11/17/2025 04:44:47 PM (4 months ago)
Author:
samsonovteamwork
Message:

Added tag ver 1.1.2

Location:
form-attribution-tracking/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • form-attribution-tracking/trunk/assets/js/referral-source.js

    r3397093 r3397405  
    105105
    106106    /**
    107      * Get the referral source with intelligent detection and persistence
    108      */
    109     function getReferralSource() {
    110         log('Getting referral source...');
    111        
    112         // 1. Check UTM parameters first (highest priority)
    113         const utmSource = getUrlParameter('utm_source');
    114         if (utmSource) {
    115             log('UTM source found', utmSource);
    116             // If we have UTM, store it as the original source and return it
    117             setCookie(CONFIG.cookieName, utmSource, CONFIG.cookieDays);
    118             return utmSource;
    119         }
    120 
    121         // 2. Check UTM medium and campaign for additional context
    122         const utmMedium = getUrlParameter('utm_medium');
    123         const utmCampaign = getUrlParameter('utm_campaign');
    124         if (utmMedium || utmCampaign) {
    125             const combinedUtm = [utmMedium, utmCampaign].filter(Boolean).join(' / ');
    126             log('UTM medium/campaign found', combinedUtm);
    127             setCookie(CONFIG.cookieName, combinedUtm, CONFIG.cookieDays);
    128             return combinedUtm;
    129         }
    130 
    131         // 3. Check if we already have a stored original referrer source
    132         const storedSource = getCookie(CONFIG.cookieName);
    133         if (storedSource) {
    134             log('Stored source found', storedSource);
    135             return storedSource;
    136         }
    137 
    138         // 4. Parse document.referrer for new visitors
    139         const referrer = document.referrer;
    140         log('Document referrer', referrer);
    141        
    142         // If no referrer, it's direct traffic
    143         if (!referrer) {
    144             log('No referrer - direct traffic');
    145             setCookie(CONFIG.cookieName, 'direct', CONFIG.cookieDays);
    146             return 'direct';
    147         }
    148 
    149         try {
    150             const referrerUrl = new URL(referrer);
    151             const hostname = referrerUrl.hostname.toLowerCase().replace('www.', '');
    152            
    153             // Skip if referrer is same as current site (internal navigation)
    154             const currentHostname = window.location.hostname.toLowerCase().replace('www.', '');
    155             if (hostname === currentHostname) {
    156                 log('Internal referrer detected');
    157                 // This is internal navigation - check if we have a stored source
    158                 const existing = getCookie(CONFIG.cookieName);
    159                 if (existing) {
    160                     return existing;
    161                 } else {
    162                     // No stored source and internal referrer = direct traffic
    163                     setCookie(CONFIG.cookieName, 'direct', CONFIG.cookieDays);
    164                     return 'direct';
     107     * Get the attribution data with intelligent detection and persistence
     108     * Returns an object with all attribution fields or null
     109     */
     110    function getAttributionData() {
     111        log('Getting attribution data...');
     112
     113        // 1. Check if we already have stored attribution data (first-touch attribution)
     114        const storedData = getCookie(CONFIG.cookieName);
     115        if (storedData) {
     116            try {
     117                const parsed = JSON.parse(storedData);
     118                if (parsed && typeof parsed === 'object') {
     119                    log('Stored attribution data found', parsed);
     120                    return parsed;
     121                }
     122            } catch (e) {
     123                // If cookie is old format (plain string), we'll create new attribution data below
     124                log('Old format cookie detected, will create new attribution data', storedData);
     125            }
     126        }
     127
     128        // 2. Create new attribution data for first-time visitors
     129        const attribution = {
     130            utm_source: getUrlParameter('utm_source') || null,
     131            utm_medium: getUrlParameter('utm_medium') || null,
     132            utm_campaign: getUrlParameter('utm_campaign') || null,
     133            utm_term: getUrlParameter('utm_term') || null,
     134            utm_content: getUrlParameter('utm_content') || null,
     135            gclid: getUrlParameter('gclid') || null,
     136            landing_page: window.location.href,
     137            timestamp: new Date().toISOString()
     138        };
     139
     140        // 3. Determine the source if no UTM parameters
     141        if (!attribution.utm_source) {
     142            const referrer = document.referrer;
     143            log('Document referrer', referrer);
     144
     145            if (!referrer) {
     146                attribution.utm_source = 'direct';
     147            } else {
     148                try {
     149                    const referrerUrl = new URL(referrer);
     150                    const hostname = referrerUrl.hostname.toLowerCase().replace('www.', '');
     151                    const currentHostname = window.location.hostname.toLowerCase().replace('www.', '');
     152
     153                    if (hostname === currentHostname) {
     154                        // Internal referrer - this is direct traffic
     155                        attribution.utm_source = 'direct';
     156                    } else {
     157                        // External referrer - parse and categorize it
     158                        attribution.utm_source = parseReferrerSource(hostname);
     159                        if (!attribution.utm_medium) {
     160                            attribution.utm_medium = 'referral';
     161                        }
     162                    }
     163                } catch (e) {
     164                    log('Error parsing referrer', e);
     165                    attribution.utm_source = 'direct';
    165166                }
    166167            }
    167            
    168             // This is an external referrer - parse and categorize it
    169             const parsedSource = parseReferrerSource(hostname);
    170             log('External referrer parsed', { hostname, parsedSource });
    171             setCookie(CONFIG.cookieName, parsedSource, CONFIG.cookieDays);
    172             return parsedSource;
    173            
    174         } catch (e) {
    175             // If URL parsing fails, return direct
    176             log('Error parsing referrer', e);
    177             setCookie(CONFIG.cookieName, 'direct', CONFIG.cookieDays);
    178             return 'direct';
    179         }
     168        }
     169
     170        // 4. Store the attribution data as JSON
     171        const jsonData = JSON.stringify(attribution);
     172        setCookie(CONFIG.cookieName, jsonData, CONFIG.cookieDays);
     173        log('New attribution data created and stored', attribution);
     174
     175        return attribution;
     176    }
     177
     178    /**
     179     * Backward compatibility: Get just the referral source
     180     */
     181    function getReferralSource() {
     182        const attribution = getAttributionData();
     183        return attribution ? attribution.utm_source : 'direct';
    180184    }
    181185
     
    189193
    190194    /**
    191      * Populate form fields with referral source
     195     * Populate form fields with attribution data
    192196     */
    193197    function populateFormFields() {
    194         const referralSource = getReferralSource();
    195         if (!referralSource) {
    196             log('No referral source to populate');
     198        const attribution = getAttributionData();
     199        if (!attribution) {
     200            log('No attribution data to populate');
    197201            return;
    198202        }
    199203
    200         log('Populating form fields with referral source', referralSource);
    201 
    202         // Find all possible referral source fields
    203         const selectors = [
    204             // Direct name match (Fluent Forms)
    205             `input[name="${CONFIG.fieldName}"]`,
    206            
    207             // ID-based selectors (Formidable Forms)
    208             `input[id*="${CONFIG.fieldName}"]`,
    209             `input[id="field_${CONFIG.fieldName}"]`,
    210            
    211             // aria-describedby selectors (Formidable Forms)
    212             `input[aria-describedby*="${CONFIG.fieldName}"]`,
    213            
    214             // Generic selectors for various patterns
    215             'input[name*="referral"]',
    216             'input[name*="source"]',
    217             'input[id*="referral"]',
    218             'input[id*="source"]',
    219             'input[class*="referral"]',
    220             'input[class*="source"]',
    221            
    222             // Formidable-specific patterns
    223             'input[name^="item_meta["][id*="referral"]',
    224             'input[name^="item_meta["][id*="source"]'
    225         ];
     204        log('Populating form fields with attribution data', attribution);
     205
     206        // Map of attribution fields to their values
     207        const attributionFields = {
     208            'attribution_source': attribution.utm_source || '',
     209            'attribution_medium': attribution.utm_medium || '',
     210            'attribution_campaign': attribution.utm_campaign || '',
     211            'attribution_term': attribution.utm_term || '',
     212            'attribution_content': attribution.utm_content || '',
     213            'attribution_gclid': attribution.gclid || '',
     214            'attribution_landing_page': attribution.landing_page || '',
     215            'attribution_timestamp': attribution.timestamp || ''
     216        };
    226217
    227218        let fieldsPopulated = 0;
    228219
    229         selectors.forEach(selector => {
    230             const fields = document.querySelectorAll(selector);
    231             log(`Selector "${selector}" found ${fields.length} fields`);
    232            
    233             fields.forEach(field => {
    234                 if (field.type === 'hidden' || field.type === 'text') {
    235                     if (!field.value || field.value === '') {
    236                         field.value = referralSource;
    237                         fieldsPopulated++;
    238                         log('Field populated', {
    239                             selector,
    240                             id: field.id,
    241                             name: field.name,
    242                             type: field.type,
    243                             value: referralSource
    244                         });
    245                     } else {
    246                         log('Field skipped (already has value)', {
    247                             selector,
    248                             id: field.id,
    249                             name: field.name,
    250                             existingValue: field.value
    251                         });
     220        // Populate each attribution field
     221        Object.entries(attributionFields).forEach(([fieldName, value]) => {
     222            // Define selectors for each field type
     223            const selectors = [
     224                // Direct name match (Fluent Forms)
     225                `input[name="${fieldName}"]`,
     226
     227                // ID-based selectors (Formidable Forms)
     228                `input[id*="${fieldName}"]`,
     229                `input[id="field_${fieldName}"]`,
     230
     231                // aria-describedby selectors (Formidable Forms)
     232                `input[aria-describedby*="${fieldName}"]`,
     233
     234                // Formidable-specific patterns
     235                `input[name^="item_meta["][id*="${fieldName}"]`,
     236
     237                // Gravity Forms patterns
     238                `input[class*="${fieldName}"]`
     239            ];
     240
     241            selectors.forEach(selector => {
     242                const fields = document.querySelectorAll(selector);
     243
     244                fields.forEach(field => {
     245                    if (field.type === 'hidden' || field.type === 'text') {
     246                        if (!field.value || field.value === '') {
     247                            field.value = value;
     248                            fieldsPopulated++;
     249                            log('Field populated', {
     250                                selector,
     251                                fieldName,
     252                                id: field.id,
     253                                name: field.name,
     254                                type: field.type,
     255                                value: value
     256                            });
     257                        }
    252258                    }
    253                 } else {
    254                     log('Field skipped (wrong type)', {
    255                         selector,
    256                         id: field.id,
    257                         name: field.name,
    258                         type: field.type
    259                     });
    260                 }
     259                });
    261260            });
    262261        });
    263262
    264263        log(`Total fields populated: ${fieldsPopulated}`);
    265        
     264
    266265        // Trigger custom event for other scripts
    267266        if (fieldsPopulated > 0) {
     267            window.dispatchEvent(new CustomEvent('attributionDataPopulated', {
     268                detail: { attribution: attribution, fieldsCount: fieldsPopulated }
     269            }));
     270        }
     271
     272        // Also trigger legacy event for backward compatibility
     273        if (attribution.utm_source) {
    268274            window.dispatchEvent(new CustomEvent('referralSourcePopulated', {
    269                 detail: { source: referralSource, fieldsCount: fieldsPopulated }
     275                detail: { source: attribution.utm_source, fieldsCount: fieldsPopulated }
    270276            }));
    271277        }
     
    323329    // Expose functions globally for backward compatibility and manual usage
    324330    window.getReferralSource = getReferralSource;
     331    window.getAttributionData = getAttributionData;
    325332    window.populateFormFields = populateFormFields;
    326333    window.FormAttributionTracking = {
    327334        getReferralSource,
     335        getAttributionData,
    328336        populateFormFields,
    329337        setCookie,
  • form-attribution-tracking/trunk/attribution-tracking.php

    r3397396 r3397405  
    44 * Plugin URI: https://wordpress.org/plugins/form-attribution-tracking/
    55 * Description: Automatically adds referral source fields to Formidable, Fluent  and Gravity Forms
    6  * Version: 1.1.1
     6 * Version: 1.1.2
    77 * Author: Ryan Howard
    88 * Author URI: https://www.ryanhoward.dev
     
    2323
    2424// Define plugin constants
    25 define('ATTRIBUTION_TRACKING_VERSION', '1.1.1');
     25define('ATTRIBUTION_TRACKING_VERSION', '1.1.2');
    2626define('ATTRIBUTION_TRACKING_PLUGIN_FILE', __FILE__);
    2727define('ATTRIBUTION_TRACKING_PLUGIN_DIR', plugin_dir_path(__FILE__));
  • form-attribution-tracking/trunk/readme.md

    r3397396 r3397405  
    66Tested up to: 6.8
    77Requires PHP: 8.0
    8 Stable tag: 1.1.1
     8Stable tag: 1.1.2
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
  • form-attribution-tracking/trunk/src/Abstracts/AbstractFormIntegration.php

    r3397093 r3397405  
    1313{
    1414    /**
    15      * The field name used for referral source
     15     * Attribution field definitions (field_name => label)
    1616     */
    17     protected const FIELD_NAME = 'referral_source';
     17    protected const ATTRIBUTION_FIELDS = [
     18        'attribution_source' => 'Attribution Source',
     19        'attribution_medium' => 'Attribution Medium',
     20        'attribution_campaign' => 'Attribution Campaign',
     21        'attribution_term' => 'Attribution Keyword',
     22        'attribution_content' => 'Attribution Content',
     23        'attribution_gclid' => 'Google Click ID',
     24        'attribution_landing_page' => 'Landing Page',
     25        'attribution_timestamp' => 'First Click Time',
     26    ];
    1827
    1928    /**
    20      * The field label used for referral source
     29     * Legacy field name for backward compatibility
    2130     */
    22     protected const FIELD_LABEL = 'Referral Source';
     31    protected const FIELD_NAME = 'attribution_source';
     32
     33    /**
     34     * Legacy field label for backward compatibility
     35     */
     36    protected const FIELD_LABEL = 'Attribution Source';
    2337
    2438    /**
    2539     * The field description
    2640     */
    27     protected const FIELD_DESCRIPTION = 'Automatically populated referral source field';
     41    protected const FIELD_DESCRIPTION = 'Automatically populated attribution tracking fields';
    2842
    2943    /**
  • form-attribution-tracking/trunk/src/Integrations/FluentFormsIntegration.php

    r3397112 r3397405  
    9191    {
    9292        $field_name = $this->getReferralSourceFieldName();
    93 
     93       
    9494        // Only populate if field is empty (JavaScript failed)
    9595        if (!isset($data[$field_name]) || empty($data[$field_name])) {
     
    121121
    122122    /**
    123      * Add referral source field to a specific form
    124      * 
     123     * Add attribution fields to a specific form
     124     *
    125125     * @param int $formId The form ID
    126126     * @return bool Success status
     
    132132        }
    133133
    134         // Check if field already exists
     134        // Check if fields already exist
    135135        if ($this->hasReferralSourceField($formId)) {
    136             $this->log('Referral source field already exists', ['form_id' => $formId]);
     136            $this->log('Attribution fields already exist', ['form_id' => $formId]);
    137137            return true;
    138138        }
     
    152152            }
    153153
    154             // Create referral source field
    155             $referralField = [
    156                 'index' => count($formFields['fields']),
    157                 'element' => 'input_hidden',
    158                 'attributes' => [
    159                     'type' => 'hidden',
    160                     'name' => static::FIELD_NAME,
    161                     'value' => '',
    162                     'class' => 'referral-source-field',
    163                 ],
    164                 'settings' => [
    165                     'admin_field_label' => static::FIELD_LABEL,
    166                     'help_message' => static::FIELD_DESCRIPTION,
    167                 ],
    168                 'editor_options' => [
    169                     'title' => static::FIELD_LABEL,
    170                     'icon_class' => 'ff-edit-hidden-field',
    171                     'template' => 'inputHidden',
    172                 ],
    173                 'uniqElKey' => 'el_' . time() . '_' . wp_rand(1000, 9999),
    174             ];
    175 
    176             // Add the field to form fields
    177             $formFields['fields'][] = $referralField;
     154            $baseIndex = count($formFields['fields']);
     155            $createdFields = [];
     156
     157            // Create all attribution fields
     158            foreach (static::ATTRIBUTION_FIELDS as $fieldKey => $fieldLabel) {
     159                $attributionField = [
     160                    'index' => $baseIndex++,
     161                    'element' => 'input_hidden',
     162                    'attributes' => [
     163                        'type' => 'hidden',
     164                        'name' => $fieldKey,
     165                        'value' => '',
     166                        'class' => 'attribution-field attribution-' . str_replace('_', '-', $fieldKey),
     167                    ],
     168                    'settings' => [
     169                        'admin_field_label' => $fieldLabel,
     170                        'help_message' => static::FIELD_DESCRIPTION,
     171                    ],
     172                    'editor_options' => [
     173                        'title' => $fieldLabel,
     174                        'icon_class' => 'ff-edit-hidden-field',
     175                        'template' => 'inputHidden',
     176                    ],
     177                    'uniqElKey' => 'el_' . time() . '_' . wp_rand(1000, 9999),
     178                ];
     179
     180                // Add the field to form fields
     181                $formFields['fields'][] = $attributionField;
     182                $createdFields[] = ['key' => $fieldKey, 'label' => $fieldLabel];
     183
     184                // Small delay to ensure unique timestamps
     185                usleep(1000);
     186            }
    178187
    179188            // Update the form
     
    181190            $form->save();
    182191
    183             $this->log('Referral source field created successfully', [
    184                 'form_id' => $formId,
    185                 'field_key' => $referralField['uniqElKey']
     192            $this->log('Attribution fields created successfully', [
     193                'form_id' => $formId,
     194                'fields_created' => count($createdFields),
     195                'fields' => $createdFields
    186196            ]);
    187197
     
    189199
    190200        } catch (\Exception $e) {
    191             $this->log('Exception while creating referral source field', [
     201            $this->log('Exception while creating attribution fields', [
    192202                'form_id' => $formId,
    193203                'error' => $e->getMessage()
     
    198208
    199209    /**
    200      * Remove referral source field from a specific form
    201      * 
     210     * Remove all attribution fields from a specific form
     211     *
    202212     * @param int $formId The form ID
    203213     * @return bool Success status
     
    212222            $form = \FluentForm\App\Models\Form::find($formId);
    213223            if (!$form) {
    214                 return true; // Form doesn't exist, consider field removed
     224                return true; // Form doesn't exist, consider fields removed
    215225            }
    216226
     
    218228            $formFields = json_decode($form->form_fields, true);
    219229            if (!$formFields || !isset($formFields['fields'])) {
    220                 return true; // No fields structure, consider field removed
    221             }
    222 
    223             // Find and remove referral source field
    224             $fieldFound = false;
     230                return true; // No fields structure, consider fields removed
     231            }
     232
     233            // Get list of attribution field names
     234            $attributionFieldNames = array_keys(static::ATTRIBUTION_FIELDS);
     235
     236            // Find and remove all attribution fields
     237            $removedCount = 0;
    225238            $newFields = [];
    226            
     239
    227240            foreach ($formFields['fields'] as $field) {
    228                 if (isset($field['attributes']['name']) && 
    229                     $field['attributes']['name'] === static::FIELD_NAME &&
     241                if (isset($field['attributes']['name']) &&
     242                    in_array($field['attributes']['name'], $attributionFieldNames, true) &&
    230243                    $field['element'] === 'input_hidden') {
    231                     $fieldFound = true;
     244                    $removedCount++;
    232245                    continue; // Skip this field (remove it)
    233246                }
     
    235248            }
    236249
    237             if (!$fieldFound) {
    238                 return true; // Field didn't exist, consider it removed
     250            if ($removedCount === 0) {
     251                return true; // No fields to remove
    239252            }
    240253
     
    244257            $form->save();
    245258
    246             $this->log('Referral source field removed successfully', ['form_id' => $formId]);
     259            $this->log('Attribution fields removed successfully', [
     260                'form_id' => $formId,
     261                'fields_removed' => $removedCount
     262            ]);
    247263            return true;
    248264
    249265        } catch (\Exception $e) {
    250             $this->log('Exception while removing referral source field', [
     266            $this->log('Exception while removing attribution fields', [
    251267                'form_id' => $formId,
    252268                'error' => $e->getMessage()
     
    257273
    258274    /**
    259      * Check if a form already has a referral source field
    260      * 
     275     * Check if a form already has attribution fields
     276     *
    261277     * @param int $formId The form ID
    262278     * @return bool
     
    280296            }
    281297
    282             // Look for referral source field
     298            // Look for the main attribution_source field
    283299            foreach ($formFields['fields'] as $field) {
    284                 if (isset($field['attributes']['name']) && 
    285                     $field['attributes']['name'] === static::FIELD_NAME &&
     300                if (isset($field['attributes']['name']) &&
     301                    $field['attributes']['name'] === 'attribution_source' &&
    286302                    $field['element'] === 'input_hidden') {
    287303                    return true;
     
    292308
    293309        } catch (\Exception $e) {
    294             $this->log('Exception while checking for referral source field', [
     310            $this->log('Exception while checking for attribution source field', [
    295311                'form_id' => $formId,
    296312                'error' => $e->getMessage()
  • form-attribution-tracking/trunk/src/Integrations/FormidableFormsIntegration.php

    r3397093 r3397405  
    114114
    115115    /**
    116      * Add referral source field to a specific form
    117      * 
     116     * Add attribution fields to a specific form
     117     *
    118118     * @param int $formId The form ID
    119119     * @return bool Success status
     
    125125        }
    126126
    127         // Check if field already exists
     127        // Check if fields already exist
    128128        if ($this->hasReferralSourceField($formId)) {
    129             $this->log('Referral source field already exists', ['form_id' => $formId]);
     129            $this->log('Attribution fields already exist', ['form_id' => $formId]);
    130130            return true;
    131131        }
    132132
    133133        try {
    134             $field_values = [
    135                 'form_id' => $formId,
    136                 'type' => 'hidden',
    137                 'name' => static::FIELD_LABEL,
    138                 'description' => static::FIELD_DESCRIPTION,
    139                 'field_key' => static::FIELD_NAME,
    140                 'default_value' => '',
    141                 'field_order' => 999, // Put it at the end
    142                 'required' => 0,
    143                 'field_options' => [
    144                     'size' => '',
    145                     'max' => '',
    146                     'label_position' => '',
    147                     'invalid' => '',
    148                     'classes' => 'referral-source-field',
    149                     'description_loc' => '',
    150                 ]
    151             ];
    152 
    153             // Use Formidable's filter to customize field before creation
    154             $field_values = apply_filters('frm_before_field_created', $field_values);
    155            
    156             // Create the field
    157             $field_id = \FrmField::create($field_values);
    158 
    159             if ($field_id) {
    160                 $this->log('Referral source field created successfully', [
     134            $fieldOrder = 999; // Start at the end
     135            $createdFields = [];
     136
     137            // Create all attribution fields
     138            foreach (static::ATTRIBUTION_FIELDS as $fieldKey => $fieldLabel) {
     139                $field_values = [
    161140                    'form_id' => $formId,
    162                     'field_id' => $field_id
     141                    'type' => 'hidden',
     142                    'name' => $fieldLabel,
     143                    'description' => static::FIELD_DESCRIPTION,
     144                    'field_key' => $fieldKey,
     145                    'default_value' => '',
     146                    'field_order' => $fieldOrder++,
     147                    'required' => 0,
     148                    'field_options' => [
     149                        'size' => '',
     150                        'max' => '',
     151                        'label_position' => '',
     152                        'invalid' => '',
     153                        'classes' => 'attribution-field attribution-' . str_replace('_', '-', $fieldKey),
     154                        'description_loc' => '',
     155                    ]
     156                ];
     157
     158                // Use Formidable's filter to customize field before creation
     159                $field_values = apply_filters('frm_before_field_created', $field_values);
     160
     161                // Create the field
     162                $field_id = \FrmField::create($field_values);
     163
     164                if ($field_id) {
     165                    $createdFields[] = ['key' => $fieldKey, 'id' => $field_id];
     166                } else {
     167                    $this->log('Failed to create attribution field', [
     168                        'form_id' => $formId,
     169                        'field_key' => $fieldKey
     170                    ]);
     171                }
     172            }
     173
     174            if (count($createdFields) > 0) {
     175                $this->log('Attribution fields created successfully', [
     176                    'form_id' => $formId,
     177                    'fields_created' => count($createdFields),
     178                    'fields' => $createdFields
    163179                ]);
    164180                return true;
    165181            }
    166182
    167             $this->log('Failed to create referral source field', ['form_id' => $formId]);
     183            $this->log('Failed to create any attribution fields', ['form_id' => $formId]);
    168184            return false;
    169185
    170186        } catch (\Exception $e) {
    171             $this->log('Exception while creating referral source field', [
     187            $this->log('Exception while creating attribution fields', [
    172188                'form_id' => $formId,
    173189                'error' => $e->getMessage()
     
    178194
    179195    /**
    180      * Remove referral source field from a specific form
    181      * 
     196     * Remove all attribution fields from a specific form
     197     *
    182198     * @param int $formId The form ID
    183199     * @return bool Success status
     
    190206
    191207        try {
    192             $field = $this->getReferralSourceField($formId);
    193            
    194             if (!$field) {
    195                 return true; // Field doesn't exist, consider it removed
    196             }
    197 
    198             $result = \FrmField::destroy($field->id);
    199            
    200             if ($result) {
    201                 $this->log('Referral source field removed successfully', [
     208            $removedCount = 0;
     209
     210            // Remove all attribution fields
     211            foreach (array_keys(static::ATTRIBUTION_FIELDS) as $fieldKey) {
     212                $fields = \FrmField::getAll([
     213                    'fi.form_id' => $formId,
     214                    'fi.type' => 'hidden',
     215                    'fi.field_key like' => $fieldKey,
     216                ], 'id ASC');
     217
     218                foreach ($fields as $field) {
     219                    $result = \FrmField::destroy($field->id);
     220                    if ($result) {
     221                        $removedCount++;
     222                        $this->log('Attribution field removed', [
     223                            'form_id' => $formId,
     224                            'field_id' => $field->id,
     225                            'field_key' => $fieldKey
     226                        ]);
     227                    }
     228                }
     229            }
     230
     231            if ($removedCount > 0) {
     232                $this->log('Attribution fields removed successfully', [
    202233                    'form_id' => $formId,
    203                     'field_id' => $field->id
     234                    'fields_removed' => $removedCount
    204235                ]);
    205                 return true;
    206             }
    207 
    208             return false;
     236            }
     237
     238            return true;
    209239
    210240        } catch (\Exception $e) {
    211             $this->log('Exception while removing referral source field', [
     241            $this->log('Exception while removing attribution fields', [
    212242                'form_id' => $formId,
    213243                'error' => $e->getMessage()
     
    261291
    262292    /**
    263      * Get the referral source field for a specific form
    264      * 
     293     * Get the main attribution source field for a specific form
     294     *
    265295     * @param int $formId
    266296     * @return object|null
     
    273303
    274304        try {
    275             // Use getAll to search by multiple criteria
    276             $fields = \FrmField::getAll( [
    277                     'fi.form_id'         => $formId,
    278                     'fi.type'            => 'hidden',
    279                     'fi.field_key like'  => static::FIELD_NAME,
    280                 ],
    281                 'id ASC'
    282             );
    283 
    284             $this->log('Searching for referral source field', [
     305            // Look for the main attribution_source field
     306            $fields = \FrmField::getAll([
     307                'fi.form_id' => $formId,
     308                'fi.type' => 'hidden',
     309                'fi.field_key like' => 'attribution_source',
     310            ], 'id ASC');
     311
     312            $this->log('Searching for attribution source field', [
    285313                'form_id' => $formId,
    286                 'field_key' => static::FIELD_NAME,
     314                'field_key' => 'attribution_source',
    287315                'fields_found' => count($fields)
    288316            ]);
     
    292320
    293321        } catch (\Exception $e) {
    294             $this->log('Exception while getting referral source field', [
     322            $this->log('Exception while getting attribution source field', [
    295323                'form_id' => $formId,
    296324                'error' => $e->getMessage()
  • form-attribution-tracking/trunk/src/Integrations/GravityFormsIntegration.php

    r3397093 r3397405  
    5050
    5151        if ($current !== '') {
    52             return; // JS уже заполнил
     52            return; // JavaScript already populated the field
    5353        }
    5454
     
    6565
    6666    /**
    67      * ✅ Добавление поля без GFAPI::add_field() (чистый, рабочий способ)
     67     * Add attribution fields to a specific form
     68     *
     69     * @param int $formId The form ID
     70     * @return bool Success status
    6871     */
    6972    public function addReferralSourceField(int $formId): bool
     
    7477
    7578        if ($this->hasReferralSourceField($formId)) {
    76             $this->log('Referral source field already exists', ['form_id' => $formId]);
     79            $this->log('Attribution fields already exist', ['form_id' => $formId]);
    7780            return true;
    7881        }
     
    8891        }
    8992
    90         $fieldArr = [
    91             'type'         => 'hidden',
    92             'label'        => static::FIELD_LABEL,
    93             'adminLabel'   => static::FIELD_LABEL,
    94             'description'  => static::FIELD_DESCRIPTION,
    95             'inputName'    => static::FIELD_NAME,
    96             'visibility'   => 'hidden',
    97             'allowsPrepopulate' => true,
    98             'defaultValue' => '',
    99         ];
    100 
    101         $fieldObj = \GF_Fields::create($fieldArr);
    102         $form['fields'][] = $fieldObj;
     93        $createdFields = [];
     94
     95        // Create all attribution fields
     96        foreach (static::ATTRIBUTION_FIELDS as $fieldKey => $fieldLabel) {
     97            $fieldArr = [
     98                'type' => 'hidden',
     99                'label' => $fieldLabel,
     100                'adminLabel' => $fieldLabel,
     101                'description' => static::FIELD_DESCRIPTION,
     102                'inputName' => $fieldKey,
     103                'visibility' => 'hidden',
     104                'allowsPrepopulate' => true,
     105                'defaultValue' => '',
     106                'cssClass' => 'attribution-field attribution-' . str_replace('_', '-', $fieldKey),
     107            ];
     108
     109            $fieldObj = \GF_Fields::create($fieldArr);
     110            $form['fields'][] = $fieldObj;
     111            $createdFields[] = ['key' => $fieldKey, 'label' => $fieldLabel];
     112        }
    103113
    104114        $result = \GFAPI::update_form($form);
    105115        if (is_wp_error($result)) {
    106             $this->log('Failed to update form when adding field', [
     116            $this->log('Failed to update form when adding attribution fields', [
    107117                'form_id' => $formId,
    108                 'error'   => $result->get_error_message(),
     118                'error' => $result->get_error_message(),
    109119            ]);
    110120            return false;
    111121        }
    112122
    113         $this->log('Referral source field created successfully', ['form_id' => $formId]);
     123        $this->log('Attribution fields created successfully', [
     124            'form_id' => $formId,
     125            'fields_created' => count($createdFields),
     126            'fields' => $createdFields
     127        ]);
    114128
    115129        return true;
     
    127141        }
    128142
     143        // Get list of attribution field names
     144        $attributionFieldNames = array_keys(static::ATTRIBUTION_FIELDS);
     145
    129146        $newFields = [];
    130         $removed   = false;
     147        $removedCount = 0;
    131148
    132149        foreach ($form['fields'] as $field) {
    133150            if ($field instanceof \GF_Field &&
    134151                $field->type === 'hidden' &&
    135                 $field->inputName === static::FIELD_NAME) {
    136                 $removed = true;
     152                in_array($field->inputName, $attributionFieldNames, true)) {
     153                $removedCount++;
    137154                continue;
    138155            }
     
    140157        }
    141158
    142         if (!$removed) {
     159        if ($removedCount === 0) {
    143160            return true;
    144161        }
     
    148165
    149166        if (is_wp_error($res)) {
    150             $this->log('Failed to remove referral field', [
     167            $this->log('Failed to remove attribution fields', [
    151168                'form_id' => $formId,
    152                 'error'   => $res->get_error_message(),
     169                'error' => $res->get_error_message(),
    153170            ]);
    154171            return false;
    155172        }
    156173
    157         $this->log('Referral source field removed', ['form_id' => $formId]);
     174        $this->log('Attribution fields removed successfully', [
     175            'form_id' => $formId,
     176            'fields_removed' => $removedCount
     177        ]);
    158178        return true;
    159179    }
     
    200220        }
    201221
     222        // Look for the main attribution_source field
    202223        foreach ($form['fields'] as $field) {
    203224            if ($field instanceof \GF_Field &&
    204225                $field->type === 'hidden' &&
    205                 $field->inputName === static::FIELD_NAME) {
     226                $field->inputName === 'attribution_source') {
    206227                return $field;
    207228            }
  • form-attribution-tracking/trunk/src/Plugin.php

    r3397093 r3397405  
    250250            'attribution_tracking_general',
    251251            ['option_name' => 'auto_add_to_new_forms', 'description' => __('Automatically add referral source field to new forms', 'form-attribution-tracking')]
    252         );
    253 
    254         add_settings_field(
    255             'field_name',
    256             __('Field Name', 'form-attribution-tracking'),
    257             [$this, 'renderTextField'],
    258             'form-attribution-tracking',
    259             'attribution_tracking_general',
    260             ['option_name' => 'field_name', 'description' => __('Name for the referral source field', 'form-attribution-tracking')]
    261252        );
    262253
     
    709700        global $wpdb;
    710701
    711         $field_name = $this->getOption('field_name', 'referral_source');
     702        $field_name = 'attribution';
    712703        $like = $wpdb->esc_like($field_name) . '%';
    713704
  • form-attribution-tracking/trunk/src/Views/admin-page.php

    r3397093 r3397405  
    4646                        </th>
    4747                        <td>
    48                             <input type="checkbox" 
    49                                    id="auto_add_to_new_forms" 
    50                                    name="auto_add_to_new_forms" 
    51                                    value="1" 
     48                            <input type="checkbox"
     49                                   id="auto_add_to_new_forms"
     50                                   name="auto_add_to_new_forms"
     51                                   value="1"
    5252                                   <?php checked($plugin->getOption('auto_add_to_new_forms', true)); ?> />
    5353                            <p class="description">
    54                                 <?php esc_html_e('Automatically add referral source field to newly created forms', 'form-attribution-tracking'); ?>
     54                                <?php esc_html_e('Automatically add full Google Ads attribution tracking fields to newly created forms (captures UTM parameters, GCLID, landing page, and timestamp)', 'form-attribution-tracking'); ?>
    5555                            </p>
    5656                        </td>
    5757                    </tr>
    5858                   
    59                     <tr>
    60                         <th scope="row">
    61                             <label for="field_name">
    62                                 <?php esc_html_e('Field Name', 'form-attribution-tracking'); ?>
    63                             </label>
    64                         </th>
    65                         <td>
    66                             <input type="text"
    67                                    id="field_name"
    68                                    name="field_name"
    69                                    value="<?php echo esc_attr($plugin->getOption('field_name', 'referral_source')); ?>"
    70                                    class="regular-text" />
    71                             <p class="description">
    72                                 <?php esc_html_e('Name for the referral source field (used in forms and database)', 'form-attribution-tracking'); ?>
    73                             </p>
    74                         </td>
    75                     </tr>
    7659                   
    7760                    <tr>
     
    8265                        </th>
    8366                        <td>
    84                             <input type="number" 
    85                                    id="cookie_duration" 
    86                                    name="cookie_duration" 
    87                                    value="<?php echo esc_attr($plugin->getOption('cookie_duration', 30)); ?>" 
    88                                    min="1" 
     67                            <input type="number"
     68                                   id="cookie_duration"
     69                                   name="cookie_duration"
     70                                   value="<?php echo esc_attr($plugin->getOption('cookie_duration', 30)); ?>"
     71                                   min="1"
    8972                                   max="365" />
    9073                            <p class="description">
    91                                 <?php esc_html_e('How long to store referral source in visitor cookies (1-365 days)', 'form-attribution-tracking'); ?>
     74                                <?php esc_html_e('How long to store first-touch attribution data in visitor cookies (1-365 days)', 'form-attribution-tracking'); ?>
    9275                            </p>
    9376                        </td>
     
    135118                <h2><?php esc_html_e('Available Form Plugins', 'form-attribution-tracking'); ?></h2>
    136119                <p class="description">
    137                     <?php esc_html_e('Manage referral source fields across all forms in your detected form plugins.', 'form-attribution-tracking'); ?>
     120                    <?php esc_html_e('Manage Google Ads attribution tracking fields across all forms in your detected form plugins. Each form will receive 8 hidden fields to capture complete attribution data.', 'form-attribution-tracking'); ?>
    138121                </p>
    139122               
Note: See TracChangeset for help on using the changeset viewer.