# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from __future__ import annotations import base64 import json import os from typing import TYPE_CHECKING from odoo import _, models from odoo.exceptions import UserError, ValidationError if TYPE_CHECKING: from odoo.addons.survey_extra_fields.models.survey_question import SurveyQuestion class SurveyUserInput(models.Model): _inherit = "survey.user_input" def _save_lines( self, question: SurveyQuestion, answer: str | None, comment: str | None = None, overwrite_existing: bool = True, ) -> None: if question.question_type == "file": old_answers = self.env["survey.user_input.line"].search([ ("user_input_id", "=", self.id), ("question_id", "=", question.id), ]) if old_answers and not overwrite_existing: raise UserError(_("This answer cannot be overwritten.")) if not answer and any(line.value_file for line in old_answers): # No new file was submitted: a file input cannot be pre-filled # by the browser when navigating back to a previous page, so an # empty answer here does not mean the user removed their file. # Keep the previously uploaded file instead of overwriting it # with a skipped answer. return vals = { "user_input_id": self.id, "question_id": question.id, "skipped": False, "answer_type": "file", } file_data = json.loads(answer) if answer else {} if file_data.get("cleared"): # The user explicitly removed the file: drop the stored data and # mark the line as skipped. vals.update(answer_type=None, skipped=True, value_file=False, value_file_fname=False) elif file_data: file_b64 = file_data.get("data", "") file_name = file_data.get("name", "") self._check_file_constraints(question, file_b64, file_name) vals["value_file"] = file_b64 vals["value_file_fname"] = file_name else: vals.update(answer_type=None, skipped=True) if old_answers: old_answers.write(vals) else: self.env["survey.user_input.line"].create(vals) else: return super()._save_lines( question, answer, comment=comment, overwrite_existing=overwrite_existing ) def _check_file_constraints( self, question: SurveyQuestion, file_b64: str, file_name: str ) -> None: if question.max_file_size: file_size = len(base64.b64decode(file_b64)) max_bytes = question.max_file_size * 1024 * 1024 if file_size > max_bytes: raise ValidationError( _("The file '%(name)s' exceeds the maximum allowed size of %(size)s MB.", name=file_name, size=question.max_file_size) ) if question.allowed_extensions: allowed = [ allowed_extension.strip().lower() for allowed_extension in question.allowed_extensions.split(",") if allowed_extension.strip() ] file_extension = os.path.splitext(file_name)[1].lower() if file_extension not in allowed: raise ValidationError( _("The file '%(name)s' is not allowed. Accepted formats: %(exts)s.", name=file_name, exts=question.allowed_extensions) )