diff --git a/addons/website/static/src/js/utils.js b/addons/website/static/src/js/utils.js index 3a269e9c9f5..51a1dc37819 100644 --- a/addons/website/static/src/js/utils.js +++ b/addons/website/static/src/js/utils.js @@ -414,6 +414,30 @@ function isMobile(self) { return isMobile; } +/** + * Returns the parsed data coming from the data-for element for the given form. + * + * @param {string} formId + * @param {HTMLElement} parentEl + * @returns {Object|undefined} the parsed data + */ +function getParsedDataFor(formId, parentEl) { + const dataForEl = parentEl.querySelector(`[data-for='${formId}']`); + if (!dataForEl) { + return; + } + return JSON.parse(dataForEl.dataset.values + // replaces `True` by `true` if they are after `,` or `:` or `[` + .replace(/([,:\[]\s*)True/g, '$1true') + // replaces `False` and `None` by `""` if they are after `,` or `:` or `[` + .replace(/([,:\[]\s*)(False|None)/g, '$1""') + // replaces the `'` by `"` if they are before `,` or `:` or `]` or `}` + .replace(/'(\s*[,:\]}])/g, '"$1') + // replaces the `'` by `"` if they are after `{` or `[` or `,` or `:` + .replace(/([{\[:,]\s*)'/g, '$1"') + ); +} + export default { loadAnchors: loadAnchors, autocompleteWithPages: autocompleteWithPages, @@ -427,4 +451,5 @@ export default { generateGMapIframe: generateGMapIframe, generateGMapLink: generateGMapLink, isMobile: isMobile, + getParsedDataFor: getParsedDataFor, }; diff --git a/addons/website/static/src/snippets/s_website_form/000.js b/addons/website/static/src/snippets/s_website_form/000.js index 1fea0d616db..9b94f121110 100644 --- a/addons/website/static/src/snippets/s_website_form/000.js +++ b/addons/website/static/src/snippets/s_website_form/000.js @@ -18,6 +18,7 @@ import { serializeDateTime, } from "@web/core/l10n/dates"; const { DateTime } = luxon; +import wUtils from '@website/js/utils'; publicWidget.registry.EditModeWebsiteForm = publicWidget.Widget.extend({ selector: '.s_website_form form, form.s_website_form', // !compatibility @@ -124,26 +125,17 @@ const { DateTime } = luxon; // Because, using t-att- inside form make it non-editable // Data-fill-with attribute is given during registry and is used by // to know which user data should be used to prfill fields. - const dataForEl = document.querySelector(`[data-for='${this.el.id}']`); + let dataForValues = wUtils.getParsedDataFor(this.el.id, document); this.editTranslations = !!this._getContext(true).edit_translations; // On the "edit_translations" mode, a with a translated term // will replace the attribute value, leading to some inconsistencies // (setting again the on the attributes after the editor's // cleanup, setting wrong values on the attributes after translating // default values...) - if (!this.editTranslations && (dataForEl || Object.keys(this.preFillValues).length)) { - const dataForValues = dataForEl ? - JSON.parse(dataForEl.dataset.values - // replaces `True` by `true` if they are after `,` or `:` or `[` - .replace(/([,:\[]\s*)True/g, '$1true') - // replaces `False` and `None` by `""` if they are after `,` or `:` or `[` - .replace(/([,:\[]\s*)(False|None)/g, '$1""') - // replaces the `'` by `"` if they are before `,` or `:` or `]` or `}` - .replace(/'(\s*[,:\]}])/g, '"$1') - // replaces the `'` by `"` if they are after `{` or `[` or `,` or `:` - .replace(/([{\[:,]\s*)'/g, '$1"') - ) : {}; - const fieldNames = this.$el.serializeArray().map(el => el.name); + if (!this.editTranslations + && (dataForValues || Object.keys(this.preFillValues).length)) { + dataForValues = dataForValues || {}; + const fieldNames = this.$target.serializeArray().map(el => el.name); // All types of inputs do not have a value property (eg:hidden), // for these inputs any function that is supposed to put a value // property actually puts a HTML value attribute. Because of diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js index fa08489cbbe..51b8072c577 100644 --- a/addons/website/static/src/snippets/s_website_form/options.js +++ b/addons/website/static/src/snippets/s_website_form/options.js @@ -9,6 +9,7 @@ import { unique } from "@web/core/utils/arrays"; import { _t } from "@web/core/l10n/translation"; import { renderToElement } from "@web/core/utils/render"; import { formatDate, formatDateTime } from "@web/core/l10n/dates"; +import wUtils from '@website/js/utils'; let currentActionName; @@ -415,6 +416,14 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ if (!this.$target[0].dataset.model_name) { proms.push(this._applyFormModel()); } + // Get the email_to value from the data-for attribute if it exists. We + // use it if there is no value on the email_to input. + const formId = this.$target[0].id; + const dataForValues = wUtils.getParsedDataFor(formId, this.$target[0].ownerDocument); + if (dataForValues) { + this.dataForEmailTo = dataForValues['email_to']; + } + this.defaultEmailToValue = "info@yourcompany.example.com"; return Promise.all(proms); }, /** @@ -601,6 +610,16 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ return this.activeForm.id; case 'addActionField': { const value = this.$target.find(`.s_website_form_dnone input[name="${params.fieldName}"]`).val(); + if (params.fieldName === 'email_to') { + // For email_to, we try to find a value in this order: + // 1. The current value of the input + // 2. The data-for value if it exists + // 3. The default value (`defaultEmailToValue`) + if (value && value !== this.defaultEmailToValue) { + return value; + } + return this.dataForEmailTo || this.defaultEmailToValue; + } if (value) { return value; } else { @@ -672,7 +691,12 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ */ _addHiddenField: function (value, fieldName) { this.$target.find(`.s_website_form_dnone:has(input[name="${fieldName}"])`).remove(); - if (value) { + // For the email_to field, we keep the field even if it has no value so + // that the email is sent to data-for value or to the default email. + if (fieldName === 'email_to' && !value && !this.dataForEmailTo) { + value = this.defaultEmailToValue; + } + if (value || fieldName === 'email_to') { const hiddenField = renderToElement('website.form_field_hidden', { field: { name: fieldName, diff --git a/addons/website/static/tests/tours/website_form_editor.js b/addons/website/static/tests/tours/website_form_editor.js index 48f8715e2a8..8087354ee5b 100644 --- a/addons/website/static/tests/tours/website_form_editor.js +++ b/addons/website/static/tests/tours/website_form_editor.js @@ -590,6 +590,10 @@ content: "Change a random option", trigger: '[data-set-mark] input', run: 'text_blur **', + }, { + content: "Check that the recipient email is correct", + trigger: 'we-input[data-field-name="email_to"] input:propValue("website_form_contactus_edition_no_email@mail.com")', + isCheck: true, }, ])); diff --git a/addons/website/tests/test_website_form_editor.py b/addons/website/tests/test_website_form_editor.py index 445cbb6191c..b807e0b1b62 100644 --- a/addons/website/tests/test_website_form_editor.py +++ b/addons/website/tests/test_website_form_editor.py @@ -31,6 +31,7 @@ class TestWebsiteFormEditor(HttpCaseWithUserPortal): 'The email was edited, the form should have been sent to the configured email') def test_website_form_contact_us_edition_no_email(self): + self.env.company.email = 'website_form_contactus_edition_no_email@mail.com' self.start_tour('/web', 'website_form_contactus_edition_no_email', login="admin") self.start_tour('/contactus', 'website_form_contactus_submit', login="portal") mail = self.env['mail.mail'].search([], order='id desc', limit=1)