Google Analytics

Documentation courtesy of Bill Brown, Web Application Developer, from The University of Chicago

Google Analytics page and event tracking can be setup in uPortal. The example below uses the analytics tracking API (http://code.google.com/apis/analytics/docs/tracking/home.html),
a java filter and a uPortal IParameterProcessor to achieve session based tracking of portal events for users of a uPortal instance.

The example can be adapted for your institutions requirements by swaping in different user attributes.

Step 1: Add the attached java class files

  • The attached java classes, GoogleSessionThemeParameter, below needs to be added to the following location: ../uportal-impl/src/main/java/org/jasig/portal/layout/dlm/providers

These java class files will be now be located alongside the other theme processors.

Step 2: Add Parameters

  1. Open universality.xsl for editing located at ../uportal-war/src/main/resources/layout/theme/universality/universality.xsl
  2. At the top of the file where the other parameters of the page are set including the standard “USER_ID” we add the following three parameters:
  <xsl:param name="affiliations">none</xsl:param>
  <xsl:param name="serverEnv">localhost</xsl:param>
  <xsl:param name="sessionDataRan">no</xsl:param>

affiliations is a user attribute we use to recognize the logged in user’s affiliation to the institution and has values such as “Student”, “Faculty” or “Staff” .
Other institutions may call this something else but it is just an example of an attribute that is pulled from the person directory of attributes for a user.

serverEnv is a variable we are using to track which server environment we are tracking data for in Google analytics.
For example we use development, staging and production environments. We have a Google analytics account for each environment for a total of three distinct environments.

sessionDataRan is a variable used in the xsl to control when to output once-per-session javascript in the page. This is key to tracking some of the data we are interested in within the Google analytics.

These three parameters are set in the GoogleSessionThemeParameter (attached) class which is a custom IParameterProcessor (wired into the portal in the uportal-impl/src/main/properties/context/layoutContext.xml file). Every time a page loads in the portal, the IParameterProcessor is called and sets the above parameters so that they can be read in the universality.xml xsl page. 

Step 3: Add Google Analytics code

  1. Edit universality.xsl (../uportal-war/src/main/resources/layout/theme/universality/universality.xsl)
  2. Add the two following template code snippets, page.js and page-bottom.js, to universality.xsl

This section sets up asynchronous google analytics tracking. It is located in the page.js template which puts the script at the top of every generated page in the head element.
It makes use of the “serverEnv” to create a tracker for one of the three the server environments (development, staging and production) running the code.

<xsl:template name="page.js">
        <script type="text/javascript">
		<xsl:choose>
			<xsl:when test="$serverEnv='my.uchicago.edu'">
				var env = "<xsl:value-of select="$serverEnv" />";
				var gaAcct = "*************";
			</xsl:when>
			<xsl:when test="$serverEnv='mystage.uchicago.edu'">
				var env = "<xsl:value-of select="$serverEnv" />";
				var gaAcct = "*************";
			</xsl:when>
			<xsl:otherwise>
				var env = "<xsl:value-of select="$serverEnv" />";
				var gaAcct = "*************";
			</xsl:otherwise>
		</xsl:choose>
		var _gaq = _gaq || [];
		_gaq.push(['_setAccount', gaAcct],['_setDomain', env],['_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);
		})();
	</script>
</xsl:template>

Located in the page-bottom.js template, this next piece of code is output at the bottom of the webpage just before the closing body tab. The section reads the theme parameter “sessionDataRan”
to determine if the javascript should be output in the page to the browser. The first time a user logs into the portal this value defaults to “no” and will include this snippet of javascript
on the portal homepage. GoogleSessionThemeParameter then changes the value to “yes”, so subsequent reloading of the page will not output this section of javascript.

<xsl:template name="page-bottom.js">
  	<xsl:if test="$sessionDataRan!='yes'">

	  	<script type="text/javascript">

	       up.jQuery(document).ready(function(){
	        	var affiliations = "<xsl:value-of select="$affiliations" />".split(";");
	        	for ( var i in affiliations ){
		          	_gaq.push(['_trackEvent','Person','<xsl:value-of select="$USER_ID" />',affiliations[i]]);
	          	}
	          	for ( var i in affiliations ){
		          	_gaq.push(['_trackEvent','Affiliation',affiliations[i],'<xsl:value-of select="$USER_ID" />']);
	          	}
	       });
	    </script>
    </xsl:if>
  </xsl:template>

Step 4: Add tracking

Once the tracking api is loaded, we place tracking code on any of the user initiated events we want to track during their session.

For example, in the sections below we are tracking these events: portlet minimize, portlet maximize, portlet removed, change portlet tab, add content, add tab and customize tab layout.

Components.xsl

<a id="contentDialogLink" onclick="_gaq.push(['_trackEvent', 'Portal','ADD CONTENT']);" href="javascript:;"
    						title="{$TOKEN[@name='PREFERENCES_LINK_ADD_CONTENT_LONG_LABEL']}">
    						<span><xsl:value-of select="$TOKEN[@name='PREFERENCES_LINK_ADD_CONTENT_LABEL']"/></span>
    					</a>

...
<a id="layoutDialogLink" onclick="_gaq.push(['_trackEvent', 'Portal','TAB LAYOUT']);" href="javascript:;"
    						title="{$TOKEN[@name='PREFERENCES_LINK_LAYOUT_LONG_LABEL']}">
    						<span><xsl:value-of select="$TOKEN[@name='PREFERENCES_LINK_LAYOUT_LABEL']"/></span>
    					</a>
...
<a id="addTabLink" onclick="_gaq.push(['_trackEvent', 'Portal','ADD TAB']);" href="javascript:;"
                    		title="{$TOKEN[@name='PREFERENCES_LINK_ADD_TAB_LONG_LABEL']}">
                    		    <span><xsl:value-of select="$TOKEN[@name='PREFERENCES_LINK_ADD_TAB_LABEL']"/></span>
                    		</a>

 
Content.xsl

      <xsl:if test="not(@hasHelp='false')"> <!-- Help. -->
      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Help']);" href="{$BASE_ACTION_URL}?uP_help_target={@ID}#{@ID}"
          title="{$TOKEN[@name='PORTLET_HELP_LONG_LABEL']}" class="up-portlet-control help">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_HELP_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="not(@hasAbout='false')"> <!-- About. -->
      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-About']);" href="{$BASE_ACTION_URL}?uP_about_target={@ID}#{@ID}"
          title="{$TOKEN[@name='PORTLET_ABOUT_LONG_LABEL']}" class="up-portlet-control about">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_ABOUT_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="not(@editable='false')"> <!-- Edit. -->
      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Edit']);" href="{$BASE_ACTION_URL}?uP_edit_target={@ID}#{@ID}"
          title="{$TOKEN[@name='PORTLET_EDIT_LONG_LABEL']}" class="up-portlet-control edit">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_EDIT_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="@printable='true'"> <!-- Print. -->
      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Print']);" href="{$BASE_ACTION_URL}?uP_print_target={@ID}#{@ID}"
          title="{$TOKEN[@name='PORTLET_PRINT_LONG_LABEL']}" class="up-portlet-control print">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_PRINT_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="not(//focused) and @minimized='false'"> <!-- Focus. -->
        <!-- UNCOMMENT FOR MINIMIZE CONTROL -->
        <a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Minimize']);"
           href="{$BASE_ACTION_URL}?uP_root=root&amp;uP_tcattr=minimized&amp;minimized_channelId={@ID}&amp;minimized_{@ID}_value=true&amp;uP_save=all"
           title="{$TOKEN[@name='PORTLET_MINIMIZE_LONG_LABEL']}" class="up-portlet-control minimize">
          <span><xsl:value-of select="$TOKEN[@name='PORTLET_MINIMIZE_LABEL']"/></span>
        </a>

      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Maximize']);" href="{$BASE_ACTION_URL}?uP_root={@ID}"
          title="{$TOKEN[@name='PORTLET_MAXIMIZE_LONG_LABEL']}" class="up-portlet-control focus">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_MAXIMIZE_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="@minimized='true'"> <!-- Return from Minimized. -->
        <!-- UNCOMMENT FOR UNMINIMIZE CONTROL -->
        <a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-UnMinimized']);"
           href="{$BASE_ACTION_URL}?uP_root=root&amp;uP_tcattr=minimized&amp;minimized_channelId={@ID}&amp;minimized_{@ID}_value=false&amp;uP_save=all"
           title="{$TOKEN[@name='PORTLET_RETURN_LONG_LABEL']}" class="up-portlet-control return">
          <span><xsl:value-of select="$TOKEN[@name='PORTLET_RETURN_LABEL']"/></span>
        </a>

      </xsl:if>

      <xsl:if test="not(@dlm:deleteAllowed='false') and not(//focused) and /layout/navigation/tab[@activeTab='true']/@immutable='false'"> <!-- Remove. -->
      	<a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Removed']);" id="removePortlet_{@ID}"
           title="{$TOKEN[@name='PORTLET_REMOVE_LONG_LABEL']}" href="{$BASE_ACTION_URL}?uP_remove_target={@ID}"
           class="up-portlet-control remove">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_REMOVE_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="//focused"> <!-- Return from Focused. -->
        <a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-UnMaximize']);" href="{$HOME_ACTION_URL}"
          title="{$TOKEN[@name='PORTLET_RETURN_LONG_LABEL']}" class="up-portlet-control return">
      	  <span><xsl:value-of select="$TOKEN[@name='PORTLET_RETURN_LABEL']"/></span>
        </a>
      </xsl:if>

      <xsl:if test="//focused[@in-user-layout='no'] and upGroup:isChannelDeepMemberOf(//focused/channel/@fname, 'local.1')"> <!-- Add to layout. -->
        <a onclick="_gaq.push(['_trackEvent','Portlet', '{$PORTLET_TITLE}-Add']);" id="focusedContentDialogLink" href="javascript:;"
          title="{$TOKEN[@name='PORTLET_ADD_LONG_LABEL']}" class="up-portlet-control add">
          <span><xsl:value-of select="$TOKEN[@name='PORTLET_ADD_LABEL']"/></span>
        </a>
      </xsl:if>

Navigation.xsl

<xsl:for-each select="tabChannel">
    <li>
       <a onclick="_gaq.push(['_trackEvent', 'Portal','{@name}']);" href="{$BASE_ACTION_URL}?uP_root={@ID}&amp;uP_sparam=activeTab&amp;activeTab={$TAB_POSITION}"
         title="{@name}">  <!-- Navigation item link. -->
         <span><xsl:value-of select="@name"/></span>
       </a>
     </li>
 </xsl:for-each>

Step 5: Rebuild uPortal

After adding all of your tracking code, you should rebuild and redeploy your portal.

Step 6: Restart Tomcat

It will take 24 hours for you tracking code to show up in the portal.

Here is a shared link to the custom report we created in google analytics to view the data captured

http://www.google.com/analytics/reporting/edit_custom_report?share=1JWn-CsBAAA.5cmZVfTgv7FSDQaf3SApcGAvGcpxBXiS-FtJmzYNNtX1hksXVl4-tYJ0_inUcykuV8UtMv4FhYgXpDwi-RVMiQ.q0LruKs21ojmLA20F1ZXQQ

(I think you have to be logged into analytics to view the link).

  File Modified

Java Source GoogleSessionThemeParameter.java Contribution from The University of Chicago (Bill Brown)

Aug 10, 2011 by Laura McCord

Shared Google Analytics Implementations

Having problems with these instructions?

Please send us feedback at uportal-user@lists.ja-sig.org