WordPress Tracking Number of Downloads

December 19, 2020

Motivation

Dealing with WordPress again. What I want to achieve is for the documents(PDFs, Images etc) that I have on a WordPress site, the visitors will have to key in their information in order to download them. This way, the number of downloads per document(publication, in my case) and also the details of people who are downloading the documents can be recorded and analysed later on.

Process

My initial idea was to look for a free backend service which would serve to process API calls from my front-end. On the pages where I display the documents, I will have a form that collects visitor information before allowing download to happen. When information is collected, download will initiate and an API call will be made to communicate with the backend.

Front-end can be make up of a simple HTML form + Javascript for any data preprocessing. The issue I had was that even if I am able to find a free backend service, the POST API call that I make to that backend will contain authentication keys. Leaving API keys in plain javascript is definitely not a good idea. Although, I must say that there are many interesting Database-as-a-service that caught my attention. One of them is Quickmetrics. It provides free 10000 events per day and 5 metrics to track. No time limit and no credit card required. I will definitely check this out in the future. The documentation also seems to be straight to the point.

Ok, so sending request from Javascript may not be a good idea. The way to go will then be via PHP. There is plenty of opportunities to write PHP in WordPress and my first instinct was to check if there is a plugin available. I did not manage to find suitable ones and those form service plugins are mostly limited in their free versions. So the next step for me was to download a plugin that I used before to easily insert PHP code into a page: Post Snippets. It makes creating shortcodes fairly easy. Also, even if PHP is not used, it is still a good way to write some HTML/Javascript code that can be used in different pages.


My PHP code to render the front-end form is as follows:

Part 1: Copy and edit a html form template from W3 School
echo "<h3>Please tell us about you before downloading</h3>"; $html= <<<HTML <form id="toolkit" method="post" action=""> <label for="name">Name:</label> <input type="text" id="name" name="name" placeholder="e.g John" required maxlength="100" minlength="1" /> <label for="email">Email:</label> <input type="email" id="email" name="email" placeholder="e.g John@example.com" required /> <label for="organisation">Organisation:</label> <input type="text" id="organisation" name="organisation" placeholder="e.g Google" maxlength="100" minlength="1" required /> <input type="hidden" id="publication" name="publication" value="" /> <input type="hidden" name="action" value="addDownload" /> <input type="submit" /> </form> HTML; echo $html;
Part 2: Copy and add in the style for the html form as well
$style = <<<STYLE <style> /* Style inputs, select elements and textareas */ input[type="text"], select, input[type="email"] { width: 100%; padding: 12px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; resize: vertical; } /* Style the label to display next to the inputs */ label { padding: 12px 12px 12px 0; display: inline-block; } /* Style the submit button */ input[type="submit"] { background-color: #4caf50; color: white; padding: 12px 20px; margin-top: 10px; border: none; border-radius: 4px; cursor: pointer; float: right; } /* Style the container */ .container { border-radius: 5px; background-color: #f2f2f2; padding: 20px; } /* Floating column for labels: 25% width */ .col-25 { float: left; width: 25%; margin-top: 6px; } /* Floating column for inputs: 75% width */ .col-75 { float: left; width: 75%; margin-top: 6px; } /* Clear floats after the columns */ .row:after { content: ""; display: table; clear: both; } /* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */ @media screen and (max-width: 600px) { .col-25, .col-75, input[type="submit"] { width: 100%; margin-top: 0; } } </style> STYLE; echo $style ;
Part 3: Add in JS logic to handle form submit

Two things I did here:

  • Submit the completed form via Ajax to “/wp-admin/admin-ajax.php” (my backend)
  • Trigger auto download of document by simulating clicking of downloable link to the document
$js = <<<JS <script> jQuery("#toolkit").submit(ajaxSubmit); const submitBtn = document.querySelector("input[type='submit']"); // add title of publication as part of form const title = document.querySelector(".blog-post-detail .inner h2").innerText; const publication = document.querySelector("#publication"); publication.value = title; function ajaxSubmit() { // handle storing of data var newDownloaderForm = jQuery(this).serialize(); jQuery.ajax({ type: "POST", url: "/wp-admin/admin-ajax.php", data: newDownloaderForm, success: function (data) { console.log(data); submitBtn.value = "Thank You"; submitBtn.style.backgroundColor = "grey"; submitBtn.disable = true; }, error: function (error) { console.log(error); submitBtn.value = "Something is wrong...please try again"; submitBtn.style.backgroundColor = "grey"; submitBtn.disable = true; }, }); // handle auto downloading of files const link = document.createElement("a"); link.href ="{link}"; link.download = ""; link.dispatchEvent(new MouseEvent("click")); return false; } </script> JS; echo $js;

So that’s all for the front-end. Not the best way to do things but it does the job. The last part of the puzzle is to setup the backend to receive the form and store data accordingly.

I honestly do not know WordPress well enough to find an alternative. So here, I will add my backend processing code in the current theme’s functions.php file (accessible from Appearance -> Theme Editor -> functions.php). I am aware that if the theme changes, this code will no longer work but oh well…

Part 4: Add in PHP function to be triggered to save form data
// Add to the bottom of functions.php // Added to deal with tracking of document downloads // Every download will add a post of post type: downloader // and it includes the form data // https://stackoverflow.com/questions/43557755/how-to-call-ajax-in-wordpress wp_enqueue_script('jquery'); function update() { $new_post = array( 'post_title' => sanitize_text_field($_POST['publication']), 'post_status' => 'publish', 'post_author' => 1, 'post_type' => 'downloader' ); // Insert the post into the database $new_post_id = wp_insert_post( $new_post ); update_field('name', sanitize_text_field($_POST['name']) , $new_post_id); update_field('email', sanitize_email($_POST['email']), $new_post_id); update_field('organisation', sanitize_text_field($_POST['organisation']), $new_post_id); update_field('publication', sanitize_text_field($_POST['publication']), $new_post_id); } // this is invoked when ajax call from front-end JS is invoked // because of <input type="hidden" name="action" value="addDownload" /> // the ajax action(function) to call is specified in the form to be named "addDownload" function addDownload() { update(); echo "done"; } add_action('wp_ajax_addDownload', 'addDownload'); add_action('wp_ajax_nopriv_addDownload', 'addDownload');

This is by no means a difficult task but I am really happy to actually know how to make an ajax call within WordPress.

Achievement unlocked!



Profile picture

Written by Liu Yongliang who lives in Singapore. Also on Dev.to, LinkedIn, GitHub

© Copyright 2022 Liu Yongliang