As a Product Manager, I want to track clicks on educational/help content
Context
Track user interactions (clicks) with all available help resources and educational materials within the plugin UI under main sections:
-
Getting Started
-
Frequently Asked Questions
-
Documentation (differentiate between the one on the homepage and the sidebar)
-
Beacon directly (Click on the Help bzacon button)
-
"Need help?" right side of sections title
-
How to correctly measure your website’s loading time
Dependencies
MixPanel SDK must be installed & configured (done by Remy in a previous PR): https://github.com/wp-media/wp-rocket/issues/7436
Expected behavior
When a user clicks on one of the aforementioned button, if data tracking is activated (opted-in), then, a MixPanel event "WPM Button Clicked" must be fired with the following properties:
- button (string): identifier of which button was clicked.
- brand (string): WP Media
- Product (string): WP Rocket
- Context (string): "wp_plugin"
- path (string): current path the user is on (wp-admin/options-general.php?page=wprocket most likely?)
The events must be identified to the email (hashed) of the WP Rocket licence.
Acceptance Criteria
- Install WP Rocket. keep the data tracking opted-out (by default)
- Click on the tracked buttons. Nothing must be sent to MixPanel.
- Activate the data tracking.
- Click on the tracked buttons. Events must be sent to MixPanel.
- The event must be linked to the MixPanel user with the email on the WP Rocket licence.
- The properties of the events must be as expected. In particular, one must be able to precisely identify which button was clicked.
Additional Information
Recommendation from Remy is to use the JS SDK for this, as it's mostly front-end tracking. The alternative could be to use AJAX calls but the performance drawbacks are too big.
@DahmaniAdame Could you confirm this expectation: "The events must be identified to the email (hashed) of the WP Rocket licence."? If we do this, then we won't track each user on the website as different users (if there are 2 admins on the website for instance).
On the other hand, if we track based on the user email on the WordPress website, it might not match the email of the user who purchased WP Rocket, so the tracking on MixPanel will be much more complex.
Scope a solution
Technical Solution
Implementation Approach
Leverage the existing MixPanel JavaScript SDK that's already loaded in the admin area to track help resource interactions directly from the frontend using the "WPM Button Clicked" event with consistent properties across all touchpoints.
Development Tasks
1. Frontend: Enhanced Beacon Click Handler File: beacon.js:
var $ = jQuery;
$(document).ready(function(){
if ('Beacon' in window) {
var $help = $('.wpr-infoAction--help');
$help.on('click', function(e){
var ids = $(this).data('beacon-id');
var button = $(this).data('track-button') || 'Beacon Help';
var context = $(this).data('track-context') || 'Settings';
// Track with MixPanel JS SDK
wprTrackHelpButton(button, context);
// Continue with existing beacon functionality
wprCallBeacon(ids);
return false;
});
function wprCallBeacon(aID){
aID = aID.split(',');
if ( aID.length === 0 ) {
return;
}
if ( aID.length > 1 ) {
window.Beacon("suggest", aID);
setTimeout(function() {
window.Beacon("open");
}, 200);
} else {
window.Beacon("article", aID.toString());
}
}
}
// MixPanel tracking function
function wprTrackHelpButton(button, context) {
if (typeof mixpanel !== 'undefined' && mixpanel.track) {
// Check if user has opted in
if (typeof rocket_mixpanel_optin !== 'undefined' && !rocket_mixpanel_optin) {
return;
}
mixpanel.track('WPM Button Clicked', {
'button': button,
'brand': 'WP Rocket',
'product': 'WP Rocket',
'context': context,
'path': window.location.pathname + window.location.search
});
}
}
// Make function globally available
window.wprTrackHelpButton = wprTrackHelpButton;
});
2. Frontend: Generic Help Button Tracking File main.js
Add the following js:
$(document).ready(function() {
// Track clicks on various help elements
$(document).on('click', '[data-track-help]', function(e) {
if (typeof window.wprTrackHelpButton === 'function') {
var $el = $(this);
var button = $el.data('track-help');
var context = $el.data('track-context') || '';
window.wprTrackHelpButton(button, context);
}
});
// Track specific help resource clicks with explicit selectors
$(document).on('click', '.wpr-getting-started-link', function() {
if (typeof window.wprTrackHelpButton === 'function') {
window.wprTrackHelpButton('Getting Started', 'Dashboard');
}
});
$(document).on('click', '.wpr-faq-link', function() {
if (typeof window.wprTrackHelpButton === 'function') {
window.wprTrackHelpButton('FAQ', 'Sidebar');
}
});
$(document).on('click', '.wpr-documentation-link', function() {
if (typeof window.wprTrackHelpButton === 'function') {
window.wprTrackHelpButton('Documentation', 'Sidebar');
}
});
// Track "How to measure loading time" link
$(document).on('click', 'a[href*="correctly-measure-website-loading-time"]', function() {
if (typeof window.wprTrackHelpButton === 'function') {
window.wprTrackHelpButton('Loading Time Guide', 'Performance');
}
});
// Track "Need help?" links
$(document).on('click', '.wpr-need-help-link', function() {
if (typeof window.wprTrackHelpButton === 'function') {
window.wprTrackHelpButton('Need Help', 'General');
}
});
});
3. Backend: Expose Opt-in Status to JavaScript We need proper way to know if the user opt-in for tracking: File: Tracking.php
/**
* Add opt-in status to admin scripts
*/
public function localize_optin_status() {
if (!current_user_can('rocket_manage_options')) {
return;
}
wp_localize_script('rocket-admin', 'rocket_mixpanel_optin', $this->optin->is_optin_enabled());
}
File: Subscriber.php:
Modify the get_subscribed_events() to add the localize optin status function.
public static function get_subscribed_events() {
return [
'admin_init' => 'migrate_optin',
'update_option_wp_rocket_settings' => 'track_option_change',
'wp_ajax_rocket_toggle_optin' => 'ajax_toggle_optin',
'admin_enqueue_scripts' => ['localize_optin_status', 15], // After scripts are enqueued
];
}
public function localize_optin_status() {
$this->tracking->localize_optin_status();
}
4. Backend: Update Templates with Tracking Attributes File: sidebar.php: Update existing button to include more info
<?php
<button class="wpr-infoAction wpr-infoAction--help"
data-beacon-id="existing-beacon-id"
data-track-button="Beacon Help"
data-track-context="Settings">
<i class="wpr-icon wpr-icon--help"></i>
</button>
Results
Events properties Schema:
{
'button': 'Beacon Help' | 'FAQ' | 'Documentation' | 'Tutorial Video Title' | 'Loading Time Guide' | 'Ask Support',
'brand': 'WP Rocket',
'product': 'WP Rocket',
'context': 'Settings' | 'Sidebar' | 'Dashboard' | 'FAQ' | 'Getting Started' | 'Documentation',
'path': '/wp-admin/options-general.php?page=wprocket' // Current admin page path
}
Efforts
S
@DahmaniAdame Could you confirm this expectation: "The events must be identified to the email (hashed) of the WP Rocket licence."?
Yes. No need to over complicate things. There will generally be one person managing performance and interacting with the plugin. Worst case scenario, we will be collecting the general usage patterns of that website. But my bet is it's going to be diluted into the most common scenario: one person doing it all.
@Miraeld I like the idea of having data attributes, just a small note, plz use wpr_ as a prefix to make sure that it won't conflict with any other js.
Looks good to me but didn't test it.
@Miraeld @DahmaniAdame Can you check on MixPanel the first events we received? (on the WP Media MixPanel account for now). I am not sure we can identify which button was clicked (see the AC "In particular, one must be able to precisely identify which button was clicked."). We have the info about "button" and "context" being "Documentation" and "Sidebar". I'm not sure this is precise enough for you @DahmaniAdame ? SHouldn't we have a unique identifier per button?
@MathieuLamiot it's the documentation button on the sidebar. It's identifiable.
But I would rather stick with actual buttons labels. It makes it easy to spot.
SHouldn't we have a unique identifier per button?
Could be helpful to have a static identifier to avoid mixups, but not sure how friendly it will be on Mixpanel?
You can send whatever string you want to MixPanel, so that would be fine if we have a string ID per button. As long as you have enough granularity for you @DahmaniAdame, all good from my perspective 👌
Sounds good to me. Having a unique ID will reduce confusion for sure. Now that I think of it, it should also help test wording changes as well without loosing the tracker item data. The ID will need to be a predefined static string and not dynamically generated (from the label content... etc.).
If we go with the ID solution, could we get a list of IDs defined by Product, so we won't be going back and forth ?
Created this issue as a follow up: https://github.com/wp-media/wp-rocket/issues/7494
Sent to ready for QA based on this discussion: https://group-onecom.slack.com/archives/C08EFCYRQ2X/p1752117316919649?thread_ts=1752053118.937739&cid=C08EFCYRQ2X