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>
224 lines
11 KiB
Python
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
|