Skip to content

Commit

Permalink
[IMP] website: introduce cookies bar on website
Browse files Browse the repository at this point in the history
With this commit, it is now possible to activate a cookies bar on a website
through the res.settings.

The cookies bar can be edited in the frontend by entering edit mode (to be
editable, it will be shown even if the user has hidden the cookies bar).

The cookies bar will appear until the user clicks on 'I agree'.

task-2087003
  • Loading branch information
rdeodoo committed Jan 20, 2020
1 parent 6f2df3f commit 1fe9dcd
Show file tree
Hide file tree
Showing 14 changed files with 402 additions and 15 deletions.
128 changes: 128 additions & 0 deletions addons/website/data/website_data.xml
Expand Up @@ -115,6 +115,134 @@
</record>
</data>

<template id="cookie_policy" name="Cookie Policy">
<t t-call="website.layout">
<div id="wrap">
<div 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>
</div>
</t>
</template>

<data noupdate="1">
<record id="homepage_page" model="website.page">
<field name="is_published">True</field>
Expand Down
1 change: 1 addition & 0 deletions addons/website/models/res_config_settings.py
Expand Up @@ -28,6 +28,7 @@ def _default_website(self):
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)
Expand Down
21 changes: 21 additions & 0 deletions addons/website/models/website.py
Expand Up @@ -59,6 +59,7 @@ def _default_language(self):
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
Expand Down Expand Up @@ -168,6 +169,26 @@ def write(self, values):
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
Expand Down
41 changes: 39 additions & 2 deletions 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({

Expand Down Expand Up @@ -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);
});
54 changes: 54 additions & 0 deletions 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');
Expand Down Expand Up @@ -1026,6 +1027,59 @@ 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.allChildNodes === undefined) {
this.allChildNodes = [];
}

for (const selector of selectorsToKeep) {
const $el = $content.find(selector);
if ($el.length && $el[0].childNodes.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.allChildNodes[selector] = $el[0].childNodes;
}
$template.find(selector).html(this.allChildNodes[selector]);
}

$content.html($template);
},
});

/**
* Allows edition of 'cover_properties' in website models which have such
* fields (blogs, posts, events, ...).
Expand Down
8 changes: 4 additions & 4 deletions addons/website/static/src/snippets/s_popup/000.js
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
},
});
Expand Down
3 changes: 2 additions & 1 deletion 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
Expand Down
2 changes: 1 addition & 1 deletion addons/website/static/src/snippets/s_popup/options.js
Expand Up @@ -8,7 +8,7 @@ options.registry.SnippetPopup = options.Class.extend({
* @override
*/
start: function () {
this.$target.find('.s_popup_close').on('click', () => {
this.$target.find('.js_close_popup').on('click', () => {
this.onTargetHide();
this.trigger_up('snippet_option_visibility_update', {show: false});
});
Expand Down

0 comments on commit 1fe9dcd

Please sign in to comment.