diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js index 91cf65d00e6..26dbb69ab3e 100644 --- a/addons/bus/static/src/js/bus.js +++ b/addons/bus/static/src/js/bus.js @@ -10,19 +10,28 @@ this.activated = false; this.channels = []; this.last = 0; + this.stop = false; }, start_polling: function(){ if(!this.activated){ - this.poll(); + setTimeout(this.poll(), 1); + this.stop = false; } }, + stop_polling: function(){ + this.activated = false; + this.stop = true; + this.channels = []; + }, poll: function() { var self = this; self.activated = true; var data = {'channels': self.channels, 'last': self.last, 'options' : self.options}; - openerp.jsonRpc('/longpolling/poll', 'call', data).then(function(result) { + openerp.session.rpc('/longpolling/poll', data, {shadow : true}).then(function(result) { _.each(result, _.bind(self.on_notification, self)); - self.poll(); + if(!self.stop){ + self.poll(); + } }, function(unused, e) { // random delay to avoid massive longpolling setTimeout(_.bind(self.poll, self), bus.ERROR_DELAY + (Math.floor((Math.random()*20)+1)*1000)); diff --git a/addons/im_chat/im_chat.py b/addons/im_chat/im_chat.py index 2a8a6daf78a..def76df77d7 100644 --- a/addons/im_chat/im_chat.py +++ b/addons/im_chat/im_chat.py @@ -52,6 +52,12 @@ class im_chat_session(osv.Model): 'uuid': lambda *args: '%s' % uuid.uuid4(), } + def is_in_session(self, cr, uid, uuid, user_id, context=None): + """ return if the given user_id is in the session """ + sids = self.search(cr, uid, [('uuid', '=', uuid)], context=context, limit=1) + for session in self.browse(cr, uid, sids, context=context): + return user_id and user_id in [u.id for u in session.user_ids] + def users_infos(self, cr, uid, ids, context=None): """ get the user infos for all the user in the session """ for session in self.pool["im_chat.session"].browse(cr, uid, ids, context=context): @@ -220,6 +226,17 @@ class im_chat_message(osv.Model): self.pool['bus.bus'].sendmany(cr, uid, notifications) return message_id + def get_messages(self, cr, uid, uuid, last_id=False, limit=20, context=None): + """ get messages (id desc) from given last_id in the given session """ + Session = self.pool['im_chat.session'] + if Session.is_in_session(cr, uid, uuid, uid, context=context): + domain = [("to_id.uuid", "=", uuid)] + if last_id: + domain.append(("id", "<", last_id)); + return self.search_read(cr, uid, domain, ['id', 'create_date','to_id','from_id', 'type', 'message'], limit=limit, context=context) + return False + + class im_chat_presence(osv.Model): """ im_chat_presence status can be: online, away or offline. This model is a one2one, but is not attached to res_users to avoid database concurrence errors @@ -400,4 +417,9 @@ class Controller(openerp.addons.bus.bus.Controller): headers.append(('Content-Length', len(image_data))) return request.make_response(image_data, headers) + @openerp.http.route(['/im_chat/history'], type="json", auth="none") + def history(self, uuid, last_id=False, limit=20): + registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context + return registry["im_chat.message"].get_messages(cr, openerp.SUPERUSER_ID, uuid, last_id, limit, context=context) + # vim:et: diff --git a/addons/im_chat/static/src/js/im_chat.js b/addons/im_chat/static/src/js/im_chat.js index ca701d2fa23..8d16afa8f57 100644 --- a/addons/im_chat/static/src/js/im_chat.js +++ b/addons/im_chat/static/src/js/im_chat.js @@ -1,6 +1,7 @@ (function(){ "use strict"; + var _t = openerp._t; var _lt = openerp._lt; var QWeb = openerp.qweb; @@ -99,10 +100,7 @@ if(session.state !== 'closed'){ conv = new im_chat.Conversation(this, this, session, this.options); conv.appendTo($("body")); - conv.on("destroyed", this, function() { - delete this.sessions[session.uuid]; - this.calc_positions(); - }); + conv.on("destroyed", this, _.bind(this.delete_session, this)); this.sessions[session.uuid] = conv; this.calc_positions(); } @@ -124,6 +122,10 @@ } return conv; }, + delete_session: function(uuid){ + delete this.sessions[uuid]; + this.calc_positions(); + }, received_message: function(message) { var self = this; var session_id = message.to_id[0]; @@ -250,12 +252,17 @@ load_history: function(){ var self = this; if(this.loading_history){ - var domain = [["to_id.uuid", "=", this.get("session").uuid]]; - _.first(this.get("messages")) && domain.push(['id','<', _.first(this.get("messages")).id]); - new openerp.Model("im_chat.message").call("search_read", [domain, ['id', 'create_date','to_id','from_id', 'type', 'message'], 0, NBR_LIMIT_HISTORY]).then(function(messages){ - self.insert_messages(messages); - if(messages.length != NBR_LIMIT_HISTORY){ - self.loading_history = false; + var data = {uuid: self.get("session").uuid, limit: NBR_LIMIT_HISTORY}; + var lastid = _.first(this.get("messages")) ? _.first(this.get("messages")).id : false; + if(lastid){ + data["last_id"] = lastid; + } + openerp.session.rpc("/im_chat/history", data).then(function(messages){ + if(messages){ + self.insert_messages(messages); + if(messages.length != NBR_LIMIT_HISTORY){ + self.loading_history = false; + } } }); } @@ -410,7 +417,7 @@ this.update_fold_state('closed'); }, destroy: function() { - this.trigger("destroyed"); + this.trigger("destroyed", this.get('session').uuid); return this._super(); } }); @@ -436,7 +443,7 @@ update_status: function(){ this.$(".oe_im_user_online").toggle(this.get('im_status') !== 'offline'); var img_src = (this.get('im_status') == 'away' ? '/im_chat/static/src/img/yellow.png' : '/im_chat/static/src/img/green.png'); - this.$(".oe_im_user_online").attr('src', openerp.session.server + img_src); + this.$(".oe_im_user_online").attr('src', img_src); }, activate_user: function() { this.trigger("activate_user", this.get("id")); @@ -556,7 +563,7 @@ var self = this; var sessions = new openerp.web.Model("im_chat.session"); return sessions.call("session_get", [user_id]).then(function(session) { - self.c_manager.activate_session(session, true); + self.c_manager.activate_session(session, true); }); }, update_users_status: function(users_list){ diff --git a/addons/im_chat/static/src/xml/im_chat.xml b/addons/im_chat/static/src/xml/im_chat.xml index 01db7b8277d..0ca4fe307d2 100644 --- a/addons/im_chat/static/src/xml/im_chat.xml +++ b/addons/im_chat/static/src/xml/im_chat.xml @@ -69,7 +69,7 @@ - + diff --git a/addons/im_livechat/im_livechat.py b/addons/im_livechat/im_livechat.py index ca7d1708bf5..a8beabcd842 100644 --- a/addons/im_livechat/im_livechat.py +++ b/addons/im_livechat/im_livechat.py @@ -21,6 +21,7 @@ import random import openerp +import json import openerp.addons.im_chat.im_chat from openerp.osv import osv, fields @@ -193,6 +194,15 @@ class im_chat_session(osv.Model): 'fullname' : fields.function(_get_fullname, type="char", string="Complete name"), } + def is_in_session(self, cr, uid, uuid, user_id, context=None): + """ return if the given user_id is in the session """ + sids = self.search(cr, uid, [('uuid', '=', uuid)], context=context, limit=1) + for session in self.browse(cr, uid, sids, context=context): + if session.anonymous_name and not user_id: + return True + else: + return super(im_chat_session, self).is_in_session(cr, uid, uuid, user_id, context=context) + def users_infos(self, cr, uid, ids, context=None): """ add the anonymous user in the user of the session """ for session in self.browse(cr, uid, ids, context=context): @@ -237,3 +247,4 @@ class LiveChatController(http.Controller): reg = openerp.modules.registry.RegistryManager.get(db) with reg.cursor() as cr: return len(reg.get('im_livechat.channel').get_available_users(cr, uid, channel)) > 0 + diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js index e969ef73c07..2d665098bce 100644 --- a/addons/im_livechat/static/src/js/im_livechat.js +++ b/addons/im_livechat/static/src/js/im_livechat.js @@ -7,9 +7,9 @@ "use strict"; var _t = openerp._t; - var im_livechat = {}; openerp.im_livechat = im_livechat; + im_livechat.COOKIE_NAME = 'livechat_conversation'; /* The state of anonymous session is hold by the client and not the server. @@ -19,7 +19,7 @@ init: function(){ this._super.apply(this, arguments); this.shown = true; - this.loading_history = false; // unactivate the loading history + this.loading_history = true; // since the session is kept after a refresh, anonymous can reload their history }, show: function(){ this._super.apply(this, arguments); @@ -48,40 +48,78 @@ var session = this.get('session'); session.state = state; this.set('session', session); + openerp.set_cookie(im_livechat.COOKIE_NAME, JSON.stringify(session), 60*60); + }, + click_close: function(e) { + this._super(e); + openerp.set_cookie(im_livechat.COOKIE_NAME, "", -1); }, }); + // To avoid exeption when the anonymous has close his + // conversation and when operator send him a message. + openerp.im_chat.ConversationManager.include({ + received_message: function(message) { + try{ + this._super(message); + }catch(e){} + } + }); + + // TODO move this to openerpframework.js, ask xmo + openerp.set_cookie = function(name, value, ttl) { + ttl = ttl || 24*60*60*365; + document.cookie = [ + name + '=' + value, + 'path=/', + 'max-age=' + ttl, + 'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString() + ].join(';'); + }; + im_livechat.LiveSupport = openerp.Widget.extend({ init: function(server_url, db, channel, options) { + var self = this; options = options || {}; _.defaults(options, { buttonText: _t("Chat with one of our collaborators"), inputPlaceholder: null, defaultMessage: _t("How may I help you?"), - defaultUsername: _t("Anonymous"), + defaultUsername: _t("Visitor"), }); - openerp.session = new openerp.Session(); - + openerp.session = new openerp.Session(null, server_url, { use_cors: false }); // load the qweb templates var defs = []; - var templates = ['/im_livechat/static/src/xml/im_livechat.xml','/im_chat/static/src/xml/im_chat.xml']; + var templates = ['/im_livechat/static/src/xml/im_livechat.xml', '/im_chat/static/src/xml/im_chat.xml']; _.each(templates, function(tmpl){ defs.push(openerp.session.rpc('/web/proxy/load', {path: tmpl}).then(function(xml) { openerp.qweb.add_template(xml); })); }); return $.when.apply($, defs).then(function() { - return openerp.session.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) { - if(activated){ - var button = new im_livechat.ChatButton(null, channel, options); - button.appendTo($("body")); - if (options.auto){ - button.click(); - } - } - }); + self.setup(db, channel, options); }); }, + setup: function(db, channel, options){ + var self = this; + var session = openerp.get_cookie(im_livechat.COOKIE_NAME); + if(session){ + self.build_button(channel, options, JSON.parse(session)); + }else{ + openerp.session.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) { + if(activated){ + self.build_button(channel, options); + } + }); + } + }, + build_button: function(channel, options, session){ + var button = new im_livechat.ChatButton(null, channel, options, session); + button.appendTo($("body")); + if (options.auto){ + button.click(); + } + } }); im_livechat.ChatButton = openerp.Widget.extend({ @@ -89,56 +127,79 @@ events: { "click": "click" }, - init: function(parent, channel, options) { + init: function(parent, channel, options, session) { this._super(parent); this.channel = channel; this.options = options; this.text = options.buttonText; + this.session = session || false; + this.conv = false; + this.no_session_message = _t("None of our collaborators seems to be available, please try again later."); }, start: function() { this.$().append(openerp.qweb.render("chatButton", {widget: this})); + // set up the manager + this.manager = new openerp.im_chat.ConversationManager(this, this.options); + this.manager.set("bottom_offset", $('.oe_chat_button').outerHeight()); + this.manager.notification = function(notif){ // override the notification default function + alert(notif); + } + if(this.session){ + this.set_conversation(this.session); + } }, click: function() { - if (! this.manager) { - this.manager = new openerp.im_chat.ConversationManager(this, this.options); - this.manager.set("bottom_offset", $('.oe_chat_button').outerHeight()); // TODO correct the value (no hardcode damned !) - // override the notification default function - this.manager.notification = function(notif){ - alert(notif); + var self = this; + if (!this.conv){ + openerp.session.rpc("/im_livechat/get_session", {"channel_id" : self.channel, "anonymous_name" : this.options["defaultUsername"]}, {shadow: true}).then(function(session) { + if (! session) { + self.manager.notification(self.no_session_message); + return; + } + session.state = 'open'; + // save the session in a cookie + openerp.set_cookie(im_livechat.COOKIE_NAME, JSON.stringify(session), 60*60); + // create the conversation with the received session + self.set_conversation(session, true); + }); + } + }, + set_conversation: function(session, welcome_message){ + var self = this; + this.session = session; + if(session.state === 'closed'){ + return; + } + this.conv = this.manager.apply_session(session); + this.conv.on("destroyed", this, function() { + openerp.bus.bus.stop_polling(); + delete self.conv; + delete self.session; + }); + // start the polling + openerp.bus.bus.add_channel(session.uuid); + openerp.bus.bus.start_polling(); + // add the automatic welcome message + if(welcome_message){ + this.send_welcome_message(); + } + }, + send_welcome_message: function(){ + var self = this; + if(this.session.users.length > 0){ + if (self.options.defaultMessage) { + setTimeout(function(){ + self.conv.received_message({ + id : 1, + type: "message", + message: self.options.defaultMessage, + create_date: openerp.datetime_to_str(new Date()), + from_id: [self.session.users[0].id, self.session.users[0].name], + to_id: [0, self.session.uuid] + }); + }, 1000); } } - return this.chat(); - }, - chat: function() { - var self = this; - if (_.keys(this.manager.sessions).length > 0) - return; - - openerp.session.rpc("/im_livechat/get_session", {"channel_id" : self.channel, "anonymous_name" : this.options["defaultUsername"]}, {shadow: true}).then(function(session) { - if (! session) { - self.manager.notification(_t("None of our collaborators seems to be available, please try again later.")); - return; - } - var conv = self.manager.activate_session(session, [], true); - // start the polling - openerp.bus.bus.add_channel(session.uuid); - openerp.bus.bus.start_polling(); - // add the automatic welcome message - if(session.users.length > 0){ - if (self.options.defaultMessage) { - setTimeout(function(){ - conv.received_message({ - id : 1, - type: "message", - message: self.options.defaultMessage, - create_date: openerp.datetime_to_str(new Date()), - from_id: [session.users[0].id, session.users[0].name], - to_id: [0, session.uuid] - }); - }, 1000); - } - } - }); } });