[MERGE] bugfixes: default language for partners, password change, validation of custom selection fields, ...

See the bug links

lp bug: https://launchpad.net/bugs/670264 fixed
lp bug: https://launchpad.net/bugs/692891 fixed
lp bug: https://launchpad.net/bugs/695678 fixed
lp bug: https://launchpad.net/bugs/631547 fixed
lp bug: https://launchpad.net/bugs/632927 fixed

bzr revid: odo@openerp.com-20110106173828-kdv1gtdvbws1ceb5
This commit is contained in:
Olivier Dony 2011-01-06 18:38:28 +01:00
commit 7d3375f49f
10 changed files with 163 additions and 146 deletions

View File

@ -28,6 +28,8 @@
<field name="translatable">True</field> <field name="translatable">True</field>
</record> </record>
<function name="install_lang" model="res.lang"/>
<record id="ad" model="res.country"> <record id="ad" model="res.country">
<field name="name">Andorra, Principality of</field> <field name="name">Andorra, Principality of</field>
<field name="code">ad</field> <field name="code">ad</field>

View File

@ -70,34 +70,6 @@
====================== ======================
--> -->
<record id="view_change_user_password_form" model="ir.ui.view">
<field name="name">change.user.password</field>
<field name="model">change.user.password</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Change Password">
<field name="current_password" password="True" readonly="0" colspan="4"/>
<field name="new_password" password="True" readonly="0" colspan="4"/>
<field name="confirm_password" password="True" readonly="0" colspan="4"/>
<label colspan="1" string=""/>
<label colspan="3" string="You must logout and login again after changing your password."/>
<separator colspan="4" />
<label align="0.0" colspan="2" string=""/>
<button colspan="1" icon="gtk-cancel" special="cancel" string="Cancel"/>
<button colspan="1" icon="gtk-ok" name="change_password" string="Change" type="object"/>
</form>
</field>
</record>
<record id="action_view_change_password_form" model="ir.actions.act_window">
<field name="name">Change Password</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">change.user.password</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="view_users_form_simple_modif" model="ir.ui.view"> <record id="view_users_form_simple_modif" model="ir.ui.view">
<field name="name">res.users.form.modif</field> <field name="name">res.users.form.modif</field>
<field name="model">res.users</field> <field name="model">res.users</field>
@ -119,8 +91,6 @@
<field name="context_lang" completion="1" readonly="0"/> <field name="context_lang" completion="1" readonly="0"/>
<field name="context_tz" completion="1" readonly="0"/> <field name="context_tz" completion="1" readonly="0"/>
<field name="menu_tips" colspan="2" readonly="0"/> <field name="menu_tips" colspan="2" readonly="0"/>
<label string="" colspan="1"/>
<button name="%(action_view_change_password_form)d" string="Change Password" type="action" icon="gtk-execute"/>
<separator string="Email &amp; Signature" colspan="4"/> <separator string="Email &amp; Signature" colspan="4"/>
<group colspan="4"><field name="user_email" widget="email" readonly="0"/></group> <group colspan="4"><field name="user_email" widget="email" readonly="0"/></group>
<field colspan="4" name="signature" readonly="0" nolabel="1"/> <field colspan="4" name="signature" readonly="0" nolabel="1"/>
@ -139,7 +109,7 @@
<field name="name" select="1"/> <field name="name" select="1"/>
<field name="active"/> <field name="active"/>
<field name="login" select="1"/> <field name="login" select="1"/>
<field name="password" password="True"/> <field name="new_password" password="True"/>
<newline/> <newline/>
<notebook colspan="4"> <notebook colspan="4">
<page string="User"> <page string="User">

View File

