From aa412d99ec8dcbd5f49a45c051adcd483da83e2c Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Thu, 13 Mar 2014 15:22:58 +0100 Subject: [PATCH 01/17] [FIX] website tour: create a stub for bootstrap tour in automatic/test mode bzr revid: chm@openerp.com-20140313142258-wblpqpwp9oubboc9 --- addons/website/static/src/js/website.tour.js | 16 ++++++++++++++++ addons/website_sale/tests/test_ui.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index a17115d2184..61b8f851339 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -3,9 +3,22 @@ var website = openerp.website; +// don't need template to use bootstrap Tour in automatic mode if (typeof QWeb2 !== "undefined") website.add_template_file('/website/static/src/xml/website.tour.xml'); +// don't need to use bootstrap Tour to launch an automatic tour +function bootstrap_tour_stub () { + if (typeof Tour === "undefined") { + window.Tour = function Tour() {}; + Tour.prototype.addSteps = function () {}; + Tour.prototype.end = function () {}; + Tour.prototype.goto = function () {}; + } +} + + + if (website.EditorBar) website.EditorBar.include({ tours: [], @@ -175,6 +188,9 @@ website.Tour = openerp.Class.extend({ }, registerTour: function () { + if (this.automatic) { + bootstrap_tour_stub(); + } this.tour = new Tour({ name: this.id, storage: this.tourStorage, diff --git a/addons/website_sale/tests/test_ui.py b/addons/website_sale/tests/test_ui.py index b024e405a1c..fcb84773036 100644 --- a/addons/website_sale/tests/test_ui.py +++ b/addons/website_sale/tests/test_ui.py @@ -14,7 +14,7 @@ class TestUi(openerp.tests.HttpCase): self.phantom_js("/", "openerp.website.Tour.run_test('shop')", "openerp.website.Tour.Shop", login="admin") def test_02_admin_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour", login="admin") + self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour", login="admin", inject=inject) def test_03_demo_checkout(self): self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", login="demo", inject=inject) From 51a0bb001bc496718c30e34701c82964ccb0cdc8 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 14 Mar 2014 12:35:10 +0100 Subject: [PATCH 02/17] [IMP] website snippet: add onfocus, onblur, onclone and onremove on snippet options; fix banner id bzr revid: chm@openerp.com-20140314113510-dqj70i85z41q5j0k --- .../static/src/js/website.snippets.editor.js | 76 +++++++++++++++---- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8ff89466368..1fddc7b869b 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -419,7 +419,7 @@ $target.data("overlay").remove(); $target.removeData("overlay"); } - $target.find("[data-snippet-id]").each(function () { + $target.find(website.snippet.globalSelector).each(function () { var $snippet = $(this); $snippet.removeData("snippet-editor"); if ($snippet.data("overlay")) { @@ -676,6 +676,30 @@ // start is call just after the init start: function () { }, + /* onFocus + * This method is called when the user click inside the snippet in the dom + */ + onFocus : function () { + }, + + /* onFocus + * This method is called when the user click outside the snippet in the dom, after a focus + */ + onBlur : function () { + }, + + /* on_clone + * This method is called when the snippet is cloned ($clone is allready inserted) + */ + on_clone: function ($clone) { + }, + + /* on_remove + * This method is called when the snippet is removed (dom is removing after this call) + */ + on_remove: function () { + }, + /* * drop_and_build_snippet * This method is called just after that a thumbnail is drag and dropped into a drop zone @@ -806,23 +830,36 @@ }); website.snippet.options.slider = website.snippet.Option.extend({ + unique_id: function () { + var id = 0; + $(".carousel").each(function () { + var cid = 1 + parseInt($(this).attr("id").replace(/[^0123456789]/g, ''),10); + if (id < cid) id = cid; + }); + return "myCarousel" + id; + }, drop_and_build_snippet: function() { - var id = $(".carousel").length; - this.id = "myCarousel" + id; + this.id = this.unique_id(); this.$target.attr("id", this.id); - this.$target.find(".carousel-control").attr("href", "#myCarousel" + id); - this.$target.find("[data-target]").attr("data-target", "#myCarousel" + id); + this.$target.find("[data-slide]").attr("href", "#" + this.id); + this.$target.find("[data-slide-to]").attr("data-target", "#" + this.id); this.rebind_event(); }, + on_clone: function ($clone) { + var id = this.unique_id(); + $clone.attr("id", id); + $clone.find("[data-slide]").attr("href", "#" + id); + $clone.find("[data-slide-to]").attr("data-target", "#" + id); + }, // rebind event to active carousel on edit mode rebind_event: function () { var self = this; - this.$target.find('.carousel-indicators [data-target]').off('click').on('click', function () { + this.$target.find('.carousel-indicators [data-slide-to]').off('click').on('click', function () { self.$target.carousel(+$(this).data('slide-to')); }); this.$target.attr('contentEditable', 'false'); - this.$target.find('.oe_structure, .content>.row').attr('contentEditable', 'true'); + this.$target.find('.oe_structure, .content>.row, [data-slide]').attr('contentEditable', 'true'); }, clean_for_save: function () { this._super(); @@ -1187,25 +1224,22 @@ this._super(); }, hide_remove_button: function() { - this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", - !this.$target.siblings().length && this.$target.parents("[data-snippet-id]:first").find("[data-snippet-id='colmd']").length > 1); + this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length); }, onFocus : function () { this._super(); this.hide_remove_button(); }, - on_clone: function () { - var $clone = this.$target.clone(false); + on_clone: function ($clone) { var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, ''); $clone.attr("class", _class); - this.$target.after($clone); this.hide_remove_button(); return false; }, on_remove: function () { if (!this.$target.siblings().length) { - var $parent = this.$target.parents("[data-snippet-id]:first"); - if($parent.find("[data-snippet-id='colmd']").length > 1) { + var $parent = this.$target.parents(".row:first"); + if($parent.find("[class*='col-md']").length > 1) { return false; } else { if (!$parent.data("snippet-editor")) { @@ -1455,7 +1489,7 @@ get_parent_block: function () { var self = this; var $button = this.$overlay.find('.oe_snippet_parent'); - var $parent = this.$target.parents("[data-snippet-id]:first"); + var $parent = this.$target.parents(website.snippet.globalSelector).first(); if ($parent.length) { $button.removeClass("hidden"); $button.off("click").on('click', function (event) { @@ -1483,12 +1517,18 @@ on_clone: function () { var $clone = this.$target.clone(false); this.$target.after($clone); + for (var i in this.styles){ + this.styles[i].on_clone($clone); + } return false; }, on_remove: function () { this.onBlur(); var index = _.indexOf(this.BuildingBlock.snippets, this.$target.get(0)); + for (var i in this.styles){ + this.styles[i].on_remove(); + } delete this.BuildingBlock.snippets[index]; this.$target.remove(); this.$overlay.remove(); @@ -1511,12 +1551,18 @@ */ onFocus : function () { this.$overlay.addClass('oe_active'); + for (var i in this.styles){ + this.styles[i].onFocus(); + } }, /* onFocus * This method is called when the user click outside the snippet in the dom, after a focus */ onBlur : function () { + for (var i in this.styles){ + this.styles[i].onBlur(); + } this.$overlay.removeClass('oe_active'); }, From 8ffc4d0a5a6eddf3bbfd76699e40420830f7b868 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 11:42:51 +0100 Subject: [PATCH 03/17] [ADD] support for presized fields in website/image also add presized field to attachments (automatically computed from datas) bzr revid: xmo@openerp.com-20140317104251-ddsa9cfpq1vkk2k1 --- addons/website/controllers/main.py | 46 ++++++++++++++++++++++-------- addons/website/models/website.py | 18 +++++++++++- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index eff28b61fcd..aee0997e4b4 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -314,6 +314,23 @@ class Website(openerp.addons.web.controllers.main.Home): '/website/image///' ], auth="public", website=True) def website_image(self, model, id, field, max_width=maxint, max_height=maxint): + """ Fetches the requested field and ensures it does not go above + (max_width, max_height), resizing it if necessary. + + Resizing is bypassed if the object provides a $field_big, which will + be interpreted as a pre-resized version of the base field. + + If the record is not found or does not have the requested field, + returns a placeholder image via :meth:`~.placeholder`. + + Sets and checks conditional response parameters: + * :mailheader:`ETag` is always set (and checked) + * :mailheader:`Last-Modified is set iif the record has a concurrency + field (``__last_update``) + + The requested field is assumed to be base64-encoded image data in + all cases. + """ Model = request.registry[model] response = werkzeug.wrappers.Response() @@ -322,15 +339,17 @@ class Website(openerp.addons.web.controllers.main.Home): ids = Model.search(request.cr, request.uid, [('id', '=', id)], context=request.context) \ - or Model.search(request.cr, openerp.SUPERUSER_ID, - [('id', '=', id), ('website_published', '=', True)], context=request.context) + or Model.search(request.cr, openerp.SUPERUSER_ID, + [('id', '=', id), ('website_published', '=', True)], context=request.context) if not ids: return self.placeholder(response) + presized = '%s_big' % field concurrency = '__last_update' [record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id], - [concurrency, field], context=request.context) + [concurrency, field, presized], + context=request.context) if concurrency in record: server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT @@ -354,25 +373,28 @@ class Website(openerp.addons.web.controllers.main.Home): if response.status_code == 304: return response - data = record[field].decode('base64') - fit = int(max_width), int(max_height) + data = (record.get(presized) or record[field]).decode('base64') - buf = cStringIO.StringIO(data) - - image = Image.open(buf) - image.load() + image = Image.open(cStringIO.StringIO(data)) response.mimetype = Image.MIME[image.format] + # record provides a pre-resized version of the base field, use that + # directly + if record.get(presized): + response.set_data(data) + return response + + fit = int(max_width), int(max_height) w, h = image.size max_w, max_h = fit if w < max_w and h < max_h: - response.data = data + response.set_data(data) else: image.thumbnail(fit, Image.ANTIALIAS) image.save(response.stream, image.format) - # invalidate content-length computed by make_conditional as writing - # to response.stream does not do it (as of werkzeug 0.9.3) + # invalidate content-length computed by make_conditional as + # writing to response.stream does not do it (as of werkzeug 0.9.3) del response.headers['Content-Length'] return response diff --git a/addons/website/models/website.py b/addons/website/models/website.py index f48dbd06be5..1a541865987 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -580,10 +580,26 @@ class ir_attachment(osv.osv): return hashlib.new('sha1', attachment_dict['datas']).hexdigest() return None + def _datas_big(self, cr, uid, ids, name, arg, context=None): + result = dict.fromkeys(ids, False) + if context and context.get('bin_size'): + return result + + for record in self.browse(cr, uid, ids, context=context): + if not record.datas: continue + try: + result[record.id] = openerp.tools.image_resize_image_big(record.datas) + except IOError: # apparently the error PIL.Image.open raises + pass + + return result + _columns = { 'datas_checksum': fields.function(_datas_checksum, size=40, string="Datas checksum", type='char', store=True, select=True), - 'website_url': fields.function(_website_url_get, string="Attachment URL", type='char') + 'website_url': fields.function(_website_url_get, string="Attachment URL", type='char'), + 'datas_big': fields.function (_datas_big, type='binary', store=True, + string="Resized file content"), } def create(self, cr, uid, values, context=None): From a2ef044c477a049875a2077e4f2b4420bb35ef75 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 12:09:04 +0100 Subject: [PATCH 04/17] [IMP] skip injection of data-oe-* attributes outside of editable mode bzr revid: xmo@openerp.com-20140317110904-k8bptgv7y2yhhh8s --- openerp/addons/base/ir/ir_qweb.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index e9e0386b4e2..0b3bf91dc1f 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -488,7 +488,6 @@ class FieldConverter(osv.AbstractModel): A default configuration key is ``widget`` which can override the field's own ``_type``. """ - content = None try: content = self.record_to_html( cr, uid, field_name, record, @@ -503,12 +502,14 @@ class FieldConverter(osv.AbstractModel): field_name, record._model._name, exc_info=True) content = None - g_att += ''.join( - ' %s="%s"' % (name, werkzeug.utils.escape(value)) - for name, value in self.attributes( - cr, uid, field_name, record, options, - source_element, g_att, t_att, qweb_context) - ) + if context and context.get('inherit_branding'): + # add branding attributes + g_att += ''.join( + ' %s="%s"' % (name, werkzeug.utils.escape(value)) + for name, value in self.attributes( + cr, uid, field_name, record, options, + source_element, g_att, t_att, qweb_context) + ) return self.render_element(cr, uid, source_element, t_att, g_att, qweb_context, content) From 44bebab6825a067ef0ddf3494d49249ada8baaf0 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 12:49:38 +0100 Subject: [PATCH 05/17] [FIX] website: typo: message let the user think that he can change the mobile view while he cannot bzr revid: chm@openerp.com-20140317114938-3ghhwm0km3u5wyu1 --- addons/website/static/src/js/website.tour.banner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index ff3ed030f76..a92934df89d 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -101,7 +101,7 @@ element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]', placement: 'right', title: _t("Check Mobile Preview"), - content: _t("Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog."), + content: _t("Scroll to check rendering and then close the mobile preview."), popover: { next: _t("Continue") }, }, { From 19aacaaea8f809ec39dcbc3e4a2e997e85c22d20 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 12:49:56 +0100 Subject: [PATCH 06/17] [FIX] website: readability block with container css bzr revid: chm@openerp.com-20140317114956-j5sqt6flie5fla31 --- addons/website/static/src/css/website.css | 2 +- addons/website/static/src/css/website.sass | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index a6e4c3eb916..1f0242d93d1 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -116,7 +116,7 @@ header a.navbar-brand img { } /* ----- BOOTSTRAP FIX ----- */ -.container .container { +.container .container, .readable .container { padding-left: 0; padding-right: 0; width: auto; diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index d6fbd0ac7bf..e08d5c1dbe3 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -85,7 +85,7 @@ header /* ----- BOOTSTRAP FIX ----- */ -.container +.container, .readable .container padding-left: 0 padding-right: 0 From d3c7750b96253e952f199abdbbb5eede67d3c873 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 13:40:56 +0100 Subject: [PATCH 07/17] [FIX] inject WORD JOINER inside p so they don't seemingly disappear after saving bzr revid: xmo@openerp.com-20140317124056-kg2jngp2mg769v7m --- addons/website/static/src/css/website.css | 6 +++++- addons/website/static/src/css/website.sass | 9 ++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 1f0242d93d1..fd5492c9bc2 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -102,6 +102,10 @@ header a.navbar-brand img { max-height: 50px; } +div:before, p:before { + content: "\2060"; +} + /* ----- Snippets Styles ----- */ .readable { font-size: 120%; @@ -217,7 +221,6 @@ footer { /* -- Hack for removing double scrollbar from mobile preview -- */ div#mobile-preview.modal { overflow: hidden; - overflow-y: hidden; } ul.nav-stacked > li > a { @@ -284,6 +287,7 @@ ul.nav-stacked > li > a { section, .carousel, .parallax, .row, .hr, .blockquote { min-height: 30px; } + .carousel, .parallax, .blockquote { overflow: hidden; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index e08d5c1dbe3..44afd627214 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -68,6 +68,12 @@ header img max-height: 50px +// add {WORD JOINER} (equivalent to deprecated ZERO WIDTH NO-BREAK SPACE) at +// the beginning of paragraphs (and divs) so they don't "disappear" after +// saving when used solely for spacing. +div:before, p:before + content: '\2060' + /* ----- Snippets Styles ----- */ .readable @@ -78,11 +84,9 @@ header /* ----- EDITOR ----- */ - .css_non_editable_mode_hidden display: none - /* ----- BOOTSTRAP FIX ----- */ .container, .readable @@ -180,7 +184,6 @@ footer /* -- Hack for removing double scrollbar from mobile preview -- */ div#mobile-preview.modal overflow: hidden - overflow-y: hidden ul.nav-stacked > li > a padding: 2px 15px From 09deba48268bed7a77050d9b1d9776b485cb44e2 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 17 Mar 2014 13:43:43 +0100 Subject: [PATCH 08/17] [FIX] tests dont generate traceback on aborted test requests bzr revid: al@openerp.com-20140317124343-kgesa5c215z19vpn --- openerp/sql_db.py | 10 +++++----- openerp/tests/common.py | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openerp/sql_db.py b/openerp/sql_db.py index 694ae02b3d9..59841656d8c 100644 --- a/openerp/sql_db.py +++ b/openerp/sql_db.py @@ -148,7 +148,7 @@ class Cursor(object): def check(f): @wraps(f) def wrapper(self, *args, **kwargs): - if self.__closed: + if self._closed: msg = 'Unable to use a closed cursor.' if self.__closer: msg += ' It was closed at %s, line %s' % self.__closer @@ -165,7 +165,7 @@ class Cursor(object): self.sql_log = _logger.isEnabledFor(logging.DEBUG) self.sql_log_count = 0 - self.__closed = True # avoid the call of close() (by __del__) if an exception + self._closed = True # avoid the call of close() (by __del__) if an exception # is raised by any of the following initialisations self._pool = pool self.dbname = dbname @@ -180,7 +180,7 @@ class Cursor(object): self.__caller = frame_codeinfo(currentframe(),2) else: self.__caller = False - self.__closed = False # real initialisation value + self._closed = False # real initialisation value self.autocommit(False) self.__closer = False @@ -189,7 +189,7 @@ class Cursor(object): self.cache = {} def __del__(self): - if not self.__closed and not self._cnx.closed: + if not self._closed and not self._cnx.closed: # Oops. 'self' has not been closed explicitly. # The cursor will be deleted by the garbage collector, # but the database connection is not put back into the connection @@ -302,7 +302,7 @@ class Cursor(object): # collected as fast as they should). The problem is probably due in # part because browse records keep a reference to the cursor. del self._obj - self.__closed = True + self._closed = True # Clean the underlying connection. self._cnx.rollback() diff --git a/openerp/tests/common.py b/openerp/tests/common.py index b0dcac47408..99f5919f2fc 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -15,9 +15,10 @@ import time import unittest2 import urllib2 import xmlrpclib - from datetime import datetime, timedelta +import werkzeug + import openerp _logger = logging.getLogger(__name__) @@ -46,6 +47,8 @@ def acquire_test_cursor(session_id): cr = HTTP_SESSION.get(session_id) if cr: cr._test_lock.acquire() + if cr._closed: + werkzeug.exceptions.abort(500) return cr def release_test_cursor(cr): From 25732de9cebc92fc4ccb231c8ad073a16b6035f6 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 14:37:00 +0100 Subject: [PATCH 09/17] [FIX] website tour: typo for banner (from usertest feedback) bzr revid: chm@openerp.com-20140317133700-8xrneej6aytua4j4 --- addons/website/static/src/js/website.tour.banner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index a92934df89d..304d4fe7ead 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -35,7 +35,7 @@ element: 'button[data-action=snippet]', placement: 'bottom', title: _t("Insert building blocks"), - content: _t("To add content in a page, you can insert building blocks."), + content: _t("Click here to insert blocks of centent in the page."), popover: { fixed: true }, }, { From cd4f83122ac61d44eeb45840aa6a2ec251ed9033 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 14:51:44 +0100 Subject: [PATCH 10/17] [FIX] reduce span of paragraph spacer thing to not break layout bzr revid: xmo@openerp.com-20140317135144-hvuxshfp4cjudnbh --- addons/website/static/src/css/website.css | 2 +- addons/website/static/src/css/website.sass | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index fd5492c9bc2..dc0628ca3c5 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -102,7 +102,7 @@ header a.navbar-brand img { max-height: 50px; } -div:before, p:before { +#wrapwrap p:empty:after { content: "\2060"; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 44afd627214..a15953e0f2b 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -69,9 +69,9 @@ header max-height: 50px // add {WORD JOINER} (equivalent to deprecated ZERO WIDTH NO-BREAK SPACE) at -// the beginning of paragraphs (and divs) so they don't "disappear" after -// saving when used solely for spacing. -div:before, p:before +// the beginning of paragraphs so they don't "disappear" after saving when +// used solely for spacing. +#wrapwrap p:empty:after content: '\2060' /* ----- Snippets Styles ----- */ From 627b5b671edd98303f3b8636d66ad11c4184c532 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 17 Mar 2014 14:52:39 +0100 Subject: [PATCH 11/17] [FIX] tests dont log traceback on aborted request while testing bzr revid: al@openerp.com-20140317135239-08aefkdxiil7bpcs --- openerp/http.py | 6 ++++-- openerp/tests/common.py | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openerp/http.py b/openerp/http.py index 34b9cf19abf..438c8ec1cc0 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -361,7 +361,7 @@ class JsonRequest(WebRequest): response['id'] = self.jsonrequest.get('id') response["result"] = self._call_function(**self.params) except AuthenticationError, e: - _logger.exception("Exception during JSON request handling.") + _logger.exception("JSON-RPC AuthenticationError in %s.", self.httprequest.path) se = serialize_exception(e) error = { 'code': 100, @@ -369,7 +369,9 @@ class JsonRequest(WebRequest): 'data': se } except Exception, e: - _logger.exception("Exception during JSON request handling.") + # Mute test cursor error for runbot + if not (openerp.tools.config['test_enable'] and isinstance(e, psycopg2.OperationalError)): + _logger.exception("JSON-RPC Exception in %s.", self.httprequest.path) se = serialize_exception(e) error = { 'code': 200, diff --git a/openerp/tests/common.py b/openerp/tests/common.py index 99f5919f2fc..a53a8d82240 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -47,8 +47,6 @@ def acquire_test_cursor(session_id): cr = HTTP_SESSION.get(session_id) if cr: cr._test_lock.acquire() - if cr._closed: - werkzeug.exceptions.abort(500) return cr def release_test_cursor(cr): From d3111df3aa5e8ca3d57e2e71c82267c7952a98ea Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 15:13:24 +0100 Subject: [PATCH 12/17] [FIX] website: The user does not see the content menu because of the tutorial is hover bzr revid: chm@openerp.com-20140317141324-th65fhx512h2p1fa --- addons/website/static/src/js/website.tour.banner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 304d4fe7ead..9c8afcd61e5 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -107,7 +107,7 @@ { waitNot: '.modal', element: '#content-menu-button', - placement: 'bottom', + placement: 'left', title: _t("Add new pages and menus"), content: _t("The 'Content' menu allows you to add pages or add the top menu."), popover: { next: _t("Close Tutorial") }, From 363bbd00dbf40670303947dc290723c7beb74199 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 15:17:49 +0100 Subject: [PATCH 13/17] [FIX] website_sale: display empty message for public user and allow admin to edit 'no product found' message bzr revid: chm@openerp.com-20140317141749-vmsx9x1kkmygnc61 --- addons/website_sale/views/website_sale.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index a3576b90c2c..f0c14ee1573 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -200,8 +200,8 @@
-

