Some checks failed
pre-commit / pre-commit (pull_request) Failing after 1m40s
* Show a stored file (after navigating back) the same way as a freshly selected one: a "filename + Remove file" chip, input hidden until removal. * Keep the stored file when a page is re-submitted without a new selection (instead of overwriting it with a skipped answer). * "Remove file" now sends a sentinel to actually delete the stored file, so the removal persists across navigation.
95 lines
3.7 KiB
Python
95 lines
3.7 KiB
Python
# 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)
|
|
)
|