@ -19,14 +19,16 @@
# #
############################################################################## ##############################################################################
import logging import logging
import re
import time
from operator import itemgetter from operator import itemgetter
from osv import fields,osv from osv import fields,osv
import ir, re import ir
import netsvc import netsvc
from osv.orm import except_orm, browse_record from osv.orm import except_orm, browse_record
import time
import tools import tools
from tools.safe_eval import safe_eval as eval
from tools import config from tools import config
from tools.translate import _ from tools.translate import _
import pooler import pooler
@ -152,21 +154,29 @@ class ir_model_fields(osv.osv):
_description = "Fields" _description = "Fields"
_columns = { _columns = {
'name': fields.char('Name', required=True, size=64, select=1), 'name': fields.char('Name', required=True, size=64, select=1),
'model': fields.char('Object Name', size=64, required=True, select=1), 'model': fields.char('Object Name', size=64, required=True, select=1,
'relation': fields.char('Object Relation', size=64), help="The technical name of the model this field belongs to"),
'relation_field': fields.char('Relation Field', size=64), 'relation': fields.char('Object Relation', size=64,
'model_id': fields.many2one('ir.model', 'Object ID', required=True, select=True, ondelete='cascade'), help="For relationship fields, the technical name of the target model"),
'relation_field': fields.char('Relation Field', size=64,
help="For one2many fields, the field on the target model that implement the opposite many2one relationship"),
'model_id': fields.many2one('ir.model', 'Model', required=True, select=True, ondelete='cascade',
help="The model this field belongs to"),
'field_description': fields.char('Field Label', required=True, size=256), 'field_description': fields.char('Field Label', required=True, size=256),
'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True), 'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True),
'selection': fields.char('Field Selection',size=128), 'selection': fields.char('Selection Options',size=128, help="List of options for a selection field, "
"specified as a Python expression defining a list of (key, label) pairs. "
"For example: [('blue','Blue'),('yellow','Yellow')]"),
'required': fields.boolean('Required'), 'required': fields.boolean('Required'),
'readonly': fields.boolean('Readonly'), 'readonly': fields.boolean('Readonly'),
'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True), 'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search (deprecated)')],'Searchable', required=True),
'translate': fields.boolean('Translate'), 'translate': fields.boolean('Translate', help="Whether values for this field can be translated (enables the translation mechanism for that field)"),
'size': fields.integer('Size'), 'size': fields.integer('Size'),
'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Type', required=True, readonly=True, select=1), 'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Type', required=True, readonly=True, select=1),
'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'), 'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'),
'domain': fields.char('Domain', size=256), 'domain': fields.char('Domain', size=256, help="The optional domain to restrict possible values for relationship fields, "
"specified as a Python expression defining a list of triplets. "
"For example: [('color','=','red')]"),
'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'), 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
'view_load': fields.boolean('View Auto-Load'), 'view_load': fields.boolean('View Auto-Load'),
'selectable': fields.boolean('Selectable'), 'selectable': fields.boolean('Selectable'),
@ -174,7 +184,7 @@ class ir_model_fields(osv.osv):
_rec_name='field_description' _rec_name='field_description'
_defaults = { _defaults = {
'view_load': lambda *a: 0, 'view_load': lambda *a: 0,
'selection': lambda *a: "[]", 'selection': lambda *a: "[('key','label')]",
'domain': lambda *a: "[]", 'domain': lambda *a: "[]",
'name': lambda *a: 'x_', 'name': lambda *a: 'x_',
'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base', 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
@ -185,6 +195,25 @@ class ir_model_fields(osv.osv):
'selectable': lambda *a: 1, 'selectable': lambda *a: 1,
} }
_order = "name" _order = "name"
def _check_selection(self, cr, uid, ids, context=None):
selection_field = self.browse(cr, uid, ids[0], context=context)
try:
selection_list = eval(selection_field.selection)
except Exception:
logging.getLogger('ir.model').error('Invalid selection list definition for fields.selection %s', selection_field.name , exc_info=True)
return False
if not (isinstance(selection_list, list) and selection_list):
return False
for selection_item in selection_list:
if not (isinstance(selection_item, (tuple,list)) and len(selection_item) == 2):
return False
return True
_constraints = [
(_check_selection, "Wrong list of values specified for a field of type selection, it should be written as [('key','value')]", ['selection'])
]
def _size_gt_zero_msg(self, cr, user, ids, context=None): def _size_gt_zero_msg(self, cr, user, ids, context=None):
return _('Size of the field can never be less than 1 !') return _('Size of the field can never be less than 1 !')

View File

