Improve Accuracy & Identify Traffic That SiteCatalyst Can’t

I’ve been doing a lot of work recently with my Traffic Sources reports. My goals have been to clean up messy data that could come in, and to make it easier to look at traffic from different sections of the same referrer. Now I would like to see what I can do to make the standard Referrer and Referring Domains reports a little more accurate, and try to fill in some of the holes they create which prevent me from getting a really good summary view of my traffic.

Overall the standard Referrer and Referring Domains reports do a pretty good job at telling me where my visitors came from, but there is one item that is a major problem for me. That one item is called “Typed/Bookmarked”.

According to the SiteCatalyst Knowledge Base, “Typed/Bookmarked line items occur in reporting where a referrer for an image request is not present.” So in other words, if SiteCatalyst does not see a referrer value, then it simply can not tell you where that visitor came from, so they get dropped into the Typed/Bookmarked bucket. Typically that’s fine. There is no way to know completely where every single one of your visitors came from. Thats just the nature of the beast. But one problem I have is that even though SiteCatalyst may not know where that visitor came from, I possibly do. So how do I know but SiteCatalyst doesn’t you ask? Tracking codes.

Yesterday Omniture shared a study that was done by BtoB Magazine which said “email is used by 88% of marketers surveyed and ranked as their No. 1 form of digital outreach”. Its been no secret that running email campaigns is a great way to get more visits for your site, and ultimately more conversions. Judging by my inbox there are a lot of marketers out there that agree. Email marketing best practices recommend that tracking codes are included on all of the URL’s in the email. This is typically the best way to determine the effectiveness of those email campaigns, and hopefully it’s something you’ve been doing with your own email campaigns. The problem with this is that most email applications are not going to pass a referrer value to the site. So even though we are able to track the performance of these emails in our campaign reporting, when looking at our Referrer reports we are not able to see that traffic credited to the correct source. No referring value means the Traffic Sources reports consider it to be Typed/Bookmarked traffic, when we know it isn’t. Our Typed/Bookmarked values get over inflated, and the email campaign traffic doesn’t get properly credited. So what can be done about it?

Here’s what I like to do. I add in a tiny bit of code to the doPlugins section of my s_code.js file that checks to see if the image request has no referring URL, and if the current URL has a tracking code associated with one of my email campaigns. If that criteria is met then inject a specific referring domain value to my traffic sources report, correctly attributing that visit as being from one of my email campaigns. The code to do this is quite simple:

	var s_eml = s.getQueryParam('eml');

Now lets say I’m running an email campaign which contains links to my home page. I made sure that the URL’s for those links have the query string parameter eml=56789. The parameter eml is the tracking code I use specifically for my email campaigns, and 56789 is the identifier for that specific campaign. When a visitor tries to access a page of my site using one of those URL’s containing my email tracking code and they do not have a referring URL value, my normal campaign tracking does it’s job, and this new snippet of code inject’s the value of mail://email.campaign/56789 as the referring domain. If the visitor was using using an email application that did pass a referrer value, then that passed value will always take precedence. Injecting that new value as the referring URL will accomplish a couple of things. First that whole value will now appear in my Referrers report. With that I’m able to compare the traffic generated from specific email campaigns to other traffic sources. Comparing traffic generated from an email campaign to traffic generated from an organic source wasn’t always the easiest thing to do in a single SiteCatalyst report.
Email Referrer

Next in my Referring Domains report I will get the value of email.campaign, and more importantly I won’t register another instance of Typed/Bookmarked. With this I can get a look at the traffic generated from the email campaign compared to all my other known referrers as a whole to see how it stacks up.
Email Referring Domains

Here’s an additional bonus I get from doing this. If you take a look at that URL value I used as the referrer value, it does not begin with http://, but it begins with mail://. In the Traffic Sources reports you will find a report called Referrer Type. This report is basically a glorified SAINT classification that looks at each referring URL and assigns it to a different bucket. When a SiteCatalyst see’s a referring URL beginning with the value of mail:// or the value of imap:// it then gets classified to the Mail bucket in the Referrer Type report. I’m now starting to get a better view of all my traffic sources in one pretty graph.

Email Referrer Types

Another source of traffic for some sites that is also not being accurately represented in the Traffic Sources reports is when a visitor comes to the site by clicking a link in a pdf. Last week after the latest iPad was announced, a friend sent me a pdf that came from Apple about using the iPad at Work. It contained good information, so I passed it along to a couple of other friends. Looking at the pdf a little closer, there was one thing that caught my eye. It contained 25 individual links back to I wasn’t viewing this email in a web browser but in the simple Preview app on my Mac, yet every single one of those links was clickable. I thought that was great, another opportunities to drive traffic to the site. But this was also not so great because it was another opportunities to take a visitor and classify them as being Typed/Bookmarked, even though Apple could know easily and specifically where they were coming from.

