summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérome Maes <jem@openerp.com>2015-04-21 15:20:49 +0200
committerJérome Maes <jem@openerp.com>2015-04-28 17:04:16 +0200
commita80219e2928ce4b40831ce6a5c7247bb60e0ddcb (patch)
treeb0dda178f2699d1e970da40ace44c23932924d68
parent7096fee53ce686236b2d235b28095e117f3e2d52 (diff)
[MIG] bus, im_chat, im_livechat, website_livechat : migration to new API.
Migrate the models and controller to new API. Renaming xml id according to convention, renaming openerp tag into odoo tag. Add comment strings and documentations.
-rw-r--r--addons/bus/controllers/main.py3
-rw-r--r--addons/bus/models/bus.py88
-rw-r--r--addons/bus/views/bus.xml4
-rw-r--r--addons/im_chat/controllers/main.py68
-rw-r--r--addons/im_chat/data/im_chat_session_data.xml6
-rw-r--r--addons/im_chat/models/im_chat_session.py655
-rw-r--r--addons/im_chat/models/res_users.py63
-rw-r--r--addons/im_chat/security/im_chat_session_security.xml10
-rw-r--r--addons/im_chat/views/im_chat_session_templates.xml5
-rw-r--r--addons/im_chat/views/im_chat_session_views.xml11
-rw-r--r--addons/im_livechat/controllers/main.py45
-rw-r--r--addons/im_livechat/data/im_livechat_channel_data.xml21
-rw-r--r--addons/im_livechat/data/im_livechat_channel_demo.xml18
-rw-r--r--addons/im_livechat/models/im_chat_session.py109
-rw-r--r--addons/im_livechat/models/im_livechat_channel.py446
-rw-r--r--addons/im_livechat/report/__init__.py3
-rw-r--r--addons/im_livechat/report/im_livechat_report.py39
-rw-r--r--addons/im_livechat/report/im_livechat_report_views.xml25
-rw-r--r--addons/im_livechat/security/im_livechat_channel_security.xml18
-rw-r--r--addons/im_livechat/security/ir.model.access.csv14
-rw-r--r--addons/im_livechat/static/src/js/im_livechat.js4
-rw-r--r--addons/im_livechat/views/im_chat_session_views.xml38
-rw-r--r--addons/im_livechat/views/im_livechat_channel_templates.xml9
-rw-r--r--addons/im_livechat/views/im_livechat_channel_views.xml56
-rw-r--r--addons/website_livechat/__init__.py1
-rw-r--r--addons/website_livechat/__openerp__.py1
-rw-r--r--addons/website_livechat/controllers/__init__.py1
-rw-r--r--addons/website_livechat/controllers/main.py4
-rw-r--r--addons/website_livechat/models/__init__.py3
-rw-r--r--addons/website_livechat/models/im_livechat.py3
-rw-r--r--addons/website_livechat/models/website.py17
-rw-r--r--addons/website_livechat/security/website_livechat.xml2
-rw-r--r--addons/website_livechat/views/website_livechat_view.xml4
-rw-r--r--addons/website_livechat/website_livechat_data.xml8
34 files changed, 932 insertions, 870 deletions
diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py
index 49f1b97ae77..ee9902ec983 100644
--- a/addons/bus/controllers/main.py
+++ b/addons/bus/controllers/main.py
@@ -16,8 +16,7 @@ class BusController(openerp.http.Controller):
def send(self, channel, message):
if not isinstance(channel, basestring):
raise Exception("bus.Bus only string channels are allowed.")
- registry, cr, uid = request.registry, request.cr, request.session.uid
- return registry['bus.bus'].sendone(cr, uid, channel, message)
+ return request.env['bus.bus'].sendone(channel, message)
# override to add channels
def _poll(self, dbname, channels, last, options):
diff --git a/addons/bus/models/bus.py b/addons/bus/models/bus.py
index 00c6e71a6e0..36882c5a5d8 100644
--- a/addons/bus/models/bus.py
+++ b/addons/bus/models/bus.py
@@ -2,19 +2,19 @@
import datetime
import json
import logging
+import random
import select
+import simplejson
import threading
import time
-import random
-import simplejson
import openerp
-from openerp.osv import osv, fields
-from openerp.http import request
+from openerp import api, fields, models
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
_logger = logging.getLogger(__name__)
+# longpolling timeout connection
TIMEOUT = 50
#----------------------------------------------------------
@@ -28,53 +28,67 @@ def hashable(key):
key = tuple(key)
return key
-class ImBus(osv.Model):
+
+class ImBus(models.Model):
+
_name = 'bus.bus'
- _columns = {
- 'id' : fields.integer('Id'),
- 'create_date' : fields.datetime('Create date'),
- 'channel' : fields.char('Channel'),
- 'message' : fields.char('Message'),
- }
-
- def gc(self, cr, uid):
+
+ create_date = fields.Datetime('Create date')
+ channel = fields.Char('Channel')
+ message = fields.Char('Message')
+
+ @api.model
+ def gc(self):
timeout_ago = datetime.datetime.utcnow()-datetime.timedelta(seconds=TIMEOUT*2)
domain = [('create_date', '<', timeout_ago.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
- ids = self.search(cr, openerp.SUPERUSER_ID, domain)
- self.unlink(cr, openerp.SUPERUSER_ID, ids)
+ return self.sudo().search(domain).unlink()
- def sendmany(self, cr, uid, notifications):
+ @api.model
+ def sendmany(self, notifications):
channels = set()
for channel, message in notifications:
channels.add(channel)
values = {
- "channel" : json_dump(channel),
- "message" : json_dump(message)
+ "channel": json_dump(channel),
+ "message": json_dump(message)
}
- self.pool['bus.bus'].create(cr, openerp.SUPERUSER_ID, values)
- cr.commit()
+ self.sudo().create(values)
+ self.env.cr.commit()
if random.random() < 0.01:
- self.gc(cr, uid)
+ self.gc()
if channels:
with openerp.sql_db.db_connect('postgres').cursor() as cr2:
cr2.execute("notify imbus, %s", (json_dump(list(channels)),))
- def sendone(self, cr, uid, channel, message):
- self.sendmany(cr, uid, [[channel, message]])
+ @api.model
+ def sendone(self, channel, message):
+ self.sendmany([[channel, message]])
- def poll(self, cr, uid, channels, last=0):
+ @api.model
+ def poll(self, channels, last=0):
# first poll return the notification in the 'buffer'
if last == 0:
timeout_ago = datetime.datetime.utcnow()-datetime.timedelta(seconds=TIMEOUT)
domain = [('create_date', '>', timeout_ago.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
- else:
- # else returns the unread notifications
- domain = [('id','>',last)]
+ else: # else returns the unread notifications
+ domain = [('id', '>', last)]
channels = [json_dump(c) for c in channels]
- domain.append(('channel','in',channels))
- notifications = self.search_read(cr, openerp.SUPERUSER_ID, domain)
- return [{"id":notif["id"], "channel": simplejson.loads(notif["channel"]), "message":simplejson.loads(notif["message"])} for notif in notifications]
+ domain.append(('channel', 'in', channels))
+ notifications = self.sudo().search_read(domain)
+ # list of notification to return
+ result = []
+ for notif in notifications:
+ result.append({
+ 'id': notif['id'],
+ 'channel': simplejson.loads(notif['channel']),
+ 'message': simplejson.loads(notif['message']),
+ })
+ return result
+
+#----------------------------------------------------------
+# Dispatcher
+#----------------------------------------------------------
class ImDispatch(object):
def __init__(self):
self.channels = {}
@@ -97,8 +111,8 @@ class ImDispatch(object):
# or wait for future ones
if not notifications:
event = self.Event()
- for c in channels:
- self.channels.setdefault(hashable(c), []).append(event)
+ for channel in channels:
+ self.channels.setdefault(hashable(channel), []).append(event)
try:
event.wait(timeout=timeout)
with registry.cursor() as cr:
@@ -116,7 +130,7 @@ class ImDispatch(object):
cr.execute("listen imbus")
cr.commit();
while True:
- if select.select([conn], [], [], TIMEOUT) == ([],[],[]):
+ if select.select([conn], [], [], TIMEOUT) == ([], [], []):
pass
else:
conn.poll()
@@ -125,10 +139,10 @@ class ImDispatch(object):
channels.extend(json.loads(conn.notifies.pop().payload))
# dispatch to local threads/greenlets
events = set()
- for c in channels:
- events.update(self.channels.pop(hashable(c),[]))
- for e in events:
- e.set()
+ for channel in channels:
+ events.update(self.channels.pop(hashable(channel), []))
+ for event in events:
+ event.set()
def run(self):
while True:
diff --git a/addons/bus/views/bus.xml b/addons/bus/views/bus.xml
index 8c4e1eb8ab2..a196a780549 100644
--- a/addons/bus/views/bus.xml
+++ b/addons/bus/views/bus.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<openerp>
+<odoo>
<data>
<template id="assets_backend" name="im assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
@@ -7,4 +7,4 @@
</xpath>
</template>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_chat/controllers/main.py b/addons/im_chat/controllers/main.py
index a643f5ac528..35ede172690 100644
--- a/addons/im_chat/controllers/main.py
+++ b/addons/im_chat/controllers/main.py
@@ -1,47 +1,50 @@
# -*- coding: utf-8 -*
import base64
import openerp
-import simplejson
from openerp import SUPERUSER_ID
from openerp.http import request
-#----------------------------------------------------------
-# Controllers
-#----------------------------------------------------------
-class Controller(openerp.addons.bus.controllers.main.BusController):
+
+class ChatController(openerp.addons.bus.controllers.main.BusController):
+
+ def _default_request_uid(self):
+ """ For Anonymous people, they receive the access right of SUPERUSER_ID since they have NO access (auth=none)
+ !!! Each time a method from this controller is call, there is a check if the user (who can be anonymous and Sudo access)
+ can access to the ressource, thanks to 'is_in_session', (for instance).
+ """
+ return request.session.uid and request.session.uid or SUPERUSER_ID
+
+ # --------------------------
+ # Extends BUS Controller Poll
+ # --------------------------
def _poll(self, dbname, channels, last, options):
if request.session.uid:
- registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
- registry.get('im_chat.presence').update(cr, uid, options.get('im_presence', False), context=context)
+ request.env['im_chat.presence'].update(options.get('im_presence'))
+ # channel to receive message
+ channels.append((request.db, 'im_chat.session', request.session.uid))
## For performance issue, the real time status notification is disabled. This means a change of status are still braoadcasted
## but not received by anyone. Otherwise, all listening user restart their longpolling at the same time and cause a 'ConnectionPool Full Error'
## since there is not enought cursors for everyone. Now, when a user open his list of users, an RPC call is made to update his user status list.
##channels.append((request.db,'im_chat.presence'))
- # channel to receive message
- channels.append((request.db,'im_chat.session', request.uid))
- return super(Controller, self)._poll(dbname, channels, last, options)
-
- @openerp.http.route('/im_chat/init', type="json", auth="none")
- def init(self):
- registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
- notifications = registry['im_chat.message'].init_messages(cr, uid, context=context)
- return notifications
+ return super(ChatController, self)._poll(dbname, channels, last, options)
+ # --------------------------
+ # Anonymous routes (Common Methods)
+ # --------------------------
@openerp.http.route('/im_chat/post', type="json", auth="none")
- def post(self, uuid, message_type, message_content):
- registry, cr, uid, context = request.registry, request.cr, request.session.uid, request.context
- # execute the post method as SUPERUSER_ID
- message_id = registry["im_chat.message"].post(cr, openerp.SUPERUSER_ID, uid, uuid, message_type, message_content, context=context)
- return message_id
+ def post(self, uuid, message_content, message_type):
+ request_uid = self._default_request_uid()
+ return request.env["im_chat.message"].sudo(request_uid).send_message(request.session.uid, uuid, message_content, message_type)
- @openerp.http.route(['/im_chat/image/<string:uuid>/<string:user_id>'], type='http', auth="none")
+ @openerp.http.route(['/im_chat/image/<string:uuid>/<int:user_id>', '/im_chat/image/<string:uuid>/<string:user_id>'], type='http', auth="none")
def image(self, uuid, user_id):
- registry, cr, context, uid = request.registry, request.cr, request.context, request.session.uid
- # get the image
- Session = registry.get("im_chat.session")
- image_b64 = Session.get_image(cr, openerp.SUPERUSER_ID, uuid, simplejson.loads(user_id), context)
- # built the response
+ request_uid = self._default_request_uid()
+ # anonymous will have a user_id = False, interpreted by string
+ if isinstance(user_id, (basestring, unicode)):
+ user_id = False
+ # get image for the people in the channel
+ image_b64 = request.env['im_chat.session'].sudo(request_uid).session_user_image(uuid, user_id)
image_data = base64.b64decode(image_b64)
headers = [('Content-Type', 'image/png')]
headers.append(('Content-Length', len(image_data)))
@@ -49,5 +52,12 @@ class Controller(openerp.addons.bus.controllers.main.BusController):
@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 or openerp.SUPERUSER_ID, request.context
- return registry["im_chat.message"].get_messages(cr, uid, uuid, last_id, limit, context=context)
+ request_uid = self._default_request_uid()
+ return request.env["im_chat.message"].sudo(request_uid).fetch_message(uuid, last_id, limit)
+
+ # --------------------------
+ # User routes (Only Logged User Methods)
+ # --------------------------
+ @openerp.http.route('/im_chat/init', type="json", auth="user")
+ def init(self):
+ return request.env['im_chat.message'].get_init_notifications()
diff --git a/addons/im_chat/data/im_chat_session_data.xml b/addons/im_chat/data/im_chat_session_data.xml
index fc2d12d797c..bd77ecbf8da 100644
--- a/addons/im_chat/data/im_chat_session_data.xml
+++ b/addons/im_chat/data/im_chat_session_data.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
-<openerp>
- <data >
+<odoo>
+ <data>
<!-- Smileys -->
<record id="im_chat_smiley_smile" model="im_chat.shortcode">
@@ -155,4 +155,4 @@
</record>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_chat/models/im_chat_session.py b/addons/im_chat/models/im_chat_session.py
index 39948ebb07e..0d3f11c5d14 100644
--- a/addons/im_chat/models/im_chat_session.py
+++ b/addons/im_chat/models/im_chat_session.py
@@ -1,202 +1,251 @@
# -*- coding: utf-8 -*-
-import base64
+import cgi
import datetime
-import logging
-import time
-import uuid
import random
import re
-import openerp
-import cgi
+import time
+import uuid
from openerp import tools, _
-from openerp.http import request
-from openerp.osv import osv, fields
+from openerp import api, fields, models
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+
from openerp.addons.bus.models.bus import TIMEOUT
-from openerp.exceptions import UserError
-_logger = logging.getLogger(__name__)
DISCONNECTION_TIMER = TIMEOUT + 5
AWAY_TIMER = 600 # 10 minutes
-#----------------------------------------------------------
-# Models
-#----------------------------------------------------------
-class im_chat_conversation_state(osv.Model):
- """ Adds a state on the m2m between user and session. """
+
+
+class ImChatConversationState(models.Model):
+ """ User Session State
+ Adds a state on the m2m between user and session.
+ """
+
_name = 'im_chat.conversation_state'
- _table = "im_chat_session_res_users_rel"
-
- _columns = {
- "state" : fields.selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')]),
- "session_id" : fields.many2one('im_chat.session', 'Session', required=True, ondelete="cascade"),
- "user_id" : fields.many2one('res.users', 'Users', required=True, ondelete="cascade"),
- }
- _defaults = {
- "state" : 'open'
- }
-
-class im_chat_session(osv.Model):
- """ Conversations."""
- _order = 'id desc'
+ _description = 'Chat Conversation State'
+ _table = 'im_chat_session_res_users_rel'
+
+ state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Status', default='open')
+ session_id = fields.Many2one('im_chat.session', string='Session', required=True, ondelete='cascade')
+ user_id = fields.Many2one('res.users', string='Users', required=True, ondelete='cascade')
+
+
+
+class ImChatSession(models.Model):
+ """ Session
+ Reprensenting a conversation between users. It manages anonymous user for method starting
+ with 'session_'.
+ """
+
_name = 'im_chat.session'
_rec_name = 'uuid'
+ _description = 'Chat Conversation'
+ _order = 'id desc'
- _columns = {
- 'uuid': fields.char('UUID', size=50, select=True),
- 'message_ids': fields.one2many('im_chat.message', 'to_id', 'Messages'),
- 'user_ids': fields.many2many('res.users', 'im_chat_session_res_users_rel', 'session_id', 'user_id', "Session Users"),
- 'session_res_users_rel': fields.one2many('im_chat.conversation_state', 'session_id', 'Relation Session Users'),
- }
- _defaults = {
- '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]
- return False
+ def _default_uuid(self):
+ return uuid.uuid4()
+
+ uuid = fields.Char(string='UUID', index=True, default=_default_uuid)
+ message_ids = fields.One2many('im_chat.message', 'to_id', string='Messages')
+ session_res_users_rel = fields.One2many('im_chat.conversation_state', 'session_id', string='Relation Session Users')
+ user_ids = fields.Many2many('res.users', 'im_chat_session_res_users_rel', 'session_id', 'user_id', string='Session Users')
+
+ # --------------------------
+ # 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
- 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):
- users_infos = self.pool["res.users"].read(cr, uid, [u.id for u in session.user_ids], ['id','name', 'im_status'], context=context)
- return users_infos
-
- def is_private(self, cr, uid, ids, context=None):
- for session_id in ids:
- """ return true if the session is private between users no external messages """
- mess_ids = self.pool["im_chat.message"].search(cr, uid, [('to_id','=',session_id),('from_id','=',None)], context=context)
- return len(mess_ids) == 0
-
- def session_info(self, cr, uid, ids, context=None):
- """ get the session info/header of a given session """
- for session in self.browse(cr, uid, ids, context=context):
- info = {
- 'uuid': session.uuid,
- 'users': session.users_infos(),
- 'state': 'open',
- }
- # add uid_state if available
- if uid:
- domain = [('user_id','=',uid), ('session_id','=',session.id)]
- uid_state = self.pool['im_chat.conversation_state'].search_read(cr, uid, domain, ['state'], context=context)
- if uid_state:
- info['state'] = uid_state[0]['state']
- return info
-
- def session_get(self, cr, uid, user_to, context=None):
- """ returns the canonical session between 2 users, create it if needed """
- session_id = False
+ @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:
- sids = self.search(cr, uid, [('user_ids','in', user_to),('user_ids', 'in', [uid])], context=context, limit=1)
- for sess in self.browse(cr, uid, sids, context=context):
- if len(sess.user_ids) == 2 and sess.is_private():
- session_id = sess.id
- break
- else:
- session_id = self.create(cr, uid, { 'user_ids': [(6,0, (user_to, uid))] }, context=context)
- return self.session_info(cr, uid, [session_id], context=context)
-
- def update_state(self, cr, uid, uuid, state=None, context=None):
- """ modify the fold_state of the given session, and broadcast to himself (e.i. : to sync multiple tabs) """
- domain = [('user_id','=',uid), ('session_id.uuid','=',uuid)]
- ids = self.pool['im_chat.conversation_state'].search(cr, uid, domain, context=context)
- for sr in self.pool['im_chat.conversation_state'].browse(cr, uid, ids, context=context):
+ 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 = sr.state
- if sr.state == 'open':
+ state = session_state.state
+ if session_state.state == 'open':
state = 'folded'
else:
state = 'open'
- self.pool['im_chat.conversation_state'].write(cr, uid, ids, {'state': state}, context=context)
- self.pool['bus.bus'].sendone(cr, uid, (cr.dbname, 'im_chat.session', uid), sr.session_id.session_info())
-
- def add_user(self, cr, uid, uuid, user_id, context=None):
- """ add the given user to the given session """
- sids = self.search(cr, uid, [('uuid', '=', uuid)], context=context, limit=1)
- for session in self.browse(cr, uid, sids, context=context):
- if user_id not in [u.id for u in session.user_ids]:
- self.write(cr, uid, [session.id], {'user_ids': [(4, user_id)]}, 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 is added : 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)
- # send a message to the conversation
- 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)
+ session_state.write({'state': state})
+ self.env['bus.bus'].sendone((self._cr.dbname, 'im_chat.session', self._uid), session_state.session_id.session_info())
+
+ # --------------------------
+ # User Actions
+ # --------------------------
+ @api.model
+ def add_user(self, uuid):
+ """ Add the current user to the session, and broadcast the meta message.
+ Note : the user need to be logged
+ :param uuid : the uuid of the session
+ """
+ session = self.search([('uuid', '=', uuid)], limit=1)
+ if user_id not in [u.id for u in session.user_ids]:
+ session.write({'user_ids': [(4, self._uid)]})
+ # notify the all the channel users and anonymous channel
+ notifications = []
+ for user in session.user_ids:
+ info = session.sudo(user.id).session_info()
+ notifications.append([(self._cr.dbname, 'im_chat.session', user.id), info])
+ # anonymous are not notified when a new user is added : cannot execute session_info as uid = None
+ info = session.sudo().session_info()
+ notifications.append([session.uuid, info])
+ self.env['bus.bus'].sendmany(notifications)
+ # send a message to the conversation
+ user = self.env['res.users'].browse(self._uid)
+ message = _("%s joined the conversation.") % (user.name,)
+ self.env["im_chat.message"].send_message(session.uuid, "meta", message)
+
+ @api.model
+ def quit_user(self, uuid):
+ """ Remove the current user from the given session.
+ Note : the user need to be logged
+ """
+ session = self.search([('uuid', '=', uuid)], limit=1)
+ if self._uid in [u.id for u in session.user_ids] and len(session.user_ids) > 2:
+ return session._remove_user()
+ return False
+
+ @api.multi
+ def _remove_user(self):
+ """ Private implementation of removing the current user from the current session,
+ and broadcast the meta message.
+ Note : the user need to be logged
+ """
+ self.ensure_one()
# 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)
+ user = self.env['res.users'].browse(self._uid)
+ message = _("%s left the conversation.") % (user.name,)
+ self.env["im_chat.message"].send_message(self.uuid, "meta", message)
+ # close his session state
+ self.update_state(self.uuid, 'closed')
+ # remove the user from session
+ self.write({"user_ids": [(3, self._uid)]})
# 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
- image_b64 = 'R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
- # get the session
- if user_id:
- session_id = self.pool["im_chat.session"].search(cr, uid, [('uuid','=',uuid), ('user_ids','in', user_id)])
- if session_id:
- # get the image of the user
- res = self.pool["res.users"].read(cr, uid, [user_id], ["image_small"])[0]
- if res["image_small"]:
- image_b64 = res["image_small"]
- return image_b64
+ for user in self.user_ids:
+ info = self.sudo(user.id).session_info()
+ notifications.append([(self._cr.dbname, 'im_chat.session', user.id), info])
+ # anonymous are not notified when a new user left : cannot execute session_info as uid = None
+ info = self.sudo().session_info()
+ notifications.append([self.uuid, info])
+ self.env['bus.bus'].sendmany(notifications)
+ return True
+
-class im_chat_message(osv.Model):
- """ Sessions messsages type can be 'message' or 'meta'.
+
+class ImChatMessage(models.Model):
+ """ Session Messsages
+ The chat provide 2 message type : 'message' (normal user message)
+ or 'meta' (message relative to changes about sessions)
For anonymous message, the from_id is False.
Messages are sent to a session not to users.
"""
+
_name = 'im_chat.message'
- _order = "create_date asc"
- _columns = {
- 'create_date': fields.datetime('Create Date', required=True, select=True),
- 'from_id': fields.many2one('res.users', 'Author'),
- 'to_id': fields.many2one('im_chat.session', 'Session To', required=True, select=True, ondelete='cascade'),
- 'type': fields.selection([('message','Message'), ('meta','Meta')], 'Type'),
- 'message': fields.char('Message'),
- }
- _defaults = {
- 'type' : 'message',
- }
+ _description = 'Chat Message'
+ _order = 'create_date asc'
+
+ @api.model
+ def _default_type(self):
+ return [('message', 'Message'), ('meta', 'Meta')]
+ create_date = fields.Datetime(string="Create date", required=True, index=True)
+ from_id = fields.Many2one('res.users', 'Author')
+ to_id = fields.Many2one('im_chat.session', 'Session', required=True, index=True)
+ type = fields.Selection('_default_type', 'Message type', default='message')
+ message = fields.Char('Message')
+
+ @api.model
def _escape_keep_url(self, message):
- """ escape the message and transform the url into clickable link """
+ """ Escape the message and transform the url into clickable link
+ :param message : the message to escape url and transform them into clickable link
+ :returns the escaped message
+ """
safe_message = ""
first = 0
last = 0
@@ -209,91 +258,116 @@ class im_chat_message(osv.Model):
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
+ @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', [uid]), ('create_date','>',threshold)]
+ domain = [('to_id.user_ids', 'in', [self._uid]), ('create_date', '>', threshold)]
# get the message since the last poll of the user
- presence_ids = self.pool['im_chat.presence'].search(cr, uid, [('user_id', '=', uid)], context=context)
- if presence_ids:
- presence = self.pool['im_chat.presence'].browse(cr, uid, presence_ids, context=context)[0]
- threshold = presence.last_poll
- domain.append(('create_date','>',threshold))
- messages = self.search_read(cr, uid, domain, ['from_id','to_id','create_date','type','message'], order='id asc', context=context)
+ 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 = map(lambda m: m['to_id'][0], messages)
- domain = [('user_id','=',uid), '|', ('state','!=','closed'), ('session_id', 'in', session_ids)]
- session_rels_ids = self.pool['im_chat.conversation_state'].search(cr, uid, domain, context=context)
- # re-open the session where a message have been recieve recently
- session_rels = self.pool['im_chat.conversation_state'].browse(cr, uid, session_rels_ids, context=context)
+ 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'})
- reopening_session = []
+ # create the notifications (session infos first, then messages)
notifications = []
- for sr in session_rels:
- si = sr.session_id.session_info()
- si['state'] = sr.state
- if sr.state == 'closed':
- si['state'] = 'folded'
- reopening_session.append(sr.id)
- notifications.append([(cr.dbname,'im_chat.session', uid), si])
- for m in messages:
- notifications.append([(cr.dbname,'im_chat.session', uid), m])
- self.pool['im_chat.conversation_state'].write(cr, uid, reopening_session, {'state': 'folded'}, context=context)
+ 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
- def post(self, cr, uid, from_uid, uuid, message_type, message_content, context=None):
- """ post and broadcast a message, return the message id """
- message_id = False
- Session = self.pool['im_chat.session']
- session_ids = Session.search(cr, uid, [('uuid','=',uuid)], context=context)
+
+ @api.model
+ def send_message(self, from_uid, uuid, message_content, message_type='message'):
+ """ Post and broadcast a message for the given session, return the message id
+ :param from_uid : user id of the author of the message
+ :param uuid : the uuid of the session
+ :param message_content : the content of the message
+ :param message_type : the type of the message
+ :returns the identifier of the new created message
+ :rtype : integer
+ """
+ session = self.env['im_chat.session'].search([('uuid', '=', uuid)], limit=1)
+ # escape the new message
+ message_content = self._escape_keep_url(message_content)
+ message_content = self.env['im_chat.shortcode'].replace_shortcode(message_content)
+ # create the chat message
+ values = {
+ "from_id": from_uid,
+ "to_id": session.id,
+ "type": message_type,
+ "message": message_content,
+ }
+ message = self.create(values)
+ # broadcast the message
+ data = message.read(['from_id', 'to_id', 'create_date', 'type', 'message'])[0]
notifications = []
- for session in Session.browse(cr, uid, session_ids, context=context):
- # 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,
- "type": message_type,
- "message": message_content,
- }
- # save it
- message_id = self.create(cr, uid, vals, context=context)
- # broadcast it to channel (anonymous users) and users_ids
- data = self.read(cr, uid, [message_id], ['from_id','to_id','create_date','type','message'], context=context)[0]
- notifications.append([uuid, data])
- for user in session.user_ids:
- notifications.append([(cr.dbname, 'im_chat.session', user.id), data])
- 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):
+ notifications.append([uuid, data])
+ for user in session.sudo().user_ids: # sudo required for portal user
+ notifications.append([(self._cr.dbname, 'im_chat.session', user.id), data])
+ self.env['bus.bus'].sendmany(notifications)
+ return message.id
+
+ @api.model
+ def fetch_message(self, uuid, last_id=False, limit=20):
+ """ Return messages for the given session uuid. If the session is public, fetch_message
+ as 'sudo', else (private session) fetch them as the current user. The security rule
+ 'message_rule_1' will check the access right.
+ :param uuid : uuid of the session to fetch messages
+ :param last_id : last message id to start the research
+ :param limit : maximum number of messages to fetch
+ :returns list of messages values
+ :rtype : list(dict)
+ """
+ session = self.env['im_chat.session'].search([('uuid', '=', uuid)], limit=1)
+ if session and session.is_in_session():
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, order="id desc", context=context)
+ domain.append(("id", "<", last_id))
+ return self.search_read(domain, ['id', 'create_date', 'to_id', 'from_id', 'type', 'message'], limit=limit, order="id desc")
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'),
- }
+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 :
@@ -305,91 +379,84 @@ class im_chat_shortcode(osv.Model):
return substitution
return cgi.escape(substitution)
- def create(self, cr, uid, values, context=None):
- if values.get('substitution'):
- values['substitution'] = self._sanitize_shorcode(values['substitution'])
- return super(im_chat_shortcode, self).create(cr, uid, values, context=context)
-
- def write(self, cr, uid, ids, values, context=None):
- if values.get('substitution'):
- values['substitution'] = self._sanitize_shorcode(values['substitution'])
- return super(im_chat_shortcode, self).write(cr, uid, ids, values, context=context)
-
- 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)
+ @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
-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
+
+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.')]
- _columns = {
- 'user_id' : fields.many2one('res.users', 'Users', required=True, select=True, ondelete="cascade"),
- 'last_poll': fields.datetime('Last Poll'),
- 'last_presence': fields.datetime('Last Presence'),
- 'status' : fields.selection([('online','Online'), ('away','Away'), ('offline','Offline')], 'IM Status'),
- }
- _defaults = {
- 'last_poll' : fields.datetime.now,
- 'last_presence' : fields.datetime.now,
- 'status' : 'offline'
- }
- _sql_constraints = [('im_chat_user_status_unique','unique(user_id)', 'A user can only have one IM status.')]
-
- def update(self, cr, uid, presence=True, context=None):
- """ register the poll, and change its im status if necessary. It also notify the Bus if the status has changed. """
- presence_ids = self.search(cr, uid, [('user_id', '=', uid)], context=context)
- presences = self.browse(cr, uid, presence_ids, context=context)
+ 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
- vals = {
+ values = {
'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
- 'status' : presences and presences[0].status or 'offline'
+ 'status' : presence and presence.status or 'offline'
}
# update the user or a create a new one
- if not presences:
- vals['status'] = 'online'
- vals['user_id'] = uid
- self.create(cr, uid, vals, context=context)
- else:
- if presence:
- vals['last_presence'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
- vals['status'] = 'online'
+ 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(presences[0].last_presence, DEFAULT_SERVER_DATETIME_FORMAT) < threshold:
- vals['status'] = 'away'
- send_notification = presences[0].status != vals['status']
+ 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.now() - datetime.datetime.strptime(presences[0].last_poll, DEFAULT_SERVER_DATETIME_FORMAT)
- if (delta > datetime.timedelta(seconds=TIMEOUT) or send_notification):
+ 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'):
- self.write(cr, uid, presence_ids, vals, context=context)
+ presence.write(values)
# avoid TransactionRollbackError
- cr.commit()
+ self.env.cr.commit() # TODO : check if still necessary
# notify if the status has changed
- if send_notification:
- self.pool['bus.bus'].sendone(cr, uid, (cr.dbname,'im_chat.presence'), {'id': uid, 'im_status': vals['status']})
+ 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(cr, uid, context=context)
+ self.check_users_disconnection()
return True
- def check_users_disconnection(self, cr, uid, context=None):
- """ disconnect the users having a too old last_poll """
- dt = (datetime.datetime.now() - datetime.timedelta(0, DISCONNECTION_TIMER)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
- presence_ids = self.search(cr, uid, [('last_poll', '<', dt), ('status' , '!=', 'offline')], context=context)
- self.write(cr, uid, presence_ids, {'status': 'offline'}, context=context)
- presences = self.browse(cr, uid, presence_ids, context=context)
+ @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([(cr.dbname,'im_chat.presence'), {'id': presence.user_id.id, 'im_status': presence.status}])
- self.pool['bus.bus'].sendmany(cr, uid, notifications)
- return True
+ 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/im_chat/models/res_users.py
index 7fda73a0b76..5e9d664b6a8 100644
--- a/addons/im_chat/models/res_users.py
+++ b/addons/im_chat/models/res_users.py
@@ -1,30 +1,33 @@
# -*- coding: utf-8 -*-
+from openerp import api, fields, models
-from openerp.osv import osv, fields
+class ResUsers(models.Model):
-
-class res_users(osv.osv):
_inherit = "res.users"
- def _get_im_status(self, cr, uid, ids, fields, arg, context=None):
- """ function computing the im_status field of the users """
- r = dict((i, 'offline') for i in ids)
- status_ids = self.pool['im_chat.presence'].search(cr, uid, [('user_id', 'in', ids)], context=context)
- status = self.pool['im_chat.presence'].browse(cr, uid, status_ids, context=context)
- for s in status:
- r[s.user_id.id] = s.status
- return r
+ im_status = fields.Char('IM Status', compute='_compute_im_status')
- _columns = {
- 'im_status' : fields.function(_get_im_status, type="char", string="IM Status"),
- }
+ @api.multi
+ def _compute_im_status(self):
+ """ Compute the im_status of the users """
+ res = dict((uid, 'offline') for uid in self.ids)
+ presences = self.env['im_chat.presence'].search([('user_id', 'in', self.ids)])
+ for presence in presences:
+ res[presence.user_id.id] = presence.status
+ for user in self:
+ user.im_status = res.get(user.id)
- def im_search(self, cr, uid, name, limit=20, context=None):
- """ search users with a name and return its id, name and im_status """
- result = [];
+ @api.model
+ def im_search(self, name, limit=20):
+ """ Search users with a name and return its id, name and im_status.
+ Note : the user must be logged
+ :param name : the user name to search
+ :param limit : the limit of result to return
+ """
+ result = []
# find the employee group
- group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1]
+ group_employee = self.env['ir.model.data'].get_object_reference('base', 'group_user')[1]
where_clause_base = " U.active = 't' "
query_params = ()
@@ -33,7 +36,7 @@ class res_users(osv.osv):
query_params = query_params + ('%'+name+'%',)
# first query to find online employee
- cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
+ self._cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
FROM im_chat_presence S
JOIN res_users U ON S.user_id = U.id
JOIN res_partner P ON P.id = U.partner_id
@@ -43,12 +46,12 @@ class res_users(osv.osv):
AND S.status = 'online'
ORDER BY P.name
LIMIT %s
- ''', query_params + (uid, group_employee, limit))
- result = result + cr.dictfetchall()
+ ''', query_params + (self._uid, group_employee, limit))
+ result = result + self._cr.dictfetchall()
# second query to find other online people
- if(len(result) < limit):
- cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
+ if len(result) < limit:
+ self._cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
FROM im_chat_presence S
JOIN res_users U ON S.user_id = U.id
JOIN res_partner P ON P.id = U.partner_id
@@ -57,12 +60,12 @@ class res_users(osv.osv):
AND S.status = 'online'
ORDER BY P.name
LIMIT %s
- ''', query_params + (tuple([u["id"] for u in result]) + (uid,), limit-len(result)))
- result = result + cr.dictfetchall()
+ ''', query_params + (tuple([u["id"] for u in result]) + (self._uid,), limit-len(result)))
+ result = result + self._cr.dictfetchall()
# third query to find all other people
- if(len(result) < limit):
- cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
+ if len(result) < limit:
+ self._cr.execute('''SELECT U.id as id, P.name as name, COALESCE(S.status, 'offline') as im_status
FROM res_users U
LEFT JOIN im_chat_presence S ON S.user_id = U.id
LEFT JOIN res_partner P ON P.id = U.partner_id
@@ -70,6 +73,6 @@ class res_users(osv.osv):
AND U.id NOT IN %s
ORDER BY P.name
LIMIT %s
- ''', query_params + (tuple([u["id"] for u in result]) + (uid,), limit-len(result)))
- result = result + cr.dictfetchall()
- return result \ No newline at end of file
+ ''', query_params + (tuple([u["id"] for u in result]) + (self._uid,), limit-len(result)))
+ result = result + self._cr.dictfetchall()
+ return result
diff --git a/addons/im_chat/security/im_chat_session_security.xml b/addons/im_chat/security/im_chat_session_security.xml
index bcc03d68b17..a70fe1835b6 100644
--- a/addons/im_chat/security/im_chat_session_security.xml
+++ b/addons/im_chat/security/im_chat_session_security.xml
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
-<openerp>
+<odoo>
<data>
- <record id="message_rule_1" model="ir.rule">
+ <record id="im_chat_message_rule_user" model="ir.rule">
<field name="name">Can only read messages that you sent or messages sent to you</field>
<field name="model_id" ref="model_im_chat_message"/>
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
@@ -12,7 +12,7 @@
<field name="perm_create" eval="1"/>
</record>
- <record id="users_rule_1" model="ir.rule">
+ <record id="im_chat_session_rule_user" model="ir.rule">
<field name="name">Can only modify your session</field>
<field name="model_id" ref="model_im_chat_session"/>
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
@@ -23,7 +23,7 @@
<field name="perm_create" eval="1"/>
</record>
- <record id="session_relation_rule_1" model="ir.rule">
+ <record id="im_chat_session_relation_rule_user" model="ir.rule">
<field name="name">Can only modify your own session relations</field>
<field name="model_id" ref="model_im_chat_conversation_state"/>
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
@@ -34,4 +34,4 @@
<field name="perm_create" eval="1"/>
</record>
</data>
-</openerp> \ No newline at end of file
+</odoo>
diff --git a/addons/im_chat/views/im_chat_session_templates.xml b/addons/im_chat/views/im_chat_session_templates.xml
index c2d9a0244f2..7007e148699 100644
--- a/addons/im_chat/views/im_chat_session_templates.xml
+++ b/addons/im_chat/views/im_chat_session_templates.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-
-<openerp>
+<odoo>
<data>
<template id="assets_backend" name="im_chat assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
@@ -11,4 +10,4 @@
</xpath>
</template>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_chat/views/im_chat_session_views.xml b/addons/im_chat/views/im_chat_session_views.xml
index 5854eb71d59..e281950d102 100644
--- a/addons/im_chat/views/im_chat_session_views.xml
+++ b/addons/im_chat/views/im_chat_session_views.xml
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<openerp>
+<odoo>
<data>
- <record id="action_im_chat_shortcode" model="ir.actions.act_window">
+ <!-- im_chat.shortcode -->
+ <record id="im_chat_shortcode_action" model="ir.actions.act_window">
<field name="name">Chat Shortcode</field>
<field name="res_model">im_chat.shortcode</field>
<field name="view_mode">tree,form</field>
@@ -15,7 +16,7 @@
</field>
</record>
- <record id="im_chat_shortcode_tree_view" model="ir.ui.view">
+ <record id="im_chat_shortcode_view_tree" model="ir.ui.view">
<field name="name">im_chat.shortcode.tree</field>
<field name="model">im_chat.shortcode</field>
<field name="arch" type="xml">
@@ -27,7 +28,7 @@
</field>
</record>
- <record id="im_chat_shortcode_form_view" model="ir.ui.view">
+ <record id="im_chat_shortcode_view_form" model="ir.ui.view">
<field name="name">im_chat.shortcode.form</field>
<field name="model">im_chat.shortcode</field>
<field name="arch" type="xml">
@@ -44,4 +45,4 @@
</record>
</data>
-</openerp> \ No newline at end of file
+</odoo>
diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py
index db18dadaff5..68d5fbb7f29 100644
--- a/addons/im_livechat/controllers/main.py
+++ b/addons/im_livechat/controllers/main.py
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
import json
-import openerp
-import openerp.addons.im_chat.im_chat
-from openerp import http
+from openerp import http, _
+from openerp import SUPERUSER_ID
from openerp.http import request
@@ -11,31 +10,30 @@ class LivechatController(http.Controller):
@http.route('/im_livechat/support/<string:dbname>/<int:channel_id>', type='http', auth='none')
def support_page(self, dbname, channel_id, **kwargs):
- registry, cr, uid, context = openerp.modules.registry.RegistryManager.get(dbname), request.cr, openerp.SUPERUSER_ID, request.context
- info = registry.get('im_livechat.channel').get_info_for_chat_src(cr, uid, channel_id)
+ Channel = request.env['im_livechat.channel']
+ info = Channel.get_channel_infos(channel_id)
info["dbname"] = dbname
info["channel"] = channel_id
- info["channel_name"] = registry.get('im_livechat.channel').read(cr, uid, channel_id, ['name'], context=context)["name"]
+ info["channel_name"] = Channel.browse(channel_id).name
return request.render('im_livechat.support_page', info)
@http.route('/im_livechat/loader/<string:dbname>/<int:channel_id>', type='http', auth='none')
def loader(self, dbname, channel_id, **kwargs):
- registry, cr, uid, context = openerp.modules.registry.RegistryManager.get(dbname), request.cr, openerp.SUPERUSER_ID, request.context
- info = registry.get('im_livechat.channel').get_info_for_chat_src(cr, uid, channel_id)
+ info = request.env['im_livechat.channel'].sudo().get_channel_infos(channel_id)
info["dbname"] = dbname
info["channel"] = channel_id
- info["username"] = kwargs.get("username", "Visitor")
+ info["username"] = kwargs.get("username", _("Visitor"))
# find the country from the request
country_id = False
country_code = request.session.geoip and request.session.geoip.get('country_code') or False
if country_code:
- country_ids = registry.get('res.country').search(cr, uid, [('code', '=', country_code)], context=context)
- if country_ids:
- country_id = country_ids[0]
+ countries = request.env['res.country'].search([('code', '=', country_code)])
+ if countries:
+ country_id = countries[0]
# extract url
url = request.httprequest.headers.get('Referer') or request.httprequest.base_url
# find the match rule for the given country and url
- rule = registry.get('im_livechat.channel.rule').match_rule(cr, uid, channel_id, url, country_id, context=context)
+ rule = request.env['im_livechat.channel.rule'].match_rule(channel_id, url, country_id)
if rule:
if rule.action == 'hide_button':
# don't return the initialization script, since its blocked (in the country)
@@ -50,22 +48,19 @@ class LivechatController(http.Controller):
@http.route('/im_livechat/get_session', type="json", auth="none")
def get_session(self, channel_id, anonymous_name, **kwargs):
- cr, uid, context, db = request.cr, request.session.uid, request.context, request.db
- reg = openerp.modules.registry.RegistryManager.get(db)
# if geoip, add the country name to the anonymous name
if request.session.geoip:
anonymous_name = anonymous_name + " ("+request.session.geoip.get('country_name', "")+")"
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
if request.session.uid:
anonymous_name = False
- return reg.get("im_livechat.channel").get_channel_session(cr, uid, channel_id, anonymous_name, context=context)
+ return request.env['im_livechat.channel'].sudo(request.session.uid).get_channel_session(channel_id, anonymous_name)
@http.route('/im_livechat/available', type='json', auth="none")
def available(self, db, channel):
- cr, uid, context, db = request.cr, request.session.uid or openerp.SUPERUSER_ID, request.context, request.db
- 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
+ user_id = request.session.uid or SUPERUSER_ID
+ channel = request.env['im_livechat.channel'].sudo(user_id).browse(channel)
+ return len(channel.get_available_users()) > 0
@http.route('/rating/livechat/feedback', type='json', auth='none')
def rating_livechat_feedback(self, uuid, rate, reason=None, **kwargs):
@@ -76,14 +71,14 @@ class LivechatController(http.Controller):
session = session_ids[0]
# limit the creation : only ONE rating per session
values = {
- 'rating' : rate,
+ 'rating': rate,
}
if not session.rating_ids:
values.update({
- 'res_id' : session.id,
- 'res_model' : 'im_chat.session',
- 'rating' : rate,
- 'feedback' : reason,
+ 'res_id': session.id,
+ 'res_model': 'im_chat.session',
+ 'rating': rate,
+ 'feedback': reason,
})
# find the partner related to the user of the conversation
rated_partner_id = False
diff --git a/addons/im_livechat/data/im_livechat_channel_data.xml b/addons/im_livechat/data/im_livechat_channel_data.xml
index 7a97ae788e1..be5c2fedf6e 100644
--- a/addons/im_livechat/data/im_livechat_channel_data.xml
+++ b/addons/im_livechat/data/im_livechat_channel_data.xml
@@ -1,16 +1,15 @@
<?xml version="1.0"?>
-<openerp>
-
+<odoo>
<data noupdate="1">
<!-- After installation of the module, open the related menu -->
- <record id="action_client_livechat_menu" model="ir.actions.client">
+ <record id="ir_action_client_open_livechat_menu" model="ir.actions.client">
<field name="name">Open Livechat Channel Menu</field>
<field name="tag">reload</field>
<field name="params" eval="{'menu_id': ref('support_channels')}"/>
</record>
<record id="base.open_menu" model="ir.actions.todo">
- <field name="action_id" ref="action_client_livechat_menu"/>
+ <field name="action_id" ref="ir_action_client_open_livechat_menu"/>
<field name="state">open</field>
</record>
@@ -30,7 +29,7 @@
<data>
<!-- Custom Filters -->
- <record id="filter_session_by_day" model="ir.filters">
+ <record id="im_livechat_report_filter_session_by_day" model="ir.filters">
<field name="name">Sessions by day</field>
<field name="model_id">im_livechat.report</field>
<field name="domain">[('nbr_speakers','&gt;', 1)]</field>
@@ -39,7 +38,7 @@
<field name="user_id" eval="False"/>
</record>
- <record id="filter_session_last_24h" model="ir.filters">
+ <record id="im_livechat_report_filter_session_last_24h" model="ir.filters">
<field name="name">Last 24h stats</field>
<field name="model_id">im_livechat.report</field>
<field name="domain">[('start_date','&gt;', (context_today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d') )]</field>
@@ -47,7 +46,7 @@
<field name="user_id" eval="False"/>
</record>
- <record id="filter_operator_stats" model="ir.filters">
+ <record id="im_livechat_report_filter_operator_stats" model="ir.filters">
<field name="name">Operators stats</field>
<field name="model_id">im_livechat.report</field>
<field name="domain">[('nbr_speakers','&gt;', 1), ('user_id', '!=', False)]</field>
@@ -57,19 +56,19 @@
<!-- Smiley for rating to be displayed in the conversation -->
- <record id="im_livechat_rating_0" model="im_chat.shortcode">
+ <record id="im_chat_shortcode_rating_0" model="im_chat.shortcode">
<field name="source">:rating_0</field>
<field name="substitution">&lt;img src='/rating/static/src/img/rating_0.png'/&gt;</field>
<field name="description">Rating Smiley Bad</field>
</record>
- <record id="im_livechat_rating_5" model="im_chat.shortcode">
+ <record id="im_chat_shortcode_rating_5" model="im_chat.shortcode">
<field name="source">:rating_5</field>
<field name="substitution">&lt;img src='/rating/static/src/img/rating_5.png'/&gt;</field>
<field name="description">Rating Smiley Okay</field>
</record>
- <record id="im_livechat_rating_10" model="im_chat.shortcode">
+ <record id="im_chat_shorcode_rating_10" model="im_chat.shortcode">
<field name="source">:rating_10</field>
<field name="substitution">&lt;img src='/rating/static/src/img/rating_10.png'/&gt;</field>
<field name="description">Rating Smiley Good</field>
@@ -77,4 +76,4 @@
</data>
-</openerp> \ No newline at end of file
+</odoo>
diff --git a/addons/im_livechat/data/im_livechat_channel_demo.xml b/addons/im_livechat/data/im_livechat_channel_demo.xml
index 3da9b3998de..848965ec448 100644
--- a/addons/im_livechat/data/im_livechat_channel_demo.xml
+++ b/addons/im_livechat/data/im_livechat_channel_demo.xml
@@ -1,22 +1,20 @@
<?xml version="1.0"?>
-<openerp>
+<odoo>
<data>
-
- <record id="channel_website" model="im_livechat.channel">
+ <record id="im_livechat_channel_demo" model="im_livechat.channel">
<field name="name">YourWebsite.com</field>
<field name="default_message">Hello, how may I help you?</field>
</record>
- <record id="group_im_livechat" model="res.groups">
- <field name="users" eval="[(4, ref('base.user_demo'))]"/>
- </record>
-
- <record id="channel_rule" model="im_livechat.channel.rule">
+ <record id="im_livechat_channel_rule_demo" model="im_livechat.channel.rule">
<field name="regex_url">/im_livechat/</field>
<field name="action">auto_popup</field>
<field name="auto_popup_timer">3</field>
- <field name="channel_id" ref="channel_website" />
+ <field name="channel_id" ref="im_livechat_channel_demo" />
</record>
+ <record id="im_livechat.im_livechat_group_user" model="res.groups">
+ <field name="users" eval="[(4, ref('base.user_demo'))]"/>
+ </record>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_livechat/models/im_chat_session.py b/addons/im_livechat/models/im_chat_session.py
index fd02df0ec84..0cae7f76c46 100644
--- a/addons/im_livechat/models/im_chat_session.py
+++ b/addons/im_livechat/models/im_chat_session.py
@@ -1,67 +1,78 @@
# -*- coding: utf-8 -*-
-import openerp
+from openerp import api, fields, models
+from openerp import SUPERUSER_ID
-from openerp.osv import osv, fields
-class im_chat_session(osv.Model):
+class ImChatSession(models.Model):
+ """ Chat Session
+ Reprensenting a conversation between users.
+ It extends the base method for anonymous usage.
+ """
_name = 'im_chat.session'
_inherit = ['im_chat.session', 'rating.mixin']
- def _get_fullname(self, cr, uid, ids, fields, arg, context=None):
+
+ anonymous_name = fields.Char('Anonymous Name')
+ create_date = fields.Datetime('Create Date', required=True)
+ channel_id = fields.Many2one('im_livechat.channel', 'Channel')
+ fullname = fields.Char('Complete Name', compute='_compute_fullname')
+
+ @api.multi
+ @api.depends('anonymous_name', 'user_ids')
+ def _compute_fullname(self):
""" built the complete name of the session """
- result = {}
- sessions = self.browse(cr, uid, ids, context=context)
- for session in sessions:
+ for session in self:
names = []
for user in session.user_ids:
names.append(user.name)
if session.anonymous_name:
names.append(session.anonymous_name)
- result[session.id] = ', '.join(names)
- return result
-
- _columns = {
- 'anonymous_name' : fields.char('Anonymous Name'),
- 'create_date': fields.datetime('Create Date', required=True, select=True),
- 'channel_id': fields.many2one("im_livechat.channel", "Channel"),
- '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 user_id == openerp.SUPERUSER_ID:
- return True
- else:
- return super(im_chat_session, self).is_in_session(cr, uid, uuid, user_id, context=context)
- return False
+ session.fullname = ', '.join(names)
- 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):
- users_infos = super(im_chat_session, self).users_infos(cr, uid, ids, context=context)
- if session.anonymous_name:
- users_infos.append({'id' : False, 'name' : session.anonymous_name, 'im_status' : 'online'})
- return users_infos
+ @api.multi
+ def is_in_session(self):
+ """ Return True if the current user is in the user_ids of the session. False otherwise.
+ If this is executed as sudo, this might be the anonymous user.
+ """
+ self.ensure_one()
+ if self.anonymous_name and self._uid == SUPERUSER_ID:
+ return True
+ else:
+ return super(ImChatSession, self).is_in_session()
- 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 session.anonymous_name:
- # an identified user can leave an anonymous session if there is still another idenfied user in it
- if uid and uid in [u.id for u in session.user_ids] and len(session.user_ids) > 1:
- self.remove_user(cr, uid, session.id, context=context)
- return True
- return False
- else:
- return super(im_chat_session, self).quit_user(cr, uid, session.id, context=context)
+ @api.multi
+ def session_user_info(self):
+ """ Get the user infos for all the identified user in the session + the anonymous if anonymous session
+ :returns a list of user infos
+ :rtype : list(dict)
+ """
+ self.ensure_one()
+ users_infos = super(ImChatSession, self).session_user_info()
+ if self.anonymous_name:
+ users_infos.append({'id': False, 'name': self.anonymous_name, 'im_status': 'online'})
+ return users_infos
+ @api.model
+ def quit_user(self, uuid):
+ """ Remove the current user from the given session.
+ Note : an anonymous user cannot leave the session, since he is not registered.
+ Required to modify the base comportement, since a session can contain only 1 identified user.
+ :param uuid : the uuid of the session to quit
+ """
+ session = self.search([('uuid', '=', uuid)], limit=1)
+ if session.anonymous_name:
+ # an identified user can leave an anonymous session if there is still another idenfied user in it
+ if self._uid and self._uid in [u.id for u in session.user_ids] and len(session.user_ids) > 1:
+ self._remove_user()
+ return True
+ return False
+ else:
+ return super(ImChatSession, self).quit_user(uuid)
- def cron_remove_empty_session(self, cr, uid, context=None):
- groups = self.pool['im_chat.message'].read_group(cr, uid, [], ['to_id'], ['to_id'], context=context)
+ @api.model
+ def cron_remove_empty_session(self):
+ groups = self.env['im_chat.message'].read_group([], ['to_id'], ['to_id'])
not_empty_session_ids = [group['to_id'][0] for group in groups]
- empty_session_ids = self.search(cr, uid, [('id', 'not in', not_empty_session_ids), ('channel_id', '!=', False)], context=context)
- self.unlink(cr, uid, empty_session_ids, context=context)
+ empty_sessions = self.search([('id', 'not in', not_empty_session_ids), ('channel_id', '!=', False)])
+ empty_sessions.unlink()
diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py
index 06f5fb5eb88..258b4e2aeb1 100644
--- a/addons/im_livechat/models/im_livechat_channel.py
+++ b/addons/im_livechat/models/im_livechat_channel.py
@@ -1,311 +1,239 @@
# -*- coding: utf-8 -*-
import openerp
-import openerp.addons.im_chat.im_chat
import random
import re
-from openerp.osv import osv, fields
-from openerp import api, models, tools
-from openerp import SUPERUSER_ID
+from openerp import api, fields, models
+from openerp import tools
-class im_livechat_channel(osv.Model):
+class ImLivechatChannel(models.Model):
+ """ Livechat Channel
+ Define a communication channel, which can be accessed with 'script_external' (script tag to put on
+ external website), 'script_internal' (code to be integrated with odoo website) or via 'web_page' link.
+ It provides rating tools, and access rules for anonymous people.
+ """
+
_name = 'im_livechat.channel'
+ _description = 'Livechat Channel'
- def _get_default_image(self, cr, uid, context=None):
+ def _default_image(self):
image_path = openerp.modules.get_module_resource('im_livechat', 'static/src/img', 'default.png')
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
- def _get_image(self, cr, uid, ids, name, args, context=None):
- result = dict.fromkeys(ids, False)
- for obj in self.browse(cr, uid, ids, context=context):
- result[obj.id] = tools.image_get_resized_images(obj.image)
- return result
- def _set_image(self, cr, uid, id, name, value, args, context=None):
- return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
-
- def _are_you_inside(self, cr, uid, ids, name, arg, context=None):
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
- res[record.id] = False
- for user in record.user_ids:
- if user.id == uid:
- res[record.id] = True
- break
- return res
-
- def _script_external(self, cr, uid, ids, name, arg, context=None):
+
+ def _default_user_ids(self):
+ return [(6, 0, [self._uid])]
+
+
+ # attribute fields
+ name = fields.Char('Name', required=True, help="The name of the channel")
+ button_text = fields.Char('Text of the Button', default='Have a Question? Chat with us.',
+ help="Default text displayed on the Livechat Support Button")
+ default_message = fields.Char('Welcome Message', default='How may I help you?',
+ help="This is an automated 'welcome' message that your visitor will see when they initiate a new chat session.")
+ input_placeholder = fields.Char('Chat Input Placeholder')
+
+ # computed fields
+ web_page = fields.Char('Web Page', compute='_compute_web_page_link', store=False, readonly=True,
+ help="URL to a static page where you client can discuss with the operator of the channel.")
+ are_you_inside = fields.Boolean(string='Are you inside the matrix?',
+ compute='_are_you_inside', store=False, readonly=True)
+ script_internal = fields.Text('Script (internal)', compute='_compute_script_internal', store=False, readonly=True)
+ script_external = fields.Text('Script (external)', compute='_compute_script_external', store=False, readonly=True)
+ nbr_session = fields.Integer('Number of session', compute='_compute_nbr_session', store=False, readonly=True)
+ rating_percentage_satisfaction = fields.Integer('% Happy', compute='_compute_percentage_satisfaction', store=False, default=-1)
+
+ # images fields
+ image = fields.Binary('Image', default=_default_image,
+ help="This field holds the image used as photo for the group, limited to 1024x1024px.")
+ image_small = fields.Binary('Thumbnail', compute='_compute_image', store=True,
+ help="Small-sized photo of the group. It is automatically "\
+ "resized as a 64x64px image, with aspect ratio preserved. "\
+ "Use this field anywhere a small image is required.")
+ image_medium = fields.Binary('Medium', compute='_compute_image', store=True,
+ help="Medium-sized photo of the group. It is automatically "\
+ "resized as a 128x128px image, with aspect ratio preserved. "\
+ "Use this field in form views or some kanban views.")
+
+ # relationnal fields
+ user_ids = fields.Many2many('res.users', 'im_livechat_channel_im_user', 'channel_id', 'user_id', string='Operators', default=_default_user_ids)
+ session_ids = fields.One2many('im_chat.session', 'channel_id', 'Sessions')
+ rule_ids = fields.One2many('im_livechat.channel.rule', 'channel_id', 'Rules')
+
+
+ @api.one
+ def _are_you_inside(self):
+ self.are_you_inside = bool(self.env.uid in [u.id for u in self.user_ids])
+
+ @api.one
+ @api.depends('image')
+ def _compute_image(self):
+ if self.image:
+ self.image_small = tools.image_resize_image_small(self.image)
+ self.image_medium = tools.image_resize_image_medium(self.image)
+ else:
+ self.image_small = False
+ self.image_medium = False
+
+ @api.multi
+ def _compute_script_internal(self):
+ view = self.env['ir.model.data'].get_object('im_livechat', 'internal_loader')
values = {
- "url": self.pool.get('ir.config_parameter').get_param(cr, openerp.SUPERUSER_ID, 'web.base.url'),
- "dbname":cr.dbname
+ "url": self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
+ "dbname": self._cr.dbname,
}
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
+ for record in self:
values["channel"] = record.id
- res[record.id] = self.pool['ir.ui.view'].render(cr, uid, 'im_livechat.external_loader', values, context=context)
- return res
+ record.script_internal = view.render(values)
- def _script_internal(self, cr, uid, ids, name, arg, context=None):
+ @api.multi
+ def _compute_script_external(self):
+ view = self.env['ir.model.data'].get_object('im_livechat', 'external_loader')
values = {
- "url": self.pool.get('ir.config_parameter').get_param(cr, openerp.SUPERUSER_ID, 'web.base.url'),
- "dbname":cr.dbname
+ "url": self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
+ "dbname": self._cr.dbname,
}
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
+ for record in self:
values["channel"] = record.id
- res[record.id] = self.pool['ir.ui.view'].render(cr, uid, 'im_livechat.internal_loader', values, context=context)
- return res
-
- def _web_page(self, cr, uid, ids, name, arg, context=None):
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
- res[record.id] = self.pool.get('ir.config_parameter').get_param(cr, openerp.SUPERUSER_ID, 'web.base.url') + \
- "/im_livechat/support/%s/%i" % (cr.dbname, record.id)
- return res
-
- def _compute_nbr_session(self, cr, uid, ids, name, arg, context=None):
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
- res[record.id] = len(record.session_ids)
- return res
-
- # RATING METHOD
- def _compute_percentage_satisfaction(self, cr, uid, ids, name, arg, context=None):
- res = {}
- for record in self.browse(cr, uid, ids, context=context):
+ record.script_external = view.render(values)
+
+ @api.multi
+ def _compute_web_page_link(self):
+ base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ for record in self:
+ record.web_page = "%s/im_livechat/support/%s/%i" % (base_url, self._cr.dbname, record.id)
+
+ @api.multi
+ @api.depends('session_ids')
+ def _compute_nbr_session(self):
+ for record in self:
+ record.nbr_session = len(record.session_ids)
+
+ @api.multi
+ @api.depends('session_ids.rating_ids')
+ def _compute_percentage_satisfaction(self):
+ for record in self:
repartition = record.session_ids.rating_get_grades()
total = sum(repartition.values())
if total > 0:
happy = repartition['great']
- res[record.id] = ((happy*100) / total) if happy > 0 else 0
+ record.rating_percentage_satisfaction = ((happy*100) / total) if happy > 0 else 0
else:
- res[record.id] = -1
- return res
-
- def action_view_rating(self, cr, uid, ids, context=None):
- channels = self.browse(cr, uid, ids, context=context)
- action = self.pool['ir.actions.act_window'].for_xml_id(cr, uid, 'rating', 'action_view_rating', context=context)
- action['domain'] = [('res_id', 'in', [s.id for s in channels.session_ids]), ('res_model', '=', 'im_chat.session')]
+ record.rating_percentage_satisfaction = -1
+
+
+ # --------------------------
+ # Action Methods
+ # --------------------------
+ @api.multi
+ def action_join(self):
+ self.ensure_one()
+ return self.write({'user_ids': [(4, self._uid)]})
+
+ @api.multi
+ def action_quit(self):
+ self.ensure_one()
+ return self.write({'user_ids': [(3, self._uid)]})
+
+ @api.multi
+ def action_view_rating(self):
+ """ Action to display the rating relative to the channel, so all rating of the
+ sessions of the current channel
+ :returns : the ir.action 'action_view_rating' with the correct domain
+ """
+ self.ensure_one()
+ action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating')
+ action['domain'] = [('res_id', 'in', [s.id for s in self.session_ids]), ('res_model', '=', 'im_chat.session')]
return action
- _columns = {
- 'name': fields.char(string="Channel Name", size=200, required=True),
- 'user_ids': fields.many2many('res.users', 'im_livechat_channel_im_user', 'channel_id', 'user_id', string="Users"),
- 'are_you_inside': fields.function(_are_you_inside, type='boolean', string='Are you inside the matrix?', store=False),
- 'script_internal': fields.function(_script_internal, type='text', string='Script (internal)', store=False),
- 'script_external': fields.function(_script_external, type='text', string='Script (external)', store=False),
- 'web_page': fields.function(_web_page, type='char', string='Web Page', store=False),
- 'button_text': fields.char(string="Text of the Button"),
- 'input_placeholder': fields.char(string="Chat Input Placeholder"),
- 'default_message': fields.char(string="Welcome Message", help="This is an automated 'welcome' message that your visitor will see when they initiate a new chat session."),
- # image: all image fields are base64 encoded and PIL-supported
- 'image': fields.binary("Photo",
- help="This field holds the image used as photo for the group, limited to 1024x1024px."),
- 'image_medium': fields.function(_get_image, fnct_inv=_set_image,
- string="Medium-sized photo", type="binary", multi="_get_image",
- store={
- 'im_livechat.channel': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
- },
- help="Medium-sized photo of the group. It is automatically "\
- "resized as a 128x128px image, with aspect ratio preserved. "\
- "Use this field in form views or some kanban views."),
- 'image_small': fields.function(_get_image, fnct_inv=_set_image,
- string="Small-sized photo", type="binary", multi="_get_image",
- store={
- 'im_livechat.channel': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
- },
- help="Small-sized photo of the group. It is automatically "\
- "resized as a 64x64px image, with aspect ratio preserved. "\
- "Use this field anywhere a small image is required."),
- 'session_ids' : fields.one2many('im_chat.session', 'channel_id', 'Sessions'),
- 'nbr_session' : fields.function(_compute_nbr_session, type='integer', string='Number of session', store=False),
- 'rule_ids': fields.one2many('im_livechat.channel.rule','channel_id','Rules'),
- # rating field
- 'rating_percentage_satisfaction' : fields.function(_compute_percentage_satisfaction, type='integer', string='% Happy', store=False, default=-1),
- }
-
- def _default_user_ids(self, cr, uid, context=None):
- return [(6, 0, [uid])]
-
- _defaults = {
- 'button_text': "Have a Question? Chat with us.",
- 'input_placeholder': "How may I help you?",
- 'default_message': '',
- 'user_ids': _default_user_ids,
- 'image': _get_default_image,
- }
-
- def get_available_users(self, cr, uid, channel_id, context=None):
- """ get available user of a given channel """
- channel = self.browse(cr, SUPERUSER_ID, channel_id, context=context)
- users = []
- for user_id in channel.user_ids:
- if (user_id.im_status == 'online'):
- users.append(user_id)
- return users
-
- def get_channel_session(self, cr, uid, channel_id, anonymous_name, context=None):
+ # --------------------------
+ # Channel Methods
+ # --------------------------
+ @api.multi
+ def get_available_users(self):
+ """ get available user of a given channel
+ :retuns : return the res.users having their im_status online
+ """
+ self.ensure_one()
+ return self.sudo().user_ids.filtered(lambda user: user.im_status == 'online')
+
+ @api.model
+ def get_channel_session(self, channel_id, anonymous_name):
""" return a session given a channel : create on with a registered user, or return false otherwise """
# get the avalable user of the channel
- users = self.get_available_users(cr, SUPERUSER_ID, channel_id, context=context)
+ users = self.sudo().browse(channel_id).get_available_users()
if len(users) == 0:
return False
user_id = random.choice(users).id
# user to add to the session
user_to_add = [(4, user_id)]
- if uid:
- user_to_add.append((4, uid))
+ if self.env.uid:
+ user_to_add.append((4, self.env.uid))
# create the session, and add the link with the given channel
- Session = self.pool["im_chat.session"]
- newid = Session.create(cr, SUPERUSER_ID, {'user_ids': user_to_add, 'channel_id': channel_id, 'anonymous_name' : anonymous_name}, context=context)
- return Session.session_info(cr, SUPERUSER_ID, [newid], context=context)
-
- def test_channel(self, cr, uid, channel, context=None):
- if not channel:
- return {}
- return {
- 'url': self.browse(cr, uid, channel[0], context=context or {}).web_page,
- 'type': 'ir.actions.act_url'
- }
+ session = self.env["im_chat.session"].sudo().create({'user_ids': user_to_add, 'channel_id': channel_id, 'anonymous_name': anonymous_name})
+ return session.sudo().session_info()
- def get_info_for_chat_src(self, cr, uid, channel, context=None):
- url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- chan = self.browse(cr, uid, channel, context=context)
+ @api.model
+ def get_channel_infos(self, channel_id):
+ url = self.env['ir.config_parameter'].get_param('web.base.url')
+ channel = self.browse(channel_id)
return {
"url": url,
- 'buttonText': chan.button_text,
- 'inputPlaceholder': chan.input_placeholder,
- 'defaultMessage': chan.default_message,
- "channelName": chan.name,
+ 'buttonText': channel.button_text,
+ 'inputPlaceholder': channel.input_placeholder,
+ 'defaultMessage': channel.default_message,
+ "channelName": channel.name,
}
- def join(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, {'user_ids': [(4, uid)]})
- return True
- def quit(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, {'user_ids': [(3, uid)]})
- return True
+class ImLivechatChannelRule(models.Model):
+ """ Channel Rules
+ Rules defining access to the channel (countries, and url matching). It also provide the 'auto pop'
+ option to open automatically the conversation.
+ """
-class im_livechat_channel_rule(osv.Model):
_name = 'im_livechat.channel.rule'
-
- _columns = {
- 'regex_url' : fields.char('URL Regex', help="Regular expression identifying the web page on which the rules will be applied."),
- 'action' : fields.selection([('display_button', 'Display button'),('auto_popup','Auto-popup'), ('hide_button', 'Hide button')], 'Action', size=32, required=True,
- help="Select 'Display button' to simply display the chat button on the pages."\
- " Select 'Auto-popup' for to display the button, and automatically open the conversation window."\
- " Select 'Hide button' to hide the chat button on the pages."),
- 'auto_popup_timer' : fields.integer('Auto-popup Timer', help="Delay (in seconds) to automatically open the conversation window. Note: the selected action must be 'Auto-popup', otherwise this parameter will not be taken into account."),
- 'channel_id': fields.many2one('im_livechat.channel', 'Channel', help="Channel on which the rules applies"),
- 'country_ids': fields.many2many('res.country', 'im_livechat_channel_country_rel', 'channel_id', 'country_id', 'Countries', help="Countries where this rule apply. If you set select 'Belgium' and 'France' and you set the action to 'Hide Button', people from these two countries won't see the support button for the specified URL. (This feature requires GeoIP installed on your server.)"),
- 'sequence' : fields.integer('Matching Order', help="Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen.")
- }
-
- _defaults = {
- 'auto_popup_timer': 0,
- 'action' : 'display_button',
- 'sequence' : 10,
- }
-
- _order = "sequence asc"
-
- def match_rule(self, cr, uid, channel_id, url, country_id=False, context=None):
- """ determine if a rule of the given channel match with the given url """
- def _match(rule_ids):
- for rule in self.browse(cr, uid, rule_ids, context=context):
+ _description = 'Channel Rules'
+ _order = 'sequence asc'
+
+
+ regex_url = fields.Char('URL Regex',
+ help="Regular expression identifying the web page on which the rules will be applied.")
+ action = fields.Selection([('display_button', 'Display the button'), ('auto_popup', 'Auto popup'), ('hide_button', 'Hide the button')],
+ string='Action', required=True, default='display_button',
+ help="* Select 'Display the button' to simply display the chat button on the pages.\n"\
+ "* Select 'Auto popup' for to display the button, and automatically open the conversation window.\n"\
+ "* Select 'Hide the button' to hide the chat button on the pages.")
+ auto_popup_timer = fields.Integer('Auto popup timer', default=0,
+ help="Delay (in seconds) to automatically open the converssation window. Note : the selected action must be 'Auto popup', otherwise this parameter will not be take into account.")
+ channel_id = fields.Many2one('im_livechat.channel', 'Channel',
+ help="The channel of the rule")
+ country_ids = fields.Many2many('res.country', 'im_livechat_channel_country_rel', 'channel_id', 'country_id', 'Country',
+ help="The actual rule will match only for this country. So if you set select 'Belgium' and 'France' and you set the action to 'Hide Buttun', this 2 country will not be see the support button for the specified URL. This feature requires GeoIP installed on your server.")
+ sequence = fields.Integer('Matching order', default=10,
+ help="Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen.")
+
+
+ def match_rule(self, channel_id, url, country_id=False):
+ """ determine if a rule of the given channel match with the given url
+ :param channel_id : the identifier of the channel_id
+ :param url : the url to match with a rule
+ :param country_id : the identifier of the country
+ :returns the rule that match the given condition. False otherwise.
+ :rtype : im_livechat.channel.rule
+ """
+ def _match(rules):
+ for rule in rules:
if re.search(rule.regex_url, url):
return rule
return False
# first, search the country specific rules (the first match is returned)
if country_id: # don't include the country in the research if geoIP is not installed
domain = [('country_ids', 'in', [country_id]), ('channel_id', '=', channel_id)]
- rule_ids = self.search(cr, uid, domain, context=context)
- rule = _match(rule_ids)
+ rule = _match(self.search(domain))
if rule:
return rule
# second, fallback on the rules without country
domain = [('country_ids', '=', False), ('channel_id', '=', channel_id)]
- rule_ids = self.search(cr, uid, domain, context=context)
- return _match(rule_ids)
-
-
-class im_chat_session(osv.Model):
-
- _name = 'im_chat.session'
- _inherit = ['im_chat.session', 'rating.mixin']
-
- def _get_fullname(self, cr, uid, ids, fields, arg, context=None):
- """ built the complete name of the session """
- result = {}
- sessions = self.browse(cr, uid, ids, context=context)
- for session in sessions:
- names = []
- for user in session.user_ids:
- names.append(user.name)
- if session.anonymous_name:
- names.append(session.anonymous_name)
- result[session.id] = ', '.join(names)
- return result
-
- _columns = {
- 'anonymous_name' : fields.char('Anonymous Name'),
- 'create_date': fields.datetime('Create Date', required=True, select=True),
- 'channel_id': fields.many2one("im_livechat.channel", "Channel"),
- '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 user_id == openerp.SUPERUSER_ID:
- return True
- else:
- return super(im_chat_session, self).is_in_session(cr, uid, uuid, user_id, context=context)
- return False
-
- 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):
- users_infos = super(im_chat_session, self).users_infos(cr, uid, ids, context=context)
- if session.anonymous_name:
- users_infos.append({'id' : False, 'name' : session.anonymous_name, 'im_status' : 'online'})
- return users_infos
-
- 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 session.anonymous_name:
- # an identified user can leave an anonymous session if there is still another idenfied user in it
- if uid and uid in [u.id for u in session.user_ids] and len(session.user_ids) > 1:
- self.remove_user(cr, uid, session.id, context=context)
- return True
- return False
- else:
- return super(im_chat_session, self).quit_user(cr, uid, session.id, context=context)
-
-
- def cron_remove_empty_session(self, cr, uid, context=None):
- groups = self.pool['im_chat.message'].read_group(cr, uid, [], ['to_id'], ['to_id'], context=context)
- not_empty_session_ids = [group['to_id'][0] for group in groups]
- empty_session_ids = self.search(cr, uid, [('id', 'not in', not_empty_session_ids), ('channel_id', '!=', False)], context=context)
- self.unlink(cr, uid, empty_session_ids, context=context)
-
-
-
-class Rating(models.Model):
-
- _inherit = "rating.rating"
-
- @api.one
- @api.depends('res_model', 'res_id')
- def _compute_res_name(self):
- # cannot change the rec_name of session since it is use to create the bus channel
- # so, need to override this method to set the same alternative rec_name as in reporting
- if self.res_model == 'im_chat.session':
- current_object = self.env[self.res_model].sudo().browse(self.res_id)
- self.res_name = ('%s / %s') % (current_object.channel_id.name, current_object.id)
- else:
- super(Rating, self)._compute_res_name()
+ return _match(self.search(domain))
diff --git a/addons/im_livechat/report/__init__.py b/addons/im_livechat/report/__init__.py
index 974388ee165..edfc5d9deeb 100644
--- a/addons/im_livechat/report/__init__.py
+++ b/addons/im_livechat/report/__init__.py
@@ -1 +1,2 @@
-import im_livechat_report \ No newline at end of file
+# -*- coding: utf-8 -*-
+import im_livechat_report
diff --git a/addons/im_livechat/report/im_livechat_report.py b/addons/im_livechat/report/im_livechat_report.py
index 1237ee97333..4fa0436b001 100644
--- a/addons/im_livechat/report/im_livechat_report.py
+++ b/addons/im_livechat/report/im_livechat_report.py
@@ -1,28 +1,29 @@
-from openerp.osv import fields,osv
-from openerp import tools
+# -*- coding: utf-8 -*-
+from openerp import api, fields, models, tools
-class im_livechat_report(osv.Model):
+class im_livechat_report(models.Model):
""" Livechat Support Report """
+
_name = "im_livechat.report"
- _auto = False
_description = "Livechat Support Report"
- _columns = {
- 'session_name': fields.char('Session Name', readonly=True),
- 'uuid': fields.char('UUID', readonly=True),
- 'start_date': fields.datetime('Start Date of session', readonly=True, help="Start date of the conversation"),
- 'start_date_hour': fields.char('Hour of start Date of session', readonly=True),
- 'duration': fields.float('Average duration', digits=(16,2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)"),
- 'time_in_session': fields.float('Time in session', digits=(16,2), readonly=True, group_operator="avg", help="Average time the user spend in the conversation"),
- 'time_to_answer': fields.float('Time to answer', digits=(16,2), readonly=True, group_operator="avg", help="Average time to give the first answer to the visitor"),
- 'nbr_messages': fields.integer('Average message', readonly=True, group_operator="avg", help="Number of message in the conversation"),
- 'nbr_user_messages': fields.integer('Average of messages/user', readonly=True, group_operator="avg", help="Average number of message per user"),
- 'nbr_speakers': fields.integer('# of speakers', readonly=True, group_operator="avg", help="Number of different speakers"),
- 'user_id': fields.many2one('res.users', 'User', readonly=True),
- 'session_id': fields.many2one('im_chat.session', 'Session', readonly=True),
- 'channel_id': fields.many2one('im_livechat.channel', 'Channel', readonly=True),
- }
_order = 'start_date, session_name'
+ _auto = False
+
+ session_name = fields.Char('Session Name', readonly=True)
+ uuid = fields.Char('UUID', readonly=True)
+ start_date = fields.Datetime('Start Date of session', readonly=True, help="Start date of the conversation")
+ start_date_hour = fields.Char('Hour of start Date of session', readonly=True)
+ duration = fields.Float('Average duration', digits=(16, 2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)")
+ time_in_session = fields.Float('Time in session', digits=(16, 2), readonly=True, group_operator="avg", help="Average time the user spend in the conversation")
+ time_to_answer = fields.Float('Time to answer', digits=(16, 2), readonly=True, group_operator="avg", help="Average time to give the first answer to the visitor")
+ nbr_messages = fields.Integer('Average message', readonly=True, group_operator="avg", help="Number of message in the conversation")
+ nbr_user_messages = fields.Integer('Average of messages/user', readonly=True, group_operator="avg", help="Average number of message per user")
+ nbr_speakers = fields.Integer('# of speakers', readonly=True, group_operator="avg", help="Number of different speakers")
+ user_id = fields.Many2one('res.users', 'User', readonly=True)
+ session_id = fields.Many2one('im_chat.session', 'Session', readonly=True)
+ channel_id = fields.Many2one('im_livechat.channel', 'Channel', readonly=True)
+
def init(self, cr):
# Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view !
diff --git a/addons/im_livechat/report/im_livechat_report_views.xml b/addons/im_livechat/report/im_livechat_report_views.xml
index f1c7d124716..71c8faf306c 100644
--- a/addons/im_livechat/report/im_livechat_report_views.xml
+++ b/addons/im_livechat/report/im_livechat_report_views.xml
@@ -1,8 +1,8 @@
<?xml version="1.0"?>
-<openerp>
+<odoo>
<data>
- <record model="ir.ui.view" id="view_livechat_support_session_pivot">
+ <record id="im_livechat_report_view_pivot" model="ir.ui.view">
<field name="name">im_livechat.report.pivot</field>
<field name="model">im_livechat.report</field>
<field name="arch" type="xml">
@@ -15,7 +15,7 @@
</field>
</record>
- <record model="ir.ui.view" id="view_livechat_support_session_graph">
+ <record id="im_livechat_report_view_graph" model="ir.ui.view">
<field name="name">im_livechat.report.graph</field>
<field name="model">im_livechat.report</field>
<field name="arch" type="xml">
@@ -26,7 +26,7 @@
</field>
</record>
- <record id="im_livechat_session_search" model="ir.ui.view">
+ <record id="im_livechat_report_view_search" model="ir.ui.view">
<field name="name">im_livechat.report.search</field>
<field name="model">im_livechat.report</field>
<field name="arch" type="xml">
@@ -49,7 +49,7 @@
</field>
</record>
- <record id="action_livechat_support_report" model="ir.actions.act_window">
+ <record id="im_livechat_report_action" model="ir.actions.act_window">
<field name="name">Livechat Support Report</field>
<field name="res_model">im_livechat.report</field>
<field name="view_type">form</field>
@@ -58,9 +58,18 @@
</record>
- <menuitem id="menu_reporting_livechat" name="Report" parent="menu_livechat_root" sequence="50" groups="group_im_livechat_manager"/>
+ <menuitem
+ id="menu_reporting_livechat"
+ name="Report"
+ parent="menu_livechat_root"
+ sequence="50"
+ groups="im_livechat_group_manager"/>
- <menuitem name="Support Statistics" parent="menu_reporting_livechat" id="livechat_support_report_session" action="action_livechat_support_report"/>
+ <menuitem
+ id="livechat_support_report_session"
+ name="Support Statistics"
+ parent="menu_reporting_livechat"
+ action="im_livechat_report_action"/>
</data>
-</openerp> \ No newline at end of file
+</odoo>
diff --git a/addons/im_livechat/security/im_livechat_channel_security.xml b/addons/im_livechat/security/im_livechat_channel_security.xml
index 8e000aacfbd..8b0e38e9bf8 100644
--- a/addons/im_livechat/security/im_livechat_channel_security.xml
+++ b/addons/im_livechat/security/im_livechat_channel_security.xml
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<openerp>
+<odoo>
<data>
<record id="module_category_im_livechat" model="ir.module.category">
@@ -7,24 +7,24 @@
<field name="sequence" eval="20" />
</record>
- <record id="group_im_livechat" model="res.groups">
+ <record id="im_livechat_group_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="module_category_im_livechat"/>
<field name="comment">The user will be able to join support channels.</field>
</record>
- <record id="group_im_livechat_manager" model="res.groups">
+ <record id="im_livechat_group_manager" model="res.groups">
<field name="name">Manager</field>
<field name="comment">The user will be able to delete support channels.</field>
<field name="category_id" ref="module_category_im_livechat"/>
- <field name="implied_ids" eval="[(4, ref('im_livechat.group_im_livechat'))]"/>
+ <field name="implied_ids" eval="[(4, ref('im_livechat.im_livechat_group_user'))]"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
- <record id="message_rule_1" model="ir.rule">
+ <record id="im_chat_message_rule_manager" model="ir.rule">
<field name="name">Live Support Managers can read messages from live support</field>
<field name="model_id" ref="im_chat.model_im_chat_message"/>
- <field name="groups" eval="[(6,0,[ref('im_livechat.group_im_livechat_manager')])]"/>
+ <field name="groups" eval="[(6,0,[ref('im_livechat.im_livechat_group_manager')])]"/>
<field name="domain_force">[('to_id.channel_id', '!=', None)]</field>
<field name="perm_unlink" eval="0"/>
<field name="perm_write" eval="0"/>
@@ -32,10 +32,10 @@
<field name="perm_create" eval="0"/>
</record>
- <record id="session_rule_1" model="ir.rule">
+ <record id="im_chat_session_rule_manager" model="ir.rule">
<field name="name">Live Support Managers can see all session from live support</field>
<field name="model_id" ref="im_chat.model_im_chat_session"/>
- <field name="groups" eval="[(6,0,[ref('im_livechat.group_im_livechat_manager')])]"/>
+ <field name="groups" eval="[(6,0,[ref('im_livechat.im_livechat_group_manager')])]"/>
<field name="domain_force">[('to_id.channel_id', '!=', None)]</field>
<field name="perm_unlink" eval="1"/>
<field name="perm_write" eval="0"/>
@@ -44,4 +44,4 @@
</record>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_livechat/security/ir.model.access.csv b/addons/im_livechat/security/ir.model.access.csv
index f571df74e69..f6cc4ed628d 100644
--- a/addons/im_livechat/security/ir.model.access.csv
+++ b/addons/im_livechat/security/ir.model.access.csv
@@ -1,8 +1,8 @@
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_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_livechat_support_report,im_livechat.report,model_im_livechat_report,group_im_livechat_manager,1,1,1,1
-access_ls_chann_rule1,im_livechat.channel.rule,model_im_livechat_channel_rule,,1,0,0,0
-access_ls_chann_rule2,im_livechat.channel.rule,model_im_livechat_channel_rule,group_im_livechat,1,1,1,0
-access_ls_chann_rule3,im_livechat.channel.rule,model_im_livechat_channel_rule,group_im_livechat_manager,1,1,1,1
+access_livechat_channel,im_livechat.channel,model_im_livechat_channel,,1,0,0,0
+access_livechat_channel_user,im_livechat.channel,model_im_livechat_channel,im_livechat_group_user,1,1,1,0
+access_livechat_channel_manager,im_livechat.channel,model_im_livechat_channel,im_livechat_group_manager,1,1,1,1
+access_livechat_support_report,im_livechat.report,model_im_livechat_report,im_livechat_group_manager,1,1,1,1
+access_livechat_channel_rule,im_livechat.channel.rule,model_im_livechat_channel_rule,,1,0,0,0
+access_livechat_channel_rule_user,im_livechat.channel.rule,model_im_livechat_channel_rule,im_livechat_group_user,1,1,1,0
+access_livechat_channel_rule_manager,im_livechat.channel.rule,model_im_livechat_channel_rule,im_livechat_group_user,1,1,1,1
diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js
index 2351a60ccd7..88c234378a0 100644
--- a/addons/im_livechat/static/src/js/im_livechat.js
+++ b/addons/im_livechat/static/src/js/im_livechat.js
@@ -259,10 +259,10 @@ var Feedback = Widget.extend({
_send_feedback: function(close){
var self = this;
var uuid = this.conversation.get('session').uuid;
- return user_session.rpc("/rating/livechat/feedback", {uuid: uuid, rate: this.rating, reason : this.reason}).then(function(res) {
+ return user_session.rpc('/rating/livechat/feedback', {uuid: uuid, rate: this.rating, reason : this.reason}).then(function(res) {
if(close){
self.trigger("feedback_sent"); // will close the conversation
- self.conversation.send_message(_.str.sprintf(_t("I rated you with :rating_%d"), self.rating), "message");
+ self.conversation.message_send(_.str.sprintf(_t("I rated you with :rating_%d"), self.rating), "message");
}
});
}
diff --git a/addons/im_livechat/views/im_chat_session_views.xml b/addons/im_livechat/views/im_chat_session_views.xml
index 294d43c40a2..fc864ddf0e6 100644
--- a/addons/im_livechat/views/im_chat_session_views.xml
+++ b/addons/im_livechat/views/im_chat_session_views.xml
@@ -2,17 +2,7 @@
<openerp>
<data>
- <!-- History : form, list and search views -->
- <record id="action_session_history" model="ir.actions.act_window">
- <field name="name">History</field>
- <field name="res_model">im_chat.session</field>
- <field name="view_type">form</field>
- <field name="view_mode">list,form</field>
- <field name="domain">[('channel_id', '!=', None)]</field>
- <field name="context">{'group_by': ['channel_id', 'create_date:month']}</field>
- </record>
-
- <record id="im_chat_session_search" model="ir.ui.view">
+ <record id="im_chat_session_view_search" model="ir.ui.view">
<field name="name">im_chat.session.search</field>
<field name="model">im_chat.session</field>
<field name="arch" type="xml">
@@ -30,7 +20,7 @@
</field>
</record>
- <record id="im_chat_session_list_view" model="ir.ui.view">
+ <record id="im_chat_session_view_tree" model="ir.ui.view">
<field name="name">im_chat.session.tree</field>
<field name="model">im_chat.session</field>
<field name="arch" type="xml">
@@ -42,7 +32,7 @@
</field>
</record>
- <record id="im_chat_session_form_view" model="ir.ui.view">
+ <record id="im_chat_session_view_form" model="ir.ui.view">
<field name="name">im_chat.session.form</field>
<field name="model">im_chat.session</field>
<field name="arch" type="xml">
@@ -87,5 +77,27 @@
</field>
</record>
+
+ <record id="im_chat_session_action" model="ir.actions.act_window">
+ <field name="name">History</field>
+ <field name="res_model">im_chat.session</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">list,form</field>
+ <field name="domain">[('channel_id', '!=', None)]</field>
+ <field name="context">{'group_by': ['channel_id', 'create_date:month']}</field>
+ </record>
+
+ <record id="im_chat_session_action_from_channel" model="ir.actions.act_window">
+ <field name="name">Sessions</field>
+ <field name="res_model">im_chat.session</field>
+ <field name="view_mode">list,form</field>
+ <field name="domain">[('channel_id', 'in', [active_id])]</field>
+ <field name="context">{
+ 'search_default_channel_id': [active_id],
+ 'default_channel_id': active_id,
+ }</field>
+ <field name="search_view_id" ref="im_chat_session_view_search"/>
+ </record>
+
</data>
</openerp>
diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml
index e9b83daa70a..28d3d130648 100644
--- a/addons/im_livechat/views/im_livechat_channel_templates.xml
+++ b/addons/im_livechat/views/im_livechat_channel_templates.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<openerp>
+<odoo>
<data>
<!-- Template rendering the external HTML support page -->
@@ -84,8 +84,8 @@
<!-- Template rendering all the scripts required to execute the Livechat from a page containing Odoo -->
<template id="internal_loader" name="All the scripts to launch the LiveSupport from an internal Web Page">
<!-- css style -->
- <link property='stylesheet' t-att-href="'%s/web/css/im_livechat.internal_lib' % (url)" rel="stylesheet"/>
- <!-- js of all the required lib (internal and external) -->
+ <link t-att-href="'%s/web/css/im_livechat.internal_lib' % (url)" rel="stylesheet"/>
+ <!-- js of all the required lib -->
<script t-att-src="'%s/web/js/im_livechat.internal_lib' % (url)" type="text/javascript" />
<!-- the loader -->
<script t-att-src="'%s/im_livechat/loader/%s/%i' % (url, dbname, channel)" type="text/javascript" />
@@ -114,7 +114,6 @@
<script type="text/javascript" src="/web/static/src/js/framework/widget.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/registry.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/session.js"></script>
- <!--<script type="text/javascript" src="/web/static/src/js/framework/model.js"></script>-->
<script type="text/javascript" src="/web/static/src/js/framework/utils.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/core.js"></script>
@@ -169,4 +168,4 @@
</template>
</data>
-</openerp>
+</odoo>
diff --git a/addons/im_livechat/views/im_livechat_channel_views.xml b/addons/im_livechat/views/im_livechat_channel_views.xml
index 964c7a897c6..d11868b0a45 100644
--- a/addons/im_livechat/views/im_livechat_channel_views.xml
+++ b/addons/im_livechat/views/im_livechat_channel_views.xml
@@ -2,10 +2,7 @@
<openerp>
<data>
- <!--
- Views for im_livechat.channel
- -->
- <record id="action_support_channels" model="ir.actions.act_window">
+ <record id="im_livechat_channel_action" model="ir.actions.act_window">
<field name="name">Live Chat Channels</field>
<field name="res_model">im_livechat.channel</field>
<field name="view_mode">kanban,form</field>
@@ -23,8 +20,8 @@
</field>
</record>
- <record id="support_channel_kanban" model="ir.ui.view">
- <field name="name">support_channel.kanban</field>
+ <record id="im_livechat_channel_view_kanban" model="ir.ui.view">
+ <field name="name">im_livechat.channel.kanban</field>
<field name="model">im_livechat.channel</field>
<field name="arch" type="xml">
<kanban>
@@ -55,8 +52,8 @@
</i>
</p>
<div style="margin-top:5px;">
- <button t-if="record.are_you_inside.raw_value" name="quit" type="object" class="btn btn-default btn-xs">Quit</button>
- <button t-if="! record.are_you_inside.raw_value" name="join" type="object" class="btn btn-info btn-xs">Join</button>
+ <button t-if="record.are_you_inside.raw_value" name="action_quit" type="object" class="btn btn-default btn-xs">Quit</button>
+ <button t-if="! record.are_you_inside.raw_value" name="action_join" type="object" class="btn btn-info btn-xs">Join</button>
</div>
</div>
</div>
@@ -68,11 +65,11 @@
</field>
</record>
- <record id="support_channel_form" model="ir.ui.view">
- <field name="name">support_channel.form</field>
+ <record id="im_livechat_channel_view_form" model="ir.ui.view">
+ <field name="name">im_livechat.channel.form</field>
<field name="model">im_livechat.channel</field>
<field name="arch" type="xml">
- <form string="Support Channels" version="7.0">
+ <form>
<sheet>
<field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/>
<div class="oe_title">
@@ -81,14 +78,13 @@
<field name="name" placeholder="e.g. YourWebsite.com"/>
</h1>
<div>
- <button type="object" name="join" class="oe_highlight" string="Join Channel" attrs='{"invisible": [["are_you_inside", "=", True]]}'/>
- <button type="object" name="quit" string="Leave Channel" attrs='{"invisible": [["are_you_inside", "=", False]]}'/>
- <button type="object" name="test_channel" string="Test" attrs='{"invisible": [["web_page", "=", False]]}'/>
+ <button type="object" name="action_join" class="oe_highlight" string="Join Channel" attrs='{"invisible": [["are_you_inside", "=", True]]}'/>
+ <button type="object" name="action_quit" string="Leave Channel" attrs='{"invisible": [["are_you_inside", "=", False]]}'/>
<field name="are_you_inside" invisible="1"/>
</div>
</div>
<div class="oe_right oe_button_box" name="buttons">
- <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible':[('nbr_session','=', 0)]}" name="%(action_session_channel)d" icon="fa-comments">
+ <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible':[('nbr_session','=', 0)]}" name="%(im_chat_session_action_from_channel)d" icon="fa-comments">
<field string="Sessions" name="nbr_session" widget="statinfo"/>
</button>
<button name="action_view_rating" attrs="{'invisible':[('rating_percentage_satisfaction','=', -1)]}" class="oe_stat_button" type="object" icon="fa-smile-o">
@@ -146,10 +142,8 @@
- <!--
- Views for im_livechat_channel_rule
- -->
- <record id="im_livechat_channel_rule_tree_view" model="ir.ui.view">
+ <!-- im_livechat.channel.rule -->
+ <record id="im_livechat_channel_rule_view_tree" model="ir.ui.view">
<field name="name">im.livechat.channel.rule.tree</field>
<field name="model">im_livechat.channel.rule</field>
<field name="arch" type="xml">
@@ -162,7 +156,7 @@
</field>
</record>
- <record id="im_livechat_channel_rule_form_view" model="ir.ui.view">
+ <record id="im_livechat_channel_rule_view_form" model="ir.ui.view">
<field name="name">im_livechat.channel.rule.form</field>
<field name="model">im_livechat.channel.rule</field>
<field name="type">form</field>
@@ -185,11 +179,27 @@
<!-- Menu items -->
- <menuitem id="menu_livechat_root" groups="group_im_livechat" name="Live Chat" sequence="34"/>
+ <menuitem
+ id="menu_livechat_root"
+ name="Live Chat"
+ groups="im_livechat_group_user"
+ sequence="34"/>
- <menuitem name="Channels" parent="menu_livechat_root" id="support_channels" action="action_support_channels" groups="group_im_livechat" sequence="5"/>
+ <menuitem
+ id="support_channels"
+ name="Channels"
+ parent="menu_livechat_root"
+ action="im_livechat_channel_action"
+ groups="im_livechat_group_user"
+ sequence="5"/>
- <menuitem name="History" parent="menu_livechat_root" id="session_history" action="action_session_history" groups="group_im_livechat" sequence="10"/>
+ <menuitem
+ id="session_history"
+ name="History"
+ parent="menu_livechat_root"
+ action="im_chat_session_action"
+ groups="im_livechat_group_user"
+ sequence="10"/>
</data>
</openerp>
diff --git a/addons/website_livechat/__init__.py b/addons/website_livechat/__init__.py
index 9f86759e32b..43dbbaa6318 100644
--- a/addons/website_livechat/__init__.py
+++ b/addons/website_livechat/__init__.py
@@ -1,2 +1,3 @@
+# -*- coding: utf-8 -*-
import controllers
import models
diff --git a/addons/website_livechat/__openerp__.py b/addons/website_livechat/__openerp__.py
index 2ef1660950c..51f3ed8a949 100644
--- a/addons/website_livechat/__openerp__.py
+++ b/addons/website_livechat/__openerp__.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
{
'name': 'Website Live Support',
'category': 'Website',
diff --git a/addons/website_livechat/controllers/__init__.py b/addons/website_livechat/controllers/__init__.py
index 8ee9bae18d9..a84d81a791b 100644
--- a/addons/website_livechat/controllers/__init__.py
+++ b/addons/website_livechat/controllers/__init__.py
@@ -1 +1,2 @@
+# -*- coding: utf-8 -*-
import main
diff --git a/addons/website_livechat/controllers/main.py b/addons/website_livechat/controllers/main.py
index 3347348c461..2b3fdbed712 100644
--- a/addons/website_livechat/controllers/main.py
+++ b/addons/website_livechat/controllers/main.py
@@ -4,12 +4,12 @@ from openerp import http
from openerp.http import request
-class website_rating_livechat(http.Controller):
+class WebsiteLivechat(http.Controller):
@http.route('/livechat/', type='http', auth="public", website=True)
def channel_list(self, **kw):
# display the list of the channel
- channels = request.env['im_livechat.channel'].sudo().search([('website_published','=', True)])
+ channels = request.env['im_livechat.channel'].search([('website_published', '=', True)])
values = {
'channels' : channels
}
diff --git a/addons/website_livechat/models/__init__.py b/addons/website_livechat/models/__init__.py
index 2f372141da0..87c8e105bca 100644
--- a/addons/website_livechat/models/__init__.py
+++ b/addons/website_livechat/models/__init__.py
@@ -1,2 +1,3 @@
-import website
+# -*- coding: utf-8 -*-
import im_livechat
+import website
diff --git a/addons/website_livechat/models/im_livechat.py b/addons/website_livechat/models/im_livechat.py
index f294869a23b..fb4e58cda49 100644
--- a/addons/website_livechat/models/im_livechat.py
+++ b/addons/website_livechat/models/im_livechat.py
@@ -3,7 +3,8 @@ from openerp import api, models, fields
from openerp.addons.website.models.website import slug
-class im_livechat_channel(models.Model):
+class ImLivechatChannel(models.Model):
+
_name = 'im_livechat.channel'
_inherit = ['im_livechat.channel', 'website.published.mixin']
diff --git a/addons/website_livechat/models/website.py b/addons/website_livechat/models/website.py
index 0bda152772c..d1892d739de 100644
--- a/addons/website_livechat/models/website.py
+++ b/addons/website_livechat/models/website.py
@@ -1,16 +1,16 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv
-from openerp import api, fields, models, SUPERUSER_ID
+from openerp import api, fields, models
from openerp.http import request
-class website(models.Model):
+class Website(models.Model):
_inherit = "website"
channel_id = fields.Many2one('im_livechat.channel', string='Live Chat Channel')
-class website_config_settings(models.TransientModel):
+class WebsiteConfigSettings(models.TransientModel):
_inherit = 'website.config.settings'
@@ -18,15 +18,16 @@ class website_config_settings(models.TransientModel):
-class ir_ui_view(osv.Model):
+class IrUiView(models.Model):
_inherit = "ir.ui.view"
- def _prepare_qcontext(self, cr, uid, context=None):
- qcontext = super(ir_ui_view, self)._prepare_qcontext(cr, uid, context=context)
+ @api.model
+ def _prepare_qcontext(self):
+ qcontext = super(IrUiView, self)._prepare_qcontext()
if request and getattr(request, 'website_enabled', False):
if request.website.channel_id:
- qcontext['website_livechat_url'] = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url')
- qcontext['website_livechat_dbname'] = cr.dbname
+ qcontext['website_livechat_url'] = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ qcontext['website_livechat_dbname'] = self._cr.dbname
qcontext['website_livechat_channel'] = request.website.channel_id.id
return qcontext
diff --git a/addons/website_livechat/security/website_livechat.xml b/addons/website_livechat/security/website_livechat.xml
index 0f22a8faecf..aa3293b05e2 100644
--- a/addons/website_livechat/security/website_livechat.xml
+++ b/addons/website_livechat/security/website_livechat.xml
@@ -2,7 +2,7 @@
<odoo>
<data>
- <record model="ir.rule" id="website_channel_public">
+ <record id="im_livechat_channel_rule_public" model="ir.rule">
<field name="name">website_livechat.channel.public</field>
<field name="model_id" ref="im_livechat.model_im_livechat_channel"/>
<field name="domain_force">[('website_published', '=', True)]</field>
diff --git a/addons/website_livechat/views/website_livechat_view.xml b/addons/website_livechat/views/website_livechat_view.xml
index f4d0a3ba118..524cc7d4a49 100644
--- a/addons/website_livechat/views/website_livechat_view.xml
+++ b/addons/website_livechat/views/website_livechat_view.xml
@@ -3,9 +3,9 @@
<data>
<record id="im_livechat_channel_form_view" model="ir.ui.view">
- <field name="name">im_livechat.channel.form.with.website_published</field>
+ <field name="name">im_livechat.channel.form.inherit.website_livechat</field>
<field name="model">im_livechat.channel</field>
- <field name="inherit_id" ref="im_livechat.support_channel_form"/>
+ <field name="inherit_id" ref="im_livechat.im_livechat_channel_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="open_website_url"
diff --git a/addons/website_livechat/website_livechat_data.xml b/addons/website_livechat/website_livechat_data.xml
index 5e37397c471..850898308f9 100644
--- a/addons/website_livechat/website_livechat_data.xml
+++ b/addons/website_livechat/website_livechat_data.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
- <openerp>
+<odoo>
<data noupdate="1">
- <record id="channel_website" model="im_livechat.channel">
+ <record id="im_livechat_channel_data_website" model="im_livechat.channel">
<field name="name">YourWebsiteWithOdoo.com</field>
<field name="default_message">Hello, how may I help you?</field>
</record>
<record id="website.default_website" model="website">
- <field name="channel_id" ref="website_livechat.channel_website"></field>
+ <field name="channel_id" ref="website_livechat.im_livechat_channel_data_website"></field>
</record>
</data>
- </openerp> \ No newline at end of file
+</odoo>