diff --git a/README.md b/README.md index 33742e5..cb2676c 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,9 @@ Bigger improvements: 1. follow the usual [module installation process](http://doc.silverstripe.org/modules#installation) 2. activate the logger by adding the following to the end of your mysite/_config.php: `GoogleLogger::activate('UA-XXXXX-Y');` (hardcode google code, useful in combination with _ss_environment.php) or `GoogleLogger::activate('SiteConfig');` (use SiteConfig for setup) 3. activate the analyzer by adding the following to the end of your mysite/_config.php: `GoogleAnalyzer::activate('1234567', "my@google.login", "mypassword");` (hardcode credentials, useful in combination with _ss_environment.php) or `GoogleAnalyzer::activate('SiteConfig');` (use SiteConfig for setup) -4. run dev/build (http://www.mysite.com/dev/build?flush=all) -5. if you're using SiteConfig populate your siteconfig in the CMS. +4. If you wish to active the event tracking helper, include `GoogleLogger::set_event_tracking_enabled(true)` +5. run dev/build (http://www.mysite.com/dev/build?flush=all) +6. if you're using SiteConfig populate your siteconfig in the CMS. ## Retrieving your credentials from GA diff --git a/code/GoogleAnalyzer.php b/code/GoogleAnalyzer.php index 5c5dd3b..882f757 100644 --- a/code/GoogleAnalyzer.php +++ b/code/GoogleAnalyzer.php @@ -1,5 +1,8 @@ addFieldToTab('Root', new Tab('GoogleAnalytics', 'Google Analytics')); - $fields->addFieldToTab("Root.GoogleAnalytics", new TabSet('Stats')); $fields->addFieldToTab('Root.GoogleAnalytics.Stats', new Tab('Performance', 'Performance')); $fields->addFieldToTab("Root.GoogleAnalytics.Stats.Performance", new GooglePerformanceChart($this->owner)); diff --git a/code/GoogleConfig.php b/code/GoogleConfig.php index e07c826..e384bd7 100644 --- a/code/GoogleConfig.php +++ b/code/GoogleConfig.php @@ -1,5 +1,8 @@ 'Varchar', ); - public function updateCMSFields(FieldSet $fields) { + public function updateCMSFields(FieldList $fields) { $fields->addFieldToTab("Root", new Tab('GoogleAnalytics')); $fields->addFieldToTab('Root.GoogleAnalytics', new TextField('GoogleAnalyticsCode', 'Google Analytics Code (UA-XXXXXX-X)')); diff --git a/code/GoogleLogger.php b/code/GoogleLogger.php index 22bc2ea..eb8cbdf 100644 --- a/code/GoogleLogger.php +++ b/code/GoogleLogger.php @@ -5,6 +5,11 @@ class GoogleLogger extends Extension { // the Google Analytics code to be used in the JS snippet or public static $google_analytics_code; + /** + * @var bool + */ + public static $include_event_tracking = false; + // supported web crawlers, keys for nice names and values for signature regexes public static $web_crawlers = array( 'Google' => 'googlebot', @@ -58,5 +63,25 @@ public function onAfterInit() { } } } + + // include event tracking api if required, jQuery 1.5 is required for automatic data attributes + if(self::event_tracking_enabled()) { + Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); + Requirements::javascript('googleanalytics/javascript/googleanalytics.event.tracking.js'); + } + } + + /** + * @param bool + */ + public static function set_event_tracking_enabled($bool) { + self::$include_event_tracking = (bool) $bool; + } + + /** + * @return bool + */ + public static function event_tracking_enabled() { + return self::$include_event_tracking; } } \ No newline at end of file diff --git a/docs/_manifest_exclude b/docs/_manifest_exclude new file mode 100644 index 0000000..e69de29 diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..c81dd0e --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,85 @@ +# Google Analytics Module + +## Features + +### Event Tracking API + +Included in the module is a simple jQuery plugin which wraps Google Analytics Event +based tracking and provides several shortcuts to including event tracking in your +project. + +To enable event tracking, first you will need to enable the Google Logger then include +`GoogleLogger::set_event_tracking_enabled(true)` in your mysite/_config.php file: + + GoogleLogger::activate('UA-XXXX-XX'); + GoogleLogger::set_event_tracking_enabled(true); + +After including the event tracking API you can call `analyticsTrackEvent(1)` from your +own code whenever you need to track an interaction event. + +#### analyticsTrackEvent(1) + +`analyticsTrackEvent` takes an object with a number of properties. You can define +which ever properties you need to, otherwise the plugin will revert to some common +default values. Each of the provided properties in the object can either be a literal +value or a function which returns a literal value. This allows you to build complex +event labels and categories based on the entire DOM of a page and not just limited to +a single event object. + + $(this).analyticsTrackEvent({ + category: .. + label: .. + action: ... + value: .. + }) + + +Property | Default Value | Optional? +------------- | --------------- | ----- +Category | window.location | No +Action | 'click' | No +Label | $(this).text() | No +Value | NULL | Yes + + +#### Examples + +Tracks any link with a class of *track*. Takes the default options of `analyticsTrackEvent(1)` +as shown in the table above + + $("a.track").click(function() { + $(this).analyticsTrackEvent(); + }); + +Tracks users focusing into a given text box. Sends a custom label and action label. + + $("input.track").focus(function() { + $(this).trackEvent({ + label: 'Entered Text into box', + action: 'focus' + }); + }); + +Tracks when a user leaves an input box, what was the value that they entered into +the box. Demonstrates how you can use a function to return a dynamic value for any +of the properties. + + $("input.track").blur(function() { + $(this).trackEvent({ + category: function(elem) { return $(elem).parents("form").attr("name"); }, + label: function(elem) { return $(elem).val(); }, + action: 'leave text field' + }) + }); + +## FAQ + +### I'm not seeing any event information + +Google takes up to 48 hours to process event tracking information in many cases, +so you may have to wait several hours to see your results. + +If you still don't see any information appearing, try using the official +`ga_debug.js` extension to Google Chrome. + +http://code.google.com/apis/analytics/docs/tracking/gaTrackingTroubleshooting.html#gaDebug \ No newline at end of file diff --git a/javascript/googleanalytics.event.tracking.js b/javascript/googleanalytics.event.tracking.js new file mode 100644 index 0000000..c25c490 --- /dev/null +++ b/javascript/googleanalytics.event.tracking.js @@ -0,0 +1,64 @@ +;(function($) { + /** + * A simple helper function for wrapping Google Analytic event tracking + * into sites through jQuery and HTML5 data attributes. + * + * See the provided documentation folder for instructions and examples + * on how to add event tracking. + */ + $.fn.analyticsTrackEvent = function(args) { + _gaq = _gaq || []; + + /** + * Evaluates an argument. If a non terminal (i.e a function) return + * the result of that function. Otherwise returns the terminal value. + */ + var e = function(f) { + if($.isFunction(f)) return f(self); return f; + }; + + var category, action, label, value, self = $(this[0]); + + var args = $.extend({ + category: function(ele) { + category = $(ele).data('category'); + if(!category) category = window.location.toString(); + + return category; + }, + action: function(ele) { + action = $(ele).data('action'); + if(!action) action = 'click'; + + return action; + }, + label: function(ele) { + // optional but recommened field + label = $(ele).data('label'); + if(!label) label = $(ele).text(); + + return label; + }, + value: function(ele) { + return $(ele).data('value'); + }, + }, args); + + + var track = ['_trackEvent', e(args.category), e(args.action)]; + + label = e(args.label); + value = parseInt(e(args.value)); + + if(label) track.push(label); + if(value) track.push(value); + + try { + _gaq.push(track); + } catch(err) { + if(window.console != undefined) { + window.console.log(err); + } + } + } +})(jQuery); \ No newline at end of file diff --git a/thirdparty/excanvas/excanvas.js b/thirdparty/excanvas/excanvas.js index 8217105..a34ca1d 100644 --- a/thirdparty/excanvas/excanvas.js +++ b/thirdparty/excanvas/excanvas.js @@ -1,35 +1,35 @@ -// Copyright 2006 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_|| -b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d','","");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("g.x)g.x=e.x;if(h.y==null||e.yg.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_), -z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l')}else a.push('');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("')}a.push("");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(), -this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a, -0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager= -M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})(); +// Copyright 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_|| +b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d','","");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("g.x)g.x=e.x;if(h.y==null||e.yg.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_), +z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l')}else a.push('');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("')}a.push("");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(), +this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a, +0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager= +M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})();