<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>jensbits.com &#187; Google Analytics</title>
	<atom:link href="http://www.jensbits.com/tag/google-analytics/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jensbits.com</link>
	<description></description>
	<lastBuildDate>Wed, 21 Jul 2010 03:44:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Google Analytics Data Export API with Google Chart Visualizations</title>
		<link>http://www.jensbits.com/2010/06/23/google-analytics-data-export-api-with-google-chart-visualizations-2/</link>
		<comments>http://www.jensbits.com/2010/06/23/google-analytics-data-export-api-with-google-chart-visualizations-2/#comments</comments>
		<pubDate>Wed, 23 Jun 2010 06:45:54 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[Google Analytics]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=769</guid>
		<description><![CDATA[You can punch into the Google Analytics Data Export API, pull out some stats, and stuff them into some nice graphical charts using Google Chart Visualizations. This demo is done in PHP. Authenticate the User The user can authenticate via the ClientLogin or using the AuthSub login which is actually more secure. For the ClientLogin, [...]


No related posts.]]></description>
			<content:encoded><![CDATA[<p>You can punch into the Google Analytics Data Export API, pull out some stats, and stuff them into some nice graphical charts using Google Chart Visualizations. This demo is done in PHP.<br />
<img src="/images/googlechartviz.gif" alt="Google Chart Visualizations" /></p>
<h2>Authenticate the User</h2>
<p>The user can authenticate via the ClientLogin or using the AuthSub login which is actually more secure. For the ClientLogin, a typical username/password form is used. For the AuthSub login a link is used to send the user to Google to log in. Both are shown below. Normally you would use one or the other.</p>
<pre class="brush: php;">
&lt;form name=&quot;loginForm&quot; action=&quot;&lt;?php echo $_SERVER['PHP_SELF']; ?&gt;&quot; method=&quot;post&quot;&gt;
		    &lt;label for=&quot;email&quot;&gt;Gmail:&lt;/label&gt;
		    &lt;input id=&quot;email&quot; type=&quot;text&quot; name=&quot;email&quot; /&gt;
		    &lt;label for=&quot;password&quot;&gt;Password:&lt;/label&gt;
		    &lt;input type=&quot;password&quot; name=&quot;password&quot; id=&quot;password&quot;/&gt;
		    &lt;br /&gt;&lt;br /&gt;
		    &lt;button type=&quot;submit&quot; id=&quot;submitLogin&quot;&gt;Submit&lt;/button&gt;
		&lt;/form&gt;
        &lt;p&gt;&lt;a class=&quot;button&quot; href=&quot;https://www.google.com/accounts/AuthSubRequest?next=http://www.jensbits.com/demos/ga/app/&amp;scope=https://www.google.com/analytics/feeds/&amp;secure=0&amp;session=1&quot;&gt;Or,authenticate using AuthSub through Google.&lt;/a&gt;&lt;/p&gt;
</pre>
<p>And, of course, the two authentication methods use different http calls to return a token that can be used to access the API.</p>
<pre class="brush: php;">
//ClientLogin: try to log in and get session token for multiple API calls
if(isset($_POST['email']) &amp;&amp; isset($_POST['password'])){
	$_SESSION['sessionToken'] = googleLogin($_POST['email'],$_POST['password']);
}
//AuthSub: exchange token for session token so multiple calls can be made to api
if(isset($_REQUEST['token'])){
	$_SESSION['authSub'] = true;
	$_SESSION['sessionToken'] = get_session_token($_REQUEST['token']);
}

//returns sessionToken for multiple calls to API
function googleLogin($email,$passwd){

    $clientlogin_url = &quot;https://www.google.com/accounts/ClientLogin&quot;;
     $clientlogin_post = array(
    &quot;accountType&quot; =&gt; &quot;GOOGLE&quot;,
    &quot;Email&quot; =&gt; $email,
    &quot;Passwd&quot; =&gt; $passwd,
    &quot;service&quot; =&gt; &quot;analytics&quot;,
    &quot;source&quot; =&gt; &quot;my-analytics&quot;
	);

	$curl = curl_init($clientlogin_url);

	curl_setopt($curl, CURLOPT_POST, true);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $clientlogin_post);
	curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

	$response = curl_exec($curl);

	preg_match(&quot;/Auth=([a-z0-9_\-]+)/i&quot;, $response, $matches);
	$sessionToken = $matches[1];

	if (strlen($sessionToken) == 0){
		$sessionToken = &quot;Authentication Failed.&quot;;
	}

 	return $sessionToken;
}

//AuthSub returns session token for multiple calls to API
	function get_session_token($onetimetoken) {
		$output = call_api($onetimetoken, &quot;https://www.google.com/accounts/AuthSubSessionToken&quot;);

		if (preg_match(&quot;/Token=(.*)/&quot;, $output, $matches))
		{
			$sessionToken = $matches[1];
		} else {
			echo &quot;Error authenticating with Google.&quot;;
			exit;
		}

		return $sessionToken;
	}
</pre>
<h2>Data Requests</h2>
<p>Once authenticated to a Google Analytics account and a multi-use session token is acquired, the data requests can be made. The first one will request the profiles (websites) associated with the account. If there is more than one, a dropdown select is populated allowing for the selection of the profile from which to pull data.</p>
<pre class="brush: php;">
$accountxml = call_api($_SESSION['sessionToken'],&quot;https://www.google.com/analytics/feeds/accounts/default&quot;);
// Get an array with the available accounts
$profiles = parse_account_list($accountxml);
</pre>
<p>The call_api function is going to return the XML data from Google based on the request URL sent in. In this case, it is getting the profile data and the parse_account_list function is rolling through that XML and putting the profile data in an array.</p>
<pre class="brush: php;">
//gets the data
function call_api($sessionToken,$url){
	$curl = curl_init($url);

	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	if (isset($_SESSION['authSub'])){
		$curlheader[0] = sprintf(&quot;Authorization: AuthSub token=\&quot;%s\&quot;/n&quot;, $sessionToken);
	} else {
		$curlheader[0] = &quot;Authorization: GoogleLogin auth=&quot; . $sessionToken;
	}
	curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);

	$response = curl_exec($curl);
	curl_close($curl);

	return $response;
}

//returns accounts list as array
function parse_account_list($xml){
	$doc = new DOMDocument();
	if(stripos($xml,&quot;&lt;&quot;) !== FALSE)
	{
		$doc-&gt;loadXML($xml);

		$entries = $doc-&gt;getElementsByTagName('entry');
		$i = 0;
		$profiles= array();
		foreach($entries as $entry)
		{
			$profiles[$i] = array();

			$title = $entry-&gt;getElementsByTagName('title');
			$profiles[$i][&quot;title&quot;] = $title-&gt;item(0)-&gt;nodeValue;

			$entryid = $entry-&gt;getElementsByTagName('id');
			$profiles[$i][&quot;entryid&quot;] = $entryid-&gt;item(0)-&gt;nodeValue;

			$tableId = $entry-&gt;getElementsByTagName('tableId');
			$profiles[$i][&quot;tableId&quot;] = $tableId-&gt;item(0)-&gt;nodeValue;

			$i++;
		}
		return $profiles;
	} else {
		$sessionToken = &quot;Authentication Failed.&quot;;
	}

}
</pre>
<p>The dropdown of the profile array:</p>
<pre class="brush: php;">
echo &quot;&lt;form name='siteSelect' id='siteSelect' method='post' action='&quot; . $_SERVER['PHP_SELF'] . &quot;'&gt;&lt;p&gt;&lt;label for='tableId'&gt;Select Site:&lt;/label&gt;&lt;select name='tableId' id='tableId'&gt;&quot;;
		foreach($profiles as $profile)
		{
			if($profile[&quot;tableId&quot;] == $table_Id)
				$selected = &quot;selected='selected'&quot;;
				echo &quot;&lt;option value='&quot; . $profile[&quot;tableId&quot;] . &quot;|&quot; . $profile[&quot;title&quot;] . &quot;'&quot; . $selected  . &quot;&gt;&quot; . $profile[&quot;title&quot;] . &quot;&lt;/option&gt;&quot;;
				$selected = &quot; &quot;;
		}
		echo &quot;&lt;/select&gt;&lt;/p&gt;&quot;;
</pre>
<p>The parse_data function below is going to roll through the data returned from Google Analytics and spit out an array that can be used to create the Google Visualization graphs.</p>
<pre class="brush: php;">
//returns data as array
function parse_data($xml){
		$doc = new DOMDocument();
		$doc-&gt;loadXML($xml);

		$entries = $doc-&gt;getElementsByTagName('entry');
		$i = 0;
		$results = array();
		foreach($entries as $entry)
		{
			$countries[$i] = array();

			$dimensions = $entry-&gt;getElementsByTagName('dimension');
			foreach($dimensions as $dimension)
			{
				$results[$i][ltrim($dimension-&gt;getAttribute(&quot;name&quot;),&quot;ga:&quot;)] =  $dimension-&gt;getAttribute('value');
			}

			$metrics = $entry-&gt;getElementsByTagName('metric');
			foreach($metrics as $metric)
			{
				$results[$i][ltrim($metric-&gt;getAttribute('name'),&quot;ga:&quot;)] =  $metric-&gt;getAttribute('value');
			}

			$i++;
		}
		return $results;
}
</pre>
<h2>Graph Generation</h2>
<p>Google Visualizations requires the inclusion of a javascript file in the head tag and empty div&#8217;s that will be the target for the graphs:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://www.google.com/jsapi&quot;&gt;&lt;/script&gt;
</pre>
<p>The target div&#8217;s should be placed on the page where you want them to appear.</p>
<pre class="brush: xml;">
&lt;div id='barchart_div'&gt;&lt;/div&gt;
&lt;div id='piechart_div'&gt;&lt;/div&gt;
</pre>
<p>Finally, the data can be added to the chart generation javascript:</p>
<pre class="brush: jscript;">
&lt;script type=&quot;text/javascript&quot;&gt;
      google.load(&quot;visualization&quot;, &quot;1&quot;, {packages:[&quot;piechart&quot;]});
      google.setOnLoadCallback(drawPieChart);
      function drawPieChart() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Referrer');
        data.addColumn('number', 'Visits');
        data.addRows(&lt;?php echo sizeof($referrers) ?&gt;);
        &lt;?php
        $row = 0;
        foreach($referrers as $referrer)
		{
		?&gt;
		data.setValue(&lt;?php echo $row ?&gt;,0,'&lt;?php echo $referrer[&quot;source&quot;] ?&gt;');
		data.setValue(&lt;?php echo $row ?&gt;,1,&lt;?php echo $referrer[&quot;visits&quot;] ?&gt;);
		&lt;?php
		$row++;
		}
		?&gt;

        var chart = new google.visualization.PieChart(document.getElementById('piechart_div'));
        chart.draw(data, {width: 600, height: 440, is3D: true, title: 'Referrer/Visits'});
	  	}

	  google.load(&quot;visualization&quot;, &quot;1&quot;, {packages:[&quot;columnchart&quot;]});
      google.setOnLoadCallback(drawBarChart);
      function drawBarChart() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Day');
        data.addColumn('number', 'Visits');
        data.addRows(&lt;?php echo sizeof($visitsgraph) ?&gt;);
		&lt;?php
         $row = 0;
        foreach($visitsgraph as $visits)
		{
		?&gt;
		data.setValue(&lt;?php echo $row ?&gt;,0,'&lt;?php if ($visits_graph_type == &quot;month&quot;){echo date(&quot;M&quot;, mktime(0, 0, 0, $visits[&quot;month&quot;])).&quot; &quot;.$visits[&quot;year&quot;];}else{echo substr($visits['date'],6,2).&quot;-&quot;.date('M', mktime(0, 0, 0, substr($visits['date'],4,2))).&quot;-&quot;.substr($visits['date'],0,4);} ?&gt;');
		data.setValue(&lt;?php echo $row ?&gt;,1,&lt;?php echo $visits[&quot;visits&quot;] ?&gt;);
		&lt;?php
		$row++;
		}
		?&gt;
        var chart = new google.visualization.ColumnChart(document.getElementById('barchart_div'));
        chart.draw(data, {width: 700, height: 400, is3D: true, title: 'Visits'});
      }

    &lt;/script&gt;
