summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/website/data/website_data.xml126
-rw-r--r--addons/website/models/res_config_settings.py1
-rw-r--r--addons/website/models/website.py21
-rw-r--r--addons/website/static/src/js/backend/res_config_settings.js41
-rw-r--r--addons/website/static/src/js/editor/snippets.options.js58
-rw-r--r--addons/website/static/src/snippets/s_popup/000.js8
-rw-r--r--addons/website/static/src/snippets/s_popup/000.scss3
-rw-r--r--addons/website/static/src/snippets/s_popup/options.js5
-rw-r--r--addons/website/static/src/xml/website.cookies_bar.xml75
-rw-r--r--addons/website/static/src/xml/website.res_config_settings.xml22
-rw-r--r--addons/website/views/res_config_settings_views.xml13
-rw-r--r--addons/website/views/snippets/s_popup.xml15
-rw-r--r--addons/website/views/snippets/snippets.xml9
-rw-r--r--addons/website/views/website_templates.xml25
14 files changed, 407 insertions, 15 deletions
diff --git a/addons/website/data/website_data.xml b/addons/website/data/website_data.xml
index 2304a8b3a7e..d24e2683fde 100644
--- a/addons/website/data/website_data.xml
+++ b/addons/website/data/website_data.xml
@@ -107,6 +107,132 @@
</record>
</data>
+ <template id="cookie_policy" name="Cookie Policy">
+ <t t-call="website.layout">
+ <div id="wrap" class="oe_structure">
+ <section class="pt8 pb8">
+ <div class="container">
+ <h2 class="pt16">Cookie Policy</h2>
+ <p>
+ 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.
+ <br/>
+ 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.
+ </p>
+ <p>
+ Here is an overview of the cookies that may be stored on your device when you visit our website:
+ </p>
+ <div class="table-responsive">
+ <table class="small table table-bordered text-center">
+ <thead class="thead-light">
+ <tr>
+ <th scope="col" style="width: 20%">Category of Cookie</th>
+ <th scope="col" style="width: 50%; min-width: 200px;">Purpose</th>
+ <th scope="col" style="width: 30%">Examples</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <p>Session &amp; Security</p>
+ </td>
+ <td>
+ <p>
+ 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.
+ </p>
+ <p>The website will not work properly if you reject or discard those cookies.</p>
+ </td>
+ <td>
+ session_id (Odoo)<br/>
+ fileToken (Odoo)
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p>Preferences</p>
+ </td>
+ <td>
+ <p>Remember information about the preferred look or behavior of the website, such as your preferred language or region.</p>
+ <p>Your experience may be degraded if you discard those cookies, but the website will still work.</p>
+ </td>
+ <td>
+ frontend_lang (Odoo)
+ </td>
+ </tr>
+ <tr>
+ <td>Interaction History</td>
+ <td>
+ <p>
+ 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.
+ </p>
+ <p>We may not be able to provide the best service to you if you reject those cookies, but the website will work.</p>
+ </td>
+ <td>
+ im_livechat_previous_operator_pid (Odoo)<br/>
+ utm_campaign (Odoo)<br/>
+ utm_source (Odoo)<br/>
+ utm_medium (Odoo)
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p>Advertising &amp; Marketing</p>
+ </td>
+ <td>
+ <p>
+ 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.
+ </p>
+ <p>Note that some third-party services may install additional cookies on your browser in order to identify you.</p>
+ <p>
+ You may opt-out of a third-party's use of cookies by visiting the <a href="https://optout.networkadvertising.org/?c=1" rel="nofollow">Network Advertising Initiative opt-out page</a>.
+ The website will still work if you reject or discard those cookies.
+ </p>
+ </td>
+ <td>
+ __gads (Google)<br/>
+ __gac (Google)
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <p>Analytics</p>
+ </td>
+ <td>
+ <p>
+ Understand how visitors engage with our website, via Google Analytics.
+ Learn more about <a href="https://developers.google.com/analytics/resources/concepts/gaConceptsCookies?hl=en">Analytics cookies and privacy information.</a>
+ </p>
+ <p>The website will still work if you reject or discard those cookies.</p>
+ </td>
+ <td>
+ _ga (Google)<br/>
+ _gat (Google)<br/>
+ _gid (Google)<br/>
+ _gac_* (Google)
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <p>
+ 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.
+ </p>
+ <p>We do not currently support Do Not Track signals, as there is no industry standard for compliance.</p>
+ </div>
+ </section>
+ </div>
+ </t>
+ </template>
+
<data noupdate="1">
<record id="homepage_page" model="website.page">
<field name="is_published">True</field>
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 <p> 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates id="template" xml:space="preserve">
+ <t t-name="website.cookies_bar.text_title">
+ <div class="o_cookies_bar_text_title">
+ <p>Respecting your privacy is our priority.</p>
+ </div>
+ </t>
+ <t t-name="website.cookies_bar.text_primary">
+ <div class="o_cookies_bar_text_primary">
+ We use cookies to provide you a better user experience.
+ </div>
+ </t>
+ <t t-name="website.cookies_bar.text_secondary">
+ <div class="o_cookies_bar_text_secondary">
+ 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.
+ <br/>
+ By clicking on this banner, you give us permission to collect data.
+ </div>
+ </t>
+
+ <t t-name="website.cookies_bar.discrete">
+ <section class="pt4 pb4">
+ <div class="container text-600 d-flex align-items-center">
+ <div class="mb0">
+ <t t-call="website.cookies_bar.text_primary"/>
+ </div>
+ <a href="/cookie-policy" class="o_cookies_bar_text_policy btn btn-link btn-sm text-nowrap ml-auto">Cookie Policy</a>
+ <a href="#" role="button" class="js_close_popup o_cookies_bar_text_button btn btn-primary btn-sm text-nowrap">I agree</a>
+ </div>
+ </section>
+ </t>
+ <t t-name="website.cookies_bar.classic">
+ <section class="pt24 pb24">
+ <div class="container text-600 d-flex align-items-center">
+ <div>
+ <div class="h3 text-900">
+ <t t-call="website.cookies_bar.text_title"/>
+ </div>
+ <div class="small">
+ <t t-call="website.cookies_bar.text_primary"/>
+ </div>
+ <div class="small">
+ <t t-call="website.cookies_bar.text_secondary"/>
+ </div>
+ </div>
+ <div class="ml-auto">
+ <a href="#" role="button" class="js_close_popup o_cookies_bar_text_button btn btn-primary btn-block text-nowrap">I agree</a>
+ <a href="/cookie-policy" class="o_cookies_bar_text_policy btn btn-link btn-sm text-nowrap">Cookie Policy</a>
+ </div>
+ </div>
+ </section>
+ </t>
+ <t t-name="website.cookies_bar.popup">
+ <section class="p-5">
+ <div class="container text-800 text-center">
+ <img t-attf-src="/web/image/website/#{websiteId}/logo/250x250" class="img img-fluid" alt="Website Logo"/>
+ <br/><br/>
+ <div class="h3">
+ <t t-call="website.cookies_bar.text_title"/>
+ </div>
+ <br/>
+ <div class="mb0 text-left">
+ <t t-call="website.cookies_bar.text_primary"/>
+ </div>
+ <div class="text-left">
+ <t t-call="website.cookies_bar.text_secondary"/>
+ </div>
+ <br/>
+ <a href="#" role="button" class="js_close_popup o_cookies_bar_text_button btn btn-primary btn-lg">Cookie Policy</a>
+ <br/>
+ <a href="/cookie-policy" class="o_cookies_bar_text_policy btn btn-link">I agree</a>
+ </div>
+ </section>
+ </t>
+</templates>
diff --git a/addons/website/static/src/xml/website.res_config_settings.xml b/addons/website/static/src/xml/website.res_config_settings.xml
new file mode 100644
index 00000000000..6b6d48172bd
--- /dev/null
+++ b/addons/website/static/src/xml/website.res_config_settings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates id="template" xml:space="preserve">
+<t t-name="website.res_config_settings.cookies_modal_main">
+ <main role="alert">
+ <p>
+ <b>Cookie bars may significantly impair the experience</b> 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.
+ </p>
+ <p>
+ For session cookies, authentification and analytics*,
+ <b>you do not need to ask for the consent</b> (see e.g. Opinion
+ 04/2012 on Cookie Consent Exemption by the EU Art.29 WP).
+ </p>
+ <p>
+ * provided that your analytics is anonymized, which is not the
+ case by default with Google Analytics.
+ </p>
+ </main>
+</t>
+</templates>
diff --git a/addons/website/views/res_config_settings_views.xml b/addons/website/views/res_config_settings_views.xml
index 6a32d2b55a7..870eb8cd59e 100644
--- a/addons/website/views/res_config_settings_views.xml
+++ b/addons/website/views/res_config_settings_views.xml
@@ -127,7 +127,18 @@
</div>
</div>
</div>
-
+ <div class="col-12 col-lg-6 o_setting_box" id="website_cookies_bar_setting">
+ <div class="o_setting_left_pane">
+ <field name="website_cookies_bar" widget="website_cookiesbar_field"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="website_cookies_bar"/>
+ <span class="fa fa-lg fa-globe" title="Values set here are website-specific." groups="website.group_multi_website"/>
+ <div class="text-muted">
+ Display a customizable cookies bar on your website.
+ </div>
+ </div>
+ </div>
<div class="col-12 col-lg-6 o_setting_box" id="cdn_setting" title="A CDN helps you serve your website’s content with high availability and high performance to any visitor wherever they are located." groups="base.group_no_one">
<div class="o_setting_left_pane">
<field name="cdn_activated"/>
diff --git a/addons/website/views/snippets/s_popup.xml b/addons/website/views/snippets/s_popup.xml
index 3ca662e8ff6..ea0b13a1c14 100644
--- a/addons/website/views/snippets/s_popup.xml
+++ b/addons/website/views/snippets/s_popup.xml
@@ -4,10 +4,10 @@
<template id="s_popup" name="Popup">
<div class="s_popup o_snippet_invisible">
<div class='modal s_popup_main popup_backdrop s_popup_center o_one_page bg-black-25 '
- data-show-after="5000" data-display="afterDelay" data-consents-delay="7">
+ data-show-after="5000" data-display="afterDelay" data-consents-duration="7">
<div class='modal-dialog modal-dialog-centered s_popup_frame modal-md'>
<div class='oe_structure modal-content s_popup_content oe_img_bg oe_custom_bg' style="background-color: #FFFFFF">
- <div class='s_popup_close o_we_no_overlay o_not_editable'>&amp;times;</div>
+ <div class='js_close_popup s_popup_close o_we_no_overlay o_not_editable'>&amp;times;</div>
<section class="s_banner parallax pt96 pb96">
<span class="s_parallax_bg oe_img_bg oe_custom_bg" style="background-image: url('/web/image/website.s_banner_default_image');"></span>
<div class="container">
@@ -37,24 +37,27 @@
<div data-js="SnippetPopup" data-selector=".s_popup" data-target=".s_popup_main">
<we-select string="Position">
<we-button data-set-layout="modal" data-name="position_centered">Center</we-button>
- <we-button data-set-layout="fixed">Bottom</we-button>
+ <we-button data-set-layout="fixed" data-name="position_bottom">Bottom</we-button>
</we-select>
<we-select string="Size" data-apply-to=".s_popup_frame">
<we-button data-select-class="modal-sm">Small</we-button>
- <we-button data-select-class="modal-md">Medium</we-button>
+ <we-button data-select-class="modal-md" data-name="s_popup_size_md">Medium</we-button>
<we-button data-select-class="modal-lg">Large</we-button>
<we-button data-select-class="modal-xl">Big</we-button>
- <we-button data-select-class="s_popup_full">Full</we-button>
+ <we-button data-select-class="s_popup_full" data-name="s_popup_size_full">Full</we-button>
</we-select>
<we-colorpicker string="Backdrop Color" data-select-style="true"
data-css-property="background-color" data-color-prefix="bg-" data-dependencies="position_centered"/>
+ </div>
+ <div data-js="SnippetPopup" data-selector=".s_popup" data-exclude="#website_cookies_bar" data-target=".s_popup_main">
<we-colorpicker string="Text Color" data-select-style="true" data-css-property="color" data-color-prefix="text-"/>
<we-select string="Display" data-attribute-name="display" data-attribute-default-value="always">
<we-button data-select-data-attribute="afterDelay" data-name="show_delay">Delay</we-button>
<we-button data-select-data-attribute="mouseExit">On Exit</we-button>
</we-select>
<we-input string="Delay" data-select-data-attribute="" data-attribute-name="showAfter" data-unit="s" data-save-unit="ms" data-dependencies="show_delay"/>
- <we-input string="Period between consents" data-select-data-attribute="7days" data-attribute-name="consentsDelay" data-unit="days"/>
+ <t t-set="unit_popup_duration">days</t>
+ <we-input string="Hide For" title="Once the user closes the popup, it won't be shown again for that period of time." t-attf-data-select-data-attribute="7#{unit_popup_duration}" data-attribute-name="consentsDuration" t-att-data-unit="unit_popup_duration"/>
<we-select string="Show on" data-no-preview="true">
<we-button data-move-block="moveToBody">This page</we-button>
<we-button data-move-block="moveToFooter">All pages</we-button>
diff --git a/addons/website/views/snippets/snippets.xml b/addons/website/views/snippets/snippets.xml
index 7838a0da9e5..3ef28a99e45 100644
--- a/addons/website/views/snippets/snippets.xml
+++ b/addons/website/views/snippets/snippets.xml
@@ -467,5 +467,14 @@
data-name="scroll_button_opt"/>
</we-row>
</div>
+
+ <!-- Cookies Bar -->
+ <div data-selector="#website_cookies_bar" data-js="CookiesBar" data-target=".s_popup_main">
+ <we-select string="Layout" class="o_we_inline">
+ <we-button data-select-class="o_cookies_discrete" data-select-layout="discrete" data-trigger="position_bottom,s_popup_size_full">Discrete</we-button>
+ <we-button data-select-class="o_cookies_classic" data-select-layout="classic" data-trigger="position_bottom,s_popup_size_full">Classic</we-button>
+ <we-button data-select-class="o_cookies_popup" data-select-layout="popup" data-trigger="position_centered,s_popup_size_md">Popup</we-button>
+ </we-select>
+ </div>
</template>
</odoo>
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index b40c459e081..dd6092435dc 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -271,6 +271,31 @@
</xpath>
</template>
+<template id="cookies_bar" inherit_id="website.footer_custom" name="Cookies Bar">
+ <xpath expr="//div[@id='footer']" position="before">
+ <div id="website_cookies_bar" t-if="website.cookies_bar" class="s_popup o_snippet_invisible" data-name="Cookies Bar">
+ <div class='s_popup_main s_popup_fixed popup_backdrop d-none o_cookies_discrete'
+ data-show-after="500" data-display="afterDelay" data-consents-duration="999">
+ <div class='s_popup_frame s_popup_full'>
+ <div class='oe_structure s_popup_content bg-white'>
+ <section class="pt4 pb4">
+ <div class="container text-600 d-flex align-items-center">
+ <div class="mb0">
+ <div class="o_cookies_bar_text_primary">
+ We use cookies to provide you a better user experience.
+ </div>
+ </div>
+ <a href="/cookie-policy" class="o_cookies_bar_text_policy btn btn-link btn-sm text-nowrap ml-auto">Cookie Policy</a>
+ <a href="#" role="button" class="js_close_popup o_cookies_bar_text_button btn btn-primary btn-sm text-nowrap">I agree</a>
+ </div>
+ </section>
+ </div>
+ </div>
+ </div>
+ </div>
+ </xpath>
+</template>
+
<template id="language_selector" name="Language Selector">
<ul class="js_language_selector mb0 list-inline" t-if="(request and request.is_frontend_multilang and len(languages) &gt; 1) or (website and (editable or translatable))">
<li class="list-inline-item">