summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorJérome Maes <jem@openerp.com>2015-06-01 16:37:07 +0200
committerAaron Bohy <aab@odoo.com>2015-09-01 20:16:08 +0200
commit4af96310721237d3640bbd162ff9ea84e627028d (patch)
tree2deb74fa477c9ea33b1a0a65199b96e2d6e5f02b /addons
parent5907be1f335dc21934619a34fc928a19e7885211 (diff)
[MOV] mail, im_chat : move files and code from im_chat to mail and bus
- im_chat.session will be replaced by mail.channel - im_chat.shortode is renamed into mail.shortcode - im_chat.presence is moved to bus module - js and controller code is moved from im_chat to mail module This commit only move files, and modify manifests, bundles, ... The code will be adapt in the next commits.
Diffstat (limited to 'addons')
-rw-r--r--addons/bus/controllers/main.py3
-rw-r--r--addons/bus/models/__init__.py2
-rw-r--r--addons/bus/models/bus_presence.py87
-rw-r--r--addons/bus/models/res_users.py (renamed from addons/im_chat/models/res_users.py)0
-rw-r--r--addons/bus/security/ir.model.access.csv4
-rw-r--r--addons/bus/static/src/js/bus.js16
-rw-r--r--addons/mail/__openerp__.py3
-rw-r--r--addons/mail/controllers/__init__.py2
-rw-r--r--addons/mail/controllers/bus.py (renamed from addons/im_chat/controllers/main.py)0
-rw-r--r--addons/mail/data/mail_shortcode_data.xml (renamed from addons/im_chat/data/im_chat_session_data.xml)0
-rw-r--r--addons/mail/models/__init__.py3
-rw-r--r--addons/mail/models/mail_channel.py138
-rw-r--r--addons/mail/models/mail_shortcode.py48
-rw-r--r--addons/mail/static/src/audio/purr.mp3 (renamed from addons/im_chat/static/src/audio/purr.mp3)bin85262 -> 85262 bytes
-rw-r--r--addons/mail/static/src/audio/purr.ogg (renamed from addons/im_chat/static/src/audio/purr.ogg)bin47390 -> 47390 bytes
-rw-r--r--addons/mail/static/src/audio/ting.mp3 (renamed from addons/im_chat/static/src/audio/ting.mp3)bin10866 -> 10866 bytes
-rw-r--r--addons/mail/static/src/audio/ting.ogg (renamed from addons/im_chat/static/src/audio/ting.ogg)bin14654 -> 14654 bytes
-rw-r--r--addons/mail/static/src/css/mail_chat_backend.css (renamed from addons/im_chat/static/src/css/im_chat_backend.css)0
-rw-r--r--addons/mail/static/src/css/mail_chat_common.css (renamed from addons/im_chat/static/src/css/im_chat_common.css)0
-rw-r--r--addons/mail/static/src/img/smiley/avatar.jpg (renamed from addons/im_chat/static/src/img/avatar.jpg)bin6462 -> 6462 bytes
-rw-r--r--addons/mail/static/src/img/smiley/beer.png (renamed from addons/im_chat/static/src/img/beer.png)bin6097 -> 6097 bytes
-rw-r--r--addons/mail/static/src/img/smiley/boar.png (renamed from addons/im_chat/static/src/img/boar.png)bin4798 -> 4798 bytes
-rw-r--r--addons/mail/static/src/img/smiley/cat.png (renamed from addons/im_chat/static/src/img/cat.png)bin6083 -> 6083 bytes
-rw-r--r--addons/mail/static/src/img/smiley/confused.png (renamed from addons/im_chat/static/src/img/confused.png)bin4633 -> 4633 bytes
-rw-r--r--addons/mail/static/src/img/smiley/cookie.png (renamed from addons/im_chat/static/src/img/cookie.png)bin8077 -> 8077 bytes
-rw-r--r--addons/mail/static/src/img/smiley/ghost.png (renamed from addons/im_chat/static/src/img/ghost.png)bin4513 -> 4513 bytes
-rw-r--r--addons/mail/static/src/img/smiley/green.png (renamed from addons/im_chat/static/src/img/green.png)bin1239 -> 1239 bytes
-rw-r--r--addons/mail/static/src/img/smiley/heart.png (renamed from addons/im_chat/static/src/img/heart.png)bin3302 -> 3302 bytes
-rw-r--r--addons/mail/static/src/img/smiley/laugh.png (renamed from addons/im_chat/static/src/img/laugh.png)bin6347 -> 6347 bytes
-rw-r--r--addons/mail/static/src/img/smiley/man.png (renamed from addons/im_chat/static/src/img/man.png)bin5985 -> 5985 bytes
-rw-r--r--addons/mail/static/src/img/smiley/man_with_turban.png (renamed from addons/im_chat/static/src/img/man_with_turban.png)bin6526 -> 6526 bytes
-rw-r--r--addons/mail/static/src/img/smiley/musti.png (renamed from addons/im_chat/static/src/img/musti.png)bin1017 -> 1017 bytes
-rw-r--r--addons/mail/static/src/img/smiley/neutral.png (renamed from addons/im_chat/static/src/img/neutral.png)bin4843 -> 4843 bytes
-rw-r--r--addons/mail/static/src/img/smiley/open_mouth.png (renamed from addons/im_chat/static/src/img/open_mouth.png)bin4519 -> 4519 bytes
-rw-r--r--addons/mail/static/src/img/smiley/pinky.png (renamed from addons/im_chat/static/src/img/pinky.png)bin2978 -> 2978 bytes
-rw-r--r--addons/mail/static/src/img/smiley/poop.png (renamed from addons/im_chat/static/src/img/poop.png)bin4754 -> 4754 bytes
-rw-r--r--addons/mail/static/src/img/smiley/postal_horn.png (renamed from addons/im_chat/static/src/img/postal_horn.png)bin4815 -> 4815 bytes
-rw-r--r--addons/mail/static/src/img/smiley/sad.png (renamed from addons/im_chat/static/src/img/sad.png)bin4764 -> 4764 bytes
-rw-r--r--addons/mail/static/src/img/smiley/scream.png (renamed from addons/im_chat/static/src/img/scream.png)bin6503 -> 6503 bytes
-rw-r--r--addons/mail/static/src/img/smiley/smile.png (renamed from addons/im_chat/static/src/img/smile.png)bin5890 -> 5890 bytes
-rw-r--r--addons/mail/static/src/img/smiley/stuck_out_tongue.png (renamed from addons/im_chat/static/src/img/stuck_out_tongue.png)bin5955 -> 5955 bytes
-rw-r--r--addons/mail/static/src/img/smiley/thumbsdown.png (renamed from addons/im_chat/static/src/img/thumbsdown.png)bin5110 -> 5110 bytes
-rw-r--r--addons/mail/static/src/img/smiley/thumbsup.png (renamed from addons/im_chat/static/src/img/thumbsup.png)bin5045 -> 5045 bytes
-rw-r--r--addons/mail/static/src/img/smiley/trollface.png (renamed from addons/im_chat/static/src/img/trollface.png)bin2828 -> 2828 bytes
-rw-r--r--addons/mail/static/src/img/smiley/wink.png (renamed from addons/im_chat/static/src/img/wink.png)bin5253 -> 5253 bytes
-rw-r--r--addons/mail/static/src/img/smiley/yellow.png (renamed from addons/im_chat/static/src/img/yellow.png)bin1235 -> 1235 bytes
-rw-r--r--addons/mail/static/src/img/smiley/yum.png (renamed from addons/im_chat/static/src/img/yum.png)bin5886 -> 5886 bytes
-rw-r--r--addons/mail/static/src/js/mail_chat_backend.js (renamed from addons/im_chat/static/src/js/im_chat_backend.js)0
-rw-r--r--addons/mail/static/src/js/mail_chat_common.js (renamed from addons/im_chat/static/src/js/im_chat_common.js)0
-rw-r--r--addons/mail/static/src/xml/mail_chat_common.xml (renamed from addons/im_chat/static/src/xml/im_chat.xml)0
-rw-r--r--addons/mail/views/mail_shortcode_views.xml (renamed from addons/im_chat/views/im_chat_session_views.xml)0
-rw-r--r--addons/mail/views/mail_templates.xml5
52 files changed, 309 insertions, 2 deletions
diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py
index ee9902ec983..3da386e80b2 100644
--- a/addons/bus/controllers/main.py
+++ b/addons/bus/controllers/main.py
@@ -20,6 +20,9 @@ class BusController(openerp.http.Controller):
# override to add channels
def _poll(self, dbname, channels, last, options):
+ # update the user presence
+ if request.session.uid and 'im_presence' in options:
+ request.env['bus.presence'].update(options.get('im_presence'))
request.cr.close()
request._cr = None
return dispatch.poll(dbname, channels, last)
diff --git a/addons/bus/models/__init__.py b/addons/bus/models/__init__.py
index c1c365d98c0..208eb0c6c81 100644
--- a/addons/bus/models/__init__.py
+++ b/addons/bus/models/__init__.py
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
import bus
+import bus_presence
+import res_users
diff --git a/addons/bus/models/bus_presence.py b/addons/bus/models/bus_presence.py
new file mode 100644
index 00000000000..d89cfc9a104
--- /dev/null
+++ b/addons/bus/models/bus_presence.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+import datetime
+import random
+import time
+
+from openerp import api, fields, models
+from openerp import tools
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+
+from openerp.addons.bus.models.bus import TIMEOUT
+
+DISCONNECTION_TIMER = TIMEOUT + 5
+AWAY_TIMER = 600 # 10 minutes
+
+
+class ImChatPresence(models.Model):
+ """ User Chat Presence
+ Its status is 'online', 'away' or 'offline'. This model should be a one2one, but is not
+ attached to res_users to avoid database concurrence errors. Since the 'update' method is executed
+ at each poll, if the user have multiple opened tabs, concurrence errors can happend, but are 'muted-logged'.
+ """
+
+ _name = 'im_chat.presence'
+ _description = 'User Chat Presence'
+
+ _sql_constraints = [('im_chat_user_status_unique', 'unique(user_id)', 'A user can only have one IM status.')]
+
+ user_id = fields.Many2one('res.users', 'Users', required=True, index=True, ondelete='cascade')
+ last_poll = fields.Datetime('Last Poll', default=lambda self: fields.Datetime.now())
+ last_presence = fields.Datetime('Last Presence', default=lambda self: fields.Datetime.now())
+ status = fields.Selection([('online', 'Online'), ('away', 'Away'), ('offline', 'Offline')], 'IM Status', default='offline')
+
+
+ @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.
+ :param user_presence : True, if the user (self._uid) is still detected using its browser.
+ :type user_presence : boolean
+ """
+ presence = self.search([('user_id', '=', self._uid)], limit=1)
+ # set the default values
+ send_notification = True
+ values = {
+ 'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
+ 'status' : presence and presence.status or 'offline'
+ }
+ # update the user or a create a new one
+ if not presence: # create a new presence for the user
+ values['status'] = 'online'
+ 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'
+ 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)
+ # 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, 'im_chat.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()
+ 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'})
+ notifications = []
+ for presence in presences:
+ notifications.append([(self._cr.dbname, 'im_chat.presence'), {'id': presence.user_id.id, 'im_status': presence.status}])
+ self.env['bus.bus'].sendmany(notifications)
diff --git a/addons/im_chat/models/res_users.py b/addons/bus/models/res_users.py
index 5e9d664b6a8..5e9d664b6a8 100644
--- a/addons/im_chat/models/res_users.py
+++ b/addons/bus/models/res_users.py
diff --git a/addons/bus/security/ir.model.access.csv b/addons/bus/security/ir.model.access.csv
index 8a679b74f81..4a626a0eb5a 100644
--- a/addons/bus/security/ir.model.access.csv
+++ b/addons/bus/security/ir.model.access.csv
@@ -1,2 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_bus_bus,bus.bus public,model_bus_bus,,0,0,0,0 \ No newline at end of file
+access_bus_bus,bus.bus public,model_bus_bus,,0,0,0,0
+access_bus_presence,bus.presence,model_bus_presence,base.group_user,1,1,1,1
+access_bus_presence_portal,bus.presence,model_bus_presence,base.group_portal,1,1,1,1
diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js
index b0756132012..3c828668308 100644
--- a/addons/bus/static/src/js/bus.js
+++ b/addons/bus/static/src/js/bus.js
@@ -9,12 +9,21 @@ bus.ERROR_DELAY = 10000;
bus.Bus = Widget.extend({
init: function(){
+ var self = this;
this._super();
this.options = {};
this.activated = false;
this.channels = [];
this.last = 0;
this.stop = false;
+
+ // bus presence
+ this.set("window_focus", true);
+ this.on("change:window_focus", self, function(e) {
+ self.options.im_presence = self.get("window_focus");
+ });
+ $(window).on("focus", _.bind(this.window_focus, this));
+ $(window).on("blur", _.bind(this.window_blur, this));
},
start_polling: function(){
if(!this.activated){
@@ -57,6 +66,13 @@ bus.Bus = Widget.extend({
delete_channel: function(channel){
this.channels = _.without(this.channels, channel);
},
+ // bus presence : window focus/unfocus
+ window_focus: function() {
+ this.set("window_focus", true);
+ },
+ window_blur: function() {
+ this.set("window_focus", false);
+ },
});
// singleton
diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py
index e1f124c77c5..1e9149dc4fb 100644
--- a/addons/mail/__openerp__.py
+++ b/addons/mail/__openerp__.py
@@ -36,9 +36,11 @@ Main Features
'views/mail_followers_views.xml',
'views/mail_thread_views.xml',
'views/mail_channel_views.xml',
+ 'views/mail_shortcode_views.xml',
'views/res_config_views.xml',
'data/mail_data.xml',
'data/mail_channel_data.xml',
+ 'data/mail_shortcode.xml',
'security/mail_security.xml',
'security/ir.model.access.csv',
'views/mail_alias_views.xml',
@@ -58,6 +60,7 @@ Main Features
'application': True,
'qweb': [
'static/src/xml/mail.xml',
+ 'static/src/xml/mail_chat_common.xml',
'static/src/xml/mail_followers.xml',
'static/src/xml/announcement.xml',
],
diff --git a/addons/mail/controllers/__init__.py b/addons/mail/controllers/__init__.py
index 8ee9bae18d9..c59e37b30c8 100644
--- a/addons/mail/controllers/__init__.py
+++ b/addons/mail/controllers/__init__.py
@@ -1 +1,3 @@
+# -*- coding: utf-8 -*
import main
+import bus
diff --git a/addons/im_chat/controllers/main.py b/addons/mail/controllers/bus.py
index 35ede172690..35ede172690 100644
--- a/addons/im_chat/controllers/main.py
+++ b/addons/mail/controllers/bus.py
diff --git a/addons/im_chat/data/im_chat_session_data.xml b/addons/mail/data/mail_shortcode_data.xml
index bd77ecbf8da..bd77ecbf8da 100644
--- a/addons/im_chat/data/im_chat_session_data.xml
+++ b/addons/mail/data/mail_shortcode_data.xml
diff --git a/addons/mail/models/__init__.py b/addons/mail/models/__init__.py
index ca72296a6fb..d24e5295cf0 100644
--- a/addons/mail/models/__init__.py
+++ b/addons/mail/models/__init__.py
@@ -9,9 +9,10 @@ import mail_mail
import mail_thread
import mail_channel
import mail_template
+import mail_shortcode
import res_partner
import res_users
import res_config
import update
import ir_actions
-import html2text \ No newline at end of file
+import html2text
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index f118133ddf9..635a4999cce 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -14,6 +14,8 @@ class ChannelPartner(models.Model):
partner_id = fields.Many2one('res.partner', string='Recipient')
channel_id = fields.Many2one('mail.channel', string='Channel')
seen_message_id = fields.Many2one('mail.message', string='Last Seen')
+ state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Status', default='open')
+ seen_datetime = fields.Datetime('Last Seen Datetime')
class Channel(models.Model):
@@ -161,3 +163,139 @@ class Channel(models.Model):
headers['X-Forge-To'] = list_to
res['headers'] = repr(headers)
return res
+
+
+ # --------------------------
+ # Session Methods
+ # --------------------------
+ @api.multi
+ def session_info(self):
+ """ Get the header of the current session
+ :returns the values of the session
+ :rtype : dict
+ """
+ self.ensure_one()
+ info = {
+ 'uuid': self.uuid,
+ 'users': self.session_user_info(),
+ 'state': 'open',
+ }
+ # add user session state, if available and if user is logged
+ if self._uid:
+ domain = [('user_id', '=', self._uid), ('session_id', 'in', self.ids)]
+ session_state = self.env['im_chat.conversation_state'].search(domain, limit=1)
+ if session_state:
+ info['state'] = session_state.state
+ return info
+
+ @api.multi
+ def session_user_info(self):
+ """ Get the user infos for all the identified user in the session
+ :returns a list of user infos
+ :rtype : list(dict)
+ """
+ self.ensure_one()
+ return self.env['res.users'].browse(self.user_ids.ids).read(['id', 'name', 'im_status'])
+
+ @api.model
+ def session_user_image(self, uuid, user_id):
+ """ Get the avatar of a user in the given session
+ :param uuid : the uuid of the session
+ :param user_id : the user identifier
+ """
+ # get the session
+ image_b64 = False
+ if user_id:
+ session = self.env["im_chat.session"].search([('uuid', '=', uuid)], limit=1)
+ if session and session.sudo(user_id).is_in_session():
+ image_b64 = self.env["res.users"].sudo().browse(user_id).image_small
+ # set default image if not found
+ if not image_b64:
+ image_b64 = 'R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
+ return image_b64
+
+ @api.model
+ def session_get(self, user_to):
+ """ Get the canonical session between 2 users, create it if needed.
+ To reuse an old session, this one must be private, and contains only 2 users.
+ :param user_to : the identifier of the user
+ :returns a session header, or False if the user_to was False
+ :rtype : dict
+ """
+ if user_to:
+ session = self.search([('user_ids', 'in', user_to), ('user_ids', 'in', [self._uid])], limit=1)
+ if not (session and len(session.user_ids) == 2 and session.is_private()):
+ session = self.create({'user_ids': [(6, 0, (user_to, self._uid))]})
+ return session.session_info()
+
+ # --------------------------
+ # Utils Methods
+ # --------------------------
+ @api.multi
+ def is_private(self):
+ """ Return true if the session is private between identified users (no external messages).
+ The only way to do that is to check if there is a message without author (from_id is False).
+ """
+ self.ensure_one()
+ message_ids = self.env["im_chat.message"].search([('to_id', 'in', self.ids), ('from_id', '=', None)])
+ return len(message_ids) == 0
+
+ @api.multi
+ def is_in_session(self):
+ """ Return True if the current user is in the user_ids of the session. False otherwise.
+ Note : the user need to be logged
+ """
+ self.ensure_one()
+ user_id = self._uid
+ return user_id and user_id in [u.id for u in self.user_ids]
+
+ @api.model
+ def update_state(self, uuid, state=None):
+ """ Update the fold_state of the given session. In order to syncronize web browser
+ tabs, the change will be broadcast to himself (the current user channel).
+ Note : the user need to be logged
+ :param status : the new status of the session for the current user.
+ """
+ domain = [('user_id', '=', self._uid), ('session_id.uuid', '=', uuid)]
+ for session_state in self.env['im_chat.conversation_state'].search(domain):
+ if not state:
+ state = session_state.state
+ if session_state.state == 'open':
+ state = 'folded'
+ else:
+ state = 'open'
+ session_state.write({'state': state})
+ self.env['bus.bus'].sendone((self._cr.dbname, 'im_chat.session', self._uid), session_state.session_id.session_info())
+
+ @api.model
+ def get_init_notifications(self):
+ """ Get unread messages and old messages received less than AWAY_TIMER
+ ago and the session_info for open or folded window
+ Note : the user need to be logged
+ """
+ # get the message since the AWAY_TIMER
+ threshold = datetime.datetime.now() - datetime.timedelta(seconds=AWAY_TIMER)
+ threshold = threshold.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+ domain = [('to_id.user_ids', 'in', [self._uid]), ('create_date', '>', threshold)]
+
+ # get the message since the last poll of the user
+ presence = self.env['im_chat.presence'].search([('user_id', '=', self._uid)], limit=1)
+ if presence:
+ domain.append(('create_date', '>', presence.last_poll))
+ messages = self.search_read(domain, ['from_id', 'to_id', 'create_date', 'type', 'message'], order='id asc')
+
+ # get the session of the messages and the not-closed ones
+ session_ids = [m['to_id'][0] for m in messages]
+ domain = [('user_id', '=', self._uid), '|', ('state', '!=', 'closed'), ('session_id', 'in', session_ids)]
+ session_states = self.env['im_chat.conversation_state'].search(domain)
+ # re-open the session where a message have been recieved recently
+ session_states.filtered(lambda r: r.state == 'closed').write({'state': 'folded'})
+
+ # create the notifications (session infos first, then messages)
+ notifications = []
+ for state in session_states:
+ session_infos = state.session_id.session_info()
+ notifications.append([(self._cr.dbname, 'im_chat.session', self._uid), session_infos])
+ for message in messages:
+ notifications.append([(self._cr.dbname, 'im_chat.session', self._uid), message])
+ return notifications
diff --git a/addons/mail/models/mail_shortcode.py b/addons/mail/models/mail_shortcode.py
new file mode 100644
index 00000000000..42627e5abe5
--- /dev/null
+++ b/addons/mail/models/mail_shortcode.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+import cgi
+import re
+
+from openerp import api, fields, models
+
+
+class ImChatShortcode(models.Model):
+ """ Shortcode
+ Canned Responses, allowing the user to defined shortcuts in its chat message.
+ These shortcode are globals and are available for every user. Smiley use this mecanism.
+ """
+
+ _name = 'im_chat.shortcode'
+ _description = 'Canned Response / Shortcode'
+
+ source = fields.Char('Shortcut', required=True, index=True, help="The shortcut which must be replace in the Chat Messages")
+ substitution = fields.Char('Substitution', required=True, index=True, help="The excaped html code replacing the shortcut")
+ description = fields.Char('Description')
+
+ @api.model
+ def create(self, values):
+ if values.get('substitution'):
+ values['substitution'] = self._sanitize_shorcode(values['substitution'])
+ return super(ImChatShortcode, self).create(values)
+
+ @api.multi
+ def write(self, values):
+ if values.get('substitution'):
+ values['substitution'] = self._sanitize_shorcode(values['substitution'])
+ return super(ImChatShortcode, self).write(values)
+
+ def _sanitize_shorcode(self, substitution):
+ """ Sanitize the shortcode substitution :
+ - HTML substitution : only allow the img tag (smiley)
+ - escape other substitutions to avoid XSS
+ """
+ is_img_tag = re.match(r'''^<img\s+src=('|")([^'"]*)\1\s*/?>$''', substitution, re.M|re.I)
+ if is_img_tag:
+ return substitution
+ return cgi.escape(substitution)
+
+ @api.model
+ def replace_shortcode(self, message):
+ for shortcode in self.search([]):
+ regex = '(?:^|\s)(%s)(?:\s|$)' % re.escape(shortcode.source)
+ message = re.sub(regex, " " + shortcode.substitution + " ", message)
+ return message
diff --git a/addons/im_chat/static/src/audio/purr.mp3 b/addons/mail/static/src/audio/purr.mp3
index 849d303c3b1..849d303c3b1 100644
--- a/addons/im_chat/static/src/audio/purr.mp3
+++ b/addons/mail/static/src/audio/purr.mp3
Binary files differ
diff --git a/addons/im_chat/static/src/audio/purr.ogg b/addons/mail/static/src/audio/purr.ogg
index 2ddc77537f1..2ddc77537f1 100644
--- a/addons/im_chat/static/src/audio/purr.ogg
+++ b/addons/mail/static/src/audio/purr.ogg
Binary files differ
diff --git a/addons/im_chat/static/src/audio/ting.mp3 b/addons/mail/static/src/audio/ting.mp3
index fd29bf599d6..fd29bf599d6 100644
--- a/addons/im_chat/static/src/audio/ting.mp3
+++ b/addons/mail/static/src/audio/ting.mp3
Binary files differ
diff --git a/addons/im_chat/static/src/audio/ting.ogg b/addons/mail/static/src/audio/ting.ogg
index cc8c0f01eff..cc8c0f01eff 100644
--- a/addons/im_chat/static/src/audio/ting.ogg
+++ b/addons/mail/static/src/audio/ting.ogg
Binary files differ
diff --git a/addons/im_chat/static/src/css/im_chat_backend.css b/addons/mail/static/src/css/mail_chat_backend.css
index d01577eb56a..d01577eb56a 100644
--- a/addons/im_chat/static/src/css/im_chat_backend.css
+++ b/addons/mail/static/src/css/mail_chat_backend.css
diff --git a/addons/im_chat/static/src/css/im_chat_common.css b/addons/mail/static/src/css/mail_chat_common.css
index 1ad2981eb41..1ad2981eb41 100644
--- a/addons/im_chat/static/src/css/im_chat_common.css
+++ b/addons/mail/static/src/css/mail_chat_common.css
diff --git a/addons/im_chat/static/src/img/avatar.jpg b/addons/mail/static/src/img/smiley/avatar.jpg
index 7168794022e..7168794022e 100644
--- a/addons/im_chat/static/src/img/avatar.jpg
+++ b/addons/mail/static/src/img/smiley/avatar.jpg
Binary files differ
diff --git a/addons/im_chat/static/src/img/beer.png b/addons/mail/static/src/img/smiley/beer.png
index cd78bed7440..cd78bed7440 100644
--- a/addons/im_chat/static/src/img/beer.png
+++ b/addons/mail/static/src/img/smiley/beer.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/boar.png b/addons/mail/static/src/img/smiley/boar.png
index 3ef50325016..3ef50325016 100644
--- a/addons/im_chat/static/src/img/boar.png
+++ b/addons/mail/static/src/img/smiley/boar.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/cat.png b/addons/mail/static/src/img/smiley/cat.png
index dbf1b0276ab..dbf1b0276ab 100644
--- a/addons/im_chat/static/src/img/cat.png
+++ b/addons/mail/static/src/img/smiley/cat.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/confused.png b/addons/mail/static/src/img/smiley/confused.png
index 8dc494db083..8dc494db083 100644
--- a/addons/im_chat/static/src/img/confused.png
+++ b/addons/mail/static/src/img/smiley/confused.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/cookie.png b/addons/mail/static/src/img/smiley/cookie.png
index 177d0f9ad86..177d0f9ad86 100644
--- a/addons/im_chat/static/src/img/cookie.png
+++ b/addons/mail/static/src/img/smiley/cookie.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/ghost.png b/addons/mail/static/src/img/smiley/ghost.png
index 671dd0c9e2e..671dd0c9e2e 100644
--- a/addons/im_chat/static/src/img/ghost.png
+++ b/addons/mail/static/src/img/smiley/ghost.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/green.png b/addons/mail/static/src/img/smiley/green.png
index 0700a5ef71b..0700a5ef71b 100644
--- a/addons/im_chat/static/src/img/green.png
+++ b/addons/mail/static/src/img/smiley/green.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/heart.png b/addons/mail/static/src/img/smiley/heart.png
index 7d7790ce4df..7d7790ce4df 100644
--- a/addons/im_chat/static/src/img/heart.png
+++ b/addons/mail/static/src/img/smiley/heart.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/laugh.png b/addons/mail/static/src/img/smiley/laugh.png
index 11c91eb22e6..11c91eb22e6 100644
--- a/addons/im_chat/static/src/img/laugh.png
+++ b/addons/mail/static/src/img/smiley/laugh.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/man.png b/addons/mail/static/src/img/smiley/man.png
index c4a4217f4d4..c4a4217f4d4 100644
--- a/addons/im_chat/static/src/img/man.png
+++ b/addons/mail/static/src/img/smiley/man.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/man_with_turban.png b/addons/mail/static/src/img/smiley/man_with_turban.png
index 8a0bda1ca77..8a0bda1ca77 100644
--- a/addons/im_chat/static/src/img/man_with_turban.png
+++ b/addons/mail/static/src/img/smiley/man_with_turban.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/musti.png b/addons/mail/static/src/img/smiley/musti.png
index f6843ddf95c..f6843ddf95c 100644
--- a/addons/im_chat/static/src/img/musti.png
+++ b/addons/mail/static/src/img/smiley/musti.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/neutral.png b/addons/mail/static/src/img/smiley/neutral.png
index 682a1ba066d..682a1ba066d 100644
--- a/addons/im_chat/static/src/img/neutral.png
+++ b/addons/mail/static/src/img/smiley/neutral.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/open_mouth.png b/addons/mail/static/src/img/smiley/open_mouth.png
index daf914274a6..daf914274a6 100644
--- a/addons/im_chat/static/src/img/open_mouth.png
+++ b/addons/mail/static/src/img/smiley/open_mouth.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/pinky.png b/addons/mail/static/src/img/smiley/pinky.png
index 4466946601c..4466946601c 100644
--- a/addons/im_chat/static/src/img/pinky.png
+++ b/addons/mail/static/src/img/smiley/pinky.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/poop.png b/addons/mail/static/src/img/smiley/poop.png
index 73a4dc84008..73a4dc84008 100644
--- a/addons/im_chat/static/src/img/poop.png
+++ b/addons/mail/static/src/img/smiley/poop.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/postal_horn.png b/addons/mail/static/src/img/smiley/postal_horn.png
index 5b7ed85d4fc..5b7ed85d4fc 100644
--- a/addons/im_chat/static/src/img/postal_horn.png
+++ b/addons/mail/static/src/img/smiley/postal_horn.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/sad.png b/addons/mail/static/src/img/smiley/sad.png
index 82552008719..82552008719 100644
--- a/addons/im_chat/static/src/img/sad.png
+++ b/addons/mail/static/src/img/smiley/sad.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/scream.png b/addons/mail/static/src/img/smiley/scream.png
index 76bfc6b8a65..76bfc6b8a65 100644
--- a/addons/im_chat/static/src/img/scream.png
+++ b/addons/mail/static/src/img/smiley/scream.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/smile.png b/addons/mail/static/src/img/smiley/smile.png
index 81a83968996..81a83968996 100644
--- a/addons/im_chat/static/src/img/smile.png
+++ b/addons/mail/static/src/img/smiley/smile.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/stuck_out_tongue.png b/addons/mail/static/src/img/smiley/stuck_out_tongue.png
index ea9aeb99e29..ea9aeb99e29 100644
--- a/addons/im_chat/static/src/img/stuck_out_tongue.png
+++ b/addons/mail/static/src/img/smiley/stuck_out_tongue.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/thumbsdown.png b/addons/mail/static/src/img/smiley/thumbsdown.png
index 74a5005c893..74a5005c893 100644
--- a/addons/im_chat/static/src/img/thumbsdown.png
+++ b/addons/mail/static/src/img/smiley/thumbsdown.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/thumbsup.png b/addons/mail/static/src/img/smiley/thumbsup.png
index 116388fc3e2..116388fc3e2 100644
--- a/addons/im_chat/static/src/img/thumbsup.png
+++ b/addons/mail/static/src/img/smiley/thumbsup.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/trollface.png b/addons/mail/static/src/img/smiley/trollface.png
index eb222e2ad69..eb222e2ad69 100644
--- a/addons/im_chat/static/src/img/trollface.png
+++ b/addons/mail/static/src/img/smiley/trollface.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/wink.png b/addons/mail/static/src/img/smiley/wink.png
index 756766dd3e9..756766dd3e9 100644
--- a/addons/im_chat/static/src/img/wink.png
+++ b/addons/mail/static/src/img/smiley/wink.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/yellow.png b/addons/mail/static/src/img/smiley/yellow.png
index 726570e518f..726570e518f 100644
--- a/addons/im_chat/static/src/img/yellow.png
+++ b/addons/mail/static/src/img/smiley/yellow.png
Binary files differ
diff --git a/addons/im_chat/static/src/img/yum.png b/addons/mail/static/src/img/smiley/yum.png
index fc39637ecd8..fc39637ecd8 100644
--- a/addons/im_chat/static/src/img/yum.png
+++ b/addons/mail/static/src/img/smiley/yum.png
Binary files differ
diff --git a/addons/im_chat/static/src/js/im_chat_backend.js b/addons/mail/static/src/js/mail_chat_backend.js
index cd1cc00fdf2..cd1cc00fdf2 100644
--- a/addons/im_chat/static/src/js/im_chat_backend.js
+++ b/addons/mail/static/src/js/mail_chat_backend.js
diff --git a/addons/im_chat/static/src/js/im_chat_common.js b/addons/mail/static/src/js/mail_chat_common.js
index 0aaa0e68d16..0aaa0e68d16 100644
--- a/addons/im_chat/static/src/js/im_chat_common.js
+++ b/addons/mail/static/src/js/mail_chat_common.js
diff --git a/addons/im_chat/static/src/xml/im_chat.xml b/addons/mail/static/src/xml/mail_chat_common.xml
index eb479863b3e..eb479863b3e 100644
--- a/addons/im_chat/static/src/xml/im_chat.xml
+++ b/addons/mail/static/src/xml/mail_chat_common.xml
diff --git a/addons/im_chat/views/im_chat_session_views.xml b/addons/mail/views/mail_shortcode_views.xml
index e281950d102..e281950d102 100644
--- a/addons/im_chat/views/im_chat_session_views.xml
+++ b/addons/mail/views/mail_shortcode_views.xml
diff --git a/addons/mail/views/mail_templates.xml b/addons/mail/views/mail_templates.xml
index 7472cdd39ff..8fa17f867d7 100644
--- a/addons/mail/views/mail_templates.xml
+++ b/addons/mail/views/mail_templates.xml
@@ -11,6 +11,11 @@
<script type="text/javascript" src="/mail/static/src/js/mail_followers.js"></script>
<script type="text/javascript" src="/mail/static/src/js/many2many_tags_email.js"></script>
<script type="text/javascript" src="/mail/static/src/js/announcement.js"></script>
+
+ <link rel="stylesheet" href="/mail/static/src/css/mail_chat_common.css"/>
+ <link rel="stylesheet" href="/mail/static/src/css/mail_chat_backend.css"/>
+ <script type="text/javascript" src="/mail/static/src/js/mail_chat_common.js"></script>
+ <script type="text/javascript" src="/mail/static/src/js/mail_chat_backend.js"></script>
</xpath>
</template>
</data>