@ -257,12 +257,14 @@ class ir_ui_menu(osv.osv):
def read_image(self, path): def read_image(self, path):
path_info = path.split(',') path_info = path.split(',')
icon_path = addons.get_module_resource(path_info[0],path_info[1]) icon_path = addons.get_module_resource(path_info[0],path_info[1])
icon_file = tools.file_open(icon_path,'rb') icon_image = False
try: if icon_path:
icon = icon_file.read() try:
return base64.encodestring(icon) icon_file = tools.file_open(icon_path,'rb')
finally: icon_image = base64.encodestring(icon_file.read())
icon_file.close() finally:
icon_file.close()
return icon_image
def _get_image_icon(self, cr, uid, ids, name, args, context=None): def _get_image_icon(self, cr, uid, ids, name, args, context=None):
res = {} res = {}
@ -304,7 +306,7 @@ class ir_ui_menu(osv.osv):
('ir.actions.server', 'ir.actions.server'), ('ir.actions.server', 'ir.actions.server'),
]), ]),
} }
def _rec_message(self, cr, uid, ids, context=None): def _rec_message(self, cr, uid, ids, context=None):
return _('Error ! You can not create recursive Menu.') return _('Error ! You can not create recursive Menu.')

View File

@ -138,7 +138,7 @@ class res_partner(osv.osv):
'email': fields.related('address', 'email', type='char', size=240, string='E-mail'), 'email': fields.related('address', 'email', type='char', size=240, string='E-mail'),
'company_id': fields.many2one('res.company', 'Company', select=1), 'company_id': fields.many2one('res.company', 'Company', select=1),
} }
def _default_category(self, cr, uid, context={}): def _default_category(self, cr, uid, context={}):
if 'category_id' in context and context['category_id']: if 'category_id' in context and context['category_id']:
return [context['category_id']] return [context['category_id']]

View File

@ -24,10 +24,66 @@ from locale import localeconv
import tools import tools
from tools.safe_eval import safe_eval as eval from tools.safe_eval import safe_eval as eval
from tools.translate import _ from tools.translate import _
import locale
import logging
class lang(osv.osv): class lang(osv.osv):
_name = "res.lang" _name = "res.lang"
_description = "Languages" _description = "Languages"
def install_lang(self, cr, uid, **args):
lang_ids = self.search(cr, uid, [('code','=', tools.config.get('lang'))])
values_obj = self.pool.get('ir.values')
if not lang_ids:
lang_id = self.load_lang(cr, uid, tools.config.get('lang'))
default_value = values_obj.get(cr, uid, 'default', False, 'res.partner')
if not default_value:
values_obj.set(cr, uid, 'default', False, 'lang', ['res.partner'], tools.config.get('lang'))
return True
def load_lang(self, cr, uid, lang, lang_name=None):
# create the language with locale information
fail = True
logger = logging.getLogger('i18n')
iso_lang = tools.get_iso_codes(lang)
for ln in tools.get_locales(lang):
try:
locale.setlocale(locale.LC_ALL, str(ln))
fail = False
break
except locale.Error:
continue
if fail:
lc = locale.getdefaultlocale()[0]
msg = 'Unable to get information for locale %s. Information from the default locale (%s) have been used.'
logger.warning(msg, lang, lc)
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,
'name': 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' : fix_xa0(str(locale.localeconv()['decimal_point'])),
'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])),
}
lang_id = False
try:
lang_id = self.create(cr, uid, lang_info)
finally:
tools.resetlocale()
return lang_id
def _get_default_date_format(self,cursor,user,context={}): def _get_default_date_format(self,cursor,user,context={}):
return '%m/%d/%Y' return '%m/%d/%Y'
@ -69,7 +125,7 @@ class lang(osv.osv):
thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep'] thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
decimal_point = lang_obj.decimal_point decimal_point = lang_obj.decimal_point
grouping = lang_obj.grouping grouping = lang_obj.grouping
return (grouping, thousands_sep, decimal_point) return (grouping, thousands_sep, decimal_point)
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
for lang_id in ids : for lang_id in ids :
@ -95,19 +151,19 @@ class lang(osv.osv):
def _group(self, cr, uid, ids, s, monetary=False, grouping=False, thousands_sep=''): def _group(self, cr, uid, ids, s, monetary=False, grouping=False, thousands_sep=''):
grouping = eval(grouping) grouping = eval(grouping)
if not grouping: if not grouping:
return (s, 0) return (s, 0)
result = "" result = ""
seps = 0 seps = 0
spaces = "" spaces = ""
if s[-1] == ' ': if s[-1] == ' ':
sp = s.find(' ') sp = s.find(' ')
spaces = s[sp:] spaces = s[sp:]
s = s[:sp] s = s[:sp]
while s and grouping: while s and grouping:
# if grouping is -1, we are done # if grouping is -1, we are done
if grouping[0] == -1: if grouping[0] == -1:
@ -139,7 +195,7 @@ class lang(osv.osv):
if percent[0] != '%': if percent[0] != '%':
raise ValueError("format() must be given exactly one %char format specifier") raise ValueError("format() must be given exactly one %char format specifier")
lang_grouping, thousands_sep, decimal_point = self._lang_data_get(cr, uid, ids[0], monetary) lang_grouping, thousands_sep, decimal_point = self._lang_data_get(cr, uid, ids[0], monetary)
formatted = percent % value formatted = percent % value
# floats and decimal ints need special action! # floats and decimal ints need special action!

