[IMP] im_chat, im_livechat : add reporting view, quit conversation, option dropdown menu, feedback for live support, better access rules and shortcode model.
This commit is contained in:
@@ -19,6 +19,8 @@ chat in real time. It support several chats in parallel.
|
||||
'security/ir.model.access.csv',
|
||||
'security/im_security.xml',
|
||||
'views/im_chat.xml',
|
||||
'views/im_chat_view.xml',
|
||||
'im_chat_data.xml'
|
||||
],
|
||||
'depends' : ['base', 'web', 'bus'],
|
||||
'qweb': ['static/src/xml/*.xml'],
|
||||
|
||||
@@ -5,10 +5,11 @@ import logging
|
||||
import time
|
||||
import uuid
|
||||
import random
|
||||
|
||||
import re
|
||||
import simplejson
|
||||
|
||||
import openerp
|
||||
import cgi
|
||||
|
||||
from openerp.http import request
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
@@ -133,6 +134,34 @@ class im_chat_session(osv.Model):
|
||||
user = self.pool['res.users'].read(cr, uid, user_id, ['name'], context=context)
|
||||
self.pool["im_chat.message"].post(cr, uid, uid, session.uuid, "meta", user['name'] + " joined the conversation.", context=context)
|
||||
|
||||
def remove_user(self, cr, uid, session_id, context=None):
|
||||
""" private implementation of removing a user from a given session (and notify the other people) """
|
||||
session = self.browse(cr, openerp.SUPERUSER_ID, session_id, context=context)
|
||||
# send a message to the conversation
|
||||
user = self.pool['res.users'].read(cr, uid, uid, ['name'], context=context)
|
||||
self.pool["im_chat.message"].post(cr, uid, uid, session.uuid, "meta", user['name'] + " left the conversation.", context=context)
|
||||
# close his session state, and remove the user from session
|
||||
self.update_state(cr, uid, session.uuid, 'closed', context=None)
|
||||
self.write(cr, uid, [session.id], {"user_ids": [(3, uid)]}, context=context)
|
||||
# notify the all the channel users and anonymous channel
|
||||
notifications = []
|
||||
for channel_user_id in session.user_ids:
|
||||
info = self.session_info(cr, channel_user_id.id, [session.id], context=context)
|
||||
notifications.append([(cr.dbname, 'im_chat.session', channel_user_id.id), info])
|
||||
# anonymous are not notified when a new user left : cannot exec session_info as uid = None
|
||||
info = self.session_info(cr, openerp.SUPERUSER_ID, [session.id], context=context)
|
||||
notifications.append([session.uuid, info])
|
||||
self.pool['bus.bus'].sendmany(cr, uid, notifications)
|
||||
|
||||
def quit_user(self, cr, uid, uuid, context=None):
|
||||
""" action of leaving a given session """
|
||||
sids = self.search(cr, uid, [('uuid', '=', uuid)], context=context, limit=1)
|
||||
for session in self.browse(cr, openerp.SUPERUSER_ID, sids, context=context):
|
||||
if uid and uid in [u.id for u in session.user_ids] and len(session.user_ids) > 2:
|
||||
self.remove_user(cr, uid, session.id, context=context)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_image(self, cr, uid, uuid, user_id, context=None):
|
||||
""" get the avatar of a user in the given session """
|
||||
#default image
|
||||
@@ -165,6 +194,20 @@ class im_chat_message(osv.Model):
|
||||
'type' : 'message',
|
||||
}
|
||||
|
||||
def _escape_keep_url(self, message):
|
||||
""" escape the message and transform the url into clickable link """
|
||||
safe_message = ""
|
||||
first = 0
|
||||
last = 0
|
||||
for m in re.finditer('(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?', message):
|
||||
last = m.start()
|
||||
safe_message += cgi.escape(message[first:last])
|
||||
safe_message += '<a href="%s" target="_blank">%s</a>' % (cgi.escape(m.group(0)), m.group(0))
|
||||
first = m.end()
|
||||
last = m.end()
|
||||
safe_message += cgi.escape(message[last:])
|
||||
return safe_message
|
||||
|
||||
def init_messages(self, cr, uid, context=None):
|
||||
""" get unread messages and old messages received less than AWAY_TIMER
|
||||
ago and the session_info for open or folded window
|
||||
@@ -210,7 +253,9 @@ class im_chat_message(osv.Model):
|
||||
session_ids = Session.search(cr, uid, [('uuid','=',uuid)], context=context)
|
||||
notifications = []
|
||||
for session in Session.browse(cr, uid, session_ids, context=context):
|
||||
# build the new message
|
||||
# build and escape the new message
|
||||
message_content = self._escape_keep_url(message_content)
|
||||
message_content = self.pool['im_chat.shortcode'].replace_shortcode(cr, uid, message_content, context=context)
|
||||
vals = {
|
||||
"from_id": from_uid,
|
||||
"to_id": session.id,
|
||||
@@ -238,6 +283,24 @@ class im_chat_message(osv.Model):
|
||||
return False
|
||||
|
||||
|
||||
class im_chat_shortcode(osv.Model):
|
||||
""" Message shortcuts """
|
||||
_name = "im_chat.shortcode"
|
||||
|
||||
_columns = {
|
||||
'source' : fields.char('Shortcut', required=True, select=True, help="The shortcut which must be replace in the Chat Messages"),
|
||||
'substitution' : fields.char('Substitution', required=True, select=True, help="The html code replacing the shortcut"),
|
||||
'description' : fields.char('Description'),
|
||||
}
|
||||
|
||||
def replace_shortcode(self, cr, uid, message, context=None):
|
||||
ids = self.search(cr, uid, [], context=context)
|
||||
for shortcode in self.browse(cr, uid, ids, context=context):
|
||||
regex = "(?:^|\s)(%s)(?:\s|$)" % re.escape(shortcode.source)
|
||||
message = re.sub(regex, " " + shortcode.substitution + " ", message)
|
||||
return message
|
||||
|
||||
|
||||
class im_chat_presence(osv.Model):
|
||||
""" im_chat_presence status can be: online, away or offline.
|
||||
This model is a one2one, but is not attached to res_users to avoid database concurrence errors
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Smiley (html code) -->
|
||||
<record id="im_chat_smiley_smile" model="im_chat.shortcode">
|
||||
<field name="source">:)</field>
|
||||
<field name="substitution">&#128522;</field>
|
||||
<field name="description">Smile</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_sad" model="im_chat.shortcode">
|
||||
<field name="source">:(</field>
|
||||
<field name="substitution">&#9785;</field>
|
||||
<field name="description">Sad</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_laugh" model="im_chat.shortcode">
|
||||
<field name="source">:D</field>
|
||||
<field name="substitution">&#128517;</field>
|
||||
<field name="description">Laugh</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_wink" model="im_chat.shortcode">
|
||||
<field name="source">;)</field>
|
||||
<field name="substitution">&#128521;</field>
|
||||
<field name="description">Wink</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_speechless" model="im_chat.shortcode">
|
||||
<field name="source">:|</field>
|
||||
<field name="substitution">&#128528;</field>
|
||||
<field name="description">Speechless</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_cheeky" model="im_chat.shortcode">
|
||||
<field name="source">:p</field>
|
||||
<field name="substitution">&#128523;</field>
|
||||
<field name="description">Cheeky</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_worried" model="im_chat.shortcode">
|
||||
<field name="source">:s</field>
|
||||
<field name="substitution">&#128534;</field>
|
||||
<field name="description">Worried</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_smirking" model="im_chat.shortcode">
|
||||
<field name="source">:/</field>
|
||||
<field name="substitution">&#128527;</field>
|
||||
<field name="description">Smirking</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_surprised" model="im_chat.shortcode">
|
||||
<field name="source">:O</field>
|
||||
<field name="substitution">&#128561;</field>
|
||||
<field name="description">Surprised</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_kitten" model="im_chat.shortcode">
|
||||
<field name="source">:kitten</field>
|
||||
<field name="substitution">&#128569;</field>
|
||||
<field name="description">Kitten</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_heart" model="im_chat.shortcode">
|
||||
<field name="source">&lt;3</field>
|
||||
<field name="substitution">&#9829;</field>
|
||||
<field name="description">Heart</field>
|
||||
</record>
|
||||
|
||||
<!-- Smiley image -->
|
||||
<record id="im_chat_smiley_pinky" model="im_chat.shortcode">
|
||||
<field name="source">:pinky</field>
|
||||
<field name="substitution"><img src='/im_chat/static/src/img/pinky.png'/></field>
|
||||
<field name="description">Pinky</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_smiley_musti" model="im_chat.shortcode">
|
||||
<field name="source">:musti</field>
|
||||
<field name="substitution"><img src='/im_chat/static/src/img/musti.png'/></field>
|
||||
<field name="description">Musti</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -3,7 +3,4 @@ access_im_chat_message,im_chat.message,model_im_chat_message,base.group_user,1,0
|
||||
access_im_chat_session,im_chat.session,model_im_chat_session,base.group_user,1,1,1,0
|
||||
access_im_chat_conversation_state,im_chat.conversation_state,model_im_chat_conversation_state,base.group_user,1,1,1,0
|
||||
access_im_chat_presence,im_chat.presence,model_im_chat_presence,base.group_user,1,1,1,1
|
||||
access_im_chat_message_portal,im_chat.message,model_im_chat_message,base.group_portal,1,0,1,0
|
||||
access_im_chat_session_portal,im_chat.session,model_im_chat_session,base.group_portal,1,1,1,0
|
||||
access_im_chat_conversation_state_portal,im_chat.conversation_state,model_im_chat_conversation_state,base.group_portal,1,1,1,0
|
||||
access_im_chat_presence_portal,im_chat.presence,model_im_chat_presence,base.group_portal,1,1,1,1
|
||||
access_im_chat_shortcode,im_chat.shortcode,model_im_chat_shortcode,base.group_user,1,1,1,1
|
||||
|
||||
|
@@ -93,6 +93,25 @@
|
||||
float: right;
|
||||
color: gray ;
|
||||
}
|
||||
|
||||
/* options */
|
||||
.oe_im_chatview .oe_im_chatview_option_group{
|
||||
z-index: 10;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_option_list{
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_option_list li{
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.oe_im_chatview .oe_im_chatview_right div, .oe_im_chatview .oe_im_chatview_right button{
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
className: "openerp_style oe_im_chatview",
|
||||
events: {
|
||||
"keydown input": "keydown",
|
||||
"click .oe_im_chatview_options" : "click_options",
|
||||
"click .oe_im_chatview_close": "click_close",
|
||||
"click .oe_im_chatview_header": "click_header"
|
||||
},
|
||||
@@ -207,6 +208,38 @@
|
||||
self.$().show();
|
||||
// prepare the header and the correct state
|
||||
self.update_session();
|
||||
|
||||
self.init_options();
|
||||
},
|
||||
init_options: function(){
|
||||
this.define_options();
|
||||
if(this.$('.oe_im_chatview_option_list li').length === 0){
|
||||
this.$('.oe_im_chatview_option_group').hide();
|
||||
}
|
||||
},
|
||||
define_options: function(){
|
||||
// add here the option to put in the dropdown menu
|
||||
this._add_option("Shortcuts", "option_shortcut", "fa fa-info-circle");
|
||||
this.$('.oe_im_chatview_option_list .option_shortcut').on('click', _.bind(this.click_option_shortcut, this));
|
||||
// quit the conversation
|
||||
this._add_option('Quit discussion', 'im_chat_option_quit', 'fa fa-minus-square');
|
||||
this.$('.oe_im_chatview_option_list .im_chat_option_quit').on('click', this, _.bind(this.action_quit_conversation, this));
|
||||
|
||||
},
|
||||
_add_option: function(label, style_class, icon_fa_class){
|
||||
if(icon_fa_class){
|
||||
label = '<i class="'+icon_fa_class+'"></i> ' + label;
|
||||
}
|
||||
this.$('.oe_im_chatview_option_list').append('<li class="'+style_class+'">'+label+'</li>');
|
||||
},
|
||||
action_quit_conversation: function(){
|
||||
var self = this;
|
||||
var Session = new openerp.Model("im_chat.session");
|
||||
return Session.call("quit_user", [this.get("session").uuid]).then(function(res) {
|
||||
if(! res){
|
||||
self.do_warn(_t("Warning"), _t("You are only 2 identified users. Just close the conversation to leave."));
|
||||
}
|
||||
});
|
||||
},
|
||||
show: function(){
|
||||
this.$().animate({
|
||||
@@ -276,6 +309,7 @@
|
||||
this.set("pending", this.get("pending") + 1);
|
||||
}
|
||||
this.insert_messages([message]);
|
||||
this._go_bottom();
|
||||
},
|
||||
send_message: function(message, type) {
|
||||
var self = this;
|
||||
@@ -292,15 +326,22 @@
|
||||
},
|
||||
insert_messages: function(messages){
|
||||
var self = this;
|
||||
var get_anonymous_name = function(){
|
||||
var name = self.options["defaultUsername"];
|
||||
_.each(self.get('session').users, function(u){
|
||||
if(!u.id){
|
||||
name = u.name;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
};
|
||||
// avoid duplicated messages
|
||||
messages = _.filter(messages, function(m){ return !_.contains(_.pluck(self.get("messages"), 'id'), m.id) ; });
|
||||
// escape the message content and set the timezone
|
||||
_.map(messages, function(m){
|
||||
if(!m.from_id){
|
||||
m.from_id = [false, self.options["defaultUsername"]];
|
||||
m.from_id = [false, get_anonymous_name()];
|
||||
}
|
||||
m.message = self.escape_keep_url(m.message);
|
||||
m.message = self.smiley(m.message);
|
||||
m.create_date = Date.parse(m.create_date).setTimezone("UTC").toString("yyyy-MM-dd HH:mm:ss");
|
||||
return m;
|
||||
});
|
||||
@@ -355,62 +396,6 @@
|
||||
this.$("input").val("");
|
||||
this.send_message(mes, "message");
|
||||
},
|
||||
get_smiley_list: function(){
|
||||
var kitten = jQuery.deparam !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined;
|
||||
var smileys = {
|
||||
":'(": "😢",
|
||||
":O" : "😱",
|
||||
"3:)": "😈",
|
||||
":)" : "😊",
|
||||
":D" : "😅",
|
||||
";)" : "😉",
|
||||
":p" : "😋",
|
||||
":(" : "☹",
|
||||
":|" : "😐",
|
||||
":/" : "😏",
|
||||
"8)" : "😳",
|
||||
":s" : "😖",
|
||||
":pinky" : "<img src='/im_chat/static/src/img/pinky.png'/>",
|
||||
":musti" : "<img src='/im_chat/static/src/img/musti.png'/>",
|
||||
};
|
||||
if(kitten){
|
||||
_.extend(smileys, {
|
||||
":)" : "😺",
|
||||
":D" : "😹",
|
||||
";)" : "😼",
|
||||
":p" : "😽",
|
||||
":(" : "🙀",
|
||||
":|" : "😿",
|
||||
});
|
||||
}
|
||||
return smileys;
|
||||
},
|
||||
smiley: function(str){
|
||||
var re_escape = function(str){
|
||||
return String(str).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1');
|
||||
};
|
||||
var smileys = this.get_smiley_list();
|
||||
_.each(_.keys(smileys), function(key){
|
||||
str = str.replace( new RegExp("(?:^|\\s)(" + re_escape(key) + ")(?:\\s|$)"), ' <span class="smiley">'+smileys[key]+'</span> ');
|
||||
});
|
||||
return str;
|
||||
},
|
||||
escape_keep_url: function(str){
|
||||
var url_regex = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/gi;
|
||||
var last = 0;
|
||||
var txt = "";
|
||||
while (true) {
|
||||
var result = url_regex.exec(str);
|
||||
if (! result)
|
||||
break;
|
||||
txt += _.escape(str.slice(last, result.index));
|
||||
last = url_regex.lastIndex;
|
||||
var url = _.escape(result[0]);
|
||||
txt += '<a href="' + url + '" target="_blank">' + url + '</a>';
|
||||
}
|
||||
txt += _.escape(str.slice(last, str.length));
|
||||
return txt;
|
||||
},
|
||||
_go_bottom: function() {
|
||||
this.$(".oe_im_chatview_content").scrollTop(this.$(".oe_im_chatview_content").get(0).scrollHeight);
|
||||
},
|
||||
@@ -420,11 +405,27 @@
|
||||
focus: function() {
|
||||
this.$(".oe_im_chatview_input").focus();
|
||||
},
|
||||
click_header: function(){
|
||||
this.update_fold_state();
|
||||
click_options: function(e){
|
||||
this.$('.oe_im_chatview_options').dropdown();
|
||||
},
|
||||
click_option_shortcut: function(){
|
||||
openerp.client.action_manager.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'im_chat.shortcode',
|
||||
view_mode: 'tree,form',
|
||||
view_type: 'tree',
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
target: "current",
|
||||
limit: 80,
|
||||
});
|
||||
},
|
||||
click_header: function(event){
|
||||
var classes = event.target.className.split(' ');
|
||||
if(_.contains(classes, 'oe_im_chatview_header_name') || _.contains(classes, 'oe_im_chatview_header')){
|
||||
this.update_fold_state();
|
||||
}
|
||||
},
|
||||
click_close: function(event) {
|
||||
event.stopPropagation();
|
||||
this.update_fold_state('closed');
|
||||
},
|
||||
destroy: function() {
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
<span class="oe_im_chatview_header_name"></span>
|
||||
<span class="oe_im_chatview_nbr_messages"/>
|
||||
<span class="oe_im_chatview_right">
|
||||
<div class="btn-group oe_im_chatview_option_group">
|
||||
<span class="oe_im_chatview_options dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cogs"></i>
|
||||
</span>
|
||||
<ul class="dropdown-menu oe_im_chatview_option_list" role="menu">
|
||||
<!-- options -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="oe_im_chatview_close">×</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="action_im_chat_shortcode" 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>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define a new chat shortcode.
|
||||
</p><p>
|
||||
A shortcode is a keyboard shortcut. For instance, you type #gm and it will be transformed into "Good Morning".
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_shortcode_tree_view" model="ir.ui.view">
|
||||
<field name="name">im_chat.shortcode.tree</field>
|
||||
<field name="model">im_chat.shortcode</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Shortcodes">
|
||||
<field name="source"/>
|
||||
<field name="substitution"/>
|
||||
<field name="description"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_shortcode_form_view" model="ir.ui.view">
|
||||
<field name="name">im_chat.shortcode.form</field>
|
||||
<field name="model">im_chat.shortcode</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Session Form" version="7.0">
|
||||
<sheet>
|
||||
<group colspan="2" col="2">
|
||||
<field name="source"/>
|
||||
<field name="substitution"/>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -1 +1,2 @@
|
||||
import im_livechat
|
||||
import report
|
||||
@@ -20,7 +20,9 @@ chat operators.
|
||||
"security/im_livechat_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/im_livechat_view.xml",
|
||||
"views/im_livechat.xml"
|
||||
"views/im_livechat.xml",
|
||||
"report/im_livechat_report.xml",
|
||||
"im_livechat_data.xml"
|
||||
],
|
||||
'demo': [
|
||||
"im_livechat_demo.xml",
|
||||
|
||||
@@ -23,11 +23,14 @@ import random
|
||||
import openerp
|
||||
import json
|
||||
import openerp.addons.im_chat.im_chat
|
||||
import datetime
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp import tools
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
|
||||
class im_livechat_channel(osv.Model):
|
||||
_name = 'im_livechat.channel'
|
||||
@@ -173,6 +176,7 @@ class im_livechat_channel(osv.Model):
|
||||
return True
|
||||
|
||||
class im_chat_session(osv.Model):
|
||||
|
||||
_inherit = 'im_chat.session'
|
||||
|
||||
def _get_fullname(self, cr, uid, ids, fields, arg, context=None):
|
||||
@@ -190,8 +194,11 @@ class im_chat_session(osv.Model):
|
||||
|
||||
_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"),
|
||||
'feedback_rating': fields.selection([('10','Good'),('5','Ok'),('1','Bad')], 'Grade', help='Feedback from user (Bad, Ok or Good)'),
|
||||
'feedback_reason': fields.text('Reason', help="Reason explaining the rating."),
|
||||
}
|
||||
|
||||
def is_in_session(self, cr, uid, uuid, user_id, context=None):
|
||||
@@ -212,6 +219,18 @@ class im_chat_session(osv.Model):
|
||||
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)
|
||||
|
||||
class LiveChatController(http.Controller):
|
||||
|
||||
@@ -249,3 +268,13 @@ class LiveChatController(http.Controller):
|
||||
with reg.cursor() as cr:
|
||||
return len(reg.get('im_livechat.channel').get_available_users(cr, uid, channel)) > 0
|
||||
|
||||
|
||||
@http.route('/im_livechat/feedback', type='json', auth="none")
|
||||
def feedback(self, uuid, rating, reason=None):
|
||||
cr, uid, context, db = request.cr, request.uid or openerp.SUPERUSER_ID, request.context, request.db
|
||||
registry = openerp.modules.registry.RegistryManager.get(db)
|
||||
Session = registry['im_chat.session']
|
||||
session_ids = Session.search(cr, uid, [('uuid','=',uuid)], context=context)
|
||||
Session.write(cr, uid, session_ids, {'feedback_rating' : str(rating), 'feedback_reason' : reason}, context=context)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
|
||||
<data noupdate="1">
|
||||
<!-- After installation of the module, open the related menu -->
|
||||
<record id="action_client_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="state">open</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data>
|
||||
<!-- Custom Filters -->
|
||||
<record id="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','>', 1)]</field>
|
||||
<field name="context">{'group_by': ['start_date:day', 'uuid']}</field>
|
||||
<field name="is_default">v</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="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','>', (context_today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d') )]</field>
|
||||
<field name="context">{'group_by': ['start_date:day', 'uuid']}</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="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','>', 1), ('user_id', '!=', False)]</field>
|
||||
<field name="context">{'group_by': ['start_date:month', 'user_id', 'uuid']}</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1 @@
|
||||
import im_livechat_report
|
||||
@@ -0,0 +1,55 @@
|
||||
from openerp.osv import fields,osv
|
||||
from openerp import tools
|
||||
|
||||
|
||||
class im_livechat_report(osv.Model):
|
||||
""" Livechat Support Report """
|
||||
_name = "im_livechat.report"
|
||||
_auto = False
|
||||
_description = "Livechat Support Report"
|
||||
_columns = {
|
||||
'uuid': fields.char('UUID', size=50, 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"),
|
||||
'rating': fields.float('Rating', readonly=True, group_operator="avg", help="Average Rating"),
|
||||
'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, uuid'
|
||||
|
||||
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 !
|
||||
tools.drop_view_if_exists(cr, 'im_livechat_report')
|
||||
cr.execute("""
|
||||
CREATE OR REPLACE VIEW im_livechat_report AS (
|
||||
SELECT
|
||||
min(M.id) as id,
|
||||
S.uuid as uuid,
|
||||
S.create_date as start_date,
|
||||
to_char(date_trunc('hour', S.create_date), 'YYYY-MM-DD HH24:MI:SS') as start_date_hour,
|
||||
EXTRACT('epoch' from ((SELECT (max(create_date)-min(create_date)) FROM im_chat_message WHERE to_id=S.id AND from_id = U.id))) as time_in_session,
|
||||
EXTRACT('epoch' from ((SELECT min(create_date) FROM im_chat_message WHERE to_id=S.id AND from_id IS NOT NULL)-(SELECT min(create_date) FROM im_chat_message WHERE to_id=S.id AND from_id IS NULL))) as time_to_answer,
|
||||
EXTRACT('epoch' from (max((SELECT (max(create_date)) FROM im_chat_message WHERE to_id=S.id))-S.create_date)) as duration,
|
||||
(SELECT count(distinct COALESCE(from_id, 0)) FROM im_chat_message WHERE to_id=S.id) as nbr_speakers,
|
||||
(SELECT count(id) FROM im_chat_message WHERE to_id=S.id) as nbr_messages,
|
||||
count(M.id) as nbr_user_messages,
|
||||
CAST(S.feedback_rating AS INT) as rating,
|
||||
U.id as user_id,
|
||||
S.channel_id as channel_id
|
||||
FROM im_chat_message M
|
||||
LEFT JOIN im_chat_session S on (S.id = M.to_id)
|
||||
LEFT JOIN res_users U on (U.id = M.from_id)
|
||||
WHERE S.channel_id IS NOT NULL
|
||||
GROUP BY U.id, M.to_id, S.id
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="view_livechat_support_session_graph">
|
||||
<field name="name">im_livechat.report.graph</field>
|
||||
<field name="model">im_livechat.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Livechat Support Statistics" type="pivot">
|
||||
<field name="uuid" type="row"/>
|
||||
<field name="duration" type="measure"/>
|
||||
<field name="nbr_messages" type="measure"/>
|
||||
<field name="time_to_answer" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_livechat_session_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">
|
||||
<search string="Search report">
|
||||
<filter name="missed_session" string="Missed sessions" domain="[('nbr_speakers','<=', 1)]"/>
|
||||
<filter name="treated_session" string="Treated sessions" domain="[('nbr_speakers','>', 1)]"/>
|
||||
<filter name="last_24h" string="Last 24h" domain="[('start_date','>', (context_today() - datetime.timedelta(days=1)).strftime('%%Y-%%m-%%d') )]"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter name="group_by_session" string="Session" domain="[]" context="{'group_by':'uuid'}"/>
|
||||
<filter name="group_by_channel" string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
|
||||
<filter name="group_by_user" string="Operator" domain="[('user_id','!=', False)]" context="{'group_by':'user_id'}"/>
|
||||
<separator orientation="vertical" />
|
||||
<filter name="group_by_hour" string="Creation date (hour)" domain="[]" context="{'group_by':'start_date_hour'}"/>
|
||||
<filter name="group_by_day" string="Creation date (day)" domain="[]" context="{'group_by':'start_date:day'}"/>
|
||||
<filter name="group_by_week" string="Creation date (week)" domain="[]" context="{'group_by':'start_date:week'}"/>
|
||||
<filter name="group_by_month" string="Creation date (month)" domain="[]" context="{'group_by':'start_date:month'}" />
|
||||
<filter name="group_by_year" string="Creation date (year)" domain="[]" context="{'group_by':'start_date:year'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_livechat_support_report" 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>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="view_livechat_support_session_graph"></field>
|
||||
<field name="help">Livechat Support Analysis allows you to easily check and analyse your company livechat session performance. Extract informations about the missed sessions, the audiance, the duration of a session, etc.</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem id="menu_reporting_livechat" name="Livechat" parent="base.menu_reporting" sequence="50"/>
|
||||
|
||||
<menuitem name="Support Statistics" parent="menu_reporting_livechat" id="livechat_support_report_session" action="action_livechat_support_report" groups="group_im_livechat_manager" />
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -32,5 +32,16 @@
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record id="session_rule_1" 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="domain_force">[('to_id.channel_id', '!=', None)]</field>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -2,3 +2,4 @@ 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
|
||||
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/* feedback */
|
||||
.oe_im_chatview_feedback{
|
||||
padding: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_text {
|
||||
color: white;
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_small_text {
|
||||
color: white;
|
||||
vertical-align: middle;
|
||||
font-size: 12px;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_choices{
|
||||
height:40px;
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_choices a{
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
opacity: 0.65;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_choices a:hover{
|
||||
opacity:1;
|
||||
}
|
||||
.oe_im_chatview_feedback .oe_im_feedback_choices a.selected{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* feedback reason */
|
||||
.oe_im_feedback_reason {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.oe_im_feedback_reason textarea {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
resize: none;
|
||||
}
|
||||
.oe_im_feedback_btn input{
|
||||
float: right;
|
||||
margin-left : 10px;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 962 B |
Binary file not shown.
|
After Width: | Height: | Size: 886 B |
Binary file not shown.
|
After Width: | Height: | Size: 739 B |
@@ -20,6 +20,12 @@
|
||||
this._super.apply(this, arguments);
|
||||
this.shown = true;
|
||||
this.loading_history = true; // since the session is kept after a refresh, anonymous can reload their history
|
||||
|
||||
this.feedback = false;
|
||||
this.rating = false;
|
||||
},
|
||||
define_options: function(){
|
||||
// no options for anonymous user
|
||||
},
|
||||
show: function(){
|
||||
this._super.apply(this, arguments);
|
||||
@@ -50,10 +56,52 @@
|
||||
this.set('session', session);
|
||||
openerp.set_cookie(im_livechat.COOKIE_NAME, JSON.stringify(session), 60*60);
|
||||
},
|
||||
click_close: function(e) {
|
||||
this._super(e);
|
||||
openerp.set_cookie(im_livechat.COOKIE_NAME, "", -1);
|
||||
click_close: function(event) {
|
||||
this.$('.oe_im_chatview_input').attr('disabled','disabled');
|
||||
if(!this.feedback && (this.get('messages').length > 1)){
|
||||
this.feedback = true;
|
||||
this.$(".oe_im_chatview_content").html(openerp.qweb.render("support_feedback"));
|
||||
this.$('.oe_im_feedback_choices a').on('click', _.bind(this.choose_feedback, this));
|
||||
this.$('.oe_im_chatview_feedback #submit').on('click', _.bind(this.submit_feedback, this));
|
||||
}else{
|
||||
if(this.rating){
|
||||
this.send_feedback(this.rating);
|
||||
}
|
||||
// delete cookie
|
||||
openerp.set_cookie(im_livechat.COOKIE_NAME, "", -1);
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
choose_feedback: function(e){
|
||||
var self = this;
|
||||
this.rating = parseInt($(e.currentTarget).data('value'));
|
||||
this.$('.oe_im_feedback_choices a').removeClass('selected');
|
||||
this.$('.oe_im_feedback_choices a[data-value="'+this.rating+'"]').addClass('selected');
|
||||
},
|
||||
submit_feedback: function(e){
|
||||
var self = this;
|
||||
if(this.rating){
|
||||
var reason = self.$('.oe_im_chatview_feedback textarea').val();
|
||||
if(this.rating > 1){
|
||||
this.send_feedback(this.rating, reason);
|
||||
}else{
|
||||
if(reason){
|
||||
this.send_feedback(this.rating, reason);
|
||||
}else{
|
||||
self.$('.oe_im_chatview_feedback textarea').css('border', 'red solid 2px');
|
||||
}
|
||||
}
|
||||
}else{
|
||||
this.feedback = false;
|
||||
}
|
||||
},
|
||||
send_feedback: function(rate, reason){
|
||||
var self = this;
|
||||
openerp.session.rpc("/im_livechat/feedback", {uuid: this.get('session').uuid, rating: rate, reason : reason}).then(function(res) {
|
||||
self.$(".oe_im_chatview_feedback").html($(document.createElement('div')).addClass("oe_im_feedback_text").html(_t("Thanks you for your feedback. You can close the chat window now.")));
|
||||
});
|
||||
this.rating = false;
|
||||
}
|
||||
});
|
||||
|
||||
// To avoid exeption when the anonymous has close his
|
||||
@@ -79,7 +127,7 @@
|
||||
|
||||
im_livechat.LiveSupport = openerp.Widget.extend({
|
||||
init: function(server_url, db, channel, options) {
|
||||
var self = this;
|
||||
this._super();
|
||||
options = options || {};
|
||||
_.defaults(options, {
|
||||
buttonText: _t("Chat with one of our collaborators"),
|
||||
@@ -88,6 +136,10 @@
|
||||
defaultUsername: _t("Visitor"),
|
||||
});
|
||||
openerp.session = new openerp.Session(null, server_url, { use_cors: false });
|
||||
this.load_template(db, channel, options);
|
||||
},
|
||||
load_template: function(db, channel, options){
|
||||
var self = this;
|
||||
// load the qweb templates
|
||||
var defs = [];
|
||||
var templates = ['/im_livechat/static/src/xml/im_livechat.xml', '/im_chat/static/src/xml/im_chat.xml'];
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates>
|
||||
<t t-name="chatButton">
|
||||
<t t-esc="widget.text"/>
|
||||
</t>
|
||||
|
||||
<t t-name="chatButton">
|
||||
<t t-esc="widget.text"/>
|
||||
</t>
|
||||
|
||||
<t t-name="support_feedback">
|
||||
<div class="oe_im_chatview_feedback">
|
||||
<div class="oe_im_feedback_text">
|
||||
Please take a second to rate our Livechat Support ...
|
||||
</div>
|
||||
<div class="oe_im_feedback_choices">
|
||||
<a data-value="10" name="good" title="Good"><img src='/im_livechat/static/src/img/good.png'/></a>
|
||||
<a data-value="5" name="ok" title="OK"><img src='/im_livechat/static/src/img/ok.png'/></a>
|
||||
<a data-value="1" name="bad" title="Bad"><img src='/im_livechat/static/src/img/bad.png'/></a>
|
||||
</div>
|
||||
<div class="oe_im_feedback_small_text">
|
||||
Click on the icon that represent the most your feeling.
|
||||
</div>
|
||||
<div class="oe_im_feedback_reason">
|
||||
<textarea id="reason" placeholder="Explain your note"></textarea>
|
||||
</div>
|
||||
<div class="oe_im_feedback_btn">
|
||||
<input type="button" id="submit" value="Send" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -119,6 +119,8 @@
|
||||
<script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script>
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="/im_chat/static/src/css/im_common.css"></link>
|
||||
<!-- Style for livechat extern only -->
|
||||
<link rel="stylesheet" href="/im_livechat/static/src/css/im_livechat.css"></link>
|
||||
</template>
|
||||
|
||||
<!-- the js code to initialize the LiveSupport object -->
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<data>
|
||||
<menuitem id="im_livechat" name="Live Chat" parent="mail.mail_feeds_main" groups="group_im_livechat"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_support_channels">
|
||||
<!-- Support Channel -->
|
||||
<record id="action_support_channels" 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,7 +24,7 @@
|
||||
<menuitem name="Channels" parent="im_livechat" id="support_channels" action="action_support_channels" groups="group_im_livechat"/>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="support_channel_kanban">
|
||||
<record id="support_channel_kanban" model="ir.ui.view">
|
||||
<field name="name">support_channel.kanban</field>
|
||||
<field name="model">im_livechat.channel</field>
|
||||
<field name="arch" type="xml">
|
||||
@@ -121,43 +122,79 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_history">
|
||||
|
||||
<!-- Session History for Support -->
|
||||
<record id="action_session_history" model="ir.actions.act_window">
|
||||
<field name="name">History</field>
|
||||
<field name="res_model">im_chat.message</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="domain">[('to_id.channel_id', '!=', None)]</field>
|
||||
<field name="context">{'search_default_group_by_to_id': 1}</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>
|
||||
<menuitem name="History" parent="im_livechat" id="history" action="action_history" groups="group_im_livechat_manager"/>
|
||||
<menuitem name="History" parent="im_livechat" id="session_history" action="action_session_history" groups="group_im_livechat"/>
|
||||
|
||||
<record id="im_message_form" model="ir.ui.view">
|
||||
<field name="name">im.chat.message.tree</field>
|
||||
<field name="model">im_chat.message</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="History" create="false">
|
||||
<field name="to_id"/>
|
||||
<field name="create_date"/>
|
||||
<field name="from_id"/>
|
||||
<field name="message"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_message_search" model="ir.ui.view">
|
||||
<field name="name">im.chat.message.search</field>
|
||||
<field name="model">im_chat.message</field>
|
||||
<record id="im_chat_session_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">
|
||||
<search string="Search history">
|
||||
<filter name="to_id" string="My Sessions" domain="[('to_id.user_ids','in', uid)]"/>
|
||||
<field name="from_id"/>
|
||||
<field name="to_id"/>
|
||||
<filter name="id" string="My sessions" domain="[('user_ids','in', uid)]"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter name="group_by_to_id" string="Session" domain="[]" context="{'group_by':'to_id'}"/>
|
||||
<filter name="group_by_date" string="Date" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
<filter name="group_by_channel" string="Channel" domain="[]" context="{'group_by':'channel_id'}"/>
|
||||
<separator orientation="vertical" />
|
||||
<filter name="group_by_day" string="Creation date (day)" domain="[]" context="{'group_by':'create_date:day'}"/>
|
||||
<filter name="group_by_week" string="Creation date (week)" domain="[]" context="{'group_by':'create_date:week'}"/>
|
||||
<filter name="group_by_month" string="Creation date (month)" domain="[]" context="{'group_by':'create_date:month'}" />
|
||||
<filter name="group_by_year" string="Creation date (year)" domain="[]" context="{'group_by':'create_date:year'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_session_list_view" model="ir.ui.view">
|
||||
<field name="name">im_chat.session.tree</field>
|
||||
<field name="model">im_chat.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="History" create="false">
|
||||
<field name="id"/>
|
||||
<field name="fullname"/>
|
||||
<field name="uuid"/>
|
||||
<field name="create_date"/>
|
||||
<field name="feedback_rating"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="im_chat_session_form_view" model="ir.ui.view">
|
||||
<field name="name">im_chat.session.form</field>
|
||||
<field name="model">im_chat.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Session Form" version="7.0" create="false" edit="false">
|
||||
<sheet>
|
||||
<group>
|
||||
<group string="General">
|
||||
<field name="id"/>
|
||||
<field name="uuid" readonly="1"/>
|
||||
<field name="fullname" widget="html"/>
|
||||
<field name="create_date" readonly="1"/>
|
||||
</group>
|
||||
<group string="Feedback">
|
||||
<field name="feedback_rating"/>
|
||||
<field name="feedback_reason"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="message_ids" type="tree">
|
||||
<tree string="History">
|
||||
<field name="create_date" />
|
||||
<field name="from_id" />
|
||||
<field name="message" />
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
Reference in New Issue
Block a user