Wesley Bakker

Interesting things I encounter doing my job...

Sponsors

News

Wesley Bakker
motion10
Rivium Quadrant 151
2909 LC Capelle aan den IJssel
Region of Rotterdam
The Netherlands
Phone: +31 10 2351035

(feel free to chat with me)
 

Add to Technorati Favorites

SharePoint Google Analytics Feature

Being nice to Google

After my last post I felt the need to say something nice about Google and implement one of their features in SharePoint as a feature. The chosen feature is Google Analytics. Google Analytics is a great tool but implementing it in SharePoint isn't as simple as it might seem at first. In this post I'll walk you through in how to create such a feature. You can find the complete source attached.

Site ID

You can't just edit your master page and append the generated script in your header. This is because you need to register your site first. This registration gives you a site id and that site id should be appended to your scripts src url. But then we have a problem with URL zones. What if someone can reach my site through http://intranet.motion10.com and http://extranet.motion10.com? So we must create something a little bit more robust.

UI

First of all we must take care for the UI. If we want our clients to enter their Google Analytics Site ID per url zone we should provide some sort of interface. So a layouts page is what we'll need. Fortunately it's not that hard to create a layouts page yourselfs. Here's a snippet out of the GoogleAnalyticsFeatureSettings.aspx. (Find the full source attached.)

<wssuc:InputFormControl LabelText="Intranet zone" LabelAssociatedControlID="GoogleAnalyticsSiteIDTextBoxIntranet" ID="InputFormControlIntranet" runat="server">
    <Template_Control>
      <sp:InputFormTextBox Title="Intranet zone" class="ms-input" ID="GoogleAnalyticsSiteIDTextBoxIntranet" Columns="35" Runat="server" MaxLength="16" />
    </Template_Control>
</wssuc:InputFormControl>
<wssuc:InputFormControl LabelText="Internet zone" LabelAssociatedControlID="GoogleAnalyticsSiteIDTextBoxInternet" ID="InputFormControlInternet" runat="server">
    <Template_Control>
      <sp:InputFormTextBox Title="Internet zone" class="ms-input" ID="GoogleAnalyticsSiteIDTextBoxInternet" Columns="35" Runat="server" MaxLength="16" />
    </Template_Control>
</wssuc:InputFormControl>
<wssuc:InputFormControl LabelText="Custom zone" LabelAssociatedControlID="GoogleAnalyticsSiteIDTextBoxCustom" ID="InputFormControlCustom" runat="server">
    <Template_Control>
      <sp:InputFormTextBox Title="Custom zone" class="ms-input" ID="GoogleAnalyticsSiteIDTextBoxCustom" Columns="35" Runat="server" MaxLength="16" />
    </Template_Control>
</wssuc:InputFormControl>

With this declarations we're not there yet. We do need some code to get and set this values and save them in some persistend place. Fortunately we can create a class that inherits from LayoutsPageBase and if we add proteced members in that class with the same ID's we used in our declarations, ASP.Net will bind those two together. Here's some code snippet that shows you how.

