Skip to content

Commit

Permalink
[MERGE] saas2
Browse files Browse the repository at this point in the history
bzr revid: nicolas.vanhoren@openerp.com-20131018103501-sns9zca0nmpm9efn
  • Loading branch information
nicolas-van committed Oct 18, 2013
2 parents 954f9ea + 71f1665 commit 5696282
Show file tree
Hide file tree
Showing 10 changed files with 454 additions and 76 deletions.
6 changes: 6 additions & 0 deletions openerp/addons/base/ir/ir_attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,14 @@ def _search(self, cr, uid, args, offset=0, limit=None, order=None, context=None,
return len(result) if count else list(result)

def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'read', context=context)
return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)

def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'write', context=context, values=vals)
if 'file_size' in vals:
del vals['file_size']
Expand All @@ -275,6 +279,8 @@ def copy(self, cr, uid, id, default=None, context=None):
return super(ir_attachment, self).copy(cr, uid, id, default, context)

def unlink(self, cr, uid, ids, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'unlink', context=context)
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
if location:
Expand Down
2 changes: 1 addition & 1 deletion openerp/addons/base/ir/ir_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def _str_to_float(self, cr, uid, model, column, value, context=None):

def _str_id(self, cr, uid, model, column, value, context=None):
return value, []
_str_to_char = _str_to_text = _str_to_binary = _str_id
_str_to_reference = _str_to_char = _str_to_text = _str_to_binary = _str_id

def _str_to_date(self, cr, uid, model, column, value, context=None):
try:
Expand Down
3 changes: 3 additions & 0 deletions openerp/addons/base/ir/ir_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ def create(self, cr, user, vals, context=None):
select=vals.get('select_level', '0'),
update_custom_fields=True)
self.pool[vals['model']]._auto_init(cr, ctx)
self.pool[vals['model']]._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res

Expand Down Expand Up @@ -356,6 +357,7 @@ def create(self, cr, user, vals, context=None):
select=vals.get('select_level', '0'),
update_custom_fields=True)
self.pool[vals['model']]._auto_init(cr, ctx)
self.pool[vals['model']]._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)

return res
Expand Down Expand Up @@ -470,6 +472,7 @@ def write(self, cr, user, ids, vals, context=None):
for col_name, col_prop, val in patch_struct[1]:
setattr(obj._columns[col_name], col_prop, val)
obj._auto_init(cr, ctx)
obj._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res

