You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Problem Statement
A Drupal website includes a webform tool that allows users (likely healthcare professionals) to perform financial calculations for kidney transplant programs. The website owners need to track user engagement with minimal personal data collection.
Key Requirements:
Track when a user selects a provider/institution from the dropdown (first form field)
Record which calculation tools the user shows interest in:
Track if the user completes and submits the form
Track if the user downloads the generated report
Create distinct records for different users from the same institution
Prevent duplicate records for the same user visiting multiple times
Provide administrators with a dashboard to view this tracking data
Form Structure:
Initial dropdown to select a provider/institution (required field)
Three checkbox options that reveal different calculation tools
Each tool contains additional form fields (not to be tracked individually)
Form generates a downloadable report
Data Privacy Constraints:
Do not track specific financial data entered
Do not track personal identifying information
Only track institution selection and user interaction patterns
Desired Outcome
A tracking system that:
Creates a record when a provider is first selected via AJAX
Updates the record to show which calculation tools were explored
Flags when a form is submitted
Flags when a report is downloaded
Uses session tracking to differentiate between users while maintaining privacy
Provides administrators with actionable data on institutional interest and tool usage
Functions through Drupal's webform module with custom extensions
This will allow administrators to follow up with institutions showing interest in specific kidney transplant program analysis tools.
/** * Fields for the InstitutionTracking entity: * - provider (string, required): The selected institution/hospital * - session_id (string): To identify unique sessions * - ip_address (string): Additional identifier for unique visits * - timestamp (datetime): When the tracking began * - interests (array): Which option checkboxes they selected * - living_donation (boolean) * - paired_exchange (boolean) * - staff_cost (boolean) * - form_submitted (boolean): If they completed and submitted the form * - report_downloaded (boolean): If they downloaded the report */
2. JavaScript/AJAX Trigger for Initial Selection
(function($,Drupal){Drupal.behaviors.webformInstitutionTracking={attach: function(context,settings){// Target the provider select field$('.provider-select',context).once('institution-tracking').change(function(){varselectedProvider=$(this).val();// Only trigger if an actual provider is selected (not the "- Select -" option)if(selectedProvider&&selectedProvider!=='- Select -'){// Call AJAX endpoint to record the initial selection$.ajax({url: '/webform-tracking/record',type: 'POST',data: {'provider': selectedProvider,'webform_id': settings.webformId},success: function(response){// Store the tracking ID in browser session storageif(response.tracking_id){sessionStorage.setItem('webform_tracking_id',response.tracking_id);}}});}});// Track checkbox selections for interest areas$('.interest-checkbox',context).once('interest-tracking').change(function(){vartrackingId=sessionStorage.getItem('webform_tracking_id');varinterestField=$(this).attr('name');varisChecked=$(this).is(':checked');if(trackingId){$.ajax({url: '/webform-tracking/update-interest',type: 'POST',data: {'tracking_id': trackingId,'interest_field': interestField,'value': isChecked}});}});}};})(jQuery,Drupal);
3. AJAX Controller for Recording Initial Selection
/** * Records the initial provider selection. */publicfunctionrecordInitialSelection(Request$request) {
$provider = $request->request->get('provider');
$webform_id = $request->request->get('webform_id');
$session_id = $request->getSession()->getId();
$ip_address = $request->getClientIp();
// Check if we already have a record for this session and IP within the last day$existing = \Drupal::entityTypeManager()
->getStorage('institution_tracking')
->loadByProperties([
'session_id' => $session_id,
'ip_address' => $ip_address,
'timestamp' => \Drupal::time()->getRequestTime() - 86400, // Last 24 hours
]);
// If no existing record found, create a new oneif (empty($existing)) {
$tracking = \Drupal\webform_institution_tracking\Entity\InstitutionTracking::create([
'provider' => $provider,
'session_id' => $session_id,
'ip_address' => $ip_address,
'timestamp' => \Drupal::time()->getRequestTime(),
'interests' => [
'living_donation' => FALSE,
'paired_exchange' => FALSE,
'staff_cost' => FALSE,
],
'form_submitted' => FALSE,
'report_downloaded' => FALSE,
]);
$tracking->save();
returnnewJsonResponse(['tracking_id' => $tracking->id()]);
} else {
$tracking = reset($existing);
returnnewJsonResponse(['tracking_id' => $tracking->id()]);
}
}
4. Update Interests Controller
/** * Updates which interest areas the user selected. */publicfunctionupdateInterests(Request$request) {
$tracking_id = $request->request->get('tracking_id');
$interest_field = $request->request->get('interest_field');
$value = (bool) $request->request->get('value');
if ($tracking_id && $interest_field) {
$tracking = \Drupal\webform_institution_tracking\Entity\InstitutionTracking::load($tracking_id);
if ($tracking) {
$interests = $tracking->get('interests')->value;
$interests[$interest_field] = $value;
$tracking->set('interests', $interests);
$tracking->save();
returnnewJsonResponse(['status' => 'updated']);
}
}
returnnewJsonResponse(['status' => 'error'], 400);
}
5. Form Submission Tracking
/** * Implements hook_webform_submission_form_alter(). */functionwebform_institution_tracking_webform_submission_form_alter(&$form, \Drupal\Core\Form\FormStateInterface$form_state, $form_id) {
// Add the tracking ID to the form as a hidden field $form['tracking_id'] = [
'#type' => 'hidden',
'#default_value' => '',
'#attributes' => [
'class' => ['tracking-id-field'],
],
];
// Add JavaScript to populate the hidden field with the tracking ID$form['#attached']['library'][] = 'webform_institution_tracking/tracking_script';
// Add a submit handler to update the tracking record$form['actions']['submit']['#submit'][] = 'webform_institution_tracking_submission_handler';
}
/** * Custom submit handler for webform. */functionwebform_institution_tracking_submission_handler(&$form, \Drupal\Core\Form\FormStateInterface$form_state) {
$tracking_id = $form_state->getValue('tracking_id');
if (!empty($tracking_id)) {
$tracking = \Drupal\webform_institution_tracking\Entity\InstitutionTracking::load($tracking_id);
if ($tracking) {
$tracking->set('form_submitted', TRUE);
$tracking->save();
}
}
}
6. Report Download Tracking
/** * Implements hook_file_download(). */functionwebform_institution_tracking_file_download($uri) {
// Check if this is a report downloadif (strpos($uri, 'webform-reports') !== FALSE) {
$tracking_id = \Drupal::request()->query->get('tracking');
if (!empty($tracking_id)) {
$tracking = \Drupal\webform_institution_tracking\Entity\InstitutionTracking::load($tracking_id);
if ($tracking) {
$tracking->set('report_downloaded', TRUE);
$tracking->save();
}
}
}
}
7. Admin Interface with Views
Create a View to display:
Provider name
Date/time of visit
Which tools they were interested in (checkboxes)
Whether they submitted the form
Whether they downloaded the report
Add filters for:
Date range
Provider name
Interest areas
Completion status
8. JavaScript to Pass Tracking ID on Download
// Inject tracking ID into report download links$('.report-download-link').once('tracking-injection').each(function(){vartrackingId=sessionStorage.getItem('webform_tracking_id');if(trackingId){varhref=$(this).attr('href');if(href.indexOf('?')!==-1){$(this).attr('href',href+'&tracking='+trackingId);}else{$(this).attr('href',href+'?tracking='+trackingId);}}});
This solution:
Creates a record when a provider is first selected
Tracks which interest areas they select (the 3 checkboxes)
Records form submission and report download as boolean flags
Uses session and IP to identify unique/repeat visitors
Provides admin reporting via Views
Doesn't track detailed form data, only high-level interest and completion status
Would you like me to provide more detail on any specific part of this implementation?