diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py index 91673e0a5d88a..2b06f7ebdd0a0 100644 --- a/addons/web_editor/models/ir_qweb.py +++ b/addons/web_editor/models/ir_qweb.py @@ -9,6 +9,7 @@ """ import ast +import babel import base64 import io import itertools @@ -21,6 +22,7 @@ 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 @@ 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 @@ -202,6 +204,21 @@ def attributes(self, record, field_name, options, values): 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 @@ -210,7 +227,9 @@ def from_html(self, model, field, element): 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): @@ -221,15 +240,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 @@ -239,11 +270,11 @@ def from_html(self, model, field, element): 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 e608e84f27ce2..3dd24d09db598 100644 --- a/addons/web_editor/static/src/js/editor/rte.js +++ b/addons/web_editor/static/src/js/editor/rte.js @@ -466,7 +466,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 @@ -534,6 +534,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); @@ -648,6 +659,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; } @@ -697,6 +711,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 fd42d78a8bd2f..1f96066da6eaf 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"]):hover { + &:not([data-oe-model="ir.ui.view"]):not([data-oe-type="html"]):hover, + &.o_editable_date_field_linked { box-shadow: $o-brand-odoo 0 0 5px 2px inset; } }