[FIX][ADD] im, im_livechat: fix security rules and add history when opening a conversation

bzr revid: dle@openerp.com-20140128162649-aptjndl85ocmem8a
This commit is contained in:
Denis Ledoux 2014-01-28 17:26:49 +01:00
parent cbba7eb4da
commit f13dd8cee7
10 changed files with 84 additions and 24 deletions

View File

@ -27,7 +27,6 @@ from openerp.addons.web.http import request
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
import datetime import datetime
from openerp.osv import osv, fields, expression from openerp.osv import osv, fields, expression
import time
import logging import logging
import json import json
import select import select
@ -202,7 +201,7 @@ class im_session(osv.osv):
return res return res
_columns = { _columns = {
'user_ids': fields.many2many('im.user'), 'user_ids': fields.many2many('im.user', 'im_session_im_user_rel', 'im_session_id', 'im_user_id', 'Users'),
"name": fields.function(_calc_name, string="Name", type='char'), "name": fields.function(_calc_name, string="Name", type='char'),
} }
@ -225,6 +224,9 @@ class im_session(osv.osv):
}, context=context) }, context=context)
return self.read(cr, uid, session_id, context=context) return self.read(cr, uid, session_id, context=context)
def get_session_users(self, cr, uid, session_id, context=None):
return self.read(cr, openerp.SUPERUSER_ID, session_id, ['user_ids'], context=context)
def add_to_session(self, cr, uid, session_id, user_id, uuid=None, context=None): def add_to_session(self, cr, uid, session_id, user_id, uuid=None, context=None):
my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context) my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
session = self.read(cr, uid, session_id, context=context) session = self.read(cr, uid, session_id, context=context)
@ -259,7 +261,7 @@ class im_user(osv.osv):
return ['&', ('im_last_status', '=', True), ('im_last_status_update', '>', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))] return ['&', ('im_last_status', '=', True), ('im_last_status_update', '>', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
else: else:
return ['|', ('im_last_status', '=', False), ('im_last_status_update', '<=', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))] return ['|', ('im_last_status', '=', False), ('im_last_status_update', '<=', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
# TODO: Remove fields arg in trunk. Also in im.js.
def search_users(self, cr, uid, text_search, fields, limit, context=None): def search_users(self, cr, uid, text_search, fields, limit, context=None):
my_id = self.get_my_id(cr, uid, None, context) my_id = self.get_my_id(cr, uid, None, context)
group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1] group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1]
@ -271,7 +273,7 @@ class im_user(osv.osv):
if len(found) < limit: if len(found) < limit:
found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", False], ["id", "not in", found]], found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", False], ["id", "not in", found]],
order="name asc", limit=limit-len(found), context=context) order="name asc", limit=limit-len(found), context=context)
users = self.read(cr, uid, found, fields, context=context) users = self.read(cr,openerp.SUPERUSER_ID, found, ["name", "user_id", "uuid", "im_status"], context=context)
users.sort(key=lambda obj: found.index(obj['id'])) users.sort(key=lambda obj: found.index(obj['id']))
return users return users
@ -319,6 +321,9 @@ class im_user(osv.osv):
continue continue
return res return res
def get_users(self, cr, uid, ids, context=None):
return self.read(cr,openerp.SUPERUSER_ID, ids, ["name", "im_status", "uuid"], context=context)
_columns = { _columns = {
'name': fields.function(_get_name, type='char', size=200, string="Name", store=True, readonly=True), 'name': fields.function(_get_name, type='char', size=200, string="Name", store=True, readonly=True),
'assigned_name': fields.char(string="Assigned Name", size=200, required=False), 'assigned_name': fields.char(string="Assigned Name", size=200, required=False),
@ -341,3 +346,16 @@ class im_user(osv.osv):
('user_uniq', 'unique (user_id)', 'Only one chat user per OpenERP user.'), ('user_uniq', 'unique (user_id)', 'Only one chat user per OpenERP user.'),
('uuid_uniq', 'unique (uuid)', 'Chat identifier already used.'), ('uuid_uniq', 'unique (uuid)', 'Chat identifier already used.'),
] ]
class res_users(osv.osv):
_inherit = "res.users"
def _get_im_user(self, cr, uid, ids, field_name, arg, context=None):
result = dict.fromkeys(ids, False)
for index, im_user in enumerate(self.pool['im.user'].search_read(cr, uid, domain=[('user_id', 'in', ids)], fields=['name', 'user_id'], context=context)):
result[ids[index]] = im_user.get('user_id') and (im_user['user_id'][0], im_user['name']) or False
return result
_columns = {
'im_user_id' : fields.function(_get_im_user, type='many2one', string="IM User", relation="im.user"),
}

