From 6956a3fccce883a6a65899cab72a64cc47448334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 14:37:57 +0200 Subject: [PATCH 01/25] Add StringIO in localcontext of report --- trytond/report/report.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/report/report.py b/trytond/report/report.py index 01f622c692..0cca3725f9 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -342,6 +342,7 @@ class Report(object): browse(cursor, user, user) localcontext['formatLang'] = self.format_lang localcontext['decodestring'] = decodestring + localcontext['StringIO'] = StringIO.StringIO localcontext['time'] = time localcontext['datetime'] = datetime localcontext.update(context) From 81af21c376c365877e171169bbbf0be717301874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 15:35:51 +0200 Subject: [PATCH 02/25] Allow to use list of ids for xxx2many for issue200 Use string for many2one set operator Fix some security SQL injections --- trytond/osv/fields.py | 112 +++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/trytond/osv/fields.py b/trytond/osv/fields.py index 898fdae863..bdd7fc064e 100644 --- a/trytond/osv/fields.py +++ b/trytond/osv/fields.py @@ -14,13 +14,13 @@ Relationals fields - Values: ('create', { fields }) create - ('write', ID, { fields }) modification - ('unlink', ID) remove (delete) - ('remove', ID) unlink one (target id or target of relation) - ('add', ID) link - ('remove_all') unlink all - ('set', ids) set a list of links + Values: ('create', { fields }) create + ('write', ids, { fields }) modification + ('unlink', ids) remove (delete) + ('remove', ids) unlink one (target id or target of relation) + ('add', ids) link + ('remove_all') unlink all + ('set', ids) set a list of links """ import psycopg2 @@ -348,21 +348,22 @@ class Many2One(Column): table = obj_src.pool.get(self._obj)._table if type(values) == type([]): for act in values: - if act[0] == 0: - id_new = obj.create(cursor, act[2]) + if act[0] == 'create': + id_new = obj.create(cursor, user, act[2], context=context) cursor.execute('UPDATE "' + obj_src._table + '" ' \ 'SET "' + field + '" = %s ' \ 'WHERE id = %s', (id_new, obj_id)) - elif act[0] == 1: - obj.write(cursor, [act[1]], act[2], context=context) - elif act[0] == 2: + elif act[0] == 'write': + obj.write(cursor, act[1], act[2], context=context) + elif act[0] == 'unlink': + obj.unlink(cursor, user, act[1], context=context) cursor.execute('DELETE FROM "' + table + '" ' \ 'WHERE id = %s', (act[1],)) - elif act[0] == 3 or act[0] == 5: + elif act[0] == 'remove' or act[0] == 'remove_all': cursor.execute('UPDATE "' + obj_src._table + '" ' \ 'SET "' + field + '" = NULL ' \ 'WHERE id = %s', (obj_id,)) - elif act[0] == 4: + elif act[0] == 'add': cursor.execute('UPDATE "' + obj_src._table + '" ' \ 'SET "' + field + '" = %s ' \ 'WHERE id = %s', (act[1], obj_id)) @@ -431,39 +432,51 @@ class One2Many(Column): obj.create(cursor, user, act[1], context=context) elif act[0] == 'write': act[2][self._field] = obj_id - obj.write(cursor, user, [act[1]] , act[2], context=context) + obj.write(cursor, user, act[1] , act[2], context=context) elif act[0] == 'unlink': - obj.unlink(cursor, user, [act[1]], context=context) + obj.unlink(cursor, user, act[1], context=context) elif act[0] == 'remove': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = NULL ' \ - 'WHERE id = %s '\ - 'AND "' + self._field + '" = %s', (act[1], obj_id)) + 'WHERE id IN (' \ + + ','.join(['%s' for x in ids]) + ') ' \ + 'AND "' + self._field + '" = %s', + ids + [obj_id]) elif act[0] == 'add': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = %s ' \ - 'WHERE id = %s', (obj_id, act[1])) + 'WHERE id IN (' \ + + ','.join(['%s' for x in ids]) + ')', + [obj_id] + ids) elif act[0] == 'remove_all': cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = NULL ' \ 'WHERE "' + self._field + '" = %s', (obj_id,)) elif act[0] == 'set': if not act[1]: - ids2 = [0] + ids = [0] else: - ids2 = act[1] + ids = list(act[1]) cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = NULL ' \ 'WHERE "' + self._field + '" = %s ' \ 'AND id not IN (' + \ - ','.join([str(x) for x in ids2]) + ')', - (obj_id,)) + ','.join(['%s' for x in ids]) + ')', + [obj_id] + ids) if act[1]: cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = %s ' \ 'WHERE id IN (' + \ - ','.join([str(x) for x in act[1]]) + ')', - (obj_id,)) + ','.join(['%s' for x in ids]) + ')', + [obj_id] + ids) else: raise Exception('Bad arguments') @@ -525,33 +538,52 @@ class Many2Many(Column): obj = obj.pool.get(self._obj) for act in values: if act[0] == 'create': - idnew = obj.create(cursor, user, act[1]) + idnew = obj.create(cursor, user, act[1], context=context) cursor.execute('INSERT INTO "' + self._rel + '" ' \ '(' + self._id1 + ', ' + self._id2 + ') ' \ 'VALUES (%s, %s)', (obj_id, idnew)) elif act[0] == 'write': - obj.write(cursor, user, [act[1]] , act[2], context=context) + obj.write(cursor, user, act[1] , act[2], context=context) elif act[0] == 'unlink': - obj.unlink(cursor, user, [act[1]], context=context) + obj.unlink(cursor, user, act[1], context=context) elif act[0] == 'remove': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) cursor.execute('DELETE FROM "' + self._rel + '" ' \ 'WHERE "' + self._id1 + '" = %s ' \ - 'AND "'+ self._id2 + '" = %s', (obj_id, act[1])) + 'AND "'+ self._id2 + '" IN (' \ + + ','.join(['%s' for x in ids]) + ')', + [obj_id] + ids) elif act[0] == 'add': - cursor.execute('SELECT * FROM "' + self._rel + '" ' \ + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) + cursor.execute('SELECT "' + self._id2 + '" ' \ + 'FROM "' + self._rel + '" ' \ 'WHERE ' + self._id1 + ' = %s ' \ - 'AND ' + self._id2 + ' = %s ', - (obj_id, act[1])) - if cursor.rowcount: - continue - cursor.execute('INSERT INTO "' + self._rel + '" ' \ - '(' + self._id1 + ', ' + self._id2 + ') ' \ - 'VALUES (%s, %s)', (obj_id, act[1])) + 'AND ' + self._id2 + ' IN (' + \ + ','.join(['%s' for x in ids]) + ')', + [obj_id] + ids) + existing_ids = [] + for row in cursor.fetchall(): + existing_ids.append(row[0]) + new_ids = [x for x in ids if x not in existing_ids] + for new_id in new_ids: + cursor.execute('INSERT INTO "' + self._rel + '" ' \ + '("' + self._id1 + '", "' + self._id2 + '") ' \ + 'VALUES (%s, %s)', (obj_id, new_id)) elif act[0] == 'remove_all': cursor.execute('UPDATE "' + self._rel + '" ' \ 'SET "' + self._id2 + '" = NULL ' \ 'WHERE "' + self._id2 + '" = %s', (obj_id,)) elif act[0] == 'set': + if not act[1]: + ids = [] + else: + ids = list(act[1]) domain1, domain2 = obj.pool.get('ir.rule').domain_get(cursor, user, obj._name) if domain1: @@ -566,10 +598,10 @@ class Many2Many(Column): obj._table + '.id ' + domain1 + ')', [obj_id, obj_id] + domain2) - for act_nbr in act[1]: + for new_id in ids: cursor.execute('INSERT INTO "' + self._rel + '" ' \ - '(' + self._id1 + ', ' + self._id2 + ') ' \ - 'VALUES (%s, %s)', (obj_id, act_nbr)) + '("' + self._id1 + '", "' + self._id2 + '") ' \ + 'VALUES (%s, %s)', (obj_id, new_id)) else: raise Exception('Bad arguments') From 503f006019ed8fd0de39f8ea4c631b0d675409fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 16:08:52 +0200 Subject: [PATCH 03/25] Fix remove on xxx2many for empty list --- trytond/osv/fields.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/trytond/osv/fields.py b/trytond/osv/fields.py index bdd7fc064e..4dcac3f037 100644 --- a/trytond/osv/fields.py +++ b/trytond/osv/fields.py @@ -440,6 +440,8 @@ class One2Many(Column): ids = [act[1]] else: ids = list(act[1]) + if not ids: + continue cursor.execute('UPDATE "' + _table + '" ' \ 'SET "' + self._field + '" = NULL ' \ 'WHERE id IN (' \ @@ -551,6 +553,8 @@ class Many2Many(Column): ids = [act[1]] else: ids = list(act[1]) + if not ids: + continue cursor.execute('DELETE FROM "' + self._rel + '" ' \ 'WHERE "' + self._id1 + '" = %s ' \ 'AND "'+ self._id2 + '" IN (' \ From 69bf9b728d03430a2d0ad6a2fdee54294ff1ef10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 20:35:13 +0200 Subject: [PATCH 04/25] Fix typo --- trytond/osv/orm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index 077674b355..646454d9c2 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -1504,11 +1504,11 @@ class ORM(object): # Allow root user to update/delete if user == 0: return True - cursor.execute('SELECT values '\ - 'FROM ir_model_data '\ - 'WHERE model = %s '\ - 'AND db_id in (' + ','.join('%s' for x in ids)+ ') ', - [self._name]+ids) + cursor.execute('SELECT values ' \ + 'FROM ir_model_data ' \ + 'WHERE model = %s ' \ + 'AND db_id in (' + ','.join('%s' for x in ids)+ ') ', + [self._name] + ids) if cursor.rowcount == 0: return True if values == None: From 57e37e2b0be47f9b254a06b7f22300f4c4bacdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 20:35:31 +0200 Subject: [PATCH 05/25] Remove TODO validate --- trytond/osv/orm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index 646454d9c2..a6fc973ada 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -1520,7 +1520,6 @@ class ORM(object): return False return True - # TODO: Validate def write(self, cursor, user, ids, vals, context=None): ''' Update ids with the content of vals. From 068ca16930d1d93178e25422db7ddad28be6c338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 23:14:37 +0200 Subject: [PATCH 06/25] Add verbose = False for pywebdav 0.8 --- trytond/webdavsvc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/webdavsvc.py b/trytond/webdavsvc.py index f8700106ac..21097cee67 100644 --- a/trytond/webdavsvc.py +++ b/trytond/webdavsvc.py @@ -44,6 +44,7 @@ if DAV_VERSION == '0.8': class DAV: lockemulation = False + verbose = False class _Config: From 11cfae3218d5fd5c26f427b53ee5fbcbd27164f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 23:18:50 +0200 Subject: [PATCH 07/25] Improve webdav for wrong uri --- trytond/webdav/webdav.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py index 6a4cb6243b..b13602cf9f 100644 --- a/trytond/webdav/webdav.py +++ b/trytond/webdav/webdav.py @@ -219,25 +219,27 @@ class Collection(OSV): def get_creationdate(self, cursor, user, uri, context=None): object_name, object_id = self._uri2object(cursor, user, uri, context=context) - model_obj = self.pool.get(object_name) - if model_obj._log_access: - cursor.execute('SELECT EXTRACT(epoch FROM create_date) ' \ - 'FROM "' + model_obj._table +'" ' \ - 'WHERE id = %s', (object_id,)) - if cursor.rowcount: - return cursor.fetchone()[0] + if object_name: + model_obj = self.pool.get(object_name) + if model_obj._log_access: + cursor.execute('SELECT EXTRACT(epoch FROM create_date) ' \ + 'FROM "' + model_obj._table +'" ' \ + 'WHERE id = %s', (object_id,)) + if cursor.rowcount: + return cursor.fetchone()[0] return time.time() def get_lastmodified(self, cursor, user, uri, context=None): object_name, object_id = self._uri2object(cursor, user, uri, context=context) - model_obj = self.pool.get(object_name) - if model_obj._log_access and object_id: - cursor.execute('SELECT EXTRACT(epoch FROM write_date) ' \ - 'FROM "' + model_obj._table +'" ' \ - 'WHERE id = %s', (object_id,)) - if cursor.rowcount: - return cursor.fetchone()[0] + if object_name: + model_obj = self.pool.get(object_name) + if model_obj._log_access and object_id: + cursor.execute('SELECT EXTRACT(epoch FROM write_date) ' \ + 'FROM "' + model_obj._table +'" ' \ + 'WHERE id = %s', (object_id,)) + if cursor.rowcount: + return cursor.fetchone()[0] return time.time() def get_data(self, cursor, user, uri, context=None): @@ -342,6 +344,8 @@ class Collection(OSV): from DAV.errors import DAV_Forbidden object_name, object_id = self._uri2object(cursor, user, uri, context=context) + if not object_name: + raise DAV_Forbidden if object_name != 'ir.attachment' \ or not object_id: raise DAV_Forbidden From f1fd0a309ff80ad68c7916d198187307a04c9c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 23:30:50 +0200 Subject: [PATCH 08/25] Add mime type for pdf and odt --- trytond/webdav/webdav.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py index b13602cf9f..8c5d0cbd78 100644 --- a/trytond/webdav/webdav.py +++ b/trytond/webdav/webdav.py @@ -37,6 +37,8 @@ class Collection(OSV): ] self.ext2mime = { '.png': 'image/png', + '.odt': 'application/vnd.oasis.opendocument.text', + '.pdf': 'application/pdf', } def default_domain(self, cursor, user, context=None): From 5b982b9e9ff2969d5d175e1b30dabb09dd4d680f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 23 Jul 2008 23:32:23 +0200 Subject: [PATCH 09/25] Convert "unable to set locale" into warning --- trytond/report/report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trytond/report/report.py b/trytond/report/report.py index 0cca3725f9..495e6ded6f 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. "Report" -from trytond.netsvc import Service, service_exist, Logger, LOG_ERROR +from trytond.netsvc import Service, service_exist, Logger, LOG_WARNING from trytond import pooler import copy import xml @@ -483,7 +483,7 @@ class Report(object): language = _LOCALE2WIN32.get(language, language) locale.setlocale(locale.LC_ALL, str(language + '.' + encoding)) except Exception: - Logger().notify_channel('web-service', LOG_ERROR, + Logger().notify_channel('web-service', LOG_WARNING, 'Report %s: unable to set locale "%s"' % \ (self._name, language + '.' + encoding)) if date: From b1a9433904e264fcd96bdbfbde0f36af57f569d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 12:54:12 +0200 Subject: [PATCH 10/25] Remove encode stuff as we use unicode everywhere --- trytond/ir/translation.py | 4 +--- trytond/ir/ui/view.py | 4 ++-- trytond/osv/fields.py | 28 ++++------------------------ trytond/osv/orm.py | 8 +------- trytond/security.py | 3 --- trytond/tools/convert.py | 28 ++++++++++++++-------------- 6 files changed, 22 insertions(+), 53 deletions(-) diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 9ec4035478..d0203af934 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -204,8 +204,6 @@ class Translation(OSV, Cacheable): if source: source = source.strip().replace('\n',' ') - if isinstance(source, unicode): - source = source.encode('utf8') cursor.execute('SELECT value ' \ 'FROM ir_translation ' \ 'WHERE lang=%s ' \ @@ -486,7 +484,7 @@ class ReportTranslationUpdate(Wizard): return strings if node.nodeValue: - txt = node.nodeValue.encode('utf-8').strip() + txt = node.nodeValue.strip() if txt: strings.append(txt) diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py index c8baec681f..3e26ee4f6d 100644 --- a/trytond/ir/ui/view.py +++ b/trytond/ir/ui/view.py @@ -172,11 +172,11 @@ class View(OSV): if element.get('string'): string = element.get('string') if string: - strings.append(string.encode('utf-8')) + strings.append(string) if element.get('sum'): string = element.get('sum') if string: - strings.append(string.encode('utf-8')) + strings.append(string) for child in element: strings.extend(self._translate_view(child)) return strings diff --git a/trytond/osv/fields.py b/trytond/osv/fields.py index 4dcac3f037..453cfe44f9 100644 --- a/trytond/osv/fields.py +++ b/trytond/osv/fields.py @@ -31,9 +31,11 @@ import sha def _symbol_f(symb): if symb is None or symb == False: return None + elif isinstance(symb, str): + return unicode(symb, 'utf-8') elif isinstance(symb, unicode): - return symb.encode('utf-8') - return str(symb) + return symb + return unicode(symb) class Column(object): _classic_read = True @@ -163,28 +165,6 @@ class Char(Column): def __init__(self, string, size=None, **args): Column.__init__(self, string=string, size=size, **args) - self._symbol_set = (self._symbol_c, self._symbol_set_char) - - def _symbol_set_char(self, symb): - """ - takes a string (encoded in utf8) - and returns a string (encoded in utf8) - """ - #TODO we need to remove the "symb==False" from the next line BUT - #TODO for now too many things rely on this broken behavior - #TODO the 'symb is None' test should be common to all data types - if symb is None or symb == False: - return None - - # we need to convert the string to a unicode object to be able - # to evaluate its length (and possibly truncate it) reliably - if isinstance(symb, str): - u_symb = unicode(symb, 'utf8') - elif isinstance(symb, unicode): - u_symb = symb - else: - u_symb = unicode(symb) - return u_symb.encode('utf8') def sql_type(self): if self.size: diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index a6fc973ada..4f6321350e 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -2587,13 +2587,7 @@ class ORM(object): else: add_null = False if arg[1] in ('like', 'ilike'): - if isinstance(arg[2], str): - str_utf8 = arg[2] - elif isinstance(arg[2], unicode): - str_utf8 = arg[2].encode('utf-8') - else: - str_utf8 = str(arg[2]) - qu2.append('%%%s%%' % str_utf8) + qu2.append('%%%s%%' % arg[2]) if not str_utf8: add_null = True else: diff --git a/trytond/security.py b/trytond/security.py index 8b338805ad..654f4c4c73 100644 --- a/trytond/security.py +++ b/trytond/security.py @@ -10,8 +10,6 @@ SESSION_TIMEOUT = 600 #seconds _USER_CACHE = {} def login(dbname, loginname, password, cache=True): - loginname = loginname.encode('utf-8') - password = password.encode('utf-8') cursor = pooler.get_db(dbname).cursor() password_sha = sha.new(password).hexdigest() cursor.execute('SELECT id FROM res_user ' @@ -40,7 +38,6 @@ def check_super(passwd): raise Exception('AccessDenied') def check(dbname, user, session, outdate_timeout=True): - session = session.encode('utf-8') result = False if _USER_CACHE.get(dbname, {}).has_key(user): to_del = [] diff --git a/trytond/tools/convert.py b/trytond/tools/convert.py index 59c7a1697f..da981ff4c8 100644 --- a/trytond/tools/convert.py +++ b/trytond/tools/convert.py @@ -48,11 +48,11 @@ class MenuitemTagHandler: values = {} - self.xml_id = attributes['id'].encode('utf8') + self.xml_id = attributes['id'] for attr in ('name', 'icon', 'sequence', 'parent', 'action', 'groups'): if attributes.get(attr): - values[attr] = attributes.get(attr).encode('utf8') + values[attr] = attributes.get(attr) if values.get('parent'): @@ -83,7 +83,7 @@ class MenuitemTagHandler: values['action'] = '%s,%s' % (action_type, action_id) - icon = attributes.get('icon', '').encode('utf8') + icon = attributes.get('icon', '') if icon: values['icon'] = icon elif action_type == 'ir.action.wizard': @@ -160,12 +160,12 @@ class RecordTagHandler: # Manage the top level tag if name == "record": - self.model = self.mh.pool.get(attributes["model"].encode('utf8')) + self.model = self.mh.pool.get(attributes["model"]) assert self.model, "The model %s does not exist !" % \ - (attributes["model"].encode('utf8'),) + (attributes["model"],) - self.xml_id = attributes["id"].encode('utf8') - self.update = bool(int(attributes.get('update', '0').encode('utf8'))) + self.xml_id = attributes["id"] + self.update = bool(int(attributes.get('update', '0'))) # create/update a dict containing fields values self.values = {} @@ -178,8 +178,8 @@ class RecordTagHandler: # Manage included tags: elif name == "field": - field_name = attributes['name'].encode('utf8') - field_type = attributes.get('type', '').encode('utf8') + field_name = attributes['name'] + field_type = attributes.get('type', '') # Create a new entry in the values self.values[field_name] = "" # Remind the current name (see characters) @@ -189,13 +189,13 @@ class RecordTagHandler: self.cdata = "start" # Catch the known attributes - search_attr = attributes.get('search','').encode('utf8') - ref_attr = attributes.get('ref', '').encode('utf8') - eval_attr = attributes.get('eval', '').encode('utf8') + search_attr = attributes.get('search','') + ref_attr = attributes.get('ref', '') + eval_attr = attributes.get('eval', '') if search_attr: if attributes.get('model', ''): - search_model = attributes['model'].encode('utf8') + search_model = attributes['model'] else: search_model = self.model._columns[field_name]._obj f_obj = self.mh.pool.get(search_model) @@ -237,7 +237,7 @@ class RecordTagHandler: data = CDATA_START.sub('', data) self.start_cdata = "inside" - self.values[self.current_field] += data.encode('utf8') + self.values[self.current_field] += data def endElement(self, name): From dfe9d4418c12d0604c60ba754c4b535350e636cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 18:07:41 +0200 Subject: [PATCH 11/25] Fix if python has ipv6 but the kernel not --- trytond/netsvc.py | 7 ++++--- trytond/pysocket.py | 24 ++++++++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/trytond/netsvc.py b/trytond/netsvc.py index 93daef440e..7690b74ded 100644 --- a/trytond/netsvc.py +++ b/trytond/netsvc.py @@ -360,14 +360,15 @@ class TinySocketClientThread(threading.Thread): class TinySocketServerThread(threading.Thread): def __init__(self, interface, port, secure=False): threading.Thread.__init__(self) - familly = socket.AF_INET + self.socket = None if socket.has_ipv6: try: socket.getaddrinfo(interface or None, port, socket.AF_INET6) - familly = socket.AF_INET6 + self.socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) except: pass - self.socket = socket.socket(familly, socket.SOCK_STREAM) + if self.socket is None: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if secure: self.socket = start_SSL(self.socket) diff --git a/trytond/pysocket.py b/trytond/pysocket.py index e2e0b2e1a7..cddcee9ef8 100644 --- a/trytond/pysocket.py +++ b/trytond/pysocket.py @@ -36,26 +36,29 @@ class PySocket: if host in DNS_CACHE: host = DNS_CACHE[host] if not self.sock: - familly = socket.AF_INET + self.sock = None if socket.has_ipv6: try: socket.getaddrinfo(host, int(port), socket.AF_INET6) - familly = socket.AF_INET6 + self.sock = socket.socket(socket.AF_INET6, + socket.SOCK_STREAM) except: pass - self.sock = socket.socket(familly, socket.SOCK_STREAM) + if self.sock is None: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(120) self.sock.connect((host, int(port))) DNS_CACHE[hostname], port = self.sock.getpeername()[:2] try: - familly = socket.AF_INET + sock = None if socket.has_ipv6: try: socket.getaddrinfo(host, int(port), socket.AF_INET6) - familly = socket.AF_INET6 + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) except: pass - sock = socket.socket(familly, socket.SOCK_STREAM) + if sock is None: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(120) sock.connect((host, int(port))) ssl_sock = socket.ssl(sock) @@ -94,16 +97,17 @@ class PySocket: def reconnect(self): if self.host and self.port: self.disconnect() - familly = socket.AF_INET + self.sock = None if socket.has_ipv6: try: socket.getaddrinfo(self.host, int(self.port), socket.AF_INET6) - familly = socket.AF_INET6 + self.sock = socket.socket(socket.AF_INET6, + socket.SOCK_STREAM) except: pass - self.sock = socket.socket( - familly, socket.SOCK_STREAM) + if self.sock is None: + self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) self.sock.settimeout(120) self.sock.connect((self.host, int(self.port))) if self.ssl: From 2dfd7d5e924a3eac9d1233186f3c565199e67f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 18:40:22 +0200 Subject: [PATCH 12/25] Set IntegrityError as UserError --- trytond/osv/osv.py | 14 +++++++++++++- trytond/report/report.py | 7 +++++++ trytond/sql_db.py | 1 + trytond/wizard/wizard.py | 9 ++++++++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/trytond/osv/osv.py b/trytond/osv/osv.py index d717ab5f41..f93c667afb 100644 --- a/trytond/osv/osv.py +++ b/trytond/osv/osv.py @@ -6,7 +6,7 @@ from trytond.netsvc import Service, LocalService, Logger, LOG_ERROR from trytond import pooler import copy import sys -from psycopg2 import IntegrityError +from trytond.sql_db import IntegrityError from trytond.tools import UpdateableDict import traceback from trytond.tools import Cache @@ -50,11 +50,17 @@ class OSVService(Service): 'ConcurrencyException') \ and not CONFIG['verbose']: raise + if isinstance(exception, IntegrityError) and not CONFIG['verbose']: + raise Exception('UserError', 'Constraint Error', + *exception.args) tb_s = reduce(lambda x, y: x+y, traceback.format_exception(*sys.exc_info())) logger = Logger() logger.notify_channel("web-services", LOG_ERROR, 'Exception in call: \n' + tb_s) + if isinstance(exception, IntegrityError): + raise Exception('UserError', 'Constraint Error', + *exception.args) raise def execute(self, dbname, user, object_name, method, *args, **kargs): @@ -83,11 +89,17 @@ class OSVService(Service): 'ConcurrencyException') \ and not CONFIG['verbose']: raise + if isinstance(exception, IntegrityError) and not CONFIG['verbose']: + raise Exception('UserError', 'Constraint Error', + *exception.args) tb_s = reduce(lambda x, y: x+y, traceback.format_exception(*sys.exc_info())) logger = Logger() logger.notify_channel("web-services", LOG_ERROR, 'Exception in call: \n' + tb_s) + if isinstance(exception, IntegrityError): + raise Exception('UserError', 'Constraint Error', + *exception.args) raise return True diff --git a/trytond/report/report.py b/trytond/report/report.py index 495e6ded6f..dce3b97de7 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -25,6 +25,7 @@ import tempfile from genshi.filters import Translator import traceback from trytond.config import CONFIG +from trytond.sql_db import IntegrityError MODULE_LIST = [] MODULE_CLASS_LIST = {} @@ -193,10 +194,16 @@ class ReportService(Service): 'ConcurrencyException') \ and not CONFIG['verbose']: raise + if isinstance(exception, IntegrityError) and not CONFIG['verbose']: + raise Exception('UserError', 'Constraint Error', + *exception.args) tb_s = reduce(lambda x, y: x+y, traceback.format_exception(*sys.exc_info())) Logger().notify_channel("web-services", LOG_ERROR, 'Exception in call: ' + tb_s) + if isinstance(exception, IntegrityError): + raise Exception('UserError', 'Constraint Error', + *exception.args) raise def execute(self, dbname, user, report_name, ids, datas, context=None): diff --git a/trytond/sql_db.py b/trytond/sql_db.py index e8ee5cfa73..ddf51e79f5 100644 --- a/trytond/sql_db.py +++ b/trytond/sql_db.py @@ -1,6 +1,7 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. from psycopg2.pool import ThreadedConnectionPool from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE, cursor +from psycopg2 import IntegrityError import psycopg2 import re import os diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index 7319fc0859..ad9dd63909 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -7,6 +7,8 @@ from xml import dom from trytond.osv import OSV from trytond.config import CONFIG import sys +from trytond.sql_db import IntegrityError +import traceback MODULE_LIST = [] MODULE_CLASS_LIST = {} @@ -37,11 +39,16 @@ class WizardService(Service): 'ConcurrencyException') \ and not CONFIG['verbose']: raise - import traceback + if isinstance(exception, IntegrityError) and not CONFIG['verbose']: + raise Exception('UserError', 'Constraint Error', + *exception.args) tb_s = reduce(lambda x, y: x+y, traceback.format_exception(*sys.exc_info())) Logger().notify_channel("web-services", LOG_ERROR, 'Exception in call: ' + tb_s) + if isinstance(exception, IntegrityError): + raise Exception('UserError', 'Constraint Error', + *exception.args) raise def execute(self, dbname, user, wizard_name, data, state='init', From f4f3b128eb7f90b9d524f78a47573a9bbdfa12a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 20:56:05 +0200 Subject: [PATCH 13/25] Fix typo --- trytond/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index 4f6321350e..bf0436b82a 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -2588,7 +2588,7 @@ class ORM(object): add_null = False if arg[1] in ('like', 'ilike'): qu2.append('%%%s%%' % arg[2]) - if not str_utf8: + if not arg[2]: add_null = True else: if arg[0] in table._columns: From cf6e7c0a496bc368106d07183c2b145f3f810d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 21:00:52 +0200 Subject: [PATCH 14/25] Fix missing LOG_ERROR import for issue210 --- trytond/report/report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/report/report.py b/trytond/report/report.py index dce3b97de7..8b8e804625 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. "Report" -from trytond.netsvc import Service, service_exist, Logger, LOG_WARNING +from trytond.netsvc import Service, service_exist, Logger, LOG_WARNING, LOG_ERROR from trytond import pooler import copy import xml From b5583498d9e31a3a2f24a4c3dd8c155be04abc14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 21:06:05 +0200 Subject: [PATCH 15/25] Add fix for migration to unicode --- trytond/tools/convert.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/trytond/tools/convert.py b/trytond/tools/convert.py index da981ff4c8..c7a2a97bef 100644 --- a/trytond/tools/convert.py +++ b/trytond/tools/convert.py @@ -520,6 +520,11 @@ class TrytondXmlHandler(sax.handler.ContentHandler): 'datetime': datetime, }) + for key in old_values: + if isinstance(old_values[key], str): + # Fix for migration to unicode + old_values[key] = old_values[key].decode('utf-8') + # Check if values for this record has been modified in the # db, if not it's ok to overwrite them. if model != db_model: @@ -610,7 +615,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler): 'model': model, 'module': module, 'db_id': db_id, - 'values': str(values), + 'values': values, 'date_update': time.strftime('%Y-%m-%d %H:%M:%S'), }) From 0ca9bf467083adfca5f96ab75bf45cc86b26c058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 24 Jul 2008 23:12:07 +0200 Subject: [PATCH 16/25] Add psyco --- trytond/trytond.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/trytond/trytond.py b/trytond/trytond.py index 51f34306ee..e185a44e15 100644 --- a/trytond/trytond.py +++ b/trytond/trytond.py @@ -179,6 +179,13 @@ class TrytonServer(object): if CONFIG['webdav']: webdavd.start() #DISPATCHER.run() + + try: + import psyco + psyco.full() + except ImportError: + pass + count = 0 while True: time.sleep(1) From 7f32771048f23ede55c284968fd8449b62ed4592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 25 Jul 2008 12:49:00 +0200 Subject: [PATCH 17/25] Change unlink to delete for issue201 --- trytond/ir/action.py | 4 +- trytond/ir/default.py | 2 +- trytond/ir/model.py | 10 ++-- trytond/ir/model.xml | 6 +-- trytond/ir/module/module.py | 10 ++-- trytond/ir/property.py | 2 +- trytond/ir/rule.py | 8 +-- trytond/ir/translation.py | 4 +- trytond/ir/ui/menu.py | 2 +- trytond/ir/ui/view.py | 10 ++-- trytond/module.py | 2 +- trytond/osv/fields.py | 24 ++++----- trytond/osv/orm.py | 13 ++--- trytond/res/group.xml | 4 +- trytond/res/ir.xml | 96 +++++++++++++++++------------------ trytond/res/user.py | 4 +- trytond/res/user.xml | 4 +- trytond/tools/convert.py | 22 ++++---- trytond/webdav/webdav.py | 4 +- trytond/wizard/wizard.py | 2 +- trytond/workflow/workflow.xml | 24 ++++----- 21 files changed, 125 insertions(+), 132 deletions(-) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 11c762b9e7..9ed2402d77 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -85,7 +85,7 @@ class ActionKeyword(OSV): res.append([model.model, model.name]) return res - def unlink(self, cursor, user, ids, context=None): + def delete(self, cursor, user, ids, context=None): if isinstance(ids, (int, long)): ids = [ids] for keyword in self.browse(cursor, user, ids, context=context): @@ -95,7 +95,7 @@ class ActionKeyword(OSV): .fields_view_get(cursor.dbname) except: pass - return super(ActionKeyword, self).unlink(cursor, user, ids, + return super(ActionKeyword, self).delete(cursor, user, ids, context=context) def create(self, cursor, user, vals, context=None): diff --git a/trytond/ir/default.py b/trytond/ir/default.py index 062bab33e2..b6a7904862 100644 --- a/trytond/ir/default.py +++ b/trytond/ir/default.py @@ -67,7 +67,7 @@ class Default(OSV): ('user', '=', user_default), ], context=context) if default_ids: - self.unlink(cursor, user, default_ids, context=context) + self.delete(cursor, user, default_ids, context=context) self.create(cursor, user, { 'model': model_id, 'field': field_id, diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 8368635222..703359bb18 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -65,7 +65,7 @@ class ModelAccess(OSV): perm_read = fields.Boolean('Read Access') perm_write = fields.Boolean('Write Access') perm_create = fields.Boolean('Create Access') - perm_unlink = fields.Boolean('Delete Permission') + perm_delete = fields.Boolean('Delete Permission') description = fields.Text('Description') def __init__(self): @@ -78,12 +78,12 @@ class ModelAccess(OSV): 'read': 'You can not read this document! (%s)', 'write': 'You can not write in this document! (%s)', 'create': 'You can not create this kind of document! (%s)', - 'unlink': 'You can not delete this document! (%s)', + 'delete': 'You can not delete this document! (%s)', }) def check(self, cursor, user, model_name, mode='read', raise_exception=True, context=None): - assert mode in ['read', 'write', 'create', 'unlink'], \ + assert mode in ['read', 'write', 'create', 'delete'], \ 'Invalid access mode for security' if user == 0: return True @@ -129,8 +129,8 @@ class ModelAccess(OSV): self.check(cursor.dbname) return res - def unlink(self, cursor, user, ids, context=None): - res = super(ModelAccess, self).unlink(cursor, user, ids, + def delete(self, cursor, user, ids, context=None): + res = super(ModelAccess, self).delete(cursor, user, ids, context=context) self.check(cursor.dbname) return res diff --git a/trytond/ir/model.xml b/trytond/ir/model.xml index b6e46b5a3d..246524fbf8 100644 --- a/trytond/ir/model.xml +++ b/trytond/ir/model.xml @@ -130,7 +130,7 @@ - + @@ -154,8 +154,8 @@