def url_for(path_or_uri, lang=None): current_path = request.httprequest.path # should already be text location = pycompat.to_text(path_or_uri).strip() force_lang = lang is not None url = werkzeug.urls.url_parse(location) if not url.netloc and not url.scheme and (url.path or force_lang): location = werkzeug.urls.url_join(current_path, location) lang = pycompat.to_text(lang or request.context.get('lang') or 'en_US') langs = [lg[0] for lg in request.env['ir.http']._get_language_codes()] if (len(langs) > 1 or force_lang) and is_multilang_url(location, langs): ps = location.split(u'/') if ps[1] in langs: # Replace the language only if we explicitly provide a language to url_for if force_lang: ps[1] = lang # Remove the default language unless it's explicitly provided elif ps[1] == request.env['ir.http']._get_default_lang().code: ps.pop(1) # Insert the context language or the provided language elif lang != request.env['ir.http']._get_default_lang().code or force_lang: ps.insert(1, lang) location = u'/'.join(ps) return location
def to_html(self, sep=None, css=True, js=True, debug=False, async_load=False, url_for=(lambda url: url), **kw): if 'async' in kw: _logger.warning( "Using deprecated argument 'async' in to_html call, use 'async_load' instead." ) async_load = kw['async'] nodes = self.to_node(css=css, js=js, debug=debug, async_load=async_load) if sep is None: sep = u'\n ' response = [] for tagName, attributes, content in nodes: html = u"<%s " % tagName for name, value in attributes.items(): if value or isinstance(value, string_types): html += u' %s="%s"' % (name, escape(to_text(value))) if content is None: html += u'/>' else: html += u'>%s</%s>' % (escape(to_text(content)), tagName) response.append(html) return sep + sep.join(response)
def _compile_directive_snippet(self, el, options): el.set('t-call', el.attrib.pop('t-snippet')) name = self.env['ir.ui.view'].search([('key', '=', el.attrib.get('t-call'))]).display_name thumbnail = el.attrib.pop('t-thumbnail', "oe-thumbnail") div = u'<div name="%s" data-oe-type="snippet" data-oe-thumbnail="%s">' % ( escape(pycompat.to_text(name)), escape(pycompat.to_text(thumbnail)) ) return [self._append(ast.Str(div))] + self._compile_node(el, options) + [self._append(ast.Str(u'</div>'))]
def to_html(self): tagName, attributes, content = self.to_node() html = u"<%s " % tagName for name, value in attributes.items(): if value or isinstance(value, string_types): html += u' %s="%s"' % (name, escape(to_text(value))) if content is None: html += u'/>' else: html += u'>%s</%s>' % (escape(to_text(content)), tagName) return html
def record_to_html(self, record, field_name, options): assert options['tagName'] != 'img',\ "Oddly enough, the root tag of an image field can not be img. " \ "That is because the image goes into the tag, or it gets the " \ "hose again." if options.get('qweb_img_raw_data', False): return super(Image, self).record_to_html(record, field_name, options) aclasses = ['img', 'img-fluid'] if options.get('qweb_img_responsive', True) else ['img'] aclasses += options.get('class', '').split() classes = ' '.join(map(escape, aclasses)) src, src_zoom = self._get_src_urls(record, field_name, options) if options.get('alt-field') and getattr(record, options['alt-field'], None): alt = escape(record[options['alt-field']]) elif options.get('alt'): alt = options['alt'] else: alt = escape(record.display_name) itemprop = None if options.get('itemprop'): itemprop = options['itemprop'] atts = OrderedDict() atts["src"] = src atts["itemprop"] = itemprop atts["class"] = classes atts["style"] = options.get('style') atts["alt"] = alt atts["data-zoom"] = src_zoom and u'1' or None atts["data-zoom-image"] = src_zoom atts["data-no-post-process"] = options.get('data-no-post-process') atts = self.env['ir.qweb']._post_processing_att( 'img', atts, options.get('template_options')) img = ['<img'] for name, value in atts.items(): if value: img.append(' ') img.append(escape(pycompat.to_text(name))) img.append('="') img.append(escape(pycompat.to_text(value))) img.append('"') img.append('/>') return u''.join(img)
def to_node(self): if self.url: attr = OrderedDict([ ["type", "text/css"], ["rel", "stylesheet"], ["href", self.html_url], ["media", escape(to_text(self.media)) if self.media else None] ]) return ("link", attr, None) else: attr = OrderedDict([ ["type", "text/css"], ["media", escape(to_text(self.media)) if self.media else None] ]) return ("style", attr, self.with_header())
def _compile_directive_install(self, el, options): if self.user_has_groups('base.group_system'): module = self.env['ir.module.module'].search([ ('name', '=', el.attrib.get('t-install')) ]) if not module or module.state == 'installed': return [] name = el.attrib.get('string') or 'Snippet' thumbnail = el.attrib.pop('t-thumbnail', 'oe-thumbnail') div = u'<div name="%s" data-oe-type="snippet" data-module-id="%s" data-oe-thumbnail="%s"><section/></div>' % ( escape(pycompat.to_text(name)), module.id, escape(pycompat.to_text(thumbnail))) return [self._append(ast.Str(div))] else: return []
def content_common(self, xmlid=None, model='ir.attachment', id=None, field='datas', filename=None, filename_field='datas_fname', mimetype=None, download=None, access_token=None): obj = None if xmlid: obj = request.env.ref(xmlid, False) elif id and model in request.env.registry: obj = request.env[model].browse(int(id)) if not obj or not obj.exists() or field not in obj: return request.not_found() try: last_update = obj['__last_update'] except AccessError: return wrappers.Response(status=403, headers=[]) status, headers, content = None, [], None content = obj.with_context({'stream': True})[field] or b'' if not filename: if filename_field in obj: filename = obj[filename_field] else: filename = "%s-%s-%s" % (obj._name, obj.id, field) mimetype = 'mimetype' in obj and obj.mimetype or False if not mimetype and filename: mimetype = mimetypes.guess_type(filename)[0] headers += [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')] etag = bool(request) and request.httprequest.headers.get('If-None-Match') retag = '"%s"' % hashlib.md5(pycompat.to_text(content).encode('utf-8')).hexdigest() status = status or (304 if etag == retag else 200) headers.append(('ETag', retag)) if download: headers.append(('Content-Disposition', http.content_disposition(filename))) return wrappers.Response(content, headers=headers, direct_passthrough=True, status=status)
def iap_authorize(env, key, account_token, credit, dbuuid=False, description=None, credit_template=None, ttl=4320): endpoint = iap_get_endpoint(env) params = { 'account_token': account_token, 'credit': credit, 'key': key, 'description': description, 'ttl': ttl } if dbuuid: params.update({'dbuuid': dbuuid}) try: transaction_token = iap_jsonrpc(endpoint + '/iap/1/authorize', params=params) except InsufficientCreditError as e: if credit_template: arguments = json.loads(e.args[0]) arguments['body'] = pycompat.to_text( env['ir.qweb']._render(credit_template)) e.args = (json.dumps(arguments), ) raise e return transaction_token
def value_to_html(self, value, options): if not value: return '' lang = self.user_lang() locale = babel.Locale.parse(lang.code) if isinstance(value, pycompat.string_types): value = fields.Datetime.from_string(value) value = fields.Datetime.context_timestamp(self, value) if options and 'format' in options: pattern = options['format'] else: if options and options.get('time_only'): strftime_pattern = (u"%s" % (lang.time_format)) else: strftime_pattern = (u"%s %s" % (lang.date_format, lang.time_format)) pattern = posix_to_ldml(strftime_pattern, locale=locale) if options and options.get('hide_seconds'): pattern = pattern.replace(":ss", "").replace(":s", "") return pycompat.to_text(babel.dates.format_datetime(value, format=pattern, locale=locale))
def value_to_html(self, value, options): """ value_to_html(value, field, options=None) Converts a single value to its HTML version/output :rtype: unicode """ return html_escape(pycompat.to_text(value), options)
def _authenticate(self, conf, login, password): """ Authenticate a user against the specified LDAP server. In order to prevent an unintended 'unauthenticated authentication', which is an anonymous bind with a valid dn and a blank password, check for empty passwords explicitely (:rfc:`4513#section-6.3.1`) :param dict conf: LDAP configuration :param login: username :param password: Password for the LDAP user :return: LDAP entry of authenticated user or False :rtype: dictionary of attributes """ if not password: return False dn, entry = self._get_entry(conf, login) if not dn: return False try: conn = self._connect(conf) conn.simple_bind_s(dn, to_text(password)) conn.unbind() except ldap.INVALID_CREDENTIALS: return False except ldap.LDAPError as e: _logger.error('An LDAP exception occurred: %s', e) return entry
def attributes(self, record, field_name, options, values): attrs = super(Date, self).attributes(record, field_name, options, values) attrs['data-oe-original'] = record[field_name] if record._fields[field_name].type == 'datetime': attrs = self.env['ir.qweb.field.datetime'].attributes( record, field_name, options, values) attrs['data-oe-type'] = 'datetime' return attrs lg = self.env['res.lang']._lang_get(self.env.user.lang) locale = babel.Locale.parse(lg.code) babel_format = value_format = posix_to_ldml(lg.date_format, locale=locale) if record[field_name]: date = fields.Date.from_string(record[field_name]) value_format = pycompat.to_text( babel.dates.format_date(date, format=babel_format, locale=locale)) attrs['data-oe-original-with-format'] = value_format return attrs
def attributes(self, record, field_name, options, values): attrs = super(DateTime, self).attributes(record, field_name, options, values) value = record[field_name] lg = self.env['res.lang']._lang_get(self.env.user.lang) locale = babel.Locale.parse(lg.code) babel_format = value_format = posix_to_ldml( '%s %s' % (lg.date_format, lg.time_format), locale=locale) tz = record.env.context.get('tz') or self.env.user.tz if isinstance(value, pycompat.string_types): value = fields.Datetime.from_string(value) if value: # convert from UTC (server timezone) to user timezone value = fields.Datetime.context_timestamp(self.with_context(tz=tz), timestamp=value) value_format = pycompat.to_text( babel.dates.format_datetime(value, format=babel_format, locale=locale)) value = fields.Datetime.to_string(value) attrs['data-oe-original'] = value attrs['data-oe-original-with-format'] = value_format attrs['data-oe-original-tz'] = tz return attrs
def charge(env, key, account_token, credit, description=None, credit_template=None, dbuuid=False): """ Account charge context manager: takes a hold for ``credit`` amount before executing the body, then captures it if there is no error, or cancels it if the body generates an exception. :param str key: service identifier :param str account_token: user identifier :param int credit: cost of the body's operation :param description: a description of the purpose of the charge, the user will be able to see it in their dashboard :type description: str :param credit_template: a QWeb template to render and show to the user if their account does not have enough credits for the requested operation :type credit_template: str """ endpoint = get_endpoint(env) params = { 'account_token': account_token, 'credit': credit, 'key': key, 'description': description, 'dbuuid': dbuuid, } try: transaction_token = jsonrpc(endpoint + '/iap/1/authorize', params=params) except InsufficientCreditError as e: if credit_template: arguments = json.loads(e.args[0]) arguments['body'] = pycompat.to_text( env['ir.qweb'].render(credit_template)) e.args = (json.dumps(arguments), ) raise e try: transaction = IapTransaction() transaction.credit = credit yield transaction except Exception as e: params = { 'token': transaction_token, 'key': key, } r = jsonrpc(endpoint + '/iap/1/cancel', params=params) raise e else: params = { 'token': transaction_token, 'key': key, 'credit_to_capture': transaction.credit, } r = jsonrpc(endpoint + '/iap/1/capture', params=params) # noqa
def value_to_html(self, value, options): if 'decimal_precision' in options: precision = self.env['decimal.precision'].search([ ('name', '=', options['decimal_precision']) ]).digits else: precision = options['precision'] if precision is None: fmt = '%f' else: value = float_utils.float_round(value, precision_digits=precision) fmt = '%.{precision}f'.format(precision=precision) formatted = self.user_lang().format(fmt, value, grouping=True).replace( r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}') # %f does not strip trailing zeroes. %g does but its precision causes # it to switch to scientific notation starting at a million *and* to # strip decimals. So use %f and if no precision was specified manually # strip trailing 0. if precision is None: formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted) return pycompat.to_text(formatted)
def nl2br(string): """ Converts newlines to HTML linebreaks in ``string``. returns the unicode result :param str string: :rtype: unicode """ return pycompat.to_text(string).replace(u'\n', u'<br>\n')
def fiscal_pos_map_to_csv(self): writer = pycompat.csv_writer(open('account.fiscal.' 'position.tax.template-%s.csv' % self.suffix, 'wb')) fiscal_pos_map_iterator = self.iter_fiscal_pos_map() keys = next(fiscal_pos_map_iterator) writer.writerow(keys) for row in fiscal_pos_map_iterator: writer.writerow([pycompat.to_text(s) for s in row.values()])
def value_to_html(self, value, options): locale = babel.Locale.parse(self.user_lang().code) if isinstance(value, pycompat.string_types): value = fields.Datetime.from_string(value) # value should be a naive datetime in UTC. So is fields.Datetime.now() reference = fields.Datetime.from_string(options['now']) return pycompat.to_text(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
def to_node(self): if self.url: attr = OrderedDict([ ["type", "text/css"], ["rel", "stylesheet"], ["href", self.html_url], ["media", escape(to_text(self.media)) if self.media else None], ['data-asset-xmlid', self.bundle.name], ['data-asset-version', self.bundle.version], ]) return ("link", attr, None) else: attr = OrderedDict([ ["type", "text/css"], ["media", escape(to_text(self.media)) if self.media else None], ['data-asset-xmlid', self.bundle.name], ['data-asset-version', self.bundle.version], ]) return ("style", attr, self.with_header())
def record_to_html(self, record, field_name, options): if not getattr(record, field_name): return None view = getattr(record, field_name) if view._name != "ir.ui.view": _logger.warning("%s.%s must be a 'ir.ui.view' model." % (record, field_name)) return None return pycompat.to_text(view._render(options.get('values', {}), engine='ir.qweb'))
def value_to_html(self, value, options): if not value: return '' options = options or {} lang = self.user_lang() locale = babel_locale_parse(lang.code) format_func = babel.dates.format_datetime if isinstance(value, str): value = fields.Datetime.from_string(value) value = fields.Datetime.context_timestamp(self, value) if options.get('tz_name'): tzinfo = babel.dates.get_timezone(options['tz_name']) else: tzinfo = None if 'format' in options: pattern = options['format'] else: if options.get('time_only'): strftime_pattern = (u"%s" % (lang.time_format)) elif options.get('date_only'): strftime_pattern = (u"%s" % (lang.date_format)) else: strftime_pattern = (u"%s %s" % (lang.date_format, lang.time_format)) pattern = posix_to_ldml(strftime_pattern, locale=locale) if options.get('hide_seconds'): pattern = pattern.replace(":ss", "").replace(":s", "") if options.get('time_only'): format_func = babel.dates.format_time return pycompat.to_text(format_func(value, format=pattern, locale=locale)) if options.get('date_only'): format_func = babel.dates.format_date return pycompat.to_text(format_func(value, format=pattern, locale=locale)) return pycompat.to_text(format_func(value, format=pattern, tzinfo=tzinfo, locale=locale))
def _query(self, conf, filter, retrieve_attributes=None): """ Query an LDAP server with the filter argument and scope subtree. Allow for all authentication methods of the simple authentication method: - authenticated bind (non-empty binddn + valid password) - anonymous bind (empty binddn + empty password) - unauthenticated authentication (non-empty binddn + empty password) .. seealso:: :rfc:`4513#section-5.1` - LDAP: Simple Authentication Method. :param dict conf: LDAP configuration :param filter: valid LDAP filter :param list retrieve_attributes: LDAP attributes to be retrieved. \ If not specified, return all attributes. :return: ldap entries :rtype: list of tuples (dn, attrs) """ results = [] try: conn = self._connect(conf) ldap_password = conf['ldap_password'] or '' ldap_binddn = conf['ldap_binddn'] or '' conn.simple_bind_s(to_text(ldap_binddn), to_text(ldap_password)) results = conn.search_st(to_text(conf['ldap_base']), ldap.SCOPE_SUBTREE, filter, retrieve_attributes, timeout=60) conn.unbind() except ldap.INVALID_CREDENTIALS: _logger.error('LDAP bind failed.') except ldap.LDAPError as e: _logger.error('An LDAP exception occurred: %s', e) return results
def record_to_html(self, record, field_name, options): if not getattr(record, field_name): return None view = getattr(record, field_name) if view._name != "ir.ui.view": _logger.warning("%s.%s must be a 'ir.ui.view' model." % (record, field_name)) return None view = view.with_context(object=record) return pycompat.to_text(view.render(view._context, engine='ir.qweb'))
def url_lang(path_or_uri, lang_code=None): ''' Given a relative URL, make it absolute and add the required lang or remove useless lang. Nothing will be done for absolute URL. If there is only one language installed, the lang will not be handled unless forced with `lang` parameter. :param lang_code: Must be the lang `code`. It could also be something else, such as `'[lang]'` (used for url_return). ''' Lang = request.env['res.lang'] location = pycompat.to_text(path_or_uri).strip() force_lang = lang_code is not None url = werkzeug.urls.url_parse(location) # relative URL with either a path or a force_lang if not url.netloc and not url.scheme and (url.path or force_lang): location = werkzeug.urls.url_join(request.httprequest.path, location) lang_url_codes = [url_code for _, url_code, *_ in Lang.get_available()] lang_code = pycompat.to_text(lang_code or request.context['lang']) lang_url_code = Lang._lang_code_to_urlcode(lang_code) lang_url_code = lang_url_code if lang_url_code in lang_url_codes else lang_code if (len(lang_url_codes) > 1 or force_lang) and is_multilang_url( location, lang_url_codes): ps = location.split(u'/') default_lg = request.env['ir.http']._get_default_lang() if ps[1] in lang_url_codes: # Replace the language only if we explicitly provide a language to url_for if force_lang: ps[1] = lang_url_code # Remove the default language unless it's explicitly provided elif ps[1] == default_lg.url_code: ps.pop(1) # Insert the context language or the provided language elif lang_url_code != default_lg.url_code or force_lang: ps.insert(1, lang_url_code) location = u'/'.join(ps) return location
def _adyen_form_get_tx_from_data(self, data): reference, pspReference = data.get('merchantReference'), data.get( 'pspReference') if not reference or not pspReference: error_msg = _( 'Adyen: received data with missing reference (%s) or missing pspReference (%s)' ) % (reference, pspReference) _logger.info(error_msg) raise ValidationError(error_msg) # find tx -> @TDENOTE use pspReference ? tx = self.env['payment.transaction'].search([('reference', '=', reference)]) if not tx or len(tx) > 1: error_msg = _('Adyen: received data for reference %s') % ( reference) if not tx: error_msg += _('; no order found') else: error_msg += _('; multiple order found') _logger.info(error_msg) raise ValidationError(error_msg) # verify shasign if len(tx.acquirer_id.adyen_skin_hmac_key) == 64: shasign_check = tx.acquirer_id._adyen_generate_merchant_sig_sha256( 'out', data) else: shasign_check = tx.acquirer_id._adyen_generate_merchant_sig( 'out', data) if to_text(shasign_check) != to_text(data.get('merchantSig')): error_msg = _( 'Adyen: invalid merchantSig, received %s, computed %s') % ( data.get('merchantSig'), shasign_check) _logger.warning(error_msg) raise ValidationError(error_msg) return tx
def encode_addr(addr): name, email = addr # If s is a <text string>, then charset is a hint specifying the # character set of the characters in the string. The Unicode string # will be encoded using the following charsets in order: us-ascii, # the charset hint, utf-8. The first character set to not provoke a # UnicodeError is used. # -> always pass a text string to Header # also Header.__str__ in Python 3 "Returns an approximation of the # Header as a string, using an unlimited line length.", the old one # was "A synonym for Header.encode()." so call encode() directly? name = Header(pycompat.to_text(name)).encode() return formataddr((name, email))
def _change_password(self, conf, login, old_passwd, new_passwd): changed = False dn, entry = self._get_entry(conf, login) if not dn: return False try: conn = self._connect(conf) conn.simple_bind_s(dn, to_text(old_passwd)) conn.passwd_s(dn, old_passwd, new_passwd) changed = True conn.unbind() except ldap.INVALID_CREDENTIALS: pass except ldap.LDAPError as e: _logger.error('An LDAP exception occurred: %s', e) return changed
def record_to_html(self, record, field_name, options): assert options['tagName'] != 'img',\ "Oddly enough, the root tag of an image field can not be img. " \ "That is because the image goes into the tag, or it gets the " \ "hose again." if options.get('qweb_img_raw_data', False): return super(Image, self).record_to_html(record, field_name, options) aclasses = ['img', 'img-responsive'] if options.get( 'qweb_img_responsive', True) else ['img'] aclasses += options.get('class', '').split() classes = ' '.join(pycompat.imap(escape, aclasses)) max_size = None if options.get('resize'): max_size = options.get('resize') else: max_width, max_height = options.get('max_width', 0), options.get( 'max_height', 0) if max_width or max_height: max_size = '%sx%s' % (max_width, max_height) sha = hashlib.sha1(getattr( record, '__last_update').encode('utf-8')).hexdigest()[0:7] max_size = '' if max_size is None else '/%s' % max_size src = '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, field_name, max_size, sha) alt = None if options.get('alt-field') and getattr(record, options['alt-field'], None): alt = escape(record[options['alt-field']]) elif options.get('alt'): alt = options['alt'] src_zoom = None if options.get('zoom') and getattr(record, options['zoom'], None): src_zoom = '/web/image/%s/%s/%s%s?unique=%s' % ( record._name, record.id, options['zoom'], max_size, sha) elif options.get('zoom'): src_zoom = options['zoom'] img = '<img class="%s" src="%s" style="%s"%s%s/>' % \ (classes, src, options.get('style', ''), ' alt="%s"' % alt if alt else '', ' data-zoom="1" data-zoom-image="%s"' % src_zoom if src_zoom else '') return pycompat.to_text(img)
def taxes_to_csv(self): writer = pycompat.csv_writer( open('account.tax.template-%s.csv' % self.suffix, 'wb')) taxes_iterator = self.iter_taxes() keys = next(taxes_iterator) writer.writerow(keys[3:] + ['sequence']) seq = 100 for row in sorted(taxes_iterator, key=lambda r: r['description']): if not _is_true(row['active']): continue seq += 1 if row['parent_id:id']: cur_seq = seq + 1000 else: cur_seq = seq writer.writerow( [pycompat.to_text(v) for v in list(row.values())[3:]] + [cur_seq])