</pre>
<h2>Recommended</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470413964" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe></p>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p id="demo"><a href="/demos/ga/app/"><span>Demo</span></a></p>
<p id="download"><a href="/media/code/GAandGoogleCharts.zip"><span>Download zip of all files</span></a></p>
<p class="donate">If this post helped you out, please consider donating to help pay the hosting fees. 100% of the donations go to the web host.</p>

<!-- Begin PayPal Donations by http://wpstorm.net/ -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><div class="paypal-donations"><input type="hidden" name="cmd" value="_donations" /><input type="hidden" name="business" value="jen@jensbits.com" /><input type="hidden" name="return" value="http://www.jensbits.com/thank-you/" /><input type="hidden" name="item_name" value="Help pay hosting. All donations go to hosting fees for this site." /><input type="hidden" name="currency_code" value="USD" /><input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /><img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /></div></form>
<!-- End PayPal Donations -->



<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2010/06/23/google-analytics-data-export-api-with-google-chart-visualizations-2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</title>
		<link>http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/</link>
		<comments>http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/#comments</comments>
		<pubDate>Sun, 16 May 2010 21:24:38 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Google Analytics]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=726</guid>
		<description><![CDATA[To send secure AuthSub requests to Google Analytics, you have to sign the request with a private key that is verified against the certificate you upload to Google. Google has a library where this can be done in Java that is specific to Google Analytics and, provided you can add .jar files to your ColdFusion [...]


Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>To send secure AuthSub requests to Google Analytics, you have to sign the request with a private key that is verified against the certificate you upload to Google. Google has a library where this can be done in Java that is specific to Google Analytics and, provided you can add .jar files to your ColdFusion server or use <a href="http://javaloader.riaforge.org/">javaloader</a>, you can run them using ColdFusion. If you need to roll your own or simply want to control the process, this example may help.</p>
<p>Before you implement any code that signs requests, the keys and certificate must be generated and then registered with Google you are attempting to access. Google has <a href="http://code.google.com/apis/gdata/docs/auth/authsub.html#Registered">instructions on how to generate the keys and certificate using OpenSSL</a> and <a href="http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html">how to register the certificate with Google</a>. You will need the private key in the PKCS#8 format.</p>
<p>Google also supplies <a href="http://code.google.com/apis/accounts/docs/AuthSub.html#signingrequests">instructions for signing the AuthSub requests</a> which is what we will step through here.</p>
<p>To get a single-use token for secure AuthSub, you send the user to authenticate through Google with a link similar to this:</p>
<pre class="brush: xml;">
&lt;a href=&quot;https://www.google.com/accounts/AuthSubRequest?next=YOUR_PAGE_HERE?secureAuth=yes&amp;scope=https://www.google.com/analytics/feeds/&amp;secure=1&amp;session=1&quot;&gt;Log in using Secure AuthSub through Google&lt;/a&gt;
</pre>
<p>Notice that secure is set to 1 indicating we want to send secure requests. Session is also set to 1 indicating we want a multi-use authorization token for multiple calls to the Google Analytics Data Export API. If you are only making one call to the API, you can set session to 0 (zero).</p>
<p>Google will then the user to your page designated by the next parameter with the token attached as a URL parameter called &#8220;token.&#8221; After getting this single-use token back from Google, you then need to send it back to Google to exchange for your multi-use token. This is where the signing begins. Every request from this point forward needs to be signed.</p>
<p>Secure AuthSub differs from AuthSub in that it requires several parameters to be added to the Authorization header of the request. They are:</p>
<ol>
<li>token: single-use token (or multi-use token once you get it) from Google</li>
<li>sigalg: rsa-sah1</li>
<li>data: the http-method, request URL, timestamp, and nonce all separated by a space.</li>
<li>sig: the signature made by the private key generated against the data parameter above.</li>
</ol>
<p>Generating this only requires a few lines of ColdFusion code:</p>
<pre class="brush: coldfusion;">
        &lt;cfset Math = createObject('java','java.lang.Math') /&gt;
        &lt;cfset randNum = createObject('java', 'java.security.SecureRandom') /&gt;
        &lt;cfset numeric_nonce = Math.abs(JavaCast(&quot;long&quot;,randNum.nextLong())) /&gt;
        &lt;cfset nonce = numeric_nonce.toString() /&gt;
        &lt;cfset timestmp = DateDiff(&quot;s&quot;,DateConvert(&quot;utc2Local&quot;, &quot;January 1 1970 00:00&quot;), Now()) /&gt;

        &lt;cfset appSignature = rsa_sha1(rsaPrivateKey, 'GET https://www.google.com/accounts/AuthSubSessionToken ' &amp; timestmp &amp; ' ' &amp; nonce) /&gt;

        &lt;cfset authHeaderValue = 'AuthSub token=&quot;' &amp; URL.token &amp; '&quot; data=&quot;GET https://www.google.com/accounts/AuthSubSessionToken ' &amp; timestmp &amp; ' ' &amp; nonce &amp; '&quot; sig=&quot;' &amp; appSignature &amp; '&quot;  sigalg=&quot;rsa-sha1&quot;' /&gt;
</pre>
<p>The rsa_sha1 function creates the signature. It was written by Sharad Gupta and used here with permission.</p>
<pre class="brush: coldfusion;">
&lt;cffunction name=&quot;rsa_sha1&quot; returntype=&quot;string&quot; access=&quot;public&quot; descrition=&quot;RSA-SHA1 computation based on supplied private key and supplied base signature string.&quot;&gt;
    &lt;!---Written by Sharad Gupta sharadg@gmail.com (used with permission)---&gt;
           &lt;cfargument name=&quot;signKey&quot; type=&quot;string&quot; required=&quot;true&quot; hint=&quot;base64 formatted PKCS8 private key&quot;&gt;
           &lt;cfargument name=&quot;signMessage&quot; type=&quot;string&quot; required=&quot;true&quot; hint=&quot;msg to sign&quot;&gt;
           &lt;cfargument name=&quot;sFormat&quot; type=&quot;string&quot; required=&quot;false&quot; default=&quot;UTF-8&quot;&gt;

           &lt;cfset var jKey = JavaCast(&quot;string&quot;, arguments.signKey)&gt;
           &lt;cfset var jMsg = JavaCast(&quot;string&quot;,arguments.signMessage).getBytes(arguments.sFormat)&gt;

           &lt;cfset var key = createObject(&quot;java&quot;, &quot;java.security.PrivateKey&quot;)&gt;
           &lt;cfset var keySpec = createObject(&quot;java&quot;,&quot;java.security.spec.PKCS8EncodedKeySpec&quot;)&gt;
           &lt;cfset var keyFactory = createObject(&quot;java&quot;,&quot;java.security.KeyFactory&quot;)&gt;
           &lt;cfset var b64dec = createObject(&quot;java&quot;, &quot;sun.misc.BASE64Decoder&quot;)&gt;

           &lt;cfset var sig = createObject(&quot;java&quot;, &quot;java.security.Signature&quot;)&gt;

           &lt;cfset var byteClass = createObject(&quot;java&quot;, &quot;java.lang.Class&quot;)&gt;
           &lt;cfset var byteArray = createObject(&quot;java&quot;,&quot;java.lang.reflect.Array&quot;)&gt;

           &lt;cfset byteClass = byteClass.forName(JavaCast(&quot;string&quot;,&quot;java.lang.Byte&quot;))&gt;
           &lt;cfset keyBytes = byteArray.newInstance(byteClass, JavaCast(&quot;int&quot;,&quot;1024&quot;))&gt;
           &lt;cfset keyBytes = b64dec.decodeBuffer(jKey)&gt;

           &lt;cfset sig = sig.getInstance(&quot;SHA1withRSA&quot;, &quot;SunJSSE&quot;)&gt;
           &lt;cfset sig.initSign(keyFactory.getInstance(&quot;RSA&quot;).generatePrivate(keySpec.init(keyBytes)))&gt;
           &lt;cfset sig.update(jMsg)&gt;
           &lt;cfset signBytes = sig.sign()&gt;

           &lt;cfreturn ToBase64(signBytes)&gt;
     &lt;/cffunction&gt;
</pre>
<p>The request for and parsing out of the multi-use token looks like this:</p>
<pre class="brush: coldfusion;">
&lt;cfhttp url=&quot;https://www.google.com/accounts/AuthSubSessionToken&quot; method=&quot;GET&quot;&gt;
 &lt;cfhttpparam name=&quot;Authorization&quot; type=&quot;header&quot; value=&quot;#authHeaderValue#&quot;&gt;
&lt;/cfhttp&gt;

&lt;cfset output = cfhttp.filecontent /&gt;

&lt;cfset authSubSessionToken = Mid(output, FindNoCase(&quot;Token=&quot;,output) + (Len(&quot;Token=&quot;)), Len(output)) /&gt;
</pre>
<p>Any more requests of the Data Export API have to be made in the same manner except now you will use the multi-use token (authSubSessionToken) as the token in the Authorization header.</p>
<h2>Recommended</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=032166034X" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p class="donate">If this post helped you out, please consider donating to help pay the hosting fees. 100% of the donations go to the web host.</p>

<!-- Begin PayPal Donations by http://wpstorm.net/ -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><div class="paypal-donations"><input type="hidden" name="cmd" value="_donations" /><input type="hidden" name="business" value="jen@jensbits.com" /><input type="hidden" name="return" value="http://www.jensbits.com/thank-you/" /><input type="hidden" name="item_name" value="Help pay hosting. All donations go to hosting fees for this site." /><input type="hidden" name="currency_code" value="USD" /><input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /><img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /></div></form>
<!-- End PayPal Donations -->



<p>Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Google Analytics Third-Party Shopping Cart (Cross-Domain) Tracking Using Traditional and Asynchronous Snippet</title>
		<link>http://www.jensbits.com/2010/02/01/google-analytics-third-party-shopping-cart-cross-domain-tracking-using-traditional-and-asynchronous-snippet/</link>
		<comments>http://www.jensbits.com/2010/02/01/google-analytics-third-party-shopping-cart-cross-domain-tracking-using-traditional-and-asynchronous-snippet/#comments</comments>
		<pubDate>Mon, 01 Feb 2010 19:04:47 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[Google Analytics]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=574</guid>
		<description><![CDATA[Cross-domain tracking is used for several situations the most obvious for third-party shopping carts. Proper installation of the Google Analytics (GA) tracking code is essential for accurate tracking of visitors and sales. GA offers two tracking code snippets for you to choose from: traditional (synchronous) and asynchronous. First, a short review of the basic installation [...]


No related posts.]]></description>
			<content:encoded><![CDATA[<p>Cross-domain tracking is used for several situations the most obvious for third-party shopping carts. Proper installation of the Google Analytics (GA) tracking code is essential for accurate tracking of visitors and sales. </p>
<p>GA offers two tracking code snippets for you to choose from: traditional (synchronous) and asynchronous. </p>
<p>First, a short review of the basic installation for both the traditional and the asynchronous:</p>
<h2>Basic Tracking Code Installation</h2>
<h3>Traditional (Synchronous) Tracking Code</h3>
<pre class="brush: jscript;">
&lt;script type=&quot;text/javascript&quot;&gt;
var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;);
document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;));
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
try{
var pageTracker = _gat._getTracker(&quot;UA-xxxxxx-x&quot;);
pageTracker._trackPageview();
} catch(err) {}
&lt;/script&gt;
</pre>
<p>Traditional (synchronous) basic installation is done by placing the above GA tracking code, after replacing &#8220;UA-xxxxxx-x&#8221; with your account number, before the <strong>closing body (&lt;/body&gt;)</strong> tag on <strong>all</strong> pages of your site. This placement is supposed to decrease the likelihood that it will interfere with other javascript on your page.</p>
<p>The drawbacks with this installation include increased page load time and the possibility it won&#8217;t run at all if the user clicks away from the page before it fully loads.</p>
<p><a href="http://code.google.com/apis/analytics/docs/tracking/gaTrackingOverview.html">More on the traditional tracking code installation from Google</a>.</p>
<h3>Asynchronous Tracking Code</h3>
<pre class="brush: jscript;">
var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
  })();
