diff options
Diffstat (limited to 'bin')
29 files changed, 382 insertions, 193 deletions
diff --git a/bin/addons/base/ir/ir.xml b/bin/addons/base/ir/ir.xml index edafae1af1c..1169798fe8e 100644 --- a/bin/addons/base/ir/ir.xml +++ b/bin/addons/base/ir/ir.xml @@ -1302,14 +1302,14 @@ <field name="subject" colspan="4" attrs="{'required':[('state','=','email')]}"/> <field name="message" select="2" colspan="4" attrs="{'required':[('state','=','email')]}"/> <newline/> - <label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e.[[ object.partner_id.name ]]" align="0.0"/> + <label colspan="4" string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/> </page> <page string="SMS Configuration" attrs="{'invisible':[('state','!=','sms')]}"> <separator colspan="4" string="SMS Configuration"/> <field name="mobile" domain="[('model_id','=',model_id)]" attrs="{'required':[('state','=','sms')]}"/> <field name="sms" colspan="4" attrs="{'required':[('state','=','sms')]}"/> <newline/> - <label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e. [[ object.partner_id.name ]]" align="0.0"/> + <label colspan="4" string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/> </page> <page string="Create / Write / Copy" attrs="{'invisible':[('state','!=','object_create'), ('state','!=','object_write'), ('state','!=','object_copy')]}"> <separator colspan="4" string="Fields Mapping"/> diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index dd5cbc833b4..6c51ab2f771 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -455,7 +455,7 @@ class actions_server(osv.osv): 'type': lambda *a: 'ir.actions.server', 'sequence': lambda *a: 5, 'code': lambda *a: """# You can use the following variables -# - object +# - object or obj # - time # - cr # - uid @@ -532,7 +532,6 @@ class actions_server(osv.osv): def run(self, cr, uid, ids, context={}): logger = netsvc.Logger() - for action in self.browse(cr, uid, ids, context): obj_pool = self.pool.get(action.model_id.model) obj = obj_pool.browse(cr, uid, context['active_id'], context=context) @@ -554,19 +553,26 @@ class actions_server(osv.osv): return self.pool.get(action.action_id.type)\ .read(cr, uid, action.action_id.id, context=context) - if action.state == 'code': - localdict = { - 'self': self.pool.get(action.model_id.model), - 'context': context, - 'time': time, - 'ids': ids, - 'cr': cr, - 'uid': uid, - 'object':obj - } - exec action.code in localdict - if 'action' in localdict: - return localdict['action'] + if config['server_actions_allow_code']: + localdict = { + 'self': self.pool.get(action.model_id.model), + 'context': context, + 'time': time, + 'ids': ids, + 'cr': cr, + 'uid': uid, + 'object':obj, + 'obj': obj, + } + exec action.code in localdict + if 'action' in localdict: + return localdict['action'] + else: + netsvc.Logger().notifyChannel( + self._name, netsvc.LOG_ERROR, + "%s is a `code` server action, but " + "it isn't allowed in this configuration.\n\n" + "See server options to enable it"%action) if action.state == 'email': user = config['email_from'] @@ -580,7 +586,7 @@ class actions_server(osv.osv): logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!') continue if not user: - raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !")) + raise osv.except_osv(_('Error'), _("Please specify server option --email-from !")) subject = self.merge_message(cr, uid, action.subject, action, context) body = self.merge_message(cr, uid, action.message, action, context) diff --git a/bin/addons/base/ir/ir_cron.py b/bin/addons/base/ir/ir_cron.py index dcf76945904..66ead94be48 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -146,16 +146,19 @@ class ir_cron(osv.osv, netsvc.Agent): def create(self, cr, uid, vals, context=None): res = super(ir_cron, self).create(cr, uid, vals, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res def write(self, cr, user, ids, vals, context=None): res = super(ir_cron, self).write(cr, user, ids, vals, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res def unlink(self, cr, uid, ids, context=None): res = super(ir_cron, self).unlink(cr, uid, ids, context=context) cr.commit() + self.cancel(cr.dbname) self._poolJobs(cr.dbname) return res ir_cron() diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index f6412e85b75..888432e7968 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -82,6 +82,8 @@ class ir_model(osv.osv): return super(ir_model,self).write(cr, user, ids, vals, context) def create(self, cr, user, vals, context=None): + if context is None: + context = {} if context and context.get('manual',False): vals['state']='manual' res = super(ir_model,self).create(cr, user, vals, context) @@ -260,6 +262,8 @@ class ir_model_fields(osv.osv): if 'model_id' in vals: model_data = self.pool.get('ir.model').browse(cr, user, vals['model_id']) vals['model'] = model_data.model + if context is None: + context = {} if context and context.get('manual',False): vals['state'] = 'manual' res = super(ir_model_fields,self).create(cr, user, vals, context) @@ -267,7 +271,7 @@ class ir_model_fields(osv.osv): if not vals['name'].startswith('x_'): raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !")) - if 'relation' in vals and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]): + if vals.get('relation',False) and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]): raise except_orm(_('Error'), _("Model %s Does not Exist !" % vals['relation'])) if self.pool.get(vals['model']): @@ -286,7 +290,7 @@ class ir_model_access(osv.osv): _columns = { 'name': fields.char('Name', size=64, required=True), 'model_id': fields.many2one('ir.model', 'Object', required=True), - 'group_id': fields.many2one('res.groups', 'Group'), + 'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade'), 'perm_read': fields.boolean('Read Access'), 'perm_write': fields.boolean('Write Access'), 'perm_create': fields.boolean('Create Access'), @@ -453,7 +457,7 @@ class ir_model_data(osv.osv): def _get_id(self, cr, uid, module, xml_id): ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)]) if not ids: - raise Exception('No references to %s.%s' % (module, xml_id)) + raise ValueError('No references to %s.%s' % (module, xml_id)) # the sql constraints ensure us we have only one result return ids[0] diff --git a/bin/addons/base/ir/ir_translation.py b/bin/addons/base/ir/ir_translation.py index 73536ac339f..8e575b6b242 100644 --- a/bin/addons/base/ir/ir_translation.py +++ b/bin/addons/base/ir/ir_translation.py @@ -150,8 +150,8 @@ class ir_translation(osv.osv): if not context: context = {} ids = super(ir_translation, self).create(cursor, user, vals, context=context) - for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) + for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) return ids @@ -159,16 +159,16 @@ class ir_translation(osv.osv): if not context: context = {} result = super(ir_translation, self).write(cursor, user, ids, vals, context=context) - for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) + for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) return result def unlink(self, cursor, user, ids, context=None): if not context: context = {} - for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context): - self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US')) + for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context): + self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src']) self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']]) result = super(ir_translation, self).unlink(cursor, user, ids, context=context) return result diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index a33686e1eb9..87afaf821fe 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -31,6 +31,10 @@ def one_in(setA, setB): return True return False +def cond(C, X, Y): + if C: return X + return Y + class many2many_unique(fields.many2many): def set(self, cr, obj, id, name, values, user=None, context=None): if not values: @@ -63,19 +67,24 @@ class ir_ui_menu(osv.osv): # radical but this doesn't frequently happen self._cache = {} - def search(self, cr, uid, args, offset=0, limit=2000, order=None, - context=None, count=False): - if context is None: - context = {} - ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1)) + def search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False): + + super_offset = cond(uid == 1, offset, 0) + super_limit = cond(uid == 1, limit, None) + super_count = cond(uid == 1, count, False) + + ids = super(ir_ui_menu, self).search(cr, uid, args, super_offset, + super_limit, order, + context=context, count=super_count) + if uid == 1 or count: + return ids if not ids: if count: return 0 return [] - if count and ids: - return ids modelaccess = self.pool.get('ir.model.access') user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id']) @@ -123,6 +132,11 @@ class ir_ui_menu(osv.osv): result.append(menu.id) self._cache[key] = True + if offset: + result = result[long(offset):] + if limit: + result = result[:long(limit)] + if count: return len(result) return result diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index b04343e821f..a4b6923c672 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -200,6 +200,10 @@ class view_sc(osv.osv): 'resource': lambda *a: 'ir.ui.menu', 'user_id': lambda obj, cr, uid, context: uid, } + _sql_constraints = [ + ('shortcut_unique', 'unique(res_id, user_id)', 'Shortcut for this menu already exists!'), + ] + view_sc() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index 0a427427515..1f5f2393c19 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -146,11 +146,13 @@ class module(osv.osv): ], string='State', readonly=True), 'demo': fields.boolean('Demo data'), 'license': fields.selection([ - ('GPL-2', 'GPL-2'), + ('GPL-2', 'GPL Version 2'), ('GPL-2 or any later version', 'GPL-2 or later version'), - ('GPL-3', 'GPL-3'), + ('GPL-3', 'GPL Version 3'), ('GPL-3 or any later version', 'GPL-3 or later version'), - ('Other proprietary', 'Other proprietary') + ('AGPL-3', 'Affero GPL-3'), + ('Other OSI approved licence', 'Other OSI Approved Licence'), + ('Other proprietary', 'Other Proprietary') ], string='License', readonly=True), 'menus_by_module': fields.function(_get_views, method=True, string='Menus', type='text', multi="meta", store=True), 'reports_by_module': fields.function(_get_views, method=True, string='Reports', type='text', multi="meta", store=True), @@ -161,7 +163,7 @@ class module(osv.osv): _defaults = { 'state': lambda *a: 'uninstalled', 'demo': lambda *a: False, - 'license': lambda *a: 'GPL-2', + 'license': lambda *a: 'AGPL-3', } _order = 'name' diff --git a/bin/addons/base/res/partner/partner_demo.xml b/bin/addons/base/res/partner/partner_demo.xml index f5d70a2490d..915ab903a3e 100644 --- a/bin/addons/base/res/partner/partner_demo.xml +++ b/bin/addons/base/res/partner/partner_demo.xml @@ -275,6 +275,26 @@ <field name="type">default</field> <field name="partner_id" ref="res_partner_agrolait"/> </record> + <record id="res_partner_address_8delivery" model="res.partner.address"> + <field name="city">Wavre</field> + <field name="name">Paul Lelitre</field> + <field name="zip">5478</field> + <field name="title">M.</field> + <field model="res.country" name="country_id" search="[('name','=','Belgium')]"/> + <field name="street">71 rue de Chimay</field> + <field name="type">delivery</field> + <field name="partner_id" ref="res_partner_agrolait"/> + </record> + <record id="res_partner_address_8invoice" model="res.partner.address"> + <field name="city">Wavre</field> + <field name="name">Serge Lelitre</field> + <field name="zip">5478</field> + <field name="title">M.</field> + <field model="res.country" name="country_id" search="[('name','=','Belgium')]"/> + <field name="street">69 rue de Chimay</field> + <field name="type">invoice</field> + <field name="partner_id" ref="res_partner_agrolait"/> + </record> <record id="res_partner_address_9" model="res.partner.address"> <field name="city">Paris</field> <field name="name">Arthur Grosbonnet</field> diff --git a/bin/addons/base/res/res_lang.py b/bin/addons/base/res/res_lang.py index 8427b366703..66c1463eba9 100644 --- a/bin/addons/base/res/res_lang.py +++ b/bin/addons/base/res/res_lang.py @@ -60,11 +60,11 @@ class lang(osv.osv): ('name_uniq', 'unique (name)', 'The name of the language must be unique !'), ('code_uniq', 'unique (code)', 'The code of the language must be unique !'), ] - + @tools.cache(skiparg=3) def _lang_data_get(self, cr, uid, lang_id, monetary=False): conv = localeconv() - lang_obj=self.browse(cr,uid,lang_id) + lang_obj = self.browse(cr, uid, lang_id) thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep'] decimal_point = lang_obj.decimal_point grouping = lang_obj.grouping @@ -80,6 +80,7 @@ class lang(osv.osv): if not grouping: return (s, 0) + result = "" seps = 0 spaces = "" @@ -129,7 +130,7 @@ class lang(osv.osv): parts = formatted.split('.') if grouping: - parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep) + parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep) formatted = decimal_point.join(parts) while seps: diff --git a/bin/addons/base/res/res_security.xml b/bin/addons/base/res/res_security.xml index 8aae3a40ae7..9d935ca3248 100644 --- a/bin/addons/base/res/res_security.xml +++ b/bin/addons/base/res/res_security.xml @@ -6,9 +6,13 @@ <field name="name">Partner Manager</field> </record> - <record model="ir.ui.menu" id="menu_base_config"> - <field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/> - </record> + <record model="ir.ui.menu" id="menu_base_partner"> + <field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/> + </record> + + <record model="ir.ui.menu" id="menu_base_config"> + <field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/> + </record> </data> </openerp> diff --git a/bin/addons/base/res/res_user.py b/bin/addons/base/res/res_user.py index 72be48dcf0a..1e5f142ee0c 100644 --- a/bin/addons/base/res/res_user.py +++ b/bin/addons/base/res/res_user.py @@ -106,10 +106,11 @@ roles() def _lang_get(self, cr, uid, context={}): obj = self.pool.get('res.lang') - ids = obj.search(cr, uid, []) + ids = obj.search(cr, uid, [('translatable','=',True)]) res = obj.read(cr, uid, ids, ['code', 'name'], context) res = [(r['code'], r['name']) for r in res] return res + def _tz_get(self,cr,uid, context={}): return [(x, x) for x in pytz.all_timezones] @@ -245,6 +246,15 @@ class users(osv.osv): result = override_password(result) else: result = map(override_password, result) + + if isinstance(result, list): + for rec in result: + if not rec.get('action_id',True): + rec['action_id'] = (self._get_menu(cr, uid),'Menu') + else: + if not result.get('action_id',True): + result['action_id'] = (self._get_menu(cr, uid),'Menu') + return result @@ -467,6 +477,13 @@ class groups2(osv.osv): ##FIXME: Is there a reason to inherit this object ? _columns = { 'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'), } + + def unlink(self, cr, uid, ids, context=None): + for record in self.read(cr, uid, ids, ['users'], context=context): + if record['users']: + raise osv.except_osv(_('Warning !'), _('Make sure you have no users linked with the group(s)!')) + return super(groups2, self).unlink(cr, uid, ids, context=context) + groups2() class res_config_view(osv.osv_memory): diff --git a/bin/addons/base/security/ir.model.access.csv b/bin/addons/base/security/ir.model.access.csv index cf0f73a5cc9..e0f7f653ad6 100644 --- a/bin/addons/base/security/ir.model.access.csv +++ b/bin/addons/base/security/ir.model.access.csv @@ -19,7 +19,7 @@ "access_ir_module_module_group_user","ir_module_module group_user","model_ir_module_module","group_system",1,1,1,1 "access_ir_module_module_dependency_group_system","ir_module_module_dependency group_system","model_ir_module_module_dependency","group_system",1,1,1,1 "access_ir_property_group_user","ir_property group_user","model_ir_property",,1,0,0,0 -"access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,1 +"access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,0 "access_ir_report_custom_group_system","ir_report_custom group_system","model_ir_report_custom",,1,0,0,0 "access_ir_report_custom_fields_group_system","ir_report_custom_fields group_system","model_ir_report_custom_fields",,1,0,0,0 "access_ir_rule_group_user","ir_rule group_user","model_ir_rule",,1,0,0,0 diff --git a/bin/import_xml.rng b/bin/import_xml.rng index c852d421e17..e139238acfe 100644 --- a/bin/import_xml.rng +++ b/bin/import_xml.rng @@ -47,11 +47,12 @@ <rng:define name="assert"> <rng:element name="assert"> <rng:attribute name="model" /> - <rng:optional><rng:attribute name="search" /> </rng:optional> + <rng:optional><rng:attribute name="search" /></rng:optional> + <rng:optional><rng:attribute name="count" /></rng:optional> <rng:optional><rng:attribute name="string" /></rng:optional> <rng:optional><rng:attribute name="id" /></rng:optional> <rng:optional><rng:attribute name="severity" /></rng:optional> - <rng:oneOrMore> + <rng:zeroOrMore> <rng:element name="test"> <rng:attribute name="expr"/> <rng:choice> @@ -59,7 +60,7 @@ <rng:empty /> </rng:choice> </rng:element> - </rng:oneOrMore> + </rng:zeroOrMore> </rng:element> </rng:define> diff --git a/bin/netsvc.py b/bin/netsvc.py index 04e29f444e7..f0fe4687417 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -382,7 +382,8 @@ class OpenERPDispatcherException(Exception): class OpenERPDispatcher: def log(self, title, msg): - Logger().notifyChannel('%s' % title, LOG_DEBUG_RPC, pformat(msg)) + if tools.config['log_level'] == logging.DEBUG_RPC: + Logger().notifyChannel('%s' % title, LOG_DEBUG_RPC, pformat(msg)) def dispatch(self, service_name, method, params): try: diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 4e6e1aade6f..2ca19bbf418 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -280,8 +280,11 @@ class expression(object): c = context.copy() c['active_test'] = False res_ids = field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c) - right = map(lambda x: x[0], res_ids) - self.__exp[i] = (left, 'in', right) + if not res_ids: + self.__exp[i] = ('id','=',0) + else: + right = map(lambda x: x[0], res_ids) + self.__exp[i] = (left, 'in', right) else: # other field type # add the time part to datetime field when it's not there: diff --git a/bin/osv/fields.py b/bin/osv/fields.py index a38301ba184..dbddbb913e9 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -39,7 +39,7 @@ from psycopg2 import Binary import warnings import tools - +from tools.translate import _ def _symbol_set(symb): if symb == None or symb == False: @@ -748,7 +748,7 @@ class related(function): ids=[ids] objlst = obj.browse(cr, uid, ids) for data in objlst: - t_id=None + t_id = None t_data = data relation = obj._name for i in range(len(self.arg)): diff --git a/bin/osv/orm.py b/bin/osv/orm.py index d9fce0c765b..e3514df8ae0 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -49,6 +49,7 @@ import string import sys import time import traceback +import datetime import types import fields @@ -58,6 +59,7 @@ from tools.translate import _ import copy import sys +import operator try: from lxml import etree @@ -86,7 +88,6 @@ def last_day_of_current_month(): def intersect(la, lb): return filter(lambda x: x in lb, la) - class except_orm(Exception): def __init__(self, name, value): self.name = name @@ -276,7 +277,7 @@ class browse_record(object): else: new_data[n] = data[n] self._data[data['id']].update(new_data) - + if not name in self._data[self._id]: #how did this happen? self.logger.notifyChannel("browse_record", netsvc.LOG_ERROR, @@ -447,7 +448,7 @@ class orm_template(object): 'name': k, 'field_description': f.string.replace("'", " "), 'ttype': f._type, - 'relation': f._obj or 'NULL', + 'relation': f._obj or '', 'view_load': (f.view_load and 1) or 0, 'select_level': tools.ustr(f.select or 0), 'readonly':(f.readonly and 1) or 0, @@ -544,6 +545,8 @@ class orm_template(object): return browse_null() def __export_row(self, cr, uid, row, fields, context=None): + if context is None: + context = {} def check_type(field_type): if field_type == 'float': @@ -597,9 +600,9 @@ class orm_template(object): cols = selection_field(self._inherits) if cols and cols._type == 'selection': sel_list = cols.selection - if type(sel_list) == type([]): - r = [x[1] for x in sel_list if r==x[0]][0] - + if r and type(sel_list) == type([]): + r = [x[1] for x in sel_list if r==x[0]] + r = r and r[0] or False if not r: if f[i] in self._columns: r = check_type(self._columns[f[i]]._type) @@ -626,7 +629,7 @@ class orm_template(object): for rr in r : if isinstance(rr.name, browse_record): rr = rr.name - rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id]) + rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id], context=context) rr_name = rr_name and rr_name[0] and rr_name[0][1] or '' dt += tools.ustr(rr_name or '') + ',' data[fpos] = dt[:-1] @@ -639,7 +642,7 @@ class orm_template(object): i += 1 if i == len(f): if isinstance(r, browse_record): - r = self.pool.get(r._table_name).name_get(cr, uid, [r.id]) + r = self.pool.get(r._table_name).name_get(cr, uid, [r.id], context=context) r = r and r[0] and r[0][1] or '' data[fpos] = tools.ustr(r or '') return [data] + lines @@ -795,12 +798,12 @@ class orm_template(object): module, xml_id = line[i].rsplit('.', 1) else: module, xml_id = current_module, line[i] - id = ir_model_data_obj._get_id(cr, uid, module, xml_id) - - res_res_id = ir_model_data_obj.read(cr, uid, [id], - ['res_id']) - if res_res_id: - res_id = res_res_id[0]['res_id'] + record_id = ir_model_data_obj._get_id(cr, uid, module, xml_id) + ir_model_data = ir_model_data_obj.read(cr, uid, [record_id], ['res_id']) + if ir_model_data: + res_id = ir_model_data[0]['res_id'] + else: + raise ValueError('No references to %s.%s' % (module, xml_id)) row[field[-1][:-3]] = res_id or False continue if (len(field) == len(prefix)+1) and \ @@ -994,7 +997,8 @@ class orm_template(object): msg = _('Insertion Failed! ' + e[1]) return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' ) #Raising Uncaught exception - raise + return (-1, res, 'Line ' + str(counter) +' : ' + str(e), '' ) + for lang in translate: context2 = context.copy() context2['lang'] = lang @@ -1003,6 +1007,8 @@ class orm_template(object): data = pickle.load(file(config.get('import_partial'))) data[filename] = initial_size - len(datas) + original_value pickle.dump(data, file(config.get('import_partial'),'wb')) + if context.get('defer_parent_store_computation'): + self._parent_store_compute(cr) cr.commit() #except Exception, e: @@ -1016,6 +1022,8 @@ class orm_template(object): # # TODO: Send a request with the result and multi-thread ! # + if context.get('defer_parent_store_computation'): + self._parent_store_compute(cr) return (done, 0, 0, 0) def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): @@ -1108,8 +1116,7 @@ class orm_template(object): if getattr(self._columns[f], arg, None): res[f][arg] = getattr(self._columns[f], arg) - #TODO: optimize - res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US') + res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US', self._columns[f].string) if res_trans: res[f]['string'] = res_trans help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US') @@ -1724,7 +1731,8 @@ class orm_template(object): for lang in langs: for field in vals: if field in self._columns: - self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field]) + src = self._columns[field].string + self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field], src) for table in self._inherits: cols = intersect(self._inherit_fields.keys(), vals) if cols: @@ -2137,6 +2145,8 @@ class orm(orm_template): return (tables, where_clause) def _parent_store_compute(self, cr): + if not self._parent_store: + return logger = netsvc.Logger() logger.notifyChannel('orm', netsvc.LOG_INFO, 'Computing parent left and right for table %s...' % (self._table, )) def browse_rec(root, pos=0): @@ -2225,8 +2235,8 @@ class orm(orm_template): logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)' % (self._table, )) if 'parent_right' not in self._columns: logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)' % (self._table, )) - if self._columns[self._parent_name].ondelete<>'cascade': - logger.notifyChannel('orm', netsvc.LOG_ERROR, "the columns %s on object must be set as ondelete='cascasde'" % (self._name, self._parent_name)) + if self._columns[self._parent_name].ondelete != 'cascade': + logger.notifyChannel('orm', netsvc.LOG_ERROR, "The column %s on object %s must be set as ondelete='cascade'" % (self._parent_name, self._name)) cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,)) cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,)) cr.commit() @@ -2472,6 +2482,8 @@ class orm(orm_template): cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (self._table,)) create = not bool(cr.fetchone()) + cr.commit() # start a new transaction + for (key, con, _) in self._sql_constraints: conname = '%s_%s' % (self._table, key) cr.execute("SELECT conname FROM pg_constraint where conname=%s", (conname,)) @@ -2481,6 +2493,7 @@ class orm(orm_template): cr.commit() except: logger.notifyChannel('orm', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,)) + cr.rollback() if create: if hasattr(self, "_sql"): @@ -2491,6 +2504,7 @@ class orm(orm_template): cr.commit() if store_compute: self._parent_store_compute(cr) + cr.commit() return todo_end def __init__(self, cr): @@ -2646,11 +2660,16 @@ class orm(orm_template): for i in range(len(field_value)): field_value2.append({}) for field2 in field_value[i]: - if obj._columns[field2]._type in ('many2one', 'one2one'): + if field2 in obj._columns.keys() and obj._columns[field2]._type in ('many2one', 'one2one'): obj2 = self.pool.get(obj._columns[field2]._obj) if not obj2.search(cr, uid, [('id', '=', field_value[i][field2])]): continue + elif field2 in obj._inherit_fields.keys() and obj._inherit_fields[field2][2]._type in ('many2one', 'one2one'): + obj2 = self.pool.get(obj._inherit_fields[field2][2]._obj) + if not obj2.search(cr, uid, + [('id', '=', field_value[i][field2])]): + continue # TODO add test for many2many and one2many field_value2[i][field2] = field_value[i][field2] field_value = field_value2 @@ -3107,6 +3126,24 @@ class orm(orm_template): self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context) + # No direct update of parent_left/right + vals.pop('parent_left', None) + vals.pop('parent_right', None) + + parents_changed = [] + if self._parent_store and (self._parent_name in vals): + # The parent_left/right computation may take up to + # 5 seconds. No need to recompute the values if the + # parent is the same. Get the current value of the parent + base_query = 'SELECT id FROM %s WHERE id IN %%s AND %s' % \ + (self._table, self._parent_name) + params = (tuple(ids),) + parent_val = vals[self._parent_name] + if parent_val: + cr.execute(base_query + " != %s", params + (parent_val,)) + else: + cr.execute(base_query + " IS NULL", params) + parents_changed = map(operator.itemgetter(0), cr.fetchall()) upd0 = [] upd1 = [] @@ -3196,41 +3233,48 @@ class orm(orm_template): self.pool.get(table).write(cr, user, nids, v, context) self._validate(cr, user, ids, context) -# TODO: use _order to set dest at the right position and not first node of parent - if self._parent_store and (self._parent_name in vals): + + # TODO: use _order to set dest at the right position and not first node of parent + # We can't defer parent_store computation because the stored function + # fields that are computer may refer (directly or indirectly) to + # parent_left/right (via a child_of domain) + if parents_changed: if self.pool._init: self.pool._init_parent[self._name]=True else: - for id in ids: + order = self._parent_order or self._order + parent_val = vals[self._parent_name] + if parent_val: + clause, params = '%s=%%s' % (self._parent_name,), (parent_val,) + else: + clause, params = '%s IS NULL' % (self._parent_name,), () + cr.execute('SELECT parent_right, id FROM %s WHERE %s ORDER BY %s' % (self._table, clause, order), params) + parents = cr.fetchall() + + for id in parents_changed: + cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,)) + pleft, pright = cr.fetchone() + distance = pright - pleft + 1 + # Find Position of the element - if vals[self._parent_name]: - cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (vals[self._parent_name],)) - else: - cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+' is null order by '+(self._parent_order or self._order)) - result_p = cr.fetchall() position = None - for (pleft,pright,pid) in result_p: - if pid == id: + for (parent_pright, parent_id) in parents: + if parent_id == id: break - position = pright+1 + position = parent_pright+1 - # It's the first node of the parent: position = parent_left+1 + # It's the first node of the parent if not position: - if not vals[self._parent_name]: + if not parent_val: position = 1 else: - cr.execute('select parent_left from '+self._table+' where id=%s', (vals[self._parent_name],)) + cr.execute('select parent_left from '+self._table+' where id=%s', (parent_val,)) position = cr.fetchone()[0]+1 - # We have the new position ! - cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (id,)) - pleft,pright = cr.fetchone() - distance = pright - pleft + 1 - - if position>pleft and position<=pright: + if pleft < position <= pright: raise except_orm(_('UserError'), _('Recursivity Detected.')) - if pleft<position: + if pleft < position: cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position)) cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position)) cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft,position-pleft, pleft, pright)) @@ -3411,7 +3455,7 @@ class orm(orm_template): self.check_access_rule(cr, user, [id_new], 'create', context=context) upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) - if self._parent_store: + if self._parent_store and not context.get('defer_parent_store_computation'): if self.pool._init: self.pool._init_parent[self._name]=True else: @@ -3639,8 +3683,9 @@ class orm(orm_template): * '&' (default), '|', '!' """ - if not context: + if context is None: context = {} + self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context) # compute the where, order by, limit and offset clauses (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context) dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context) @@ -3750,9 +3795,9 @@ class orm(orm_template): :return: dictionary containing all the field values """ - if not context: + if context is None: context = {} - if not default: + if default is None: default = {} if 'state' not in default: if 'state' in self._defaults: @@ -3761,7 +3806,11 @@ class orm(orm_template): else: default['state'] = self._defaults['state'] - data = self.read(cr, uid, [id], context=context)[0] + context_wo_lang = context + if 'lang' in context: + del context_wo_lang['lang'] + data = self.read(cr, uid, [id], context=context_wo_lang)[0] + fields = self.fields_get(cr, uid, context=context) trans_data=[] for f in fields: @@ -3796,17 +3845,14 @@ class orm(orm_template): trans_obj = self.pool.get('ir.translation') #TODO: optimize translations - trans_name='' for f in fields: - trans_flag=True + trans_name = '' if f in self._columns and self._columns[f].translate: - trans_name=self._name+","+f + trans_name = self._name+","+f elif f in self._inherit_fields and self._inherit_fields[f][2].translate: - trans_name=self._inherit_fields[f][0]+","+f - else: - trans_flag=False + trans_name = self._inherit_fields[f][0] + "," + f - if trans_flag: + if trans_name: trans_ids = trans_obj.search(cr, uid, [ ('name', '=', trans_name), ('res_id','=',data['id']) diff --git a/bin/osv/osv.py b/bin/osv/osv.py index a528d418089..08f62e50230 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -253,7 +253,8 @@ class osv(osv_base, orm.orm): for c in cls.__dict__.get(s, []): exist = False for c2 in range(len(new)): - if new[c2][2]==c[2]: + #For _constraints, we should check field and methods as well + if new[c2][2]==c[2] and new[c2][0]==c[0]: new[c2] = c exist = True break diff --git a/bin/release.py b/bin/release.py index f5fde69f1a9..817994e7d46 100644 --- a/bin/release.py +++ b/bin/release.py @@ -21,8 +21,8 @@ ############################################################################## name = 'openerp-server' -version = '5.2dev' -major_version = '5.2' +version = '6.0dev' +major_version = '6.0' description = 'OpenERP Server' long_desc = '''\ OpenERP is a complete ERP and CRM. The main features are accounting (analytic diff --git a/bin/report/custom.py b/bin/report/custom.py index e476b296056..d4309b31ab9 100644 --- a/bin/report/custom.py +++ b/bin/report/custom.py @@ -36,6 +36,7 @@ from pychart import * import misc import cStringIO from lxml import etree +from tools.translate import _ class external_pdf(render.render): def __init__(self, pdf): diff --git a/bin/report/preprocess.py b/bin/report/preprocess.py index 35234bc7ef1..c84ce66b472 100644 --- a/bin/report/preprocess.py +++ b/bin/report/preprocess.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # @@ -15,7 +15,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## @@ -35,7 +35,7 @@ class report(object): for node in root_node: if node.tag == etree.Comment: continue - if node.text: + if node.text or node.tail: def _sub3(txt): n = node while n.tag != txt.group(2): @@ -71,9 +71,9 @@ class report(object): n = n.getparent() n.set('rml_loop', txt.group(2)) return '[['+txt.group(1)+"''"+txt.group(4)+']]' - t = _regex1.sub(_sub1, node.text) + t = _regex1.sub(_sub1, node.text or node.tail) if t == " ": - t = _regex11.sub(_sub1, node.text) + t = _regex11.sub(_sub1, node.text or node.tail) t = _regex3.sub(_sub3, t) node.text = _regex2.sub(_sub2, t) self.preprocess_rml(node,type) diff --git a/bin/report/render/rml2pdf/trml2pdf.py b/bin/report/render/rml2pdf/trml2pdf.py index fa1595e13a4..a5912cd7a13 100644 --- a/bin/report/render/rml2pdf/trml2pdf.py +++ b/bin/report/render/rml2pdf/trml2pdf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # @@ -15,7 +15,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## @@ -52,13 +52,20 @@ class _rml_styles(object,): def __init__(self, nodes, localcontext): self.localcontext = localcontext self.styles = {} + self.styles_obj = {} self.names = {} self.table_styles = {} + self.default_style = reportlab.lib.styles.getSampleStyleSheet() + for node in nodes: for style in node.findall('blockTableStyle'): self.table_styles[style.get('id')] = self._table_style_get(style) for style in node.findall('paraStyle'): - self.styles[style.get('name')] = self._para_style_update(style) + sname = style.get('name') + self.styles[sname] = self._para_style_update(style) + + self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname]) + for variable in node.findall('initialize'): for name in variable.findall('name'): self.names[ name.get('id')] = name.get('value') @@ -126,17 +133,19 @@ class _rml_styles(object,): def para_style_get(self, node): style = False - if node.get('style'): - if node.get('style') in self.styles: - styles = reportlab.lib.styles.getSampleStyleSheet() - sname = node.get('style') - style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname]) + sname = node.get('style') + if sname: + if sname in self.styles_obj: + style = self.styles_obj[sname] else: sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.get('style'),) ) if not style: - styles = reportlab.lib.styles.getSampleStyleSheet() - style = styles['Normal'] - style.__dict__.update(self._para_style_update(node)) + style = self.default_style['Normal'] + para_update = self._para_style_update(node) + if para_update: + # update style only is necessary + style = copy.deepcopy(style) + style.__dict__.update(para_update) return style class _rml_doc(object): @@ -167,7 +176,7 @@ class _rml_doc(object): from reportlab.lib.fonts import addMapping from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont - + pdfmetrics.registerFont(TTFont(fontname, filename )) if (mode == 'all'): addMapping(face, 0, 0, fontname) #normal @@ -417,11 +426,11 @@ class _rml_canvas(object): self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) def setFont(self, node): - from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase import pdfmetrics fname = node.get('name') - #TODO : other fonts should be supported + #TODO : other fonts should be supported if fname not in pdfmetrics.standardFonts: - fname = self.canvas._fontname + fname = self.canvas._fontname return self.canvas.setFont(fname, utils.unit_get(node.get('size'))) def render(self, node): @@ -565,7 +574,7 @@ class _rml_flowable(object): def _illustration(self, node): class Illustration(platypus.flowables.Flowable): def __init__(self, node, localcontext, styles, self2): - self.localcontext = localcontext + self.localcontext = localcontext.copy() self.node = node self.styles = styles self.width = utils.unit_get(node.get('width')) diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index a85bac84f5c..9d2fb97362c 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -441,8 +441,8 @@ class report_sxw(report_rml, preprocess.report): rml_parser.set_context(objs, data, ids, report_xml.report_type) processed_rml = etree.XML(rml) if report_xml.header: - rml_parser._add_header(processed_rml) - processed_rml = self.preprocess_rml(processed_rml,report_xml.report_type) + rml_parser._add_header(processed_rml, self.header) + processed_rml = self.preprocess_rml(processed_rml,report_xml.report_type) if rml_parser.logo: logo = base64.decodestring(rml_parser.logo) create_doc = self.generators[report_xml.report_type] diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 308a7aa18a9..7719caa063f 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -283,8 +283,8 @@ class db(netsvc.ExportService): ## Not True: in fact, check if connection to database is possible. The database may exists return bool(sql_db.db_connect(db_name)) - def exp_list(self): - if not tools.config['list_db']: + def exp_list(self, document=False): + if not tools.config['list_db'] and not document: raise Exception('AccessDenied') db = sql_db.db_connect('template1') diff --git a/bin/tools/config.py b/bin/tools/config.py index c18a7422e2c..a629e97fc11 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -123,7 +123,6 @@ class configmanager(object): parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(), help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),)) parser.add_option('--price_accuracy', dest='price_accuracy', default='2', help='deprecated since v6.0, replaced by module decimal_precision') - parser.add_option('--no-database-list', action="store_false", dest='list_db', default=True, help="disable the ability to return the list of databases") if self.has_ssl: group = optparse.OptionGroup(parser, "SSL Configuration") @@ -136,7 +135,7 @@ class configmanager(object): default="server.pkey", help="specify the private key file for the SSL connection") parser.add_option_group(group) - + # Testing Group group = optparse.OptionGroup(parser, "Testing Configuration") group.add_option("--test-disable", action="store_true", dest="test_disable", @@ -144,7 +143,7 @@ class configmanager(object): group.add_option("--test-commit", action="store_true", dest="test_commit", default=False, help="Commit database changes performed by tests.") parser.add_option_group(group) - + # Logging Group group = optparse.OptionGroup(parser, "Logging Configuration") group.add_option("--logfile", dest="logfile", help="file where the server log will be stored") @@ -161,8 +160,7 @@ class configmanager(object): group.add_option('--email-from', dest='email_from', default='', help='specify the SMTP email address for sending email') group.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email') group.add_option('--smtp-port', dest='smtp_port', default='25', help='specify the SMTP port', type="int") - if self.has_ssl: - group.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not') + group.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not') group.add_option('--smtp-user', dest='smtp_user', default='', help='specify the SMTP username for sending email') group.add_option('--smtp-password', dest='smtp_password', default='', help='specify the SMTP password for sending email') parser.add_option_group(group) @@ -199,6 +197,13 @@ class configmanager(object): action="callback", callback=self._check_addons_path, nargs=1, type="string") parser.add_option_group(group) + security = optparse.OptionGroup(parser, 'Security-related options') + security.add_option('--no-database-list', action="store_false", dest='list_db', default=True, help="disable the ability to return the list of databases") + security.add_option('--enable-code-actions', action='store_true', + dest='server_actions_allow_code', default=False, + help='Enables server actions of state "code". Warning, this is a security risk.') + parser.add_option_group(security) + def parse_config(self): (opt, args) = self.parser.parse_args() @@ -244,13 +249,13 @@ class configmanager(object): self.options['pidfile'] = False keys = ['interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host', - 'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout', + 'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout','smtp_ssl', 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy', 'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path', 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',] if self.has_ssl: - keys.extend(['smtp_ssl', 'secure_cert_file', 'secure_pkey_file']) + keys.extend(['secure_cert_file', 'secure_pkey_file']) keys.append('secure') for arg in keys: @@ -258,7 +263,8 @@ class configmanager(object): self.options[arg] = getattr(opt, arg) keys = ['language', 'translate_out', 'translate_in', 'debug_mode', - 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog', 'list_db'] + 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog', + 'list_db', 'server_actions_allow_code'] if self.has_ssl and not self.options['secure']: keys.append('secure') diff --git a/bin/tools/convert.py b/bin/tools/convert.py index 1188f6deaa8..a7e002e1a61 100644 --- a/bin/tools/convert.py +++ b/bin/tools/convert.py @@ -26,11 +26,9 @@ import os.path import pickle import re import sys -import time from datetime import datetime from lxml import etree - import ir import misc import netsvc @@ -79,6 +77,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None): return f_val a_eval = node.get('eval','') if a_eval: + import time idref2 = idref.copy() idref2['time'] = time idref2['DateTime'] = datetime @@ -253,6 +252,14 @@ form: module.record_id""" % (xml_id,) self.pool.get(d_model).unlink(cr, self.uid, ids) self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids) + def _remove_ir_values(self, cr, name, value, model): + ir_value_ids = self.pool.get('ir.values').search(cr, self.uid, [('name','=',name),('value','=',value),('model','=',model)]) + if ir_value_ids: + self.pool.get('ir.values').unlink(cr, self.uid, ir_value_ids) + self.pool.get('ir.model.data')._unlink(cr, self.uid, 'ir.values', ir_value_ids) + + return True + def _tag_report(self, cr, rec, data_node=None): res = {} for dest,f in (('name','string'),('model','model'),('report_name','name')): @@ -262,16 +269,17 @@ form: module.record_id""" % (xml_id,) if rec.get(field): res[dest] = rec.get(field).encode('utf8') if rec.get('auto'): - res['auto'] = eval(rec.get('auto')) + res['auto'] = eval(rec.get('auto','False')) if rec.get('sxw'): sxw_content = misc.file_open(rec.get('sxw')).read() res['report_sxw_content'] = sxw_content if rec.get('header'): - res['header'] = eval(rec.get('header')) + res['header'] = eval(rec.get('header','False')) if rec.get('report_type'): res['report_type'] = rec.get('report_type') - res['multi'] = rec.get('multi') and eval(rec.get('multi')) + res['multi'] = rec.get('multi') and eval(rec.get('multi','False')) + xml_id = rec.get('id','').encode('utf8') self._test_xml_id(xml_id) @@ -291,12 +299,16 @@ form: module.record_id""" % (xml_id,) id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode) self.idref[xml_id] = int(id) - if not rec.get('menu') or eval(rec.get('menu','')): + if not rec.get('menu') or eval(rec.get('menu','False')): keyword = str(rec.get('keyword', 'client_print_multi')) keys = [('action',keyword),('res_model',res['model'])] value = 'ir.actions.report.xml,'+str(id) replace = rec.get('replace', True) self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id) + elif self.mode=='update' and eval(rec.get('menu','False'))==False: + # Special check for report having attribute menu=False on update + value = 'ir.actions.report.xml,'+str(id) + self._remove_ir_values(cr, res['name'], value, res['model']) return False def _tag_function(self, cr, rec, data_node=None): @@ -313,7 +325,7 @@ form: module.record_id""" % (xml_id,) name = rec.get("name",'').encode('utf8') xml_id = rec.get('id','').encode('utf8') self._test_xml_id(xml_id) - multi = rec.get('multi','') and eval(rec.get('multi','')) + multi = rec.get('multi','') and eval(rec.get('multi','False')) res = {'name': string, 'wiz_name': name, 'multi': multi, 'model': model} if rec.get('groups'): @@ -332,12 +344,16 @@ form: module.record_id""" % (xml_id,) id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode) self.idref[xml_id] = int(id) # ir_set - if (not rec.get('menu') or eval(rec.get('menu',''))) and id: + if (not rec.get('menu') or eval(rec.get('menu','False'))) and id: keyword = str(rec.get('keyword','') or 'client_action_multi') keys = [('action',keyword),('res_model',model)] value = 'ir.actions.wizard,'+str(id) replace = rec.get("replace",'') or True self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id) + elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False): + # Special check for wizard having attribute menu=False on update + value = 'ir.actions.wizard,'+str(id) + self._remove_ir_values(cr, string, value, model) def _tag_url(self, cr, rec, data_node=None): url = rec.get("string",'').encode('utf8') @@ -351,12 +367,16 @@ form: module.record_id""" % (xml_id,) id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.url", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode) self.idref[xml_id] = int(id) # ir_set - if (not rec.get('menu') or eval(rec.get('menu',''))) and id: + if (not rec.get('menu') or eval(rec.get('menu','False'))) and id: keyword = str(rec.get('keyword','') or 'client_action_multi') keys = [('action',keyword)] value = 'ir.actions.url,'+str(id) replace = rec.get("replace",'') or True self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=xml_id) + elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False): + # Special check for URL having attribute menu=False on update + value = 'ir.actions.url,'+str(id) + self._remove_ir_values(cr, url, value, "ir.actions.url") def _tag_act_window(self, cr, rec, data_node=None): name = rec.get('name','').encode('utf-8') @@ -379,11 +399,12 @@ form: module.record_id""" % (xml_id,) uid = self.uid # def ref() added because , if context has ref('id') eval wil use this ref - active_id=str("active_id") # for further reference in client/bin/tools/__init__.py + active_id = str("active_id") # for further reference in client/bin/tools/__init__.py def ref(str_id): return self.id_get(cr, None, str_id) context = eval(context) +# domain = eval(domain) # XXX need to test this line -> uid, active_id, active_ids, ... res = { 'name': name, diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 07e0fc38f8e..e60213ca740 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -820,6 +820,22 @@ class cache(object): def to_xml(s): return s.replace('&','&').replace('<','<').replace('>','>') +def get_encodings(): + yield 'utf8' + from locale import getpreferredencoding + prefenc = getpreferredencoding() + if prefenc: + yield prefenc + + prefenc = { + 'latin1': 'latin9', + 'iso-8859-1': 'iso8859-15', + 'cp1252': '1252', + }.get(prefenc.lower()) + if prefenc: + yield prefenc + + def ustr(value): """This method is similar to the builtin `str` method, except it will return Unicode string. @@ -829,29 +845,25 @@ def ustr(value): @rtype: unicode @return: unicode string """ + orig = value + if isinstance(value, Exception): + return exception_to_unicode(value) if isinstance(value, unicode): return value - if hasattr(value, '__unicode__'): + try: return unicode(value) - - if not isinstance(value, str): - value = str(value) - - try: # first try utf-8 - return unicode(value, 'utf-8') except: pass - try: # then extened iso-8858 - return unicode(value, 'iso-8859-15') - except: - pass + for ln in get_encodings(): + try: + return unicode(value, ln) + except: + pass + raise UnicodeError('unable de to convert %r' % (orig,)) - # else use default system locale - from locale import getlocale - return unicode(value, getlocale()[1]) def exception_to_unicode(e): if (sys.version_info[:2] < (2,6)) and hasattr(e, 'message'): @@ -914,7 +926,7 @@ def get_languages(): 'fr_BE': u'French (BE) / Français (BE)', 'fr_CH': u'French (CH) / Français (CH)', 'fr_FR': u'French / Français', - 'gl_ES': u'Galician / Spain', + 'gl_ES': u'Galician / Galego', 'gu_IN': u'Gujarati / India', 'hi_IN': u'Hindi / India', 'hr_HR': u'Croatian / hrvatski jezik', @@ -939,8 +951,8 @@ def get_languages(): 'ro_RO': u'Romanian / limba română', 'ru_RU': u'Russian / русский язык', 'si_LK': u'Sinhalese / Sri Lanka', - 'sk_SK': u'Slovak / Slovakia', - 'sl_SL': u'Slovenian / slovenščina', + 'sl_SI': u'Slovenian / slovenščina', + 'sk_SK': u'Slovak / Slovenský jazyk', 'sq_AL': u'Albanian / Shqipëri', 'sr_RS': u'Serbian / Serbia', 'sv_SE': u'Swedish / svenska', diff --git a/bin/tools/translate.py b/bin/tools/translate.py index 05f7f95e9b4..9277bf6924b 100644 --- a/bin/tools/translate.py +++ b/bin/tools/translate.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # @@ -15,7 +15,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## @@ -596,23 +596,30 @@ def trans_generate(lang, modules, dbname=None): push_translation(module, 'model', name, xml_name, encode(trad)) # parse source code for _() calls - def get_module_from_path(path,mod_paths=None): - if not mod_paths: - # First, construct a list of possible paths - def_path = os.path.abspath(os.path.join(tools.config['root_path'], 'addons')) # default addons path (base) - ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(',')) - mod_paths=[def_path] - for adp in ad_paths: - mod_paths.append(adp) - if not adp.startswith('/'): - mod_paths.append(os.path.join(def_path,adp)) - elif adp.startswith(def_path): - mod_paths.append(adp[len(def_path)+1:]) - - for mp in mod_paths: - if path.startswith(mp) and (os.path.dirname(path) != mp): - path = path[len(mp)+1:] - return path.split(os.path.sep)[0] + def get_module_from_path(path, mod_paths=None): +# if not mod_paths: +## First, construct a list of possible paths +# def_path = os.path.abspath(os.path.join(tools.config['root_path'], 'addons')) # default addons path (base) +# ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(',')) +# mod_paths=[def_path] +# for adp in ad_paths: +# mod_paths.append(adp) +# if not adp.startswith('/'): +# mod_paths.append(os.path.join(def_path,adp)) +# elif adp.startswith(def_path): +# mod_paths.append(adp[len(def_path)+1:]) +# for mp in mod_paths: +# if path.startswith(mp) and (os.path.dirname(path) != mp): +# path = path[len(mp)+1:] +# return path.split(os.path.sep)[0] + path_dir = os.path.dirname(path[1:]) + if path_dir: + if os.path.exists(os.path.join(tools.config['addons_path'],path[1:])): + return path.split(os.path.sep)[1] + else: + root_addons = os.path.join(tools.config['root_path'], 'addons') + if os.path.exists(os.path.join(root_addons,path[1:])): + return path.split(os.path.sep)[1] return 'base' # files that are not in a module are considered as being in 'base' module modobj = pool.get('ir.module.module') @@ -699,6 +706,11 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name= if not lang_name: lang_name = tools.get_languages().get(lang, lang) + def fix_xa0(s): + if s == '\xa0': + return '\xc2\xa0' + return s + lang_info = { 'code': lang, 'iso_code': iso_lang, @@ -706,9 +718,10 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name= 'translatable': 1, 'date_format' : str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')), 'time_format' : str(locale.nl_langinfo(locale.T_FMT)), - 'decimal_point' : str(locale.localeconv()['decimal_point']).replace('\xa0', '\xc2\xa0'), - 'thousands_sep' : str(locale.localeconv()['thousands_sep']).replace('\xa0', '\xc2\xa0'), + 'decimal_point' : fix_xa0(str(locale.localeconv()['decimal_point'])), + 'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])), } + try: lang_obj.create(cr, uid, lang_info) finally: @@ -767,7 +780,7 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name= # the same source obj = pool.get(model) if obj: - if not field in obj._columns: + if field not in obj.fields_get_keys(cr, uid): continue ids = obj.search(cr, uid, [(field, '=', dic['src'])]) |