Expand Down
12 changes: 6 additions & 6 deletions openerp/addons/base/ir/ir_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,18 @@ def _get_src(self, cr, uid, ids, name, arg, context=None):
'''
if context is None:
context = {}
res = {}
res = dict.fromkeys(ids, False)
for record in self.browse(cr, uid, ids, context=context):
if record.type != 'model':
res[record.id] = record.src
else:
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
#We need to take the context without the language information, because we want to read the
#value store in db and not on the one associate with current language.
context_wo_lang = context.copy()
context_wo_lang.pop('lang', None)
res[record.id] = model.read(cr, uid, record.res_id, [field], context=context_wo_lang)[field]
if model and model.exists(cr, uid, record.res_id, context=context):
# Pass context without lang, need to read real stored field, not translation
context_no_lang = dict(context, lang=None)
result = model.read(cr, uid, record.res_id, [field], context=context_no_lang)
res[record.id] = result[field] if result else False
return res

def _set_src(self, cr, uid, id, name, value, args, context=None):
Expand Down
8 changes: 4 additions & 4 deletions openerp/modules/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,18 +305,16 @@ def check_registry_signaling(cls, db_name):
r, c = cr.fetchone()
# Check if the model registry must be reloaded (e.g. after the
# database has been updated by another process).
if registry.base_registry_signaling_sequence != r:
if registry.base_registry_signaling_sequence > 1 and registry.base_registry_signaling_sequence != r:
changed = True
_logger.info("Reloading the model registry after database signaling.")
registry = cls.new(db_name)
registry.base_registry_signaling_sequence = r
# Check if the model caches must be invalidated (e.g. after a write
# occured on another process). Don't clear right after a registry
# has been reload.
elif registry.base_cache_signaling_sequence != c:
elif registry.base_cache_signaling_sequence > 1 and registry.base_cache_signaling_sequence != c:
changed = True
_logger.info("Invalidating all model caches after database signaling.")
registry.base_cache_signaling_sequence = c
registry.clear_caches()
registry.reset_any_cache_cleared()
# One possible reason caches have been invalidated is the
Expand All @@ -326,6 +324,8 @@ def check_registry_signaling(cls, db_name):
for column in model._columns.values():
if hasattr(column, 'digits_change'):
column.digits_change(cr)
registry.base_registry_signaling_sequence = r
registry.base_cache_signaling_sequence = c
finally:
cr.close()
return changed
Expand Down
39 changes: 23 additions & 16 deletions openerp/osv/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,27 +207,29 @@ def _as_display_name(cls, field, cr, uid, obj, value, context=None):
return model.name_get(cr, uid, [int(res_id)], context=context)[0][1]
return tools.ustr(value)

# takes a string (encoded in utf8) and returns a string (encoded in utf8)
def _symbol_set_char(self, symb):

#TODO:
# * we need to remove the "symb==False" from the next line BUT
# for now too many things rely on this broken behavior
# * the symb==None test should be common to all data types
if symb is None or symb == False:
return None

# we need to convert the string to a unicode object to be able
# to evaluate its length (and possibly truncate it) reliably
u_symb = tools.ustr(symb)
return u_symb[:self.size].encode('utf8')

class char(_column):
_type = 'char'

def __init__(self, string="unknown", size=None, **args):
_column.__init__(self, string=string, size=size or None, **args)
self._symbol_set = (self._symbol_c, self._symbol_set_char)

# takes a string (encoded in utf8) and returns a string (encoded in utf8)
def _symbol_set_char(self, symb):
#TODO:
# * we need to remove the "symb==False" from the next line BUT
# for now too many things rely on this broken behavior
# * the symb==None test should be common to all data types
if symb is None or symb == False:
return None

# we need to convert the string to a unicode object to be able
# to evaluate its length (and possibly truncate it) reliably
u_symb = tools.ustr(symb)

return u_symb[:self.size].encode('utf8')
# self._symbol_set_char defined to keep the backward compatibility
self._symbol_f = self._symbol_set_char = lambda x: _symbol_set_char(self, x)
self._symbol_set = (self._symbol_c, self._symbol_f)


class text(_column):
Expand Down Expand Up @@ -1116,6 +1118,11 @@ def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float
self._symbol_f = integer._symbol_f
self._symbol_set = integer._symbol_set

if type == 'char':
self._symbol_c = char._symbol_c
self._symbol_f = lambda x: _symbol_set_char(self, x)
self._symbol_set = (self._symbol_c, self._symbol_f)

def digits_change(self, cr):
if self._type == 'float':
if self.digits_compute:
Expand Down
60 changes: 35 additions & 25 deletions openerp/osv/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,8 +1010,10 @@ def not_this_field(stored_func):
raise except_orm('Error',
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name)))
self.pool._store_function.setdefault(object, [])
self.pool._store_function[object].append((self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length))
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))
t = (self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length)
if not t in self.pool._store_function[object]:
self.pool._store_function[object].append((self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length))
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))

for (key, _, msg) in self._sql_constraints:
self.pool._sql_error[self._table+'_'+key] = msg
Expand Down Expand Up @@ -2887,8 +2889,12 @@ def _save_constraint(self, cr, constraint_name, type):
"""
Record the creation of a constraint for this model, to make it possible
to delete it later when the module is uninstalled. Type can be either
'f' or 'u' depending on the constraing being a foreign key or not.
'f' or 'u' depending on the constraint being a foreign key or not.
"""
if not self._module:
# no need to save constraints for custom models as they're not part
# of any module
return
assert type in ('f', 'u')
cr.execute("""
SELECT 1 FROM ir_model_constraint, ir_module_module
Expand Down Expand Up @@ -4583,9 +4589,9 @@ def browse(self, cr, uid, select, context=None, list_class=None, fields_process=
return browse_null()

def _store_get_values(self, cr, uid, ids, fields, context):
"""Returns an ordered list of fields.functions to call due to
"""Returns an ordered list of fields.function to call due to
an update operation on ``fields`` of records with ``ids``,
obtained by calling the 'store' functions of these fields,
obtained by calling the 'store' triggers of these fields,
as setup by their 'store' attribute.
:return: [(priority, model_name, [record_ids,], [function_fields,])]
Expand All @@ -4594,42 +4600,46 @@ def _store_get_values(self, cr, uid, ids, fields, context):
stored_functions = self.pool._store_function.get(self._name, [])

# use indexed names for the details of the stored_functions:
model_name_, func_field_to_compute_, id_mapping_fnct_, trigger_fields_, priority_ = range(5)
model_name_, func_field_to_compute_, target_ids_func_, trigger_fields_, priority_ = range(5)

# only keep functions that should be triggered for the ``fields``
# only keep store triggers that should be triggered for the ``fields``
# being written to.
to_compute = [f for f in stored_functions \
triggers_to_compute = [f for f in stored_functions \
if ((not f[trigger_fields_]) or set(fields).intersection(f[trigger_fields_]))]

mapping = {}
for function in to_compute:
# use admin user for accessing objects having rules defined on store fields
target_ids = [id for id in function[id_mapping_fnct_](self, cr, SUPERUSER_ID, ids, context) if id]
to_compute_map = {}
target_id_results = {}
for store_trigger in triggers_to_compute:
target_func_id_ = id(store_trigger[target_ids_func_])
if not target_func_id_ in target_id_results:
# use admin user for accessing objects having rules defined on store fields
target_id_results[target_func_id_] = [i for i in store_trigger[target_ids_func_](self, cr, SUPERUSER_ID, ids, context) if i]
target_ids = target_id_results[target_func_id_]

# the compound key must consider the priority and model name
key = (function[priority_], function[model_name_])
key = (store_trigger[priority_], store_trigger[model_name_])
for target_id in target_ids:
mapping.setdefault(key, {}).setdefault(target_id,set()).add(tuple(function))
to_compute_map.setdefault(key, {}).setdefault(target_id,set()).add(tuple(store_trigger))

# Here mapping looks like:
# { (10, 'model_a') : { target_id1: [ (function_1_tuple, function_2_tuple) ], ... }
# (20, 'model_a') : { target_id2: [ (function_3_tuple, function_4_tuple) ], ... }
# (99, 'model_a') : { target_id1: [ (function_5_tuple, function_6_tuple) ], ... }
# Here to_compute_map looks like:
# { (10, 'model_a') : { target_id1: [ (trigger_1_tuple, trigger_2_tuple) ], ... }
# (20, 'model_a') : { target_id2: [ (trigger_3_tuple, trigger_4_tuple) ], ... }
# (99, 'model_a') : { target_id1: [ (trigger_5_tuple, trigger_6_tuple) ], ... }
# }

# Now we need to generate the batch function calls list
# call_map =
# { (10, 'model_a') : [(10, 'model_a', [record_ids,], [function_fields,])] }
call_map = {}
for ((priority,model), id_map) in mapping.iteritems():
functions_ids_maps = {}
for ((priority,model), id_map) in to_compute_map.iteritems():
trigger_ids_maps = {}
# function_ids_maps =
# { (function_1_tuple, function_2_tuple) : [target_id1, target_id2, ..] }
for id, functions in id_map.iteritems():
functions_ids_maps.setdefault(tuple(functions), []).append(id)
for functions, ids in functions_ids_maps.iteritems():
call_map.setdefault((priority,model),[]).append((priority, model, ids,
[f[func_field_to_compute_] for f in functions]))
for target_id, triggers in id_map.iteritems():
trigger_ids_maps.setdefault(tuple(triggers), []).append(target_id)
for triggers, target_ids in trigger_ids_maps.iteritems():
call_map.setdefault((priority,model),[]).append((priority, model, target_ids,
[t[func_field_to_compute_] for t in triggers]))
ordered_keys = call_map.keys()
ordered_keys.sort()
result = []
Expand Down
13 changes: 13 additions & 0 deletions openerp/tests/test_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ def test_60_email_thunderbird(self):
for ext in test_mail_examples.THUNDERBIRD_1_OUT:
self.assertNotIn(ext, new_html, 'html_email_cleaner did not erase signature / quoted content')

def test_70_read_more(self):
new_html = html_email_clean(test_mail_examples.BUG1, remove=True, shorten=True, max_length=100)
for ext in test_mail_examples.BUG_1_IN:
self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
for ext in test_mail_examples.BUG_1_OUT:
self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')

new_html = html_email_clean(test_mail_examples.BUG2, remove=True, shorten=True, max_length=4000)
for ext in test_mail_examples.BUG_2_IN:
self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content')
for ext in test_mail_examples.BUG_2_OUT:
self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content')

def test_90_misc(self):
# False boolean for text must return empty string
new_html = html_email_clean(False)
Expand Down
Loading

0 comments on commit 5696282

Please sign in to comment.