diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 4cd0492d95c..897c5b5db43 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -945,6 +945,7 @@ class ir_actions_server(osv.osv): 'user': user, 'context': context, 'workflow': workflow + 'Warning': openerp.exceptions.Warning, } def run(self, cr, uid, ids, context=None): diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index 62ecd605223..258d405deb5 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -85,8 +85,8 @@ class ir_http(osv.AbstractModel): return auth_method def _handle_exception(self, exception): - # If handle exception return something different than None, it will be used as a response - raise + # If handle_exception returns something different than None, it will be used as a response + return request._handle_exception(exception) def _dispatch(self): # locate the controller method diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index f19a1fa6746..3c195c93c6c 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -603,7 +603,7 @@ class DateTimeConverter(osv.AbstractModel): if isinstance(value, basestring): value = datetime.datetime.strptime( value, openerp.tools.DEFAULT_SERVER_DATETIME_FORMAT) - value = column.context_timestamp( + value = fields.datetime.context_timestamp( cr, uid, timestamp=value, context=context) if options and 'format' in options: diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 360c5e37b31..7c857e767ba 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -73,12 +73,18 @@ class view_custom(osv.osv): class view(osv.osv): _name = 'ir.ui.view' - def _get_model_data(self, cr, uid, ids, *args, **kwargs): - ir_model_data = self.pool.get('ir.model.data') - data_ids = ir_model_data.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)]) - result = dict(zip(ids, data_ids)) + def _get_model_data(self, cr, uid, ids, fname, args, context=None): + result = dict.fromkeys(ids, False) + IMD = self.pool['ir.model.data'] + data_ids = IMD.search_read(cr, uid, [('res_id', 'in', ids), ('model', '=', 'ir.ui.view')], ['res_id'], context=context) + result.update(map(itemgetter('res_id', 'id'), data_ids)) return result + def _views_from_model_data(self, cr, uid, ids, context=None): + IMD = self.pool['ir.model.data'] + data_ids = IMD.search_read(cr, uid, [('id', 'in', ids), ('model', '=', 'ir.ui.view')], ['res_id'], context=context) + return map(itemgetter('res_id'), data_ids) + _columns = { 'name': fields.char('View Name', required=True), 'model': fields.char('Object', select=True), @@ -97,7 +103,11 @@ class view(osv.osv): 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade', select=True), 'inherit_children_ids': fields.one2many('ir.ui.view','inherit_id', 'Inherit Views'), 'field_parent': fields.char('Child Field'), - 'model_data_id': fields.function(_get_model_data, type='many2one', relation='ir.model.data', string="Model Data", store=True), + 'model_data_id': fields.function(_get_model_data, type='many2one', relation='ir.model.data', string="Model Data", + store={ + _name: (lambda s, c, u, i, ctx=None: i, None, 10), + 'ir.model.data': (_views_from_model_data, ['model', 'res_id'], 10), + }), 'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="External ID", help="ID of the view defined in xml file"), 'groups_id': fields.many2many('res.groups', 'ir_ui_view_group_rel', 'view_id', 'group_id', diff --git a/openerp/addons/base/res/res_font.py b/openerp/addons/base/res/res_font.py index 47e49bf4d39..767fc3754c1 100644 --- a/openerp/addons/base/res/res_font.py +++ b/openerp/addons/base/res/res_font.py @@ -37,6 +37,23 @@ and Ubuntu distros, we have to override the search path, too. """ _logger = logging.getLogger(__name__) +# Alternatives for the [broken] builtin PDF fonts. Default order chosen to match +# the pre-v8 mapping from openerp.report.render.rml2pdf.customfonts.CustomTTFonts. +# Format: [ (BuiltinFontFamily, mode, [AlternativeFontName, ...]), ...] +BUILTIN_ALTERNATIVES = [ + ('Helvetica', "normal", ["DejaVuSans", "LiberationSans"]), + ('Helvetica', "bold", ["DejaVuSans-Bold", "LiberationSans-Bold"]), + ('Helvetica', 'italic', ["DejaVuSans-Oblique", "LiberationSans-Italic"]), + ('Helvetica', 'bolditalic', ["DejaVuSans-BoldOblique", "LiberationSans-BoldItalic"]), + ('Times', 'normal', ["LiberationSerif", "DejaVuSerif"]), + ('Times', 'bold', ["LiberationSerif-Bold", "DejaVuSerif-Bold"]), + ('Times', 'italic', ["LiberationSerif-Italic", "DejaVuSerif-Italic"]), + ('Times', 'bolditalic', ["LiberationSerif-BoldItalic", "DejaVuSerif-BoldItalic"]), + ('Courier', 'normal', ["FreeMono", "DejaVuSansMono"]), + ('Courier', 'bold', ["FreeMonoBold", "DejaVuSansMono-Bold"]), + ('Courier', 'italic', ["FreeMonoOblique", "DejaVuSansMono-Oblique"]), + ('Courier', 'bolditalic', ["FreeMonoBoldOblique", "DejaVuSansMono-BoldOblique"]), +] class res_font(osv.Model): _name = "res.font" @@ -113,9 +130,32 @@ class res_font(osv.Model): def _sync(self, cr, uid, context=None): """Set the customfonts.CustomTTFonts list to the content of the database""" customfonts.CustomTTFonts = [] + local_family_modes = set() + local_font_paths = {} found_fonts_ids = self.search(cr, uid, [('path', '!=', '/dev/null')], context=context) for font in self.browse(cr, uid, found_fonts_ids, context=None): + local_family_modes.add((font.family, font.mode)) + local_font_paths[font.name] = font.path customfonts.CustomTTFonts.append((font.family, font.name, font.path, font.mode)) + + # Attempt to remap the builtin fonts (Helvetica, Times, Courier) to better alternatives + # if available, because they only support a very small subset of unicode + # (missing 'č' for example) + for builtin_font_family, mode, alts in BUILTIN_ALTERNATIVES: + if (builtin_font_family, mode) not in local_family_modes: + # No local font exists with that name, try alternatives + for altern_font in alts: + if local_font_paths.get(altern_font): + altern_def = (builtin_font_family, altern_font, + local_font_paths[altern_font], mode) + customfonts.CustomTTFonts.append(altern_def) + _logger.debug("Builtin remapping %r", altern_def) + break + else: + _logger.warning("No local alternative found for builtin font `%s` (%s mode)." + "Consider installing the DejaVu fonts if you have problems " + "with unicode characters in RML reports", + builtin_font_family, mode) return True def clear_caches(self): diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index 46f0218e21f..896f455a274 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -28,6 +28,7 @@ import openerp from openerp import SUPERUSER_ID from openerp import tools from openerp.osv import osv, fields +from openerp.osv.expression import get_unaccent_wrapper from openerp.tools.translate import _ class format_address(object): @@ -228,10 +229,10 @@ class res_partner(osv.osv, format_address): _order = "display_name" _columns = { 'name': fields.char('Name', size=128, required=True, select=True), - 'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers), + 'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers, select=True), 'date': fields.date('Date', select=1), 'title': fields.many2one('res.partner.title', 'Title'), - 'parent_id': fields.many2one('res.partner', 'Related Company'), + 'parent_id': fields.many2one('res.partner', 'Related Company', select=True), 'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override 'ref': fields.char('Reference', size=64, select=1), 'lang': fields.selection(_lang_get, 'Language', @@ -629,10 +630,17 @@ class res_partner(osv.osv, format_address): if operator in ('=ilike', '=like'): operator = operator[1:] - query = ('SELECT id FROM res_partner ' + - where_str + '(email ' + operator + ''' %s - OR display_name ''' + operator + ''' %s) - ORDER BY display_name''') + unaccent = get_unaccent_wrapper(cr) + + query = """SELECT id + FROM res_partner + {where} ({email} {operator} {percent} + OR {display_name} {operator} {percent}) + ORDER BY {display_name} + """.format(where=where_str, operator=operator, + email=unaccent('email'), + display_name=unaccent('display_name'), + percent=unaccent('%s')) where_clause_params += [search_name, search_name] if limit: diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index a05009b0252..f12b7216b77 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -1,6 +1,7 @@ import unittest2 import openerp +from openerp.osv.expression import get_unaccent_wrapper from openerp.osv.orm import BaseModel import openerp.tests.common as common @@ -124,6 +125,7 @@ class test_expression(common.TransactionCase): def test_20_auto_join(self): registry, cr, uid = self.registry, self.cr, self.uid + unaccent = get_unaccent_wrapper(cr) # Get models partner_obj = registry('res.partner') @@ -180,8 +182,11 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('res_partner_bank', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table") - self.assertIn('"res_partner_bank"."name" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") + self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter") sql_query = self.query_list[2].get_sql() @@ -217,8 +222,11 @@ class test_expression(common.TransactionCase): "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table") self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join") - self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition") + self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), @@ -296,8 +304,11 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('"res_country"', sql_query[0], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") - self.assertIn('"res_country"."code" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_country"."code"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") + self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") sql_query = self.query_list[1].get_sql() @@ -327,8 +338,11 @@ class test_expression(common.TransactionCase): "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join") - self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") + self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], @@ -358,8 +372,11 @@ class test_expression(common.TransactionCase): "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") - self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition") + self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1], @@ -385,7 +402,9 @@ class test_expression(common.TransactionCase): "_auto_join on one2many with domains incorrect result") # Test produced queries that domains effectively present sql_query = self.query_list[0].get_sql() - self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1], + + expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s')) + self.assertIn(expected, sql_query[1], "_auto_join on one2many with domains incorrect result") # TDE TODO: check first domain has a correct table name self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1], @@ -447,6 +466,19 @@ class test_expression(common.TransactionCase): domain = [('x', 'in', ['y', 'z']), ('a.v', '=', 'e'), '|', '|', ('a', '=', 'b'), '!', ('c', '>', 'd'), ('e', '!=', 'f'), ('g', '=', 'h')] norm_domain = ['&', '&', '&'] + domain assert norm_domain == expression.normalize_domain(domain), "Non-normalized domains should be properly normalized" + + def test_translate_search(self): + Country = self.registry('res.country') + be = self.ref('base.be') + domains = [ + [('name', '=', 'Belgium')], + [('name', 'ilike', 'Belgi')], + [('name', 'in', ['Belgium', 'Care Bears'])], + ] + + for domain in domains: + ids = Country.search(self.cr, self.uid, domain) + self.assertListEqual([be], ids) if __name__ == '__main__': unittest2.main() diff --git a/openerp/cli/server.py b/openerp/cli/server.py index e7ba695f19c..561b9a19879 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -29,6 +29,7 @@ GNU Public Licence. (c) 2003-TODAY, Fabien Pinckaers - OpenERP SA """ +import atexit import logging import os import signal @@ -78,16 +79,23 @@ def report_configuration(): ('database user', config['db_user'])]: _logger.info("%s: %s", name, value) +def rm_pid_file(): + config = openerp.tools.config + if not openerp.evented and os.path.exists(config['pidfile']): + os.unlink(config['pidfile']) + def setup_pid_file(): """ Create a file with the process id written in it. This function assumes the configuration has been initialized. """ config = openerp.tools.config - if config['pidfile']: + if not openerp.evented and config['pidfile']: with open(config['pidfile'], 'w') as fd: pidtext = "%d" % (os.getpid()) fd.write(pidtext) + atexit.register(rm_pid_file) + def export_translation(): config = openerp.tools.config @@ -155,8 +163,6 @@ def main(args): setup_pid_file() rc = openerp.service.server.start(preload=preload, stop=stop) - if config['pidfile']: - os.unlink(config['pidfile']) sys.exit(rc) class Server(Command): diff --git a/openerp/http.py b/openerp/http.py index 5f37afbc116..2e21d27528a 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -270,6 +270,13 @@ class WebRequest(object): self.endpoint = endpoint self.auth_method = auth + + def _handle_exception(self, exception): + """Called within an except block to allow converting exceptions + to abitrary responses. Anything returned (except None) will + be used as response.""" + raise + def _call_function(self, *args, **kwargs): request = self if self.endpoint.routing['type'] != self._request_type: @@ -421,39 +428,15 @@ class JsonRequest(WebRequest): self.params = dict(self.jsonrequest.get("params", {})) self.context = self.params.pop('context', dict(self.session.context)) - def dispatch(self): - """ Calls the method asked for by the JSON-RPC2 or JSONP request - """ - if self.jsonp_handler: - return self.jsonp_handler() - response = {"jsonrpc": "2.0" } - error = None - - try: - response['id'] = self.jsonrequest.get('id') - response["result"] = self._call_function(**self.params) - except AuthenticationError, e: - _logger.exception("JSON-RPC AuthenticationError in %s.", self.httprequest.path) - se = serialize_exception(e) - error = { - 'code': 100, - 'message': "OpenERP Session Invalid", - 'data': se - } - self._failed = e # prevent tx commit - except Exception, e: - # Mute test cursor error for runbot - if not (openerp.tools.config['test_enable'] and isinstance(e, psycopg2.OperationalError)): - _logger.exception("JSON-RPC Exception in %s.", self.httprequest.path) - se = serialize_exception(e) - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': se - } - self._failed = e # prevent tx commit - if error: - response["error"] = error + def _json_response(self, result=None, error=None): + response = { + 'jsonrpc': '2.0', + 'id': self.jsonrequest.get('id') + } + if error is not None: + response['error'] = error + if result is not None: + response['result'] = result if self.jsonp: # If we use jsonp, that's mean we are called from another host @@ -466,8 +449,36 @@ class JsonRequest(WebRequest): mime = 'application/json' body = simplejson.dumps(response) - r = Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))]) - return r + return Response( + body, headers=[('Content-Type', mime), + ('Content-Length', len(body))]) + + def _handle_exception(self, exception): + """Called within an except block to allow converting exceptions + to abitrary responses. Anything returned (except None) will + be used as response.""" + _logger.exception("Exception during JSON request handling.") + self._failed = exception # prevent tx commit + error = { + 'code': 200, + 'message': "OpenERP Server Error", + 'data': serialize_exception(exception) + } + if isinstance(exception, AuthenticationError): + error['code'] = 100 + error['message'] = "OpenERP Session Invalid" + return self._json_response(error=error) + + def dispatch(self): + """ Calls the method asked for by the JSON-RPC2 or JSONP request + """ + if self.jsonp_handler: + return self.jsonp_handler() + try: + result = self._call_function(**self.params) + return self._json_response(result) + except Exception, e: + return self._handle_exception(e) def serialize_exception(e): tmp = { diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 648a8447292..52dc4a265ba 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -429,6 +429,10 @@ def select_distinct_from_where_not_null(cr, select_field, from_table): cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (select_field, from_table, select_field)) return [r[0] for r in cr.fetchall()] +def get_unaccent_wrapper(cr): + if openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent: + return lambda x: "unaccent(%s)" % (x,) + return lambda x: x # -------------------------------------------------- # ExtendedLeaf class for managing leafs and contexts @@ -630,7 +634,7 @@ class expression(object): :attr list expression: the domain expression, that will be normalized and prepared """ - self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent + self._unaccent = get_unaccent_wrapper(cr) self.joins = [] self.root_model = table @@ -1012,10 +1016,10 @@ class expression(object): else: if field._type == 'datetime' and right and len(right) == 10: - if operator in ('>', '>=', '='): - right += ' 00:00:00' - elif operator in ('<', '<='): + if operator in ('>', '<='): right += ' 23:59:59' + else: + right += ' 00:00:00' push(create_substitution_leaf(leaf, (left, operator, right), working_model)) elif field.translate and right: @@ -1030,33 +1034,37 @@ class expression(object): sql_operator = sql_operator[4:] if sql_operator[:3] == 'not' else '=' inselect_operator = 'not inselect' - subselect = '( SELECT res_id' \ - ' FROM ir_translation' \ - ' WHERE name = %s' \ - ' AND lang = %s' \ - ' AND type = %s' - instr = ' %s' - #Covering in,not in operators with operands (%s,%s) ,etc. - if sql_operator == 'in': - instr = ','.join(['%s'] * len(right)) - subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \ - ') UNION (' \ - ' SELECT id' \ - ' FROM "' + working_model._table + '"' \ - ' WHERE "' + left + '" ' + sql_operator + ' ' + " (" + instr + "))" - else: - subselect += ' AND value ' + sql_operator + instr + \ - ') UNION (' \ - ' SELECT id' \ - ' FROM "' + working_model._table + '"' \ - ' WHERE "' + left + '" ' + sql_operator + instr + ")" + unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x - params = [working_model._name + ',' + left, - context.get('lang', False) or 'en_US', - 'model', - right, - right, - ] + trans_left = unaccent('value') + quote_left = unaccent(_quote(left)) + instr = unaccent('%s') + + if sql_operator == 'in': + # params will be flatten by to_sql() => expand the placeholders + instr = '(%s)' % ', '.join(['%s'] * len(right)) + + subselect = """(SELECT res_id + FROM ir_translation + WHERE name = %s + AND lang = %s + AND type = %s + AND {trans_left} {operator} {right} + ) UNION ( + SELECT id + FROM "{table}" + WHERE {left} {operator} {right} + ) + """.format(trans_left=trans_left, operator=sql_operator, + right=instr, table=working_model._table, left=quote_left) + + params = ( + working_model._name + ',' + left, + context.get('lang') or 'en_US', + 'model', + right, + right, + ) push(create_substitution_leaf(leaf, ('id', inselect_operator, (subselect, params)), working_model)) else: @@ -1177,10 +1185,9 @@ class expression(object): if left in model._columns: format = need_wildcard and '%s' or model._columns[left]._symbol_set[0] - if self.has_unaccent and sql_operator in ('ilike', 'not ilike'): - query = '(unaccent(%s."%s") %s unaccent(%s))' % (table_alias, left, sql_operator, format) - else: - query = '(%s."%s" %s %s)' % (table_alias, left, sql_operator, format) + unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x + column = '%s.%s' % (table_alias, _quote(left)) + query = '(%s %s %s)' % (unaccent(column), sql_operator, unaccent(format)) elif left in MAGIC_COLUMNS: query = "(%s.\"%s\" %s %%s)" % (table_alias, left, sql_operator) params = right diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index d5d0ee0fa30..f60e83726c4 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -648,7 +648,10 @@ class one2many(_column): else: cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%s', (act[1],)) elif act[0] == 4: - cr.execute("select 1 from {0} where id=%s and {1}=%s".format(_table, self._fields_id), (act[1], id)) + # table of the field (parent_model in case of inherit) + field_model = self._fields_id in obj.pool[self._obj]._columns and self._obj or obj.pool[self._obj]._all_columns[self._fields_id].parent_model + field_table = obj.pool[field_model]._table + cr.execute("select 1 from {0} where id=%s and {1}=%s".format(field_table, self._fields_id), (act[1], id)) if not cr.fetchone(): # Must use write() to recompute parent_store structure if needed and check access rules obj.write(cr, user, [act[1]], {self._fields_id:id}, context=context or {}) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 7b7c6065907..af64e559b54 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -469,7 +469,12 @@ class browse_record(object): else: new_data[field_name] = browse_null() elif field_column._type in ('one2many', 'many2many') and len(result_line[field_name]): - new_data[field_name] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool[field_column._obj], self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in result_line[field_name]], self._context) + new_data[field_name] = self._list_class( + (browse_record(self._cr, self._uid, id, self._table.pool.get(field_column._obj), + self._cache, context=self._context, list_class=self._list_class, + fields_process=self._fields_process) + for id in result_line[field_name]), + context=self._context) elif field_column._type == 'reference': if result_line[field_name]: if isinstance(result_line[field_name], browse_record): @@ -1911,7 +1916,7 @@ class BaseModel(object): return result def _view_look_dom_arch(self, cr, uid, node, view_id, context=None): - return self['ir.ui.view'].postprocess_and_fields( + return self.pool['ir.ui.view'].postprocess_and_fields( cr, uid, self._name, node, view_id, context=context) def search_count(self, cr, user, args, context=None): @@ -4250,7 +4255,7 @@ class BaseModel(object): if isinstance(select, (int, long)): return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) elif isinstance(select, list): - return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context=context) + return self._list_class((browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select), context=context) else: return browse_null() diff --git a/openerp/report/render/rml2pdf/trml2pdf.py b/openerp/report/render/rml2pdf/trml2pdf.py index aaed9ddf4c6..6ce69578a8e 100644 --- a/openerp/report/render/rml2pdf/trml2pdf.py +++ b/openerp/report/render/rml2pdf/trml2pdf.py @@ -88,50 +88,30 @@ def _open_image(filename, path=None): class NumberedCanvas(canvas.Canvas): def __init__(self, *args, **kwargs): canvas.Canvas.__init__(self, *args, **kwargs) - self._codes = [] - self._flag=False - self._pageCount=0 - self._currentPage =0 - self._pageCounter=0 - self.pages={} + self._saved_page_states = [] def showPage(self): - self._currentPage +=1 - if not self._flag: - self._pageCount += 1 - else: - self.pages.update({self._currentPage:self._pageCount}) - self._codes.append({'code': self._code, 'stack': self._codeStack}) + self._saved_page_states.append(dict(self.__dict__)) self._startPage() - self._flag=False - def pageCount(self): - if self.pages.get(self._pageCounter,False): - self._pageNumber=0 - self._pageCounter +=1 - key=self._pageCounter - if not self.pages.get(key,False): - while not self.pages.get(key,False): - key += 1 + def save(self): + """add page info to each page (page x of y)""" + for state in self._saved_page_states: + self.__dict__.update(state) + self.draw_page_number() + canvas.Canvas.showPage(self) + canvas.Canvas.save(self) + + def draw_page_number(self): + page_count = len(self._saved_page_states) self.setFont("Helvetica", 8) self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40), " %(this)i / %(total)i" % { 'this': self._pageNumber+1, - 'total': self.pages.get(key,False), + 'total': page_count, } ) - def save(self): - """add page info to each page (page x of y)""" - # reset page counter - self._pageNumber = 0 - for code in self._codes: - self._code = code['code'] - self._codeStack = code['stack'] - self.pageCount() - canvas.Canvas.showPage(self) -# self.restoreState() - self._doc.SaveToFile(self._filename, self) class PageCount(platypus.Flowable): def __init__(self, story_count=0): @@ -303,6 +283,9 @@ class _rml_doc(object): from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont + if mode: + mode = mode.lower() + if fontname not in pdfmetrics._fonts: pdfmetrics.registerFont(TTFont(fontname, filename)) if mode == 'all': @@ -310,14 +293,14 @@ class _rml_doc(object): addMapping(face, 0, 1, fontname) #italic addMapping(face, 1, 0, fontname) #bold addMapping(face, 1, 1, fontname) #italic and bold - elif (mode== 'normal') or (mode == 'regular') or (mode == 'book'): - addMapping(face, 0, 0, fontname) #normal - elif mode == 'italic': + elif mode in ['italic', 'oblique']: addMapping(face, 0, 1, fontname) #italic elif mode == 'bold': addMapping(face, 1, 0, fontname) #bold - elif mode == 'bolditalic': + elif mode in ('bolditalic', 'bold italic','boldoblique', 'bold oblique'): addMapping(face, 1, 1, fontname) #italic and bold + else: + addMapping(face, 0, 0, fontname) #normal def _textual_image(self, node): rc = '' diff --git a/openerp/service/server.py b/openerp/service/server.py index f9db66a28ef..0cdb9f27e54 100644 --- a/openerp/service/server.py +++ b/openerp/service/server.py @@ -440,7 +440,7 @@ class PreforkServer(CommonServer): sys.exit(0) def long_polling_spawn(self): - nargs = stripped_sys_argv('--pidfile', '--workers') + nargs = stripped_sys_argv() cmd = nargs[0] cmd = os.path.join(os.path.dirname(cmd), "openerp-gevent") nargs[0] = cmd @@ -834,8 +834,13 @@ def _reexec(updated_modules=None): def load_test_file_yml(registry, test_file): with registry.cursor() as cr: - openerp.tools.convert_yaml_import(cr, 'base', file(test_file), 'test', {}, 'test', True) - cr.rollback() + openerp.tools.convert_yaml_import(cr, 'base', file(test_file), 'test', {}, 'init') + if config['test_commit']: + _logger.info('test %s has been commited', test_file) + cr.commit() + else: + _logger.info('test %s has been rollbacked', test_file) + cr.rollback() def load_test_file_py(registry, test_file): # Locate python module based on its filename and run the tests @@ -887,10 +892,10 @@ def start(preload=None, stop=False): """ global server load_server_wide_modules() - if config['workers']: - server = PreforkServer(openerp.service.wsgi_server.application) - elif openerp.evented: + if openerp.evented: server = GeventServer(openerp.service.wsgi_server.application) + elif config['workers']: + server = PreforkServer(openerp.service.wsgi_server.application) else: server = ThreadedServer(openerp.service.wsgi_server.application) diff --git a/openerp/tools/image.py b/openerp/tools/image.py index 08c2fd91f21..93458fb9408 100644 --- a/openerp/tools/image.py +++ b/openerp/tools/image.py @@ -70,7 +70,11 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file image_stream = StringIO.StringIO(base64_source.decode(encoding)) image = Image.open(image_stream) # store filetype here, as Image.new below will lose image.format - filetype = filetype or image.format + filetype = (filetype or image.format).upper() + + filetype = { + 'BMP': 'PNG', + }.get(filetype, filetype) asked_width, asked_height = size if asked_width is None: