diff --git a/openerp-server b/openerp-server index 2698307ff42..dbae1c31884 100755 --- a/openerp-server +++ b/openerp-server @@ -252,6 +252,11 @@ if __name__ == "__main__": import_translation() sys.exit(0) + if not config["stop_after_init"]: + # Some module register themselves when they are loaded so we need the + # services to be running before loading any registry. + start_services() + if config['db_name']: for dbname in config['db_name'].split(','): preload_registry(dbname) @@ -261,7 +266,6 @@ if __name__ == "__main__": setup_pid_file() setup_signal_handlers() - start_services() logger = logging.getLogger('server') logger.info('OpenERP server is running, waiting for connections...') quit_on_signals() diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index 3ab0bb3f167..045fed2e2a0 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -217,7 +217,7 @@ {'active_test': False} - + @@ -1198,7 +1198,7 @@ ir.model.data form -
+ @@ -1216,7 +1216,7 @@ ir.model.data tree - + @@ -1230,7 +1230,7 @@ ir.model.data search - + @@ -1270,7 +1270,7 @@ - Object Identifiers + External Identifiers ir.model.data form diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index a9d2a21b7a5..1ae54af0cb9 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -561,14 +561,27 @@ class ir_model_access(osv.osv): ir_model_access() class ir_model_data(osv.osv): + """Holds external identifier keys for records in the database. + This has two main uses: + + * allows easy data integration with third-party systems, + making import/export/sync of data possible, as records + can be uniquely identified across multiple systems + * allows tracking the origin of data installed by OpenERP + modules themselves, thus making it possible to later + update them seamlessly. + """ _name = 'ir.model.data' __logger = logging.getLogger('addons.base.'+_name) _order = 'module,model,name' _columns = { - 'name': fields.char('XML Identifier', required=True, size=128, select=1), - 'model': fields.char('Object', required=True, size=64, select=1), + 'name': fields.char('External Identifier', required=True, size=128, select=1, + help="External Key/Identifier that can be used for " + "data integration with third-party systems"), + 'model': fields.char('Model Name', required=True, size=64, select=1), 'module': fields.char('Module', required=True, size=64, select=1), - 'res_id': fields.integer('Resource ID', select=1), + 'res_id': fields.integer('Record ID', select=1, + help="ID of the target record in the database"), 'noupdate': fields.boolean('Non Updatable'), 'date_update': fields.datetime('Update Date'), 'date_init': fields.datetime('Init Date') @@ -580,7 +593,7 @@ class ir_model_data(osv.osv): 'module': '' } _sql_constraints = [ - ('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same id for the same module !'), + ('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same external ID in the same module!'), ] def __init__(self, pool, cr): diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py index 940909b0d71..813ab514b0f 100644 --- a/openerp/addons/base/ir/ir_translation.py +++ b/openerp/addons/base/ir/ir_translation.py @@ -59,7 +59,7 @@ class ir_translation(osv.osv): # These two columns map to ir_model_data.module and ir_model_data.name. # They are used to resolve the res_id above after loading is done. 'module': fields.char('Module', size=64, help='Maps to the ir_model_data for which this translation is provided.'), - 'xml_id': fields.char('XML Id', size=128, help='Maps to the ir_model_data for which this translation is provided.'), + 'xml_id': fields.char('External ID', size=128, help='Maps to the ir_model_data for which this translation is provided.'), } def _auto_init(self, cr, context={}): diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 56f7ae76519..90990834c9b 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -73,11 +73,12 @@ class view(osv.osv): ('calendar', 'Calendar'), ('diagram','Diagram'), ('gantt', 'Gantt'), + ('kanban', 'Kanban'), ('search','Search')), 'View Type', required=True, select=True), 'arch': fields.text('View Architecture', required=True), 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade', select=True), 'field_parent': fields.char('Child Field',size=64), - 'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="XML ID", + 'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="External ID", method=True, help="ID of the view defined in xml file"), } _defaults = { diff --git a/openerp/addons/base/rng/view.rng b/openerp/addons/base/rng/view.rng index b641330b7ba..3cad1a9a369 100644 --- a/openerp/addons/base/rng/view.rng +++ b/openerp/addons/base/rng/view.rng @@ -111,6 +111,19 @@ + + + + + + + + + + + + + @@ -460,6 +473,7 @@ + @@ -618,6 +632,7 @@ + diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index fb973d2e521..7a8f460f5cb 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -32,7 +32,9 @@ * size """ +import base64 import datetime as DT +import re import string import sys import warnings @@ -727,34 +729,41 @@ def get_nice_size(value): size = len(value) return tools.human_size(size) +# See http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char +# and http://bugs.python.org/issue10066 +invalid_xml_low_bytes = re.compile(r'[\x00-\x08\x0b-\x0c\x0e-\x1f]') + def sanitize_binary_value(value): # binary fields should be 7-bit ASCII base64-encoded data, # but we do additional sanity checks to make sure the values - # are not something else that won't pass via xmlrpc + # are not something else that won't pass via XML-RPC if isinstance(value, (xmlrpclib.Binary, tuple, list, dict)): # these builtin types are meant to pass untouched return value - # For all other cases, handle the value as a binary string: - # it could be a 7-bit ASCII string (e.g base64 data), but also - # any 8-bit content from files, with byte values that cannot - # be passed inside XML! - # See for more info: + # Handle invalid bytes values that will cause problems + # for XML-RPC. See for more info: # - http://bugs.python.org/issue10066 # - http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char - # - # One solution is to convert the byte-string to unicode, - # so it gets serialized as utf-8 encoded data (always valid XML) - # If invalid XML byte values were present, tools.ustr() uses - # the Latin-1 codec as fallback, which converts any 8-bit - # byte value, resulting in valid utf-8-encoded bytes - # in the end: - # >>> unicode('\xe1','latin1').encode('utf8') == '\xc3\xa1' - # Note: when this happens, decoding on the other endpoint - # is not likely to produce the expected output, but this is - # just a safety mechanism (in these cases base64 data or - # xmlrpc.Binary values should be used instead) - return tools.ustr(value) + + # Coercing to unicode would normally allow it to properly pass via + # XML-RPC, transparently encoded as UTF-8 by xmlrpclib. + # (this works for _any_ byte values, thanks to the fallback + # to latin-1 passthrough encoding when decoding to unicode) + value = tools.ustr(value) + + # Due to Python bug #10066 this could still yield invalid XML + # bytes, specifically in the low byte range, that will crash + # the decoding side: [\x00-\x08\x0b-\x0c\x0e-\x1f] + # So check for low bytes values, and if any, perform + # base64 encoding - not very smart or useful, but this is + # our last resort to avoid crashing the request. + if invalid_xml_low_bytes.search(value): + # b64-encode after restoring the pure bytes with latin-1 + # passthrough encoding + value = base64.b64encode(value.encode('latin-1')) + + return value # ---------------------------------------------------------