[REF] base, *: refactor binary_content
*: tools, web, website, website_forum, mail, im_livechat This commit refactors ir_http to make it more readable and flexible. Move the resize function of web/image to odoo.tools Co-authored-by: XavierDo <xdo@odoo.com> Co-authored-by: Antony Lesuisse <al@openerp.com> closes: #28563 task: #1908896
This commit is contained in:
co-authored by
XavierDo
Antony Lesuisse
parent
89f20d2c29
commit
7d85ab1eac
@@ -6,7 +6,6 @@ import base64
|
||||
from odoo import http, _
|
||||
from odoo.http import request
|
||||
from odoo.addons.base.models.assetsbundle import AssetsBundle
|
||||
from odoo.addons.web.controllers.main import binary_content
|
||||
|
||||
|
||||
class LivechatController(http.Controller):
|
||||
@@ -22,7 +21,7 @@ class LivechatController(http.Controller):
|
||||
if isinstance(mock_attachment, list): # suppose that CSS asset will not required to be split in pages
|
||||
mock_attachment = mock_attachment[0]
|
||||
# can't use /web/content directly because we don't have attachment ids (attachments must be created)
|
||||
status, headers, content = binary_content(id=mock_attachment.id, unique=asset.checksum)
|
||||
status, headers, content = request.env['ir.http'].binary_content(id=mock_attachment.id, unique=asset.checksum)
|
||||
content_base64 = base64.b64decode(content) if content else ''
|
||||
headers.append(('Content-Length', len(content_base64)))
|
||||
return request.make_response(content_base64, headers)
|
||||
|
||||
@@ -9,7 +9,6 @@ import werkzeug
|
||||
from werkzeug import url_encode
|
||||
|
||||
from odoo import api, http, registry, SUPERUSER_ID, _
|
||||
from odoo.addons.web.controllers.main import binary_content
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.http import request
|
||||
from odoo.tools import consteq
|
||||
@@ -216,7 +215,8 @@ class MailController(http.Controller):
|
||||
# if the current user has access to the document, get the partner avatar as sudo()
|
||||
request.env[res_model].browse(res_id).check_access_rule('read')
|
||||
if partner_id in request.env[res_model].browse(res_id).sudo().exists().message_ids.mapped('author_id').ids:
|
||||
status, headers, _content = binary_content(model='res.partner', id=partner_id, field='image_medium', default_mimetype='image/png', env=request.env(user=SUPERUSER_ID))
|
||||
status, headers, _content = request.env['ir.http'].sudo().binary_content(
|
||||
model='res.partner', id=partner_id, field='image_medium', default_mimetype='image/png')
|
||||
# binary content return an empty string and not a placeholder if obj[field] is False
|
||||
if _content != '':
|
||||
content = _content
|
||||
|
||||
@@ -36,7 +36,7 @@ import odoo
|
||||
import odoo.modules.registry
|
||||
from odoo.api import call_kw, Environment
|
||||
from odoo.modules import get_resource_path
|
||||
from odoo.tools import crop_image, topological_sort, html_escape, pycompat
|
||||
from odoo.tools import limited_image_resize, topological_sort, html_escape, pycompat
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from odoo.tools.translate import _
|
||||
from odoo.tools.misc import str2bool, xlwt, file_open
|
||||
@@ -424,16 +424,6 @@ def xml2json_from_elementtree(el, preserve_whitespaces=False):
|
||||
res["children"] = kids
|
||||
return res
|
||||
|
||||
def binary_content(xmlid=None, model='ir.attachment', id=None, field='datas', unique=False,
|
||||
filename=None, filename_field='datas_fname', download=False, mimetype=None,
|
||||
default_mimetype='application/octet-stream', related_id=None, access_mode=None, access_token=None,
|
||||
env=None):
|
||||
return request.registry['ir.http'].binary_content(
|
||||
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
|
||||
filename_field=filename_field, download=download, mimetype=mimetype,
|
||||
default_mimetype=default_mimetype, related_id=related_id, access_mode=access_mode, access_token=access_token,
|
||||
env=env)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Odoo Web web Controllers
|
||||
#----------------------------------------------------------
|
||||
@@ -1029,18 +1019,14 @@ class Binary(http.Controller):
|
||||
'/web/content/<string:model>/<int:id>/<string:field>/<string:filename>'], type='http', auth="public")
|
||||
def content_common(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
filename=None, filename_field='datas_fname', unique=None, mimetype=None,
|
||||
download=None, data=None, token=None, access_token=None, related_id=None, access_mode=None,
|
||||
**kw):
|
||||
status, headers, content = binary_content(
|
||||
download=None, data=None, token=None, access_token=None, **kw):
|
||||
|
||||
status, headers, content = request.env['ir.http'].binary_content(
|
||||
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
|
||||
filename_field=filename_field, download=download, mimetype=mimetype,
|
||||
access_token=access_token, related_id=related_id, access_mode=access_mode)
|
||||
if status == 304:
|
||||
response = werkzeug.wrappers.Response(status=status, headers=headers)
|
||||
elif status == 301:
|
||||
return werkzeug.utils.redirect(content, code=301)
|
||||
elif status != 200:
|
||||
response = request.not_found()
|
||||
filename_field=filename_field, download=download, mimetype=mimetype, access_token=access_token)
|
||||
|
||||
if status != 200:
|
||||
return request.env['ir.http']._response_by_status(status, headers, content)
|
||||
else:
|
||||
content_base64 = base64.b64decode(content)
|
||||
headers.append(('Content-Length', len(content_base64)))
|
||||
@@ -1068,39 +1054,18 @@ class Binary(http.Controller):
|
||||
'/web/image/<int:id>-<string:unique>/<int:width>x<int:height>/<string:filename>'], type='http', auth="public")
|
||||
def content_image(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
filename_field='datas_fname', unique=None, filename=None, mimetype=None,
|
||||
download=None, width=0, height=0, crop=False, related_id=None, access_mode=None,
|
||||
access_token=None, avoid_if_small=False, upper_limit=False, signature=False):
|
||||
status, headers, content = binary_content(
|
||||
download=None, width=0, height=0, crop=False, access_token=None, avoid_if_small=False,
|
||||
upper_limit=False, **kw):
|
||||
status, headers, content = request.env['ir.http'].binary_content(
|
||||
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
|
||||
filename_field=filename_field, download=download, mimetype=mimetype,
|
||||
default_mimetype='image/png', related_id=related_id, access_mode=access_mode, access_token=access_token)
|
||||
if status == 304:
|
||||
return werkzeug.wrappers.Response(status=304, headers=headers)
|
||||
elif status == 301:
|
||||
return werkzeug.utils.redirect(content, code=301)
|
||||
elif status != 200 and download:
|
||||
return request.not_found()
|
||||
default_mimetype='image/png', access_token=access_token)
|
||||
|
||||
if headers and dict(headers).get('Content-Type', '') == 'image/svg+xml': # we shan't resize svg images
|
||||
height = 0
|
||||
width = 0
|
||||
else:
|
||||
height = int(height or 0)
|
||||
width = int(width or 0)
|
||||
if status != 200 and download:
|
||||
return request.env['ir.http']._response_by_status(status, headers, content)
|
||||
|
||||
if crop and (width or height):
|
||||
content = crop_image(content, type='center', size=(width, height), ratio=(1, 1))
|
||||
|
||||
elif content and (width or height):
|
||||
if not upper_limit:
|
||||
# resize maximum 500*500
|
||||
if width > 500:
|
||||
width = 500
|
||||
if height > 500:
|
||||
height = 500
|
||||
content = odoo.tools.image_resize_image(base64_source=content, size=(width or None, height or None),
|
||||
encoding='base64', upper_limit=upper_limit,
|
||||
avoid_if_small=avoid_if_small)
|
||||
content = limited_image_resize(
|
||||
content, width=width, height=height, crop=crop, upper_limit=upper_limit, avoid_if_small=avoid_if_small)
|
||||
|
||||
if content:
|
||||
image_base64 = base64.b64decode(content)
|
||||
|
||||
@@ -237,25 +237,22 @@ class Http(models.AbstractModel):
|
||||
html = request.env['ir.ui.view'].render_template('website.http_error', values)
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
|
||||
@classmethod
|
||||
def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
unique=False, filename=None, filename_field='datas_fname', download=False,
|
||||
mimetype=None, default_mimetype='application/octet-stream',
|
||||
access_token=None, related_id=None, access_mode=None, env=None):
|
||||
env = env or request.env
|
||||
access_token=None):
|
||||
obj = None
|
||||
if xmlid:
|
||||
obj = cls._xmlid_to_obj(env, xmlid)
|
||||
elif id and model in env:
|
||||
obj = env[model].browse(int(id))
|
||||
obj = self._xmlid_to_obj(self.env, xmlid)
|
||||
elif id and model in self.env:
|
||||
obj = self.env[model].browse(int(id))
|
||||
if obj and 'website_published' in obj._fields:
|
||||
if env[obj._name].sudo().search([('id', '=', obj.id), ('website_published', '=', True)]):
|
||||
env = env(user=SUPERUSER_ID)
|
||||
return super(Http, cls).binary_content(
|
||||
if self.env[obj._name].sudo().search([('id', '=', obj.id), ('website_published', '=', True)]):
|
||||
self.sudo()
|
||||
return super(Http, self).binary_content(
|
||||
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
|
||||
filename_field=filename_field, download=download, mimetype=mimetype,
|
||||
default_mimetype=default_mimetype, access_token=access_token, related_id=related_id,
|
||||
access_mode=access_mode, env=env)
|
||||
default_mimetype=default_mimetype, access_token=access_token)
|
||||
|
||||
@classmethod
|
||||
def _xmlid_to_obj(cls, env, xmlid):
|
||||
|
||||
@@ -12,7 +12,6 @@ from datetime import datetime
|
||||
|
||||
from odoo import http, modules, SUPERUSER_ID, tools, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.addons.web.controllers.main import binary_content
|
||||
from odoo.addons.website.models.ir_http import sitemap_qs2dom
|
||||
from odoo.http import request
|
||||
|
||||
@@ -591,7 +590,8 @@ class WebsiteForum(http.Controller):
|
||||
|
||||
@http.route(['/forum/user/<int:user_id>/avatar'], type='http', auth="public", website=True, sitemap=False)
|
||||
def user_avatar(self, user_id=0, **post):
|
||||
status, headers, content = binary_content(model='res.users', id=user_id, field='image_medium', default_mimetype='image/png', env=request.env(user=SUPERUSER_ID))
|
||||
status, headers, content = request.env['ir.http'].sudo().binary_content(
|
||||
model='res.users', id=user_id, field='image_medium', default_mimetype='image/png')
|
||||
|
||||
if not content:
|
||||
img_path = modules.get_module_resource('web', 'static/src/img', 'placeholder.png')
|
||||
|
||||
+131
-136
@@ -74,6 +74,10 @@ class IrHttp(models.AbstractModel):
|
||||
_name = 'ir.http'
|
||||
_description = "HTTP Routing"
|
||||
|
||||
#------------------------------------------------------
|
||||
# Routing map
|
||||
#------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
def _get_converters(cls):
|
||||
return {'model': ModelConverter, 'models': ModelsConverter, 'int': SignedIntConverter}
|
||||
@@ -236,41 +240,124 @@ class IrHttp(models.AbstractModel):
|
||||
if hasattr(cls, '_routing_map'):
|
||||
del cls._routing_map
|
||||
|
||||
@classmethod
|
||||
def content_disposition(cls, filename):
|
||||
return content_disposition(filename)
|
||||
#------------------------------------------------------
|
||||
# Binary server
|
||||
#------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
def _xmlid_to_obj(cls, env, xmlid):
|
||||
return env.ref(xmlid, False)
|
||||
|
||||
@classmethod
|
||||
def _check_access_mode(cls, env, id, access_mode, model, access_token=None, related_id=None):
|
||||
"""
|
||||
Implemented by each module to define an additional way to check access.
|
||||
def _get_record_and_check(self, xmlid=None, model=None, id=None, field='datas', access_token=None):
|
||||
# get object and content
|
||||
record = None
|
||||
if xmlid:
|
||||
record = self._xmlid_to_obj(self.env, xmlid)
|
||||
elif id and model in self.env:
|
||||
record = self.env[model].browse(int(id))
|
||||
|
||||
:param env: the env of binary_content
|
||||
:param id: id of the record from which to fetch the binary
|
||||
:param access_mode: typically a string that describes the behaviour of the custom check
|
||||
:param model: the model of the object for which binary_content was called
|
||||
:param related_id: optional id to check security.
|
||||
:return: True if the test passes, else False.
|
||||
"""
|
||||
return False
|
||||
# obj exists
|
||||
if not record or not record.exists() or field not in record:
|
||||
return None, 404
|
||||
|
||||
# access token grant access
|
||||
if model == 'ir.attachment' and access_token:
|
||||
record = record.sudo()
|
||||
if not consteq(record.access_token or '', access_token):
|
||||
return None, 403
|
||||
|
||||
# check read access
|
||||
try:
|
||||
last_update = record['__last_update']
|
||||
except AccessError:
|
||||
return None, 403
|
||||
|
||||
return record, 200
|
||||
|
||||
@classmethod
|
||||
def _get_special_models(cls):
|
||||
"""
|
||||
:return: the set of models that have to pass through several specific fields checks:
|
||||
used for the 'url' and the 'access_token' fields.
|
||||
"""
|
||||
return {'ir.attachment'}
|
||||
def _binary_ir_attachment_redirect_content(cls, record, default_mimetype='application/octet-stream'):
|
||||
# mainly used for theme images attachemnts
|
||||
status = content = filename = mimetype = filehash = None
|
||||
if record.type == 'url' and record.url:
|
||||
# if url in in the form /somehint server locally
|
||||
url_match = re.match("^/(\w+)/(.+)$", record.url)
|
||||
if url_match:
|
||||
module = url_match.group(1)
|
||||
module_path = get_module_path(module)
|
||||
module_resource_path = get_resource_path(module, url_match.group(2))
|
||||
|
||||
@classmethod
|
||||
def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
if module_path and module_resource_path:
|
||||
module_path = os.path.join(os.path.normpath(module_path), '') # join ensures the path ends with '/'
|
||||
module_resource_path = os.path.normpath(module_resource_path)
|
||||
if module_resource_path.startswith(module_path):
|
||||
with open(module_resource_path, 'rb') as f:
|
||||
content = base64.b64encode(f.read())
|
||||
status = 200
|
||||
filename = os.path.basename(module_resource_path)
|
||||
mimetype = guess_mimetype(base64.b64decode(content), default=default_mimetype)
|
||||
filehash = '"%s"' % hashlib.md5(pycompat.to_text(content).encode('utf-8')).hexdigest()
|
||||
else:
|
||||
status = 301
|
||||
content = record.url
|
||||
|
||||
|
||||
|
||||
return status, content, filename, mimetype, filehash
|
||||
|
||||
def _binary_record_content(
|
||||
self, record, field='datas', filename=None,
|
||||
filename_field='datas_fname', default_mimetype='application/octet-stream'):
|
||||
|
||||
model = record._name
|
||||
mimetype = 'mimetype' in record and record.mimetype or False
|
||||
content = None
|
||||
filehash = 'checksum' in record and record['checksum'] or False
|
||||
|
||||
field_def = record._fields[field]
|
||||
if field_def.type == 'binary' and field_def.attachment:
|
||||
field_attachment = self.env['ir.attachment'].search_read(domain=[('res_model', '=', model), ('res_id', '=', record.id), ('res_field', '=', field)], fields=['datas', 'mimetype', 'checksum'], limit=1)
|
||||
if field_attachment:
|
||||
mimetype = field_attachment[0]['mimetype']
|
||||
content = field_attachment[0]['datas']
|
||||
filehash = field_attachment[0]['checksum']
|
||||
|
||||
if not content:
|
||||
content = record[field] or ''
|
||||
|
||||
# filename
|
||||
if not filename:
|
||||
if filename_field in record:
|
||||
filename = record[filename_field]
|
||||
else:
|
||||
filename = "%s-%s-%s" % (record._name, record.id, field)
|
||||
|
||||
if not mimetype:
|
||||
mimetype = guess_mimetype(base64.b64decode(content), default=default_mimetype)
|
||||
|
||||
if not filehash:
|
||||
filehash = '"%s"' % hashlib.md5(pycompat.to_text(content).encode('utf-8')).hexdigest()
|
||||
|
||||
status = 200 if content else 404
|
||||
return status, content, filename, mimetype, filehash
|
||||
|
||||
def _binary_set_headers(self, status, content, filename, mimetype, unique, filehash=None, download=False):
|
||||
headers = [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
|
||||
# cache
|
||||
etag = bool(request) and request.httprequest.headers.get('If-None-Match')
|
||||
status = status or (304 if filehash and etag == filehash else 200)
|
||||
if filehash:
|
||||
headers.append(('ETag', filehash))
|
||||
headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
|
||||
# content-disposition default name
|
||||
if download:
|
||||
headers.append(('Content-Disposition', content_disposition(filename)))
|
||||
|
||||
return (status, headers, content)
|
||||
|
||||
def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
unique=False, filename=None, filename_field='datas_fname', download=False,
|
||||
mimetype=None, default_mimetype='application/octet-stream',
|
||||
access_token=None, related_id=None, access_mode=None, env=None):
|
||||
access_token=None):
|
||||
""" Get file, attachment or downloadable content
|
||||
|
||||
If the ``xmlid`` and ``id`` parameter is omitted, fetches the default value for the
|
||||
@@ -286,126 +373,34 @@ class IrHttp(models.AbstractModel):
|
||||
:param str filename_field: if not create an filename with model-id-field
|
||||
:param bool download: apply headers to download the file
|
||||
:param str mimetype: mintype of the field (for headers)
|
||||
:param related_id: the id of another record used for custom_check
|
||||
:param access_mode: if truthy, will call custom_check to fetch the object that contains the binary.
|
||||
:param str default_mimetype: default mintype if no mintype found
|
||||
:param str access_token: optional token for unauthenticated access
|
||||
only available for ir.attachment
|
||||
:param Environment env: by default use request.env
|
||||
:returns: (status, headers, content)
|
||||
"""
|
||||
env = env or request.env
|
||||
attachment_models = cls._get_special_models()
|
||||
# get object and content
|
||||
obj = None
|
||||
if xmlid:
|
||||
obj = cls._xmlid_to_obj(env, xmlid)
|
||||
elif id and model in env.registry:
|
||||
obj = env[model].browse(int(id))
|
||||
# obj exists
|
||||
if not obj or not obj.exists() or field not in obj:
|
||||
return (404, [], None)
|
||||
record, status = self._get_record_and_check(xmlid=xmlid, model=model, id=id, field=field, access_token=access_token)
|
||||
|
||||
# access token grant access
|
||||
if model in attachment_models and access_token:
|
||||
obj = obj.sudo()
|
||||
if access_mode:
|
||||
if not cls._check_access_mode(env, id, access_mode, model, access_token=access_token,
|
||||
related_id=related_id):
|
||||
return (403, [], None)
|
||||
elif not consteq(obj.access_token or u'', access_token):
|
||||
return (403, [], None)
|
||||
if not record:
|
||||
return (status or 404, [], None)
|
||||
|
||||
# check read access
|
||||
try:
|
||||
last_update = obj['__last_update']
|
||||
except AccessError:
|
||||
return (403, [], None)
|
||||
content, headers, status = None, [], None
|
||||
|
||||
status, headers, content = None, [], None
|
||||
if record._name == 'ir.attachment':
|
||||
status, content, filename, mimetype, filehash = self._binary_ir_attachment_redirect_content(record, default_mimetype=default_mimetype)
|
||||
if not content:
|
||||
status, content, filename, mimetype, filehash = self._binary_record_content(
|
||||
record, field=field, filename=None, filename_field=filename_field,
|
||||
default_mimetype='application/octet-stream')
|
||||
|
||||
# attachment by url check
|
||||
module_resource_path = None
|
||||
if model in attachment_models and obj.type == 'url' and obj.url:
|
||||
url_match = re.match("^/(\w+)/(.+)$", obj.url)
|
||||
if url_match:
|
||||
module = url_match.group(1)
|
||||
module_path = get_module_path(module)
|
||||
module_resource_path = get_resource_path(module, url_match.group(2))
|
||||
if module_path and module_resource_path:
|
||||
module_path = os.path.join(os.path.normpath(module_path), '') # join ensures the path ends with '/'
|
||||
module_resource_path = os.path.normpath(module_resource_path)
|
||||
if module_resource_path.startswith(module_path):
|
||||
with open(module_resource_path, 'rb') as f:
|
||||
content = base64.b64encode(f.read())
|
||||
last_update = str(os.path.getmtime(module_resource_path))
|
||||
status, headers, content = self._binary_set_headers(
|
||||
status, content, filename, mimetype, unique, filehash=False, download=download)
|
||||
|
||||
if not module_resource_path:
|
||||
module_resource_path = obj.url
|
||||
return status, headers, content
|
||||
|
||||
if not content:
|
||||
status = 301
|
||||
content = module_resource_path
|
||||
else:
|
||||
content = obj[field] or ''
|
||||
|
||||
# filename
|
||||
if not filename:
|
||||
if filename_field in obj:
|
||||
filename = obj[filename_field]
|
||||
elif module_resource_path:
|
||||
filename = os.path.basename(module_resource_path)
|
||||
else:
|
||||
filename = "%s-%s-%s" % (obj._name, obj.id, field)
|
||||
|
||||
# mimetype
|
||||
mimetype = 'mimetype' in obj and obj.mimetype or False
|
||||
if not mimetype:
|
||||
if filename:
|
||||
mimetype = mimetypes.guess_type(filename)[0]
|
||||
if not mimetype and getattr(env[model]._fields[field], 'attachment', False):
|
||||
# for binary fields, fetch the ir_attachement for mimetype check
|
||||
attach_mimetype = env['ir.attachment'].search_read(domain=[('res_model', '=', model), ('res_id', '=', id), ('res_field', '=', field)], fields=['mimetype'], limit=1)
|
||||
mimetype = attach_mimetype and attach_mimetype[0]['mimetype']
|
||||
if not mimetype:
|
||||
mimetype = guess_mimetype(base64.b64decode(content), default=default_mimetype)
|
||||
|
||||
headers += [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
|
||||
|
||||
# cache
|
||||
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))
|
||||
headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
|
||||
|
||||
# content-disposition default name
|
||||
if download:
|
||||
headers.append(('Content-Disposition', cls.content_disposition(filename)))
|
||||
return (status, headers, content)
|
||||
|
||||
|
||||
def convert_exception_to(to_type, with_message=False):
|
||||
""" Should only be called from an exception handler. Fetches the current
|
||||
exception data from sys.exc_info() and creates a new exception of type
|
||||
``to_type`` with the original traceback.
|
||||
|
||||
If ``with_message`` is ``True``, sets the new exception's message to be
|
||||
the stringification of the original exception. If ``False``, does not
|
||||
set the new exception's message. Otherwise, uses ``with_message`` as the
|
||||
new exception's message.
|
||||
|
||||
:type with_message: str|bool
|
||||
"""
|
||||
etype, original, tb = sys.exc_info()
|
||||
try:
|
||||
if with_message is False:
|
||||
message = None
|
||||
elif with_message is True:
|
||||
message = str(original)
|
||||
else:
|
||||
message = str(with_message)
|
||||
|
||||
raise pycompat.reraise(to_type, to_type(message), tb)
|
||||
except to_type as e:
|
||||
return e
|
||||
def _response_by_status(self, status, headers, content):
|
||||
if status == 304:
|
||||
return werkzeug.wrappers.Response(status=status, headers=headers)
|
||||
elif status == 301:
|
||||
return werkzeug.utils.redirect(content, code=301)
|
||||
elif status != 200:
|
||||
return request.not_found()
|
||||
|
||||
@@ -19,7 +19,6 @@ class test_ir_http_mimetype(common.TransactionCase):
|
||||
id=attachment.id,
|
||||
mimetype=None,
|
||||
default_mimetype='application/octet-stream',
|
||||
env=self.env
|
||||
)
|
||||
mimetype = dict(headers).get('Content-Type')
|
||||
self.assertEqual(mimetype, 'image/gif')
|
||||
@@ -35,7 +34,6 @@ class test_ir_http_mimetype(common.TransactionCase):
|
||||
id=attachment.id,
|
||||
mimetype=None,
|
||||
default_mimetype='application/octet-stream',
|
||||
env=self.env
|
||||
)
|
||||
mimetype = dict(headers).get('Content-Type')
|
||||
# TODO: fix and change it in master, should be image/gif
|
||||
@@ -53,7 +51,6 @@ class test_ir_http_mimetype(common.TransactionCase):
|
||||
id=partner.id,
|
||||
field='image',
|
||||
default_mimetype='application/octet-stream',
|
||||
env=self.env
|
||||
)
|
||||
mimetype = dict(headers).get('Content-Type')
|
||||
self.assertEqual(mimetype, 'image/gif')
|
||||
@@ -75,7 +72,6 @@ class test_ir_http_mimetype(common.TransactionCase):
|
||||
id=prop.id,
|
||||
field='value_binary',
|
||||
default_mimetype='application/octet-stream',
|
||||
env=self.env
|
||||
)
|
||||
mimetype = dict(headers).get('Content-Type')
|
||||
self.assertEqual(mimetype, 'image/gif')
|
||||
@@ -92,11 +88,10 @@ class test_ir_http_mimetype(common.TransactionCase):
|
||||
defaults = {
|
||||
'id': attachment.id,
|
||||
'default_mimetype': 'image/gif',
|
||||
'env': public_user.sudo(public_user.id).env,
|
||||
}
|
||||
|
||||
def test_access(**kwargs):
|
||||
status, _, _ = self.env['ir.http'].binary_content(
|
||||
status, _, _ = self.env['ir.http'].sudo(public_user.id).binary_content(
|
||||
**dict(defaults, **kwargs)
|
||||
)
|
||||
return status
|
||||
|
||||
+26
-5
@@ -75,11 +75,9 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
||||
asked_width, asked_height = size
|
||||
if upper_limit:
|
||||
if asked_width:
|
||||
if asked_width >= image.size[0]:
|
||||
asked_width = image.size[0]
|
||||
asked_width = min(asked_width, image.size[0])
|
||||
if asked_height:
|
||||
if asked_height >= image.size[1]:
|
||||
asked_height = image.size[1]
|
||||
asked_height = min(asked_height, image.size[1])
|
||||
|
||||
if image.size[0] >= image.size[1]:
|
||||
asked_height = None
|
||||
@@ -187,7 +185,7 @@ def image_resize_image_small(base64_source, size=(64, 64), encoding='base64', fi
|
||||
# ----------------------------------------
|
||||
# Crop Image
|
||||
# ----------------------------------------
|
||||
def crop_image(data, type='top', ratio=False, size=None, image_format=None):
|
||||
def crop_image(data, type='top', ratio=None, size=None, image_format=None):
|
||||
""" Used for cropping image and create thumbnail
|
||||
:param data: base64 data of image.
|
||||
:param type: Used for cropping position possible
|
||||
@@ -319,6 +317,29 @@ def image_resize_images(vals, big_name='image', medium_name='image_medium', smal
|
||||
elif big_name in vals or medium_name in vals or small_name in vals:
|
||||
vals[big_name] = vals[medium_name] = vals[small_name] = False
|
||||
|
||||
def limited_image_resize(content, width=None, height=None, crop=False, upper_limit=False, avoid_if_small=False):
|
||||
"""
|
||||
:param content: bytes (should be an image)
|
||||
"""
|
||||
if content:
|
||||
signatures = [b'\xFF\xD8\xFF', b'\x89PNG\r\n\x1A\n']
|
||||
decoded_content = base64.b64decode(content)
|
||||
is_image = any(decoded_content.startswith(signature) for signature in signatures)
|
||||
if (width or height) and is_image:
|
||||
height = int(height or 0)
|
||||
width = int(width or 0)
|
||||
if crop:
|
||||
return crop_image(content, type='center', size=(width, height), ratio=(1, 1))
|
||||
else:
|
||||
if not upper_limit:
|
||||
# resize maximum 500*500
|
||||
width = min(width, 500)
|
||||
height = min(height, 500)
|
||||
return image_resize_image(
|
||||
base64_source=content, size=(width or None, height or None), encoding='base64', upper_limit=upper_limit,
|
||||
avoid_if_small=avoid_if_small)
|
||||
return content
|
||||
|
||||
def image_data_uri(base64_source):
|
||||
"""This returns data URL scheme according RFC 2397
|
||||
(https://tools.ietf.org/html/rfc2397) for all kind of supported images
|
||||
|
||||
Reference in New Issue
Block a user