- [[ format(get_text(o,data['form']['followup_id'])) ]]
+ [[ format(get_text(o,data['form']['followup_id'])) ]]
diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py
index 1e957a184c3..7b3d88af154 100644
--- a/addons/auth_ldap/users_ldap.py
+++ b/addons/auth_ldap/users_ldap.py
@@ -99,6 +99,9 @@ class CompanyLDAP(osv.osv):
filter = filter_format(conf['ldap_filter'], (login,))
try:
results = self.query(conf, filter)
+
+ # Get rid of (None, attrs) for searchResultReference replies
+ results = [i for i in results if i[0]]
if results and len(results) == 1:
dn = results[0][0]
conn = self.connect(conf)
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index f0862601611..a8efc343420 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -1330,13 +1330,13 @@ class calendar_event(osv.Model):
new_id = get_real_ids(arg[2])
new_arg = (arg[0], arg[1], new_id)
new_args.append(new_arg)
- #offset, limit and count must be treated separately as we may need to deal with virtual ids
- if context.get('virtual_id', True):
- res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
- res = self.get_recurrent_ids(cr, uid, res, args, order=order, context=context)
- else:
- res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=order, context=context, count=False)
+ if not context.get('virtual_id', True):
+ return super(calendar_event, self).search(cr, uid, new_args, offset=offset, limit=limit, order=order, context=context, count=count)
+
+ # offset, limit, order and count must be treated separately as we may need to deal with virtual ids
+ res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
+ res = self.get_recurrent_ids(cr, uid, res, args, order=order, context=context)
if count:
return len(res)
elif limit:
diff --git a/addons/calendar/calendar_view.xml b/addons/calendar/calendar_view.xml
index 93649d046a1..01c4b596592 100644
--- a/addons/calendar/calendar_view.xml
+++ b/addons/calendar/calendar_view.xml
@@ -313,7 +313,7 @@
Meetings
calendar.event
form,calendar,tree,gantt
-
+
diff --git a/addons/decimal_precision/decimal_precision.py b/addons/decimal_precision/decimal_precision.py
index 27ce5641559..9af09704b61 100644
--- a/addons/decimal_precision/decimal_precision.py
+++ b/addons/decimal_precision/decimal_precision.py
@@ -50,9 +50,9 @@ class decimal_precision(orm.Model):
self.precision_get.clear_cache(self)
for obj in self.pool.obj_list():
for colname, col in self.pool.get(obj)._columns.items():
- if isinstance(col, (fields.float, fields.function)):
+ if hasattr(col, 'digits_change'):
col.digits_change(cr)
- RegistryManager.signal_registry_change(cr.dbname)
+ RegistryManager.signal_caches_change(cr.dbname)
def create(self, cr, uid, data, context=None):
res = super(decimal_precision, self).create(cr, uid, data, context=context)
diff --git a/addons/document_ftp/ftpserver/abstracted_fs.py b/addons/document_ftp/ftpserver/abstracted_fs.py
index 4a2d6e5e1ec..514ba10f2f6 100644
--- a/addons/document_ftp/ftpserver/abstracted_fs.py
+++ b/addons/document_ftp/ftpserver/abstracted_fs.py
@@ -262,6 +262,7 @@ class abstracted_fs(object):
if path == '/' and mode in ('list', 'cwd'):
return (None, None, None )
+ if path == '..': path = self.cwd + '/..'
path = _to_unicode(os.path.normpath(path)) # again, for '/db/../ss'
if path == '.': path = ''
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 29d1cb3a66d..fcfe869da90 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -417,8 +417,8 @@ class email_template(osv.osv):
# create a mail_mail based on values, without attachments
values = self.generate_email(cr, uid, template_id, res_id, context=context)
- assert values.get('email_from'), 'email_from is missing or empty after template rendering, send_mail() cannot proceed'
-
+ if not values.get('email_from'):
+ raise osv.except_osv(_('Warning!'),_("Sender email is missing or empty after template rendering. Specify one to deliver your message"))
# process partner_to field that is a comma separated list of partner_ids -> recipient_ids
# NOTE: only usable if force_send is True, because otherwise the value is
# not stored on the mail_mail, and therefore lost -> fixed in v8
diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py
index df0119ed302..16df99a5f77 100644
--- a/addons/hw_escpos/controllers/main.py
+++ b/addons/hw_escpos/controllers/main.py
@@ -21,6 +21,7 @@ except ImportError:
try:
from .. import escpos
from ..escpos import printer
+ from ..escpos import supported_devices
except ImportError:
escpos = printer = None
@@ -39,21 +40,16 @@ class EscposDriver(Thread):
self.queue = Queue()
self.status = {'status':'connecting', 'messages':[]}
- self.supported_printers = [
- { 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
- { 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
- ]
-
- def connected_usb_devices(self,devices):
+ def connected_usb_devices(self):
connected = []
- for device in devices:
+ for device in supported_devices.device_list:
if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
connected.append(device)
return connected
def get_escpos_printer(self):
try:
- printers = self.connected_usb_devices(self.supported_printers)
+ printers = self.connected_usb_devices()
if len(printers) > 0:
self.set_status('connected','Connected to '+printers[0]['name'])
return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
diff --git a/addons/hw_escpos/escpos/__init__.py b/addons/hw_escpos/escpos/__init__.py
index 22a5af61029..3fdeddee28b 100644
--- a/addons/hw_escpos/escpos/__init__.py
+++ b/addons/hw_escpos/escpos/__init__.py
@@ -1 +1 @@
-__all__ = ["constants","escpos","exceptions","printer"]
+__all__ = ["constants","escpos","exceptions","printer","supported_devices"]
diff --git a/addons/hw_escpos/escpos/supported_devices.py b/addons/hw_escpos/escpos/supported_devices.py
new file mode 100644
index 00000000000..2c6a6be2d07
--- /dev/null
+++ b/addons/hw_escpos/escpos/supported_devices.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+# This is a list of esc/pos compatible usb printers. The vendor and product ids can be found by
+# typing lsusb in a linux terminal, this will give you the ids in the form ID VENDOR:PRODUCT
+
+device_list = [
+ { 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
+ { 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
+]
+
diff --git a/addons/im/im.py b/addons/im/im.py
index 20b8a966172..af3e8e5b10b 100644
--- a/addons/im/im.py
+++ b/addons/im/im.py
@@ -185,8 +185,8 @@ class im_message(osv.osv):
def post(self, cr, uid, message, to_session_id, technical=False, uuid=None, context=None):
assert_uuid(uuid)
my_id = self.pool.get('im.user').get_my_id(cr, uid, uuid)
- session = self.pool.get('im.session').browse(cr, uid, to_session_id, context)
- to_ids = [x.id for x in session.user_ids if x.id != my_id]
+ session_user_ids = self.pool.get('im.session').get_session_users(cr, uid, to_session_id, context=context).get("user_ids", [])
+ to_ids = [user_id for user_id in session_user_ids if user_id != my_id]
self.create(cr, openerp.SUPERUSER_ID, {"message": message, 'from_id': my_id,
'to_id': [(6, 0, to_ids)], 'session_id': to_session_id, 'technical': technical}, context=context)
notify_channel(cr, "im_channel", {'type': 'message', 'receivers': [my_id] + to_ids})
@@ -202,7 +202,7 @@ class im_session(osv.osv):
return res
_columns = {
- 'user_ids': fields.many2many('im.user'),
+ 'user_ids': fields.many2many('im.user', 'im_session_im_user_rel', 'im_session_id', 'im_user_id', 'Users'),
"name": fields.function(_calc_name, string="Name", type='char'),
}
@@ -225,6 +225,9 @@ class im_session(osv.osv):
}, context=context)
return self.read(cr, uid, session_id, context=context)
+ def get_session_users(self, cr, uid, session_id, context=None):
+ return self.read(cr, openerp.SUPERUSER_ID, session_id, ['user_ids'], context=context)
+
def add_to_session(self, cr, uid, session_id, user_id, uuid=None, context=None):
my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
session = self.read(cr, uid, session_id, context=context)
@@ -259,7 +262,7 @@ class im_user(osv.osv):
return ['&', ('im_last_status', '=', True), ('im_last_status_update', '>', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
else:
return ['|', ('im_last_status', '=', False), ('im_last_status_update', '<=', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
-
+ # TODO: Remove fields arg in trunk. Also in im.js.
def search_users(self, cr, uid, text_search, fields, limit, context=None):
my_id = self.get_my_id(cr, uid, None, context)
group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1]
@@ -271,7 +274,7 @@ class im_user(osv.osv):
if len(found) < limit:
found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", False], ["id", "not in", found]],
order="name asc", limit=limit-len(found), context=context)
- users = self.read(cr, uid, found, fields, context=context)
+ users = self.read(cr,openerp.SUPERUSER_ID, found, ["name", "user_id", "uuid", "im_status"], context=context)
users.sort(key=lambda obj: found.index(obj['id']))
return users
@@ -319,6 +322,9 @@ class im_user(osv.osv):
continue
return res
+ def get_users(self, cr, uid, ids, context=None):
+ return self.read(cr,openerp.SUPERUSER_ID, ids, ["name", "im_status", "uuid"], context=context)
+
_columns = {
'name': fields.function(_get_name, type='char', size=200, string="Name", store=True, readonly=True),
'assigned_name': fields.char(string="Assigned Name", size=200, required=False),
@@ -341,3 +347,16 @@ class im_user(osv.osv):
('user_uniq', 'unique (user_id)', 'Only one chat user per OpenERP user.'),
('uuid_uniq', 'unique (uuid)', 'Chat identifier already used.'),
]
+
+class res_users(osv.osv):
+ _inherit = "res.users"
+
+ def _get_im_user(self, cr, uid, ids, field_name, arg, context=None):
+ result = dict.fromkeys(ids, False)
+ for index, im_user in enumerate(self.pool['im.user'].search_read(cr, uid, domain=[('user_id', 'in', ids)], fields=['name', 'user_id'], context=context)):
+ result[ids[index]] = im_user.get('user_id') and (im_user['user_id'][0], im_user['name']) or False
+ return result
+
+ _columns = {
+ 'im_user_id' : fields.function(_get_im_user, type='many2one', string="IM User", relation="im.user"),
+ }
diff --git a/addons/im/security/im_security.xml b/addons/im/security/im_security.xml
index d6b854c8a98..6713b88cda6 100644
--- a/addons/im/security/im_security.xml
+++ b/addons/im/security/im_security.xml
@@ -2,10 +2,10 @@
- Can only read messages that you sent or messages sent to you
+ Can only read messages from a session where user is
- ["|", ('to_id.user_id', 'in', [user.id]), ('from_id.user_id', '=', user.id)]
+ [('session_id.user_ids', 'in', user.im_user_id.id)]
diff --git a/addons/im/security/ir.model.access.csv b/addons/im/security/ir.model.access.csv
index 651aa68c1bb..5137990498a 100644
--- a/addons/im/security/ir.model.access.csv
+++ b/addons/im/security/ir.model.access.csv
@@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_im_message,im.message,model_im_message,base.group_user,1,0,1,0
+access_im_message,im.message,model_im_message,,1,0,1,0
access_im_user,im.user,model_im_user,,1,1,1,0
access_im_session,im.session,model_im_session,,1,0,0,0
\ No newline at end of file
diff --git a/addons/im/static/src/js/im.js b/addons/im/static/src/js/im.js
index aa2e7d963b6..95f29bc5c49 100644
--- a/addons/im/static/src/js/im.js
+++ b/addons/im/static/src/js/im.js
@@ -92,6 +92,7 @@
search_changed: function(e) {
var users = new instance.web.Model("im.user");
var self = this;
+ // TODO: Remove fields arg in trunk. Also in im.js.
return this.user_search_dm.add(users.call("search_users", [this.get("current_search"), ["name", "user_id", "uuid", "im_status"],
USERS_LIMIT], {context:new instance.web.CompoundContext()})).then(function(users) {
var logged_users = _.filter(users, function(u) { return !!u.im_status; });
diff --git a/addons/im/static/src/js/im_common.js b/addons/im/static/src/js/im_common.js
index 83f92265ec6..5a88ddfb581 100644
--- a/addons/im/static/src/js/im_common.js
+++ b/addons/im/static/src/js/im_common.js
@@ -145,7 +145,7 @@ function declare($, _, openerp) {
if (_.size(no_cache) === 0)
def = $.when();
else
- def = im_common.connection.model("im.user").call("read", [_.values(no_cache), []]).then(function(users) {
+ def = im_common.connection.model("im.user").call("get_users", [_.values(no_cache)]).then(function(users) {
self.add_to_user_cache(users);
});
return def.then(function() {
@@ -230,7 +230,8 @@ function declare($, _, openerp) {
return self.chat_with_users(users);
});
},
- activate_session: function(session_id, focus) {
+ activate_session: function(session_id, focus, message) {
+ var self = this;
var conv = _.find(this.conversations, function(conv) {return conv.session_id == session_id;});
var def = $.when();
if (! conv) {
@@ -244,6 +245,9 @@ function declare($, _, openerp) {
this.calc_positions();
this.trigger("new_conversation", conv);
}, this));
+ def = def.then(function(){
+ return self.load_history(conv, message);
+ });
}
if (focus) {
def = def.then(function() {
@@ -252,13 +256,32 @@ function declare($, _, openerp) {
}
return def.then(function() {return conv});
},
- received_messages: function(messages) {
+ load_history: function(conv, message){
+ var self = this;
+ var domain = [["session_id", "=", conv.session_id]];
+ if (!_.isUndefined(message)){
+ domain.push(["date", "<", message.date]);
+ }
+ return im_common.connection.model("im.message").call("search_read", [domain, [], 0, 10]).then(function(messages){
+ messages.reverse();
+ var users = _.unique(_.map(messages, function(message){
+ return message.from_id[0];
+ }));
+ return self.ensure_users(users).then(function(){
+ return self.received_messages(messages, true);
+ });
+ });
+ },
+ received_messages: function(messages, seen) {
var self = this;
var defs = [];
var received = false;
+ if (_.isUndefined(seen)){
+ seen = false;
+ }
_.each(messages, function(message) {
if (! message.technical) {
- defs.push(self.activate_session(message.session_id[0]).then(function(conv) {
+ defs.push(self.activate_session(message.session_id[0], false, message).then(function(conv) {
received = self.my_id !== message.from_id[0];
return conv.received_message(message);
}));
@@ -269,7 +292,7 @@ function declare($, _, openerp) {
}
});
return $.when.apply($, defs).then(function(){
- if (! self.get("window_focus") && received) {
+ if (! self.get("window_focus") && received && !seen) {
self.set("waiting_messages", self.get("waiting_messages") + messages.length);
self.ting.play();
self.create_ting();
@@ -368,7 +391,7 @@ function declare($, _, openerp) {
refresh_users: function() {
var self = this;
var user_ids;
- return im_common.connection.model("im.session").call("read", [self.session_id]).then(function(session) {
+ return im_common.connection.model("im.session").call("get_session_users", [self.session_id]).then(function(session) {
user_ids = _.without(session.user_ids, self.c_manager.me.get("id"));
return self.c_manager.ensure_users(user_ids);
}).then(function(users) {
@@ -449,7 +472,7 @@ function declare($, _, openerp) {
date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2);
var to_show = _.map(items, im_common.escape_keep_url);
this.last_bubble = $(openerp.qweb.render("im_common.conversation_bubble", {"items": to_show, "user": user, "time": date}));
- $(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble);
+ $(this.$(".oe_im_chatview_conversation")).append(this.last_bubble);
this._go_bottom();
},
_go_bottom: function() {
diff --git a/addons/im/static/src/xml/im_common.xml b/addons/im/static/src/xml/im_common.xml
index 462e242befd..efaaf602577 100644
--- a/addons/im/static/src/xml/im_common.xml
+++ b/addons/im/static/src/xml/im_common.xml
@@ -11,7 +11,7 @@
All users are offline. They will receive your messages on their next connection.