</pre>
<p>Asynchronous basic installation is done by placing the above GA tracking code, after replacing &#8220;UA-xxxxxx-x&#8221; with your account number, before the <strong>closing head (&lt;/head&gt;)</strong> tag on <strong>all</strong> pages of your site. Like the traditional code, this placement is meant to decrease the likelihood that it will interfere with other javascript on your page.</p>
<p>According to <a href="http://analytics.blogspot.com/2009/12/google-analytics-launches-asynchronous.html">Google</a>, the asynchronous code has the following advantages:</p>
<ul>
<li>Faster tracking code load times for your web pages due to improved browser execution</li>
<li>Enhanced data collection and accuracy</li>
<li>Elimination of tracking errors from dependencies when the JavaScript hasn&#8217;t fully loaded</li>
</ul>
<p><a href="http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html">More on the asynchronous tracking code installation from Google</a>.</p>
<h2>Third-Party Shopping Cart (Cross-Domain) Tracking</h2>
<p>It is essential that your GA tracking code is installed correctly for this to work properly. </p>
<p>GA tracks with first-party cookies. If you do not install this correctly, the cookies will be set incorrectly, have the wrong data in them, or both. More on this later&#8230;</p>
<h3>Traditional (Synchronous) Tracking Code</h3>
<p>Put the following code, replacing &#8220;UA-xxxxxx-x&#8221; with your account number, on <strong>ALL</strong> pages of your site and the shopping cart pages <strong>anywhere between the body tags but ABOVE</strong> any calls to pageTracker:</p>
<pre class="brush: jscript;">
&lt;script type=&quot;text/javascript&quot;&gt;
var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;);
document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;));
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
var pageTracker = _gat._getTracker(&quot;UA-xxxxxx-x&quot;);
pageTracker._setDomainName(&quot;none&quot;);
pageTracker._setAllowLinker(true);
pageTracker._trackPageview();
&lt;/script&gt;
</pre>
<p>Links to the shopping cart when adding items to the cart, for example, must include <code>onclick="pageTracker._link(this.href); return false;"</code></p>
<p>Forms that submit to the shopping cart must include <code>onsubmit="pageTracker._linkByPost(this)"</code></p>
<p>Do not forget to put that on ALL forms if checkout is a multi-form (multi-step) process. Your data will be incorrect if you do not do this.</p>
<p><strong>If your third-party cart already includes built-in GA code, follow their instructions exactly.</strong></p>
<p><a href="http://www.google.com/support/analytics/bin/answer.py?hl=en&#038;answer=55532">More on the traditional code installation of third-party shopping cart tracking from Google</a>.</p>
<h3>Asynchronous Tracking Code</h3>
<p>Put the following code, replacing &#8220;UA-xxxxxx-x&#8221; with your account number, on <strong>ALL</strong> pages of your site and the shopping cart pages just above the <strong>closing head (&lt;/head&gt;) </strong>tag:</p>
<pre class="brush: jscript;">
 var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-xxxxxx-x']);
  _gaq.push(['_setDomainName', 'none']);
  _gaq.push(['_setAllowLinker', true]);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
  })();
</pre>
<p>Links to the shopping cart when adding items to the cart, for example, must include <code>onclick="_gaq.push(['_link', 'https://your-cart.com/cart.html']); return false;"</code></p>
<p>Replace &#8220;https://your-cart.com/cart.html&#8221; with the actual link to your cart.</p>
<p>Forms that submit to the shopping cart must include <code>onsubmit='_gaq.push(['_linkByPost', this]);'</code></p>
<p>Do not forget to put that on ALL forms if checkout is a multi-form (multi-step) process. You will data will be incorrect if you do this incorrectly.</p>
<p><strong>Do not add the asynchronous tracking to third-party cart that have their own GA solution built-in unless specifically instructed to do so.</strong></p>
<p><a href="http://code.google.com/apis/analytics/docs/tracking/asyncMigrationExamples.html">More on the asynchronous code installation of third-party shopping cart tracking from Google</a>.</p>
<h2>Why It&#8217;s Important to Do This Correctly</h2>
<p>The cookies set by GA when the setDomainName parameter is set to &#8220;none&#8221; may in some cases have different HOST setting. That is, it may have a www.mydomain.com when the original code without the setDomainName may set HOST to just .mydomain.com. Or, worse still, you could lose all the visitor data and have the sale appear to have been referred from your site and nothing else.</p>
<p>This results in bad data coming into GA particularly in the referrer area. It probably disassociates your keywords to your sales as well.</p>
<h2>Recommended Reading</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470130652" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p class="donate">If this post helped you out, please consider donating to help pay the hosting fees.</p>

