{"id":50709238,"url":"https://github.com/coowinit/wordpress-facebook-ads-form-tracking","last_synced_at":"2026-06-09T14:01:46.662Z","repository":{"id":354563499,"uuid":"1224137475","full_name":"coowinit/wordpress-facebook-ads-form-tracking","owner":"coowinit","description":"一个用于 WordPress 自定义表单的 Facebook 广告询盘来源追踪方案，可在邮件中显示 Facebook Ads: Yes/No。","archived":false,"fork":false,"pushed_at":"2026-04-29T05:19:59.000Z","size":26,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T05:24:14.940Z","etag":null,"topics":["ajax-form","facebook-ads","form-tracking","lead-tracking","meta-ads","utm-tracking","wordpress","wordpress-theme","wp-mail"],"latest_commit_sha":null,"homepage":"https://coowinit.github.io/wordpress-facebook-ads-form-tracking/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coowinit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T01:59:55.000Z","updated_at":"2026-04-29T05:20:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/coowinit/wordpress-facebook-ads-form-tracking","commit_stats":null,"previous_names":["coowinit/wordpress-facebook-ads-form-tracking"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/coowinit/wordpress-facebook-ads-form-tracking","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coowinit%2Fwordpress-facebook-ads-form-tracking","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coowinit%2Fwordpress-facebook-ads-form-tracking/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coowinit%2Fwordpress-facebook-ads-form-tracking/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coowinit%2Fwordpress-facebook-ads-form-tracking/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coowinit","download_url":"https://codeload.github.com/coowinit/wordpress-facebook-ads-form-tracking/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coowinit%2Fwordpress-facebook-ads-form-tracking/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34110012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajax-form","facebook-ads","form-tracking","lead-tracking","meta-ads","utm-tracking","wordpress","wordpress-theme","wp-mail"],"created_at":"2026-06-09T14:01:46.002Z","updated_at":"2026-06-09T14:01:46.650Z","avatar_url":"https://github.com/coowinit.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WordPress 表单 Facebook 广告来源追踪指南\n\n这是一个适用于 WordPress 自定义主题表单的简化版 Facebook / Meta 广告来源追踪方案。\n\n目标很简单：\n\n\u003e 用户从 Facebook 广告进入网站并提交表单后，询盘邮件中显示 `Facebook Ads: Yes`；非广告来源则显示 `Facebook Ads: No`。\n\n同时，本方案还会记录：\n\n- 用户第一次进入网站的页面\n- 带有 `fb_ads=1` 参数的 Facebook 广告落地页\n- 用户最终提交表单的页面\n\n---\n\n## 适用场景\n\n适合以下情况：\n\n- WordPress 自定义主题\n- 表单通过 Ajax 提交\n- 表单最终通过 `wp_mail()` 发送到邮箱\n- 不想接入复杂的 GA4 / Google Tag Manager / Meta Pixel 转化统计\n- 只想在邮件里快速判断询盘是否来自 Facebook 广告\n\n---\n\n## 实现原理\n\n广告链接中添加一个最简单的参数：\n\n```txt\nfb_ads=1\n```\n\n例如：\n\n```txt\nhttps://www.example.com/contact-us/?fb_ads=1\n```\n\n或者：\n\n```txt\nhttps://www.example.com/product-page/?fb_ads=1\n```\n\n当用户访问带有 `fb_ads=1` 的页面时，前端 JavaScript 会把来源信息保存到浏览器的 `sessionStorage` 中；为了兼容旧版本，也可以同时读取 `localStorage`。\n\n这样即使用户不是在广告落地页直接提交表单，而是先浏览其他页面，再进入 Contact 页面提交表单，也仍然可以识别为 Facebook 广告来源。\n\n示例路径：\n\n```txt\n1. 用户点击 Facebook 广告\n2. 进入 https://www.example.com/product-page/?fb_ads=1\n3. 浏览其他页面\n4. 进入 https://www.example.com/contact-us/\n5. 提交表单\n6. 邮件中显示 Facebook Ads: Yes\n```\n\n\n### 最终增强版推荐架构\n\n经过多表单测试后，推荐使用下面这套更稳的组合：\n\n| 模块 | 作用 |\n|---|---|\n| 前端 `sessionStorage` | 记录本次浏览会话中的 Facebook 广告来源 |\n| 全站 `wp_footer` 脚本 | 自动给页面里的所有表单补充隐藏字段 |\n| `window.evodekFbAdsTracking` | 给免费样品页、多步骤表单、手动 `new FormData()` 的 JS 读取使用 |\n| PHP Session | 服务器端兜底，防止部分表单漏传隐藏字段 |\n| 统一邮件函数 | 所有表单邮件统一输出 `Facebook Ads Tracking` 区块 |\n\n这种方式可以覆盖：\n\n- Contact Us / 代理商表单\n- 购物车提交表单\n- Decking Calculator 表单\n- Cladding Calculator 表单\n- Free Samples 免费样品多步骤表单\n\n---\n\n## 增强版：服务器端 PHP Session 兜底\n\n如果主题里已经有购物车功能，并且已经启用了 PHP Session，可以直接复用。\n\n如果没有，可以先添加：\n\n```php\nadd_action('init', 'theme_start_session', 1);\n\nfunction theme_start_session() {\n    if (!session_id()) {\n        session_start();\n    }\n}\n```\n\n然后添加服务器端兜底记录：\n\n```php\nadd_action('init', 'theme_capture_fb_ads_tracking_server_side', 2);\n\nfunction theme_capture_fb_ads_tracking_server_side() {\n    if (is_admin()) {\n        return;\n    }\n\n    if (!isset($_SESSION) || !is_array($_SESSION)) {\n        return;\n    }\n\n    $current_url = esc_url_raw(home_url(add_query_arg(array(), $_SERVER['REQUEST_URI'] ?? '/')));\n\n    if (empty($_SESSION['theme_first_landing_page'])) {\n        $_SESSION['theme_first_landing_page'] = $current_url;\n    }\n\n    if (isset($_GET['fb_ads']) \u0026\u0026 sanitize_text_field(wp_unslash($_GET['fb_ads'])) === '1') {\n        $_SESSION['theme_facebook_ads'] = 'Yes';\n        $_SESSION['theme_fb_ads_landing_page'] = $current_url;\n    }\n}\n```\n\n这一步的作用是：即使某个 Ajax 表单没有把隐藏字段提交到后台，PHP 仍然可以尝试从 Session 中识别广告来源。\n\n---\n\n## 增强版：自动给所有表单补隐藏字段\n\n如果网站有很多表单页面，不想每个页面手动添加隐藏域，可以让全站脚本自动补充字段。\n\n核心逻辑如下：\n\n```js\nvar trackingFieldNames = [\n  'facebook_ads',\n  'first_landing_page',\n  'fb_ads_landing_page'\n];\n\nfunction ensureTrackingFieldsInForms() {\n  var forms = document.querySelectorAll('form');\n\n  forms.forEach(function (form) {\n    trackingFieldNames.forEach(function (name) {\n      if (!form.querySelector('[name=\"' + name + '\"]')) {\n        var input = document.createElement('input');\n        input.type = 'hidden';\n        input.name = name;\n        input.className = 'theme-fb-ads-tracking-field';\n        form.appendChild(input);\n      }\n    });\n  });\n}\n```\n\n同时建议在全站脚本里暴露一个全局对象：\n\n```js\nwindow.evodekFbAdsTracking = {\n  facebook_ads: facebookAds,\n  first_landing_page: firstLandingPage,\n  fb_ads_landing_page: fbAdsLandingPage\n};\n```\n\n这样免费样品页这类独立 JS 文件也能读取到广告来源数据。\n\n---\n\n## Meta / Facebook 广告后台参数设置\n\n如果广告落地页是：\n\n```txt\nhttps://www.example.com/contact-us/\n```\n\n完整 URL 可以写成：\n\n```txt\nhttps://www.example.com/contact-us/?fb_ads=1\n```\n\n如果 Meta 广告后台有单独的 **URL Parameters** 输入框，只需要填写：\n\n```txt\nfb_ads=1\n```\n\n不需要填写复杂的 UTM 参数。\n\n---\n\n## 最终邮件显示效果\n\nFacebook 广告来的询盘：\n\n```txt\nFacebook Ads: Yes\nFirst Landing Page: https://www.example.com/product-page/?fb_ads=1\nFacebook Ads Landing Page: https://www.example.com/product-page/?fb_ads=1\nSubmitted Page URL: https://www.example.com/contact-us/\n```\n\n非 Facebook 广告来的询盘：\n\n```txt\nFacebook Ads: No\nFirst Landing Page: https://www.example.com/contact-us/\nFacebook Ads Landing Page:\nSubmitted Page URL: https://www.example.com/contact-us/\n```\n\n---\n\n## 第一步：在 functions.php 中加入全站追踪脚本\n\n将以下代码添加到主题的 `functions.php` 文件中。\n\n建议放在表单相关函数附近，或者文件底部。\n\n```php\n/**\n * 全站记录 Facebook 广告来源\n *\n * 广告链接示例：\n * https://www.example.com/contact-us/?fb_ads=1\n */\nadd_action('wp_footer', 'theme_output_fb_ads_tracking_script');\n\nfunction theme_output_fb_ads_tracking_script() {\n    if (is_admin()) {\n        return;\n    }\n    ?\u003e\n    \u003cscript\u003e\n    (function () {\n        var params = new URLSearchParams(window.location.search);\n        var hasFbAdsParam = params.get('fb_ads') === '1';\n        var currentUrl = window.location.href;\n\n        // 记录用户第一次进入网站的页面\n        if (!localStorage.getItem('first_landing_page')) {\n            localStorage.setItem('first_landing_page', currentUrl);\n        }\n\n        // 如果当前页面 URL 中带有 fb_ads=1，则标记为 Facebook 广告来源\n        if (hasFbAdsParam) {\n            localStorage.setItem('facebook_ads', 'Yes');\n            localStorage.setItem('fb_ads_landing_page', currentUrl);\n        } else if (!localStorage.getItem('facebook_ads')) {\n            localStorage.setItem('facebook_ads', 'No');\n        }\n\n        function setField(id, value) {\n            var field = document.getElementById(id);\n            if (field) {\n                field.value = value || '';\n            }\n        }\n\n        // 自动填充表单隐藏字段\n        setField('facebook_ads', localStorage.getItem('facebook_ads') || 'No');\n        setField('first_landing_page', localStorage.getItem('first_landing_page') || '');\n        setField('fb_ads_landing_page', localStorage.getItem('fb_ads_landing_page') || '');\n    })();\n    \u003c/script\u003e\n    \u003c?php\n}\n```\n\n### 为什么脚本要放到 functions.php？\n\n因为脚本需要在全站运行。\n\n如果只放在 Contact 页面，当用户先进入产品页：\n\n```txt\nhttps://www.example.com/product-page/?fb_ads=1\n```\n\n然后再进入 Contact 页面提交表单，Contact 页面就无法知道用户最初是从广告进来的。\n\n通过 `wp_footer` 输出到全站，就可以在用户第一次进入网站时立即记录来源。\n\n---\n\n## 第二步：在表单页面加入隐藏字段\n\n在表单提交按钮之前加入以下隐藏字段。\n\n```html\n\u003cinput type=\"hidden\" name=\"facebook_ads\" id=\"facebook_ads\" value=\"No\"\u003e\n\u003cinput type=\"hidden\" name=\"first_landing_page\" id=\"first_landing_page\"\u003e\n\u003cinput type=\"hidden\" name=\"fb_ads_landing_page\" id=\"fb_ads_landing_page\"\u003e\n```\n\n示例：\n\n```html\n\u003cform id=\"contactus\" class=\"mb-4\"\u003e\n    \u003c!-- 其他表单字段 --\u003e\n\n    \u003cinput type=\"hidden\" name=\"facebook_ads\" id=\"facebook_ads\" value=\"No\"\u003e\n    \u003cinput type=\"hidden\" name=\"first_landing_page\" id=\"first_landing_page\"\u003e\n    \u003cinput type=\"hidden\" name=\"fb_ads_landing_page\" id=\"fb_ads_landing_page\"\u003e\n\n    \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n如果你的表单使用的是 jQuery Ajax，并通过下面这种方式收集字段：\n\n```js\nvar formData = $form.serializeArray();\n```\n\n那么新增的隐藏字段会自动随表单一起提交，不需要额外处理。\n\n---\n\n## 第三步：Ajax 提交时保留最终提交页面 URL\n\n如果你的 Ajax 里已经有类似代码，可以保留：\n\n```js\nformData.push({ name: 'page_url', value: window.location.href });\n```\n\n完整示例：\n\n```js\njQuery(function($){\n    $('#contactus').on('submit', function(e){\n        e.preventDefault();\n\n        var $form = $(this);\n        var $btn  = $form.find('button[type=\"submit\"]');\n\n        if ($btn.prop('disabled')) {\n            return;\n        }\n\n        var formData = $form.serializeArray();\n        formData.push({ name: 'action', value: 'send_contact_us_email' });\n        formData.push({ name: 'page_url', value: window.location.href });\n\n        $.ajax({\n            url: ajaxurl_or_admin_ajax_url,\n            type: 'POST',\n            dataType: 'json',\n            data: $.param(formData),\n            beforeSend: function(){\n                $btn.prop('disabled', true).text('Sending...');\n            },\n            success: function(res){\n                if (res.success) {\n                    alert(res.data.msg || 'Submitted successfully.');\n                    $form[0].reset();\n                } else {\n                    alert(res.data \u0026\u0026 res.data.msg ? res.data.msg : 'Submission failed.');\n                }\n            },\n            error: function(){\n                alert('Network or server error, please try again later.');\n            },\n            complete: function(){\n                $btn.prop('disabled', false).text('Submit');\n            }\n        });\n    });\n});\n```\n\n注意：`ajaxurl_or_admin_ajax_url` 需要替换成你主题中的 Ajax 地址，例如：\n\n```php\n\u003c?php echo esc_url(admin_url('admin-ajax.php')); ?\u003e\n```\n\n---\n\n---\n\n## 第三步补充：手动 `new FormData()` 的表单需要额外处理\n\n有些复杂表单并不是直接提交整个 `\u003cform\u003e`，也不是用：\n\n```js\nvar formData = $form.serializeArray();\n```\n\n而是像免费样品页这种逻辑，在独立 JS 文件中手动创建：\n\n```js\nconst formData = new FormData();\n```\n\n然后逐个添加字段：\n\n```js\nformData.append('action', 'evodek_submit_sample_request');\nformData.append('first_name', payload.first_name || '');\nformData.append('email', payload.email || '');\n```\n\n这种写法需要特别注意：\n\n\u003e 即使页面里已经有隐藏字段，如果 JS 没有把这些字段 `append()` 进去，Ajax 请求也不会提交这些 Facebook Ads 数据。\n\n### 免费样品页这类表单的推荐处理方式\n\n在独立 JS 文件中增加一个读取追踪数据的方法：\n\n```js\nfunction getFbAdsTrackingData() {\n  function getFieldValue(fieldName) {\n    const field = document.querySelector(`[name=\"${fieldName}\"], #${fieldName}`);\n    return field ? field.value : '';\n  }\n\n  const globalTracking = window.themeFbAdsTracking || window.evodekFbAdsTracking || {};\n\n  return {\n    facebook_ads:\n      getFieldValue('facebook_ads') ||\n      globalTracking.facebook_ads ||\n      sessionStorage.getItem('facebook_ads') ||\n      localStorage.getItem('facebook_ads') ||\n      'No',\n\n    first_landing_page:\n      getFieldValue('first_landing_page') ||\n      globalTracking.first_landing_page ||\n      sessionStorage.getItem('first_landing_page') ||\n      localStorage.getItem('first_landing_page') ||\n      window.location.href,\n\n    fb_ads_landing_page:\n      getFieldValue('fb_ads_landing_page') ||\n      globalTracking.fb_ads_landing_page ||\n      sessionStorage.getItem('fb_ads_landing_page') ||\n      localStorage.getItem('fb_ads_landing_page') ||\n      ''\n  };\n}\n```\n\n然后在构建 Ajax `FormData` 时追加这 3 个字段：\n\n```js\nconst fbAdsTracking = getFbAdsTrackingData();\n\nformData.append('facebook_ads', fbAdsTracking.facebook_ads === 'Yes' ? 'Yes' : 'No');\nformData.append('first_landing_page', fbAdsTracking.first_landing_page || '');\nformData.append('fb_ads_landing_page', fbAdsTracking.fb_ads_landing_page || '');\n```\n\n完整示例：\n\n```js\nfunction buildAjaxFormData(payload) {\n  const formData = new FormData();\n\n  formData.append('action', 'evodek_submit_sample_request');\n  formData.append('sample_request_nonce', ajaxNonce);\n\n  formData.append('first_name', payload.first_name || '');\n  formData.append('last_name', payload.last_name || '');\n  formData.append('phone', payload.phone || '');\n  formData.append('email', payload.email || '');\n  formData.append('message', payload.message || '');\n\n  formData.append('page_url', window.location.href);\n  formData.append('user_agent', navigator.userAgent || '');\n\n  const fbAdsTracking = getFbAdsTrackingData();\n  formData.append('facebook_ads', fbAdsTracking.facebook_ads === 'Yes' ? 'Yes' : 'No');\n  formData.append('first_landing_page', fbAdsTracking.first_landing_page || '');\n  formData.append('fb_ads_landing_page', fbAdsTracking.fb_ads_landing_page || '');\n\n  return formData;\n}\n```\n\n### 判断规则\n\n| 表单提交方式 | 是否自动带隐藏字段 | 是否需要手动 append |\n|---|---:|---:|\n| 普通表单提交 | 是 | 否 |\n| `serializeArray()` | 是 | 否 |\n| `FormData(form)` | 是 | 否 |\n| `new FormData()` 后逐个 `append()` | 否 | 是 |\n\n---\n\n## 第三步补充：更新独立 JS 文件后要处理缓存\n\n如果你修改了独立 JS 文件，例如：\n\n```txt\n/js/evodek-sample-request.js\n```\n\n浏览器或缓存插件可能仍然加载旧版本，导致新加的 Facebook Ads 字段没有生效。\n\n### 简单做法：手动修改版本号\n\n原来可能是：\n\n```php\n\u003cscript src=\"\u003c?php echo esc_url( get_theme_file_uri('js/evodek-sample-request.js?v=1.0.3') ); ?\u003e\"\u003e\u003c/script\u003e\n```\n\n修改 JS 后，把版本号改成新的：\n\n```php\n\u003cscript src=\"\u003c?php echo esc_url( get_theme_file_uri('js/evodek-sample-request.js?v=1.0.4') ); ?\u003e\"\u003e\u003c/script\u003e\n```\n\n### 推荐做法：使用 `filemtime()` 自动刷新版本\n\n更推荐用文件修改时间作为版本号：\n\n```php\n\u003c?php\n$sample_js_file = 'js/evodek-sample-request.js';\n$sample_js_path = get_theme_file_path($sample_js_file);\n$sample_js_ver  = file_exists($sample_js_path) ? filemtime($sample_js_path) : '1.0.0';\n?\u003e\n\u003cscript src=\"\u003c?php echo esc_url( get_theme_file_uri($sample_js_file) . '?v=' . $sample_js_ver ); ?\u003e\"\u003e\u003c/script\u003e\n```\n\n这样每次你上传并覆盖 `evodek-sample-request.js` 后，只要文件修改时间变了，前端就会自动加载新版 JS。\n\n### WordPress enqueue 写法\n\n如果你的主题使用 `wp_enqueue_script()`，可以这样写：\n\n```php\nadd_action('wp_enqueue_scripts', 'theme_enqueue_sample_request_script');\n\nfunction theme_enqueue_sample_request_script() {\n    $file = 'js/evodek-sample-request.js';\n    $path = get_theme_file_path($file);\n    $ver  = file_exists($path) ? filemtime($path) : '1.0.0';\n\n    wp_enqueue_script(\n        'evodek-sample-request',\n        get_theme_file_uri($file),\n        [],\n        $ver,\n        true\n    );\n}\n```\n\n### 测试时的建议\n\n修改 JS 后建议同时做这几件事：\n\n1. 页面源码中确认 JS 地址后面的 `v=` 已经变化。\n2. 浏览器按 `Ctrl + F5` 强制刷新。\n3. 使用无痕窗口测试。\n4. 如果网站有缓存插件或 CDN，清理页面缓存和静态资源缓存。\n\n\n---\n\n## 增强版：统一 PHP 邮件输出函数\n\n建议把 Facebook Ads 邮件输出封装成统一函数，然后所有表单邮件都调用它。\n\n```php\nfunction theme_get_fb_ads_tracking_data_from_post() {\n    $posted_facebook_ads        = sanitize_text_field($_POST['facebook_ads'] ?? 'No');\n    $posted_first_landing_page  = esc_url_raw($_POST['first_landing_page'] ?? '');\n    $posted_fb_ads_landing_page = esc_url_raw($_POST['fb_ads_landing_page'] ?? '');\n    $posted_page_url            = esc_url_raw($_POST['page_url'] ?? '');\n\n    $session_facebook_ads        = sanitize_text_field($_SESSION['theme_facebook_ads'] ?? 'No');\n    $session_first_landing_page  = esc_url_raw($_SESSION['theme_first_landing_page'] ?? '');\n    $session_fb_ads_landing_page = esc_url_raw($_SESSION['theme_fb_ads_landing_page'] ?? '');\n\n    $first_landing_page  = $posted_first_landing_page ?: $session_first_landing_page;\n    $fb_ads_landing_page = $posted_fb_ads_landing_page ?: $session_fb_ads_landing_page;\n\n    $facebook_ads = ($posted_facebook_ads === 'Yes' || $session_facebook_ads === 'Yes') ? 'Yes' : 'No';\n\n    $combined_urls = $posted_page_url . ' ' . $first_landing_page . ' ' . $fb_ads_landing_page;\n    if ($facebook_ads !== 'Yes' \u0026\u0026 strpos($combined_urls, 'fb_ads=1') !== false) {\n        $facebook_ads = 'Yes';\n\n        if (!$fb_ads_landing_page) {\n            $fb_ads_landing_page = $posted_page_url ?: $first_landing_page;\n        }\n    }\n\n    return [\n        'facebook_ads'        =\u003e $facebook_ads,\n        'first_landing_page'  =\u003e $first_landing_page,\n        'fb_ads_landing_page' =\u003e $fb_ads_landing_page,\n    ];\n}\n\nfunction theme_render_email_url_line($label, $url) {\n    $url = esc_url_raw($url);\n\n    if (!$url) {\n        return '\u003cp\u003e\u003cstrong\u003e' . esc_html($label) . ':\u003c/strong\u003e \u003c/p\u003e';\n    }\n\n    return '\u003cp\u003e\u003cstrong\u003e' . esc_html($label) . ':\u003c/strong\u003e \u003ca href=\"' . esc_url($url) . '\" target=\"_blank\"\u003e' . esc_html($url) . '\u003c/a\u003e\u003c/p\u003e';\n}\n\nfunction theme_get_fb_ads_tracking_email_html() {\n    $tracking = theme_get_fb_ads_tracking_data_from_post();\n\n    return ''\n        . '\u003chr\u003e'\n        . '\u003ch3\u003eFacebook Ads Tracking\u003c/h3\u003e'\n        . '\u003cp\u003e\u003cstrong\u003eFacebook Ads:\u003c/strong\u003e ' . esc_html($tracking['facebook_ads']) . '\u003c/p\u003e'\n        . theme_render_email_url_line('First Landing Page', $tracking['first_landing_page'])\n        . theme_render_email_url_line('Facebook Ads Landing Page', $tracking['fb_ads_landing_page']);\n}\n```\n\n邮件正文里只需要加入：\n\n```php\n. theme_get_fb_ads_tracking_email_html()\n```\n\n如果你的项目已经使用 `evodek_get_fb_ads_tracking_email_html()` 这个函数名，也可以继续沿用原来的命名。\n\n## 第四步：在 PHP 邮件函数中接收字段\n\n在处理表单提交的 PHP 函数中，添加以下字段接收代码。\n\n```php\n$facebook_ads        = sanitize_text_field($_POST['facebook_ads'] ?? 'No');\n$first_landing_page  = esc_url_raw($_POST['first_landing_page'] ?? '');\n$fb_ads_landing_page = esc_url_raw($_POST['fb_ads_landing_page'] ?? '');\n$page_url            = esc_url_raw($_POST['page_url'] ?? '');\n```\n\n建议对 `facebook_ads` 做一次规范化处理，避免异常值：\n\n```php\n$facebook_ads = ($facebook_ads === 'Yes') ? 'Yes' : 'No';\n```\n\n---\n\n## 第五步：把 Facebook 来源信息加入邮件内容\n\n在邮件正文 `$body` 中加入以下内容。\n\n```php\n$body .= ''\n    . '\u003cbr\u003e'\n    . '\u003cp\u003e\u003cstrong\u003eFacebook Ads:\u003c/strong\u003e ' . esc_html($facebook_ads) . '\u003c/p\u003e'\n    . '\u003cp\u003e\u003cstrong\u003eFirst Landing Page:\u003c/strong\u003e '\n    . ($first_landing_page ? '\u003ca href=\"' . esc_url($first_landing_page) . '\" target=\"_blank\"\u003e' . esc_html($first_landing_page) . '\u003c/a\u003e' : '')\n    . '\u003c/p\u003e'\n    . '\u003cp\u003e\u003cstrong\u003eFacebook Ads Landing Page:\u003c/strong\u003e '\n    . ($fb_ads_landing_page ? '\u003ca href=\"' . esc_url($fb_ads_landing_page) . '\" target=\"_blank\"\u003e' . esc_html($fb_ads_landing_page) . '\u003c/a\u003e' : '')\n    . '\u003c/p\u003e'\n    . '\u003cp\u003e\u003cstrong\u003eSubmitted Page URL:\u003c/strong\u003e '\n    . ($page_url ? '\u003ca href=\"' . esc_url($page_url) . '\" target=\"_blank\"\u003e' . esc_html($page_url) . '\u003c/a\u003e' : '')\n    . '\u003c/p\u003e';\n```\n\n如果你原本已经有 `Page URL` 字段，也可以改名为 `Submitted Page URL`，这样更清楚。\n\n---\n\n## 推荐邮件字段结构\n\n建议邮件中至少保留这些信息：\n\n```txt\nName\nEmail\nPhone\nState\nSubject\nMessage\n\nIP Address\nFacebook Ads\nFirst Landing Page\nFacebook Ads Landing Page\nSubmitted Page URL\nSubmitted At\n```\n\n含义说明：\n\n| 字段 | 含义 |\n|---|---|\n| Facebook Ads | 是否来自 Facebook 广告 |\n| First Landing Page | 用户第一次进入网站的页面 |\n| Facebook Ads Landing Page | 带有 `fb_ads=1` 的广告落地页 |\n| Submitted Page URL | 用户最终提交表单的页面 |\n| IP Address | 用户 IP |\n| Submitted At | 提交时间 |\n\n---\n\n## 测试方法\n\n### 测试 1：直接在 Contact 页面提交\n\n访问：\n\n```txt\nhttps://www.example.com/contact-us/?fb_ads=1\n```\n\n提交表单后，邮件中应该显示：\n\n```txt\nFacebook Ads: Yes\nFirst Landing Page: https://www.example.com/contact-us/?fb_ads=1\nFacebook Ads Landing Page: https://www.example.com/contact-us/?fb_ads=1\nSubmitted Page URL: https://www.example.com/contact-us/?fb_ads=1\n```\n\n---\n\n### 测试 2：先进入产品页，再进入 Contact 页面\n\n访问：\n\n```txt\nhttps://www.example.com/product-page/?fb_ads=1\n```\n\n然后手动点击进入：\n\n```txt\nhttps://www.example.com/contact-us/\n```\n\n提交表单后，邮件中应该显示：\n\n```txt\nFacebook Ads: Yes\nFirst Landing Page: https://www.example.com/product-page/?fb_ads=1\nFacebook Ads Landing Page: https://www.example.com/product-page/?fb_ads=1\nSubmitted Page URL: https://www.example.com/contact-us/\n```\n\n---\n\n### 测试 3：普通访问\n\n访问：\n\n```txt\nhttps://www.example.com/contact-us/\n```\n\n提交表单后，邮件中应该显示：\n\n```txt\nFacebook Ads: No\n```\n\n---\n\n## 测试时的注意事项\n\n因为本方案会使用浏览器的 `sessionStorage`，部分旧版本也可能使用 `localStorage`，测试时需要注意：\n\n如果你已经访问过带有 `fb_ads=1` 的链接，当前浏览器会一直记住 `Facebook Ads: Yes`。\n\n重新测试普通流量时，可以：\n\n1. 使用无痕窗口测试\n2. 或者清除浏览器缓存 / localStorage\n3. 或者在浏览器控制台执行：\n\n```js\nlocalStorage.removeItem('facebook_ads');\nlocalStorage.removeItem('first_landing_page');\nlocalStorage.removeItem('fb_ads_landing_page');\n\n// 如果你的项目使用 sessionStorage，则执行：\nsessionStorage.removeItem('facebook_ads');\nsessionStorage.removeItem('first_landing_page');\nsessionStorage.removeItem('fb_ads_landing_page');\n```\n\n---\n\n## 常见问题\n\n### 1. 只用 `fb_ads=1`，不用 UTM，可以吗？\n\n可以。\n\n如果只是为了在邮箱里判断是否来自 Facebook 广告，`fb_ads=1` 已经足够。\n\n---\n\n### 2. 这个能区分具体广告系列、广告组、广告名称吗？\n\n不能。\n\n这个方案只判断：\n\n```txt\n是不是 Facebook 广告来的\n```\n\n如果后期需要区分广告系列、广告组、广告名称，可以升级为 UTM 方案，例如：\n\n```txt\nutm_source=facebook\u0026utm_medium=paid_social\u0026utm_campaign=xxx\u0026utm_content=xxx\n```\n\n---\n\n### 3. 用户跳转多个页面后还能识别吗？\n\n可以。\n\n因为广告标记会保存到 `sessionStorage`，复杂表单还可以通过 PHP Session 做服务器端兜底。\n\n只要用户使用同一个浏览器，在没有清除浏览器数据的情况下，后续进入 Contact 页面提交表单，仍然可以识别。\n\n---\n\n### 4. 用户换浏览器或换设备后还能识别吗？\n\n不能。\n\n`sessionStorage` / `localStorage` 只保存在当前浏览器和当前设备中。\n\n---\n\n### 5. 这个能替代 Meta Pixel 吗？\n\n不能完全替代。\n\n这个方案主要用于在询盘邮件中标记来源，方便人工查看。\n\nMeta Pixel 更适合做广告后台转化归因、再营销和广告优化。\n\n---\n\n## 后期添加到其他网站的步骤\n\n1. 在 Facebook / Meta 广告链接中添加：\n\n```txt\nfb_ads=1\n```\n\n2. 在目标 WordPress 主题的 `functions.php` 中加入全站追踪脚本。\n\n3. 在表单页面加入 3 个隐藏字段：\n\n```html\n\u003cinput type=\"hidden\" name=\"facebook_ads\" id=\"facebook_ads\" value=\"No\"\u003e\n\u003cinput type=\"hidden\" name=\"first_landing_page\" id=\"first_landing_page\"\u003e\n\u003cinput type=\"hidden\" name=\"fb_ads_landing_page\" id=\"fb_ads_landing_page\"\u003e\n```\n\n4. 确认 Ajax 提交时包含：\n\n```js\nformData.push({ name: 'page_url', value: window.location.href });\n```\n\n5. 在 PHP 邮件函数中接收字段。\n\n6. 在邮件正文中输出：\n\n```txt\nFacebook Ads\nFirst Landing Page\nFacebook Ads Landing Page\nSubmitted Page URL\n```\n\n7. 如果表单使用独立 JS 且手动 `new FormData()`，记得把 `facebook_ads`、`first_landing_page`、`fb_ads_landing_page` 手动 `append()` 进去。\n8. 修改独立 JS 后，更新版本号，例如 `?v=1.0.4`，或使用 `filemtime()` 自动刷新版本。\n9. 使用无痕窗口进行测试。\n\n\n---\n\n## 最终检查清单\n\n把这套方案复用到其他网站时，建议逐项检查：\n\n1. Facebook / Meta 广告链接是否带有 `fb_ads=1`。\n2. 主题是否已经启用 PHP Session。\n3. `functions.php` 是否有服务器端兜底记录逻辑。\n4. 全站 `wp_footer` 脚本是否能正常输出。\n5. 表单隐藏字段是否存在，或者是否能自动注入。\n6. 所有邮件正文是否调用统一的 Facebook Ads 邮件输出函数。\n7. 普通 Ajax 表单是否使用 `serializeArray()` 或 `FormData(form)`。\n8. 手动 `new FormData()` 的表单是否手动 append 了 3 个字段。\n9. 独立 JS 更新后，是否修改版本号或使用 `filemtime()`。\n10. 是否用无痕窗口分别测试广告流量和普通流量。\n\n---\n\n## 安全提醒\n\n如果你的 `functions.php` 中包含 SMTP 邮箱、授权码、API Key 等敏感信息，不要上传到公开 GitHub 仓库。\n\n公开示例代码时建议替换成占位符：\n\n```php\n$phpmailer-\u003eUsername = 'your-email@example.com';\n$phpmailer-\u003ePassword = 'your-smtp-app-password';\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoowinit%2Fwordpress-facebook-ads-form-tracking","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoowinit%2Fwordpress-facebook-ads-form-tracking","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoowinit%2Fwordpress-facebook-ads-form-tracking/lists"}