summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRomain Derie <rde@odoo.com>2020-02-14 16:49:59 +0000
committerRomain Derie <rde@odoo.com>2020-02-21 18:57:59 +0000
commit4eafd788470daac1ce3da827d2bd3e648914a265 (patch)
treec70355a84f2826d8b4f94f168926ce3c6b3f067c
parent4a6d3ae9341cf1ea03db9d7d20a27a24b28e718b (diff)
[FIX] web_editor, website: handle date edition in all lang and format
Before d00c0e317, `Datetime` fields would be editable in frontend but would have unexpected results, especially in non-English languages, for when the english lang format had been changed. It would also crash when saving non-English strings, such as `Lundi`. For more details, see https://github.com/odoo/odoo/pull/44484#issuecomment-586850490 Since d00c0e317, only date displayed in lang format would be editable, which case is Event page in Odoo 12.0. Everywhere else, the format is changed for a nicer layout, either with `widget=XXX` or `t-options=YYY`, such as: `<time t-field="record.date" t-options='{"format": "MMM d, yyyy"}'/>` `<time t-field="record.date" t-options="{'time_only': 'true', 'format': 'short'}"/>` When a date parsing crashes during editor save, the problem is not only that the date can be saved, but the whole changes of the page are lost, as they won't be saved either. This commit attempts to fix every languages cases, regardless of the website lang or user lang. To do so, we store the date in the user lang format in a data attribute of every date field in the DOM. Once the field is clicked (to edit probably), that value will replace the one displayed according to the widget/options. That way, dates will always be sent to the server in the user lang format, avoiding any possible mismatch. This whole fix apply to `Datetime` and `Date` fields. opw-2183055 Closes #44484 Closes #45555 Fixes #44047 closes odoo/odoo#45725 closes odoo/odoo#45997 X-original-commit: c0db9bb8027ff926c619aebe6e0a5cdd05808232 Original-signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com> Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com> Co-authored-by: Romain Derie <rde@odoo.com> Co-authored-by: Jeremy Kersten <jke@odoo.com>
-rw-r--r--addons/web_editor/models/ir_qweb.py43
-rw-r--r--addons/web_editor/static/src/js/editor/rte.js17
-rw-r--r--addons/website/static/src/scss/website.edit_mode.scss3
3 files changed, 55 insertions, 8 deletions
diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py
index 0cf3ff932c9..815147f9c86 100644
--- a/addons/web_editor/models/ir_qweb.py
+++ b/addons/web_editor/models/ir_qweb.py
@@ -9,6 +9,7 @@ Also, adds methods to convert values back to Odoo models.
"""
import ast
+import babel
import base64
import io
import itertools
@@ -21,6 +22,7 @@ from datetime import datetime
import pytz
import requests
+from datetime import datetime
from lxml import etree, html
from PIL import Image as I
from werkzeug import urls
@@ -28,7 +30,7 @@ from werkzeug import urls
import odoo.modules
from odoo import api, models, fields
-from odoo.tools import ustr, pycompat
+from odoo.tools import ustr, posix_to_ldml, pycompat
from odoo.tools import html_escape as escape
from odoo.addons.base.models import ir_qweb
@@ -203,6 +205,21 @@ class Date(models.AbstractModel):
attrs = super(Date, self).attributes(record, field_name, options, values)
if options.get('inherit_branding'):
attrs['data-oe-original'] = record[field_name]
+
+ if record._fields[field_name].type == 'datetime':
+ attrs = self.env['ir.qweb.field.datetime'].attributes(record, field_name, options, values)
+ attrs['data-oe-type'] = 'datetime'
+ return attrs
+
+ lg = self.env['res.lang']._lang_get(self.env.user.lang)
+ locale = babel.Locale.parse(lg.code)
+ babel_format = value_format = posix_to_ldml(lg.date_format, locale=locale)
+
+ if record[field_name]:
+ date = fields.Date.from_string(record[field_name])
+ value_format = pycompat.to_text(babel.dates.format_date(date, format=babel_format, locale=locale))
+
+ attrs['data-oe-original-with-format'] = value_format
return attrs
@api.model
@@ -211,7 +228,9 @@ class Date(models.AbstractModel):
if not value:
return False
- return value
+ lg = self.env['res.lang']._lang_get(self.env.user.lang)
+ date = datetime.strptime(value, lg.date_format)
+ return fields.Date.to_string(date)
class DateTime(models.AbstractModel):
@@ -222,15 +241,27 @@ class DateTime(models.AbstractModel):
@api.model
def attributes(self, record, field_name, options, values):
attrs = super(DateTime, self).attributes(record, field_name, options, values)
+
if options.get('inherit_branding'):
value = record[field_name]
+
+ lg = self.env['res.lang']._lang_get(self.env.user.lang)
+ locale = babel.Locale.parse(lg.code)
+ babel_format = value_format = posix_to_ldml('%s %s' % (lg.date_format, lg.time_format), locale=locale)
+ tz = record.env.context.get('tz') or self.env.user.tz
+
if isinstance(value, str):
value = fields.Datetime.from_string(value)
+
if value:
# convert from UTC (server timezone) to user timezone
- value = fields.Datetime.context_timestamp(self, timestamp=value)
+ value = fields.Datetime.context_timestamp(self.with_context(tz=tz), timestamp=value)
+ value_format = pycompat.to_text(babel.dates.format_datetime(value, format=babel_format, locale=locale))
value = fields.Datetime.to_string(value)
+
attrs['data-oe-original'] = value
+ attrs['data-oe-original-with-format'] = value_format
+ attrs['data-oe-original-tz'] = tz
return attrs
@api.model
@@ -240,11 +271,11 @@ class DateTime(models.AbstractModel):
return False
# parse from string to datetime
- date_format = self.env['res.lang']._lang_get(self.env.user.lang).date_format + ' %H:%M'
- dt = datetime.strptime(value, date_format)
+ lg = self.env['res.lang']._lang_get(self.env.user.lang)
+ dt = datetime.strptime(value, '%s %s' % (lg.date_format, lg.time_format))
# convert back from user's timezone to UTC
- tz_name = self.env.context.get('tz') or self.env.user.tz
+ tz_name = element.attrib.get('data-oe-original-tz') or self.env.context.get('tz') or self.env.user.tz
if tz_name:
try:
user_tz = pytz.timezone(tz_name)
diff --git a/addons/web_editor/static/src/js/editor/rte.js b/addons/web_editor/static/src/js/editor/rte.js
index 382bb149ef3..a328adeb513 100644
--- a/addons/web_editor/static/src/js/editor/rte.js
+++ b/addons/web_editor/static/src/js/editor/rte.js
@@ -482,7 +482,7 @@ var RTEWidget = Widget.extend({
$('.o_editable')
.destroy()
- .removeClass('o_editable o_is_inline_editable');
+ .removeClass('o_editable o_is_inline_editable o_editable_date_field_linked o_editable_date_field_format_changed');
var $dirty = $('.o_dirty');
$dirty
@@ -550,6 +550,17 @@ var RTEWidget = Widget.extend({
* @param {jQuery} $editable
*/
_enableEditableArea: function ($editable) {
+ if ($editable.data('oe-type') === "datetime" || $editable.data('oe-type') === "date") {
+ var selector = '[data-oe-id="' + $editable.data('oe-id') + '"]';
+ selector += '[data-oe-field="' + $editable.data('oe-field') + '"]';
+ selector += '[data-oe-model="' + $editable.data('oe-model') + '"]';
+ var $linkedFieldNodes = this.editable().find(selector).addBack(selector);
+ $linkedFieldNodes.not($editable).addClass('o_editable_date_field_linked');
+ if (!$editable.hasClass('o_editable_date_field_format_changed')) {
+ $linkedFieldNodes.html($editable.data('oe-original-with-format'));
+ $linkedFieldNodes.addClass('o_editable_date_field_format_changed');
+ }
+ }
if ($editable.data('oe-type') === "monetary") {
$editable.attr('contenteditable', false);
$editable.find('.oe_currency_value').attr('contenteditable', true);
@@ -664,6 +675,9 @@ var RTEWidget = Widget.extend({
var $target = $(ev.target);
var $editable = $target.closest('.o_editable');
+ if (this && this.$last && this.$last.length && this.$last[0] !== $target[0]) {
+ $('.o_editable_date_field_linked').removeClass('o_editable_date_field_linked');
+ }
if (!$editable.length || $.summernote.core.dom.isContentEditableFalse($target)) {
return;
}
@@ -716,6 +730,7 @@ var RTEWidget = Widget.extend({
clearTimeout(lastTimerId);
}
}
+
if ($editable.length && (!this.$last || this.$last[0] !== $editable[0])) {
$editable.summernote(this._getConfig($editable));
diff --git a/addons/website/static/src/scss/website.edit_mode.scss b/addons/website/static/src/scss/website.edit_mode.scss
index b1a202fd86b..83f6aa49a9f 100644
--- a/addons/website/static/src/scss/website.edit_mode.scss
+++ b/addons/website/static/src/scss/website.edit_mode.scss
@@ -20,7 +20,8 @@ $-editor-messages-margin-x: 2%;
.o_editable {
&:not(:empty), &[data-oe-type] {
- &:not([data-oe-model="ir.ui.view"]):not([data-oe-type="html"]):not(.o_editable_no_shadow):hover {
+ &:not([data-oe-model="ir.ui.view"]):not([data-oe-type="html"]):not(.o_editable_no_shadow):hover,
+ &.o_editable_date_field_linked {
box-shadow: $o-brand-odoo 0 0 5px 2px inset;
}
}