Files
odoo_source/addons/web/controllers/binary.py
T
Yolann Sabaux dc8cf87fc8 [FIX] web: fetch right company_id
Steps to reproduce:
- Have two companies set up
- In settings, check for company 2 the Files Centralization
- For a Product, upload a document

Issue:
The document will not appear in Documents.
It will only appear if the option is checked for company 1

Cause:
The company_id is not fetched correctly throughout the process.
There is a similar solution for the specific `documents` upload route:
https://github.com/odoo/enterprise/blob/bdf712d66c3e5cee70a6b424b69a618fe655a39f/documents/controllers/main.py#L147-L149

Solution:
Get the id directly from the cookies

opw-2774365

closes odoo/odoo#91751

X-original-commit: 46db92d5211c215d07448676e619154390b7147f
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
Signed-off-by: yosa-odoo <yosa@odoo.com>
2022-05-19 13:45:05 +02:00

224 lines
11 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import functools
import io
import json
import logging
import os
import unicodedata
import odoo
import odoo.modules.registry
from odoo import http
from odoo.exceptions import AccessError
from odoo.http import request
from odoo.modules import get_resource_path
from odoo.tools import pycompat
from odoo.tools.mimetypes import guess_mimetype
from odoo.tools.misc import file_open, file_path
from odoo.tools.translate import _
_logger = logging.getLogger(__name__)
def clean(name):
return name.replace('\x3c', '')
class Binary(http.Controller):
@http.route(['/web/content',
'/web/content/<string:xmlid>',
'/web/content/<string:xmlid>/<string:filename>',
'/web/content/<int:id>',
'/web/content/<int:id>/<string:filename>',
'/web/content/<string:model>/<int:id>/<string:field>',
'/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='name', unique=None, mimetype=None,
download=None, data=None, token=None, access_token=None, **kw):
return request.env['ir.http']._get_content_common(xmlid=xmlid, model=model, res_id=id, field=field, unique=unique, filename=filename,
filename_field=filename_field, download=download, mimetype=mimetype, access_token=access_token, token=token)
@http.route(['/web/assets/debug/<string:filename>',
'/web/assets/debug/<path:extra>/<string:filename>',
'/web/assets/<int:id>/<string:filename>',
'/web/assets/<int:id>-<string:unique>/<string:filename>',
'/web/assets/<int:id>-<string:unique>/<path:extra>/<string:filename>'], type='http', auth="public")
def content_assets(self, id=None, filename=None, unique=None, extra=None, **kw):
id = id or request.env['ir.attachment'].sudo().search_read(
[('url', '=like', f'/web/assets/%/{extra}/{filename}' if extra else f'/web/assets/%/{filename}')],
fields=['id'], limit=1)[0]['id']
return request.env['ir.http']._get_content_common(xmlid=None, model='ir.attachment', res_id=id, field='datas', unique=unique, filename=filename,
filename_field='name', download=None, mimetype=None, access_token=None, token=None)
@http.route(['/web/image',
'/web/image/<string:xmlid>',
'/web/image/<string:xmlid>/<string:filename>',
'/web/image/<string:xmlid>/<int:width>x<int:height>',
'/web/image/<string:xmlid>/<int:width>x<int:height>/<string:filename>',
'/web/image/<string:model>/<int:id>/<string:field>',
'/web/image/<string:model>/<int:id>/<string:field>/<string:filename>',
'/web/image/<string:model>/<int:id>/<string:field>/<int:width>x<int:height>',
'/web/image/<string:model>/<int:id>/<string:field>/<int:width>x<int:height>/<string:filename>',
'/web/image/<int:id>',
'/web/image/<int:id>/<string:filename>',
'/web/image/<int:id>/<int:width>x<int:height>',
'/web/image/<int:id>/<int:width>x<int:height>/<string:filename>',
'/web/image/<int:id>-<string:unique>',
'/web/image/<int:id>-<string:unique>/<string:filename>',
'/web/image/<int:id>-<string:unique>/<int:width>x<int:height>',
'/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='raw',
filename_field='name', unique=None, filename=None, mimetype=None,
download=None, width=0, height=0, crop=False, access_token=None,
**kwargs):
# other kwargs are ignored on purpose
return request.env['ir.http']._content_image(xmlid=xmlid, model=model, res_id=id, field=field,
filename_field=filename_field, unique=unique, filename=filename, mimetype=mimetype,
download=download, width=width, height=height, crop=crop,
quality=int(kwargs.get('quality', 0)), access_token=access_token)
# backward compatibility
@http.route(['/web/binary/image'], type='http', auth="public")
def content_image_backward_compatibility(self, model, id, field, resize=None, **kw):
width = None
height = None
if resize:
width, height = resize.split(",")
return request.env['ir.http']._content_image(model=model, res_id=id, field=field, width=width, height=height)
@http.route('/web/binary/upload', type='http', auth="user")
def upload(self, ufile, callback=None):
# TODO: might be useful to have a configuration flag for max-length file uploads
out = """<script language="javascript" type="text/javascript">
var win = window.top.window;
win.jQuery(win).trigger(%s, %s);
</script>"""
try:
data = ufile.read()
args = [len(data), ufile.filename,
ufile.content_type, pycompat.to_text(base64.b64encode(data))]
except Exception as e:
args = [False, str(e)]
return out % (json.dumps(clean(callback)), json.dumps(args)) if callback else json.dumps(args)
@http.route('/web/binary/upload_attachment', type='http', auth="user")
def upload_attachment(self, model, id, ufile, callback=None):
files = request.httprequest.files.getlist('ufile')
Model = request.env['ir.attachment']
out = """<script language="javascript" type="text/javascript">
var win = window.top.window;
win.jQuery(win).trigger(%s, %s);
</script>"""
args = []
for ufile in files:
filename = ufile.filename
if request.httprequest.user_agent.browser == 'safari':
# Safari sends NFD UTF-8 (where é is composed by 'e' and [accent])
# we need to send it the same stuff, otherwise it'll fail
filename = unicodedata.normalize('NFD', ufile.filename)
try:
cids = request.httprequest.cookies.get('cids', str(request.env.user.company_id.id))
allowed_company_ids = [int(cid) for cid in cids.split(',')]
attachment = Model.with_context(allowed_company_ids=allowed_company_ids).create({
'name': filename,
'datas': base64.encodebytes(ufile.read()),
'res_model': model,
'res_id': int(id)
})
attachment._post_add_create()
except AccessError:
args.append({'error': _("You are not allowed to upload an attachment here.")})
except Exception:
args.append({'error': _("Something horrible happened")})
_logger.exception("Fail to upload attachment %s", ufile.filename)
else:
args.append({
'filename': clean(filename),
'mimetype': ufile.content_type,
'id': attachment.id,
'size': attachment.file_size
})
return out % (json.dumps(clean(callback)), json.dumps(args)) if callback else json.dumps(args)
@http.route([
'/web/binary/company_logo',
'/logo',
'/logo.png',
], type='http', auth="none", cors="*")
def company_logo(self, dbname=None, **kw):
imgname = 'logo'
imgext = '.png'
placeholder = functools.partial(get_resource_path, 'web', 'static', 'img')
dbname = request.db
uid = (request.session.uid if dbname else None) or odoo.SUPERUSER_ID
if not dbname:
response = http.send_file(placeholder(imgname + imgext))
else:
try:
# create an empty registry
registry = odoo.modules.registry.Registry(dbname)
with registry.cursor() as cr:
company = int(kw['company']) if kw and kw.get('company') else False
if company:
cr.execute("""SELECT logo_web, write_date
FROM res_company
WHERE id = %s
""", (company,))
else:
cr.execute("""SELECT c.logo_web, c.write_date
FROM res_users u
LEFT JOIN res_company c
ON c.id = u.company_id
WHERE u.id = %s
""", (uid,))
row = cr.fetchone()
if row and row[0]:
image_base64 = base64.b64decode(row[0])
image_data = io.BytesIO(image_base64)
mimetype = guess_mimetype(image_base64, default='image/png')
imgext = '.' + mimetype.split('/')[1]
if imgext == '.svg+xml':
imgext = '.svg'
response = http.send_file(image_data, filename=imgname + imgext, mimetype=mimetype, mtime=row[1])
else:
response = http.send_file(placeholder('nologo.png'))
except Exception:
response = http.send_file(placeholder(imgname + imgext))
return response
@http.route(['/web/sign/get_fonts', '/web/sign/get_fonts/<string:fontname>'], type='json', auth='public')
def get_fonts(self, fontname=None):
"""This route will return a list of base64 encoded fonts.
Those fonts will be proposed to the user when creating a signature
using mode 'auto'.
:return: base64 encoded fonts
:rtype: list
"""
supported_exts = ('.ttf', '.otf', '.woff', '.woff2')
fonts = []
fonts_directory = file_path(os.path.join('web', 'static', 'fonts', 'sign'))
if fontname:
font_path = os.path.join(fonts_directory, fontname)
with file_open(font_path, 'rb', filter_ext=supported_exts) as font_file:
font = base64.b64encode(font_file.read())
fonts.append(font)
else:
font_filenames = sorted([fn for fn in os.listdir(fonts_directory) if fn.endswith(supported_exts)])
for filename in font_filenames:
font_file = file_open(os.path.join(fonts_directory, filename), 'rb', filter_ext=supported_exts)
font = base64.b64encode(font_file.read())
fonts.append(font)
return fonts