Friday, August 26, 2011

Tracking AJAX Content with Google Analytics

Written by Robert Scavilla+
Updated: September 10, 2011


This article discusses how to configure Google Analytics for tracking AJAX content on your site. AJAX works by updating a page element dynamically without causing a page refresh. The AJAX call is initiated from an event triggered in the browser. The following code examples assume you have a working knowledge of JavaScript and jQuery. Sample code from a SEO AJAX test site is used in this article.

The Google Analytics Tracking Code

If your Google Analytics (GA) code is installed  correctly, it is loaded in the last line of the page <head> section. When the page is loaded, the _gaq variable is created and initialized to your profile id with the _setAccount call. The _trackPageview call does the work of registering the pageview in your GA profile. There are additional parameters that can be added to the _trackPageview call; however, without any parameters, the URL for the page will be recorded.

var _gaq = _gaq || [];
 _gaq.push(['_setAccount', 'UA-123456']);
 _gaq.push(['_trackPageview']);
Typically a static HTML or php page will contain the GA tracking code as well as the JavaScript code to initiate the AJAX. When this static page loads, the _trackPageview function will register the URL of the page by default.

Tracking AJAX Content

For this example the jQuery .load() function is used to handle the AJAX call.

Let's assume your page has a <div id="content"></div> element . To populate this element with results of an AJAX call, you could add the following code to the event handler:
$('#content').load('/ajax_url'); // load the content div
You could also add a _trackPageview call in the same event handler to record the pageview:
_gaq.push(['_trackPageview', '/ajax_url']);
While this will work for the most basic AJAX applications, it will not work accurately for AJAX applications that allow direct access to the AJAX content from the address bar.

AJAX and the Address Bar

If your AJAX application supports direct access to the AJAX content, you are probably using the hash fragment to maintain the state of the AJAX content. If so, you load the main static page first, then update the content element before the page is displayed. In this case, to prevent multiple pageviews from being recorded during the AJAX load, you have to add a conditional test in the Javascript code before the _trackPageview method is called in the page head section.
The following URL has a hash fragment that is used for maintaining AJAX state:

http://ajax.rswebanalytics.com/seo-for-ajax/#!ajax_and_the_address_bar

