diff --git a/addons/bus/__openerp__.py b/addons/bus/__openerp__.py
index 2489f6a38f9..2a7185fdaf7 100644
--- a/addons/bus/__openerp__.py
+++ b/addons/bus/__openerp__.py
@@ -6,6 +6,7 @@
'description': "Instant Messaging Bus allow you to send messages to users, in live.",
'depends': ['base', 'web'],
'data': [
+ 'bus_presence_cron.xml',
'views/bus.xml',
'security/ir.model.access.csv',
],
diff --git a/addons/bus/bus_presence_cron.xml b/addons/bus/bus_presence_cron.xml
new file mode 100644
index 00000000000..309c641e3f7
--- /dev/null
+++ b/addons/bus/bus_presence_cron.xml
@@ -0,0 +1,13 @@
+
+
+
+ Check User Disconnections
+ 5
+ minutes
+ -1
+
+
+
+
+
+
diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py
index 050eb684625..d755cabe54f 100644
--- a/addons/bus/controllers/main.py
+++ b/addons/bus/controllers/main.py
@@ -19,6 +19,7 @@ class BusController(openerp.http.Controller):
# override to add channels
def _poll(self, dbname, channels, last, options):
+ channels.append((request.db, 'bus.presence'))
# update the user presence
if request.session.uid and 'im_presence' in options:
request.env['bus.presence'].update(options.get('im_presence'))
diff --git a/addons/bus/models/bus_presence.py b/addons/bus/models/bus_presence.py
index 445e9491a75..70aa9ef9726 100644
--- a/addons/bus/models/bus_presence.py
+++ b/addons/bus/models/bus_presence.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import datetime
-import random
import time
from openerp import api, fields, models
@@ -11,6 +10,8 @@ from openerp.addons.bus.models.bus import TIMEOUT
DISCONNECTION_TIMER = TIMEOUT + 5
AWAY_TIMER = 600 # 10 minutes
+DISCONNECTIONS_CHECK_PERIOD = datetime.timedelta(minutes=1) # check for user disconnections every minute
+last_disconnections_check = datetime.datetime.utcnow()
class BusPresence(models.Model):
@@ -34,7 +35,7 @@ class BusPresence(models.Model):
@api.model
def update(self, user_presence=True):
""" Register the given presence of the current user, and trigger a im_status change if necessary.
- The status will not be written or sent if not necessary.
+ The status will not be sent if not necessary.
:param user_presence : True, if the user (self._uid) is still detected using its browser.
:type user_presence : boolean
"""
@@ -51,37 +52,31 @@ class BusPresence(models.Model):
values['user_id'] = self._uid
self.create(values)
else: # write the user presence if necessary
- if user_presence:
- values['last_presence'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
- values['status'] = 'online'
- else:
- threshold = datetime.datetime.now() - datetime.timedelta(seconds=AWAY_TIMER)
- if datetime.datetime.strptime(presence.last_presence, DEFAULT_SERVER_DATETIME_FORMAT) < threshold:
- values['status'] = 'away'
+ values['status'] = 'online' if user_presence else 'away'
send_notification = presence.status != values['status']
- # write only if the last_poll is passed TIMEOUT, or if the status has changed
- delta = datetime.datetime.utcnow() - datetime.datetime.strptime(presence.last_poll, DEFAULT_SERVER_DATETIME_FORMAT)
- if delta > datetime.timedelta(seconds=TIMEOUT) or send_notification:
- # Hide transaction serialization errors, which can be ignored, the presence update is not essential
- with tools.mute_logger('openerp.sql_db'):
- presence.write(values)
+ # Hide transaction serialization errors, which can be ignored, the presence update is not essential
+ with tools.mute_logger('openerp.sql_db'):
+ presence.write(values)
# avoid TransactionRollbackError
self.env.cr.commit() # TODO : check if still necessary
# notify if the status has changed
if send_notification: # TODO : add user_id to the channel tuple to allow using user_watch in controller presence
- self.env['bus.bus'].sendone((self._cr.dbname, 'bus.presence'), {'id': self._uid, 'im_status': values['status']})
- # gc : disconnect the users having a too old last_poll. 1 on 100 chance to do it.
- if random.random() < 0.01:
- self.check_users_disconnection()
+ self.env['bus.bus'].sendone((self._cr.dbname, 'bus.presence'), {'id': self.env.user.partner_id.id, 'im_status': values['status']})
+ # check for disconnected users
+ self.check_users_disconnection()
return True
@api.model
def check_users_disconnection(self):
""" Disconnect the users having a too old last_poll """
- limit_date = (datetime.datetime.utcnow() - datetime.timedelta(0, DISCONNECTION_TIMER)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
- presences = self.search([('last_poll', '<', limit_date), ('status', '!=', 'offline')])
- presences.write({'status': 'offline'})
+ global last_disconnections_check
+ now = datetime.datetime.utcnow()
notifications = []
- for presence in presences:
- notifications.append([(self._cr.dbname, 'bus.presence'), {'id': presence.user_id.id, 'im_status': presence.status}])
+ if (now - DISCONNECTIONS_CHECK_PERIOD) > last_disconnections_check:
+ last_disconnections_check = now
+ limit_date = (now - datetime.timedelta(0, DISCONNECTION_TIMER)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+ presences = self.search([('last_poll', '<', limit_date), ('status', '!=', 'offline')])
+ presences.write({'status': 'offline'})
+ for presence in presences:
+ notifications.append([(self._cr.dbname, 'bus.presence'), {'id': presence.user_id.partner_id.id, 'im_status': presence.status}])
self.env['bus.bus'].sendmany(notifications)
diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js
index 9373b7c7b1c..29540b36cdf 100644
--- a/addons/bus/static/src/js/bus.js
+++ b/addons/bus/static/src/js/bus.js
@@ -7,11 +7,15 @@ var Widget = require('web.Widget');
var bus = {};
bus.ERROR_DELAY = 10000;
+bus.AWAY_TIMEOUT = 300000; // 5 minutes
bus.Bus = Widget.extend({
init: function(){
+ var self = this;
this._super();
- this.options = {};
+ this.options = {
+ im_presence: true,
+ };
this.activated = false;
this.channels = [];
this.last = 0;
@@ -21,9 +25,14 @@ bus.Bus = Widget.extend({
// bus presence
this.set("window_focus", true);
this.on("change:window_focus", this, function () {
- this.options.im_presence = this.get("window_focus");
+ clearTimeout(self.away_timeout);
if (this.get("window_focus")) {
+ this.options.im_presence = true;
this.trigger('window_focus', this.is_master);
+ } else {
+ this.away_timeout = setTimeout(function () {
+ self.options.im_presence = false;
+ }, bus.AWAY_TIMEOUT);
}
});
$(window).on("focus", _.bind(this.window_focus, this));
@@ -120,13 +129,6 @@ var CrossTabBus = bus.Bus.extend({
}
on("storage", this.on_storage.bind(this));
- if (this.is_master) {
- setItem('bus.channels', this.channels);
- setItem('bus.options', this.options);
- } else {
- this.channels = getItem('bus.channels', this.channels);
- this.options = getItem('bus.options', this.options);
- }
},
start_polling: function(){
var self = this;
@@ -139,6 +141,13 @@ var CrossTabBus = bus.Bus.extend({
self.is_master = false;
self.stop_polling();
});
+ if (this.is_master) {
+ setItem('bus.channels', this.channels);
+ setItem('bus.options', this.options);
+ } else {
+ this.channels = getItem('bus.channels', this.channels);
+ this.options = getItem('bus.options', this.options);
+ }
return; // start_polling will be called again on tab registration
}
diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js
index 909a4a0ff5b..cd758790d64 100644
--- a/addons/mail/static/src/js/chat_manager.js
+++ b/addons/mail/static/src/js/chat_manager.js
@@ -272,6 +272,7 @@ function make_channel (data, options) {
if ('direct_partner' in data) {
channel.type = "dm";
channel.name = data.direct_partner[0].name;
+ channel.direct_partner_id = data.direct_partner[0].id;
channel.status = data.direct_partner[0].im_status;
}
return channel;
@@ -399,6 +400,9 @@ function on_notification (notification) {
} else if (model === 'res.partner') {
// channel joined/left, message marked as read/(un)starred, chat open/closed
on_partner_notification(notification[1]);
+ } else if (model === 'bus.presence') {
+ // update presence of users
+ on_presence_notification(notification[1]);
}
}
@@ -530,6 +534,14 @@ function on_chat_session_notification (chat_session) {
}
}
+function on_presence_notification (data) {
+ var dm = _.findWhere(channels, {direct_partner_id: data.id});
+ if (dm) {
+ dm.status = data.im_status;
+ chat_manager.bus.trigger('update_dm_presence', dm);
+ }
+}
+
// Public interface
//----------------------------------------------------------------------------------
var chat_manager = {
diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js
index 1a12ff36912..3d97a35af0e 100644
--- a/addons/mail/static/src/js/client_action.js
+++ b/addons/mail/static/src/js/client_action.js
@@ -52,8 +52,8 @@ var PartnerInviteDialog = Dialog.extend({
width: '100%',
allowClear: true,
multiple: true,
- formatResult: function(item){
- var css_class = "fa-circle" + (item.im_status === 'online' ? "" : "-o");
+ formatResult: function(item) {
+ var css_class = (item.im_status === 'away' ? "fa-clock-o" : "fa-circle" + (item.im_status === 'online' ? "" : "-o"));
return $('').addClass(css_class).text(item.text);
},
query: function (query) {
@@ -135,6 +135,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
this.action = action;
this.options = options || {};
this.channels_scrolltop = {};
+ this.throttled_render_sidebar = _.throttle(this.render_sidebar.bind(this), 100, { leading: false });
},
willStart: function () {
@@ -213,9 +214,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
chat_manager.bus.on('anyone_listening', self, function (channel, query) {
query.is_displayed = query.is_displayed || channel.id === self.channel.id;
});
- chat_manager.bus.on('update_needaction', self, self.render_sidebar);
chat_manager.bus.on('unsubscribe_from_channel', self, self.render_sidebar);
- chat_manager.bus.on('update_channel_unread_counter', self, self.render_sidebar);
+ chat_manager.bus.on('update_needaction', self, self.throttled_render_sidebar);
+ chat_manager.bus.on('update_channel_unread_counter', self, self.throttled_render_sidebar);
+ chat_manager.bus.on('update_dm_presence', self, self.throttled_render_sidebar);
});
},
diff --git a/addons/mail/static/src/xml/client_action.xml b/addons/mail/static/src/xml/client_action.xml
index 11b32ee0e8d..bb29d8cca27 100644
--- a/addons/mail/static/src/xml/client_action.xml
+++ b/addons/mail/static/src/xml/client_action.xml
@@ -77,7 +77,7 @@
-
+
#