+ Cookies are small bits of text sent by our servers to your computer or device when you access our services.
+ They are stored in your browser and later sent back to our servers so that we can provide contextual content.
+ Without cookies, using the web would be a much more frustrating experience.
+ We use them to support your activities on our website. For example, your session (so you don't have to login again) or your shopping cart.
+
+ Cookies are also used to help us understand your preferences based on previous or current activity on our website (the pages you have
+ visited), your language and country, which enables us to provide you with improved services.
+ We also use cookies to help us compile aggregate data about site traffic and site interaction so that we can offer
+ better site experiences and tools in the future.
+
+
+ Here is an overview of the cookies that may be stored on your device when you visit our website:
+
+
+
+
+
+
Category of Cookie
+
Purpose
+
Examples
+
+
+
+
+
+
Session & Security
+
+
+
+ Authenticate users, protect user data and allow the website to deliver the services users expects,
+ such as maintaining the content of their cart, or allowing file uploads.
+
+
The website will not work properly if you reject or discard those cookies.
+
+
+ session_id (Odoo)
+ fileToken (Odoo)
+
+
+
+
+
Preferences
+
+
+
Remember information about the preferred look or behavior of the website, such as your preferred language or region.
+
Your experience may be degraded if you discard those cookies, but the website will still work.
+
+
+ frontend_lang (Odoo)
+
+
+
+
Interaction History
+
+
+ Users to collect information about your interactions with the website, the pages you've seen,
+ and any specific marketing campaign that brought you to the website.
+
+
We may not be able to provide the best service to you if you reject those cookies, but the website will work.
+ Used to make advertising more engaging to users and more valuable to publishers and advertisers,
+ such as providing more relevant ads when you visit other websites that display ads or to improve reporting on ad campaign performance.
+
+
Note that some third-party services may install additional cookies on your browser in order to identify you.
+
+ You may opt-out of a third-party's use of cookies by visiting the Network Advertising Initiative opt-out page.
+ The website will still work if you reject or discard those cookies.
+
+ You can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies.
+ Each browser is a little different, so look at your browser's Help menu to learn the correct way to modify your cookies.
+
+
We do not currently support Do Not Track signals, as there is no industry standard for compliance.
+
+
+
+
+
+
True
diff --git a/addons/website/models/res_config_settings.py b/addons/website/models/res_config_settings.py
index 85449fd23c2..054ec2d019c 100644
--- a/addons/website/models/res_config_settings.py
+++ b/addons/website/models/res_config_settings.py
@@ -28,6 +28,7 @@ class ResConfigSettings(models.TransientModel):
website_default_lang_code = fields.Char('Default language code', related='website_id.default_lang_id.code', readonly=False)
specific_user_account = fields.Boolean(related='website_id.specific_user_account', readonly=False,
help='Are newly created user accounts website specific')
+ website_cookies_bar = fields.Boolean(related='website_id.cookies_bar', readonly=False)
google_analytics_key = fields.Char('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)
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index f9a6a9f4c77..6de22c8f241 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -59,6 +59,7 @@ class Website(models.Model):
language_ids = fields.Many2many('res.lang', 'website_lang_rel', 'website_id', 'lang_id', 'Languages', default=_active_languages)
default_lang_id = fields.Many2one('res.lang', string="Default Language", default=_default_language, required=True)
auto_redirect_lang = fields.Boolean('Autoredirect Language', default=True, help="Should users be redirected to their browser's language")
+ cookies_bar = fields.Boolean('Cookies Bar', help="Display a customizable cookies bar on your website.")
def _default_social_facebook(self):
return self.env.ref('base.main_company').social_facebook
@@ -168,6 +169,26 @@ class Website(models.Model):
if 'cdn_activated' in values or 'cdn_url' in values or 'cdn_filters' in values:
# invalidate the caches from static node at compile time
self.env['ir.qweb'].clear_caches()
+
+ if 'cookies_bar' in values:
+ if values['cookies_bar']:
+ cookies_view = self.env.ref('website.cookie_policy', raise_if_not_found=False)
+ if cookies_view:
+ cookies_view.with_context(website_id=self.id).write({'website_id': self.id})
+ specific_cook_view = self.with_context(website_id=self.id).viewref('website.cookie_policy')
+ self.env['website.page'].create({
+ 'is_published': True,
+ 'website_indexed': False,
+ 'url': '/cookie-policy',
+ 'website_id': self.id,
+ 'view_id': specific_cook_view.id,
+ })
+ else:
+ self.env['website.page'].search([
+ ('website_id', '=', self.id),
+ ('url', '=', '/cookie-policy'),
+ ]).unlink()
+
return result
@api.model
diff --git a/addons/website/static/src/js/backend/res_config_settings.js b/addons/website/static/src/js/backend/res_config_settings.js
index 47a52579447..ad9373d26f1 100644
--- a/addons/website/static/src/js/backend/res_config_settings.js
+++ b/addons/website/static/src/js/backend/res_config_settings.js
@@ -1,7 +1,14 @@
odoo.define('website.settings', function (require) {
-var BaseSettingController = require('base.settings').Controller;
-var FormController = require('web.FormController');
+const BaseSettingController = require('base.settings').Controller;
+const core = require('web.core');
+const Dialog = require('web.Dialog');
+const FieldBoolean = require('web.basic_fields').FieldBoolean;
+const fieldRegistry = require('web.field_registry');
+const FormController = require('web.FormController');
+
+const QWeb = core.qweb;
+const _t = core._t;
BaseSettingController.include({
@@ -33,4 +40,34 @@ BaseSettingController.include({
},
});
+const WebsiteCookiesbarField = FieldBoolean.extend({
+ xmlDependencies: ['/website/static/src/xml/website.res_config_settings.xml'],
+
+ _onChange: function () {
+ const checked = this.$input[0].checked;
+ if (!checked) {
+ return this._setValue(checked);
+ }
+
+ const cancelCallback = () => this.$input[0].checked = !checked;
+ Dialog.confirm(this, null, {
+ title: _t("Please confirm"),
+ $content: QWeb.render('website.res_config_settings.cookies_modal_main'),
+ buttons: [{
+ text: 'Do not activate',
+ classes: 'btn-primary',
+ close: true,
+ click: cancelCallback,
+ },
+ {
+ text: 'Activate anyway',
+ close: true,
+ click: () => this._setValue(checked),
+ }],
+ cancel_callback: cancelCallback,
+ });
+ },
+});
+
+fieldRegistry.add('website_cookiesbar_field', WebsiteCookiesbarField);
});
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index 9765bc57473..0ef07ccc12e 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -1,6 +1,7 @@
odoo.define('website.editor.snippets.options', function (require) {
'use strict';
+require('website.s_popup_options');
var core = require('web.core');
var Dialog = require('web.Dialog');
const wUtils = require('website.utils');
@@ -913,6 +914,63 @@ options.registry.anchor = options.Class.extend({
},
});
+options.registry.CookiesBar = options.registry.SnippetPopup.extend({
+ xmlDependencies: (options.registry.SnippetPopup.prototype.xmlDependencies || []).concat(
+ ['/website/static/src/xml/website.cookies_bar.xml']
+ ),
+
+ //--------------------------------------------------------------------------
+ // Options
+ //--------------------------------------------------------------------------
+
+ /**
+ * Change the cookies bar layout.
+ *
+ * @see this.selectClass for parameters
+ */
+ selectLayout: function (previewMode, widgetValue, params) {
+ let websiteId;
+ this.trigger_up('context_get', {
+ callback: function (ctx) {
+ websiteId = ctx['website_id'];
+ },
+ });
+
+ const $template = $(qweb.render(`website.cookies_bar.${widgetValue}`, {
+ websiteId: websiteId,
+ }));
+
+ const $content = this.$target.find('.s_popup_content');
+ const selectorsToKeep = [
+ '.o_cookies_bar_text_button',
+ '.o_cookies_bar_text_policy',
+ '.o_cookies_bar_text_title',
+ '.o_cookies_bar_text_primary',
+ '.o_cookies_bar_text_secondary',
+ ];
+
+ if (this.$savedSelectors === undefined) {
+ this.$savedSelectors = [];
+ }
+
+ for (const selector of selectorsToKeep) {
+ const $currentLayoutEls = $content.find(selector).contents();
+ const $newLayoutEl = $template.find(selector);
+ if ($currentLayoutEls.length) {
+ // save value before change, eg 'title' is not inside 'discrete' template
+ // but we want to preserve it in case of select another layout later
+ this.$savedSelectors[selector] = $currentLayoutEls;
+ }
+ const $savedSelector = this.$savedSelectors[selector];
+ if ($newLayoutEl.length && $savedSelector && $savedSelector.length) {
+ $newLayoutEl.empty().append($savedSelector);
+ }
+ }
+
+ $content.empty().append($template);
+ },
+});
+
/**
* Allows edition of 'cover_properties' in website models which have such
* fields (blogs, posts, events, ...).
diff --git a/addons/website/static/src/snippets/s_popup/000.js b/addons/website/static/src/snippets/s_popup/000.js
index 0e7983002f0..16820dcc988 100644
--- a/addons/website/static/src/snippets/s_popup/000.js
+++ b/addons/website/static/src/snippets/s_popup/000.js
@@ -8,14 +8,14 @@ const utils = require('web.utils');
const PopupWidget = publicWidget.Widget.extend({
selector: '.s_popup',
events: {
- 'click .s_popup_close': '_onCloseClick',
+ 'click .js_close_popup': '_onCloseClick',
},
/**
* @override
*/
start: function () {
- if (!utils.get_cookie(this.$target.attr('id'))) {
+ if (!utils.get_cookie(this.$el.attr('id'))) {
this._bindPopup();
}
return this._super(...arguments);
@@ -77,8 +77,8 @@ const PopupWidget = publicWidget.Widget.extend({
* @private
*/
_onCloseClick: function () {
- const nbDays = this.$target.find('.s_popup_main').data('consentsDelay');
- utils.set_cookie(this.$target.attr('id'), true, nbDays * 24 * 60 * 60);
+ const nbDays = this.$el.find('.s_popup_main').data('consentsDuration');
+ utils.set_cookie(this.$el.attr('id'), true, nbDays * 24 * 60 * 60);
this._hidePopup();
},
});
diff --git a/addons/website/static/src/snippets/s_popup/000.scss b/addons/website/static/src/snippets/s_popup/000.scss
index bb14c100322..71566512868 100644
--- a/addons/website/static/src/snippets/s_popup/000.scss
+++ b/addons/website/static/src/snippets/s_popup/000.scss
@@ -1,7 +1,8 @@
// s_popup
.s_popup_main {
.s_popup_content {
- min-height: var(--o-we-toolbar-height);
+ // keep lower than
height (cookies bar)
+ min-height: $o-font-size-base * $o-line-height-base;
box-shadow: $modal-content-box-shadow-sm-up;
.container {
// keep margin when fixed bottom
diff --git a/addons/website/static/src/snippets/s_popup/options.js b/addons/website/static/src/snippets/s_popup/options.js
index 4c43041c614..842cc284c83 100644
--- a/addons/website/static/src/snippets/s_popup/options.js
+++ b/addons/website/static/src/snippets/s_popup/options.js
@@ -8,7 +8,10 @@ options.registry.SnippetPopup = options.Class.extend({
* @override
*/
start: function () {
- this.$target.find('.s_popup_close').on('click', () => {
+ // Note: the link are excluded here so that internal modal buttons do
+ // not close the popup as we want to allow edition of those buttons.
+ this.$target.find('.js_close_popup:not(a, .btn)').on('click', ev => {
+ ev.stopPropagation();
this.onTargetHide();
this.trigger_up('snippet_option_visibility_update', {show: false});
});
diff --git a/addons/website/static/src/xml/website.cookies_bar.xml b/addons/website/static/src/xml/website.cookies_bar.xml
new file mode 100644
index 00000000000..8f43ccb3698
--- /dev/null
+++ b/addons/website/static/src/xml/website.cookies_bar.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
Respecting your privacy is our priority.
+
+
+
+
+ We use cookies to provide you a better user experience.
+
+
+
+
+ We use them to store info about your habits on our website. It will helps us to provide you the very best experience and customize what you see.
+
+ By clicking on this banner, you give us permission to collect data.
+
+ Cookie bars may significantly impair the experience of
+ your visitors. We recommend you avoid them unless you have
+ verified with a legal advisor that you absolutely need cookie
+ consent in your country.
+
+
+ For session cookies, authentification and analytics*,
+ you do not need to ask for the consent (see e.g. Opinion
+ 04/2012 on Cookie Consent Exemption by the EU Art.29 WP).
+
+
+ * provided that your analytics is anonymized, which is not the
+ case by default with Google Analytics.
+