diff --git a/openerp-server b/openerp-server index d2478d00ff6..7965a0c073d 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) @@ -273,7 +278,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/base_update.xml b/openerp/addons/base/base_update.xml index cef36dda8e4..b87c0ef4382 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -111,7 +111,8 @@ - + + diff --git a/openerp/addons/base/i18n/cs.po b/openerp/addons/base/i18n/cs.po index 5161c860eb2..dd8b5caee98 100644 --- a/openerp/addons/base/i18n/cs.po +++ b/openerp/addons/base/i18n/cs.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-08-13 17:34+0000\n" +"PO-Revision-Date: 2011-09-08 12:09+0000\n" "Last-Translator: Jiří Hajda \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-01 04:40+0000\n" -"X-Generator: Launchpad (build 13827)\n" +"X-Launchpad-Export-Date: 2011-09-09 04:37+0000\n" +"X-Generator: Launchpad (build 13900)\n" "X-Poedit-Language: Czech\n" #. module: base @@ -174,7 +174,7 @@ msgstr "ir.ui.view.custom" #. module: base #: model:res.country,name:base.sz msgid "Swaziland" -msgstr "Švýcarsko" +msgstr "Svazijsko" #. module: base #: code:addons/orm.py:1993 @@ -1876,7 +1876,7 @@ msgstr "Slovinsko" #. module: base #: model:res.country,name:base.pk msgid "Pakistan" -msgstr "Pakistán" +msgstr "Pákistán" #. module: base #: code:addons/orm.py:1350 @@ -3532,7 +3532,7 @@ msgstr "Pobřeží slonoviny (Cote D'Ivoire)" #. module: base #: model:res.country,name:base.kz msgid "Kazakhstan" -msgstr "Kazakstán" +msgstr "Kazachstán" #. module: base #: view:res.lang:0 @@ -3896,7 +3896,7 @@ msgstr "Měsíc" #. module: base #: model:res.country,name:base.my msgid "Malaysia" -msgstr "Malaysie" +msgstr "Malajsie" #. module: base #: view:base.language.install:0 @@ -4059,7 +4059,7 @@ msgstr "Pravidlo musí mít alespoň jedno aktivní přístupové právo !" #. module: base #: model:res.country,name:base.fj msgid "Fiji" -msgstr "Fiji" +msgstr "Fidži" #. module: base #: field:ir.model.fields,size:0 @@ -4069,7 +4069,7 @@ msgstr "Velikost" #. module: base #: model:res.country,name:base.sd msgid "Sudan" -msgstr "Sudan" +msgstr "Súdán" #. module: base #: model:res.country,name:base.fm @@ -4118,7 +4118,7 @@ msgstr "Formát času" #. module: base #: view:ir.module.module:0 msgid "Defined Reports" -msgstr "Definoavné výkazy" +msgstr "Definované výkazy" #. module: base #: view:ir.actions.report.xml:0 @@ -4617,7 +4617,7 @@ msgstr "Přístup k nabídce" #. module: base #: model:res.country,name:base.na msgid "Namibia" -msgstr "Nambie" +msgstr "Namibie" #. module: base #: model:res.country,name:base.mn @@ -4800,7 +4800,7 @@ msgstr "Adresy" #. module: base #: model:res.country,name:base.mm msgid "Myanmar" -msgstr "Barma" +msgstr "Myanmar" #. module: base #: selection:base.language.install,lang:0 @@ -5476,12 +5476,12 @@ msgstr "Globální" #. module: base #: model:res.country,name:base.mp msgid "Northern Mariana Islands" -msgstr "Severní Mariánské ostrovy" +msgstr "Severní Mariany" #. module: base #: model:res.country,name:base.sb msgid "Solomon Islands" -msgstr "Šalamounovy Ostrovy" +msgstr "Šalamounovy ostrovy" #. module: base #: code:addons/base/ir/ir_model.py:490 @@ -5682,7 +5682,7 @@ msgstr "Jazyk" #. module: base #: model:res.country,name:base.gm msgid "Gambia" -msgstr "Gambia" +msgstr "Gambie" #. module: base #: model:ir.actions.act_window,name:base.action_res_company_form @@ -6443,7 +6443,7 @@ msgstr "Mongolština / монгол" #. module: base #: model:res.country,name:base.mr msgid "Mauritania" -msgstr "Mauretánie" +msgstr "Mauritánie" #. module: base #: model:ir.model,name:base.model_ir_translation @@ -7649,7 +7649,7 @@ msgstr "Změnit heslo" #. module: base #: model:res.country,name:base.nl msgid "Netherlands" -msgstr "Nizozemí" +msgstr "Nizozemsko" #. module: base #: model:ir.ui.menu,name:base.next_id_4 @@ -8137,7 +8137,7 @@ msgstr "Asociace" #. module: base #: model:res.country,name:base.cl msgid "Chile" -msgstr "Čile" +msgstr "Chile" #. module: base #: model:ir.ui.menu,name:base.menu_address_book @@ -8185,7 +8185,7 @@ msgstr "Popisek pole" #. module: base #: model:res.country,name:base.dj msgid "Djibouti" -msgstr "Djibouti" +msgstr "Džibutsko" #. module: base #: field:ir.translation,value:0 @@ -8364,7 +8364,7 @@ msgstr "Chyba" #. module: base #: model:res.country,name:base.pm msgid "Saint Pierre and Miquelon" -msgstr "Saint Pierre a Miquelon" +msgstr "Svatý Pierre a Miquelon" #. module: base #: help:ir.actions.report.xml,header:0 @@ -8540,7 +8540,7 @@ msgstr "" #. module: base #: model:res.country,name:base.sa msgid "Saudi Arabia" -msgstr "Saudská Arábie" +msgstr "Saúdská Arábie" #. module: base #: help:res.partner,supplier:0 @@ -8637,7 +8637,7 @@ msgstr "" #. module: base #: model:res.country,name:base.sv msgid "El Salvador" -msgstr "El Salvador" +msgstr "Salvador" #. module: base #: field:res.bank,phone:0 @@ -8937,7 +8937,7 @@ msgstr "Pracovní postup, který má být spuštěn pro tento model." #. module: base #: model:res.country,name:base.jm msgid "Jamaica" -msgstr "Jamaika" +msgstr "Jamajka" #. module: base #: model:ir.actions.act_window,help:base.action_partner_category_form diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index 011ad437cca..adbab648798 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -241,7 +241,7 @@ {'active_test': False} - + @@ -1221,7 +1221,7 @@ ir.model.data form -
+ @@ -1239,7 +1239,7 @@ ir.model.data tree - + @@ -1253,7 +1253,7 @@ ir.model.data search - + @@ -1293,7 +1293,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 4092bf35b2f..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={}): @@ -177,7 +177,8 @@ class ir_translation(osv.osv): if not context: context = {} ids = super(ir_translation, self).create(cr, uid, vals, context=context) - self._get_source.clear_cache(self, vals.get('name',0), vals.get('type',0), vals.get('lang',0), vals.get('src',0)) + self._get_source.clear_cache(self, uid, vals.get('name',0), vals.get('type',0), vals.get('lang',0), vals.get('src',0)) + self._get_ids.clear_cache(self, uid, vals.get('name',0), vals.get('type',0), vals.get('lang',0), vals.get('res_id',0)) return ids def write(self, cursor, user, ids, vals, context=None): @@ -186,8 +187,8 @@ class ir_translation(osv.osv): if isinstance(ids, (int, long)): ids = [ids] result = super(ir_translation, self).write(cursor, user, ids, vals, context=context) - self._get_source.clear_cache(self, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(self, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], trans_obj['src']) self._get_ids.clear_cache(self, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], [trans_obj['res_id']]) return result diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 83660cfe753..b49a3208225 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -88,7 +88,7 @@ class ir_ui_menu(osv.osv): field = model_field.get(menu.action._name) if field and data[field]: - if not modelaccess.check(cr, uid, data[field], False): + if not modelaccess.check(cr, uid, data[field], 'read', False): continue else: # if there is no action, it's a 'folder' menu 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/res/res_user.py b/openerp/addons/base/res/res_user.py index cd8fa6563e5..f2140b30f1d 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -221,8 +221,8 @@ class users(osv.osv): 'password': fields.char('Password', size=64, invisible=True, help="Keep empty if you don't want the user to be able to connect on the system."), 'new_password': fields.function(_get_password, method=True, type='char', size=64, fnct_inv=_set_new_password, - string='Change password', help="Only specify a value if you want to change the user password. " - "This user will have to logout and login again!"), + string='Set password', help="Specify a value only when creating a user or if you're changing the user's password, " + "otherwise leave empty. After a change of password, the user has to login again."), 'user_email': fields.char('Email', size=64), 'signature': fields.text('Signature', size=64), 'active': fields.boolean('Active'), 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 # ---------------------------------------------------------