[MERGE] forward port branch saas-15 up to 925210714f
This commit is contained in:
@@ -217,10 +217,13 @@ command is available and configured properly, run the following commands :
|
||||
$ sudo systemctl start postgresql
|
||||
|
||||
In order to print PDF reports, you must install wkhtmltopdf_ yourself:
|
||||
the version of wkhtmltopdf_ available in debian repositories does not support
|
||||
headers and footers so it can not be installed automatically.
|
||||
The recommended version is 0.12.1 and is available on `the wkhtmltopdf download page`_,
|
||||
in the archive section.
|
||||
the version of wkhtmltopdf_ available in Debian repositories does
|
||||
not support headers and footers so it is not used as a direct dependency.
|
||||
The recommended version is 0.12.5 and is available on
|
||||
`the wkhtmltopdf download page`_, in the archive section. Previously
|
||||
recommended version 0.12.1 is a good alternative.
|
||||
More details on the various versions and their respective quirks can be
|
||||
found in our `wiki <https://github.com/odoo/odoo/wiki/Wkhtmltopdf>`_.
|
||||
|
||||
Repository
|
||||
^^^^^^^^^^
|
||||
@@ -606,7 +609,7 @@ official Odoo `docker image <https://registry.hub.docker.com/_/odoo/>`_ page.
|
||||
http://www.enterprisedb.com/products-services-training/pgdownload
|
||||
.. _Quilt: http://en.wikipedia.org/wiki/Quilt_(software)
|
||||
.. _saas: https://www.odoo.com/page/start
|
||||
.. _the wkhtmltopdf download page: https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.1
|
||||
.. _the wkhtmltopdf download page: https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5
|
||||
.. _UAC: http://en.wikipedia.org/wiki/User_Account_Control
|
||||
.. _wkhtmltopdf: http://wkhtmltopdf.org
|
||||
.. _pip: https://pip.pypa.io
|
||||
|
||||
@@ -445,15 +445,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),
|
||||
@@ -461,25 +461,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'})
|
||||
|
||||
@@ -449,6 +449,8 @@ class Users(models.Model):
|
||||
@api.model
|
||||
def check_credentials(self, password):
|
||||
""" Override this method to plug additional authentication methods"""
|
||||
if not password:
|
||||
raise AccessDenied()
|
||||
user = self.sudo().search([('id', '=', self._uid), ('password', '=', password)])
|
||||
if not user:
|
||||
raise AccessDenied()
|
||||
|
||||
@@ -278,30 +278,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 pycompat.izip(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 list(pycompat.izip(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)
|
||||
@@ -328,17 +334,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 list(pycompat.izip(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