Much like with emails, all it takes is a tiny couple of lines of code to identify that traffic. I like to use the value of pdf= as the query string parameter for links embedded in pdf’s, but you can obviously change it to whatever you like.

	var s_pdf = s.getQueryParam('pdf');

Just like before with the emails, the current URL needs to contain that tracking code and there must be no referrer value present for this to work.

Taking a look at that snippet of code, I used the referring URL domain value of file://pdf.document combined with a unique identifier for that pdf. Unlike with the email’s, this time I started the value with file://. This will now get assigned a new value in the Referrer Type report, the value of Hard Drive. Not the best description of what’s going on, but that’s the only value left. The Referrer Type report is set to classify all referrers into 6 different buckets, and there is no way to add any additional categories to it.

All Referrer Types

Another popular source of traffic I’ve seen that does not get proper credit in Traffic Sources reports are visits that come from a mobile application. There are tons of mobile apps which have some component to them that can take the visitor from the app to their website. Like all the other links, you hopefully have the ones in your mobile app tagged with a tracking code. If so then just one more snippet of code and that traffic can be accounted for:

	var s_app = s.getQueryParam('app');

I used the tracking code of app, but you can use what ever you would like. This app referred traffic will also be listed in the referrer type report as Hard Drive. Since I used up all the options in that report, all the extras such as mobile app traffic and pdf traffic will need to share the Hard Drive bucket.

