Added gift dimension to the web analytics tracker on gift reads#28853
Added gift dimension to the web analytics tracker on gift reads#28853jonatansberg wants to merge 1 commit into
Conversation
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run ghost:test:ci:integration |
✅ Succeeded | 2m 41s | View ↗ |
nx run ghost:test:integration |
✅ Succeeded | 2m 36s | View ↗ |
nx run ghost:test:legacy |
✅ Succeeded | 2m 54s | View ↗ |
nx run ghost:test:e2e |
✅ Succeeded | 2m 22s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | 3s | View ↗ |
nx run-many -t test:unit -p ghost |
✅ Succeeded | 28s | View ↗ |
nx run-many -t lint -p ghost |
✅ Succeeded | 34s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 5s | View ↗ |
Additional runs (2) |
✅ Succeeded | ... | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-06-30 07:21:22 UTC
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
WalkthroughGift-link tokens are now stored on Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
c82401c to
968d520
Compare
3982351 to
c8a43b1
Compare
65fe68f to
c6850b0
Compare
c8a43b1 to
2dfdea2
Compare
ab1ac81 to
6c5e42f
Compare
2dfdea2 to
96735e9
Compare
e8eb78c to
fb2eef1
Compare
96735e9 to
305746d
Compare
fb2eef1 to
13d86a8
Compare
305746d to
04cbb8f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ghost/core/core/frontend/helpers/ghost_head.js`:
- Around line 211-213: The tb_gift attribute value in ghost_head.js is being
interpolated from gift without escaping, which can break HTML attribute context;
update the tbParams construction to escape/sanitize gift before it is mapped
into the tb_gift string. Use the existing ghost_head helper logic around the
tbParams object and the final map/join expression to ensure gift is safe before
rendering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 212f9967-c23f-47af-8cc7-47d3260f184c
⛔ Files ignored due to path filters (1)
ghost/core/test/unit/frontend/helpers/__snapshots__/ghost-head.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (2)
ghost/core/core/frontend/helpers/ghost_head.jsghost/core/test/unit/frontend/helpers/ghost-head.test.js
04cbb8f to
a4ecaed
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a4ecaed7bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
a4ecaed to
8fa3a1f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ghost/core/core/frontend/services/routing/controllers/entry/gift-links.ts`:
- Around line 31-33: Keep the existing template-data contract in the gift link
flow: when building the data object in the gift-links controller, continue
populating the legacy _gift alias alongside _giftLink so overridden partials
like gift-toast can still read the token. Update the relevant data assembly in
the gift link rendering path to include both symbols in the same frame for
compatibility, preserving _gift until the compatibility window is over.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7864c690-bc4c-4133-a4b0-a96e78c970d9
⛔ Files ignored due to path filters (1)
ghost/core/test/unit/frontend/helpers/__snapshots__/ghost-head.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (4)
ghost/core/core/frontend/helpers/ghost_foot.jsghost/core/core/frontend/helpers/ghost_head.jsghost/core/core/frontend/services/routing/controllers/entry/gift-links.tsghost/core/test/unit/frontend/helpers/ghost-head.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
- ghost/core/core/frontend/helpers/ghost_head.js
- ghost/core/test/unit/frontend/helpers/ghost-head.test.js
8fa3a1f to
4178ae4
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ghost/core/core/frontend/helpers/ghost_head.js`:
- Line 212: The gift_link field in ghost_head.js is currently forwarding
dataRoot._giftLink directly into analytics, which exposes a bearer token. Update
the logic in the ghost_head helper to emit only a non-secret stable identifier
instead, such as a gift-link record ID/UUID or a server-generated keyed hash,
and ensure the analytics payload never includes the raw token value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 728cf068-10e9-46cc-8ac5-5f4469c4472a
⛔ Files ignored due to path filters (1)
ghost/core/test/unit/frontend/helpers/__snapshots__/ghost-head.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (4)
ghost/core/core/frontend/helpers/ghost_foot.jsghost/core/core/frontend/helpers/ghost_head.jsghost/core/core/frontend/services/routing/controllers/entry/gift-links.tsghost/core/test/unit/frontend/helpers/ghost-head.test.js
🚧 Files skipped from review as they are similar to previous changes (3)
- ghost/core/test/unit/frontend/helpers/ghost-head.test.js
- ghost/core/core/frontend/services/routing/controllers/entry/gift-links.ts
- ghost/core/core/frontend/helpers/ghost_foot.js
ref https://linear.app/ghost/issue/BER-3746/integrate-gift-link-usage-tracking-with-analytics - gift-link usage is moving onto the existing analytics pipeline instead of the DB counter, so the numbers stay consistent with the rest of site analytics - ghost_head's tracker emits the gift-link token as `tb_gift_link`, the same ghost_head -> ghost-stats courier path that already carries post_uuid, member_status, etc. - the reader path sets the verified token on res.locals as `_giftLink`, so it merges onto the render context root where both the tracker (dataRoot._giftLink) and ghost_foot's toast read it. This renames the internal flag (`_gift` -> `_giftLink`, matching the `gift_link` dimension) and moves it off the @DaTa frame onto res.locals so it reads like post_uuid/member; internal flag, not a theme API - always emitted, empty when not a gift, so the backend's `gift_link != ''` check decides what counts as gift traffic rather than the producer special-casing it - e2e-frontend gift-links tests now assert the rendered tracker output: a verified token reaches `tb_gift_link`, while a forged token, a token for another post, and a normal read all render an empty `tb_gift_link` — proving an unverified token never enters analytics
4178ae4 to
9c2bd8e
Compare

ref https://linear.app/ghost/issue/BER-3746/integrate-gift-link-usage-tracking-with-analytics
Stacked on #28824 (the
?gift=reader path). Producer slice of moving gift-link usage tracking off the DB counter and onto the existing web-analytics pipeline (Tinybird), so gift numbers are measured the same way as every other page view.What this does
Attaches a single new dimension —
gift_link(the gift-link token) — to thepage_hitpayload on a verified gift read, so downstream analytics can:How
entry/gift-links.ts,setGiftTemplateFlag) flags a verified gift render with a single internal_giftLinktoken — the token itself, not a boolean — set onres.localsso it merges onto the render context root. One flag, not two: it also drives the reader toast._gift→_giftLink, to match thegift_linkdimension) and moves it off the@dataframe ontores.locals, so both consumers read it from the render root likepost_uuid/member. Touchesghost_footand the reader’sgift-linkscontroller as well asghost_head.ghost_head's tracker reads it asdataRoot._giftLinkand emitstb_gift_link, via the existingghost_head→ghost-statscourier path that already carriespost_uuid,member_status, etc.ghost_footreads the same root flag for the toast.tb_gift_link="") on a normal read; only a verified gift render carries the token. The empty-vs-token distinction (not an"undefined"sentinel) is what lets the downstreamgift_link != ''filter decide what counts as gift traffic.Gated on
labs.giftLinks.This PR is producer-only
The two consumers are separate PRs so each can be reviewed independently:
gift_linkthrough the page-hit transforms, else it's dropped before Tinybird._mv_hitscolumn,gift_linksegment param, and a per-link endpoint.Until the proxy lands,
gift_linkis sent but dropped — no behaviour change.Out of scope
The pre-existing
?gift=token leak intoghost-stats'location.href(noted in #28824) is unchanged here. The token is sent deliberately asgift_linkregardless; thehreffield is inert in reports.Test
ghost-head.test.js(unit):tb_gift_linkcarries the token when_giftLinkis set, empty otherwise.e2e-frontend/gift-links.test.ts(integration): drives the real reader path with web analytics enabled and asserts the rendered tracker — a verified token reachestb_gift_link, while a forged token, a token for another post, and a normal read all render an emptytb_gift_link. Proves an unverified token never enters analytics.