diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-10-09 02:08:17 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-10-09 02:08:17 +0200 |
| commit | d74b4518052066446e2a1975a91ce53c745ca75a (patch) | |
| tree | c08180f88a512d15e37fbec628931f02b3825f65 /odoo/tools | |
| parent | ec162dd0cb5622ab6e56db1a63dab5f531a96f4a (diff) | |
| parent | f4105eb9c7010f8693e5d2fd20c0bd9d9cc2e483 (diff) | |
[MERGE] forward port branch 13.0 up to f4105eb9c70
Diffstat (limited to 'odoo/tools')
| -rw-r--r-- | odoo/tools/config.py | 24 | ||||
| -rw-r--r-- | odoo/tools/image.py | 17 | ||||
| -rw-r--r-- | odoo/tools/mail.py | 37 | ||||
| -rw-r--r-- | odoo/tools/misc.py | 57 | ||||
| -rw-r--r-- | odoo/tools/sql.py | 2 |
5 files changed, 66 insertions, 71 deletions
diff --git a/odoo/tools/config.py b/odoo/tools/config.py index f0c6cacda37..e691cdb4d08 100644 --- a/odoo/tools/config.py +++ b/odoo/tools/config.py @@ -473,7 +473,7 @@ class configmanager(object): elif isinstance(self.options[arg], str) and self.casts[arg].type in optparse.Option.TYPE_CHECKER: self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg]) - self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.join(os.path.dirname(__file__), '..')))) + self.options['root_path'] = self._normalize(os.path.join(os.path.dirname(__file__), '..')) if not self.options['addons_path'] or self.options['addons_path']=='None': default_addons = [] base_addons = os.path.join(self.options['root_path'], 'addons') @@ -485,18 +485,16 @@ class configmanager(object): self.options['addons_path'] = ','.join(default_addons) else: self.options['addons_path'] = ",".join( - os.path.abspath(os.path.expanduser(os.path.expandvars(x.strip()))) - for x in self.options['addons_path'].split(',')) + self._normalize(x) + for x in self.options['addons_path'].split(',')) self.options['upgrades_paths'] = ( - ",".join(os.path.abspath(os.path.expanduser(os.path.expandvars(x.strip()))) + ",".join(self._normalize(x) for x in self.options['upgrades_paths'].split(',')) if self.options['upgrades_paths'] else "" ) - self.options['data_dir'] = os.path.abspath(os.path.expanduser(os.path.expandvars(self.options['data_dir'].strip()))) - self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {} self.options['demo'] = (dict(self.options['init']) if not self.options['without_demo'] else {}) @@ -519,6 +517,10 @@ class configmanager(object): if opt.save: self.save() + # normalize path options + for key in ['data_dir', 'logfile', 'pidfile', 'test_file', 'screencasts', 'screenshots', 'pg_path', 'translate_out', 'translate_in', 'geoip_database']: + self.options[key] = self._normalize(self.options[key]) + conf.addons_paths = self.options['addons_path'].split(',') conf.server_wide_modules = [ @@ -553,7 +555,7 @@ class configmanager(object): upgrades_paths = [] for path in value.split(','): path = path.strip() - res = os.path.abspath(os.path.expanduser(os.path.expandvars(path))) + res = self._normalize(path) if not os.path.isdir(res): raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path)) upgrades_paths.append(res) @@ -695,4 +697,12 @@ class configmanager(object): self.options['admin_passwd'] = updated_hash return True + def _normalize(self, path): + if not path: + return path + return os.path.abspath( + os.path.expanduser( + os.path.expandvars( + path.strip()))) + config = configmanager() diff --git a/odoo/tools/image.py b/odoo/tools/image.py index 5819b8c7b10..f5b4b953da9 100644 --- a/odoo/tools/image.py +++ b/odoo/tools/image.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 +import binascii import io from PIL import Image @@ -9,6 +10,7 @@ from PIL import IcoImagePlugin from random import randrange +from odoo.exceptions import UserError from odoo.tools.translate import _ @@ -49,8 +51,7 @@ class ImageProcess(): :rtype: ImageProcess :raise: ValueError if `verify_resolution` is True and the image is too large - :raise: binascii.Error: if the base64 is incorrect - :raise: OSError if the image can't be identified by PIL + :raise: UserError if the base64 is incorrect or the image can't be identified by PIL """ self.base64_source = base64_source or False self.operationsCount = 0 @@ -215,8 +216,8 @@ class ImageProcess(): new_w, new_h = (new_w * h) // new_h, h # Correctly place the center of the crop. - x_offset = (w - new_w) * center_x - h_offset = (h - new_h) * center_y + x_offset = int((w - new_w) * center_x) + h_offset = int((h - new_h) * center_y) if new_w != w or new_h != h: self.image = self.image.crop((x_offset, h_offset, x_offset + new_w, h_offset + new_h)) @@ -339,10 +340,12 @@ def base64_to_image(base64_source): :return: the PIL image :rtype: PIL.Image - :raise: binascii.Error: if the base64 is incorrect - :raise: OSError if the image can't be identified by PIL + :raise: UserError if the base64 is incorrect or the image can't be identified by PIL """ - return Image.open(io.BytesIO(base64.b64decode(base64_source))) + try: + return Image.open(io.BytesIO(base64.b64decode(base64_source))) + except (OSError, binascii.Error): + raise UserError(_("This file could not be decoded as an image file. Please try with a different file.")) def image_to_base64(image, format, **params): diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py index 1ca5e0dc325..e1c999a041f 100644 --- a/odoo/tools/mail.py +++ b/odoo/tools/mail.py @@ -20,31 +20,6 @@ from odoo.tools import misc _logger = logging.getLogger(__name__) -# Optional Flanker dependency for email validation. -_flanker_warning = False -try: - from flanker.addresslib import address - if hasattr(address, 'six'): - # Python 3 supported - def checkmail(mail): - return bool(address.validate_address(mail)) - else: - def checkmail(mail): - global _flanker_warning - if not _flanker_warning: - _logger.info("Flanker version 0.9 or greater required for Python 3 compatibility") - _flanker_warning = True - return True - -except ImportError: - _logger.info('The flanker Python module is not installed, so email validation with flanker is unavailable') - def checkmail(mail): - global _flanker_warning - if not _flanker_warning: - _logger.info('The flanker Python module is not installed, so email validation with flanker is unavailable') - _flanker_warning = True - return True - #---------------------------------------------------------- # HTML Sanitizer #---------------------------------------------------------- @@ -286,7 +261,7 @@ def html_keep_url(text): link_tags = re.compile(r"""(?<!["'])((ftp|http|https):\/\/(\w+:{0,1}\w*@)?([^\s<"']+)(:[0-9]+)?(\/|\/([^\s<"']))?)(?![^\s<"']*["']|[^\s<"']*</a>)""") for item in re.finditer(link_tags, text): final += text[idx:item.start()] - final += '<a href="%s" target="_blank">%s</a>' % (item.group(0), item.group(0)) + final += '<a href="%s" target="_blank" rel="noreferrer noopener">%s</a>' % (item.group(0), item.group(0)) idx = item.end() final += text[idx:] return final @@ -533,16 +508,6 @@ def email_normalize(text): return False return emails[0].lower() -def email_validate(email): - """ - Check is the email is valid using email normalization + optional Flanker module. - If Flanker is installed, it will check if email is valid (checking DNS and other stuff). - If Flanker is not installed, this method only normalizes the email - :param text: string containing the email to validate - :return: normalized email if mail is valid or False - """ - return email_normalize(email) if checkmail(email) else False - def email_escape_char(email_address): """ Escape problematic characters in the given email address string""" return email_address.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_') diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index 5715c9b83c6..b65cd23cf6f 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -926,8 +926,10 @@ class ConstantMapping(Mapping): return self._value -def dumpstacks(sig=None, frame=None): - """ Signal handler: dump a stack trace for each existing thread.""" +def dumpstacks(sig=None, frame=None, thread_idents=None): + """ Signal handler: dump a stack trace for each existing thread or given + thread(s) specified through the ``thread_idents`` sequence. + """ code = [] def extract_stack(stack): @@ -944,14 +946,15 @@ def dumpstacks(sig=None, frame=None): 'url': getattr(th, 'url', 'n/a')} for th in threading.enumerate()} for threadId, stack in sys._current_frames().items(): - thread_info = threads_info.get(threadId, {}) - code.append("\n# Thread: %s (db:%s) (uid:%s) (url:%s)" % - (thread_info.get('repr', threadId), - thread_info.get('dbname', 'n/a'), - thread_info.get('uid', 'n/a'), - thread_info.get('url', 'n/a'))) - for line in extract_stack(stack): - code.append(line) + if not thread_idents or threadId in thread_idents: + thread_info = threads_info.get(threadId, {}) + code.append("\n# Thread: %s (db:%s) (uid:%s) (url:%s)" % + (thread_info.get('repr', threadId), + thread_info.get('dbname', 'n/a'), + thread_info.get('uid', 'n/a'), + thread_info.get('url', 'n/a'))) + for line in extract_stack(stack): + code.append(line) if odoo.evented: # code from http://stackoverflow.com/questions/12510648/in-gevent-how-can-i-dump-stack-traces-of-all-running-greenlets @@ -1159,6 +1162,21 @@ else: def html_escape(text): return werkzeug.utils.escape(text) + +def get_lang(env, lang_code=False): + """ + Retrieve the first lang object installed, by checking the parameter lang_code, + the context and then the company. If no lang is installed from those variables, + fallback on the first lang installed in the system. + :param str lang_code: the locale (i.e. en_US) + :return res.lang: the first lang found that is installed on the system. + """ + langs = [code for code, _ in env['res.lang'].get_installed()] + for code in [lang_code, env.context.get('lang'), env.user.company_id.partner_id.lang, langs[0]]: + if code in langs: + return env['res.lang']._lang_get(code) + + def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False, currency_obj=False): """ Assuming 'Account' decimal.precision=3: @@ -1179,9 +1197,7 @@ def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False, if isinstance(value, str) and not value: return '' - langs = [code for code, _ in env['res.lang'].get_installed()] - lang_code = env.context['lang'] if env.context.get('lang') in langs else (env.user.company_id.partner_id.lang or langs[0]) - lang_obj = env['res.lang']._lang_get(lang_code) + lang_obj = get_lang(env) res = lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary) @@ -1218,7 +1234,7 @@ def format_date(env, value, lang_code=False, date_format=False): else: value = odoo.fields.Datetime.from_string(value) - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) if not date_format: date_format = posix_to_ldml(lang.date_format, locale=locale) @@ -1237,7 +1253,7 @@ def parse_date(env, value, lang_code=False): :return: date object from the localized string :rtype: datetime.date ''' - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) try: return babel.dates.parse_date(value, locale=locale) @@ -1268,8 +1284,7 @@ def format_datetime(env, value, tz=False, dt_format='medium', lang_code=False): except Exception: localized_datetime = utc_datetime - lang_code = lang_code or env.context.get('lang') or 'en_US' - lang = env['res.lang']._lang_get(lang_code) + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code or lang_code) # lang can be inactive, so `lang`is empty if not dt_format: @@ -1299,7 +1314,7 @@ def format_time(env, value, tz=False, time_format='medium', lang_code=False): if not value: return '' - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) if not time_format: time_format = posix_to_ldml(lang.time_format, locale=locale) @@ -1307,17 +1322,17 @@ def format_time(env, value, tz=False, time_format='medium', lang_code=False): return babel.dates.format_time(value, format=time_format, locale=locale) -def _format_time_ago(env, time_delta, lang_code=False): +def _format_time_ago(env, time_delta, lang_code=False, add_direction=True): if not lang_code: langs = [code for code, _ in env['res.lang'].get_installed()] lang_code = env.context['lang'] if env.context.get('lang') in langs else (env.user.company_id.partner_id.lang or langs[0]) locale = babel.Locale.parse(lang_code) - return babel.dates.format_timedelta(-time_delta, add_direction=True, locale=locale) + return babel.dates.format_timedelta(-time_delta, add_direction=add_direction, locale=locale) def format_amount(env, amount, currency, lang_code=False): fmt = "%.{0}f".format(currency.decimal_places) - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) formatted_amount = lang.format(fmt, currency.round(amount), grouping=True, monetary=True)\ .replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}') diff --git a/odoo/tools/sql.py b/odoo/tools/sql.py index 30cebe3bcd0..e1e4256dc7f 100644 --- a/odoo/tools/sql.py +++ b/odoo/tools/sql.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +# pylint: disable=sql-injection + import logging import psycopg2 |