<!-- Begin PayPal Donations by http://wpstorm.net/ -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><div class="paypal-donations"><input type="hidden" name="cmd" value="_donations" /><input type="hidden" name="business" value="jen@jensbits.com" /><input type="hidden" name="return" value="http://www.jensbits.com/thank-you/" /><input type="hidden" name="item_name" value="Help pay hosting. All donations go to hosting fees for this site." /><input type="hidden" name="currency_code" value="USD" /><input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /><img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /></div></form>
<!-- End PayPal Donations -->



<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2010/02/01/google-analytics-third-party-shopping-cart-cross-domain-tracking-using-traditional-and-asynchronous-snippet/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Google Analytics Showing My Site as Top Referrer</title>
		<link>http://www.jensbits.com/2010/01/07/google-analytics-showing-my-site-as-top-referrer/</link>
		<comments>http://www.jensbits.com/2010/01/07/google-analytics-showing-my-site-as-top-referrer/#comments</comments>
		<pubDate>Thu, 07 Jan 2010 20:31:30 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[Google Analytics]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=533</guid>
		<description><![CDATA[Update (Feb. 1, 2010) &#8211; Important: After speaking to a few people regarding this issue, some other situations where this will occur have come to light. First (most important), make sure you tracking code is installed properly on every page of your site. If you use cross-domain tracking or sub-domain tracking, install it correctly on [...]


No related posts.]]></description>
			<content:encoded><![CDATA[<blockquote><p style="color:red">Update (Feb. 1, 2010) &#8211; Important:</p>
<p>After speaking to a few people regarding this issue, some other situations where this will occur have come to light.</p>
<p>First (most important), make sure you tracking code is installed properly on every page of your site. If you use cross-domain tracking or sub-domain tracking, <a href="/2010/02/01/google-analytics-third-party-shopping-cart-cross-domain-tracking-using-traditional-and-asychronous-snippet/">install it correctly on every page of the site</a>.</p>
<p>Second, do not mix the asynchronous tracking code with the original synchronous tracking code on the same page. This WILL result in tracking anomalies. Also, if you mix them on the same site but different pages, make sure they do the same thing. In other words, if one is set for cross-domain tracking, the other should be as well. And, finally, if you use third-party tools that incorporate GA tracking like some shopping carts, install the original tracking code unless specifically instructed to install the asynchronous code.</p></blockquote>
<p>I began to notice that my own site, jensbits.com, is listed as the top referrer in Google Analytics. This did not seem to be correct since another site I managed had no such issue. And, I also determined that this began right after certain date, in my case, August 10, 2009. So I began to investigate.</p>
<p>What happened on or about 10 August 2009? I put a post on my site that day that has proven to be quite popular. In that post, there is a link to a demo. When I originally posted the demo link, I typed in an absolute URL of http://jensbits.com/demo/forms/index.php for the link&#8217;s href. Notice the missing &#8216;www.&#8217; And, that was the culprit. Eventually, I went through my site to standardize my links and replaced all absolute paths with relative paths. It turns out, however, that loads of people apparently bookmarked the demo and were hitting it directly.</p>
<p>The cookies set by Google Analytics for the http://jensbits.com link had a host parameter (value) of &#8216;.jensbits.com.&#8217; The cookie also had a correct referrer of (direct). So far so good. All is correct.</p>
<p>The demo page has a link on it to return to the post. That link is relative and it points back to the WordPress post. WordPress will correctly rewrite the URL to include the &#8216;www.&#8217; The demo link was not a WordPress post so it did not get rewritten. After clicking on the &#8216;return to post&#8217; link and looking at the cookies again, I now have double the cookies for my site. One set of cookies has &#8216;.jensbits.com&#8217; as the host value and the other set has &#8216;www.jensbits.com&#8217; as the value.</p>
<p>The cookies set by Google Analytics for the http://www.jensbits.com link had a referrer of jensbits.com. Correct, but incorrect.</p>
<p><img src="/images/cookies-jensbits.gif" alt="I hate cookies" title="I hate cookies" /></p>
<p>The solution was to fix the canonicalization of the URLs. In other words, all the URLs had to resolve to either jensbits.com or www.jensbits.com. I choose www.jensbits.com since WordPress was doing such a fine job for my posts.</p>
<p>Once the rewrite fix was in place, I have the correct amount of cookies and the correct referrer for the site when I retrace my steps from the demo link to the main site just as I did previously.</p>
<p><img src="/images/cookies-www-jensbits.gif" alt="I love cookies" title="I love cookies" /></p>
<p>Don&#8217;t expect to see a precipitous drop off in the referrer number for your site once you institute this. It will decrease over time. Look for a trend, not a miracle. Google Analytics cookies have a lifespan. Return visitors may still carry erroneous cookie data like an albatross around their necks until it eventually dies a natural death.</p>
<p>BTW, the other site I mentioned, I had long ago put in a rewrite rule to include the &#8216;www&#8217; because of a programmatic session cookie issue with a web-based application. It was my determination at the time that is was specific to the way the programming language I was using was setting cookies.</p>
<p>One other thing to consider: If you are the site owner and/or webmaster, you should put a filter into your Google Analytics profile that prevents your visits from being entered into the reports. An<a href="http://www.google.com/support/analytics/bin/answer.py?hl=en&#038;answer=55572"> IP address filter</a> is the preferred method to accomplish this. </p>
<p>Note: This is not a WordPress issue. This can happen to any site that will resolve to with or without the &#8216;www&#8217; in the URL.</p>
<h2>Recommended Reading</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470130652" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p class="donate">If this post helped you out, please consider donating to help pay the hosting fees. 100% of the donations go to the web host.</p>

<!-- Begin PayPal Donations by http://wpstorm.net/ -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><div class="paypal-donations"><input type="hidden" name="cmd" value="_donations" /><input type="hidden" name="business" value="jen@jensbits.com" /><input type="hidden" name="return" value="http://www.jensbits.com/thank-you/" /><input type="hidden" name="item_name" value="Help pay hosting. All donations go to hosting fees for this site." /><input type="hidden" name="currency_code" value="USD" /><input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /><img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /></div></form>
<!-- End PayPal Donations -->



<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2010/01/07/google-analytics-showing-my-site-as-top-referrer/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>ColdFusion and Google Analytics: Getting Out What You Put In</title>
		<link>http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/</link>
		<comments>http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/#comments</comments>
		<pubDate>Sun, 20 Dec 2009 03:43:27 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Google Analytics]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=510</guid>
		<description><![CDATA[The Hooking into Google Analytics post details how to connect to the Data Export API of Google Analytics (GA) and get a simple visitors count in return. Here we will expand greatly on that and create a cfc that will process just about any result from the API. This code requires at least ColdFusion 8.0.1. [...]


Related posts:<ol><li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>The <a href="/2009/05/02/hooking-into-google-analytics-with-coldfusion/">Hooking into Google Analytics</a> post details how to connect to the Data Export API of Google Analytics (GA) and get a simple visitors count in return. Here we will expand greatly on that and create a cfc that will process just about any result from the API. </p>
<blockquote><p>This code requires at least ColdFusion 8.0.1. Check your version before installing.</p></blockquote>
<p>An auth token is needed for requests to the API and two ways to do that are the <a href="http://code.google.com/intl/en/apis/analytics/docs/gdata/gdataAuthentication.html">AuthSub and ClentLogin methods</a>. The method used is up to the developer as once the auth token is retrieved and set into session, data calls to the API can be repeatedly made.</p>
<pre class="brush: coldfusion;">
&lt;cffunction name=&quot;googleLogin&quot; access=&quot;public&quot; hint=&quot;GA account authorization&quot;&gt;
        &lt;cfargument name=&quot;email&quot; type=&quot;string&quot; required=&quot;yes&quot; default=&quot;&quot;&gt;
        &lt;cfargument name=&quot;password&quot; type=&quot;string&quot;required=&quot;yes&quot; default=&quot;&quot;&gt;
        &lt;cfargument name=&quot;gaLoginUrl&quot; type=&quot;string&quot; required=&quot;no&quot; default=&quot;https://www.google.com/accounts/ClientLogin&quot;&gt;

        &lt;cfset var loginAuth = &quot;&quot; /&gt;

        &lt;cfhttp url=&quot;#arguments.gaLoginUrl#&quot; method=&quot;post&quot;&gt;
            &lt;cfhttpparam name=&quot;accountType&quot; type=&quot;url&quot; value=&quot;GOOGLE&quot;&gt;
            &lt;cfhttpparam name=&quot;Email&quot; type=&quot;url&quot; value=&quot;#arguments.email#&quot;&gt;
            &lt;cfhttpparam name=&quot;Passwd&quot; type=&quot;url&quot; value=&quot;#arguments.password#&quot;&gt;
            &lt;cfhttpparam name=&quot;service&quot; type=&quot;url&quot; value=&quot;analytics&quot;&gt;
            &lt;cfhttpparam name=&quot;source&quot; type=&quot;url&quot; value=&quot;my-analytics&quot;&gt;
        &lt;/cfhttp&gt;

        &lt;cfif NOT FindNoCase(&quot;Auth=&quot;,cfhttp.filecontent)&gt;
            &lt;cfset loginAuth = &quot;Authorization Failed&quot; /&gt;
        &lt;cfelse&gt;
            &lt;cfset loginAuth = Mid(cfhttp.filecontent, FindNoCase(&quot;Auth=&quot;,cfhttp.filecontent) + (Len(&quot;Auth=&quot;)), Len(cfhttp.filecontent)) /&gt;
        &lt;/cfif&gt;

         &lt;cflock scope=&quot;session&quot; type=&quot;exclusive&quot; timeout=&quot;5&quot;&gt;
                &lt;cfset session.ga_loginAuth = loginAuth /&gt;
         &lt;/cflock&gt;

    &lt;/cffunction&gt;
</pre>
<p>To retrieve the profile or data feed, a call is made to the API via a function.</p>
<pre class="brush: coldfusion;">
   &lt;cffunction name=&quot;callApi&quot; access=&quot;public&quot; returntype=&quot;array&quot; hint=&quot;GA data as array of structures&quot;&gt;
        &lt;cfargument name=&quot;gaUrl&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;
        &lt;cfargument name=&quot;authToken&quot; type=&quot;string&quot; required=&quot;no&quot; default=&quot;#session.ga_loginAuth#&quot; /&gt;

        &lt;cfset var authTokenHeader = 'GoogleLogin auth=' &amp; arguments.authToken /&gt;

        &lt;cfset var responseOutput = &quot;&quot; /&gt;

        &lt;cfhttp url=&quot;#arguments.gaUrl#&quot; method=&quot;get&quot;&gt;
            &lt;cfhttpparam name=&quot;Authorization&quot; type=&quot;header&quot; value=&quot;#authTokenHeader#&quot;&gt;
        &lt;/cfhttp&gt;

        &lt;cfset responseOutput = cfhttp.filecontent /&gt;
        &lt;!---remove dxp: prefix from nodes that have it and strip xmlns from feed element---&gt;
         &lt;cfset responseOutput = responseOutput.ReplaceAll(&quot;(&lt;/?)(\w+:)&quot;,&quot;$1&quot;) /&gt;
         &lt;cfset responseOutput = REReplaceNoCase(responseOutput,&quot;&lt;feed[^&gt;]*&gt;&quot;,&quot;&lt;feed&gt;&quot;) /&gt;
         &lt;!---entry nodes hold the data---&gt;
         &lt;cfset entryNodes = XmlSearch(responseOutput, '//entry/') /&gt;

         &lt;cfreturn entryNodes /&gt;
    &lt;/cffunction&gt;
</pre>
<p>Next, a list of the profiles (websites) that the user has access to are retrieved by requesting an account feed. If the user has only one profile associated with their credentials, the data is displayed immediately. If they have more than one, they are presented with a select box to select the profile they want data from.</p>
<pre class="brush: coldfusion;">
&lt;cfinvoke component=&quot;ga&quot; method=&quot;parseProfiles&quot; returnvariable=&quot;profilesArray&quot;&gt;&lt;/cfinvoke&gt;
</pre>
<pre class="brush: coldfusion;">
 &lt;cffunction name=&quot;parseProfiles&quot; access=&quot;public&quot; returntype=&quot;array&quot; hint=&quot;GA profiles as array of structures&quot;&gt;

	&lt;cfset var profileArray = ArrayNew(1) /&gt;
        &lt;cfset var entryStruct = StructNew() /&gt;

        &lt;cfset entryNodes = callApi(&quot;https://www.google.com/analytics/feeds/accounts/default&quot;) /&gt;

        &lt;cfloop array=&quot;#entryNodes#&quot; index=&quot;entry&quot;&gt;
            &lt;cfset entryStruct = StructNew() /&gt;

            &lt;cfset entryStruct.title = entry.title.XmlText /&gt;
            &lt;cfset entryStruct.tableId = entry.tableId.XmlText /&gt;

            &lt;cfset arrayAppend(profileArray,duplicate(entryStruct)) /&gt;
        &lt;/cfloop&gt;

        &lt;cfreturn profileArray /&gt;
    &lt;/cffunction&gt;
</pre>
<p>A default date range of one year is set initially. A dialog box that allows the user to change the date range is presented as an option. The date range is limited to a max date of yesterday because GA does not have full data for the current day. This causes the averages that are calculated on the return data to be skewed.</p>
<p>A data feed request url is sent to GA along with the auth token  a via cfhttp tag.</p>
<pre class="brush: coldfusion;">
&lt;cfset reqUrl = &quot;https://www.google.com/analytics/feeds/data?ids=&quot; &amp; session.tableId &amp; &quot;&amp;metrics=ga:newVisits,ga:pageviews,ga:visits,ga:visitors,ga:timeOnSite&amp;start-date=&quot; &amp; session.startdate &amp; &quot;&amp;end-date=&quot; &amp; session.enddate /&gt;
</pre>
<p>The XML response is then parsed out and returned as an array of structures.</p>
<pre class="brush: coldfusion;">
&lt;cffunction name=&quot;parseData&quot; access=&quot;public&quot; hint=&quot;GA data as array of structures set in session&quot;&gt;
        &lt;cfargument name=&quot;gaUrl&quot; type=&quot;string&quot; required=&quot;yes&quot; /&gt;
        &lt;cfargument name=&quot;arrayName&quot; type=&quot;string&quot;required=&quot;yes&quot; /&gt;

        &lt;cfset var dataArray = ArrayNew(1) /&gt;
        &lt;cfset var entryStruct = StructNew() /&gt;

         &lt;cfset entryNodes = callApi(arguments.gaUrl) /&gt;

        &lt;!---CF8 loop through the entries and put each data point in structure---&gt;
        &lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes)#&quot; index=&quot;num&quot;&gt;
		&lt;!---rest of the stats data from GA, first check if dimension exists---&gt;
		&lt;cfif StructKeyExists(entryNodes[num],&quot;dimension&quot;)&gt;
		&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes[num].dimension)#&quot; index=&quot;i&quot;&gt;
		&lt;!---start after ga: to remove it---&gt;
		&lt;cfset &quot;entryStruct.#Mid(entryNodes[num].dimension[i].XmlAttributes[&quot;name&quot;],4,
Len(entryNodes[num].dimension[i].XmlAttributes[&quot;name&quot;]))#&quot; = entryNodes[num].dimension[i].XmlAttributes[&quot;value&quot;] /&gt;
		&lt;/cfloop&gt;
		&lt;/cfif&gt;

		&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes[num].metric)#&quot; index=&quot;i&quot;&gt;
			&lt;cfset &quot;entryStruct.#Mid(entryNodes[num].metric[i].XmlAttributes[&quot;name&quot;],4,
