[IMP] survey_extra_fields, survey_base : modifications to make them work in v18
This commit is contained in:
@@ -1,146 +1,148 @@
|
||||
odoo.define("survey_extra_fields.survey_form", function (require) {
|
||||
"use strict";
|
||||
/** @odoo-module **/
|
||||
|
||||
var core = require("web.core");
|
||||
var _t = core._t;
|
||||
var survey_form = require("survey.form");
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
|
||||
survey_form.include({
|
||||
_readFileAsDataURL: function (file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
resolve(e.target.result);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
reject(reader.error);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
},
|
||||
publicWidget.registry.SurveyFormWidget.include({
|
||||
_readFileAsDataURL: function (file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
resolve(e.target.result);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
reject(reader.error);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
},
|
||||
|
||||
_submitForm: function (options) {
|
||||
var self = this;
|
||||
var $fileInputs = this.$('input[data-question-type="file"]');
|
||||
var hasFiles = false;
|
||||
$fileInputs.each(function () {
|
||||
if (this.files && this.files.length > 0) {
|
||||
hasFiles = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @override
|
||||
* The base implementation builds the submit params synchronously and fires
|
||||
* the RPC immediately. File inputs need to be read asynchronously (FileReader),
|
||||
* so when the current page contains file answers we replicate the submit flow
|
||||
* here, injecting the base64 file payload before submitting.
|
||||
*/
|
||||
_submitForm: async function (options) {
|
||||
const fileInputs = this.el.querySelectorAll('input[data-question-type="file"]');
|
||||
const hasFiles = Array.from(fileInputs).some(
|
||||
(input) => input.files && input.files.length > 0
|
||||
);
|
||||
|
||||
if (!hasFiles || this.options.isStartScreen) {
|
||||
return this._super(options);
|
||||
}
|
||||
if (!hasFiles || this.options.isStartScreen) {
|
||||
return this._super(options);
|
||||
}
|
||||
|
||||
// Async flow: read files then submit
|
||||
var params = {};
|
||||
if (options.previousPageId) {
|
||||
params.previous_page_id = options.previousPageId;
|
||||
}
|
||||
const params = {};
|
||||
if (options.previousPageId) {
|
||||
params.previous_page_id = options.previousPageId;
|
||||
}
|
||||
if (options.nextSkipped) {
|
||||
params.next_skipped_page_or_question = true;
|
||||
}
|
||||
|
||||
var $form = this.$("form");
|
||||
var formData = new FormData($form[0]);
|
||||
const $form = this.$("form");
|
||||
const formData = new FormData($form[0]);
|
||||
|
||||
if (!options.skipValidation) {
|
||||
if (!this._validateForm($form, formData)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!options.skipValidation && !this._validateForm($form, formData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prepareSubmitValues(formData, params);
|
||||
this._prepareSubmitValues(formData, params);
|
||||
|
||||
// Read all selected files as base64
|
||||
var filePromises = [];
|
||||
$fileInputs.each(function () {
|
||||
if (this.files && this.files.length > 0) {
|
||||
var file = this.files[0];
|
||||
var name = this.name;
|
||||
filePromises.push(
|
||||
self._readFileAsDataURL(file).then(function (dataURL) {
|
||||
params[name] = JSON.stringify({
|
||||
data: dataURL.split(",")[1],
|
||||
name: file.name,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.preventEnterSubmit = true;
|
||||
|
||||
if (this.options.sessionInProgress) {
|
||||
this.fadeInOutDelay = 400;
|
||||
this.readonly = true;
|
||||
}
|
||||
|
||||
Promise.all(filePromises).then(function () {
|
||||
var submitPromise = self._rpc({
|
||||
route: _.str.sprintf(
|
||||
"%s/%s/%s",
|
||||
"/survey/submit",
|
||||
self.options.surveyToken,
|
||||
self.options.answerToken
|
||||
),
|
||||
params: params,
|
||||
});
|
||||
self._nextScreen(submitPromise, options);
|
||||
});
|
||||
},
|
||||
|
||||
_validateForm: function ($form, formData) {
|
||||
var result = this._super.apply(this, arguments);
|
||||
var errors = {};
|
||||
var inactiveQuestionIds = this.options.sessionInProgress
|
||||
? []
|
||||
: this._getInactiveConditionalQuestionIds();
|
||||
|
||||
$form.find('input[data-question-type="file"]').each(function () {
|
||||
var $questionWrapper = $(this).closest(".js_question-wrapper");
|
||||
var questionId = $questionWrapper.attr("id");
|
||||
if (inactiveQuestionIds.includes(parseInt(questionId))) {
|
||||
return;
|
||||
}
|
||||
var questionRequired = $questionWrapper.data("required");
|
||||
var constrErrorMsg =
|
||||
$questionWrapper.data("constrErrorMsg") ||
|
||||
_t("This question requires an answer.");
|
||||
if (questionRequired && !(this.files && this.files.length > 0)) {
|
||||
errors[questionId] = constrErrorMsg;
|
||||
return;
|
||||
}
|
||||
if (this.files && this.files.length > 0) {
|
||||
var file = this.files[0];
|
||||
var maxSizeMB = parseInt($(this).data("maxFileSize"));
|
||||
if (maxSizeMB && file.size > maxSizeMB * 1024 * 1024) {
|
||||
errors[questionId] = _.str.sprintf(
|
||||
_t("The file exceeds the maximum allowed size of %s MB."),
|
||||
maxSizeMB
|
||||
);
|
||||
return;
|
||||
}
|
||||
var allowedExtensions = $(this).data("allowedExtensions");
|
||||
if (allowedExtensions) {
|
||||
var allowed = allowedExtensions.split(",").map(function (e) {
|
||||
return e.trim().toLowerCase();
|
||||
// Read all selected files as base64 and add them to the submit params.
|
||||
const filePromises = [];
|
||||
for (const input of fileInputs) {
|
||||
if (input.files && input.files.length > 0) {
|
||||
const file = input.files[0];
|
||||
const name = input.name;
|
||||
filePromises.push(
|
||||
this._readFileAsDataURL(file).then((dataURL) => {
|
||||
params[name] = JSON.stringify({
|
||||
data: dataURL.split(",")[1],
|
||||
name: file.name,
|
||||
});
|
||||
var ext = "." + file.name.split(".").pop().toLowerCase();
|
||||
if (!allowed.includes(ext)) {
|
||||
errors[questionId] = _.str.sprintf(
|
||||
_t("This file type is not allowed. Accepted formats: %s."),
|
||||
allowedExtensions
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent user from submitting more times using enter key.
|
||||
this.preventEnterSubmit = true;
|
||||
if (this.options.sessionInProgress) {
|
||||
this.fadeInOutDelay = 400;
|
||||
this.readonly = true;
|
||||
}
|
||||
|
||||
await Promise.all(filePromises);
|
||||
|
||||
const submitPromise = rpc(
|
||||
`/survey/submit/${this.options.surveyToken}/${this.options.answerToken}`,
|
||||
params
|
||||
);
|
||||
this._nextScreen(submitPromise, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* Add client-side validation (required, max size, allowed extensions) for
|
||||
* file questions, which the base implementation does not know about.
|
||||
*/
|
||||
_validateForm: function ($form, formData) {
|
||||
const result = this._super.apply(this, arguments);
|
||||
const errors = {};
|
||||
const inactiveQuestionIds = this.options.sessionInProgress
|
||||
? []
|
||||
: this._getInactiveConditionalQuestionIds();
|
||||
|
||||
$form.find('input[data-question-type="file"]').each(function () {
|
||||
const $questionWrapper = $(this).closest(".js_question-wrapper");
|
||||
const questionId = $questionWrapper.attr("id");
|
||||
if (inactiveQuestionIds.includes(parseInt(questionId))) {
|
||||
return;
|
||||
}
|
||||
const questionRequired = $questionWrapper.data("required");
|
||||
const constrErrorMsg =
|
||||
$questionWrapper.data("constrErrorMsg") ||
|
||||
_t("This question requires an answer.");
|
||||
if (questionRequired && !(this.files && this.files.length > 0)) {
|
||||
errors[questionId] = constrErrorMsg;
|
||||
return;
|
||||
}
|
||||
if (this.files && this.files.length > 0) {
|
||||
const file = this.files[0];
|
||||
const maxSizeMB = parseInt($(this).data("maxFileSize"));
|
||||
if (maxSizeMB && file.size > maxSizeMB * 1024 * 1024) {
|
||||
errors[questionId] = _t(
|
||||
"The file exceeds the maximum allowed size of %s MB.",
|
||||
maxSizeMB
|
||||
);
|
||||
return;
|
||||
}
|
||||
const allowedExtensions = $(this).data("allowedExtensions");
|
||||
if (allowedExtensions) {
|
||||
const allowed = allowedExtensions
|
||||
.split(",")
|
||||
.map((e) => e.trim().toLowerCase());
|
||||
const ext = "." + file.name.split(".").pop().toLowerCase();
|
||||
if (!allowed.includes(ext)) {
|
||||
errors[questionId] = _t(
|
||||
"This file type is not allowed. Accepted formats: %s.",
|
||||
allowedExtensions
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (_.keys(errors).length > 0) {
|
||||
this._showErrors(errors);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
this._showErrors(errors);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
|
||||
export default publicWidget.registry.SurveyFormWidget;
|
||||
|
||||
Reference in New Issue
Block a user