]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1379607 - Reimplement Google Analytics on bugzilla.mozilla.org
authorKohei Yoshino <kohei.yoshino@gmail.com>
Tue, 28 Nov 2017 16:27:54 +0000 (11:27 -0500)
committerDylan William Hardison <dylan@hardison.net>
Tue, 28 Nov 2017 16:27:54 +0000 (11:27 -0500)
Bugzilla/CGI.pm
extensions/GoogleAnalytics/Config.pm [new file with mode: 0644]
extensions/GoogleAnalytics/Extension.pm [new file with mode: 0644]
extensions/GoogleAnalytics/lib/Config.pm [new file with mode: 0644]
extensions/GoogleAnalytics/template/en/default/admin/params/googleanalytics.html.tmpl [new file with mode: 0644]
extensions/GoogleAnalytics/template/en/default/hook/global/header-additional_header.html.tmpl [new file with mode: 0644]
extensions/GoogleAnalytics/template/en/default/hook/global/header-start.html.tmpl [new file with mode: 0644]
extensions/GoogleAnalytics/web/js/analytics.js [new file with mode: 0644]
extensions/GoogleAnalytics/web/js/dnt-helper.js [new file with mode: 0644]
index.cgi

index 248ee12cba11d4d2a8a6ded9a4dd92317856fad9..ea8d96da835e58cd89d646c8f30acb39596f3acd 100644 (file)
@@ -34,9 +34,9 @@ BEGIN {
 sub DEFAULT_CSP {
     my %policy = (
         default_src => [ 'self' ],
-        script_src  => [ 'self', 'unsafe-inline', 'unsafe-eval' ],
+        script_src  => [ 'self', 'unsafe-inline', 'unsafe-eval', 'https://www.google-analytics.com' ],
         child_src   => [ 'self', ],
-        img_src     => [ 'self', 'https://secure.gravatar.com' ],
+        img_src     => [ 'self', 'https://secure.gravatar.com', 'https://www.google-analytics.com' ],
         style_src   => [ 'self', 'unsafe-inline' ],
         object_src  => [ 'none' ],
         form_action => [
@@ -61,9 +61,9 @@ sub DEFAULT_CSP {
 sub SHOW_BUG_MODAL_CSP {
     my ($bug_id) = @_;
     my %policy = (
-        script_src  => ['self', 'nonce', 'unsafe-inline', 'unsafe-eval' ],
+        script_src  => ['self', 'nonce', 'unsafe-inline', 'unsafe-eval', 'https://www.google-analytics.com' ],
         object_src  => [correct_urlbase() . "extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf"],
-        img_src     => [ 'self', 'https://secure.gravatar.com' ],
+        img_src     => [ 'self', 'https://secure.gravatar.com', 'https://www.google-analytics.com' ],
         connect_src => [
             'self',
             # This is from extensions/OrangeFactor/web/js/orange_factor.js
diff --git a/extensions/GoogleAnalytics/Config.pm b/extensions/GoogleAnalytics/Config.pm
new file mode 100644 (file)
index 0000000..f4699db
--- /dev/null
@@ -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 (file)
index 0000000..e9b144d
--- /dev/null
@@ -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 (file)
index 0000000..f9e003c
--- /dev/null
@@ -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 (file)
index 0000000..3fdce57
--- /dev/null
@@ -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 (file)
index 0000000..fc0ebc4
--- /dev/null
@@ -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 (file)
index 0000000..27a8587
--- /dev/null
@@ -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 (file)
index 0000000..25f7d75
--- /dev/null
@@ -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 (file)
index 0000000..828fdc7
--- /dev/null
@@ -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;
+};
index d73ccd5d906da98b9ed34b90538f4f96a298811c..63865f9084775c04300987177c2c33dd4efa1fe4 100755 (executable)
--- a/index.cgi
+++ b/index.cgi
@@ -64,7 +64,7 @@ if ($can_cache && $if_none_match && any { $_ eq $weak_etag } split(/,\s*/, $if_n
 }
 else {
     my $template = Bugzilla->template;
-    $cgi->content_security_policy(script_src  => ['self']);
+    $cgi->content_security_policy(script_src  => ['self', 'https://www.google-analytics.com']);
 
     # Return the appropriate HTTP response headers.
     print $cgi->header(