Len(entryNodes[num].metric[i].XmlAttributes[&quot;name&quot;]))#&quot; = entryNodes[num].metric[i].XmlAttributes[&quot;value&quot;] /&gt;
		&lt;/cfloop&gt;

		&lt;cfset arrayAppend(dataArray,duplicate(entryStruct)) /&gt;

	&lt;/cfloop&gt;

         &lt;!---CF9 can use this simplier loop. If you have CF9, uncomment and delete loop above
            &lt;cfloop array=&quot;#entryNodes#&quot; index=&quot;entry&quot;&gt;
		&lt;cfif StructKeyExists(entry,&quot;dimension&quot;)&gt;
                	&lt;cfloop array=&quot;#entry.dimension#&quot; index=&quot;dimension&quot;&gt;
                 	&lt;cfset &quot;entryStruct.#Mid(dimension.XmlAttributes[&quot;name&quot;],4,
Len(dimension.XmlAttributes[&quot;name&quot;]))#&quot; = dimension.XmlAttributes[&quot;value&quot;] /&gt;
                 	&lt;/cfloop&gt;
                 &lt;/cfif&gt;

                 &lt;cfloop array=&quot;#entry.metric#&quot; index=&quot;metric&quot;&gt;
                 	&lt;cfset &quot;entryStruct.#Mid(metric.XmlAttributes[&quot;name&quot;],4,
Len(metric.XmlAttributes[&quot;name&quot;]))#&quot; = metric.XmlAttributes[&quot;value&quot;] /&gt;
                  &lt;/cfloop&gt;

                &lt;cfset arrayAppend(dataArray,duplicate(entryStruct)) /&gt;
           &lt;/cfloop&gt;---&gt;

        	&lt;cflock scope=&quot;session&quot; type=&quot;exclusive&quot; timeout=&quot;5&quot;&gt;
        		&lt;cfset &quot;session.#arrayName#&quot; = dataArray /&gt;
        	&lt;/cflock&gt;

    &lt;/cffunction&gt;
</pre>
<p>Once the data is in a form you can manipulate, graphs, tables, and charts of all kinds can be made.</p>
<p>ClientLogin, AuthSub, and Secure AuthSub authentication examples are included in the demo. <strong>ClientLogin recommended only for client apps.</strong> Google notice on using ClientLogin:</p>
<blockquote><p>
&#8220;Important: Do not use ClientLogin if you are writing an application that runs on<br />
your computer to make requests on behalf of 3rd party end users. Instead, use either<br />
AuthSub or OAuth, which protects end users&#8217; private data. Because ClientLogin stores<br />
the user login data, it should only be used in cases where that data is under the<br />
direct control of the user (e.g. their personal computer).&#8221;</p></blockquote>
<h2>Recommended Reading</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470130652" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p id="demo"><a href="http://cf-jensbits.com/demos/ga/login.cfm" onclick="_gaq.push(['_link', 'http://cf-jensbits.com/demos/ga/login.cfm']); return false;"><span>Demo</span></a></p>
<p id="download"><a href="http://github.com/jensbits/Google-Analytics-Data-Export-API-with-ColdFusion"><span>Download files from github</span></a></p>
<p class="donate">If this post helped you out, please consider donating to help pay the hosting fees. 100% of the donations go to the web host.</p>