View File

@ -2,10 +2,10 @@
<openerp> <openerp>
<data> <data>
<record id="message_rule_1" model="ir.rule"> <record id="message_rule_1" model="ir.rule">
<field name="name">Can only read messages that you sent or messages sent to you</field> <field name="name">Can only read messages from a session where user is</field>
<field name="model_id" ref="model_im_message"/> <field name="model_id" ref="model_im_message"/>
<field name="groups" eval="[(6,0,[ref('base.group_user')])]"/> <field name="groups" eval="[(6,0,[ref('base.group_user')])]"/>
<field name="domain_force">["|", ('to_id.user_id', 'in', [user.id]), ('from_id.user_id', '=', user.id)]</field> <field name="domain_force">[('session_id.user_ids', 'in', user.im_user_id)]</field>
<field name="perm_read" eval="1"/> <field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/> <field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/> <field name="perm_create" eval="0"/>

View File

@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_im_message,im.message,model_im_message,base.group_user,1,0,1,0 access_im_message,im.message,model_im_message,,1,0,1,0
access_im_user,im.user,model_im_user,,1,1,1,0 access_im_user,im.user,model_im_user,,1,1,1,0
access_im_session,im.session,model_im_session,,1,0,0,0 access_im_session,im.session,model_im_session,,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_im_message im.message model_im_message base.group_user 1 0 1 0
3 access_im_user im.user model_im_user 1 1 1 0
4 access_im_session im.session model_im_session 1 0 0 0

View File

