Composr Supplementary: Making an addon (part 2)

Written by Chris Graham (ocProducts)
Welcome to the second of our series of addon making tutorials. If you haven't yet read the first tutorial then it's advisable that you do so before reading this one.

Making a tracking module

Today we're going to make a module that will allow you to track visitors progress through your website, for all those who enter your website with a special 'from' label in the URL (e.g. http://yourbaseurl/?from=affiliate1).

We'll also keep tally of whether they visited the purchase module and whether they joined, to get an idea on how 'successful the visit was' from the webmaster's point of view.

As with the previous tutorial, we'll avoid making things difficult for ourselves, and just write our code in English, without using templates. As we're making a module this time, we'll be implementing using a 'mini-module'.

Here is our adminzone/pages/mini-modules_custom/admin_tracking.php file:

PHP code



Simple script to track advertising purchase successes.
Requires the Composr super_logging option enabled.


$success = array();
$joining = array();
$failure = array();
$users_done = array();
$advertiser_sessions $GLOBALS['SITE_DB']->query('SELECT member_id,s_get,ip,date_and_time FROM ' $GLOBALS['SITE_DB']->get_table_prefix() . 'stats WHERE date_and_time>' . (string)(time() - 60 60 24) . ' AND s_get LIKE \'' db_encode_like('%<param>from=%') . '\'');
foreach (
$advertiser_sessions as $session) {
    if (
array_key_exists($session['member_id'], $users_done)) {
$users_done[$session['member_id']] = 1;

    if (!
preg_match('#<param>from=([\w\d]+)</param>#'$session['get'], $matches)) {
$from $matches[1];
$user $session['member_id'];

    if (!
array_key_exists($from$success)) {
$success[$from] = 0;
$failure[$from] = 0;
$joining[$from] = 0;

'<b>Tracking information for <u>' $from '</u> visitor</b> (' $session['ip'] . ')...<br />';
$places $GLOBALS['SITE_DB']->query('SELECT the_page,date_and_time,referer FROM ' $GLOBALS['SITE_DB']->get_table_prefix() . 'stats WHERE member_id=' . (string)intval($user) . ' AND date_and_time>=' . (string)intval($session['date_and_time']) . ' ORDER BY date_and_time');
    foreach (
$places as $place) {
'<p>' escape_html($place['the_page']) . ' at ' date('Y-m-d H:i:s'$place['date_and_time']) . ' (from ' escape_html(substr($place['referer'], 0200)) . ')</p>';

$ip $GLOBALS['SITE_DB']->query_select_value_if_there('stats''ip', array('the_page' => 'site/pages/modules/join.php''member_id' => $user), ''1);
$user is_null($ip) ? null $GLOBALS['SITE_DB']->query_value_if_there('SELECT member_id FROM ' $GLOBALS['SITE_DB']->get_table_prefix() . 'stats WHERE ' db_string_equal_to('ip'$ip) . ' AND member_id>0');
    if (!
is_null($user)) {
$test is_null($user) ? null $GLOBALS['SITE_DB']->query_select_value_if_there('stats''id', array('the_page' => 'site/pages/modules_custom/purchase.php''member_id' => $user));
    if (!
is_null($test)) {
    } else {

'<br />';
'<br />';



Our mini-module in action on Heavily censored to protect our and our visitors privacy ;).

Our mini-module in action on Heavily censored to protect our and our visitors privacy <img class="top_vertical_alignment" alt=";)" src="" />.

(Click to enlarge)

We call up the mini-module by going to http://yourbaseurl/adminzone/?page=admin_tracking, as we saved it as a page in the Admin Zone.

The mini-module is actually very simple – mainly it just looks for all the hits to the website where a from parameter was given, and then puts out all the URLs they visited since entering the site, by date, along with some other basic information. It also looks for whether each hit user joined or went to purchase and tallies it against each 'from' value it finds.

But but but…

You said

But, you just threw out that code and it worked?! That kind of thing doesn't happen for me. I could never do that.

I can ensure you I certainly didn't just throw out that code ;). The reality is that even the best programmers, when they're lucky, spend half their time writing code and half of their time fixing what they thought was already perfect.

Persistence is vital. In fact, when you're learning, mistakes are vital. The absolute best way to learn to program is by making mistakes, because the process of debugging forces you to look really deeply at what's going on, giving you a much better understanding than you likely had before. In this example I used some SQL and, as a learner, you might not really understand SQL yet. When I first wrote SQL I didn't understand it either, and what I wrote back then was based on copying patterns I'd seen other people use. If there was a mistake in my SQL the process of debugging it would force me to really understand how it worked, expanding my knowledge. After time, things get easier and easier, and you're able to achieve better results, be able to build more complex systems with greater ease. I'm sure you don't know your way through the Composr API yet, but eventually you will learn it and before all too long you won't even have to look things up a lot of the time, even when writing huge modules.

If you're really serious about programming, read lots of books and write lots of addons, starting small and working your way up. In time, you'll be as good as the professionals. There's a good chance, based on our demographics, that you're still in education – well, Composr could be a good medium for you to develop professional skills that could get you a job (even one with us, in the long term) – this, and real interest, are the true motivation for most Open Source work.


Players module

Make a mini-module that shows the top 10 gift point givers, and the top 10 gift point receivers.

Be imaginative

Make a mini-module of your own design, and release it as an addon.

Points challenge

60 points will be given to any user that releases a working Composr addon that is likely useful to more than one person.
To claim your prize, post in the Addons forum and 'report post' with the phrase '60 points please' in your report.

See also


Please rate this tutorial:

Have a suggestion? Report an issue on the tracker.