I like to keep all three snippets of code wrapped up in one clean package:

	var s_eml = s.getQueryParam('eml');
	var s_pdf = s.getQueryParam('pdf');
	var s_app = s.getQueryParam('app');

		s.referrer = "mail://email.campaign/"+s_eml;
	}else if(s_pdf){
		s.referrer = "file:///pdf.document/"+s_pdf;
	}else if(s_app){
		s.referrer = "file:///mobile.application/"+s_app;

Now in my Referrer and Referring Domains reporting, I get a better look at how my visitors are arriving to my site.
All New Referrers

All New Referring Domains

Maybe you have some other kind of web app, or some kind of shared widget, or some other totally different way for visitors to follow a link to get to your site. So long as you can add a tracking code to that link you can get that traffic correctly represented in your Traffic Sources reports, and stop over inflating the Typed/Bookmarked metric. This is not meant to replace the Marketing Channels reports, the Channel Manager plugin, or the Unified Sources VISTA rule, but to improve the functionality and accuracy of some of the core SiteCatalyst reports.

I hope this helps.

How To Capture A Query String Parameter From A Referring URL In SiteCatalyst

Recently someone had asked the question, how do I capture a query string parameter that’s on referring URL? We all know how to grab a query string parameter from the current page URL by using the getQueryParam SiteCatalyst plug-in, but most people don’t know that plug-in can be used to get a query string parameter from the referring URL as well. I once had a real unique implementation that was not using that plug-in and needed to capture that value. I wanted to keep the implementation really light so I decided to give it a try using a smaller bit of code. I broke out a little JavaScript magic to see if I could make it happen simpler than using that plug-in. Turns out it’s not too difficult to accomplish, in fact I got it down to just a single line of code.

function getRefQueryParam(a){a=a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");a=RegExp("[\\?&]"+a+"=([^&#]*)").exec(document.referrer);return a==null?"":a[1]};

Its a little function called getRefQueryParam. To use it just place that code in your s_code file, and then call it with the name of the query string parameter you want to capture. In this example I want to capture the value of the parameter forumID= from the referring URL and record it in s.prop1.


With a little JavaScript goodness it is an easy value to capture.


How To Stop Google Preview From Being Counted In SiteCatalyst – Updated

UPDATE: I have a much better way to block the Google Web Preview bot from being tracked as a visitor in SiteCatalyst. The original solution I had posted here, required a block of code to be placed at the very top of the s_code file, and your account ID was put into a function call. Then when the s_code was fired, a function would first check the user agent of the visitor to see if it was the Google Web Preview bot, and if so then swap out the account ID with a blank value. The SiteCatalyst code would still fire, but when Omniture received the image request it would get discarded because of the missing account ID. The more I thought about this I figured there had to be a better way. No reason to execute all the s_code javascript and fire off the beacon call when I don’t want that visitor (Google) to be tracked. So after a little brainstorming I came up with a new and improved way to do this. Now when the user agent is determined to be the Google Web Preview bot, then the SiteCatalyst code is prevented from even firing (how it should have been originally). Even better, this can now be done by simply adding a tiny bit of code to the plugin’s section of your s_code file, right next to all your other plugin code. Thats it. No other changes need to happen. No code at the top of the page, no adding calls to functions in the account variable. Just cut, paste, and done.

Here is the code. Just add this right next to all of your other plugins.

 *  Block the Google Web Preview Bot from firing SiteCatalyst code
if(s.u.toLowerCase().indexOf('google web preview')!=-1){s.t=function(){}}

If you are using the original version from below make sure you remove it. And as with any code, make sure you fully test it before deploying to a live site.

Google Instant Preview, designed to show you a visual preview of your search results, rolled out in early November 2010. You now have the ability to click a small magnifying glass icon next to each search result to get a snapshot of what the page looks like.
Google Instant Preview

Seems like a pretty helpful feature, but how do they do it? Well it would appear that Google has a new spider that crawls the web and takes snapshots of each page in its results. In order for them to get an accurate look at what the page looks like, this new bot needs to able to execute JavaScript. Here is the problem. Since it is executing JavaScript that means it is also firing off the SiteCatalyst code and is being counted as another visitor and is registering page views.

How can you tell if this new Google Web Preview bot hit your site? If you are capturing User Agent you can see it show up in that report:
User Agent Report

NOTE: If you are not capturing user agent and would like to, a super simple way would be to use the SiteCatalyst Dynamic Variable functionality and include s.eVarX=”D=User-Agent”; in your s_code.js file. Just insert the number of the eVar you would like to use (a s.prop would work too) and you are all set.

Another way to see if you are being affected with spider traffic in your report suite from the Google Preview Bot would be to check out a Browser report (Visitor Profile > Technology > Browsers) and filter it to only show visitors using Safari 3.1 and then trend it.
Browser Report
We can see that this report suite has recorded about an additional 15,000 visitors over the last week that is just attributed to Safari 3.1. Checking the User Agent we saw earlier, the Google Web Preview bot is registering itself as Safari 3.1.

Now that we can see that the Google Web Preview bot is having an effect on our traffic how do we get rid of it? We could block that bot in our robots.txt file, but I like having that additional functionality available for my visitors in the Google search results. I just don’t want it to execute my SiteCatalyst code. Well here is how to do it.

I call this my bot detection code (real catchy title, right?). I currently have it just set to look for the Google Web Preview bot, but it could easily be modified to exclude other bots that can execute JavaScript. Here is how you implement it. In your s_code file, at the top you will have a s_account variable that contains your report suite id. It will look something like this:

var s_account="dead"

To implement the bot detection code you will want to change that line to include the function call. It should look like this:

var s_account=botCheck("dead")

Pretty simple so far, right? We just added the function call and included our report suite id in it. Next we have a block of code that needs to be added to the plug-ins section of the s_code file:

function botCheck(b){var c=navigator.userAgent.toLowerCase(),a="";a+=c.indexOf("google web preview")!=-1?"":b;return a};

And that’s all there is to it. So how does it work you ask? What it does is it removes your report suite id if it is the Google Web Preview bot that is accessing the page. The SiteCatalyst code will still fire off, but it will not include the report suite id so it will be discarded by SiteCatalyst and it will not affect your metrics.

Want to see it in action? I thought you’d never ask! Check out the page On this page I have a basic SiteCatalyst implementation, one line of code that displays your user agent, and then I print the results of the SiteCatalyst debugger right to the screen. Opening this page in a standard Firefox browser we can see that the SiteCatalyst code has fired off properly, it has displayed the correct user agent and the report suite id is contained within the image request string.
Test 1
So far so good. Using the User Agent switcher plug-in for Firefox, we can switch out user agent to the one that we found in the SiteCatalyst report to mimic the Google Web Preview bot.
Test 2
We can now see that when we use that bot’s user agent string, the report suite id is missing from the image request call. Any action that happens now will not be recorded in my report suite, and when SiteCatalyst receives this request it will be discarded. I’ve had this running for a few days now and have not found any issues, but since this is a pretty new chunk of code be sure to test it out before using it on your production site.


Track HBX Style Links in SiteCatalyst

I hear from a lot of people migrating from HBX to SiteCatalyst who are looking for ways to make that process a little easier. One of the hurdles I see is trying to migrate all of their HBX link tracking that is already in place to a format that SiteCatalyst can easily understand. That is an easy one to tackle. It’s called the setupLinkTrack plug-in.

Other than tracking all of you current HBX coded links, any SiteCatalyst user can get some benefit from it. Here is another great use for this plug-in. Recently I was reading a great article from @adamgreco about learning to track website navigation. It is a really great article and if you have not had a chance to read it you should go check it out. In one part of the article Adam writes “you should have your developer write code that will pass the name of the link to a Traffic Variable (sProp) when a visitor clicks on each link in your navigation”. Well I’m the guy responsible for the tracking code for my website, so how can I track those links without adding a whole ton of JavaScript onclicks or some other server side hacks? This plug-in will easily handle that, without adding the extra server calls that come with the standard SiteCatalyst link tracking.

First take the plug-in code and add it to the plug-in section of your s_code.js file. Make sure you have the utility functions s.split and s.apl.

 * Plugin: setupLinkTrack 2.0 - return links for HBX-based link 
 *         tracking in SiteCatalyst (requires s.split and s.apl)
s.setupLinkTrack=new Function("vl","c",""
+"var s=this;var l=s.d.links,cv,cva,vla,h,i,l,t,b,o,y,n,oc,d='';cv=s."
+"x in vla)s._hbxm(vla[x])?s[vla[x]]=cva[x]:'';}s.c_w(c,'',0);if(!s.e"
+"o&&!s.lnk)return '';o=s.eo?s.eo:s.lnk;y=s.ot(o);n=s.oid(o);if(s.eo&"
+":o.parentNode;if(!o)return '';y=s.ot(o);n=s.oid(o);}for(i=0;i<4;i++"
+"&&i>-1){eval(\"__f=/ src\s*=\s*[\'\\\"]?([^\'\\\" ]+)[\'\\\"]?/i\")"
+"+'^^'+s.pageName+' | '+(o.lid=o.lid?o.lid:'no &lid')+'^^'+o.lpos;if"
+"(t&&(h||l)){cva=s.split(cv,'^^');vla=s.split(vl,',');for(x in vla)s"
+"._hbxm(vla[x])?s[vla[x]]=cva[x]:'';}else if(!t&&oc.indexOf('.tl(')<"
+"0){s.c_w(c,cv,0);}else return ''");
s._IL=new Function("a","var s=this;return a!='undefined'?a.length:0");
s._II=new Function("a","b","c","var s=this;return a.indexOf(b,c?c:0)");
s._IS=new Function("a","b","c",""
+"var s=this;return b>s._IL(a)?'':a.substring(b,c!=null?c:s._IL(a))");
s._LN=new Function("a","b","c","d",""
+"var s=this;b=a.href;'';c=s._LVP(b,'lid');d=s._LVP("
s._LVP=new Function("a","b","c","d","e",""
+"var s=this;c=s._II(a,'&'+b+'=');c=c<0?s._II(a,'?'+b+'='):c;if(c>-1)"
+");return e}return ''");
s._LS=new Function("a",""
+"var s=this,b,c=100,d,e,f,g;b=(s._IL(a)>c)?escape(s._IS(a,0,c)):esca"
+"rn unescape(b)");
s._LSP=new Function("a","b","c","d","var s=this;d=s._IP(a,b);return d"
s._IP=new Function("a","b","var s=this;return a.split(b)");
s._RP=new Function("a","b","c","d",""
+"var s=this;d=s._II(a,b);if(d>-1){a=s._RP(s._IS(a,0,d)+','+s._IS(a,d"
+"+s._IL(b),s._IL(a)),b,c)}return a");
s._TL=new Function("a","var s=this;return a.toLowerCase()");
s._NA=new Function("a","var s=this;return new Array(a?a:0)");
s._hbxm=new Function("m","var s=this;return (''+m).indexOf('{')<0");
s._hbxln=new Function("h","var s=this,n=s.linkNames;if(n)return"
+"n,',','lnf',h);return ''");

Next in the s_doPlugins section of the s_code file you need a couple lines of setup.

s.hbx_lt = "auto" // manual, auto

The plug-in will use 4 variables and a name for a cookie it will set. For this example I am going to use props 1 thru 4.

Next we have the actual code that will appear in the anchor tag that the plug-in will look for. It is a simple name attribute tag of lid and lpos. This is what a link would look like with the code properly formatted.

<a href="my-page.php" name="&lid=Featured Articles&lpos=Left Nav">Cool Article Name</a>

Here’s what it will do. After clicking on a link that contains a lid and lpos (the exact link shown above), this is what you will find in the debugger on the page you land on:
Debugger 1
The plug-in entered the s.pageName value of the page that the click occurred into prop1, the value of lid into prop2, a combination of the prop1 and the value of lid into prop3 and the value of lpos in prop4.

Lets change up what we include in the actual link code. Lets remove the lid from the tag and just use this link:

<a href="my-page.php" name="&lpos=Left Nav">Cool Article Name</a>

This is what we will get from the debugger:
Debugger 2
You can see by not using the lid tag then the plug-in will use the actual anchor text in those positions.

Now lets say you are not interested in capturing the previous pageName value, or the combined previous pageName/lid value. You can just omit those variables in the plug-in setup. Change the code to:

s.hbx_lt = "auto" // manual, auto

You can see I left the commas in there as empty place holders. Clicking the link with that setup and a lid and lpos value will return this in the debugger:
Debugger 3

What about the other links on the page? What if they do not include a lid or lpos value? The plug-in will track those also. Clicking one of those links will return this in the debugger:
Debugger 4
The plug-in will still capture the previous pageName value, the anchor text and the combined values of the two.

I’m sure by now you noticed that auto/manual setting. Up to now its been set on auto. Let’s flip it to manual and click a link on the page that does not include any code. Here is what you will get:
Debugger 5
You will only get the two previous pageName values. If I would have omitted those two variables like I did earlier then it would have not returned anything. If you click on a link with a lid and lpos variables then it will perform the same as if it were set to auto.

If you want to not add the code to the links so you can track all of the links on a page, but you only want to do it on a specific page and not the whole site you can then wrap the code in an if statement like this:

s.hbx_lt = "auto" // manual, auto

You could also do that using document.location (or a million other ways to identify a specific page).

Since it also grabs the previous page value I no longer need the previous pageName plug-in (useful when using the getPercentPageViewed plug-in).


Implement Adobe SiteCatalyst in 5 Minutes (or less!)

One of the biggest things I always hear from people considering moving to Adobe SiteCatalyst is the fear of a difficult implementation. It feels to me that most people think they need to implement everything at once, and that is not so. You can just take baby steps and implement small sections at a time. Let’s get started with just a standard implementation to get things going. I’m going to show you how to do a basic implementation of SiteCatalyst in 5 minutes.

First we need to set up a new report suite from the SiteCatalyst Admin Console Report Suite Manager. That can be found by selecting Admin > Report Suites.
Admin Console
Once we are in the Report Suite Manager, select Create New > Report Suite.
Create New
This will open up the New Report Suite setup page. On this page we have two sections to deal with, the first being where we can choose what type of site we are creating the report suite for. I recommend just leaving this set at the default suite type. Since we plan of adding some neat customizations in the future as our analytics needs increase, the default will work fine for us right now.
Suite Type
Next on this page we have five items that need to be filled in. They are all marked with red stars, with the most important being the first one, Report Suite ID. Why is this the most important? Because it the only one we cannot change later. I recommend it should be something you can easily understand what it means. If your site is then I would just simply choose apple and not something cryptic like 89e7rghv9e7gh (yes I have seen that done before). It actually doesn’t matter what you choose (in case you had your heart set on using 89e7rghv9e7gh), anything will work, but choosing something simple may save some headaches in the future. The Site Title is what you want it to be referred to by in the SiteCatalyst interface, Time Zone is your time zone, Go Live Date is when the site is expected to launch (it must be a date in the future), and Estimated Page Views per Day is just what it says. This number is used by the Adobe engineers to determine what resources to dedicate to your site to ensure everything is recorded correctly. Remember any of these items (except Report Suite ID) can be changed later if you need to. There are other things listed here and you can fill them out or change them if you would like, they are not required in setting up the report suite. Fill out the five required fields, click submit and the report suite is created.
Configure Suite
There are some additional things we need to set up before we start with the actual data collection code. Head back to the Report Suite Manager and find you new suite in the list. Click it to highlight it and then mouse over the Edit Settings link. This will bring up a list of additional settings we can adjust. Remember this section. Later when you get more advanced with your SiteCatalyst data collection, this is where you will go to turn on and off variables for more advanced tracking. There are only two items we need to touch right now, and are going to be located under General.

Under Internal URL Filters you need to set what the domain of your site is. On every single page view there is a referrer. When your visitors go from your home page to your about us page, the referrer to that page is the home page. You do not want that showing up in your referrer reports, so this is where you list out the domain names that you do not want included in those reports. Quick tip: If your site is, and you enter in the URL filter, it will also filter out sites that have that name included in it, for example or Add a period to the front of the domain name to make sure that only your site is filtered, You can also enter the subdomain with the URL in the filter if your site only lives at a single subdomain, or you can list all of them in the filter.
Internal URL FIlter

Next you want to hit the Paid Search Detection. In here you need to set up a query sting parameter that will be used for your paid search. Even if you do not plan on doing paid search fill this out any way. It is important in making sure your Search Engine and Search Keywords reports work properly.
Configure Suite

We created our report suite, filled out a handful of variables and we are now ready to start playing with the actual collection code. From the Admin Console Home you can find a direct link to the Code Manager, or you can find it from the navigation menu on the left.
Admin Console Home

In the Code Manger we can generate all types of code for what ever we decide to track. From mobile phone apps to flash video players, all the collection code is here. We just want to generate the simple JavaScript Tracking code. Nothing fancy. Select your report suite, set your character encoding or your currency type and click Generate Code. You will get a little pop up warning you to make sure you know what you are doing or the world will end. Just click OK and move on.
Code Manager

Your code is generated now. Take the contents that are under the Core Javascript File tab and save them in a file that you name s_code.js. Take that file and upload it to your web server where it can be accessed from every page of the site.
Code Manager
Now take the contents under the Page Code tab and paste that to every page of your site, right before the closing body tag. In that code you will see the place marked INSERT-DOMAIN-AND-PATH-TO-CODE-HERE. In this position enter the path to the s_code.js file that you previously uploaded.

That’s all there is to it. This should be easily to be accomplished in 5 minutes or less. We setup the report suite, generated the page code and the s_code and added them to the site. This will give us the basic recording of the site traffic, referrals and visitor information. After this is up and running we can go back and add in some additional elements to the s_code file and the page code to customize your SiteCatalyst implementation to your specific site.


Getting Started With the Omniture PHP Measurement Library

The Omniture PHP Measurement Library lets you use PHP to record your visitor’s activity on your Web site. At first glance it can look a little intimidating, but it really is not that complicated. Once you have it implemented, all that you will see in the source code of your pages is the image request for the standard Omniture tracking pixel. This is what it looks like implemented.

Here is how to get started with it. From the Code Manager, download the OmnitureMeasurement.class.php file and add it to your server in a place so it can be referenced from every page of your site. Next you will need to include the Omniture PHP code on every page of your site.

Here is what a basic version of the PHP page code would look like:

require_once 'OmnitureMeasurement.class.php';
$s = new OmnitureMeasurement();
$s->account = 'ACCOUNT NAME HERE';
$s->botDetection = true;

$s->pageName = "Homepage";
$s->server = "Server";
$s->channel = "Channel";

$s->events = "events";
$s->prop1 = "prop1";
$s->prop2 = "prop2";
$s->eVar1 = "eVar1";
$s->eVar2 = "eVar2";

$s->currencyCode = 'USD';
$s->imageDimensions = '5x5';
$s->trackingServer = '';
$s->sendFromServer = false;
$s->debugTracking = false;

Lets break it down by section so you will see what is going on here. Here is the first section of the code:

require_once 'OmnitureMeasurement.class.php';
$s = new OmnitureMeasurement();
$s->account = 'ACCOUNT NAME HERE';
$s->botDetection = true;

The first thing we have here is the call to the Omniture Measurement class require_once ‘OmnitureMeasurement.class.php’;. This is what makes the Omniture PHP tracking work. As I said you can download it from the Admin section of SiteCatalyst. That is followed by a line of code that will get the tracking started $s = new OmnitureMeasurement();. Next is the place where you enter the report suite id that is associated with the site you plan on tracking. Finally we have a line to detect bots. When this is set to true, the Measurement Library detects if the HTTP User-Agent field is a bot, and does not send bot-generated data to Omniture. If this is not included it is set to false by default. If you decide to not include that, then be prepared to see Googlebot show up in your reporting.
Googlebot User Agent

$s->pageName = "Homepage";
$s->server = "Server";
$s->channel = "Channel";

This next section should be pretty self explanatory. This is where I set the pageName, the server, and the channel variables. On my 404 error page this is where I also include the pageType variable, which would look like $s->pageType=”errorPage”;.

EXTRA: For a lot of my sites I like to use the folder name of the URL as the page name. Here is how you can do that using PHP. I insert this code before the Omniture tracking section:

$pNme = parse_url($url, PHP_URL_PATH);
if ($pNme=="/")$pNme = "Home Page";

Then in the measurement library code I include:

$s->pageName = $pNme;

This pulls the file name from the URL and uses that as the page name. I also added a line that looks if all it finds is a slash / then I know they are on the Home Page and use that instead.

$s->events = "events";
$s->prop1 = "prop1";
$s->prop2 = "prop2";
$s->eVar1 = "eVar1";
$s->eVar2 = "eVar2";

This next section is where we are going to add the Custom Traffic variables, Custom Conversion variables, and Success Events. Again, this section should be pretty straight forward. If you know how to set props and eVars on the page and in the s_code file, you should know what to do here.

$s->currencyCode = 'USD';
$s->debugTracking = false;
$s->imageDimensions = '5x5';
$s->trackingServer = '';
$s->sendFromServer = false;
$s->debugTracking = false;

This final section contains some pretty important elements. The first thing you will see is where you declare the Currency Code used for purchases or currency events. You can see I have mine set to US currency, USD. Next I have the call that specifies the width and height of the image request, in pixels. By default, this variable is 1×1. You can see I have mine changed to 5×5 to ensure support for all mobile devices. Next I enter my tracking server info. You should be able to find that in your s_code.js file.

The next two lines are pretty cool. Let’s say you do not to show any Omniture code on the page. You have the option to not have any thing appear in the source code. When $s->sendFromServer is set to true, the Measurement Library makes a direct HTTP request from the server instead of rendering an image tag to the Web browser. If you view the source of the page you will not see anything but the visitor will still be fully tracked. You can see a live example of it here.

Without any code on the page it can be difficult to debug the code to make sure everything is being passed in successfully. That’s where the next variable comes in, $s->debugTracking. With this set to false then we do not see anything. If it is set to true then you will see an output on the page of the metrics that are being passed into Omniture. You can see a live example of it here.

The problem with that is you either have it off all the time, or you have it on where all of your visitors can see a bunch of code on the page. What I like to do is to add in a little bit of extra code that will only allow the debug code to appear only for me. I do this based off of my ip address. I include this bit of code before the Omniture code:

if ($ip=="") {
	$atHome = true;
} else {
	$atHome = false;

And then In the Omniture code I have it set as:

$s->debugTracking = $atHome;

What this does is allows me to see the variables that are being set every time I visit the site, but none of my visitors have to see it.

The last thing you will see listed in this section is $s->track(); which is the call to fire off the code. Without this nothing works. This is the equivalent of having the line var s_code=s.t();if(s_code)document.write(s_code) in the standard JavaScript tracking. In other words, make sure you include this line.

OK, what else can we do with this? Well as many people know I am a big fan of using plug-ins. Here is one to get you started. One of the more useful ones is the Time Parting plug-in. As I have written about before, I like combining all of the time variables so I only use a single prop/eVar, so it looks something like s.prop1=”thursday|10:00pm” and then using a SAINT classification to break it out. How do we replicate this using the PHP Library? Here you go.

Above the Omniture Measurement code I include this:

$dayname = date('l');
$dayname = strtolower($dayname);
$hourname = date('g');
$minname = date('i');
$ampmname = date('a'); 

if ($minname <= "14") {
	$mintype = "00";
} else if ($minname >= "15" && $minname <= "29"){
	$mintype = "15";
} else if ($minname >= "30" && $minname <= "44"){
	$mintype = "30";
} else if ($minname >= "45" && $minname <= "59"){	
	$mintype = "45";
$fulldayparting = "$dayname|$hourname:$mintype$ampmname"; 

Then in one of the available props in the code I have:

$s->prop16 = $fulldayparting; 

This returns something like thursday|10:00pm, exactly like the Javascript plug-in does. I also like to copy this same value to an eVar, but I only want to do it once per visit. I like using the eVar for when the visit started, and the prop for every page view. Here is how I get that same value recorded only once per visit.

At the top of all my ‘extra’ code that I have included before the Omniture measurement code, I include this:

$sesionnumber = $_SESSION['count'];
if ($sesionnumber == '1') {
     $fulldaypartingsession = $fulldayparting;
} else {
     $fulldaypartingsession = '';

And then with the available eVars I include:

$s->eVar16 = $fulldaypartingsession;

What this does is on the first page of the session it will set the Time Parting value, but will just leave it blank for the rest of the session giving you a good session start time.

Hopefully this is enough to get you started. There are a lot of other cool PHP tricks that will allow you to replicate most of the functions of the Javascript tracking. Make sure you download and read the PHP Measurement Library Implementation Guide from the Omniture Help Section for additional details.


Track Hash Query Parameters in Omniture SiteCatalyst

Do you have a site that uses a hash symbol in the URL with query parameters and you want a way to track them in SiteCatalyst? Well you have come to the right place.

What am I talking about? Lets say you have a URL that looks like this:
and you want to capture the value of cid=. The standard getQueryParam plugin will not work in this case. It looks for a question mark in the URL, and as you can see this one did not have one.

To capture this value we can use the getHashQueryParam s_code.js plug-in. What this plug-in does is looks for the hash symbol # at the end of the URL, then looks for the parameter you have listed in the function, then inserts the value in to the associated variable. This works just like the standard getQueryParam plugin, but looks for the hash instead.

Here’s how to use it. Insert this line of code in the s_doPlugins(s) section of your s_code.js file. Make sure you use the correct variable you want the metrics recorded in and which query string you want it to be associated with. I’m using s.prop17 and cid in this example.


Next enter this code in the plug-in’s section of your s_code file:

 * Plugin: getHashQueryParam
function getHashQueryParam(a){
if(QueryString==''){var WinExtra=window.location.hash;
if(WinExtra.length > 0){if(WinExtra.indexOf(a)>-1){
var returnValue='';var keyValPairs=QueryString.split('&');
if(!keyValPairs){ keyValPairs = new Array();  
for(var counter=0;counter<keyValPairs.length;counter++){
var keyVal=keyValPairs[counter].split('=');if(keyVal[0]==a){
returnValue=keyVal[1];break;}}return returnValue;}

Here is an example of it live in action. After you click on the link, open the debugger you will see the value entered in s.prop17.


Optimize the Time Parting Plugin to get More Detail and Use Less Variables

The Time Parting Plug-in is one of the more popular SiteCatalyst plug-ins available. A standard implementation of the Time Parting plug-in will consume 3 variables. One for Time of Day, one for the Day of Week, and one for Weekday/Weekend. How can we improve this to get more information, and more importantly use less variables? Here is how I have been doing it.

I use a combination of stacking the variables and SAINT uploads. For those of you who are not familiar with SAINT, Omniture describes it as, “…an acronym for SiteCatalyst Attribute Importing and Naming Tool. This tool enables you to download the classifications template, apply attributes to it, and then upload the data, thereby enhancing your SiteCatalyst reports with the new attributes.” This will allow us to upload a lot of detail about any variable you record.

Here’s how I’m doing it on this site. First I am using the 2.0 version of the plug-in and not the 1.4 version that I describe in a previous post. The 2.0 version includes support for Daylight Savings time and globalizes the year. You can find the 2.0 version from the SiteCatalyst Knowledge Base. If you prefer to use the 1.4 version, you can find it on this site.

/* Set Time Parting Variables */
if (s.visEvent) s.eVar16=s.prop16;   

Ok, let me explain whats going on here. As I said before the Time Parting plug-in captures 3 variables. If you notice in my code I am only using two of them. I don’t need to capture Weekday/Weekend anymore. I will take care of that later. The other two, I capture in two blank variables I created, s_day and s_hour. Next I combine the two of them in a single variable I call s_timepart, separated by a pipe. Then to ensure everything is consistent I copy the variable in all lower case to the prop that I am going to use. This next part is a little different. In the eVar I only want capture this value once per visit. Typically a simple getValOnce will be enough to get it done. Well then what happens when the visit extends from one time part into another? In that situation the Time Parting value will be different and therefore getValOnce will capture this as a new value since it has changed. I don’t want that to happen, I only want it once per visit. So this is when I tie in using the get Visit Start plug-in. This guarantees I will only capture the value only one time per visit.

This will return a report that looks like this:
Time Parting Report in SiteCatalyst

We now have a total of 672 possible options in this report. The next thing we want to do is to classify these using SAINT. I set up 5 different categories to use. Weekday/Weekend (this is why we don’t need to capture it in the code, Day of Week, Hour of Day, Hour Part and AM/PM.

I then created the template to use that contains all of these values.
SAINT Template
You can download a copy of the template that I use here.
Upload the template and that’s all there is to it. Do you have more conversions in the bottom of the hour or the top of the hour? How about morning vs afternoon? Which whole hour is the most profitable? Now you have an easy way to break down your time parting with finer granularity, at the sime time saving your self a couple of variables.


Additional Methods To Measure Interaction Using The Get Time To Complete Plug-In

Recently there was a great article on the Omniture Blog all about Capturing Time Spent on . . . well, just about anything. It’s a great post and definitely worth checking out. After reading it I was wondering if there was another way to do it? Of course there is! I present the Time To Complete Plug-in.

The getTimeToComplete plug-in will track the time it takes a user to complete some process on your site. The “clock” begins when you call the plug-in with the value “start” and stops when the plug-in with the value “stop”. The plug-in can be used to track the time to complete a checkout process, to track the time to complete an application process, to track the time a user spends viewing/using Rich Internet Applications (RIA), or to track the time between a download and a purchase.

s.getTimeToComplete( v, cn, e )

v is the Value – ‘start’ or ‘stop’
cn is the Cookie Name – example: ‘ttc’
e is the Expiration – days to expiration of the cookie, 0 for session
This function will return an empty string ” or a value in days, hours, minutes or seconds

There is a bunch of different ways to use this plug-in. I like this first method because you do not have to add a single of code to the page to make it work (I have found it is much easier to get a development team to simply upload a new s_code file as opposed to adding additional code to the site.) Lets say you want to track a form on your site. Lets say the form is at /my-form.php. Once the visitor fills out the form, they are taken to the thank you page which lets say is at /my-form-thanks.php. I would add this bit of code to the s_code file:

if (window.location.pathname=='/my-form.php') s.ttc='start';
if (window.location.pathname=='/my-form-thanks.php') s.ttc='stop';

What this does is looks for the path in the URL for /my-form.php and sets start in s.ttc. When the plug-in see’s this it set’s the cookie ttc with a start time Then when the URL path is /my-form-thanks.php stop is set. When the plug-in see’s stop, it then reads the ttc plug-in and records the time difference in s.prop1. The time value that you will get will have days and hours rounded to .2 (e.g. 1.4 days), minutes to .5 (e.g. 2.5 minutes), and seconds to 5 (e.g. 15 seconds).

NOTE:When this is implemented, if you check the debugger you will not see any value for s.prop1 until you have reached the stop point of the process.

Here is another way to use the code. Let’s say you have some events set right on the pages of your site. We want to know how long it takes to get from when event1 is set to when event2 is set. I would add this code into the s_code file:

if('event1')>-1) s.ttc='start';
if('event2')>-1) s.ttc='stop';

What this does is look for when event1 happens, then set start in s.ttc. When event2 happens stop is set, and the time value is set in s.prop1.

NOTE: Another thing to remember is this can be used to record the time of many different processes or paths on your site. If you do that I suggest using a different cookie name and variable value in each one so there are no issues.

Here is the actual plug-in code:
The getTimeToComplete plug-in returns the time to complete a task. When v is ‘start’ a cookie is written with the timestamp. When v is ‘stop’ the cookie is read and the expired time is returned in days, hours, minutes, or seconds.

 * Plugin: getTimeToComplete
s.getTimeToComplete=new Function("v","cn","e",""
+"var s=this,d=new Date,x=d,k;if(!s.ttcr){e=e?e:0;if(v=='start'||v=='"
+"_w(cn,d.getTime(),e?x:0);return '';}if(v=='stop'){k=s.c_r(cn);if(!s"
+".c_w(cn,'',d)||!k)return '';v=(d.getTime()-k)/1000;var td=86400,th="
+"3600,tm=60,r=5,u,un;if(v>td){u=td;un='days';}else if(v>th){u=th;un="
+"'hours';}else if(v>tm){r=2;u=tm;un='minutes';}else{r=.2;u=1;un='sec"
+"onds';}v=v*r/u;return (Math.round(v)/r)+' '+un;}}return '';");

I really like this plug-in because you end up with a report that is completely dedicated to the time it takes to complete that exact action.