From 49d963aafb853bdb65dcc24ff6057e9fb17868d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Wed, 29 Apr 2026 10:02:45 +0200 Subject: [PATCH] [IMP] dms: Upload file and create dms.file directly Related to https://github.com/OCA/dms/pull/470#issuecomment-4212192789 --- dms/controllers/main.py | 57 +++++++++++++- dms/models/dms_file.py | 18 ----- .../src/js/views/dms_file_upload.esm.js | 74 ++++--------------- .../views/dms_list/dms_list_controller.esm.js | 34 ++------- 4 files changed, 77 insertions(+), 106 deletions(-) diff --git a/dms/controllers/main.py b/dms/controllers/main.py index 5898e80fb..638b5141c 100644 --- a/dms/controllers/main.py +++ b/dms/controllers/main.py @@ -1,9 +1,18 @@ # Copyright 2017-2019 MuK IT GmbH +# Copyright 2026 Tecnativa - Víctor Martínez # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -from odoo import http +import json +import unicodedata + +from odoo import _, http +from odoo.exceptions import AccessError from odoo.http import request +def clean(name): + return name.replace("\x3c", "") + + class OnboardingController(http.Controller): @http.route("/config/dms.forbidden_extensions", type="json", auth="user") def forbidden_extensions(self, **_kwargs): @@ -13,3 +22,49 @@ def forbidden_extensions(self, **_kwargs): "dms.forbidden_extensions", default="" ) } + + @http.route("/web/binary/upload_dms_file", type="http", auth="user") + def upload_dms_file(self, ufile, directory_id, callback=None): + """Similar to the web upload_attachment() method, but customized to + directly create dms.file records. + """ + directory_id = int(directory_id) + files = request.httprequest.files.getlist("ufile") + Model = request.env["dms.file"] + out = """""" + 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: + dms_file = Model.create( + { + "directory_id": directory_id, + "name": filename, + "content": ufile.read(), + } + ) + except AccessError: + args.append({"error": _("You are not allowed to upload a file here.")}) + except Exception: + args.append({"error": _("Something horrible happened")}) + else: + args.append( + { + "filename": clean(filename), + "mimetype": dms_file.mimetype, + "id": dms_file.id, + "size": dms_file.size, + } + ) + return ( + out % (json.dumps(clean(callback)), json.dumps(args)) + if callback + else json.dumps(args) + ) diff --git a/dms/models/dms_file.py b/dms/models/dms_file.py index 328ebb434..9ae1f4f3e 100644 --- a/dms/models/dms_file.py +++ b/dms/models/dms_file.py @@ -648,21 +648,3 @@ def get_attachment_object(self, attachment): "res_model": attachment.res_model, "mimetype": attachment.mimetype, } - - @api.model - def get_dms_files_from_attachments(self, attachment_ids=None): - """Get the dms files from uploaded attachments. - :return: An Array of dms files. - """ - if not attachment_ids: - raise UserError(_("No attachment was provided")) - - attachments = self.env["ir.attachment"].browse(attachment_ids) - - if any( - attachment.res_id or attachment.res_model != "dms.file" - for attachment in attachments - ): - raise UserError(_("Invalid attachments!")) - - return [self.get_attachment_object(attachment) for attachment in attachments] diff --git a/dms/static/src/js/views/dms_file_upload.esm.js b/dms/static/src/js/views/dms_file_upload.esm.js index fbccce06e..cf67e63c4 100644 --- a/dms/static/src/js/views/dms_file_upload.esm.js +++ b/dms/static/src/js/views/dms_file_upload.esm.js @@ -80,37 +80,8 @@ export function createFileUploadExtension() { }, async onChangeFileInput() { - const params = { - csrf_token: odoo.csrf_token, - ufile: [...this.fileInput.el.files], - model: "dms.file", - id: 0, - }; - - const fileData = await this.http.post( - "/web/binary/upload_attachment", - params, - "text" - ); - const attachments = JSON.parse(fileData); - if (attachments.error) { - throw new Error(attachments.error); - } - - await this.onUpload(attachments); - }, - - async onUpload(attachments) { const self = this; - const attachmentIds = attachments.map((a) => a.id); - const ctx = this.props.context; const controllerID = this.actionService.currentController.jsId; - - if (!attachmentIds.length) { - this.notification.add(_t("An error occurred during the upload")); - return; - } - // Search the correct directory_id value according to the domain let directory_id = false; if (this.props.domain) { @@ -133,37 +104,22 @@ export function createFileUploadExtension() { }); } - const attachment_datas = await this.orm.call( - "dms.file", - "get_dms_files_from_attachments", - [], - {attachment_ids: attachmentIds} - ); - - const attachments_args = []; - - attachment_datas.forEach((attachment_data) => { - attachments_args.push({ - name: attachment_data.name, - content: attachment_data.datas, - mimetype: attachment_data.mimetype, - directory_id, - }); - }); + const params = { + csrf_token: odoo.csrf_token, + ufile: [...this.fileInput.el.files], + directory_id: directory_id, + }; - this.orm - .call("dms.file", "create", [attachments_args], { - context: ctx, - }) - .then(() => { - self.actionService.restore(controllerID); - }) - .catch((error) => { - self.notification.add(error.data.message, { - type: "danger", - }); - self.actionService.restore(controllerID); - }); + const fileData = await this.http.post( + "/web/binary/upload_dms_file", + params, + "text" + ); + const attachments = JSON.parse(fileData); + if (attachments.error) { + throw new Error(attachments.error); + } + self.actionService.restore(controllerID); }, }; } diff --git a/dms_field/static/src/views/dms_list/dms_list_controller.esm.js b/dms_field/static/src/views/dms_list/dms_list_controller.esm.js index 04bd1c9fe..60edf7ecc 100644 --- a/dms_field/static/src/views/dms_list/dms_list_controller.esm.js +++ b/dms_field/static/src/views/dms_list/dms_list_controller.esm.js @@ -490,40 +490,18 @@ export function getDMSListControllerObject() { const params = { csrf_token: odoo.csrf_token, ufile: [...files], - model: "dms.file", - id: 0, + directory_id: directoryId, }; const fileData = await this.http.post( - "/web/binary/upload_attachment", + "/web/binary/upload_dms_file", params, "text" ); - const attachments = JSON.parse(fileData); - if (attachments.error) { - throw new Error(attachments.error); + const dms_files = JSON.parse(fileData); + if (dms_files.error) { + throw new Error(dms_files.error); } - const attachmentIds = attachments.map((a) => a.id); - if (!attachmentIds.length) { - return "no_attachments"; - } - const attachment_datas = await this.orm.call( - "dms.file", - "get_dms_files_from_attachments", - [], - { - attachment_ids: attachmentIds, - } - ); - const attachments_args = []; - attachment_datas.forEach((attachment_data) => { - attachments_args.push({ - name: attachment_data.name, - content: attachment_data.datas, - mimetype: attachment_data.mimetype, - directory_id: directoryId, - }); - }); - return this.orm.call("dms.file", "create", [attachments_args]); + return dms_files.map((a) => a.id); }, }; }