[MERGE] forward port branch 10.0 up to 42c42832b3
This commit is contained in:
@@ -63,7 +63,18 @@ class MailMail(models.Model):
|
||||
values['notification'] = True
|
||||
if not values.get('mail_message_id'):
|
||||
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
|
||||
def unlink(self):
|
||||
|
||||
@@ -740,6 +740,10 @@ class Message(models.Model):
|
||||
# delegate creation of tracking after the create as sudo to avoid access rights issues
|
||||
tracking_values_cmd = values.pop('tracking_value_ids', False)
|
||||
message = super(Message, self).create(values)
|
||||
|
||||
if values.get('attachment_ids'):
|
||||
message.attachment_ids.check(mode='read')
|
||||
|
||||
if 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:
|
||||
self._invalidate_documents()
|
||||
res = super(Message, self).write(vals)
|
||||
if vals.get('attachment_ids'):
|
||||
for mail in self:
|
||||
mail.attachment_ids.check(mode='read')
|
||||
self._invalidate_documents()
|
||||
return res
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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
|
||||
|
||||
|
||||
@@ -12,18 +15,27 @@ class Invite(models.TransientModel):
|
||||
@api.model
|
||||
def default_get(self, 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]
|
||||
model = result.get('res_model')
|
||||
res_id = result.get('res_id')
|
||||
if self._context.get('mail_invite_follower_channel_only'):
|
||||
result['send_mail'] = False
|
||||
if 'message' in fields and model and res_id:
|
||||
model_name = self.env['ir.model']._get(model).display_name
|
||||
document_name = self.env[model].browse(res_id).name_get()[0][1]
|
||||
message = _('<div><p>Hello,</p><p>%s invited you to follow %s document: %s.</p></div>') % (user_name, model_name, document_name)
|
||||
result['message'] = message
|
||||
elif 'message' in fields:
|
||||
result['message'] = _('<div><p>Hello,</p><p>%s invited you to follow a new document.</p></div>') % user_name
|
||||
if model and res_id:
|
||||
document = self.env['ir.model']._get(model).display_name
|
||||
title = self.env[model].browse(res_id).display_name
|
||||
msg_fmt = _('%(user_name)s invited you to follow %(document)s document: %(title)s')
|
||||
else:
|
||||
msg_fmt = _('%(user_name)s invited you to follow a new document')
|
||||
|
||||
text = msg_fmt % locals()
|
||||
message = html.DIV(
|
||||
html.P(_('Hello,')),
|
||||
html.P(text)
|
||||
)
|
||||
result['message'] = etree.tostring(message)
|
||||
return result
|
||||
|
||||
res_model = fields.Char('Related Document Model', required=True, index=True, help='Model of the followed resource')
|
||||
|
||||
@@ -464,15 +464,15 @@ class IrTranslation(models.Model):
|
||||
if not callable(field.translate):
|
||||
return
|
||||
|
||||
trans = self.env['ir.translation']
|
||||
outdated = trans
|
||||
discarded = trans
|
||||
Translation = self.env['ir.translation']
|
||||
outdated = Translation
|
||||
discarded = Translation
|
||||
|
||||
for record in records:
|
||||
# get field value and terms to translate
|
||||
value = record[field.name]
|
||||
terms = set(field.get_trans_terms(value))
|
||||
record_trans = trans.search([
|
||||
translations = Translation.search([
|
||||
('type', '=', 'model'),
|
||||
('name', '=', "%s,%s" % (field.model_name, field.name)),
|
||||
('res_id', '=', record.id),
|
||||
@@ -480,25 +480,32 @@ class IrTranslation(models.Model):
|
||||
|
||||
if not terms:
|
||||
# discard all translations for that field
|
||||
discarded += record_trans
|
||||
discarded += translations
|
||||
continue
|
||||
|
||||
# remap existing translations on terms when possible
|
||||
trans_src = record_trans.mapped('src')
|
||||
for trans in record_trans:
|
||||
if trans.src == trans.value:
|
||||
discarded += trans
|
||||
elif trans.src not in terms:
|
||||
matches = get_close_matches(trans.src, terms, 1, 0.9)
|
||||
if matches:
|
||||
if matches[0] in trans_src:
|
||||
# there is already a translation for this term; discard this one
|
||||
discarded += trans
|
||||
else:
|
||||
trans.write({'src': matches[0], 'state': trans.state})
|
||||
trans_src.append(matches[0]) # avoid reuse of term
|
||||
else:
|
||||
outdated += trans
|
||||
# remap existing translations on terms when possible; each term
|
||||
# should be translated at most once per language
|
||||
done = set() # {(src, lang), ...}
|
||||
translations_to_match = []
|
||||
|
||||
for translation in translations:
|
||||
if translation.src == translation.value:
|
||||
discarded += translation
|
||||
elif translation.src in terms:
|
||||
done.add((translation.src, translation.lang))
|
||||
else:
|
||||
translations_to_match.append(translation)
|
||||
|
||||
for translation in translations_to_match:
|
||||
matches = get_close_matches(translation.src, terms, 1, 0.9)
|
||||
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
|
||||
outdated.write({'state': 'to_translate'})
|
||||
|
||||
@@ -275,30 +275,36 @@ class TestTranslation(TransactionCase):
|
||||
class TestXMLTranslation(TransactionCase):
|
||||
def setUp(self):
|
||||
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):
|
||||
""" 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>'
|
||||
terms_en = ('Knife', 'Fork', 'Spoon')
|
||||
terms_fr = ('Couteau', 'Fourchette', 'Cuiller')
|
||||
view0 = self.env['ir.ui.view'].create({
|
||||
'name': 'test',
|
||||
'model': 'res.partner',
|
||||
'arch': archf % terms_en,
|
||||
})
|
||||
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,
|
||||
})
|
||||
view0 = self.create_view(archf, terms_en, fr_FR=terms_fr)
|
||||
|
||||
env_en = self.env(context={})
|
||||
env_fr = self.env(context={'lang': 'fr_FR'})
|
||||
|
||||
# check translated field
|
||||
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>'
|
||||
terms_en = ('Knife', 'Fork', 'Spoon')
|
||||
terms_fr = (' Couteau', 'Fourchette ', ' Cuiller ')
|
||||
view0 = self.env['ir.ui.view'].create({
|
||||
'name': 'test',
|
||||
'model': 'res.partner',
|
||||
'arch': archf % terms_en,
|
||||
})
|
||||
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,
|
||||
})
|
||||
self.create_view(archf, terms_en, fr_FR=terms_fr)
|
||||
|
||||
def test_sync(self):
|
||||
""" Check translations after minor change in source terms. """
|
||||
archf = '<form string="X">%s</form>'
|
||||
terms_en = ('Bread and cheeze',)
|
||||
terms_fr = ('Pain et fromage',)
|
||||
terms_nl = ('Brood and kaas',)
|
||||
view = self.create_view(archf, terms_en, fr_FR=terms_fr, nl_NL=terms_nl)
|
||||
|
||||
env_en = self.env(context={})
|
||||
env_fr = self.env(context={'lang': 'fr_FR'})
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user