View File

@ -194,6 +194,16 @@ class users(osv.osv):
self.write(cr, uid, ids, {'address_id': address_id}, context) self.write(cr, uid, ids, {'address_id': address_id}, context)
return True return True
def _set_new_password(self, cr, uid, id, name, value, args, context=None):
if not value:
raise osv.except_osv(_('Empty new password'), _('Please provide a new password value'))
if uid == id:
# To change their own password users must use the client-specific change password wizard,
# so that the new password is immediately used for further RPC requests, otherwise the user
# will face unexpected 'Access Denied' exceptions.
raise osv.except_osv(_('Operation Canceled'), _('Please use the change password wizard (in User Preferences or User menu) to change your own password.'))
self.write(cr, uid, id, {'password': value})
_columns = { _columns = {
'name': fields.char('User Name', size=64, required=True, select=True, 'name': fields.char('User Name', size=64, required=True, select=True,
help="The new user's real name, used for searching" help="The new user's real name, used for searching"
@ -201,6 +211,10 @@ class users(osv.osv):
'login': fields.char('Login', size=64, required=True, 'login': fields.char('Login', size=64, required=True,
help="Used to log into the system"), help="Used to log into the system"),
'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."), '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(lambda *a:'', 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!"),
'email': fields.char('E-mail', size=64, 'email': fields.char('E-mail', size=64,
help='If an email is provided, the user will be sent a message ' help='If an email is provided, the user will be sent a message '
'welcoming him.\n\nWarning: if "email_from" and "smtp_server"' 'welcoming him.\n\nWarning: if "email_from" and "smtp_server"'
@ -452,7 +466,7 @@ class users(osv.osv):
(int(uid), passwd, True)) (int(uid), passwd, True))
res = cr.fetchone()[0] res = cr.fetchone()[0]
if not bool(res): if not bool(res):
raise security.ExceptionNoTb('AccessDenied') raise security.ExceptionNoTb('Accessenied')
if res: if res:
if self._uid_cache.has_key(db): if self._uid_cache.has_key(db):
ulist = self._uid_cache[db] ulist = self._uid_cache[db]
@ -476,6 +490,18 @@ class users(osv.osv):
finally: finally:
cr.close() cr.close()
def change_password(self, cr, uid, old_passwd, new_passwd):
"""Change current user password. Old password must be provided explicitly
to prevent hijacking an existing user session, or for cases where the cleartext
password is not used to authenticate requests.
:return: True
:raise: security.ExceptionNoTb when old password is wrong
"""
if self.check(cr.dbname, uid, old_passwd):
self.write(cr, uid, uid, {'password': new_passwd})
return True
users() users()
class config_users(osv.osv_memory): class config_users(osv.osv_memory):
@ -574,39 +600,4 @@ class res_config_view(osv.osv_memory):
res_config_view() res_config_view()
class change_user_password(osv.osv_memory): # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
_name = 'change.user.password'
_columns = {
'current_password':fields.char('Current Password', size=64, required=True, help="Enter your current password."),
'new_password': fields.char('New Password', size=64, required=True, help="Enter the new password."),
'confirm_password': fields.char('Confirm Password', size=64, required=True, help="Enter the new password again for confirmation."),
}
_defaults={
'current_password' : '',
'new_password' : '',
'confirm_password' : '',
}
def change_password(self, cr, uid, ids, context=None):
for form_id in ids:
password_rec = self.browse(cr, uid, form_id, context)
if password_rec.new_password != password_rec.confirm_password:
raise osv.except_osv(_('Error !'), _('The new and confirmation passwords do not match, please double-check them.'))
# Validate current password without reading it from database,
# as it could be stored differently (LDAP, encrypted/hashed, etc.)
is_correct_password = False
try:
user_obj = self.pool.get('res.users')
is_correct_password = user_obj.check(cr.dbname, uid, password_rec.current_password)
except Exception:
pass
if not is_correct_password:
raise osv.except_osv(_('Error !'), _('The current password does not match, please double-check it.'))
user_obj.write(cr, uid, [uid], {'password': password_rec.new_password}, context=context)
return {}
change_user_password()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -553,7 +553,7 @@ class many2many(_column):
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
obj = obj.pool.get(self._obj) obj = obj.pool.get(self._obj)
# static domains are lists, and are evaluated both here and on client-side, while string # static domains are lists, and are evaluated both here and on client-side, while string
# domains supposed by dynamic and evaluated on client-side only (thus ignored here) # domains supposed by dynamic and evaluated on client-side only (thus ignored here)
# FIXME: make this distinction explicit in API! # FIXME: make this distinction explicit in API!
domain = isinstance(self._domain, list) and self._domain or [] domain = isinstance(self._domain, list) and self._domain or []
@ -1049,7 +1049,8 @@ class property(function):
brs = properties.browse(cr, uid, nids, context=context) brs = properties.browse(cr, uid, nids, context=context)
for prop in brs: for prop in brs:
value = properties.get_by_record(cr, uid, prop, context=context) value = properties.get_by_record(cr, uid, prop, context=context)
res[prop.res_id.id][prop.fields_id.name] = value or False record_exists = obj.pool.get(value._name).exists(cr, uid, value.id)
res[prop.res_id.id][prop.fields_id.name] = (record_exists and value) and value or False
if value and (prop.type == 'many2one'): if value and (prop.type == 'many2one'):
replaces.setdefault(value._name, {}) replaces.setdefault(value._name, {})
replaces[value._name][value.id] = True replaces[value._name][value.id] = True

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# #
@ -15,7 +15,7 @@
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # 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/>.
# #
############################################################################## ##############################################################################
@ -50,8 +50,8 @@ class db(netsvc.ExportService):
self._pg_psw_env_var_is_set = False # on win32, pg_dump need the PGPASSWORD env var self._pg_psw_env_var_is_set = False # on win32, pg_dump need the PGPASSWORD env var
def dispatch(self, method, auth, params): def dispatch(self, method, auth, params):
if method in [ 'create', 'get_progress', 'drop', 'dump', if method in [ 'create', 'get_progress', 'drop', 'dump',
'restore', 'rename', 'restore', 'rename',
'change_admin_password', 'migrate_databases' ]: 'change_admin_password', 'migrate_databases' ]:
passwd = params[0] passwd = params[0]
params = params[1:] params = params[1:]
@ -64,7 +64,7 @@ class db(netsvc.ExportService):
raise KeyError("Method not found: %s" % method) raise KeyError("Method not found: %s" % method)
fn = getattr(self, 'exp_'+method) fn = getattr(self, 'exp_'+method)
return fn(*params) return fn(*params)
def new_dispatch(self,method,auth,params): def new_dispatch(self,method,auth,params):
pass pass
def _create_empty_database(self, name): def _create_empty_database(self, name):
@ -93,6 +93,7 @@ class db(netsvc.ExportService):
serv.actions[id]['progress'] = 0 serv.actions[id]['progress'] = 0
cr = sql_db.db_connect(db_name).cursor() cr = sql_db.db_connect(db_name).cursor()
tools.init_db(cr) tools.init_db(cr)
tools.config['lang'] = lang
cr.commit() cr.commit()
cr.close() cr.close()
cr = None cr = None
@ -392,7 +393,7 @@ class common(_ObjectService):
security.check_super(passwd) security.check_super(passwd)
else: else:
raise Exception("Method not found: %s" % method) raise Exception("Method not found: %s" % method)
fn = getattr(self, 'exp_'+method) fn = getattr(self, 'exp_'+method)
return fn(*params) return fn(*params)
@ -568,7 +569,7 @@ GNU Public Licence.
def exp_check_connectivity(self): def exp_check_connectivity(self):
return bool(sql_db.db_connect('template1')) return bool(sql_db.db_connect('template1'))
def exp_get_os_time(self): def exp_get_os_time(self):
return os.times() return os.times()
@ -598,7 +599,7 @@ class objects_proxy(netsvc.ExportService):
res = fn(db, uid, *params) res = fn(db, uid, *params)
return res return res
def new_dispatch(self,method,auth,params): def new_dispatch(self,method,auth,params):
pass pass
@ -634,7 +635,7 @@ class wizard(netsvc.ExportService):
fn = getattr(self, 'exp_'+method) fn = getattr(self, 'exp_'+method)
res = fn(db, uid, *params) res = fn(db, uid, *params)
return res return res
def new_dispatch(self,method,auth,params): def new_dispatch(self,method,auth,params):
pass pass
@ -697,7 +698,7 @@ class report_spool(netsvc.ExportService):
res = fn(db, uid, *params) res = fn(db, uid, *params)
return res return res
def new_dispatch(self,method,auth,params): def new_dispatch(self,method,auth,params):
pass pass
@ -728,7 +729,7 @@ class report_spool(netsvc.ExportService):
self._reports[id]['format'] = format self._reports[id]['format'] = format
self._reports[id]['state'] = True self._reports[id]['state'] = True
except Exception, exception: except Exception, exception:
tb = sys.exc_info() tb = sys.exc_info()
tb_s = "".join(traceback.format_exception(*tb)) tb_s = "".join(traceback.format_exception(*tb))
logger = netsvc.Logger() logger = netsvc.Logger()

View File

@ -347,7 +347,7 @@ class TinyPoFile(object):
if name is None: if name is None:
if not fuzzy: if not fuzzy:
self.warn('Missing "#:" formated comment at line %d for the following source:\n\t%s', self.warn('Missing "#:" formated comment at line %d for the following source:\n\t%s',
self.cur_line(), source[:30]) self.cur_line(), source[:30])
return self.next() return self.next()
return type, name, res_id, source, trad return type, name, res_id, source, trad
@ -541,9 +541,9 @@ def trans_generate(lang, modules, dbname=None):
query = 'SELECT name, model, res_id, module' \ query = 'SELECT name, model, res_id, module' \
' FROM ir_model_data' ' FROM ir_model_data'
query_models = """SELECT m.id, m.model, imd.module query_models = """SELECT m.id, m.model, imd.module
FROM ir_model AS m, ir_model_data AS imd FROM ir_model AS m, ir_model_data AS imd
WHERE m.id = imd.res_id AND imd.model = 'ir.model' """ WHERE m.id = imd.res_id AND imd.model = 'ir.model' """
if 'all_installed' in modules: if 'all_installed' in modules:
@ -869,42 +869,7 @@ def trans_load_data(db_name, fileobj, fileformat, lang, lang_name=None, verbose=
if not ids: if not ids:
# lets create the language with locale information # lets create the language with locale information
fail = True lang_obj.load_lang(cr, 1, lang=lang, lang_name=lang_name)
for ln in get_locales(lang):
try:
locale.setlocale(locale.LC_ALL, str(ln))
fail = False
break
except locale.Error:
continue
if fail:
lc = locale.getdefaultlocale()[0]
msg = 'Unable to get information for locale %s. Information from the default locale (%s) have been used.'
logger.warning(msg, lang, lc)
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,
'name': 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' : 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:
resetlocale()
# now, the serious things: we read the language file # now, the serious things: we read the language file