@ -92,6 +92,7 @@
search_changed: function(e) { search_changed: function(e) {
var users = new instance.web.Model("im.user"); var users = new instance.web.Model("im.user");
var self = this; var self = this;
// TODO: Remove fields arg in trunk. Also in im.js.
return this.user_search_dm.add(users.call("search_users", [this.get("current_search"), ["name", "user_id", "uuid", "im_status"], return this.user_search_dm.add(users.call("search_users", [this.get("current_search"), ["name", "user_id", "uuid", "im_status"],
USERS_LIMIT], {context:new instance.web.CompoundContext()})).then(function(users) { USERS_LIMIT], {context:new instance.web.CompoundContext()})).then(function(users) {
var logged_users = _.filter(users, function(u) { return !!u.im_status; }); var logged_users = _.filter(users, function(u) { return !!u.im_status; });

View File

@ -145,7 +145,7 @@ function declare($, _, openerp) {
if (_.size(no_cache) === 0) if (_.size(no_cache) === 0)
def = $.when(); def = $.when();
else else
def = im_common.connection.model("im.user").call("read", [_.values(no_cache), []]).then(function(users) { def = im_common.connection.model("im.user").call("get_users", [_.values(no_cache)]).then(function(users) {
self.add_to_user_cache(users); self.add_to_user_cache(users);
}); });
return def.then(function() { return def.then(function() {
@ -230,7 +230,8 @@ function declare($, _, openerp) {
return self.chat_with_users(users); return self.chat_with_users(users);
}); });
}, },
activate_session: function(session_id, focus) { activate_session: function(session_id, focus, message) {
var self = this;
var conv = _.find(this.conversations, function(conv) {return conv.session_id == session_id;}); var conv = _.find(this.conversations, function(conv) {return conv.session_id == session_id;});
var def = $.when(); var def = $.when();
if (! conv) { if (! conv) {
@ -244,6 +245,9 @@ function declare($, _, openerp) {
this.calc_positions(); this.calc_positions();
this.trigger("new_conversation", conv); this.trigger("new_conversation", conv);
}, this)); }, this));
def = def.then(function(){
return self.load_history(conv, message);
});
} }
if (focus) { if (focus) {
def = def.then(function() { def = def.then(function() {
@ -252,13 +256,32 @@ function declare($, _, openerp) {
} }
return def.then(function() {return conv}); return def.then(function() {return conv});
}, },
received_messages: function(messages) { load_history: function(conv, message){
var self = this;
var domain = [["session_id", "=", conv.session_id]];
if (!_.isUndefined(message)){
domain.push(["date", "<", message.date]);
}
return im_common.connection.model("im.message").call("search_read", [domain, [], 0, 10]).then(function(messages){
messages.reverse();
var users = _.unique(_.map(messages, function(message){
return message.from_id[0];
}));
return self.ensure_users(_.without(users, self.me.get("id"))).then(function(){
return self.received_messages(messages, true);
});
});
},
received_messages: function(messages, seen) {
var self = this; var self = this;
var defs = []; var defs = [];
var received = false; var received = false;
if (_.isUndefined(seen)){
seen = false;
}
_.each(messages, function(message) { _.each(messages, function(message) {
if (! message.technical) { if (! message.technical) {
defs.push(self.activate_session(message.session_id[0]).then(function(conv) { defs.push(self.activate_session(message.session_id[0], false, message).then(function(conv) {
received = self.my_id !== message.from_id[0]; received = self.my_id !== message.from_id[0];
return conv.received_message(message); return conv.received_message(message);
})); }));
@ -269,7 +292,7 @@ function declare($, _, openerp) {
} }
}); });
return $.when.apply($, defs).then(function(){ return $.when.apply($, defs).then(function(){
if (! self.get("window_focus") && received) { if (! self.get("window_focus") && received && !seen) {
self.set("waiting_messages", self.get("waiting_messages") + messages.length); self.set("waiting_messages", self.get("waiting_messages") + messages.length);
self.ting.play(); self.ting.play();
self.create_ting(); self.create_ting();
@ -368,7 +391,7 @@ function declare($, _, openerp) {
refresh_users: function() { refresh_users: function() {
var self = this; var self = this;
var user_ids; var user_ids;
return im_common.connection.model("im.session").call("read", [self.session_id]).then(function(session) { return im_common.connection.model("im.session").call("get_session_users", [self.session_id]).then(function(session) {
user_ids = _.without(session.user_ids, self.c_manager.me.get("id")); user_ids = _.without(session.user_ids, self.c_manager.me.get("id"));
return self.c_manager.ensure_users(user_ids); return self.c_manager.ensure_users(user_ids);
}).then(function(users) { }).then(function(users) {
@ -449,7 +472,7 @@ function declare($, _, openerp) {
date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2); date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2);
var to_show = _.map(items, im_common.escape_keep_url); var to_show = _.map(items, im_common.escape_keep_url);
this.last_bubble = $(openerp.qweb.render("im_common.conversation_bubble", {"items": to_show, "user": user, "time": date})); this.last_bubble = $(openerp.qweb.render("im_common.conversation_bubble", {"items": to_show, "user": user, "time": date}));
$(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble); $(this.$(".oe_im_chatview_conversation")).append(this.last_bubble);
this._go_bottom(); this._go_bottom();
}, },
_go_bottom: function() { _go_bottom: function() {

View File

@ -11,7 +11,7 @@
All users are offline. They will receive your messages on their next connection. All users are offline. They will receive your messages on their next connection.
</div> </div>
<div class="oe_im_chatview_content"> <div class="oe_im_chatview_content">
<div></div> <div class="oe_im_chatview_conversation"></div>
</div> </div>
<div class="oe_im_chatview_footer"> <div class="oe_im_chatview_footer">
<input class="oe_im_chatview_input" t-att-placeholder="widget.inputPlaceholder" /> <input class="oe_im_chatview_input" t-att-placeholder="widget.inputPlaceholder" />

View File

@ -170,8 +170,8 @@ class im_livechat_channel(osv.osv):
return users return users
def get_session(self, cr, uid, channel_id, uuid, context=None): def get_session(self, cr, uid, channel_id, uuid, context=None):
my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context) self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
users = self.get_available_users(cr, uid, channel_id, context=context) users = self.get_available_users(cr, openerp.SUPERUSER_ID, channel_id, context=context)
if len(users) == 0: if len(users) == 0:
return False return False
user_id = random.choice(users).id user_id = random.choice(users).id

View File

@ -125,6 +125,7 @@
<field name="res_model">im.message</field> <field name="res_model">im.message</field>
<field name="view_mode">list</field> <field name="view_mode">list</field>
<field name="domain">[('session_id.channel_id', '!=', None)]</field> <field name="domain">[('session_id.channel_id', '!=', None)]</field>
<field name="context">{'search_default_group_by_session_id': 1, 'search_default_group_by_date': 1, 'search_default_session_id': 1}</field>
</record> </record>
<menuitem name="History" parent="im_livechat" id="history" action="action_history" groups="group_im_livechat_manager"/> <menuitem name="History" parent="im_livechat" id="history" action="action_history" groups="group_im_livechat_manager"/>
@ -141,5 +142,21 @@
</field> </field>
</record> </record>
<record id="im_message_search" model="ir.ui.view">
<field name="name">im.message.search</field>
<field name="model">im.message</field>
<field name="arch" type="xml">
<search string="Search history">
<filter name="session_id" string="My Sessions" domain="[('session_id.user_ids','in', uid)]"/>
<field name="from_id"/>
<field name="to_id"/>
<group expand="0" string="Group By...">
<filter name="group_by_session_id" string="Session" domain="[]" context="{'group_by':'session_id'}"/>
<filter name="group_by_date" string="Date" domain="[]" context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -2,7 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ls_chann1,im_livechat.channel,model_im_livechat_channel,,1,0,0,0 access_ls_chann1,im_livechat.channel,model_im_livechat_channel,,1,0,0,0
access_ls_chann2,im_livechat.channel,model_im_livechat_channel,group_im_livechat,1,1,1,0 access_ls_chann2,im_livechat.channel,model_im_livechat_channel,group_im_livechat,1,1,1,0
access_ls_chann3,im_livechat.channel,model_im_livechat_channel,group_im_livechat_manager,1,1,1,1 access_ls_chann3,im_livechat.channel,model_im_livechat_channel,group_im_livechat_manager,1,1,1,1
access_ls_message_portal,im_livechat.im.message.portal,im.model_im_message,portal.group_portal,0,0,0,0
access_im_user_portal,im_livechat.im.user.portal,im.model_im_user,portal.group_portal,1,0,0,0 access_im_user_portal,im_livechat.im.user.portal,im.model_im_user,portal.group_portal,1,0,0,0
access_ls_message,im_livechat.im.message,im.model_im_message,portal.group_anonymous,0,0,0,0
access_im_user,im_livechat.im.user,im.model_im_user,portal.group_anonymous,1,0,0,0 access_im_user,im_livechat.im.user,im.model_im_user,portal.group_anonymous,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ls_chann1 im_livechat.channel model_im_livechat_channel 1 0 0 0
3 access_ls_chann2 im_livechat.channel model_im_livechat_channel group_im_livechat 1 1 1 0
4 access_ls_chann3 im_livechat.channel model_im_livechat_channel group_im_livechat_manager 1 1 1 1
access_ls_message_portal im_livechat.im.message.portal im.model_im_message portal.group_portal 0 0 0 0
5 access_im_user_portal im_livechat.im.user.portal im.model_im_user portal.group_portal 1 0 0 0
access_ls_message im_livechat.im.message im.model_im_message portal.group_anonymous 0 0 0 0
6 access_im_user im_livechat.im.user im.model_im_user portal.group_anonymous 1 0 0 0

View File

@ -107,11 +107,14 @@ define(["openerp", "im_common", "underscore", "require", "jquery",
} }
self.manager.activate_session(session_id, true).then(function(conv) { self.manager.activate_session(session_id, true).then(function(conv) {
if (self.options.defaultMessage) { if (self.options.defaultMessage) {
conv.received_message({ setTimeout(function(){
message: self.options.defaultMessage, conv.received_message({
date: openerp.datetime_to_str(new Date()), message: self.options.defaultMessage,
from_id: [conv.get("users")[0].get("id"), "Unknown"] date: openerp.datetime_to_str(new Date()),
}); from_id: [conv.get("users")[0].get("id"), "Unknown"]
});
},
2500);
} }
}); });
}); });