[MERGE] forward port of branch 8.0 up to 591e329
[odoo/odoo.git] / addons / im_chat / im_chat.py
index e69c4e1..9ad3349 100644 (file)
@@ -5,10 +5,11 @@ import logging
 import time
 import uuid
 import random
-
+import re
 import simplejson
-
 import openerp
+import cgi
+
 from openerp.http import request
 from openerp.osv import osv, fields
 from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
@@ -133,6 +134,34 @@ class im_chat_session(osv.Model):
                 user = self.pool['res.users'].read(cr, uid, user_id, ['name'], context=context)
                 self.pool["im_chat.message"].post(cr, uid, uid, session.uuid, "meta", user['name'] + " joined the conversation.", context=context)
 
+    def remove_user(self, cr, uid, session_id, context=None):
+        """ private implementation of removing a user from a given session (and notify the other people) """
+        session = self.browse(cr, openerp.SUPERUSER_ID, session_id, context=context)
+        # send a message to the conversation
+        user = self.pool['res.users'].read(cr, uid, uid, ['name'], context=context)
+        self.pool["im_chat.message"].post(cr, uid, uid, session.uuid, "meta", user['name'] + " left the conversation.", context=context)
+        # close his session state, and remove the user from session
+        self.update_state(cr, uid, session.uuid, 'closed', context=None)
+        self.write(cr, uid, [session.id], {"user_ids": [(3, uid)]}, context=context)
+        # notify the all the channel users and anonymous channel
+        notifications = []
+        for channel_user_id in session.user_ids:
+            info = self.session_info(cr, channel_user_id.id, [session.id], context=context)
+            notifications.append([(cr.dbname, 'im_chat.session', channel_user_id.id), info])
+        # anonymous are not notified when a new user left : cannot exec session_info as uid = None
+        info = self.session_info(cr, openerp.SUPERUSER_ID, [session.id], context=context)
+        notifications.append([session.uuid, info])
+        self.pool['bus.bus'].sendmany(cr, uid, notifications)
+
+    def quit_user(self, cr, uid, uuid, context=None):
+        """ action of leaving a given session """
+        sids = self.search(cr, uid, [('uuid', '=', uuid)], context=context, limit=1)
+        for session in self.browse(cr, openerp.SUPERUSER_ID, sids, context=context):
+            if uid and uid in [u.id for u in session.user_ids] and len(session.user_ids) > 2:
+                self.remove_user(cr, uid, session.id, context=context)
+                return True
+            return False
+
     def get_image(self, cr, uid, uuid, user_id, context=None):
         """ get the avatar of a user in the given session """
         #default image
@@ -165,6 +194,20 @@ class im_chat_message(osv.Model):
         'type' : 'message',
     }
 
+    def _escape_keep_url(self, message):
+        """ escape the message and transform the url into clickable link """
+        safe_message = ""
+        first = 0
+        last = 0
+        for m in re.finditer('(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?', message):
+            last = m.start()
+            safe_message += cgi.escape(message[first:last])
+            safe_message += '<a href="%s" target="_blank">%s</a>' % (cgi.escape(m.group(0)), m.group(0))
+            first = m.end()
+            last = m.end()
+        safe_message += cgi.escape(message[last:])
+        return safe_message
+
     def init_messages(self, cr, uid, context=None):
         """ get unread messages and old messages received less than AWAY_TIMER
             ago and the session_info for open or folded window
@@ -210,7 +253,9 @@ class im_chat_message(osv.Model):
         session_ids = Session.search(cr, uid, [('uuid','=',uuid)], context=context)
         notifications = []
         for session in Session.browse(cr, uid, session_ids, context=context):
-            # build the new message
+            # build and escape the new message
+            message_content = self._escape_keep_url(message_content)
+            message_content = self.pool['im_chat.shortcode'].replace_shortcode(cr, uid, message_content, context=context)
             vals = {
                 "from_id": from_uid,
                 "to_id": session.id,
@@ -238,6 +283,24 @@ class im_chat_message(osv.Model):
         return False
 
 
+class im_chat_shortcode(osv.Model):
+    """ Message shortcuts """
+    _name = "im_chat.shortcode"
+
+    _columns = {
+        'source' : fields.char('Shortcut', required=True, select=True, help="The shortcut which must be replace in the Chat Messages"),
+        'substitution' : fields.char('Substitution', required=True, select=True, help="The html code replacing the shortcut"),
+        'description' : fields.char('Description'),
+    }
+
+    def replace_shortcode(self, cr, uid, message, context=None):
+        ids = self.search(cr, uid, [], context=context)
+        for shortcode in self.browse(cr, uid, ids, context=context):
+            regex = "(?:^|\s)(%s)(?:\s|$)" % re.escape(shortcode.source)
+            message = re.sub(regex, " " + shortcode.substitution + " ", message)
+        return message
+
+
 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