summaryrefslogtreecommitdiffstats
path: root/extensions/GoogleAnalytics
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/GoogleAnalytics')
-rw-r--r--extensions/GoogleAnalytics/Config.pm16
-rw-r--r--extensions/GoogleAnalytics/Extension.pm23
-rw-r--r--extensions/GoogleAnalytics/lib/Config.pm41
-rw-r--r--extensions/GoogleAnalytics/template/en/default/admin/params/googleanalytics.html.tmpl20
-rw-r--r--extensions/GoogleAnalytics/template/en/default/hook/global/header-additional_header.html.tmpl24
-rw-r--r--extensions/GoogleAnalytics/template/en/default/hook/global/header-start.html.tmpl14
-rw-r--r--extensions/GoogleAnalytics/web/js/analytics.js21
-rw-r--r--extensions/GoogleAnalytics/web/js/dnt-helper.js54
8 files changed, 213 insertions, 0 deletions
diff --git a/extensions/GoogleAnalytics/Config.pm b/extensions/GoogleAnalytics/Config.pm
new file mode 100644
index 000000000..f4699db37
--- /dev/null
+++ b/extensions/GoogleAnalytics/Config.pm
@@ -0,0 +1,16 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::GoogleAnalytics;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use constant NAME => 'GoogleAnalytics';
+
+__PACKAGE__->NAME;
diff --git a/extensions/GoogleAnalytics/Extension.pm b/extensions/GoogleAnalytics/Extension.pm
new file mode 100644
index 000000000..e9b144da4
--- /dev/null
+++ b/extensions/GoogleAnalytics/Extension.pm
@@ -0,0 +1,23 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::GoogleAnalytics;
+
+use 5.10.1;
+use strict;
+use warnings;
+use parent qw(Bugzilla::Extension);
+
+our $VERSION = '0.1';
+
+sub config_add_panels {
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{GoogleAnalytics} = "Bugzilla::Extension::GoogleAnalytics::Config";
+}
+
+__PACKAGE__->NAME;
diff --git a/extensions/GoogleAnalytics/lib/Config.pm b/extensions/GoogleAnalytics/lib/Config.pm
new file mode 100644
index 000000000..f9e003ce0
--- /dev/null
+++ b/extensions/GoogleAnalytics/lib/Config.pm
@@ -0,0 +1,41 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::GoogleAnalytics::Config;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use Bugzilla::Config::Common;
+
+sub get_param_list {
+ my ($class) = @_;
+
+ my @params = (
+ {
+ name => 'google_analytics_tracking_id',
+ type => 't',
+ default => '',
+ checker => sub {
+ my ($tracking_id) = (@_);
+
+ return 'must be like UA-XXXXXX-X' unless $tracking_id =~ m{^(UA-[[:xdigit:]]+-[[:xdigit:]]+)?$};
+ return '';
+ }
+ },
+ {
+ name => 'google_analytics_debug',
+ type => 'b',
+ default => 0
+ },
+ );
+
+ return @params;
+}
+
+1;
diff --git a/extensions/GoogleAnalytics/template/en/default/admin/params/googleanalytics.html.tmpl b/extensions/GoogleAnalytics/template/en/default/admin/params/googleanalytics.html.tmpl
new file mode 100644
index 000000000..3fdce57e6
--- /dev/null
+++ b/extensions/GoogleAnalytics/template/en/default/admin/params/googleanalytics.html.tmpl
@@ -0,0 +1,20 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ title = "Google Analytics"
+ desc = "Configure Google Analytics"
+%]
+
+[%
+ param_descs = {
+ google_analytics_tracking_id => "Google Analytics Tracking ID",
+ google_analytics_debug => "If this option is set, the debug version of the analytics.js " _
+ "library will be used.",
+ }
+%]
diff --git a/extensions/GoogleAnalytics/template/en/default/hook/global/header-additional_header.html.tmpl b/extensions/GoogleAnalytics/template/en/default/hook/global/header-additional_header.html.tmpl
new file mode 100644
index 000000000..fc0ebc480
--- /dev/null
+++ b/extensions/GoogleAnalytics/template/en/default/hook/global/header-additional_header.html.tmpl
@@ -0,0 +1,24 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%# Disable tracking of security group members as well as private bugs #%]
+[% RETURN IF !Bugzilla.params.google_analytics_tracking_id ||
+ user.in_group(Param('insidergroup')) || (bug.defined && bug.groups_in.size) %]
+
+<meta name="google-analytics" content="[% Bugzilla.params.google_analytics_tracking_id FILTER html %]" data-location="
+ [%~ urlbase FILTER html %][% template.name.replace('\.html\.tmpl$', '') FILTER html ~%]
+ " data-title="
+ [%~ IF template.name == 'pages/user_activity.html.tmpl' ~%]
+ User Activity Report
+ [%~ ELSIF template.name == 'pages/user_profile.html.tmpl' ~%]
+ User Profile
+ [%~ ELSE ~%]
+ [% title FILTER html | collapse %]
+ [%~ END ~%]">
+<script async src="https://www.google-analytics.com/analytics
+ [%~ "_debug" IF Bugzilla.params.google_analytics_debug ~%].js"></script>
diff --git a/extensions/GoogleAnalytics/template/en/default/hook/global/header-start.html.tmpl b/extensions/GoogleAnalytics/template/en/default/hook/global/header-start.html.tmpl
new file mode 100644
index 000000000..27a8587ef
--- /dev/null
+++ b/extensions/GoogleAnalytics/template/en/default/hook/global/header-start.html.tmpl
@@ -0,0 +1,14 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF !javascript_urls %]
+ [% javascript_urls = [] %]
+[% END %]
+
+[% javascript_urls.push('extensions/GoogleAnalytics/web/js/analytics.js') %]
+[% javascript_urls.push('extensions/GoogleAnalytics/web/js/dnt-helper.js') %]
diff --git a/extensions/GoogleAnalytics/web/js/analytics.js b/extensions/GoogleAnalytics/web/js/analytics.js
new file mode 100644
index 000000000..25f7d7527
--- /dev/null
+++ b/extensions/GoogleAnalytics/web/js/analytics.js
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+$(function() {
+ var meta = $('meta[name="google-analytics"]');
+
+ if (typeof Mozilla.dntEnabled === 'function' && !Mozilla.dntEnabled() && meta.length) {
+ // Activate Google Analytics
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', meta.attr('content'), 'auto');
+ ga('set', 'anonymizeIp', true);
+ ga('set', 'location', meta.data('location'));
+ ga('set', 'title', meta.data('title'));
+ // Track page view
+ ga('send', 'pageview');
+ }
+});
diff --git a/extensions/GoogleAnalytics/web/js/dnt-helper.js b/extensions/GoogleAnalytics/web/js/dnt-helper.js
new file mode 100644
index 000000000..828fdc7ea
--- /dev/null
+++ b/extensions/GoogleAnalytics/web/js/dnt-helper.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// create namespace
+if (typeof Mozilla === 'undefined') {
+ var Mozilla = {};
+}
+
+/**
+ * Returns true or false based on whether doNotTrack is enabled. It also takes into account the
+ * anomalies, such as !bugzilla 887703, which effect versions of Fx 31 and lower. It also handles
+ * IE versions on Windows 7, 8 and 8.1, where the DNT implementation does not honor the spec.
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1217896 for more details
+ * @params {string} [dnt] - An optional mock doNotTrack string to ease unit testing.
+ * @params {string} [ua] - An optional mock userAgent string to ease unit testing.
+ * @returns {boolean} true if enabled else false
+ */
+Mozilla.dntEnabled = function(dnt, ua) {
+ 'use strict';
+
+ // for old version of IE we need to use the msDoNotTrack property of navigator
+ // on newer versions, and newer platforms, this is doNotTrack but, on the window object
+ // Safari also exposes the property on the window object.
+ var dntStatus = dnt || navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
+ var userAgent = ua || navigator.userAgent;
+
+ // List of Windows versions known to not implement DNT according to the standard.
+ var anomalousWinVersions = ['Windows NT 6.1', 'Windows NT 6.2', 'Windows NT 6.3'];
+
+ var fxMatch = userAgent.match(/Firefox\/(\d+)/);
+ var ieRegEx = /MSIE|Trident/i;
+ var isIE = ieRegEx.test(userAgent);
+ // Matches from Windows up to the first occurance of ; un-greedily
+ // http://www.regexr.com/3c2el
+ var platform = userAgent.match(/Windows.+?(?=;)/g);
+
+ // With old versions of IE, DNT did not exist so we simply return false;
+ if (isIE && typeof Array.prototype.indexOf !== 'function') {
+ return false;
+ } else if (fxMatch && parseInt(fxMatch[1], 10) < 32) {
+ // Can't say for sure if it is 1 or 0, due to Fx bug 887703
+ dntStatus = 'Unspecified';
+ } else if (isIE && platform && anomalousWinVersions.indexOf(platform.toString()) !== -1) {
+ // default is on, which does not honor the specification
+ dntStatus = 'Unspecified';
+ } else {
+ // sets dntStatus to Disabled or Enabled based on the value returned by the browser.
+ // If dntStatus is undefined, it will be set to Unspecified
+ dntStatus = { '0': 'Disabled', '1': 'Enabled' }[dntStatus] || 'Unspecified';
+ }
+
+ return dntStatus === 'Enabled' ? true : false;
+};