[FIX] website: remove Google Analytics dashboard (deprecated)
==== Short version ==== Google is deprecating Universal Analytics in July 2023 and Google Sign-In in March 2023. Google Analytics Embed API is based on Sign-In, meaning it won't work anymore. It actually already doesn't work anymore for accounts created somewhere after mid-2020 apparently. There is no plan for now for Google to allow Analytics 4 dashboard to be embed in external website. We therefore can't do anything to keep the Google Analytics dashboard in Odoo. In previous stable version, it was kept but is displaying a warning about it (as until mid 2023 old accounts can still embed it). All this is about the embed dashboard, not the tracking in itself for which Odoo is already adapted in Odoo 15.0 for Analytics 4. ==== Detailed version (following short version, read it first) ==== - Universal Analytics EOL July 2023, see [1]. - It will be replaced by Analytics 4 for which Odoo is already ready and actually using it since version 15.0 with [2]. - Google Sign-In EOL March 2023, see [3]. Analytics Embed API was based on it, it won't work anymore. - There is no plan (for now) for Google to allow Analytics 4 to be able to be embed in external websites. They seem to just have dropped the "feature". This was confirmed by Google here [4] and indirectly here [5] in the DOC: `Note: This API does not support Google Analytics 4 (GA4) properties` - While the EOL is planed for 2023, the dashboard integration is already not working anymore for new accounts. - Old projects/keys/accounts can still embed their analytics dashboard. The threshold seems to be somewhere mid-2020, according to [6]. It seems to be accurate as my own key from 2018 still works, while my keys from 2021 do not. ==== Fix ==== - In stable, warn user about it in their Odoo Analytics dashboard (this PR) and also add a warning about that on the doc. - In master, simply drop the whole google analytics dashboard integration and remove the doc about it, see [7]. ==== Useful links ==== [1]: https://support.google.com/analytics/answer/11583528?hl=en [2]: https://github.com/odoo/odoo/commit/78bc86cbeccfc5df16218aee2b0d7c501e5c05b5 [3]: https://developers.googleblog.com/2022/03/gis-jsweb-authz-migration.html [4]: https://issuetracker.google.com/issues/233738709?pli=1 [5]: https://developers.google.com/analytics/devguides/reporting/embed/v1 [6]: https://support.google.com/analytics/answer/11583832 [7]: https://www.odoo.com/documentation/15.0/applications/websites/website/optimize/google_analytics_dashboard.html Finally, note that it means that from July 2023 to Octobre 2023, while Odoo 14.0 is still supported, Google Analytics won't work anymore in that version as it will still be designed for Universal Analytics and not Analytics 4. opw-2710910 opw-2855405 opw-2881515 opw-2892370 task-2790245 task-2820890 closes odoo/odoo#96280 X-original-commit: d065595f77790fb5ab9480f6de5b88549352324b Related: odoo/enterprise#29666 Related: odoo/upgrade#3698 Related: odoo/documentation#2499 Signed-off-by: Romain Derie (rde) <rde@odoo.com>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
||||
class WebsiteBackend(http.Controller):
|
||||
@@ -19,9 +18,7 @@ class WebsiteBackend(http.Controller):
|
||||
'website_designer': has_group_designer
|
||||
},
|
||||
'currency': request.env.company.currency_id.id,
|
||||
'dashboards': {
|
||||
'visits': {},
|
||||
}
|
||||
'dashboards': {}
|
||||
}
|
||||
|
||||
current_website = website_id and Website.browse(website_id) or Website.get_current_website()
|
||||
@@ -33,40 +30,9 @@ class WebsiteBackend(http.Controller):
|
||||
website['selected'] = True
|
||||
|
||||
if has_group_designer:
|
||||
if current_website.google_management_client_id and current_website.google_analytics_key:
|
||||
dashboard_data['dashboards']['visits'] = dict(
|
||||
ga_client_id=current_website.google_management_client_id or '',
|
||||
ga_analytics_key=current_website.google_analytics_key or '',
|
||||
)
|
||||
dashboard_data['dashboards']['plausible_share_url'] = current_website._get_plausible_share_url()
|
||||
return dashboard_data
|
||||
|
||||
@http.route('/website/iframefallback', type="http", auth='user', website=True)
|
||||
def get_iframe_fallback(self):
|
||||
return request.render('website.iframefallback')
|
||||
|
||||
@http.route('/website/dashboard/set_ga_data', type='json', auth='user')
|
||||
def website_set_ga_data(self, website_id, ga_client_id, ga_analytics_key):
|
||||
if not request.env.user.has_group('base.group_system'):
|
||||
return {
|
||||
'error': {
|
||||
'title': _('Access Error'),
|
||||
'message': _('You do not have sufficient rights to perform that action.'),
|
||||
}
|
||||
}
|
||||
if not ga_analytics_key or not ga_client_id.endswith('.apps.googleusercontent.com'):
|
||||
return {
|
||||
'error': {
|
||||
'title': _('Incorrect Client ID / Key'),
|
||||
'message': _('The Google Analytics Client ID or Key you entered seems incorrect.'),
|
||||
}
|
||||
}
|
||||
Website = request.env['website']
|
||||
current_website = website_id and Website.browse(website_id) or Website.get_current_website()
|
||||
|
||||
request.env['res.config.settings'].create({
|
||||
'google_management_client_id': ga_client_id,
|
||||
'google_analytics_key': ga_analytics_key,
|
||||
'website_id': current_website.id,
|
||||
}).execute()
|
||||
return True
|
||||
|
||||
@@ -64,14 +64,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
'Google Analytics Key',
|
||||
related='website_id.google_analytics_key',
|
||||
readonly=False)
|
||||
google_management_client_id = fields.Char(
|
||||
'Google Client ID',
|
||||
related='website_id.google_management_client_id',
|
||||
readonly=False)
|
||||
google_management_client_secret = fields.Char(
|
||||
'Google Client Secret',
|
||||
related='website_id.google_management_client_secret',
|
||||
readonly=False)
|
||||
google_search_console = fields.Char(
|
||||
'Google Search Console',
|
||||
related='website_id.google_search_console',
|
||||
@@ -115,10 +107,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
"Google Analytics",
|
||||
compute='_compute_has_google_analytics',
|
||||
inverse='_inverse_has_google_analytics')
|
||||
has_google_analytics_dashboard = fields.Boolean(
|
||||
"Google Analytics Dashboard",
|
||||
compute='_compute_has_google_analytics_dashboard',
|
||||
inverse='_inverse_has_google_analytics_dashboard')
|
||||
has_google_search_console = fields.Boolean(
|
||||
"Console Google Search",
|
||||
compute='_compute_has_google_search_console',
|
||||
@@ -187,21 +175,8 @@ class ResConfigSettings(models.TransientModel):
|
||||
for config in self:
|
||||
if config.has_google_analytics:
|
||||
continue
|
||||
config.has_google_analytics_dashboard = False
|
||||
config.google_analytics_key = False
|
||||
|
||||
@api.depends('website_id')
|
||||
def _compute_has_google_analytics_dashboard(self):
|
||||
for config in self:
|
||||
config.has_google_analytics_dashboard = bool(config.google_management_client_id)
|
||||
|
||||
def _inverse_has_google_analytics_dashboard(self):
|
||||
for config in self:
|
||||
if config.has_google_analytics_dashboard:
|
||||
continue
|
||||
config.google_management_client_id = False
|
||||
config.google_management_client_secret = False
|
||||
|
||||
@api.depends('website_id')
|
||||
def _compute_has_google_search_console(self):
|
||||
for config in self:
|
||||
|
||||
@@ -108,8 +108,6 @@ class Website(models.Model):
|
||||
has_social_default_image = fields.Boolean(compute='_compute_has_social_default_image', store=True)
|
||||
|
||||
google_analytics_key = fields.Char('Google Analytics Key')
|
||||
google_management_client_id = fields.Char('Google Client ID')
|
||||
google_management_client_secret = fields.Char('Google Client Secret')
|
||||
google_search_console = fields.Char(help='Google key, or Enable to access first reply')
|
||||
|
||||
google_maps_api_key = fields.Char('Google Maps API Key')
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
/* global google, gapi */
|
||||
odoo.define('website.backend.dashboard', function (require) {
|
||||
'use strict';
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var field_utils = require('web.field_utils');
|
||||
var pyUtils = require('web.py_utils');
|
||||
var session = require('web.session');
|
||||
var time = require('web.time');
|
||||
var web_client = require('web.web_client');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var COLORS = ["#1f77b4", "#aec7e8"];
|
||||
@@ -37,7 +34,6 @@ var Dashboard = AbstractAction.extend({
|
||||
'/web/static/lib/Chart/Chart.js',
|
||||
],
|
||||
events: {
|
||||
'click .js_link_analytics_settings': 'on_link_analytics_settings',
|
||||
'click .o_dashboard_action': 'on_dashboard_action',
|
||||
'click .o_dashboard_action_form': 'on_dashboard_action_form',
|
||||
},
|
||||
@@ -105,59 +101,12 @@ var Dashboard = AbstractAction.extend({
|
||||
return prom;
|
||||
},
|
||||
|
||||
on_link_analytics_settings: function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var self = this;
|
||||
var dialog = new Dialog(this, {
|
||||
size: 'medium',
|
||||
title: _t('Connect Google Analytics'),
|
||||
$content: QWeb.render('website.ga_dialog_content', {
|
||||
ga_key: this.dashboards_data.visits.ga_client_id,
|
||||
ga_analytics_key: this.dashboards_data.visits.ga_analytics_key,
|
||||
}),
|
||||
buttons: [
|
||||
{
|
||||
text: _t("Save"),
|
||||
classes: 'btn-primary',
|
||||
close: true,
|
||||
click: function() {
|
||||
var ga_client_id = dialog.$el.find('input[name="ga_client_id"]').val();
|
||||
var ga_analytics_key = dialog.$el.find('input[name="ga_analytics_key"]').val();
|
||||
self.on_save_ga_client_id(ga_client_id, ga_analytics_key);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: _t("Cancel"),
|
||||
close: true,
|
||||
},
|
||||
],
|
||||
}).open();
|
||||
},
|
||||
|
||||
on_go_to_website: function (ev) {
|
||||
ev.preventDefault();
|
||||
var website = _.findWhere(this.websites, {selected: true});
|
||||
window.location.replace(`/web#action=website.website_preview&website_id=${website.id}`);
|
||||
},
|
||||
|
||||
on_save_ga_client_id: function(ga_client_id, ga_analytics_key) {
|
||||
var self = this;
|
||||
return this._rpc({
|
||||
route: '/website/dashboard/set_ga_data',
|
||||
params: {
|
||||
'website_id': self.website_id,
|
||||
'ga_client_id': ga_client_id,
|
||||
'ga_analytics_key': ga_analytics_key,
|
||||
},
|
||||
}).then(function (result) {
|
||||
if (result.error) {
|
||||
self.displayNotification({ title: result.error.title, message: result.error.message, type: 'danger' });
|
||||
return;
|
||||
}
|
||||
self.on_date_range_button('week');
|
||||
});
|
||||
},
|
||||
|
||||
render_dashboards: function() {
|
||||
var self = this;
|
||||
@@ -268,46 +217,9 @@ var Dashboard = AbstractAction.extend({
|
||||
self.render_graph('.o_graph_' + e.name, self.dashboards_data[e.name].graph, chart_id);
|
||||
}
|
||||
});
|
||||
this.render_graph_analytics(this.dashboards_data.visits.ga_client_id);
|
||||
}
|
||||
},
|
||||
|
||||
render_graph_analytics: function(client_id) {
|
||||
if (!this.dashboards_data.visits || !this.dashboards_data.visits.ga_client_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.load_analytics_api();
|
||||
|
||||
var $analytics_components = this.$('.js_analytics_components');
|
||||
this.addLoader($analytics_components);
|
||||
|
||||
var self = this;
|
||||
gapi.analytics.ready(function() {
|
||||
|
||||
$analytics_components.empty();
|
||||
// 1. Authorize component
|
||||
var $analytics_auth = $('<div>').addClass('col-lg-12');
|
||||
window.onOriginError = function () {
|
||||
$analytics_components.find('.js_unauthorized_message').remove();
|
||||
self.display_unauthorized_message($analytics_components, 'not_initialized');
|
||||
};
|
||||
gapi.analytics.auth.authorize({
|
||||
container: $analytics_auth[0],
|
||||
clientid: client_id
|
||||
});
|
||||
|
||||
$analytics_auth.appendTo($analytics_components);
|
||||
|
||||
self.handle_analytics_auth($analytics_components);
|
||||
gapi.analytics.auth.on('signIn', function() {
|
||||
delete window.onOriginError;
|
||||
self.handle_analytics_auth($analytics_components);
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
on_date_range_button: function(date_range) {
|
||||
if (date_range === 'week') {
|
||||
this.date_range = 'week';
|
||||
@@ -417,277 +329,7 @@ var Dashboard = AbstractAction.extend({
|
||||
this.controlPanelProps.cp_content = { $searchview, $buttons };
|
||||
},
|
||||
|
||||
// Loads Analytics API
|
||||
load_analytics_api: function() {
|
||||
var self = this;
|
||||
if (!("gapi" in window)) {
|
||||
(function(w,d,s,g,js,fjs){
|
||||
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb);}};
|
||||
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
|
||||
js.src='https://apis.google.com/js/platform.js';
|
||||
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics');};
|
||||
}(window,document,'script'));
|
||||
gapi.analytics.ready(function() {
|
||||
self.analytics_create_components();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handle_analytics_auth: function($analytics_components) {
|
||||
$analytics_components.find('.js_unauthorized_message').remove();
|
||||
|
||||
// Check if the user is authenticated and has the right to make API calls
|
||||
if (!gapi.analytics.auth.getAuthResponse()) {
|
||||
this.display_unauthorized_message($analytics_components, 'not_connected');
|
||||
} else if (gapi.analytics.auth.getAuthResponse() && gapi.analytics.auth.getAuthResponse().scope.indexOf('https://www.googleapis.com/auth/analytics') === -1) {
|
||||
this.display_unauthorized_message($analytics_components, 'no_right');
|
||||
} else {
|
||||
this.make_analytics_calls($analytics_components);
|
||||
}
|
||||
},
|
||||
|
||||
display_unauthorized_message: function($analytics_components, reason) {
|
||||
$analytics_components.prepend($(QWeb.render('website.unauthorized_analytics', {reason: reason})));
|
||||
},
|
||||
|
||||
make_analytics_calls: function($analytics_components) {
|
||||
// 2. ActiveUsers component
|
||||
var $analytics_users = $('<div>');
|
||||
var activeUsers = new gapi.analytics.ext.ActiveUsers({
|
||||
container: $analytics_users[0],
|
||||
pollingInterval: 10,
|
||||
});
|
||||
$analytics_users.appendTo($analytics_components);
|
||||
|
||||
// 3. View Selector
|
||||
var $analytics_view_selector = $('<div>').addClass('col-lg-12 o_properties_selection');
|
||||
var viewSelector = new gapi.analytics.ViewSelector({
|
||||
container: $analytics_view_selector[0],
|
||||
});
|
||||
viewSelector.execute();
|
||||
$analytics_view_selector.appendTo($analytics_components);
|
||||
|
||||
// 4. Chart graph
|
||||
var start_date = '7daysAgo';
|
||||
if (this.date_range === 'month') {
|
||||
start_date = '30daysAgo';
|
||||
} else if (this.date_range === 'year') {
|
||||
start_date = '365daysAgo';
|
||||
}
|
||||
var $analytics_chart_2 = $('<div>').addClass('col-lg-6 col-12');
|
||||
var breakdownChart = new gapi.analytics.googleCharts.DataChart({
|
||||
query: {
|
||||
'dimensions': 'ga:date',
|
||||
'metrics': 'ga:sessions',
|
||||
'start-date': start_date,
|
||||
'end-date': 'yesterday'
|
||||
},
|
||||
chart: {
|
||||
type: 'LINE',
|
||||
container: $analytics_chart_2[0],
|
||||
options: {
|
||||
title: 'All',
|
||||
width: '100%',
|
||||
tooltip: {isHtml: true},
|
||||
}
|
||||
}
|
||||
});
|
||||
$analytics_chart_2.appendTo($analytics_components);
|
||||
|
||||
// 5. Chart table
|
||||
var $analytics_chart_1 = $('<div>').addClass('col-lg-6 col-12');
|
||||
var mainChart = new gapi.analytics.googleCharts.DataChart({
|
||||
query: {
|
||||
'dimensions': 'ga:medium',
|
||||
'metrics': 'ga:sessions',
|
||||
'sort': '-ga:sessions',
|
||||
'max-results': '6'
|
||||
},
|
||||
chart: {
|
||||
type: 'TABLE',
|
||||
container: $analytics_chart_1[0],
|
||||
options: {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
});
|
||||
$analytics_chart_1.appendTo($analytics_components);
|
||||
|
||||
// Events handling & animations
|
||||
|
||||
var table_row_listener;
|
||||
|
||||
viewSelector.on('change', function(ids) {
|
||||
var options = {query: {ids: ids}};
|
||||
activeUsers.set({ids: ids}).execute();
|
||||
mainChart.set(options).execute();
|
||||
breakdownChart.set(options).execute();
|
||||
|
||||
if (table_row_listener) { google.visualization.events.removeListener(table_row_listener); }
|
||||
});
|
||||
|
||||
mainChart.on('success', function(response) {
|
||||
var chart = response.chart;
|
||||
var dataTable = response.dataTable;
|
||||
|
||||
table_row_listener = google.visualization.events.addListener(chart, 'select', function() {
|
||||
var options;
|
||||
if (chart.getSelection().length) {
|
||||
var row = chart.getSelection()[0].row;
|
||||
var medium = dataTable.getValue(row, 0);
|
||||
options = {
|
||||
query: {
|
||||
filters: 'ga:medium==' + medium,
|
||||
},
|
||||
chart: {
|
||||
options: {
|
||||
title: medium,
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
options = {
|
||||
chart: {
|
||||
options: {
|
||||
title: 'All',
|
||||
}
|
||||
}
|
||||
};
|
||||
delete breakdownChart.get().query.filters;
|
||||
}
|
||||
breakdownChart.set(options).execute();
|
||||
});
|
||||
});
|
||||
|
||||
// Add CSS animation to visually show the when users come and go.
|
||||
activeUsers.once('success', function() {
|
||||
var element = this.container.firstChild;
|
||||
var timeout;
|
||||
|
||||
this.on('change', function(data) {
|
||||
element = this.container.firstChild;
|
||||
var animationClass = data.delta > 0 ? 'is-increasing' : 'is-decreasing';
|
||||
element.className += (' ' + animationClass);
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
element.className = element.className.replace(/ is-(increasing|decreasing)/g, '');
|
||||
}, 3000);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Credits to https://github.com/googleanalytics/ga-dev-tools
|
||||
* This is the Active Users component that polls
|
||||
* the number of active users on Analytics each 5 secs
|
||||
*/
|
||||
analytics_create_components: function() {
|
||||
|
||||
gapi.analytics.createComponent('ActiveUsers', {
|
||||
|
||||
initialize: function() {
|
||||
this.activeUsers = 0;
|
||||
gapi.analytics.auth.once('signOut', this.handleSignOut_.bind(this));
|
||||
},
|
||||
|
||||
execute: function() {
|
||||
// Stop any polling currently going on.
|
||||
if (this.polling_) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
this.render_();
|
||||
|
||||
// Wait until the user is authorized.
|
||||
if (gapi.analytics.auth.isAuthorized()) {
|
||||
this.pollActiveUsers_();
|
||||
} else {
|
||||
gapi.analytics.auth.once('signIn', this.pollActiveUsers_.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
clearTimeout(this.timeout_);
|
||||
this.polling_ = false;
|
||||
this.emit('stop', {activeUsers: this.activeUsers});
|
||||
},
|
||||
|
||||
render_: function() {
|
||||
var opts = this.get();
|
||||
|
||||
// Render the component inside the container.
|
||||
this.container = typeof opts.container === 'string' ?
|
||||
document.getElementById(opts.container) : opts.container;
|
||||
|
||||
this.container.innerHTML = opts.template || this.template;
|
||||
this.container.querySelector('b').innerHTML = this.activeUsers;
|
||||
},
|
||||
|
||||
pollActiveUsers_: function() {
|
||||
var options = this.get();
|
||||
var pollingInterval = (options.pollingInterval || 5) * 1000;
|
||||
|
||||
if (isNaN(pollingInterval) || pollingInterval < 5000) {
|
||||
throw new Error('Frequency must be 5 seconds or more.');
|
||||
}
|
||||
|
||||
this.polling_ = true;
|
||||
gapi.client.analytics.data.realtime
|
||||
.get({ids:options.ids, metrics:'rt:activeUsers'})
|
||||
.then(function(response) {
|
||||
var result = response.result;
|
||||
var newValue = result.totalResults ? +result.rows[0][0] : 0;
|
||||
var oldValue = this.activeUsers;
|
||||
|
||||
this.emit('success', {activeUsers: this.activeUsers});
|
||||
|
||||
if (newValue !== oldValue) {
|
||||
this.activeUsers = newValue;
|
||||
this.onChange_(newValue - oldValue);
|
||||
}
|
||||
|
||||
if (this.polling_) {
|
||||
this.timeout_ = setTimeout(this.pollActiveUsers_.bind(this), pollingInterval);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
onChange_: function(delta) {
|
||||
var valueContainer = this.container.querySelector('b');
|
||||
if (valueContainer) { valueContainer.innerHTML = this.activeUsers; }
|
||||
|
||||
this.emit('change', {activeUsers: this.activeUsers, delta: delta});
|
||||
if (delta > 0) {
|
||||
this.emit('increase', {activeUsers: this.activeUsers, delta: delta});
|
||||
} else {
|
||||
this.emit('decrease', {activeUsers: this.activeUsers, delta: delta});
|
||||
}
|
||||
},
|
||||
|
||||
handleSignOut_: function() {
|
||||
this.stop();
|
||||
gapi.analytics.auth.once('signIn', this.handleSignIn_.bind(this));
|
||||
},
|
||||
|
||||
handleSignIn_: function() {
|
||||
this.pollActiveUsers_();
|
||||
gapi.analytics.auth.once('signOut', this.handleSignOut_.bind(this));
|
||||
},
|
||||
|
||||
template:
|
||||
'<div class="ActiveUsers">' +
|
||||
'Active Users: <b class="ActiveUsers-value"></b>' +
|
||||
'</div>'
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
// Utility functions
|
||||
addLoader: function(selector) {
|
||||
var loader = '<span class="fa fa-3x fa-spin fa-circle-o-notch fa-spin"/>';
|
||||
selector.html("<div class='o_loader'>" + loader + "</div>");
|
||||
},
|
||||
getValue: function(d) { return d[1]; },
|
||||
format_number: function(value, type, digits, symbol) {
|
||||
if (type === 'currency') {
|
||||
|
||||
@@ -132,10 +132,6 @@
|
||||
|
||||
.o_buttons {
|
||||
position: relative;
|
||||
|
||||
> button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.o_layer {
|
||||
@@ -147,61 +143,6 @@
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.o_properties_selection {
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.gapi-analytics-auth-styles-signinbutton {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ActiveUsers {
|
||||
position: relative;
|
||||
float: right;
|
||||
right: 10px;
|
||||
border: 1px solid map-get($grays, '200');
|
||||
font-weight: 300;
|
||||
white-space: nowrap;
|
||||
padding: .5em 1.5em;
|
||||
margin: .5em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ActiveUsers-value {
|
||||
font-weight: 300;
|
||||
margin-right: -.25em;
|
||||
}
|
||||
|
||||
.ActiveUsers.is-increasing {
|
||||
animation: increase 3s;
|
||||
}
|
||||
|
||||
.ActiveUsers.is-decreasing {
|
||||
animation: decrease 3s;
|
||||
}
|
||||
|
||||
@keyframes increase {
|
||||
10% {
|
||||
background-color: lighten($o-brand-primary, 30%);
|
||||
border-color: $o-brand-primary;
|
||||
color: $o-brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes decrease {
|
||||
10% {
|
||||
background-color: lighten($o-brand-primary, 30%);
|
||||
border-color: $o-brand-primary;
|
||||
color: $o-brand-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr.o_product_template {
|
||||
|
||||
@@ -30,64 +30,34 @@
|
||||
<t t-name="website.dashboard_content">
|
||||
<div class="o_website_dashboard_content">
|
||||
<t t-call="website.plausible_content"/>
|
||||
<t t-call="website.google_analytics_content"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website.google_analytics_content">
|
||||
|
||||
<t t-name="website.plausible_content">
|
||||
<div class="row o_dashboard_visits" t-if="widget.groups.website_designer">
|
||||
<div class="col-12 o_box">
|
||||
<h2>Visits</h2>
|
||||
<div t-if="widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id">
|
||||
<div class="row js_analytics_components"/>
|
||||
<a href="#" class="js_link_analytics_settings">Edit my Analytics Client ID</a>
|
||||
<h2>Analytics</h2>
|
||||
<div t-if="widget.dashboards_data.plausible_share_url" class="row">
|
||||
<div class="ratio ratio-1x1">
|
||||
<iframe t-att-src='widget.dashboards_data.plausible_share_url' class="embed-responsive-item" frameborder="0" loading="lazy"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="!(widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id)" class="col-lg-12">
|
||||
<div t-else="" class="col-lg-12">
|
||||
<div class="o_demo_background">
|
||||
<div class="o_layer">
|
||||
</div>
|
||||
<div class="o_buttons text-center">
|
||||
<h3>There is no data currently available.</h3>
|
||||
<button class="btn btn-primary js_link_analytics_settings d-block mx-auto mb8">Connect Google Analytics</button>
|
||||
<h3>Easily track your visitor with Plausible</h3>
|
||||
<a class="btn btn-primary" target="_blank"
|
||||
href="https://www.odoo.com/documentation/master/applications/websites/website/optimize/plausible.html">
|
||||
Connect Plausible
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website.plausible_content">
|
||||
<div class="row" t-if="widget.groups.website_designer && widget.dashboards_data.plausible_share_url">
|
||||
<div class="col-12 o_box">
|
||||
<h2>Analytics</h2>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="ratio ratio-1x1">
|
||||
<iframe t-att-src='widget.dashboards_data.plausible_share_url' class="embed-responsive-item" frameborder="0" loading="lazy"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-name="website.unauthorized_analytics" class="col-12 js_unauthorized_message mb16">
|
||||
<span t-if="reason === 'not_connected'">You need to log in to your Google Account before: </span>
|
||||
<span t-if="reason === 'no_right'">You do not seem to have access to this Analytics Account.</span>
|
||||
<span t-if="reason === 'not_initialized'">Google Analytics initialization failed. Maybe this domain is not whitelisted in your Google Analytics project for this client ID.</span>
|
||||
</div>
|
||||
|
||||
<div t-name="website.ga_dialog_content">
|
||||
Your Measurement ID: <input type="text" name="ga_analytics_key" placeholder="G-XXXXXXXXXX" t-att-value="ga_analytics_key" style="width: 100%"></input>
|
||||
<a href="https://support.google.com/analytics/answer/9304153" target="_blank">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
How to get my Measurement ID
|
||||
</a>
|
||||
<br/><br/>
|
||||
Your Client ID: <input type="text" name="ga_client_id" t-att-value="ga_key" style="width: 100%"></input>
|
||||
<a href="https://www.odoo.com/documentation/master/applications/websites/website/optimize/google_analytics_dashboard.html" target="_blank">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
How to get my Client ID
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<t t-name="website.DateRangeButtons">
|
||||
<!-- TODO: Hide in mobile as it is going to push in control panel and it breaks UI, maybe we will improve it in future -->
|
||||
|
||||
@@ -31,7 +31,6 @@ QUnit.module('Website Backend Dashboard', {
|
||||
if (route === '/website/fetch_dashboard_data') {
|
||||
return Promise.resolve({
|
||||
dashboards: {
|
||||
visits: {},
|
||||
sales: { summary: {} },
|
||||
},
|
||||
groups: { system: true, website_designer: true },
|
||||
|
||||
@@ -263,34 +263,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="google_analytics_dashboard_setting" attrs="{'invisible': [('has_google_analytics', '=', False)]}">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="has_google_analytics_dashboard"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="has_google_analytics_dashboard"/>
|
||||
<div class="text-muted">
|
||||
Follow your website traffic in Odoo.
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('has_google_analytics_dashboard', '=', False)]}">
|
||||
<div class="row mt16">
|
||||
<label class="col-lg-3 o_light_label" string="Client ID" for="google_management_client_id"/>
|
||||
<field name="google_management_client_id" attrs="{'required': [('has_google_analytics_dashboard', '=', True)]}"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-lg-3 o_light_label" string="Client Secret" for="google_management_client_secret"/>
|
||||
<field name="google_management_client_secret" attrs="{'required': [('has_google_analytics_dashboard', '=', True)]}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('has_google_analytics_dashboard', '=', False)]}">
|
||||
<a href="https://www.odoo.com/documentation/master/applications/websites/website/optimize/google_analytics_dashboard.html"
|
||||
class="oe_link" target="_blank">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
How to get my Client ID
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-offset-6 col-lg-6 o_setting_box" id="plausbile_setting">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="has_plausible_shared_key"/>
|
||||
|
||||
Reference in New Issue
Block a user