<!-- Begin PayPal Donations by http://wpstorm.net/ -->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><div class="paypal-donations"><input type="hidden" name="cmd" value="_donations" /><input type="hidden" name="business" value="jen@jensbits.com" /><input type="hidden" name="return" value="http://www.jensbits.com/thank-you/" /><input type="hidden" name="item_name" value="Help pay hosting. All donations go to hosting fees for this site." /><input type="hidden" name="currency_code" value="USD" /><input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /><img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /></div></form>
<!-- End PayPal Donations -->



<p>Related posts:<ol><li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Google Analytics API Login Authentication with ColdFusion</title>
		<link>http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/</link>
		<comments>http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/#comments</comments>
		<pubDate>Sun, 10 May 2009 23:43:46 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Google Analytics]]></category>
		<category><![CDATA[webdesign]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=123</guid>
		<description><![CDATA[You might also like the ColdFusion and Google Analytics: Getting Out What You Put In post. Updated 08-02-2009: Replaced this line: &#60;cfset accountXML = callApi(&#34;https://www.google.com/analytics/feeds/accounts/#urlEncodedFormat(form.Email)#&#34;,loginAuth) /&#62; with this: &#60;cfset accountXML = callApi(&#34;https://www.google.com/analytics/feeds/accounts/default&#34;,loginAuth) /&#62; My Hooking into Google Analytics with ColdFusion post received a suggestion from Raymond Camden that authentication and token acquisition could be done [...]


Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<blockquote><p>You might also like the <a href="/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/">ColdFusion and Google Analytics: Getting Out What You Put In</a> post.</p></blockquote>
<p><strong>Updated 08-02-2009:</strong> Replaced this line:</p>
<pre class="brush: coldfusion;">
&lt;cfset accountXML = callApi(&quot;https://www.google.com/analytics/feeds/accounts/#urlEncodedFormat(form.Email)#&quot;,loginAuth) /&gt;
</pre>
<p>with this:</p>
<pre class="brush: coldfusion;">
&lt;cfset accountXML = callApi(&quot;https://www.google.com/analytics/feeds/accounts/default&quot;,loginAuth) /&gt;
</pre>
<p>My <a href="/2009/05/02/hooking-into-google-analytics-with-coldfusion/">Hooking into Google Analytics with ColdFusion post</a> received a suggestion from Raymond Camden that authentication and token acquisition could be done via cfhttp for access to Google Analytics information. So, here it is again with authentication built in. I set this up with a email/password form but you could easily hard code the email and password in if you so desired.</p>
<p>1. Prompt for email and password of Google account that has access to Google Analytics data. This is an all-in-one example so the form submits back to its own page and processing continues.</p>
<pre class="brush: xml;">
&lt;form action=&quot;GAwithCFlogin.cfm&quot; method=&quot;post&quot;&gt;
Google email &lt;input type=&quot;text&quot; name=&quot;Email&quot; /&gt;&lt;br /&gt;
Password &lt;input type=&quot;password&quot; name=&quot;password&quot; /&gt;&lt;br /&gt;
&lt;input type=&quot;submit&quot; /&gt;
&lt;/form&gt;
</pre>
<p>2. Authenticate the credentials with Google.</p>
<pre class="brush: coldfusion;">
&lt;cfset loginAuth = googleLogin(form.Email,form.password) /&gt;
</pre>
<p>The googleLogin function sends the email/password combo along with the service and source values as URL parameters required by Google to Google&#8217;s client login URL via a post request. The source parameter is a <em>Short string identifying your application, for logging purposes. This string should take the form: &#8220;companyName-applicationName-versionID&#8221;</em>  according to Google. You can put in your company name or whatever name you want followed by the rest of the convention Google suggests. Example: yourcompanyname-analytics-1.0. The default start and end dates are also set here.</p>
<pre class="brush: coldfusion;">
&lt;!---dates for one full year of stats (default)---&gt;
&lt;cfset startdate = DateFormat(DateAdd(&quot;d&quot;,-365,CreateDate(Year(Now()),Month(Now()),Day(Now()))), &quot;yyyy-mm-dd&quot;) /&gt;
&lt;cfset enddate = DateFormat(DateAdd(&quot;d&quot;, -1, Now()),&quot;yyyy-mm-dd&quot;) /&gt;

&lt;cffunction name=&quot;googleLogin&quot; access=&quot;public&quot; returntype=&quot;string&quot;&gt;
	&lt;cfargument name=&quot;email&quot; type=&quot;string&quot; required=&quot;yes&quot; default=&quot;&quot;&gt;
	&lt;cfargument name=&quot;password&quot; type=&quot;string&quot;required=&quot;yes&quot; default=&quot;&quot;&gt;
    &lt;cfargument name=&quot;gaLoginUrl&quot; type=&quot;string&quot; required=&quot;no&quot; default=&quot;https://www.google.com/accounts/ClientLogin&quot;&gt;

    &lt;cfset var loginAuth = &quot;&quot; /&gt;

    &lt;cfhttp url=&quot;#arguments.gaLoginUrl#&quot; method=&quot;post&quot;&gt;
        &lt;cfhttpparam name=&quot;accountType&quot; type=&quot;url&quot; value=&quot;GOOGLE&quot;&gt;
        &lt;cfhttpparam name=&quot;Email&quot; type=&quot;url&quot; value=&quot;#arguments.email#&quot;&gt;
        &lt;cfhttpparam name=&quot;Passwd&quot; type=&quot;url&quot; value=&quot;#arguments.password#&quot;&gt;
        &lt;cfhttpparam name=&quot;service&quot; type=&quot;url&quot; value=&quot;analytics&quot;&gt;
        &lt;cfhttpparam name=&quot;source&quot; type=&quot;url&quot; value=&quot;yourCompanyName-analytics-1.0&quot;&gt;
    &lt;/cfhttp&gt;

	&lt;cfif NOT FindNoCase(&quot;Auth=&quot;,cfhttp.filecontent)&gt;
		&lt;cfset loginAuth = &quot;Authorization Failed&quot; /&gt;
	&lt;cfelse&gt;
		&lt;cfset loginAuth = Mid(cfhttp.filecontent, FindNoCase(&quot;Auth=&quot;,cfhttp.filecontent) + (Len(&quot;Auth=&quot;)), Len(cfhttp.filecontent)) /&gt;
	&lt;/cfif&gt;

    &lt;cfreturn loginAuth /&gt;
&lt;/cffunction&gt;
</pre>
<p>What should come back is a long string of authorization token values. There are several but we only need the Auth token. If it&#8217;s not in there, we send back &#8220;Authorization Failed.&#8221; If it&#8217;s in there, we strip it out and return it. </p>
<p>3. Get the accounts associated with the email. There can be more than one set up in Analytics.</p>
<pre class="brush: coldfusion;">
&lt;cfset accountXML = callApi(&quot;https://www.google.com/analytics/feeds/accounts/default&quot;,loginAuth) /&gt;
</pre>
<p>The email account is appended onto the Google URL and a get call is made from the callApi function with the loginAuth token attached as a header value.</p>
<pre class="brush: coldfusion;">
&lt;cffunction name=&quot;callApi&quot; access=&quot;public&quot; returntype=&quot;string&quot;&gt;
    &lt;cfargument name=&quot;gaUrl&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;
    &lt;cfargument name=&quot;authToken&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;

    &lt;cfset var authSubToken = 'GoogleLogin auth=' &amp; arguments.authToken /&gt;
    &lt;cfset var responseOutput = &quot;&quot; /&gt;

    &lt;cfhttp url=&quot;#arguments.gaUrl#&quot; method=&quot;get&quot;&gt;
        &lt;cfhttpparam name=&quot;Authorization&quot; type=&quot;header&quot; value=&quot;#authSubToken#&quot;&gt;
    &lt;/cfhttp&gt;

    &lt;cfset responseOutput = cfhttp.filecontent /&gt;

    &lt;cfreturn responseOutput /&gt;
&lt;/cffunction&gt;
</pre>
<p>The header value must be in the form of Authentication: GoogleLogin auth=DF3843483&#8230;(<em>it&#8217;s kind of long but you get the idea</em>). This authentication is different form AuthSub Proxy authentication used in my previous post.</p>
<p>4. Clean up the XML that returned so we can use XmlSearch() and other handy functions available in ColdFusion. For some reason the atom specification of the XML file bungles those functions up. We need to remove the xmlns nodes from the feed element and remove the dxp prefixes. Do a cfdump on the XML before and after two lines of code used to tidy them up to seed what happens. </p>
<pre class="brush: coldfusion;">
&lt;cfdump var=&quot;#accountXML#&quot;&gt;
</pre>
<p>Then roll through the accountXML with to grab all the profiles and toss them into an array so we can loop through them later.</p>
<pre class="brush: coldfusion;">
&lt;!---remove dxp: prefix from nodes that have it---&gt;
	&lt;cfset accountXML = accountXML.ReplaceAll(&quot;(&lt;/?)(\w+:)&quot;,&quot;$1&quot;) /&gt;
	&lt;!---Strip xmlns from feed element---&gt;
	&lt;cfset accountXML = REReplaceNoCase(accountXML,&quot;&lt;feed[^&gt;]*&gt;&quot;,&quot;&lt;feed&gt;&quot;) /&gt;

	&lt;cfset entryNodes = XmlSearch(accountXML, '//entry/') /&gt;

	&lt;cfset profileArray = ArrayNew(1) /&gt;

	&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes)#&quot; index=&quot;num&quot;&gt;
		&lt;cfset entryStruct = StructNew() /&gt;

		&lt;cfset entryStruct.id = entryNodes[num].id.XmlText /&gt;
		&lt;cfset entryStruct.title = entryNodes[num].title.XmlText /&gt;
		&lt;cfset entryStruct.tableId = entryNodes[num].tableId.XmlText /&gt;

		&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes[num].property)#&quot; index=&quot;i&quot;&gt;
   			&lt;cfswitch expression='#entryNodes[num].property[i].XmlAttributes[&quot;name&quot;]#'&gt;
    		    &lt;cfcase value=&quot;ga:accountId&quot;&gt;
       		     &lt;cfset entryStruct.accountId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
     		   &lt;/cfcase&gt;
      		  &lt;cfcase value=&quot;ga:accountName&quot;&gt;
    		        &lt;cfset entryStruct.accountName = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
 		       &lt;/cfcase&gt;
		         &lt;cfcase value=&quot;ga:profileId&quot;&gt;
		               &lt;cfset entryStruct.profileId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
	            &lt;/cfcase&gt;
	            &lt;cfcase value=&quot;ga:webPropertyId&quot;&gt;
	                &lt;cfset entryStruct.webPropertyId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
	            &lt;/cfcase&gt;
	        &lt;/cfswitch&gt;
	    &lt;/cfloop&gt;

		    &lt;cfset arrayAppend(profileArray,duplicate(entryStruct)) /&gt;

		&lt;/cfloop&gt;
</pre>
<p>5. Loop through the profiles, grab the page views and visitors, and tally them up for all profiles.</p>
<pre class="brush: coldfusion;">
&lt;cfset totalpageviews = 0 /&gt;
&lt;cfset totalvisitors = 0 /&gt;

		&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(profileArray)#&quot; index=&quot;num&quot;&gt;
	    	&lt;cfset reqUrl = &quot;https://www.google.com/analytics/feeds/data?ids=&quot; &amp; profileArray[num].tableId &amp; &quot;&amp;metrics=ga:pageviews,ga:visitors&amp;start-date=&quot; &amp; startdate &amp; &quot;&amp;end-date=&quot; &amp; enddate /&gt;

	 		 &lt;cfset statsXML = callApi(reqUrl,loginAuth) /&gt;
	                &lt;!---Tidy up the XML again ---&gt;
	 		 &lt;cfset statsXML = statsXML.ReplaceAll(&quot;(&lt;/?)(\w+:)&quot;,&quot;$1&quot;) /&gt;
			 &lt;cfset statsXML = REReplaceNoCase(statsXML,&quot;&lt;feed[^&gt;]*&gt;&quot;,&quot;&lt;feed&gt;&quot;) /&gt;

			 &lt;cfset metric = XmlSearch(statsXML, '//metric/') /&gt;

			 &lt;cfset pageviews = metric[1].XmlAttributes[&quot;value&quot;] /&gt;

                         &lt;cfset visitors = metric[2].XmlAttributes[&quot;value&quot;] /&gt;

			 &lt;cfset totalpageviews = totalpageviews + pageviews /&gt;

                         &lt;cfset totalvisitors = totalvisitors + visitors /&gt;

			 &lt;p&gt;&lt;strong&gt;Profile:&lt;/strong&gt; &lt;em&gt;#profileArray[num].title#&lt;/em&gt; Pageviews: #NumberFormat(pageviews, &quot;,&quot;)# Visitors: #NumberFormat(visitors, &quot;,&quot;)# &lt;/p&gt;

		 &lt;/cfloop&gt;

		&lt;p&gt;&lt;strong&gt;Total pageviews:&lt;/strong&gt; #NumberFormat(totalpageviews, &quot;,&quot;)#&lt;/p&gt;
                &lt;p&gt;&lt;strong&gt;Total visitors:&lt;/strong&gt; #NumberFormat(totalvisitors, &quot;,&quot;)#&lt;/p&gt;
</pre>
<p>All done. Thank you Raymond Camden and Alex Curelea. Alex got me pointed in the right direction with his post <a href="http://www.alexc.me/using-the-google-analytics-api-getting-total-number-of-page-views/">Using the Google Analytics API &#8211; getting total number of page views</a>. And, as I mentioned at the get go, <a href="http://www.coldfusionjedi.com/">Raymond Camden</a> made the suggestion of using the login as some of his examples have done in the past.</p>
<h2>Recommended Reading</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470130652" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p id="demo"><a href="http://cf-jensbits.com/demos/ga_apilogin/" onclick="_gaq.push(['_link', 'http://cf-jensbits.com/demos/ga_apilogin/']); return false;"><span>Demo</span></a></p>
<p id="download"><a href="/media/GAwithCFlogin.txt"><span>Download</span></a></p>
<p>Information on <a href="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request">Google&#8217;s Authentication for Installed Application ClientLogin</a> and the <a href="http://code.google.com/apis/analytics/docs/gdata/1.0/gdataProtocol.html#ClientLogin">Google Analytics Data API ClientLogin Username/Password Authentication</a>.</p>
<p>Helpful ColdFusion books include the all-in-one <a href="http://www.amazon.com/gp/product/0321223675?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321223675">ColdFusion MX 7 WACK</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321223675" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> with tons of good information even if you&#8217;ve moved up to CF8. Crazy as it seems, Adobe decided to break up ColdFusion 8 books into three volumes: <a href="http://www.amazon.com/gp/product/032151548X?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=032151548X">Volume 1: Getting Started</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=032151548X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, <a href="http://www.amazon.com/gp/product/0321515463?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321515463">Volume 2: Application Development</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321515463" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, and <a href="http://www.amazon.com/gp/product/0321515471?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321515471">Volume 3: Advanced Application Development</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321515471" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. You may be able to pick up a used copy in good condition. Follow the book link to Amazon and look for used copies underneath the books title.</p>


<p>Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/' rel='bookmark' title='Permanent Link: Hooking into Google Analytics with ColdFusion'>Hooking into Google Analytics with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Hooking into Google Analytics with ColdFusion</title>
		<link>http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/</link>
		<comments>http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/#comments</comments>
		<pubDate>Sat, 02 May 2009 21:15:27 +0000</pubDate>
		<dc:creator>jen</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Google Analytics]]></category>

		<guid isPermaLink="false">http://www.jensbits.com/?p=48</guid>
		<description><![CDATA[You might also like the ColdFusion and Google Analytics: Getting Out What You Put In post. Using Alex Curelea&#8217;s example of using PHP to hook into Google Analytics through their API, I developed a ColdFusion method. So, same thing as Alex just flavored with CF. The code grabs the profiles and pageviews, then totals up [...]


Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<blockquote><p>You might also like the <a href="/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/">ColdFusion and Google Analytics: Getting Out What You Put In</a> post.</p></blockquote>
<p>Using <a href="http://www.alexc.me/using-the-google-analytics-api-getting-total-number-of-page-views/">Alex Curelea&#8217;s example of using PHP to hook into Google Analytics</a> through their API, I developed a ColdFusion method. So, same thing as Alex just flavored with CF. The code grabs the profiles and pageviews, then totals up the pageviews for all profiles.</p>
<p><strong>Update:</strong> I followed Raymond Camden&#8217;s suggestion below and <a href="/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/">posted another version of this using ClientLogin Authentication</a>.</p>
<p>1. Authenticate user and get token from Google. Basically just a link that sends them off to Google and Google returns them to your page designated in the href with token attached as URL param.  </p>
<pre class="brush: xml;">
&lt;a href=&quot;https://www.google.com/accounts/AuthSubRequest?next=http://www.your-site-here.com/GAwithCF.cfm&amp;scope=https://www.google.com/analytics/feeds/
&amp;secure=0&amp;session=1&quot;&gt;Authenticate through Google.&lt;/a&gt;
</pre>
<p>2. Send that token back to Google and get a session token that can be used to retrieve the profile information and data. This is handled in two functions that I originally had in a CFC. I put all the code on one page for this example.</p>
<p>For the session token required for access to the data, the sessionToken function is called with the URL.token passed in. </p>
<pre class="brush: coldfusion;">
&lt;cfset authSessionToken = sessionToken(URL.token) /&gt;
</pre>
<p>The sessionToken function makes a get request to https://www.google.com/accounts/AuthSubSessionToken with the token as a header value using the callApi function. The header value must be in the form of <em>Authorization: AuthSub token=&#8221;C8b_xxxxxxxxx&#8221;</em> with the token equaling the URL.token.<br />
The session token comes back in the form of &#8220;Token=CH____88dfdsfsd.&#8221; Strip off the &#8220;Token=&#8221; and you have your session token. The default start and end dates are also set here.</p>
<pre class="brush: coldfusion;">
&lt;!---dates for one full year of stats (default)---&gt;
&lt;cfset startdate = DateFormat(DateAdd(&quot;d&quot;,-365,CreateDate(Year(Now()),Month(Now()),Day(Now()))), &quot;yyyy-mm-dd&quot;) /&gt;
&lt;cfset enddate = DateFormat(DateAdd(&quot;d&quot;, -1, Now()),&quot;yyyy-mm-dd&quot;) /&gt;

&lt;cffunction name=&quot;sessionToken&quot; access=&quot;public&quot; returntype=&quot;string&quot;&gt;
	&lt;cfargument name=&quot;authToken&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;

    &lt;cfset var output = callApi(&quot;https://www.google.com/accounts/AuthSubSessionToken&quot;,arguments.authToken) /&gt;
    &lt;cfset var authSubSessionToken = &quot;&quot; /&gt;

    &lt;cfset authSubSessionToken = Mid(output, FindNoCase(&quot;Token=&quot;,output) + (Len(&quot;Token=&quot;)), Len(output)) /&gt;

    &lt;cfreturn authSubSessionToken /&gt;
&lt;/cffunction&gt;

&lt;cffunction name=&quot;callApi&quot; access=&quot;public&quot; returntype=&quot;string&quot;&gt;
    &lt;cfargument name=&quot;gaUrl&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;
    &lt;cfargument name=&quot;authToken&quot; type=&quot;string&quot; required=&quot;yes&quot;&gt;

    &lt;cfset var authSubToken = 'AuthSub token=&quot;' &amp; arguments.authToken &amp; '&quot;' /&gt;
    &lt;cfset var responseOutput = &quot;&quot; /&gt;

    &lt;cfhttp url=&quot;#arguments.gaUrl#&quot; method=&quot;get&quot;&gt;
        &lt;cfhttpparam name=&quot;Authorization&quot; type=&quot;header&quot; value=&quot;#authSubToken#&quot;&gt;
    &lt;/cfhttp&gt;

    &lt;cfset responseOutput = cfhttp.filecontent /&gt;

    &lt;cfreturn responseOutput /&gt;
&lt;/cffunction&gt;
</pre>
<p>3. Now the callApi function can be used with the authSessionToken to return the account profiles as XML. Unfortunately, it&#8217;s an atom feed XML so a little stripping must occur before we can use the handy XMLSearch function in ColdFusion. </p>
<pre class="brush: coldfusion;">
&lt;cfset accountXML = callApi(&quot;https://www.google.com/analytics/feeds/accounts/default&quot;,authSessionToken) /&gt;
</pre>
<p>First we remove the nodes with a dxp: prefix. Then we clear the &#8220;xmlns&#8221; nodes in the feed element. That&#8217;s what bugs up XMLSearch.</p>
<pre class="brush: coldfusion;">
&lt;cfset accountXML = accountXML.ReplaceAll(&quot;(&lt;/?)(\w+:)&quot;,&quot;$1&quot;) /&gt;
&lt;cfset accountXML = REReplaceNoCase(accountXML,&quot;&lt;feed[^&gt;]*&gt;&quot;,&quot;&lt;feed&gt;&quot;) /&gt;
</pre>
<p>Anytime you want to take a peek at the XML, you can use cfdump to spit it out for you.</p>
<pre class="brush: coldfusion;">
&lt;cfdump var=&quot;#XMLParse(accountXML)#&quot;&gt;
</pre>
<p>4. Parse out the XML to put the profiles in an array for easy looping later. </p>
<pre class="brush: coldfusion;">
&lt;cfset entryNodes = XmlSearch(accountXML, '//entry/') /&gt;

&lt;cfset profileArray = ArrayNew(1) /&gt;

&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes)#&quot; index=&quot;num&quot;&gt;
    &lt;cfset entryStruct = StructNew() /&gt;

    &lt;cfset entryStruct.id = entryNodes[num].id.XmlText /&gt;
    &lt;cfset entryStruct.title = entryNodes[num].title.XmlText /&gt;
    &lt;cfset entryStruct.tableId = entryNodes[num].tableId.XmlText /&gt;

    &lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(entryNodes[num].property)#&quot; index=&quot;i&quot;&gt;
        &lt;cfswitch expression='#entryNodes[num].property[i].XmlAttributes[&quot;name&quot;]#'&gt;
            &lt;cfcase value=&quot;ga:accountId&quot;&gt;
                &lt;cfset entryStruct.accountId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
            &lt;/cfcase&gt;
            &lt;cfcase value=&quot;ga:accountName&quot;&gt;
                &lt;cfset entryStruct.accountName = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
            &lt;/cfcase&gt;
            &lt;cfcase value=&quot;ga:profileId&quot;&gt;
                &lt;cfset entryStruct.profileId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
            &lt;/cfcase&gt;
            &lt;cfcase value=&quot;ga:webPropertyId&quot;&gt;
                &lt;cfset entryStruct.webPropertyId = entryNodes[num].property[i].XmlAttributes[&quot;value&quot;] /&gt;
            &lt;/cfcase&gt;
        &lt;/cfswitch&gt;
    &lt;/cfloop&gt;

    &lt;cfset arrayAppend(profileArray,duplicate(entryStruct)) /&gt;

&lt;/cfloop&gt;
</pre>
<p>5. Loop through each profile, grab the metrics you want for each profile from Google, display them on the page, and tally up the total page views count. The XML needs some tidying again but you saw that earlier.</p>
<pre class="brush: coldfusion;">
&lt;cfset totalpageviews = 0 /&gt;

&lt;cfloop from=&quot;1&quot; to=&quot;#ArrayLen(profileArray)#&quot; index=&quot;num&quot;&gt;
    &lt;cfset reqUrl = &quot;https://www.google.com/analytics/feeds/data?ids=&quot; &amp; profileArray[num].tableId &amp; &quot;&amp;metrics=ga:pageviews&amp;start-date=&quot; &amp; startdate &amp; &quot;&amp;end-date=&quot; &amp; enddate /&gt;

    &lt;cfset statsXML = callApi(reqUrl,authSessionToken) /&gt;
    &lt;!---Tidy up the XML---&gt;
    &lt;cfset statsXML = statsXML.ReplaceAll(&quot;(&lt;/?)(\w+:)&quot;,&quot;$1&quot;) /&gt;
    &lt;cfset statsXML = REReplaceNoCase(statsXML,&quot;&lt;feed[^&gt;]*&gt;&quot;,&quot;&lt;feed&gt;&quot;) /&gt;

&lt;cfset metric = XmlSearch(statsXML, '//metric/') /&gt;

&lt;cfset pageviews = metric[1].XmlAttributes[&quot;value&quot;] /&gt;

&lt;cfset totalpageviews = totalpageviews + pageviews /&gt;

&lt;p&gt;Profile: #profileArray[num].title# #NumberFormat(pageviews, &quot;,&quot;)#&lt;/p&gt;

&lt;/cfloop&gt;

&lt;p&gt;Total pageviews: #NumberFormat(totalpageviews, &quot;,&quot;)#&lt;/p&gt;
</pre>
<p>That&#8217;s it. It should at least get you started. Thanks Alex!</p>
<h2>Recommended Reading</h2>
<div style="height: 250px;">
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470529393" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470130652" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470562315" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style="float:left;margin-right: 25px">
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=000000&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jensbits-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=0470531282" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
</div>
<p id="demo"><a href="http://cf-jensbits.com/demos/ga_authsub/" onclick="_gaq.push(['_link', 'http://cf-jensbits.com/demos/ga_authsub/']); return false;"><span>Demo</span></a></p>
<p id="download"><a href="/media/GAwithCF.txt"><span>Download</span></a></p>
<p>News of the <a href="http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html"><span>Google Analytics API</a></p>
<p>Information on <a href="http://code.google.com/apis/analytics/docs/gdata/1.0/gdataProtocol.html#AuthSub">Google AuthSub Proxy Authentication</a>.</p>
<p>Helpful ColdFusion books include the all-in-one <a href="http://www.amazon.com/gp/product/0321223675?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321223675">ColdFusion MX 7 WACK</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321223675" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> with tons of good information even if you&#8217;ve moved up to CF8. Crazy as it seems, Adobe decided to break up ColdFusion 8 books into three volumes: <a href="http://www.amazon.com/gp/product/032151548X?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=032151548X">Volume 1: Getting Started</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=032151548X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, <a href="http://www.amazon.com/gp/product/0321515463?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321515463">Volume 2: Application Development</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321515463" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, and <a href="http://www.amazon.com/gp/product/0321515471?ie=UTF8&#038;tag=jensbits-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321515471">Volume 3: Advanced Application Development</a><img src="http://www.assoc-amazon.com/e/ir?t=jensbits-20&#038;l=as2&#038;o=1&#038;a=0321515471" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. You may be able to pick up a used copy in good condition. Follow the book&#8217;s link above to Amazon and look for used copies underneath the books title.</p>


<p>Related posts:<ol><li><a href='http://www.jensbits.com/2009/12/19/coldfusion-and-google-analytics-getting-out-what-you-put-in/' rel='bookmark' title='Permanent Link: ColdFusion and Google Analytics: Getting Out What You Put In'>ColdFusion and Google Analytics: Getting Out What You Put In</a></li>
<li><a href='http://www.jensbits.com/2009/05/10/google-analytics-api-login-authentication-with-coldfusion/' rel='bookmark' title='Permanent Link: Google Analytics API Login Authentication with ColdFusion'>Google Analytics API Login Authentication with ColdFusion</a></li>
<li><a href='http://www.jensbits.com/2010/05/16/generating-signatures-in-coldfusion-with-rsa-sha1-for-secure-authsub-in-google-analytics/' rel='bookmark' title='Permanent Link: Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics'>Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.jensbits.com/2009/05/02/hooking-into-google-analytics-with-coldfusion/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
