[MERGE] forward port of branch saas-2 up to revid 5002 dle@openerp.com-20140114173613-ruxye1m7fxtcjfim
bzr revid: chs@openerp.com-20140115205357-arptzm69logz3osm
This commit is contained in:
commit
0771ca2692
|
@ -2,7 +2,7 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2004-2013 OpenERP S.A. <http://www.openerp.com>
|
# Copyright (C) 2004-2014 OpenERP S.A. <http://www.openerp.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
@ -417,7 +417,7 @@ class ir_actions_server(osv.osv):
|
||||||
|
|
||||||
def _select_objects(self, cr, uid, context=None):
|
def _select_objects(self, cr, uid, context=None):
|
||||||
model_pool = self.pool.get('ir.model')
|
model_pool = self.pool.get('ir.model')
|
||||||
ids = model_pool.search(cr, uid, [('name', 'not ilike', '.')])
|
ids = model_pool.search(cr, uid, [], limit=None)
|
||||||
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
||||||
return [(r['model'], r['name']) for r in res] + [('', '')]
|
return [(r['model'], r['name']) for r in res] + [('', '')]
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ class ir_attachment(osv.osv):
|
||||||
# performed in batch as much as possible.
|
# performed in batch as much as possible.
|
||||||
ima = self.pool.get('ir.model.access')
|
ima = self.pool.get('ir.model.access')
|
||||||
for model, targets in model_attachments.iteritems():
|
for model, targets in model_attachments.iteritems():
|
||||||
if model not in self.pool:
|
if not self.pool.get(model):
|
||||||
continue
|
continue
|
||||||
if not ima.check(cr, uid, model, 'read', False):
|
if not ima.check(cr, uid, model, 'read', False):
|
||||||
# remove all corresponding attachment ids
|
# remove all corresponding attachment ids
|
||||||
|
|
|
@ -77,7 +77,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
|
||||||
context=context)
|
context=context)
|
||||||
|
|
||||||
def write(self, cursor, user, ids, vals, context=None):
|
def write(self, cursor, user, ids, vals, context=None):
|
||||||
if vals.get('code', False):
|
if vals.get('code'):
|
||||||
vals['code'] = vals['code'].upper()
|
vals['code'] = vals['code'].upper()
|
||||||
return super(Country, self).write(cursor, user, ids, vals,
|
return super(Country, self).write(cursor, user, ids, vals,
|
||||||
context=context)
|
context=context)
|
||||||
|
|
|
@ -827,7 +827,7 @@ class users_view(osv.osv):
|
||||||
fields.append('groups_id')
|
fields.append('groups_id')
|
||||||
res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
|
res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
|
||||||
|
|
||||||
if group_fields:
|
if res and group_fields:
|
||||||
for values in (res if isinstance(res, list) else [res]):
|
for values in (res if isinstance(res, list) else [res]):
|
||||||
self._get_reified_groups(group_fields, values)
|
self._get_reified_groups(group_fields, values)
|
||||||
if inject_groups_id:
|
if inject_groups_id:
|
||||||
|
@ -859,6 +859,7 @@ class users_view(osv.osv):
|
||||||
'selection': [(False, '')] + [(g.id, g.name) for g in gs],
|
'selection': [(False, '')] + [(g.id, g.name) for g in gs],
|
||||||
'help': '\n'.join(tips),
|
'help': '\n'.join(tips),
|
||||||
'exportable': False,
|
'exportable': False,
|
||||||
|
'selectable': False,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# boolean group fields
|
# boolean group fields
|
||||||
|
@ -868,6 +869,7 @@ class users_view(osv.osv):
|
||||||
'string': g.name,
|
'string': g.name,
|
||||||
'help': g.comment,
|
'help': g.comment,
|
||||||
'exportable': False,
|
'exportable': False,
|
||||||
|
'selectable': False,
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
@ -530,4 +530,34 @@
|
||||||
!python {model: res.partner }: |
|
!python {model: res.partner }: |
|
||||||
res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
|
res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
|
||||||
assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
|
assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
|
||||||
|
-
|
||||||
|
Check that we can exclude translated fields (bug lp:1071710)
|
||||||
|
-
|
||||||
|
!python {model: res.country}: |
|
||||||
|
# first install french language
|
||||||
|
Modules = self.pool.get('ir.module.module')
|
||||||
|
base = Modules.browse(cr, uid, Modules.search(cr, uid, [('name', '=', 'base')])[0])
|
||||||
|
|
||||||
|
# hack: mark module as installed to allow install of the translation
|
||||||
|
base_state = base.state
|
||||||
|
base.write({'state': 'installed'})
|
||||||
|
base.update_translations('fr_FR')
|
||||||
|
base.write({'state': base_state})
|
||||||
|
|
||||||
|
# tests
|
||||||
|
be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
|
||||||
|
|
||||||
|
ctx = {'lang': 'fr_FR'}
|
||||||
|
not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
|
||||||
|
|
||||||
|
assert be_id not in not_be, "Search match failed"
|
||||||
|
|
||||||
|
# indirect search via m2o
|
||||||
|
Partners = self.pool.get('res.partner')
|
||||||
|
agrolait = Partners.search(cr, uid, [('name', '=', 'Agrolait')])[0]
|
||||||
|
|
||||||
|
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgium')])
|
||||||
|
assert agrolait not in not_be, "Search match failed (m2o)"
|
||||||
|
|
||||||
|
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgique')], context=ctx)
|
||||||
|
assert agrolait not in not_be, "Search match failed (m2o)"
|
||||||
|
|
|
@ -26,7 +26,7 @@ from openerp import report, tools
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
def graph_get(cr, graph, wkf_ids, nested, workitem, witm_trans, processed_subflows):
|
||||||
import pydot
|
import pydot
|
||||||
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
|
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
|
||||||
nodes = cr.dictfetchall()
|
nodes = cr.dictfetchall()
|
||||||
|
@ -40,7 +40,7 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
||||||
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
|
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
|
||||||
wkfinfo = cr.dictfetchone()
|
wkfinfo = cr.dictfetchone()
|
||||||
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
|
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
|
||||||
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, processed_subflows)
|
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, witm_trans, processed_subflows)
|
||||||
graph.add_subgraph(graph2)
|
graph.add_subgraph(graph2)
|
||||||
actfrom[n['id']] = s2
|
actfrom[n['id']] = s2
|
||||||
actto[n['id']] = s1
|
actto[n['id']] = s1
|
||||||
|
@ -89,7 +89,9 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
||||||
args['arrowtail']='inv'
|
args['arrowtail']='inv'
|
||||||
|
|
||||||
if activities[t['act_to']]['join_mode']=='AND':
|
if activities[t['act_to']]['join_mode']=='AND':
|
||||||
args['arrowhead']='crow'
|
args['arrowhead']='crow'
|
||||||
|
if t['id'] in witm_trans:
|
||||||
|
args['color'] = 'red'
|
||||||
|
|
||||||
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
|
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
|
||||||
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
|
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
|
||||||
|
@ -119,8 +121,12 @@ def graph_instance_get(cr, graph, inst_id, nested=False):
|
||||||
workitems.update(workitem_get(subflow_id))
|
workitems.update(workitem_get(subflow_id))
|
||||||
return workitems
|
return workitems
|
||||||
|
|
||||||
|
def witm_get(instance):
|
||||||
|
cr.execute("select trans_id from wkf_witm_trans where inst_id=%s", (instance,))
|
||||||
|
return set(t[0] for t in cr.fetchall())
|
||||||
|
|
||||||
processed_subflows = set()
|
processed_subflows = set()
|
||||||
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), processed_subflows)
|
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), witm_get(inst_id), processed_subflows)
|
||||||
|
|
||||||
#
|
#
|
||||||
# TODO: pas clean: concurrent !!!
|
# TODO: pas clean: concurrent !!!
|
||||||
|
|
|
@ -30,6 +30,7 @@ from os.path import join as opj
|
||||||
import openerp
|
import openerp
|
||||||
import openerp.release as release
|
import openerp.release as release
|
||||||
import openerp.tools as tools
|
import openerp.tools as tools
|
||||||
|
from openerp.tools.parse_version import parse_version
|
||||||
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
@ -93,7 +94,7 @@ class MigrationManager(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
def convert_version(version):
|
def convert_version(version):
|
||||||
if version.startswith(release.major_version) and version != release.major_version:
|
if version.count('.') >= 2:
|
||||||
return version # the version number already containt the server version
|
return version # the version number already containt the server version
|
||||||
return "%s.%s" % (release.major_version, version)
|
return "%s.%s" % (release.major_version, version)
|
||||||
|
|
||||||
|
@ -135,8 +136,6 @@ class MigrationManager(object):
|
||||||
a.update(b)
|
a.update(b)
|
||||||
return a
|
return a
|
||||||
|
|
||||||
from openerp.tools.parse_version import parse_version
|
|
||||||
|
|
||||||
parsed_installed_version = parse_version(pkg.installed_version or '')
|
parsed_installed_version = parse_version(pkg.installed_version or '')
|
||||||
current_version = parse_version(convert_version(pkg.data['version']))
|
current_version = parse_version(convert_version(pkg.data['version']))
|
||||||
|
|
||||||
|
|
|
@ -152,8 +152,8 @@ DOMAIN_OPERATORS = (NOT_OPERATOR, OR_OPERATOR, AND_OPERATOR)
|
||||||
# for consistency. This list doesn't contain '<>' as it is simpified to '!='
|
# for consistency. This list doesn't contain '<>' as it is simpified to '!='
|
||||||
# by the normalize_operator() function (so later part of the code deals with
|
# by the normalize_operator() function (so later part of the code deals with
|
||||||
# only one representation).
|
# only one representation).
|
||||||
# An internal (i.e. not available to the user) 'inselect' operator is also
|
# Internals (i.e. not available to the user) 'inselect' and 'not inselect'
|
||||||
# used. In this case its right operand has the form (subselect, params).
|
# operators are also used. In this case its right operand has the form (subselect, params).
|
||||||
TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
|
TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
|
||||||
'like', 'not like', 'ilike', 'not ilike', 'in', 'not in',
|
'like', 'not like', 'ilike', 'not ilike', 'in', 'not in',
|
||||||
'child_of')
|
'child_of')
|
||||||
|
@ -395,7 +395,7 @@ def is_leaf(element, internal=False):
|
||||||
"""
|
"""
|
||||||
INTERNAL_OPS = TERM_OPERATORS + ('<>',)
|
INTERNAL_OPS = TERM_OPERATORS + ('<>',)
|
||||||
if internal:
|
if internal:
|
||||||
INTERNAL_OPS += ('inselect',)
|
INTERNAL_OPS += ('inselect', 'not inselect')
|
||||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||||
and len(element) == 3 \
|
and len(element) == 3 \
|
||||||
and element[1] in INTERNAL_OPS \
|
and element[1] in INTERNAL_OPS \
|
||||||
|
@ -1024,6 +1024,12 @@ class expression(object):
|
||||||
if need_wildcard:
|
if need_wildcard:
|
||||||
right = '%%%s%%' % right
|
right = '%%%s%%' % right
|
||||||
|
|
||||||
|
inselect_operator = 'inselect'
|
||||||
|
if sql_operator in NEGATIVE_TERM_OPERATORS:
|
||||||
|
# negate operator (fix lp:1071710)
|
||||||
|
sql_operator = sql_operator[4:] if sql_operator[:3] == 'not' else '='
|
||||||
|
inselect_operator = 'not inselect'
|
||||||
|
|
||||||
subselect = '( SELECT res_id' \
|
subselect = '( SELECT res_id' \
|
||||||
' FROM ir_translation' \
|
' FROM ir_translation' \
|
||||||
' WHERE name = %s' \
|
' WHERE name = %s' \
|
||||||
|
@ -1031,7 +1037,7 @@ class expression(object):
|
||||||
' AND type = %s'
|
' AND type = %s'
|
||||||
instr = ' %s'
|
instr = ' %s'
|
||||||
#Covering in,not in operators with operands (%s,%s) ,etc.
|
#Covering in,not in operators with operands (%s,%s) ,etc.
|
||||||
if sql_operator in ['in', 'not in']:
|
if sql_operator == 'in':
|
||||||
instr = ','.join(['%s'] * len(right))
|
instr = ','.join(['%s'] * len(right))
|
||||||
subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \
|
subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \
|
||||||
') UNION (' \
|
') UNION (' \
|
||||||
|
@ -1051,7 +1057,7 @@ class expression(object):
|
||||||
right,
|
right,
|
||||||
right,
|
right,
|
||||||
]
|
]
|
||||||
push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_model))
|
push(create_substitution_leaf(leaf, ('id', inselect_operator, (subselect, params)), working_model))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
push_result(leaf)
|
push_result(leaf)
|
||||||
|
@ -1072,7 +1078,7 @@ class expression(object):
|
||||||
left, operator, right = leaf
|
left, operator, right = leaf
|
||||||
|
|
||||||
# final sanity checks - should never fail
|
# final sanity checks - should never fail
|
||||||
assert operator in (TERM_OPERATORS + ('inselect',)), \
|
assert operator in (TERM_OPERATORS + ('inselect', 'not inselect')), \
|
||||||
"Invalid operator %r in domain term %r" % (operator, leaf)
|
"Invalid operator %r in domain term %r" % (operator, leaf)
|
||||||
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in model._all_columns \
|
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in model._all_columns \
|
||||||
or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
|
or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
|
||||||
|
@ -1091,6 +1097,10 @@ class expression(object):
|
||||||
query = '(%s."%s" in (%s))' % (table_alias, left, right[0])
|
query = '(%s."%s" in (%s))' % (table_alias, left, right[0])
|
||||||
params = right[1]
|
params = right[1]
|
||||||
|
|
||||||
|
elif operator == 'not inselect':
|
||||||
|
query = '(%s."%s" not in (%s))' % (table_alias, left, right[0])
|
||||||
|
params = right[1]
|
||||||
|
|
||||||
elif operator in ['in', 'not in']:
|
elif operator in ['in', 'not in']:
|
||||||
# Two cases: right is a boolean or a list. The boolean case is an
|
# Two cases: right is a boolean or a list. The boolean case is an
|
||||||
# abuse and handled for backward compatibility.
|
# abuse and handled for backward compatibility.
|
||||||
|
|
|
@ -203,7 +203,8 @@ class reference(_column):
|
||||||
model_name, res_id = value.split(',')
|
model_name, res_id = value.split(',')
|
||||||
if model_name in obj.pool and res_id:
|
if model_name in obj.pool and res_id:
|
||||||
model = obj.pool[model_name]
|
model = obj.pool[model_name]
|
||||||
return model.name_get(cr, uid, [int(res_id)], context=context)[0][1]
|
names = model.name_get(cr, uid, [int(res_id)], context=context)
|
||||||
|
return names[0][1] if names else False
|
||||||
return tools.ustr(value)
|
return tools.ustr(value)
|
||||||
|
|
||||||
# takes a string (encoded in utf8) and returns a string (encoded in utf8)
|
# takes a string (encoded in utf8) and returns a string (encoded in utf8)
|
||||||
|
|
|
@ -77,6 +77,8 @@ from openerp.tools import SKIPPED_ELEMENT_TYPES
|
||||||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||||
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
|
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
|
||||||
|
|
||||||
|
AUTOINIT_RECALCULATE_STORED_FIELDS = 1000
|
||||||
|
|
||||||
def transfer_field_to_modifiers(field, modifiers):
|
def transfer_field_to_modifiers(field, modifiers):
|
||||||
default_values = {}
|
default_values = {}
|
||||||
state_exceptions = {}
|
state_exceptions = {}
|
||||||
|
@ -2838,8 +2840,8 @@ class BaseModel(object):
|
||||||
cr.execute('select id from '+self._table)
|
cr.execute('select id from '+self._table)
|
||||||
ids_lst = map(lambda x: x[0], cr.fetchall())
|
ids_lst = map(lambda x: x[0], cr.fetchall())
|
||||||
while ids_lst:
|
while ids_lst:
|
||||||
iids = ids_lst[:40]
|
iids = ids_lst[:AUTOINIT_RECALCULATE_STORED_FIELDS]
|
||||||
ids_lst = ids_lst[40:]
|
ids_lst = ids_lst[AUTOINIT_RECALCULATE_STORED_FIELDS:]
|
||||||
res = f.get(cr, self, iids, k, SUPERUSER_ID, {})
|
res = f.get(cr, self, iids, k, SUPERUSER_ID, {})
|
||||||
for key, val in res.items():
|
for key, val in res.items():
|
||||||
if f._multi:
|
if f._multi:
|
||||||
|
@ -4250,7 +4252,8 @@ class BaseModel(object):
|
||||||
if not src_trans:
|
if not src_trans:
|
||||||
src_trans = vals[f]
|
src_trans = vals[f]
|
||||||
# Inserting value to DB
|
# Inserting value to DB
|
||||||
self.write(cr, user, ids, {f: vals[f]})
|
context_wo_lang = dict(context, lang=None)
|
||||||
|
self.write(cr, user, ids, {f: vals[f]}, context=context_wo_lang)
|
||||||
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
|
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
|
||||||
|
|
||||||
|
|
||||||
|
@ -4579,7 +4582,9 @@ class BaseModel(object):
|
||||||
self._validate(cr, user, [id_new], context)
|
self._validate(cr, user, [id_new], context)
|
||||||
|
|
||||||
if not context.get('no_store_function', False):
|
if not context.get('no_store_function', False):
|
||||||
result += self._store_get_values(cr, user, [id_new], vals.keys(), context)
|
result += self._store_get_values(cr, user, [id_new],
|
||||||
|
list(set(vals.keys() + self._inherits.values())),
|
||||||
|
context)
|
||||||
result.sort()
|
result.sort()
|
||||||
done = []
|
done = []
|
||||||
for order, model_name, ids, fields2 in result:
|
for order, model_name, ids, fields2 in result:
|
||||||
|
@ -4804,6 +4809,9 @@ class BaseModel(object):
|
||||||
|
|
||||||
:param query: the current query object
|
:param query: the current query object
|
||||||
"""
|
"""
|
||||||
|
if uid == SUPERUSER_ID:
|
||||||
|
return
|
||||||
|
|
||||||
def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None):
|
def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None):
|
||||||
""" :param string parent_model: string of the parent model
|
""" :param string parent_model: string of the parent model
|
||||||
:param model child_object: model object, base of the rule application
|
:param model child_object: model object, base of the rule application
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import unittest2
|
import unittest2
|
||||||
from . import test_mail_examples
|
from . import test_mail_examples
|
||||||
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html
|
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html, email_split
|
||||||
|
|
||||||
class TestSanitizer(unittest2.TestCase):
|
class TestSanitizer(unittest2.TestCase):
|
||||||
""" Test the html sanitizer that filters html to remove unwanted attributes """
|
""" Test the html sanitizer that filters html to remove unwanted attributes """
|
||||||
|
@ -325,6 +325,19 @@ class TestHtmlTools(unittest2.TestCase):
|
||||||
for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
|
for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
|
||||||
self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
|
self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
|
||||||
|
|
||||||
|
class TestEmailTools(unittest2.TestCase):
|
||||||
|
""" Test some of our generic utility functions for emails """
|
||||||
|
|
||||||
|
def test_email_split(self):
|
||||||
|
cases = [
|
||||||
|
("John <12345@gmail.com>", ['12345@gmail.com']), # regular form
|
||||||
|
("d@x; 1@2", ['d@x', '1@2']), # semi-colon + extra space
|
||||||
|
("'(ss)' <123@gmail.com>, 'foo' <foo@bar>", ['123@gmail.com','foo@bar']), # comma + single-quoting
|
||||||
|
('"john@gmail.com"<johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting
|
||||||
|
('"<jg>" <johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting with brackets
|
||||||
|
]
|
||||||
|
for text, expected in cases:
|
||||||
|
self.assertEqual(email_split(text), expected, 'email_split is broken')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest2.main()
|
unittest2.main()
|
||||||
|
|
|
@ -29,6 +29,7 @@ import re
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from email.utils import getaddresses
|
||||||
|
|
||||||
import openerp
|
import openerp
|
||||||
from openerp.loglevels import ustr
|
from openerp.loglevels import ustr
|
||||||
|
@ -559,4 +560,9 @@ def email_split(text):
|
||||||
""" Return a list of the email addresses found in ``text`` """
|
""" Return a list of the email addresses found in ``text`` """
|
||||||
if not text:
|
if not text:
|
||||||
return []
|
return []
|
||||||
return re.findall(r'([^ ,<@]+@[^> ,]+)', text)
|
return [addr[1] for addr in getaddresses([text])
|
||||||
|
# getaddresses() returns '' when email parsing fails, and
|
||||||
|
# sometimes returns emails without at least '@'. The '@'
|
||||||
|
# is strictly required in RFC2822's `addr-spec`.
|
||||||
|
if addr[1]
|
||||||
|
if '@' in addr[1]]
|
|
@ -961,6 +961,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
|
||||||
irt_cursor.push(dic)
|
irt_cursor.push(dic)
|
||||||
|
|
||||||
irt_cursor.finish()
|
irt_cursor.finish()
|
||||||
|
trans_obj.clear_caches()
|
||||||
if verbose:
|
if verbose:
|
||||||
_logger.info("translation file loaded succesfully")
|
_logger.info("translation file loaded succesfully")
|
||||||
except IOError:
|
except IOError:
|
||||||
|
|
Loading…
Reference in New Issue