From 31b059d3f6ecaebaf2dea812a5870eb131dec38d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 Oct 2013 11:02:07 +0200 Subject: [PATCH 1/5] [IMP] url t-ization bzr revid: xmo@openerp.com-20131023090207-jto4fzz153xiqtm6 --- addons/website/models/ir_ui_view.py | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index c85245bb188..ab09336d6cb 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -100,22 +100,24 @@ class view(osv.osv): return arch - def _normalize_urls(self, element): - attr = None - if element.tag == 'form': - attr = 'action' if 'action' in element.attrib else None - elif element.tag in ['a', 'link']: - attr = 'href' if 'href' in element.attrib else None - elif element.tag in ['frame', 'iframe', 'script']: - attr = 'src' if 'src' in element.attrib else None - if attr: - value = element.attrib[attr] + URL_ATTRS = { + 'form': 'action', + 'a': 'href', + 'link': 'href', + 'frame': 'src', + 'iframe': 'src', + 'script': 'src', + } + def _normalize_urls(self, root): + for element in root.iter(): + attr = self.URL_ATTRS.get(element.tag) + if attr is None or attr not in element.attrib: + continue + + value = element.get(attr) if not urlparse(value).scheme: element.attrib.pop(attr) - element.attrib['t-' + attr] = value - for el in list(element): - self._normalize_urls(el) - return element + element.set('t-' + attr, value) def save(self, cr, uid, res_id, value, xpath=None, context=None): """ Update a view section. The view section may embed fields to write From 1ce3230d135b03be09406e7c587ae3477eafb44b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 Oct 2013 15:09:18 +0200 Subject: [PATCH 2/5] [IMP] don't completely wedge the RTE in case save fails. This may well break snippets entirely in case of save failure. bzr revid: xmo@openerp.com-20131023130918-j5s81t0j60gitr1t --- addons/website/static/src/css/editor.css | 6 +- addons/website/static/src/css/editor.sass | 4 ++ .../website/static/src/js/website.editor.js | 64 +++++++++++++++---- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 26032aaf7ce..8fa0ad80f5f 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -1,4 +1,3 @@ -@charset "utf-8"; /* ---- CKEditor Minimal Reset ---- */ .navbar.navbar-inverse .cke_chrome { border: none; @@ -174,6 +173,11 @@ table.editorbar-panel td.selected { white-space: pre-wrap; } +.oe_carlos_danger { + outline: 1px solid red !important; + background-color: #ffd9dd !important; +} + /* ---- SNIPPET EDITOR ---- */ #oe_snippets { position: fixed; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index e4ef24dad70..5dbe7a8eee9 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -154,6 +154,10 @@ table.editorbar-panel content: " " white-space: pre-wrap +.oe_carlos_danger + outline: 1px solid red !important + background-color: #ffd9dd !important + /* ---- SNIPPET EDITOR ---- */ #oe_snippets diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 2249de2a24d..f9d2aef339b 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -272,30 +272,58 @@ editor.destroy(); // FIXME: select editables then filter by dirty? var defs = this.rte.fetch_editables(root) - .removeClass('oe_editable cke_focus') - .removeAttr('contentEditable') .filter('.oe_dirty') + .removeAttr('contentEditable') + .removeClass('oe_dirty oe_editable cke_focus oe_carlos_danger') .map(function () { var $el = $(this); // TODO: Add a queue with concurrency limit in webclient // https://github.com/medikoo/deferred/blob/master/lib/ext/function/gate.js return self.saving_mutex.exec(function () { return self.saveElement($el) - .fail(function () { - var data = $el.data(); - console.error(_.str.sprintf('Could not save %s(%d).%s', data.oeModel, data.oeId, data.oeField)); + .then(undefined, function (thing, response) { + // because ckeditor regenerates all the dom, + // we can't just setup the popover here as + // everything will be destroyed by the DOM + // regeneration. Add markings instead, and + // returns a new rejection with all relevant + // info + var id = _.uniqueId('carlos_danger_'); + $el.addClass('oe_dirty oe_carlos_danger'); + $el.addClass(id); + return $.Deferred().reject({ + id: id, + error: response.data, + }); }); }); }).get(); return $.when.apply(null, defs).then(function () { website.reload(); + }, function (failed) { + // If there were errors, re-enable edition + self.rte.start_edition(true).then(function () { + // jquery's deferred being a pain in the ass + if (!_.isArray(failed)) { failed = [failed]; } + + _(failed).each(function (failure) { + $(root).find('.' + failure.id) + .removeClass(failure.id) + .popover({ + trigger: 'hover', + content: failure.error.message, + placement: 'auto top', + }) + // Force-show popovers so users will notice them. + .popover('show'); + }) + }); }); }, /** * Saves an RTE content, which always corresponds to a view section (?). */ saveElement: function ($el) { - $el.removeClass('oe_dirty'); var markup = $el.prop('outerHTML'); return openerp.jsonRpc('/web/dataset/call', 'call', { model: 'ir.ui.view', @@ -422,15 +450,25 @@ return true; }, - start_edition: function () { + /** + * Makes the page editable + * + * @param {Boolean} [restart=false] in case the edition was already set + * up once and is being re-enabled. + * @returns {$.Deferred} deferred indicating when the RTE is ready + */ + start_edition: function (restart) { var self = this; // create a single editor for the whole page var root = document.getElementById('wrapwrap'); - $(root).on('dragstart', 'img', function (e) { - e.preventDefault(); - }); - this.webkitSelectionFixer(root); - this.tableNavigation(root); + if (!restart) { + $(root).on('dragstart', 'img', function (e) { + e.preventDefault(); + }); + this.webkitSelectionFixer(root); + this.tableNavigation(root); + } + var def = $.Deferred(); var editor = this.editor = CKEDITOR.inline(root, self._config()); editor.on('instanceReady', function () { editor.setReadOnly(false); @@ -446,7 +484,9 @@ document.execCommand("enableInlineTableEditing", false, "false"); self.trigger('rte:ready'); + def.resolve(); }); + return def; }, setup_editables: function (root) { From 62bdd7bd9c2231586efbc03535460e3424e2f139 Mon Sep 17 00:00:00 2001 From: ddm Date: Wed, 23 Oct 2013 16:51:52 +0200 Subject: [PATCH 3/5] [FIX] Fixed eCommerce pager bzr revid: ddm@openerp.com-20131023145152-smhlpc0eydvos7sx --- addons/website/models/website.py | 24 ++++++++++++++++++---- addons/website/views/website_templates.xml | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index b6379bcbb79..6d8d5f520a2 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -238,10 +238,26 @@ class website(osv.osv): return { "page_count": page_count, "offset": (page - 1) * step, - "page": {'url': get_url(page), 'num': page}, - "page_start": {'url': get_url(pmin), 'num': pmin}, - "page_end": {'url': get_url(min(pmax, page + 1)), - 'num': min(pmax, page + 1)}, + "page": { + 'url': get_url(page), + 'num': page + }, + "page_start": { + 'url': get_url(pmin), + 'num': pmin + }, + "page_previous": { + 'url': get_url(max(pmin, page - 1)), + 'num': max(pmin, page - 1) + }, + "page_next": { + 'url': get_url(min(pmax, page + 1)), + 'num': min(pmax, page + 1) + }, + "page_end": { + 'url': get_url(pmax), + 'num': pmax + }, "pages": [ {'url': get_url(page), 'num': page} for page in xrange(pmin, pmax+1) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 8862e17a374..e1b79446451 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -263,13 +263,13 @@ From 1eb3d3269c367fa67525c3b65470045b9038582d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 Oct 2013 16:57:29 +0200 Subject: [PATCH 4/5] [ADD] parsing of localized date & datetime t-fields turns out we use a bunch of them in the events section thing bzr revid: xmo@openerp.com-20131023145729-tglusl27mt6ajwzn --- addons/website/models/ir_qweb.py | 37 +++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index 948422e8296..d6bd6a524c3 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -7,17 +7,19 @@ Also, adds methods to convert values back to openerp models. """ import cStringIO +import datetime import itertools import logging import re import urllib2 import werkzeug.utils +from dateutil import parser from lxml import etree, html from PIL import Image as I from openerp.osv import orm, fields -from openerp.tools import ustr +from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT REMOTE_CONNECTION_TIMEOUT = 2.5 @@ -75,19 +77,48 @@ class Float(orm.AbstractModel): return float(value.replace(lang.thousands_sep, '') .replace(lang.decimal_point, '.')) + +def parse_fuzzy(in_format, value): + day_first = in_format.find('%d') < in_format.find('%m') + + if '%y' in in_format: + year_first = in_format.find('%y') < in_format.find('%d') + else: + year_first = in_format.find('%Y') < in_format.find('%d') + + return parser.parse(value, dayfirst=day_first, yearfirst=year_first) + class Date(orm.AbstractModel): _name = 'website.qweb.field.date' _inherit = ['website.qweb.field', 'ir.qweb.field.date'] def from_html(self, cr, uid, model, column, element, context=None): - raise NotImplementedError("Can not parse and save localized dates") + lang = self.user_lang(cr, uid, context=context) + in_format = lang.date_format.encode('utf-8') + + value = element.text_content().strip() + try: + dt = datetime.datetime.strptime(in_format, value) + except ValueError: + dt = parse_fuzzy(in_format, value) + + return dt.strftime(DEFAULT_SERVER_DATE_FORMAT) class DateTime(orm.AbstractModel): _name = 'website.qweb.field.datetime' _inherit = ['website.qweb.field', 'ir.qweb.field.datetime'] def from_html(self, cr, uid, model, column, element, context=None): - raise NotImplementedError("Can not parse and save localized datetimes") + lang = self.user_lang(cr, uid, context=context) + in_format = (u"%s %s" % (lang.date_format, lang.time_format)).encode('utf-8') + + value = element.text_content().strip() + try: + dt = datetime.datetime.strptime(in_format, value) + except ValueError: + dt = parse_fuzzy(in_format, value) + + return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) class Text(orm.AbstractModel): _name = 'website.qweb.field.text' From d5379f3b7cfba31dac8b7cfa9251243e26c7f018 Mon Sep 17 00:00:00 2001 From: ddm Date: Wed, 23 Oct 2013 17:46:39 +0200 Subject: [PATCH 5/5] [FIX] eCommerce cart layout Fixed 'Continue Shopping' button spacing when cart empty bzr revid: ddm@openerp.com-20131023154639-j7385c9oaka2k3g1 --- addons/website_sale/views/website_sale.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 4f2cc38f235..c8585af266b 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -463,7 +463,7 @@