[FIX] im_livechat: matching rule in cache

The problem was that the matching rule was evaluated before sending the html
page, and the script was inserted in the page according to the matching rule
(it wasn't inserted if the rule was 'hide_button'). However, the matching rule
wasn't re-evaluated if the page was already in the cache. The page content thus
might depend on a matching rule computed for someone else in that case.

Rather than inserting the script according to the matching rule, we insert it
only if there is an operator available. We then ask for the matching rule in
the livechat widget, by RPC, and we reject its willStart deferred if the rule
says so. We also re-check for operator availability in the same RPC.
This commit is contained in:
Aaron Bohy
2015-12-24 12:59:46 +01:00
parent 4be1a8cc7d
commit d8605b7993
4 changed files with 67 additions and 71 deletions
+29 -5
View File
@@ -29,12 +29,36 @@ class LivechatController(http.Controller):
def loader(self, channel_id, **kwargs):
username = kwargs.get("username", _("Visitor"))
channel = request.env['im_livechat.channel'].sudo().browse(channel_id)
info = request.env['im_livechat.channel'].with_context(from_external_page=True).match_rules(request, channel.id, username=username)
return request.render('im_livechat.loader', {'info': info, 'web_session_required': True}) if info else False
info = request.env['im_livechat.channel'].get_livechat_info(channel.id, username=username)
return request.render('im_livechat.loader', {'info': info, 'web_session_required': True}) if info['available'] else False
@http.route('/im_livechat/available', type='json', auth="public")
def available(self, channel_id):
return len(request.env['im_livechat.channel'].browse(channel_id).get_available_users())
@http.route('/im_livechat/init', type='json', auth="public")
def livechat_init(self, channel_id):
LivechatChannel = request.env['im_livechat.channel']
available = len(LivechatChannel.browse(channel_id).get_available_users())
rule = {}
if available:
# find the country from the request
country_id = False
country_code = request.session.geoip and request.session.geoip.get('country_code') or False
if country_code:
country_ids = self.env['res.country'].sudo().search([('code', '=', country_code)])
if country_ids:
country_id = country_ids[0].id
# extract url
url = request.httprequest.headers.get('Referer')
# find the first matching rule for the given country and url
matching_rule = request.env['im_livechat.channel.rule'].sudo().match_rule(channel_id, url, country_id)
if matching_rule:
rule = {
'action': matching_rule.action,
'auto_popup_timer': matching_rule.auto_popup_timer,
'regex_url': matching_rule.regex_url,
}
return {
'available_for_me': available and (not rule or rule['action'] != 'hide_button'),
'rule': rule,
}
@http.route('/im_livechat/get_session', type="json", auth='public')
def get_session(self, channel_id, anonymous_name, **kwargs):
@@ -193,36 +193,13 @@ class ImLivechatChannel(models.Model):
}
@api.model
def match_rules(self, request, channel_id, username='Visitor'):
info = {
'server_url': self.env['ir.config_parameter'].get_param('web.base.url'),
'options': self.sudo().get_channel_infos(channel_id),
}
info['options']["default_username"] = username
# find the country from the request
country_id = False
country_code = request.session.geoip and request.session.geoip.get('country_code') or False
if country_code:
country_ids = self.env['res.country'].sudo().search([('code', '=', country_code)])
if country_ids:
country_id = country_ids[0].id
# extract url
if self.env.context.get('from_external_page', False):
url = request.httprequest.headers.get('Referer')
else:
url = request.httprequest.base_url
# find the match rule for the given country and url
rule = self.env['im_livechat.channel.rule'].sudo().match_rule(channel_id, url, country_id)
if rule:
if rule.action == 'hide_button':
# don't return the initialization script, since its blocked (in the country)
return False
rule_data = {
'action': rule.action,
'auto_popup_timer': rule.auto_popup_timer,
'regex_url': rule.regex_url,
}
info['rule'] = rule and rule_data or False
def get_livechat_info(self, channel_id, username='Visitor'):
info = {}
info['available'] = self.browse(channel_id).get_available_users(),
if info['available']:
info['server_url'] = self.env['ir.config_parameter'].get_param('web.base.url')
info['options'] = self.sudo().get_channel_infos(channel_id)
info['options']["default_username"] = username
return info
+28 -32
View File
@@ -20,7 +20,7 @@ var LivechatButton = Widget.extend({
"click": "open_chat"
},
init: function (parent, server_url, options, rule) {
init: function (parent, server_url, options) {
this._super(parent);
this.options = _.defaults(options || {}, {
placeholder: _t('Ask something ...'),
@@ -28,7 +28,6 @@ var LivechatButton = Widget.extend({
button_text: _t("Chat with one of our collaborators"),
default_message: _t("How may I help you?"),
});
this.rule = rule || {};
this.channel = null;
this.chat_window = null;
this.messages = [];
@@ -39,44 +38,41 @@ var LivechatButton = Widget.extend({
var cookie = utils.get_cookie('im_livechat_session');
var ready;
if (!cookie) {
ready = session.rpc("/im_livechat/available", {channel_id: this.options.channel_id});
ready = session.rpc("/im_livechat/init", {channel_id: this.options.channel_id}).then(function (result) {
if (!result.available_for_me) {
return $.Deferred().reject();
}
self.rule = result.rule;
});
} else {
var channel = JSON.parse(cookie);
ready = $.when(true, session.rpc("/im_livechat/history", {channel_id: channel.id, limit: 100}));
ready = session.rpc("/im_livechat/history", {channel_id: channel.id, limit: 100}).then(function (history) {
self.history = history;
});
}
return ready.then(function (available, history) {
self.available = available;
self.history = history;
if (available) {
return self.load_qweb_template();
}
});
return ready.then(this.load_qweb_template.bind(this));
},
start: function () {
if (!this.available) {
this.do_hide();
} else {
this.$el.text(this.options.button_text);
if (this.history) {
_.each(this.history.reverse(), this.add_message.bind(this));
this.open_chat();
} else if (this.rule.action === 'auto_popup') {
var auto_popup_cookie = utils.get_cookie('im_livechat_auto_popup');
if (!auto_popup_cookie || JSON.parse(auto_popup_cookie)) {
this.auto_popup_timeout = setTimeout(this.open_chat.bind(this), this.rule.auto_popup_timer*1000);
this.$el.text(this.options.button_text);
if (this.history) {
_.each(this.history.reverse(), this.add_message.bind(this));
this.open_chat();
} else if (this.rule.action === 'auto_popup') {
var auto_popup_cookie = utils.get_cookie('im_livechat_auto_popup');
if (!auto_popup_cookie || JSON.parse(auto_popup_cookie)) {
this.auto_popup_timeout = setTimeout(this.open_chat.bind(this), this.rule.auto_popup_timer*1000);
}
}
bus.on('notification', this, function (notification) {
if (this.channel && (notification[0] === this.channel.uuid)) {
this.add_message(notification[1]);
this.render_messages();
if (this.chat_window.folded) {
this.chat_window.update_unread(this.chat_window.unread_msgs+1);
}
}
bus.on('notification', this, function (notification) {
if (this.channel && (notification[0] === this.channel.uuid)) {
this.add_message(notification[1]);
this.render_messages();
if (this.chat_window.folded) {
this.chat_window.update_unread(this.chat_window.unread_msgs+1);
}
}
});
}
});
return this._super();
},
@@ -140,10 +140,10 @@
<template id="loader" name="Livechat : Javascript appending the livechat button">
<t t-if="not info">
<t t-set="info" t-value="request.env['im_livechat.channel'].match_rules(request, channel)"/>
<t t-set="info" t-value="request.env['im_livechat.channel'].get_livechat_info(channel)"/>
</t>
<t t-if="info" t-translation="off">
<t t-if="info['available']" t-translation="off">
document.addEventListener("DOMContentLoaded", function(event) {
<t t-if="web_session_required">
odoo.define('web.session', function (require) {
@@ -158,8 +158,7 @@
var button = new im_livechat.LivechatButton(
$('body'),
"<t t-esc="info['server_url']"/>",
<t t-raw="json.dumps(info.get('options', {}))"/>,
<t t-raw="json.dumps(info.get('rule', False))"/>
<t t-raw="json.dumps(info.get('options', {}))"/>
);
button.appendTo($('body'));
window.livechat_button = button;