For the above URL, the /seo-for-ajax page is loaded from the server, then the onload() function replaces the content element with the ajax_and_the_address_bar content before the page is diplayed. In this case you would not want the _trackPageview method called when the _seo-for-ajax page is loaded, rather only when the ajax_and_the_address_bar content is loaded. This is done using the following code:
<script type="text/javascript">
          var _gaq = _gaq || [];
          _gaq.push(['_setAccount', 'UA-123456']);
          // only if the hash fragment is empty, record the pageview 
          if(location.hash == '') _gaq.push(['_trackPageview']);
          (function(){var ga=document.......
</script>
<script type="text/javascript">        
function load_content()
{
    q = window.location.hash.substring(1);
    a = '//ajax_url' + q.substr(1,q.length)
    $('#content').load(a); // load the content div with the results of the AJAX request
    _gaq.push(['_trackPageview', 'ajax_url' + q.substr(1,q.length)]);
}
</script>

Saturday, June 18, 2011

Using OAuth 2.0 to Access Google APIs - PHP Code Example


Written by Robert Scavilla

Updated: December 19, 2011

This document outlines how to use OAuth 2.0 validation to access Google API feed endpoints. The Webmaster Tools API is used here only as an example of how to interact with a Google API feed.  The example code in this document is written in PHP.

OAuth 2.0

Google is now supporting OAuth 2.0 for API security validation. To summarize the draft specification of the OAuth 2.0 protocol, OAuth enables clients to access protected resources by obtaining an access token as "a string representing an access authorization issued to the client" rather than using the resource owner's credentials.  Tokens are issued to clients by an authorization server with the approval of the resource owner. For more information on OAuth 2.0, please reference the Google documentation Using OAuth 2.0 to Access Google APIs.

Getting a Google API Key

Before you begin, you must register your application with Google and get an API key. This registration associates your Google account with your application. I won’t go into great detail here, but in short here are the steps to get a Google API key for your application:
  • If you don’t already have an API project, create one. (https://code.google.com/apis/console)
  • Under the Project menu (top left) select API Access
  • Click on Create an OAuth 2.0 client ID…
  • You will be prompted to fill in your branding information. The name of your application (Product name) and the Product Logo will be displayed to the user during the authentication process.
  • When you click next, you will be prompted to fill in the Client ID settings. Under Application type, be sure Web application is selected. Next to Your site or hostname click on (more options).  The dialog box will look like this:


  • Authorized Redirect URIs: During the authentication process, the OAuth2 server will prompt the user to Allow or disallow your application to have access to the Google account. You must register at least one URL here to which you want the OAuth2 server to send the permission response. You can register multiple redirect URLs in this box.
  • Enter your root domain in the Authorized Javascript Origins – this field is not used in this example.
  • Click on the Create client ID button. You will then be taken to your API Access page. You will see four fields in the Client ID for web applications box:
Client ID: xxxxxxxx.apps.googleusercontent.com
Client secret: xcfrtgd_dfLcHxx6
Redirect URIs: https://www.example.com/oauth2callback
JavaScript origins: https://www.example.com


Ask permission to access the Google Account

The first step in OAuth2.0 Server side authentication is for the application to send a request to the Google OAuth2 server with the Client ID information and the redirect URL. The OAuth2 server prompts the user to allow or disallow your application to access the Google account.


If the user clicks on the No thanks button, the redirect URL is called with the URI parameter:
?error=access_denied

If the user clicks on the Allow Access button, the redirect URL is called with the URI parameter:
?code=4/FXxxxxxxxxxxxxx_LcSwvVZGk  .

The value of the code parameter is the authorization code. This authorization code needs to be swapped for an access token, but first, here is the code to get the authorization code.


 1 <?php
 2 $OAuth = array(
 3     'oauth_uri' => 'https://accounts.google.com/o/oauth2/auth',
 4     'client_id' => 'insert your Client ID here',
 5     'redirect_uri' => 'http://example.com/OAuth2' // replace with your redirect uri
 6 );
 7 $title = 'No Code';
 8 $AuthCode = 'Null';
 9
10 // see if error parameter exisits
11 $error = _get_url_param($_SERVER['REQUEST_URI'], 'error');
12 if ($error != NULL)
13 {   // this means the user denied api access to GWMTs
14     $title = $error;
15 }
16 else
17 {   // does the code parameter exist?
18     $AuthCode = _get_url_param($_SERVER['REQUEST_URI'], 'code');
19     if ($AuthCode == NULL)
20     {   // get authorization code
21         $OAuth_request = _formatOAuthReq($OAuth,
22                         "https://www.google.com/webmasters/tools/feeds/sites/");
23
24         header('Location: ' . $OAuth_request);
25         exit; // the redirect will come back to this page and $code will have a value
26     }
27     else
28     {
29         $title = 'Got Authorization Code';
30     }
31 }
32
33 function _formatOAuthReq($OAuthParams, $scope)
34 {
35     $uri = $OAuthParams['oauth_uri'];
36     $uri .= "?client_id=" . $OAuthParams['client_id'];
37     $uri .= "&redirect_uri=" . $OAuthParams['redirect_uri'];
38     $uri .= "&scope=" . $scope;
39     $uri .= "&response_type=code";
40
41     return $uri;
42 }
43
44 function _get_url_param($url, $name)
45 {
46     parse_str(parse_url($url, PHP_URL_QUERY), $params);
47     return isset($params[$name]) ? $params[$name] : null;
48 }
49 ?>
50 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
51 <html>
52     <head>
53         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
54         <title><?= $title; ?></title>
55     </head>
56     <body>
57         <h1>OAuth2 Authorization Code</h1>
58         <p>Code = <?= $AuthCode; ?></p>
59
60     </body>
61 </html>
62
On line 22 in the source code listing above is the URL of the Google Webmaster Tools 'sites' feed. This feed is referred to as the 'scope' of the authorization meaning that the authorization code is only valid for this feed. Each feed requires its own authorization code. You can consult the Google Webmaster Tools API documentation if you require more information on this feed.

Requesting the Access Token

Now that we have the authorization code we need to exchange it for an access token and refresh token. This requires POSTing parameters to the OAuth 2.0 server. To do the POST, I am using the PHP curl library. Changes from the first listing include:  the $OAuth array defined on line 2 of the source listing was modified, the $token array was added, and there were two additional functions added to format and POST the request. Here is the PHP code:


  1 <?php
  2 $OAuth = array(
  3     'oauth_uri' => 'https://accounts.google.com/o/oauth2/auth',
  4     'client_id' => 'insert your Client ID here',
  5     'client_secret' => 'insert your client secret here',
  6     'redirect_uri' => 'http://example.com/OAuth2', // replace with your redirect uri
  7     'oauth_token_uri' => 'https://accounts.google.com/o/oauth2/token'
  8 
  9 );
 10 $token = array(
 11     'access_token' => '',
 12     'token_type' => '',
 13     'expires_in' => '',
 14     'refresh_token' => ''
 15 );
 16 
 17 $title = 'No Code';
 18 $AuthCode = 'Null';
 19 
 20 // see if error parameter exisits
 21 $error = _get_url_param($_SERVER['REQUEST_URI'], 'error');
 22 if ($error != NULL)
 23 {   // this means the user denied api access to GWMTs
 24     $title = $error;
 25 }
 26 else
 27 {   // does the code parameter exist?
 28     $AuthCode = _get_url_param($_SERVER['REQUEST_URI'], 'code');
 29     if ($AuthCode == NULL)
 30     {   // get authorization code
 31         $OAuth_request = _formatOAuthReq($OAuth,
 32                         "https://www.google.com/webmasters/tools/feeds/sites/");
 33 
 34         header('Location: ' . $OAuth_request);
 35         exit; // the redirect will come back to this page and $code will have a value
 36     }
 37     else
 38     {
 39         $title = 'Got Authorization Code';
 40         // now exchange Authorization code for access token and refresh token
 41         $token_response = _get_auth_token($OAuth, $AuthCode);
 42         $json_obj = json_decode($token_response);
 43         $token['access_token'] = $json_obj->access_token;
 44         $token['token_type'] = $json_obj->token_type;
 45         $token['expires_in'] = $json_obj->expires_in;
 46         $token['refresh_token'] = $json_obj->refresh_token;
 47         echo 'access_token = ' . $json_obj->access_token;
 48     }
 49 }
 50 
 51 function _get_auth_token($params, $code)
 52 {
 53     $url = $params['oauth_token_uri'];
 54 
 55     $fields = array(
 56         'code' => $code,
 57         'client_id' => $params['client_id'],
 58         'client_secret' => $params['client_secret'],
 59         'redirect_uri' => $params['redirect_uri'],
 60         'grant_type' => 'authorization_code'
 61     );
 62     $response = _do_post($url, $fields);
 63     return $response;
 64 }
 65 
 66 function _do_post($url, $fields)
 67 {
 68     $fields_string = '';
 69 
 70     foreach ($fields as $key => $value)
 71     {
 72         $fields_string .= $key . '=' . $value . '&';
 73     }
 74     $fields_string = rtrim($fields_string, '&');
 75 
 76     $ch = curl_init();
 77     curl_setopt($ch, CURLOPT_URL, $url);
 78     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 79     curl_setopt($ch, CURLOPT_POST, count($fields));
 80     curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
 81     $response = curl_exec($ch);
 82     curl_close($ch);
 83 
 84     return $response;
 85 }
The response from the POST is a JSON array which is decoded on line 42 and stored in the $token array. The access token has a life of 1 hour.

Using the access token to request data from the Google Webmaster Tools 'sites' feed

Now your server app is fully authorized and we are ready to request data from a feed. Since the scope of the authorization is for the Webmaster Tools sites feed, we can only request services from that feed. The following code retrieves the list of servers registered in your Google Webmaster Account. The request is sent to the sites feed and the response is in xml format. The _parse_wmt_sites_response function parses the title field for each entry and builds an HTML list of the servers. The title field contains the URL of the site.

 36     else
 37     {
 38         $title = 'Got Authorization Code';
 39         // now exchange Authorization code for access token and refresh token
 40         $token_response = _get_auth_token($OAuth, $AuthCode);
 41         $json_obj = json_decode($token_response);
 42         $token['access_token'] = $json_obj->access_token;
 43         $token['token_type'] = $json_obj->token_type;
 44         $token['expires_in'] = $json_obj->expires_in;
 45         $token['refresh_token'] = $json_obj->refresh_token;
 46 
 47         $sites = _get_wmt_sites_feed($token);
 48     }
 49 }
 50 
 51 // Return the list of sites registered in your Google Webmaster Tools
 52 function _get_wmt_sites_feed($access_tokens)
 53 {
 54     $post_string = "https://www.google.com/webmasters/tools/feeds/sites/";
 55     $post_string .= '?v=2';
 56     $post_string .= '&oauth_token=' . $access_tokens['access_token'];
 57 
 58     $response = file_get_contents($post_string);
 59     return _parse_wmt_sites_response($response);
 60 }
 61 
 62 function _parse_wmt_sites_response($response)
 63 {
 64     $xml = simplexml_load_string($response);
 65     $response = '<br />';
 66     foreach ($xml->entry as $entry)
 67     {
 68         foreach ($entry->title as $title)
 69         {
 70             $response .= "<p><a href=\"$title\" target=\"_blank\">$title</a></p>";
 71         }
 72     }
 73     return $response;
 74 }

Access token expiration

When the access token expires an API call will return a HTTP 401 Unauthorized. To get a refresh token you can POST at any time to the token endpoint with the refresh token and the 'grant_type' parameter set to 'refresh_token'.


Get Refresh Token
 90 function _get_refresh_token($params, $code)
 91 {
 92     $url = $params['oauth_token_uri'];
 93 
 94     $fields = array(
 95         'code' => $code,
 96         'client_id' => $params['client_id'],
 97         'client_secret' => $params['client_secret'],
 98         'refresh_token' => $token['refresh_token'],
 99         'grant_type' => 'refresh_token'
100     );
101     $response = _do_post($url, $fields);
102     return $response;
103 }