[IMP] website_form, *: allow form default input value
* = web_editor Add support for default values on website form Compatible fields are: textarea, text, number, date, datetime, unique checkox, multiple checkboxes, radio buttons, select. Part of: https://github.com/odoo/odoo/pull/47949 task-2011583
This commit is contained in:
@@ -1733,6 +1733,7 @@ const ListUserValueWidget = UserValueWidget.extend({
|
||||
'click we-button.o_we_list_add_optional': '_onAddCustomItemClick',
|
||||
'click we-button.o_we_list_add_existing': '_onAddExistingItemClick',
|
||||
'click we-select.o_we_user_value_widget': '_onAddItemSelectClick',
|
||||
'click we-button.o_we_checkbox_wrapper': '_onAddItemCheckboxClick',
|
||||
'input table input': '_onListItemInput',
|
||||
},
|
||||
|
||||
@@ -1746,6 +1747,10 @@ const ListUserValueWidget = UserValueWidget.extend({
|
||||
} else {
|
||||
this.isCustom = true;
|
||||
}
|
||||
if (this.el.dataset.defaults || this.el.dataset.hasDefault) {
|
||||
this.hasDefault = this.el.dataset.hasDefault || 'unique';
|
||||
this.selected = this.el.dataset.defaults ? JSON.parse(this.el.dataset.defaults) : [];
|
||||
}
|
||||
this.listTable = document.createElement('table');
|
||||
const tableWrapper = document.createElement('div');
|
||||
tableWrapper.classList.add('o_we_table_wrapper');
|
||||
@@ -1839,6 +1844,21 @@ const ListUserValueWidget = UserValueWidget.extend({
|
||||
trEl.appendChild(draggableTdEl);
|
||||
inputTdEl.appendChild(inputEl);
|
||||
trEl.appendChild(inputTdEl);
|
||||
if (this.hasDefault) {
|
||||
const checkboxEl = document.createElement('we-button');
|
||||
checkboxEl.classList.add('o_we_user_value_widget', 'o_we_checkbox_wrapper');
|
||||
if (this.selected.includes(id)) {
|
||||
checkboxEl.classList.add('active');
|
||||
}
|
||||
const div = document.createElement('div');
|
||||
const checkbox = document.createElement('we-checkbox');
|
||||
div.appendChild(checkbox);
|
||||
checkboxEl.appendChild(div);
|
||||
checkboxEl.appendChild(checkbox);
|
||||
const checkboxTdEl = document.createElement('td');
|
||||
checkboxTdEl.appendChild(checkboxEl);
|
||||
trEl.appendChild(checkboxTdEl);
|
||||
}
|
||||
buttonTdEl.appendChild(buttonEl);
|
||||
trEl.appendChild(buttonTdEl);
|
||||
this.listTable.appendChild(trEl);
|
||||
@@ -1873,6 +1893,18 @@ const ListUserValueWidget = UserValueWidget.extend({
|
||||
if (this.isCustom) {
|
||||
this.records = values.map(v => ({id: v, display_name: v}));
|
||||
}
|
||||
if (this.hasDefault) {
|
||||
const checkboxes = [...this.listTable.querySelectorAll('we-button.o_we_checkbox_wrapper.active')];
|
||||
this.selected = checkboxes.map(el => {
|
||||
const input = el.parentElement.previousSibling.firstChild;
|
||||
const id = input.name || input.value;
|
||||
const idInt = parseInt(id);
|
||||
return isNaN(idInt) ? id : idInt;
|
||||
});
|
||||
this.records.forEach(r => {
|
||||
r.selected = this.selected.includes(r.id);
|
||||
});
|
||||
}
|
||||
this._value = JSON.stringify(values);
|
||||
this.notifyValueChange(true);
|
||||
if (!this.isCustom) {
|
||||
@@ -1931,6 +1963,18 @@ const ListUserValueWidget = UserValueWidget.extend({
|
||||
_onAddItemSelectClick(ev) {
|
||||
ev.currentTarget.querySelector('we-toggler').classList.toggle('active');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddItemCheckboxClick: function (ev) {
|
||||
const isActive = ev.currentTarget.classList.contains('active');
|
||||
if (this.hasDefault === 'unique') {
|
||||
this.listTable.querySelectorAll('we-button.o_we_checkbox_wrapper.active').forEach(el => el.classList.remove('active'));
|
||||
}
|
||||
ev.currentTarget.classList.toggle('active', !isActive);
|
||||
this._notifyCurrentState();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -1207,6 +1207,10 @@ body.editor_enable.editor_has_snippets {
|
||||
&:first-child, &:last-child {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
we-button.o_we_checkbox_wrapper {
|
||||
margin: 0 0 0 0.3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ var TranslatorMenu = EditorMenu.extend({
|
||||
'[data-oe-model][data-oe-id][data-oe-field], ' +
|
||||
'[placeholder*="data-oe-translation-id="], ' +
|
||||
'[title*="data-oe-translation-id="], ' +
|
||||
'[value*="data-oe-translation-id="], ' +
|
||||
'textarea:contains(data-oe-translation-id), ' +
|
||||
'[alt*="data-oe-translation-id="]';
|
||||
var $edit = $wrapwrap.find(selector);
|
||||
$edit.filter(':has(' + selector + ')').attr('data-oe-readonly', true);
|
||||
|
||||
@@ -60,7 +60,11 @@ var AttributeTranslateDialog = Dialog.extend({
|
||||
$input.on('change keyup', function () {
|
||||
var value = $input.val();
|
||||
$node.html(value).trigger('change', node);
|
||||
$node.data('$node').attr($node.data('attribute'), value).trigger('translate');
|
||||
if ($node.data('attribute')) {
|
||||
$node.data('$node').attr($node.data('attribute'), value).trigger('translate');
|
||||
} else {
|
||||
$node.data('$node').text(value).trigger('translate');
|
||||
}
|
||||
$node.trigger('change');
|
||||
});
|
||||
$group.append($label).append($input);
|
||||
@@ -100,36 +104,60 @@ var WysiwygTranslate = WysiwygMultizone.extend({
|
||||
|
||||
return promise.then(function () {
|
||||
self._relocateEditorBar();
|
||||
var attrs = ['placeholder', 'title', 'alt'];
|
||||
var attrs = ['placeholder', 'title', 'alt', 'value'];
|
||||
const $editable = self._getEditableArea();
|
||||
const translationRegex = /<span [^>]*data-oe-translation-id="([0-9]+)"[^>]*>(.*)<\/span>/;
|
||||
let $edited = $();
|
||||
_.each(attrs, function (attr) {
|
||||
self._getEditableArea().filter('[' + attr + '*="data-oe-translation-id="]').filter(':empty, input, select, textarea, img').each(function () {
|
||||
const attrEdit = $editable.filter('[' + attr + '*="data-oe-translation-id="]').filter(':empty, input, select, textarea, img');
|
||||
attrEdit.each(function () {
|
||||
var $node = $(this);
|
||||
var translation = $node.data('translation') || {};
|
||||
var trans = $node.attr(attr);
|
||||
var match = trans.match(/<span [^>]*data-oe-translation-id="([0-9]+)"[^>]*>(.*)<\/span>/);
|
||||
var match = trans.match(translationRegex);
|
||||
var $trans = $(trans).addClass('d-none o_editable o_editable_translatable_attribute').appendTo('body');
|
||||
$trans.data('$node', $node).data('attribute', attr);
|
||||
|
||||
translation[attr] = $trans[0];
|
||||
$node.attr(attr, match[2]);
|
||||
|
||||
var select2 = $node.data('select2');
|
||||
if (select2) {
|
||||
select2.blur();
|
||||
$node.on('translate', function () {
|
||||
select2.blur();
|
||||
});
|
||||
$node = select2.container.find('input');
|
||||
}
|
||||
$node.addClass('o_translatable_attribute').data('translation', translation);
|
||||
});
|
||||
$edited = $edited.add(attrEdit);
|
||||
});
|
||||
const textEdit = $editable.filter('textarea:contains(data-oe-translation-id)');
|
||||
textEdit.each(function () {
|
||||
var $node = $(this);
|
||||
var translation = $node.data('translation') || {};
|
||||
var trans = $node.text();
|
||||
var match = trans.match(translationRegex);
|
||||
var $trans = $(trans).addClass('d-none o_editable o_editable_translatable_text').appendTo('body');
|
||||
$trans.data('$node', $node);
|
||||
|
||||
translation['textContent'] = $trans[0];
|
||||
$node.text(match[2]);
|
||||
|
||||
$node.addClass('o_translatable_text').data('translation', translation);
|
||||
});
|
||||
$edited = $edited.add(textEdit);
|
||||
|
||||
$edited.each(function () {
|
||||
var $node = $(this);
|
||||
var select2 = $node.data('select2');
|
||||
if (select2) {
|
||||
select2.blur();
|
||||
$node.on('translate', function () {
|
||||
select2.blur();
|
||||
});
|
||||
$node = select2.container.find('input');
|
||||
}
|
||||
});
|
||||
|
||||
self.translations = [];
|
||||
self.$editables_attr = self._getEditableArea().filter('.o_translatable_attribute');
|
||||
self.$editables_attribute = $('.o_editable_translatable_attribute');
|
||||
self.$translations = self._getEditableArea().filter('.o_translatable_attribute, .o_translatable_text');
|
||||
self.$editables = $('.o_editable_translatable_attribute, .o_editable_translatable_text');
|
||||
|
||||
self.$editables_attribute.on('change', function () {
|
||||
self.$editables.on('change', function () {
|
||||
self.trigger_up('rte_change', {target: this});
|
||||
});
|
||||
|
||||
@@ -153,7 +181,7 @@ var WysiwygTranslate = WysiwygMultizone.extend({
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isDirty: function () {
|
||||
return this._super() || this.$editables_attribute.hasClass('o_dirty');
|
||||
return this._super() || this.$editables.hasClass('o_dirty');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -168,7 +196,7 @@ var WysiwygTranslate = WysiwygMultizone.extend({
|
||||
*/
|
||||
_getEditableArea: function () {
|
||||
var $editables = this._super();
|
||||
return $editables.add(this.$editables_attribute);
|
||||
return $editables.add(this.$editables);
|
||||
},
|
||||
/**
|
||||
* Return an object describing the linked record.
|
||||
@@ -262,9 +290,9 @@ var WysiwygTranslate = WysiwygMultizone.extend({
|
||||
ev.stopPropagation();
|
||||
});
|
||||
|
||||
// attributes
|
||||
// attributes and textarea text
|
||||
|
||||
this.$editables_attr.each(function () {
|
||||
this.$translations.each(function () {
|
||||
var $node = $(this);
|
||||
var translation = $node.data('translation');
|
||||
_.each(translation, function (node, attr) {
|
||||
@@ -274,7 +302,7 @@ var WysiwygTranslate = WysiwygMultizone.extend({
|
||||
});
|
||||
});
|
||||
|
||||
this.$editables_attr.prependEvent('mousedown.translator click.translator mouseup.translator', function (ev) {
|
||||
this.$translations.prependEvent('mousedown.translator click.translator mouseup.translator', function (ev) {
|
||||
if (ev.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,29 @@ odoo.define('website_form.s_website_form', function (require) {
|
||||
var _t = core._t;
|
||||
var qweb = core.qweb;
|
||||
|
||||
publicWidget.registry.EditModeWebsiteForm = publicWidget.Widget.extend({
|
||||
selector: '.s_website_form form, form.s_website_form', // !compatibility
|
||||
disabledInEditableMode: false,
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
if (this.editableMode) {
|
||||
// We do not initialize the datetime picker in edit mode but want the dates to be formated
|
||||
const dateTimeFormat = time.getLangDatetimeFormat();
|
||||
const dateFormat = time.getLangDateFormat();
|
||||
this.$target[0].querySelectorAll('.s_website_form_input.datetimepicker-input').forEach(el => {
|
||||
const value = el.getAttribute('value');
|
||||
if (value) {
|
||||
const format = el.closest('.s_website_form_field').dataset.type === 'date' ? dateFormat : dateTimeFormat;
|
||||
el.value = moment.unix(value).format(format);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
publicWidget.registry.s_website_form = publicWidget.Widget.extend({
|
||||
selector: '.s_website_form form, form.s_website_form', // !compatibility
|
||||
xmlDependencies: ['/website_form/static/src/xml/website_form.xml'],
|
||||
@@ -48,15 +71,21 @@ odoo.define('website_form.s_website_form', function (require) {
|
||||
previous: 'fa fa-chevron-left',
|
||||
up: 'fa fa-chevron-up',
|
||||
down: 'fa fa-chevron-down',
|
||||
},
|
||||
},
|
||||
locale: moment.locale(),
|
||||
format: time.getLangDatetimeFormat(),
|
||||
extraFormats: ['X'],
|
||||
};
|
||||
this.$target.find('.s_website_form_datetime, .o_website_form_datetime').datetimepicker(datepickers_options); // !compatibility
|
||||
const $datetimes = this.$target.find('.s_website_form_datetime, .o_website_form_datetime'); // !compatibility
|
||||
$datetimes.datetimepicker(datepickers_options);
|
||||
|
||||
// Adapt options to date-only pickers
|
||||
datepickers_options.format = time.getLangDateFormat();
|
||||
this.$target.find('.s_website_form_date, .o_website_form_date').datetimepicker(datepickers_options); // !compatibility
|
||||
const $dates = this.$target.find('.s_website_form_date, .o_website_form_date'); // !compatibility
|
||||
$dates.datetimepicker(datepickers_options);
|
||||
|
||||
this.$allDates = $datetimes.add($dates);
|
||||
this.$allDates.addClass('s_website_form_datepicker_initialized');
|
||||
|
||||
// Display form values from tag having data-for attribute
|
||||
// It's necessary to handle field values generated on server-side
|
||||
@@ -85,6 +114,21 @@ odoo.define('website_form.s_website_form', function (require) {
|
||||
// Empty imputs
|
||||
this.$target[0].reset();
|
||||
|
||||
// Apply default values
|
||||
const dateTimeFormat = time.getLangDatetimeFormat();
|
||||
const dateFormat = time.getLangDateFormat();
|
||||
this.$target[0].querySelectorAll('input[type="text"], input[type="email"], input[type="number"]').forEach(el => {
|
||||
let value = el.getAttribute('value');
|
||||
if (value) {
|
||||
if (el.classList.contains('datetimepicker-input')) {
|
||||
const format = el.closest('.s_website_form_field').dataset.type === 'date' ? dateFormat : dateTimeFormat;
|
||||
value = moment.unix(value).format(format);
|
||||
}
|
||||
el.value = value;
|
||||
}
|
||||
});
|
||||
this.$target[0].querySelectorAll('textarea').forEach(el => el.value = el.textContent);
|
||||
|
||||
// Remove saving of the error colors
|
||||
this.$target.find('.o_has_error').removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid');
|
||||
|
||||
@@ -94,6 +138,9 @@ odoo.define('website_form.s_website_form', function (require) {
|
||||
// Remove the success message and display the form
|
||||
this.$target.removeClass('d-none');
|
||||
this.$target.parent().find('.s_website_form_end_message').addClass('d-none');
|
||||
|
||||
// Reinitialize dates
|
||||
this.$allDates.removeClass('s_website_form_datepicker_initialized');
|
||||
},
|
||||
|
||||
send: async function (e) {
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
// Display the editable select as a single big field
|
||||
#editable_select.form-control {
|
||||
height: auto;
|
||||
|
||||
.s_website_form_select_item.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,3 +59,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body:not(.editor_enable) .s_website_form[data-vcss="001"] {
|
||||
.s_website_form_date, .s_website_form_datetime {
|
||||
&:not(.s_website_form_datepicker_initialized) {
|
||||
[value] {
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +259,14 @@ const FieldEditor = FormEditor.extend({
|
||||
const textarea = this.$target[0].querySelector('textarea');
|
||||
const input = this.$target[0].querySelector('input[type="text"], input[type="email"], input[type="number"], input[type="tel"], input[type="url"], textarea');
|
||||
field.placeholder = input && input.placeholder;
|
||||
if (input) {
|
||||
// textarea value has no attribute, date/datetime timestamp property is formated
|
||||
field.value = input.getAttribute('value') || input.value;
|
||||
} else if (field.type === 'boolean') {
|
||||
field.value = !!this.$target[0].querySelector('input[type="checkbox"][checked]');
|
||||
}
|
||||
// property value is needed for date/datetime (formated date).
|
||||
field.propertyValue = input && input.value;
|
||||
field.rows = textarea && textarea.rows;
|
||||
field.required = classList.contains('s_website_form_required');
|
||||
field.modelRequired = classList.contains('s_website_form_model_required');
|
||||
@@ -836,6 +844,19 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
field.type = value;
|
||||
await this._replaceField(field);
|
||||
},
|
||||
/**
|
||||
* Select the textarea default value
|
||||
*/
|
||||
selectTextareaValue: function (previewMode, value, params) {
|
||||
this.$target[0].textContent = value;
|
||||
this.$target[0].value = value;
|
||||
},
|
||||
/**
|
||||
* Select the date as value property and convert it to the right format
|
||||
*/
|
||||
selectValueProperty: function (previewMode, value, params) {
|
||||
this.$target[0].value = value ? moment.unix(value).format(params.format) : '';
|
||||
},
|
||||
/**
|
||||
* Select the display of the multicheckbox field (vertical & horizontal)
|
||||
*/
|
||||
@@ -891,6 +912,10 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
return this._getLabelPosition();
|
||||
case 'selectType':
|
||||
return this._getFieldType();
|
||||
case 'selectTextareaValue':
|
||||
return this.$target[0].textContent;
|
||||
case 'selectValueProperty':
|
||||
return this.$target[0].getAttribute('value') || '';
|
||||
case 'multiCheckboxDisplay': {
|
||||
const target = this._getMultipleInputs();
|
||||
return target ? target.dataset.display : '';
|
||||
@@ -950,6 +975,13 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
list.dataset.addItemTitle = _.str.sprintf(_t("Add new %s"), optionText);
|
||||
list.dataset.renderListItems = '';
|
||||
|
||||
list.dataset.hasDefault = ['one2many', 'many2many'].includes(type) ? 'multiple' : 'unique';
|
||||
const defaults = [...this.$target[0].querySelectorAll('[checked], [selected]')].map(el => {
|
||||
const idInt = parseInt(el.value);
|
||||
return isNaN(idInt) ? el.value : idInt;
|
||||
});
|
||||
list.dataset.defaults = JSON.stringify(defaults);
|
||||
|
||||
if (!this._isFieldCustom()) {
|
||||
await this._fetchFieldRecords(field);
|
||||
list.dataset.availableRecords = JSON.stringify(field.records);
|
||||
@@ -965,11 +997,16 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
*/
|
||||
_replaceField: async function (field) {
|
||||
await this._fetchFieldRecords(field);
|
||||
const activeField = this._getActiveField();
|
||||
if (activeField.type !== field.type) {
|
||||
field.value = '';
|
||||
}
|
||||
const htmlField = this._renderField(field);
|
||||
[...this.$target[0].childNodes].forEach(node => node.remove());
|
||||
[...htmlField.childNodes].forEach(node => this.$target[0].appendChild(node));
|
||||
[...htmlField.attributes].forEach(el => this.$target[0].removeAttribute(el.nodeName));
|
||||
[...htmlField.attributes].forEach(el => this.$target[0].setAttribute(el.nodeName, el.nodeValue));
|
||||
this.$target[0].querySelectorAll('input.datetimepicker-input').forEach(el => el.value = field.propertyValue);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -985,9 +1022,11 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
}
|
||||
return options.map(opt => {
|
||||
const id = parseInt(opt.value);
|
||||
const name = select ? opt : opt.nextElementSibling;
|
||||
return {
|
||||
id: isNaN(id) ? opt.value : id,
|
||||
display_name: select ? opt.textContent : opt.value
|
||||
display_name: name.textContent.trim(),
|
||||
selected: select ? opt.selected : opt.checked,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
t-att-placeholder="field.placeholder"
|
||||
t-att-id="field.id"
|
||||
t-att-rows="field.rows || 3"
|
||||
t-esc="field.value"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
@@ -147,6 +148,7 @@
|
||||
t-att-name="field.name"
|
||||
step="1"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-value="field.value"
|
||||
t-att-placeholder="field.placeholder"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
@@ -162,6 +164,7 @@
|
||||
t-att-name="field.name"
|
||||
step="any"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-value="field.value"
|
||||
t-att-placeholder="field.placeholder"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
@@ -179,6 +182,7 @@
|
||||
t-attf-data-target="##{datepickerID}"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-value="field.value"
|
||||
t-att-placeholder="field.placeholder"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
@@ -200,6 +204,7 @@
|
||||
t-attf-data-target="##{datetimepickerID}"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-value="field.value"
|
||||
t-att-placeholder="field.placeholder"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
@@ -214,14 +219,17 @@
|
||||
<t t-name="website_form.field_boolean">
|
||||
<t t-set="field.isCheck" t-value="true"/>
|
||||
<t t-call="website_form.field">
|
||||
<input
|
||||
type="checkbox"
|
||||
value="Yes"
|
||||
class="s_website_form_input"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
<div class="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
value="Yes"
|
||||
class="s_website_form_input"
|
||||
t-att-name="field.name"
|
||||
t-att-checked="field.value and 'checked' or None"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
t-att-id="field.id"
|
||||
/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -256,6 +264,7 @@
|
||||
class="s_website_form_input form-check-input"
|
||||
t-att-id="recordId"
|
||||
t-att-name="field.name"
|
||||
t-att-checked="record.selected and 'checked' or None"
|
||||
t-att-value="record.id"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
/>
|
||||
@@ -277,14 +286,12 @@
|
||||
<t t-call="website_form.field">
|
||||
<select class="form-control s_website_form_input" t-att-name="field.name" t-att-required="field.required || field.modelRequired || None" t-att-id="field.id" style="display: none">
|
||||
<t t-foreach="field.records" t-as="record">
|
||||
<option t-att-value="record.id" t-att-selected="record.selected">
|
||||
<t t-esc="record.display_name"/>
|
||||
</option>
|
||||
<option t-esc="record.display_name" t-att-value="record.id" t-att-selected="record.selected and 'selected' or None"/>
|
||||
</t>
|
||||
</select>
|
||||
<div id="editable_select" class="form-control s_website_form_input">
|
||||
<t t-foreach="field.records" t-as="record">
|
||||
<div t-esc="record.display_name" t-att-id="record.id" class="s_website_form_select_item"/>
|
||||
<div t-esc="record.display_name" t-att-id="record.id" t-attf-class="s_website_form_select_item #{record.selected and 'selected' or ''}"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
@@ -331,6 +338,7 @@
|
||||
class="s_website_form_input form-check-input"
|
||||
t-att-id="recordId"
|
||||
t-att-name="field.name"
|
||||
t-att-checked="record.selected and 'checked' or None"
|
||||
t-att-value="record.id"
|
||||
t-att-required="field.required || field.modelRequired || None"
|
||||
/>
|
||||
|
||||
@@ -118,6 +118,15 @@
|
||||
data-select-label-position="right"
|
||||
data-img="/website/static/src/img/snippets_options/pos_right.svg"/>
|
||||
</we-button-group>
|
||||
<we-input string="Default Value" data-select-textarea-value="" data-apply-to="textarea"/>
|
||||
<we-checkbox string="Default Value" data-select-attribute="checked" data-attribute-name="checked"
|
||||
data-apply-to=".col-sm > * > input[type='checkbox']" data-no-preview="true"/>
|
||||
<we-input string="Default Value" data-select-attribute="" data-attribute-name="value" data-select-property=""
|
||||
data-property-name="value" data-apply-to="input[type='text']:not(.datetimepicker-input), input[type='email'], input[type='number'], input[type='tel'], input[type='url']"/>
|
||||
<we-datetimepicker string="Default Value" data-select-attribute="" data-attribute-name="value" data-select-value-property=""
|
||||
data-apply-to=".s_website_form_datetime input"/>
|
||||
<we-datepicker string="Default Value" data-select-attribute="" data-attribute-name="value" data-select-value-property=""
|
||||
data-apply-to=".s_website_form_date input"/>
|
||||
<we-checkbox string="Required" data-name="required_opt" data-no-preview="true"
|
||||
data-toggle-required="s_website_form_required"/>
|
||||
<we-checkbox string="Hidden" data-name="hidden_opt" data-no-preview="true"
|
||||
|
||||
@@ -135,6 +135,7 @@ def encode(s):
|
||||
assert isinstance(s, str)
|
||||
return s
|
||||
|
||||
|
||||
# which elements are translated inline
|
||||
TRANSLATED_ELEMENTS = {
|
||||
'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'del', 'dfn', 'em',
|
||||
@@ -143,14 +144,16 @@ TRANSLATED_ELEMENTS = {
|
||||
'sup', 'time', 'u', 'var', 'wbr', 'text',
|
||||
}
|
||||
|
||||
# which attributes must be translated
|
||||
TRANSLATED_ATTRS = {
|
||||
# Which attributes must be translated. This is a dict, where the value indicates
|
||||
# a condition for a node to have the attribute translatable.
|
||||
TRANSLATED_ATTRS = dict.fromkeys({
|
||||
'string', 'help', 'sum', 'avg', 'confirm', 'placeholder', 'alt', 'title', 'aria-label',
|
||||
'aria-keyshortcuts', 'aria-placeholder', 'aria-roledescription', 'aria-valuetext',
|
||||
'value_label',
|
||||
}
|
||||
}, lambda e: True)
|
||||
TRANSLATED_ATTRS['value'] = lambda e: (e.tag == 'input' and e.attrib.get('type', 'text') == 'text') and 'datetimepicker-input' not in e.attrib['class'].split(' ')
|
||||
|
||||
TRANSLATED_ATTRS = TRANSLATED_ATTRS | {'t-attf-' + attr for attr in TRANSLATED_ATTRS}
|
||||
TRANSLATED_ATTRS.update({f't-attf-{attr}': cond for attr, cond in TRANSLATED_ATTRS.items()})
|
||||
|
||||
avoid_pattern = re.compile(r"\s*<!DOCTYPE", re.IGNORECASE | re.MULTILINE | re.UNICODE)
|
||||
node_pattern = re.compile(r"<[^>]*>(.*)</[^<]*>", re.DOTALL | re.MULTILINE | re.UNICODE)
|
||||
@@ -268,7 +271,8 @@ def translate_xml_node(node, callback, parse, serialize):
|
||||
|
||||
# translate the required attributes
|
||||
for name, value in result.attrib.items():
|
||||
if name in TRANSLATED_ATTRS:
|
||||
condition = TRANSLATED_ATTRS.get(name)
|
||||
if condition and condition(result):
|
||||
result.set(name, translate_text(value) or value)
|
||||
|
||||
# add the untranslated tail to result
|
||||
|
||||
Reference in New Issue
Block a user