No product defined.

- +

No product found.

+

Use the 'Content' top menu to create a new product.

From d87bc392ce05034bcb3ac839e5160e8e01afca5d Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 17 Mar 2014 15:42:54 +0100 Subject: [PATCH 14/17] [IMP] website snippet option: rename data-class into data-value bzr revid: chm@openerp.com-20140317144254-18cscu2yx5thmb89 --- .../static/src/js/website.snippets.editor.js | 51 ++++++++------- addons/website/views/snippets.xml | 62 +++++++++---------- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 1fddc7b869b..b8533236e01 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -623,7 +623,7 @@ this.required = this.$el.data("required"); this.set_active(); - this.$el.find('li[data-class] a').on('mouseover mouseout click', _.bind(this._mouse, this)); + this.$el.find('li[data-value] a').on('mouseover mouseout click', _.bind(this._mouse, this)); this.$target.on('snippet-style-reset', _.bind(this.set_active, this)); this.start(); @@ -643,9 +643,9 @@ var $prev, $next; if (event.type === 'mouseout') { $prev = $(event.currentTarget).parent(); - $next = this.$el.find("li[data-class].active"); + $next = this.$el.find("li[data-value].active"); } else { - $prev = this.$el.find("li[data-class].active"); + $prev = this.$el.find("li[data-value].active"); $next = $(event.currentTarget).parent(); } if (!$prev.length) { @@ -709,6 +709,7 @@ }, /* select * called when a user select an item + * li must have data-value attribute * variables: np = {$next, $prev} * $next is false if they are no next item selected * $prev is false if they are no previous item selected @@ -717,10 +718,10 @@ var self = this; // add or remove html class if (np.$prev) { - this.$target.removeClass(np.$prev.data('class' || "")); + this.$target.removeClass(np.$prev.data('value' || "")); } if (np.$next) { - this.$target.addClass(np.$next.data('class') || ""); + this.$target.addClass(np.$next.data('value') || ""); } }, /* preview @@ -734,10 +735,10 @@ // add or remove html class if (np.$prev) { - this.$target.removeClass(np.$prev.data('class') || ""); + this.$target.removeClass(np.$prev.data('value') || ""); } if (np.$next) { - this.$target.addClass(np.$next.data('class') || ""); + this.$target.addClass(np.$next.data('value') || ""); } }, /* set_active @@ -747,14 +748,14 @@ set_active: function () { var self = this; this.$el.find('li').removeClass("active"); - var $active = this.$el.find('li[data-class]') + var $active = this.$el.find('li[data-value]') .filter(function () { var $li = $(this); - return ($li.data('class') && self.$target.hasClass($li.data('class'))); + return ($li.data('value') && self.$target.hasClass($li.data('value'))); }) .first() .addClass("active"); - this.$el.find('li:has(li[data-class].active)').addClass("active"); + this.$el.find('li:has(li[data-value].active)').addClass("active"); }, /* clean_for_save * function called just before save vue @@ -773,7 +774,7 @@ start: function () { this._super(); var src = this._get_bg(); - this.$el.find("li[data-class].active.oe_custom_bg").data("src", src); + this.$el.find("li[data-value].active.oe_custom_bg").data("src", src); }, select: function(event, np) { var self = this; @@ -789,7 +790,7 @@ }); editor.on('cancel', self, function () { if (!np.$prev || np.$prev.data("src") === "") { - self.$target.removeClass(np.$next.data("class")); + self.$target.removeClass(np.$next.data("value")); self.$target.trigger("snippet-style-change", [self, np]); } }); @@ -799,7 +800,7 @@ } } else { this._set_bg(false); - this.$target.removeClass(np.$prev.data("class")); + this.$target.removeClass(np.$prev.data("value")); } }, preview: function (event, np) { @@ -812,20 +813,20 @@ var self = this; var bg = self.$target.css("background-image"); this.$el.find('li').removeClass("active"); - var $active = this.$el.find('li[data-class]') + var $active = this.$el.find('li[data-value]') .filter(function () { var $li = $(this); return ($li.data('src') && bg.indexOf($li.data('src')) >= 0) || - (!$li.data('src') && self.$target.hasClass($li.data('class'))); + (!$li.data('src') && self.$target.hasClass($li.data('value'))); }) .first(); if (!$active.length) { $active = this.$target.css("background-image") !== 'none' ? - this.$el.find('li[data-class].oe_custom_bg') : - this.$el.find('li[data-class=""]'); + this.$el.find('li[data-value].oe_custom_bg') : + this.$el.find('li[data-value=""]'); } $active.addClass("active"); - this.$el.find('li:has(li[data-class].active)').addClass("active"); + this.$el.find('li:has(li[data-value].active)').addClass("active"); } }); @@ -942,7 +943,7 @@ }, load_style_options : function () { this._super(); - $(".snippet-style-size li[data-class='']").remove(); + $(".snippet-style-size li[data-value='']").remove(); }, start : function () { var self = this; @@ -960,11 +961,11 @@ $active.css("background-image", self.$target.css("background-image")); } if (np.$prev) { - $active.removeClass(np.$prev.data("class")); + $active.removeClass(np.$prev.data("value")); } if (np.$next) { - $active.addClass(np.$next.data("class")); - add_class(np.$next.data("class")); + $active.addClass(np.$next.data("value")); + add_class(np.$next.data("value")); } }); this.$target.on('slid', function () { // slide.bs.carousel @@ -986,11 +987,13 @@ var bg = this.$target.data("snippet-option-ids").background; if (!bg) return $clone; - var $styles = bg.$el.find("li[data-class]:not(.oe_custom_bg)"); + var $styles = bg.$el.find("li[data-value]:not(.oe_custom_bg)"); var styles_index = $styles.index($styles.filter(".active")[0]); + $styles.removeClass("active"); var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]); + $select.addClass("active"); $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : ""); - $clone.addClass($select.data("class") || ""); + $clone.addClass($select.data("value") || ""); return $clone; }, diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index f91bd969c70..cdc4bd8f961 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -860,7 +860,7 @@ @@ -873,51 +873,51 @@ -
  • None
  • +
  • None
  • -
  • Choose an image...
  • +
  • Choose an image...
  • @@ -927,9 +927,9 @@ From 3d2452e8d4f150f193d769d8184ed2a805e02925 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 17 Mar 2014 16:18:10 +0100 Subject: [PATCH 15/17] [FIX] Force routing map to be rebuilt between each module test suite bzr revid: fme@openerp.com-20140317151810-tgsvv0ip4you1qko --- openerp/modules/loading.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index 7ca0a6785d6..681d8534c05 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -187,6 +187,10 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= # Yamel test report.record_result(load_test(module_name, idref, mode)) # Python tests + ir_http = registry['ir.http'] + if hasattr(ir_http, '_routing_map'): + # Force routing map to be rebuilt between each module test suite + del(ir_http._routing_map) report.record_result(openerp.modules.module.run_unit_tests(module_name, cr.dbname)) processed_modules.append(package.name) From 4988b40ef5255ec3e1dd3c769461de53f9865ff3 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 16:24:24 +0100 Subject: [PATCH 16/17] [FIX] qweb tests re. inherit_branding enabling/disabling injection of data attributes bzr revid: xmo@openerp.com-20140317152424-bchnq81cn2e7fhjb --- openerp/addons/base/tests/test_qweb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/tests/test_qweb.py b/openerp/addons/base/tests/test_qweb.py index 71beaab605a..ca1503a0c24 100644 --- a/openerp/addons/base/tests/test_qweb.py +++ b/openerp/addons/base/tests/test_qweb.py @@ -14,7 +14,8 @@ class TestQWebTField(common.TransactionCase): self.engine = self.registry('ir.qweb') def context(self, values): - return ir_qweb.QWebContext(self.cr, self.uid, values) + return ir_qweb.QWebContext( + self.cr, self.uid, values, context={'inherit_branding': True}) def test_trivial(self): field = document.createElement('span') From 5196cd6e8c577d35c5788df70c7c59d8e7176183 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Mar 2014 16:25:02 +0100 Subject: [PATCH 17/17] [FIX] website: set inherit_branding in test contexts to trigger generation of relevant edition attributes bzr revid: xmo@openerp.com-20140317152502-6a2agi1xjooi516e --- addons/website/tests/test_converter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py index d31df8e6956..ca72a361ac2 100644 --- a/addons/website/tests/test_converter.py +++ b/addons/website/tests/test_converter.py @@ -135,7 +135,7 @@ class TestConvertBack(common.TransactionCase): rendered = self.registry('website.qweb').render_tag_field( e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, { 'record': record, - })) + }, context={'inherit_branding': True})) element = html.fromstring( rendered, parser=html.HTMLParser(encoding='utf-8')) @@ -216,7 +216,7 @@ class TestConvertBack(common.TransactionCase): rendered = self.registry('website.qweb').render_tag_field( e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, { 'record': record, - })) + }, context={'inherit_branding': True})) element = html.fromstring(rendered, parser=html.HTMLParser(encoding='utf-8')) # emulate edition