[MERGE] forward port branch 10.0 up to 42c42832b3

This commit is contained in:
Christophe Simonis
2018-11-28 14:41:28 +01:00
5 changed files with 118 additions and 63 deletions
+12 -1
View File
@@ -63,7 +63,18 @@ class MailMail(models.Model):
values['notification'] = True values['notification'] = True
if not values.get('mail_message_id'): if not values.get('mail_message_id'):
self = self.with_context(message_create_from_mail_mail=True) self = self.with_context(message_create_from_mail_mail=True)
return super(MailMail, self).create(values) new_mail = super(MailMail, self).create(values)
if values.get('attachment_ids'):
new_mail.attachment_ids.check(mode='read')
return new_mail
@api.multi
def write(self, vals):
res = super(MailMail, self).write(vals)
if vals.get('attachment_ids'):
for mail in self:
mail.attachment_ids.check(mode='read')
return res
@api.multi @api.multi
def unlink(self): def unlink(self):
+7
View File
@@ -740,6 +740,10 @@ class Message(models.Model):
# delegate creation of tracking after the create as sudo to avoid access rights issues # delegate creation of tracking after the create as sudo to avoid access rights issues
tracking_values_cmd = values.pop('tracking_value_ids', False) tracking_values_cmd = values.pop('tracking_value_ids', False)
message = super(Message, self).create(values) message = super(Message, self).create(values)
if values.get('attachment_ids'):
message.attachment_ids.check(mode='read')
if tracking_values_cmd: if tracking_values_cmd:
message.sudo().write({'tracking_value_ids': tracking_values_cmd}) message.sudo().write({'tracking_value_ids': tracking_values_cmd})
@@ -762,6 +766,9 @@ class Message(models.Model):
if 'model' in vals or 'res_id' in vals: if 'model' in vals or 'res_id' in vals:
self._invalidate_documents() self._invalidate_documents()
res = super(Message, self).write(vals) res = super(Message, self).write(vals)
if vals.get('attachment_ids'):
for mail in self:
mail.attachment_ids.check(mode='read')
self._invalidate_documents() self._invalidate_documents()
return res return res
+21 -9
View File
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from lxml.html import builder as html
from odoo import _, api, fields, models from odoo import _, api, fields, models
@@ -12,18 +15,27 @@ class Invite(models.TransientModel):
@api.model @api.model
def default_get(self, fields): def default_get(self, fields):
result = super(Invite, self).default_get(fields) result = super(Invite, self).default_get(fields)
if self._context.get('mail_invite_follower_channel_only'):
result['send_mail'] = False
if 'message' not in fields:
return result
user_name = self.env.user.name_get()[0][1] user_name = self.env.user.name_get()[0][1]
model = result.get('res_model') model = result.get('res_model')
res_id = result.get('res_id') res_id = result.get('res_id')
if self._context.get('mail_invite_follower_channel_only'): if model and res_id:
result['send_mail'] = False document = self.env['ir.model']._get(model).display_name
if 'message' in fields and model and res_id: title = self.env[model].browse(res_id).display_name
model_name = self.env['ir.model']._get(model).display_name msg_fmt = _('%(user_name)s invited you to follow %(document)s document: %(title)s')
document_name = self.env[model].browse(res_id).name_get()[0][1] else:
message = _('<div><p>Hello,</p><p>%s invited you to follow %s document: %s.</p></div>') % (user_name, model_name, document_name) msg_fmt = _('%(user_name)s invited you to follow a new document')
result['message'] = message
elif 'message' in fields: text = msg_fmt % locals()
result['message'] = _('<div><p>Hello,</p><p>%s invited you to follow a new document.</p></div>') % user_name message = html.DIV(
html.P(_('Hello,')),
html.P(text)
)
result['message'] = etree.tostring(message)
return result return result
res_model = fields.Char('Related Document Model', required=True, index=True, help='Model of the followed resource') res_model = fields.Char('Related Document Model', required=True, index=True, help='Model of the followed resource')
+28 -21
View File
@@ -464,15 +464,15 @@ class IrTranslation(models.Model):
if not callable(field.translate): if not callable(field.translate):
return return
trans = self.env['ir.translation'] Translation = self.env['ir.translation']
outdated = trans outdated = Translation
discarded = trans discarded = Translation
for record in records: for record in records:
# get field value and terms to translate # get field value and terms to translate
value = record[field.name] value = record[field.name]
terms = set(field.get_trans_terms(value)) terms = set(field.get_trans_terms(value))
record_trans = trans.search([ translations = Translation.search([
('type', '=', 'model'), ('type', '=', 'model'),
('name', '=', "%s,%s" % (field.model_name, field.name)), ('name', '=', "%s,%s" % (field.model_name, field.name)),
('res_id', '=', record.id), ('res_id', '=', record.id),
@@ -480,25 +480,32 @@ class IrTranslation(models.Model):
if not terms: if not terms:
# discard all translations for that field # discard all translations for that field
discarded += record_trans discarded += translations
continue continue
# remap existing translations on terms when possible # remap existing translations on terms when possible; each term
trans_src = record_trans.mapped('src') # should be translated at most once per language
for trans in record_trans: done = set() # {(src, lang), ...}
if trans.src == trans.value: translations_to_match = []
discarded += trans
elif trans.src not in terms: for translation in translations:
matches = get_close_matches(trans.src, terms, 1, 0.9) if translation.src == translation.value:
if matches: discarded += translation
if matches[0] in trans_src: elif translation.src in terms:
# there is already a translation for this term; discard this one done.add((translation.src, translation.lang))
discarded += trans else:
else: translations_to_match.append(translation)
trans.write({'src': matches[0], 'state': trans.state})
trans_src.append(matches[0]) # avoid reuse of term for translation in translations_to_match:
else: matches = get_close_matches(translation.src, terms, 1, 0.9)
outdated += trans src = matches[0] if matches else None
if not src:
outdated += translation
elif (src, translation.lang) in done:
discarded += translation
else:
translation.write({'src': src, 'state': translation.state})
done.add((src, translation.lang))
# process outdated and discarded translations # process outdated and discarded translations
outdated.write({'state': 'to_translate'}) outdated.write({'state': 'to_translate'})
+50 -32
View File
@@ -275,30 +275,36 @@ class TestTranslation(TransactionCase):
class TestXMLTranslation(TransactionCase): class TestXMLTranslation(TransactionCase):
def setUp(self): def setUp(self):
super(TestXMLTranslation, self).setUp() super(TestXMLTranslation, self).setUp()
self.env['ir.translation'].load_module_terms(['base'], ['fr_FR']) self.env['ir.translation'].load_module_terms(['base'], ['fr_FR', 'nl_NL'])
def create_view(self, archf, terms, **kwargs):
view = self.env['ir.ui.view'].create({
'name': 'test',
'model': 'res.partner',
'arch': archf % terms,
})
for lang, trans_terms in kwargs.items():
for src, val in zip(terms, trans_terms):
self.env['ir.translation'].create({
'type': 'model',
'name': 'ir.ui.view,arch_db',
'lang': lang,
'res_id': view.id,
'src': src,
'value': val,
'state': 'translated',
})
return view
def test_copy(self): def test_copy(self):
""" Create a simple view, fill in translations, and copy it. """ """ Create a simple view, fill in translations, and copy it. """
env_en = self.env(context={})
env_fr = self.env(context={'lang': 'fr_FR'})
archf = '<form string="%s"><div>%s</div><div>%s</div></form>' archf = '<form string="%s"><div>%s</div><div>%s</div></form>'
terms_en = ('Knife', 'Fork', 'Spoon') terms_en = ('Knife', 'Fork', 'Spoon')
terms_fr = ('Couteau', 'Fourchette', 'Cuiller') terms_fr = ('Couteau', 'Fourchette', 'Cuiller')
view0 = self.env['ir.ui.view'].create({ view0 = self.create_view(archf, terms_en, fr_FR=terms_fr)
'name': 'test',
'model': 'res.partner', env_en = self.env(context={})
'arch': archf % terms_en, env_fr = self.env(context={'lang': 'fr_FR'})
})
for src, value in zip(terms_en, terms_fr):
self.env['ir.translation'].create({
'type': 'model',
'name': 'ir.ui.view,arch_db',
'lang': 'fr_FR',
'res_id': view0.id,
'src': src,
'value': value,
})
# check translated field # check translated field
self.assertEqual(view0.with_env(env_en).arch_db, archf % terms_en) self.assertEqual(view0.with_env(env_en).arch_db, archf % terms_en)
@@ -325,17 +331,29 @@ class TestXMLTranslation(TransactionCase):
archf = '<form string="%s"><div>%s</div><div>%s</div></form>' archf = '<form string="%s"><div>%s</div><div>%s</div></form>'
terms_en = ('Knife', 'Fork', 'Spoon') terms_en = ('Knife', 'Fork', 'Spoon')
terms_fr = (' Couteau', 'Fourchette ', ' Cuiller ') terms_fr = (' Couteau', 'Fourchette ', ' Cuiller ')
view0 = self.env['ir.ui.view'].create({ self.create_view(archf, terms_en, fr_FR=terms_fr)
'name': 'test',
'model': 'res.partner', def test_sync(self):
'arch': archf % terms_en, """ Check translations after minor change in source terms. """
}) archf = '<form string="X">%s</form>'
for src, value in zip(terms_en, terms_fr): terms_en = ('Bread and cheeze',)
self.env['ir.translation'].create({ terms_fr = ('Pain et fromage',)
'type': 'model', terms_nl = ('Brood and kaas',)
'name': 'ir.ui.view,arch_db', view = self.create_view(archf, terms_en, fr_FR=terms_fr, nl_NL=terms_nl)
'lang': 'fr_FR',
'res_id': view0.id, env_en = self.env(context={})
'src': src, env_fr = self.env(context={'lang': 'fr_FR'})
'value': value, env_nl = self.env(context={'lang': 'nl_NL'})
})
self.assertEqual(view.with_env(env_en).arch_db, archf % terms_en)
self.assertEqual(view.with_env(env_fr).arch_db, archf % terms_fr)
self.assertEqual(view.with_env(env_nl).arch_db, archf % terms_nl)
# modify source term in view (fixed type in 'cheeze')
terms_en = ('Bread and cheese',)
view.write({'arch_db': archf % terms_en})
# check whether translations have been synchronized
self.assertEqual(view.with_env(env_en).arch_db, archf % terms_en)
self.assertEqual(view.with_env(env_fr).arch_db, archf % terms_fr)
self.assertEqual(view.with_env(env_nl).arch_db, archf % terms_nl)