public class GoogleAnalyticsFeatureSettings : LayoutsPageBase {
    protected InputFormTextBox GoogleAnalyticsSiteIDTextBoxDefault;
    protected InputFormControl InputFormControlDefault;
    protected InputFormTextBox GoogleAnalyticsSiteIDTextBoxIntranet;
    protected InputFormControl InputFormControlIntranet;

------------------------------------------------

private void Page_Load(object sender, EventArgs e) {
    if (!Page.IsPostBack) {
        GoogleAnalyticsSiteIDTextBoxDefault.Text = SolutionPackUtility.GetGoogleAnalyticsSiteIdByUrlZone(SPUrlZone.Default);
        InputFormControlDefault.LabelText += GenerateLabelTextForUrlZone(SPUrlZone.Default);
        
        GoogleAnalyticsSiteIDTextBoxIntranet.Text = SolutionPackUtility.GetGoogleAnalyticsSiteIdByUrlZone(SPUrlZone.Intranet);
        InputFormControlIntranet.LabelText += GenerateLabelTextForUrlZone(SPUrlZone.Intranet);

------------------------------------------------

protected void SaveButton_Click(object sender, EventArgs e) {
    if (!string.IsNullOrEmpty(GoogleAnalyticsSiteIDTextBoxDefault.Text)) {
        SolutionPackUtility.SetGoogleAnalyticsSiteIdByUrlZone(GoogleAnalyticsSiteIDTextBoxDefault.Text,
                                                              SPUrlZone.Default);
    }

    if (!string.IsNullOrEmpty(GoogleAnalyticsSiteIDTextBoxIntranet.Text)) {
        SolutionPackUtility.SetGoogleAnalyticsSiteIdByUrlZone(GoogleAnalyticsSiteIDTextBoxIntranet.Text,
                                                              SPUrlZone.Intranet);
    }

Nice code right, but where do we persist these ID's? I decided to use the site collections RootWeb properties bag. It could have been a WebApplication scoped feature but I wanted the option to activate and deavtivate per Site Collection so the Site Collection's RootWeb propertybag will do.

public static string GetGoogleAnalyticsSiteIdByUrlZone(SPUrlZone urlZone) {
    string retVal = string.Empty;
    string propertyKey = CreateZoneDependendKey(googleAnalyticsSiteIdPropertyKey, urlZone);
    if (RootWeb.Properties.ContainsKey(propertyKey)) {
        retVal = RootWeb.Properties[propertyKey];
    }
    return retVal;
}

public static void SetGoogleAnalyticsSiteIdByUrlZone(string value, SPUrlZone urlZone) {
    string retVal = string.Empty;
    string propertyKey = CreateZoneDependendKey(googleAnalyticsSiteIdPropertyKey, urlZone);

    if (GetGoogleAnalyticsSiteIdByUrlZone(urlZone) != value) {
        if (RootWeb.Properties.ContainsKey(propertyKey)) {
            RootWeb.Properties[propertyKey] = value;
        }
        else {
            RootWeb.Properties.Add(propertyKey, value);
        }
        RootWeb.Properties.Update();
    }
}

Nice UI but where do we inject the code?

The created UI does nothing but giving the user an interface where he or she can enter site id's per url zone. It doesn't inject the script into each and every page yet. We need something to inject some script into each and every page. For frequent readers of my blog this should sound very familiar. We used this technique for jQuery and the 'StayHere' feature as well. We simply add a usercontrol and an element in our feature that attaches the user control to the AdditionalPagehead delegate control. The user control looks something like this:

<%@ Assembly Name="SharePointSolutionPack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a7cd02bdf107f7a" %>
<%@ Control Language="C#" ClassName="GoogleAnalyticsControl" %>
<%@ Import Namespace="Motion10.SharePoint2007" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e) {
        if (!Page.IsPostBack)
        {
            if (string.IsNullOrEmpty(SolutionPackUtility.GoogleAnalyticsSiteId))
            {
                GoogleAnalyticsMultiView.SetActiveView(GoogleAnalyticsErrorView);
            }

            this.DataBind();
        }
    }
</script>
<asp:MultiView ID="GoogleAnalyticsMultiView" runat="server" ActiveViewIndex="0">
    <asp:View ID="GoogleAnalyticsCodeView" runat="server">
<script type="text/javascript">
    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
    try {
        var pageTracker = _gat._getTracker("<%# SolutionPackUtility.GoogleAnalyticsSiteId %>");
        pageTracker._trackPageview();
    } catch (err) { }
</script>
    </asp:View>
    <asp:View ID="GoogleAnalyticsErrorView" runat="server">
<!--The Google Analytics Feature on this site is enabled but the Google Analytics Site Id has not been set for this URL Zone.
    You can find this setting under:Site Settings - Site Collection Administration - Google Analytics Feature Settings.-->
    </asp:View>
</asp:MultiView>

Conclusion

With some straightforward code we were able to add Google Analytics to our site collection features. It's a little bit of work that truly pays off. A lot of customers nowadays want more than just basic site analytics.

Cheers

Wes

Comments

Links (1/15/2009) « Steve Pietrek - Everything SharePoint said:

Pingback from  Links (1/15/2009) &laquo; Steve Pietrek - Everything SharePoint

# January 15, 2009 8:37 PM

Raquel said:

Might you have instructions on how to package and deploy this?

Much appreciated.

# January 20, 2009 5:28 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)