Skip to content

feat: add link click conversion tracking for external links#1166

Merged
danieliser merged 5 commits into
developfrom
fix/pid-mailto-tel
Jan 23, 2026
Merged

feat: add link click conversion tracking for external links#1166
danieliser merged 5 commits into
developfrom
fix/pid-mailto-tel

Conversation

@danieliser

@danieliser danieliser commented Jan 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Fix mailto/tel URLs getting broken ?pid= parameters appended
  • Add beacon analytics tracking for all non-internal link clicks
  • Track link clicks as conversions with eventData.type: 'link_click' for segmentation

Changes

JavaScript (pum-url-tracking.js):

  • Fixed isInternalUrl() to exclude non-HTTP protocols from PID tracking
  • Added shouldTrackClick(), getLinkType(), attachClickTracking() methods
  • Fires conversion beacon on click for non-internal links

PHP (new LinkClickTracking.php service):

  • Hooks into pum_analytics_conversion action
  • Tracks site-wide and per-popup link click counts with atomic SQL updates
  • Mirrors existing FormConversionTracking pattern

Link Behavior

Link Type PID Appended? Beacon Tracked?
Internal (/page, https://same-site.com) ✅ Yes ❌ No
External (https://other-site.com) ❌ No ✅ Yes
Email (mailto:x@y.com) ❌ No ✅ Yes
Phone (tel:123-456) ❌ No ✅ Yes
JavaScript (javascript:fn()) ❌ No ✅ Yes
Anchor (#, #section) ❌ No ✅ Yes

Test Plan

  • Verify internal links get ?pid=X appended
  • Verify mailto/tel links work without broken PID
  • Check Network tab shows beacon requests on external link clicks
  • Confirm no double-counting with form conversion tracking

Summary by CodeRabbit

  • New Features

    • External and special-link click beacon tracking for popups.
    • Automatic link-type detection to tailor tracking (external, mailto, tel, anchor, etc.).
    • New analytics service recording site-wide and per-popup link click counts with query/reset.
  • Improvements

    • Ensured PID tracking runs earlier to improve reliability.
  • Bug Fixes

    • Correct handling of mailto: and tel: links to avoid unwanted URL modifications.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 23, 2026

Copy link
Copy Markdown

Walkthrough

Adds client-side beacon-style click tracking for external/special links in popups and a new server-side LinkClickTracking service that records and increments site-wide and per-popup link click counts. (48 words)

Changes

Cohort / File(s) Summary
Client-side link tracking
assets/js/src/site/plugins/pum-url-tracking.js
Added shouldTrackClick(), getLinkType(), and attachClickTracking(); updated isInternalUrl() and processPopupLinks() to attach one-time beacon click handlers for non-internal/special links. (+113/-2)
Backend service registration
classes/Plugin/Core.php
Registers link_click_tracking container service (constructs \PopupMaker\Services\LinkClickTracking) and invokes its init() during service initialization. (+16/-0)
Link click tracking service
classes/Services/LinkClickTracking.php
New PopupMaker\Services\LinkClickTracking class: hooks into pum_analytics_conversion, implements track_link_click() with validation, atomically increments site-wide and per-popup counters, exposes get/reset methods, and fires popup_maker/link_click_tracked. (+232/-0)
Changelog
CHANGELOG.md
Added entries for link click conversion tracking, PID tracking reliability, and mailto/tel handling fixes. (+3/-0)
Controller hook priority
classes/Controllers/CallToActions.php
Sets template_redirect hook priority to 0 to ensure PID tracking runs earlier. (+2/-1)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PopupJS as Popup (JS)
    participant ClickH as Click Handler
    participant Beacon as Analytics Beacon
    participant Server as WP Server
    participant DB as Database

    User->>PopupJS: Click external/special link
    PopupJS->>PopupJS: shouldTrackClick(url)?
    PopupJS->>PopupJS: getLinkType(url)
    PopupJS->>ClickH: attachClickTracking($link)
    ClickH->>ClickH: validate analytics availability
    ClickH->>ClickH: build payload (pid, link type, href)
    ClickH->>Beacon: fire beacon POST
    Beacon->>Server: deliver tracking event
    Server->>Server: LinkClickTracking::track_link_click(popup_id, args)
    Server->>Server: validate popup and event type
    Server->>DB: atomic increment site count
    Server->>DB: atomic increment popup meta count
    Server->>Server: do_action('popup_maker/link_click_tracked', ...)
    Server-->>Beacon: response OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature added: link click conversion tracking for external links, matching the primary change in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@danieliser danieliser changed the base branch from master to develop January 23, 2026 20:18
@danieliser

Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jan 23, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@danieliser danieliser closed this Jan 23, 2026
@danieliser danieliser reopened this Jan 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@assets/js/src/site/plugins/pum-url-tracking.js`:
- Around line 129-179: In attachClickTracking, before building or sending the
beacon payload (the data.eventData.url field), detect and redact special-scheme
targets (mailto:, tel:, javascript:) so PII/inline JS is not sent; only include
the full href for http/https links and replace special schemes with a safe
placeholder (e.g. the scheme name or null) or omit the url property. Apply the
redaction immediately after computing href/getLinkType and before applying
window.PUM.hooks and before calling window.PUM_Analytics.beacon, ensuring
getLinkType still receives the original href if needed or update calls to use
the redacted value for analytics only. Ensure this change respects existing hook
usage (pass redacted data into applyFilters) and preserves the pum-click-tracked
guard and event binding in attachClickTracking.
- Around line 84-102: The shouldTrackClick function currently counts hash-only
and same-page anchor links as conversions; update shouldTrackClick to return
false for URLs that are purely fragment identifiers (e.g., start with '#') or
point to an anchor on the same page (i.e., after parsing the URL the pathname
and host match the current location and only a hash differs). Implement a check
in shouldTrackClick to detect url.charAt(0) === '#' (or equivalent fragment-only
detection) and to parse full URLs to ignore same-page anchors, then return false
for those cases before the existing CTA check.

In `@classes/Services/LinkClickTracking.php`:
- Around line 99-108: The public action hook string used in LinkClickTracking
(the do_action call currently using 'popup_maker/link_click_tracked') must be
renamed to use the pum_ prefix; update the do_action call in the
LinkClickTracking class to emit 'pum_link_click_tracked' (or the agreed pum_
prefixed name) and update any listeners/tests referencing
'popup_maker/link_click_tracked' to the new 'pum_link_click_tracked' hook so
public API naming is consistent.
🧹 Nitpick comments (1)
classes/Services/LinkClickTracking.php (1)

119-145: Use %i placeholders for table identifiers in prepared queries.

WordPress 6.2.0+ supports %i for safely escaping table and column names. Since this plugin requires WordPress 6.6+, you can use %i for both $wpdb->options and $wpdb->postmeta table references instead of direct interpolation.

♻️ Proposed changes
-			$wpdb->prepare(
-				"SELECT option_id FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
-				self::SITE_COUNT_KEY
-			)
+			$wpdb->prepare(
+				'SELECT option_id FROM %i WHERE option_name = %s LIMIT 1',
+				$wpdb->options,
+				self::SITE_COUNT_KEY
+			)
@@
-			$wpdb->prepare(
-				"UPDATE {$wpdb->options} SET option_value = option_value + 1 WHERE option_name = %s",
-				self::SITE_COUNT_KEY
-			)
+			$wpdb->prepare(
+				'UPDATE %i SET option_value = option_value + 1 WHERE option_name = %s',
+				$wpdb->options,
+				self::SITE_COUNT_KEY
+			)
@@
-			$wpdb->prepare(
-				"SELECT meta_id FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s LIMIT 1",
-				$popup_id,
-				self::POPUP_META_KEY
-			)
+			$wpdb->prepare(
+				'SELECT meta_id FROM %i WHERE post_id = %d AND meta_key = %s LIMIT 1',
+				$wpdb->postmeta,
+				$popup_id,
+				self::POPUP_META_KEY
+			)
@@
-			$wpdb->prepare(
-				"UPDATE {$wpdb->postmeta} SET meta_value = meta_value + 1 WHERE post_id = %d AND meta_key = %s",
-				$popup_id,
-				self::POPUP_META_KEY
-			)
+			$wpdb->prepare(
+				'UPDATE %i SET meta_value = meta_value + 1 WHERE post_id = %d AND meta_key = %s',
+				$wpdb->postmeta,
+				$popup_id,
+				self::POPUP_META_KEY
+			)

Also applies to lines 157–179.

Comment thread assets/js/src/site/plugins/pum-url-tracking.js
Comment thread assets/js/src/site/plugins/pum-url-tracking.js
Comment thread classes/Services/LinkClickTracking.php
@danieliser danieliser closed this Jan 23, 2026
@danieliser danieliser reopened this Jan 23, 2026
@danieliser danieliser closed this Jan 23, 2026
@danieliser danieliser reopened this Jan 23, 2026
@danieliser danieliser force-pushed the fix/pid-mailto-tel branch 2 times, most recently from 960c77a to 7e4cf39 Compare January 23, 2026 21:39
Non-HTTP protocols like mailto:, tel:, and javascript: cannot handle
query parameters. The ?pid= was being appended incorrectly, breaking
these special links.

Added regex check to skip any URL with a protocol that isn't http(s).
- Fire beacon analytics for mailto, tel, and external link clicks
- Track link clicks as conversions with eventData.type: 'link_click'
- Add LinkClickTracking service with atomic counter updates
- Preserve existing PID tracking for internal links only
Modernizes SQL queries to use wpdb::prepare() %i placeholder
for table name identifiers instead of PHP string interpolation.
Ensures template_redirect fires before other plugins that might
intercept/redirect, preventing missed tracking events.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

5-26: Fix markdownlint MD036 by using headings for section labels.

The lint error indicates emphasis is used as a heading. Converting these labels to headings will satisfy MD036 and keep formatting consistent.

♻️ Proposed change
-**Features**
+### Features
@@
-**Improvements**
+### Improvements
@@
-**Fixes**
+### Fixes
♻️ Duplicate comments (1)
classes/Services/LinkClickTracking.php (1)

99-107: Rename public hook to the pum_ prefix.

This should follow the public hook naming convention for Popup Maker.

♻️ Proposed change
-do_action( 'popup_maker/link_click_tracked', $popup_id, $event_data );
+do_action( 'pum_link_click_tracked', $popup_id, $event_data );

As per coding guidelines, public hooks should use the pum_ prefix.

@danieliser danieliser merged commit e609ed8 into develop Jan 23, 2026
9 checks passed
@danieliser danieliser deleted the fix/pid-mailto-tel branch January 23, 2026 22:48
@coderabbitai coderabbitai Bot mentioned this pull request Feb 3, 2026
16 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant