From 02224a3f112426c9e15a8af7cde2e7a5406d1885 Mon Sep 17 00:00:00 2001 From: "Dharti Ratani (Open ERP)" Date: Thu, 12 Dec 2013 10:55:44 +0530 Subject: [PATCH 001/129] [FIX]Added the refernce of the supplier in price_get method while creating purchase order from purchase requisition, as without supplier the price unit in PO was 0 when the pricelist item was based on Supplier Price in the product form bzr revid: dhr@tinyerp.com-20131212052544-skljxsrxea4o8efm --- addons/purchase_requisition/purchase_requisition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index c1d80c7bf46..c31f8a69a37 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -109,7 +109,7 @@ class purchase_requisition(osv.osv): seller_delay = product_supplier.delay seller_qty = product_supplier.qty supplier_pricelist = supplier.property_product_pricelist_purchase or False - seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, False, {'uom': default_uom_po_id})[supplier_pricelist.id] + seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, supplier.id, {'uom': default_uom_po_id})[supplier_pricelist.id] if seller_qty: qty = max(qty,seller_qty) date_planned = self._planned_date(requisition_line.requisition_id, seller_delay) From 262a405b0ab1018bde19e61d38a4525582831a16 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 3 Jan 2014 14:07:03 +0100 Subject: [PATCH 002/129] Remove old FR VAT taxes 19.6% and 7.0%. Remove the FR VAT tax 5.0% that has never seen the light ! bzr revid: alexis@via.ecp.fr-20140103130703-kpf5rojc4azwq8w1 --- addons/l10n_fr/fr_fiscal_templates.xml | 79 ----- addons/l10n_fr/fr_tax.xml | 387 ------------------------- 2 files changed, 466 deletions(-) diff --git a/addons/l10n_fr/fr_fiscal_templates.xml b/addons/l10n_fr/fr_fiscal_templates.xml index bf49d15e08c..cca96422c22 100644 --- a/addons/l10n_fr/fr_fiscal_templates.xml +++ b/addons/l10n_fr/fr_fiscal_templates.xml @@ -40,11 +40,6 @@ - - - - - @@ -57,22 +52,12 @@ - - - - - - - - - - @@ -91,17 +76,6 @@ - - - - - - - - - - - @@ -124,17 +98,6 @@ - - - - - - - - - - - @@ -146,17 +109,6 @@ - - - - - - - - - - - @@ -177,11 +129,6 @@ - - - - - @@ -194,22 +141,12 @@ - - - - - - - - - - @@ -224,11 +161,6 @@ - - - - - @@ -241,23 +173,12 @@ - - - - - - - - - - - diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml index 8633cca7141..22e1e12f03c 100644 --- a/addons/l10n_fr/fr_tax.xml +++ b/addons/l10n_fr/fr_tax.xml @@ -29,28 +29,6 @@ sale - - - - TVA collectée (vente) 19,6% - 19.6 - - percent - - - - - - - - - - - - - - sale - @@ -96,51 +74,7 @@ sale - - - TVA collectée (vente) 7,0% - 7.0 - - percent - - - - - - - - - - - - - - sale - - - - TVA collectée (vente) 5,0% - 5.0 - - percent - - - - - - - - - - - - - - sale - - - TVA collectée (vente) 5,5% 5.5 @@ -208,28 +142,6 @@ purchase - - - TVA déductible (achat) 19,6% - ACH-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% @@ -274,51 +186,7 @@ purchase - - - TVA déductible (achat) 7,0% - ACH-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% - ACH-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% ACH-5.5 @@ -387,29 +255,6 @@ purchase - - - TVA déductible (achat) 19,6% TTC - ACH-19.6-TTC - - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% TTC @@ -456,53 +301,7 @@ purchase - - - TVA déductible (achat) 7,0% TTC - ACH-7.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% TTC - ACH-5.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% TTC ACH-5.5-TTC @@ -573,28 +372,6 @@ purchase - - - TVA déd./immobilisation (achat) 19,6% - IMMO-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déd./immobilisation (achat) 8,5% @@ -639,51 +416,7 @@ purchase - - - TVA déd./immobilisation (achat) 7,0% - IMMO-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déd./immobilisation (achat) 5,0% - IMMO-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déd./immobilisation (achat) 5,5% IMMO-5.5 @@ -751,28 +484,6 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_due-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA due s/ acq. intracommunautaire (achat) 8,5% @@ -817,51 +528,7 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_due-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA due s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_due-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA due s/ acq. intracommunautaire (achat) 5,5% ACH_UE_due-5.5 @@ -925,24 +592,6 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_ded.-19.6 - - percent - - - - - - - - - - purchase - - TVA déd. s/ acq. intracommunautaire (achat) 8,5% @@ -979,43 +628,7 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_ded.-7.0 - - percent - - - - - - - - - - purchase - - - - TVA déd. s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_ded.-5.0 - - percent - - - - - - - - - - purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 5,5% ACH_UE_ded.-5.5 From 6abf20906e7b5d3e319a227301fbcf1a6d4492b7 Mon Sep 17 00:00:00 2001 From: jke-openerp Date: Sat, 1 Feb 2014 21:04:13 +0100 Subject: [PATCH 003/129] [Typo] Replace t-esc in t-field for amount_untaxed in checkout of website_sale. Else display monetary does not work bzr revid: jke@openerp.com-20140201200413-zww5pcobl6vmvqe1 --- 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 ed2007e27c7..cc1220a57e7 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -832,7 +832,7 @@
Subtotal:
-
From c18d3504689228807d6703b920b43d6c4e6e6721 Mon Sep 17 00:00:00 2001 From: jke-openerp Date: Sat, 1 Feb 2014 21:47:41 +0100 Subject: [PATCH 004/129] [FIX] Fix bug where button with contenteditable does not work in cross browser. Fix : replace tag 'button' by 'a' with class 'a-submit' bzr revid: jke@openerp.com-20140201204741-ygdxaauq0m5axttg --- addons/website_sale/static/src/js/website_sale.js | 4 ++++ addons/website_sale/views/website_sale.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 7f6d24373f7..eedbb5a046c 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -60,6 +60,10 @@ $(document).ready(function () { return false; }); + $('.a-submit').on('click', function () { + $(this).closest('form').submit(); + }); + // change price when they are variants $('form.js_add_cart_json label').on('mouseup', function (ev) { ev.preventDefault(); diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index ed2007e27c7..d6c86c81e8b 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -331,7 +331,7 @@ }'/>
- + Add to Cart


From 2e3e44255ebb4e7530c48b35df62a12c99b4323c Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Thu, 20 Feb 2014 15:52:00 +0530 Subject: [PATCH 005/129] [FIX] Editor bar is showing border. bzr revid: psa@tinyerp.com-20140220102200-2r2urlrrkngv20at --- addons/website/static/src/css/editor.css | 1 + addons/website/static/src/css/editor.sass | 1 + 2 files changed, 2 insertions(+) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 4fb4496e692..df2188e794d 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -26,6 +26,7 @@ -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; + -ms-filter: "alpha(opacity=50)"; } /* ---- OpenERP Style ---- {{{ */ diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 2e6513f7c1d..49b71ffefdf 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -23,6 +23,7 @@ background: transparent border: none +box-shadow(none) + -ms-filter: "alpha(opacity=50)" // }}} From 2e49e46698c52edf29c7a42b1a67f8f2cb110e34 Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Fri, 21 Feb 2014 11:42:36 +0530 Subject: [PATCH 006/129] [FIX] Fixed the sliding issue of carousel library in IE9. The transition was not working in any snippet bzr revid: psa@tinyerp.com-20140221061236-7jcbgae7m081pjv8 --- addons/website/static/lib/bootstrap/js/bootstrap.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/website/static/lib/bootstrap/js/bootstrap.js b/addons/website/static/lib/bootstrap/js/bootstrap.js index b22be6fce1d..2d31d0eb3c3 100644 --- a/addons/website/static/lib/bootstrap/js/bootstrap.js +++ b/addons/website/static/lib/bootstrap/js/bootstrap.js @@ -418,6 +418,17 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery" setTimeout(function () { that.$element.trigger('slid') }, 0) }) .emulateTransitionEnd(600) + } else if(this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.animate({left: (direction == 'right' ? '100%' : '-100%')}, 600, function(){ + $active.removeClass('active') + that.sliding = false + setTimeout(function() { that.$element.trigger('slid')}, 0) + }) + $next.addClass(type).css({left: (direction == 'right' ? '-100%' : '100%')}).animate({left: 0}, 600, function() { + $next.removeClass(type).addClass('active') + }) } else { this.$element.trigger(e) if (e.isDefaultPrevented()) return From e7306f2c4ad577fd94bbb28e1c7636ef6799bad7 Mon Sep 17 00:00:00 2001 From: Gery Debongnie Date: Wed, 26 Feb 2014 12:17:10 +0100 Subject: [PATCH 007/129] [FIX] fixes various problems with the graph view and the filters in the searchbar. Now, it should properly compute and update the groupby/col_groupby in the search bar (addon web_graph) bzr revid: ged@openerp.com-20140226111710-q11qx1tb2l0nryxf --- addons/web_graph/static/src/css/graph.css | 4 + addons/web_graph/static/src/js/graph_view.js | 92 ++++++++++++------- .../web_graph/static/src/js/graph_widget.js | 4 + 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/addons/web_graph/static/src/css/graph.css b/addons/web_graph/static/src/css/graph.css index b7d82597c4c..2213f655a6b 100644 --- a/addons/web_graph/static/src/css/graph.css +++ b/addons/web_graph/static/src/css/graph.css @@ -1,3 +1,7 @@ +.graph_main_content { + position: relative; +} + .graph_main_content td { font-size: 12px; margin: 45px; diff --git a/addons/web_graph/static/src/js/graph_view.js b/addons/web_graph/static/src/js/graph_view.js index ace2f14ae20..25a1f932cc4 100644 --- a/addons/web_graph/static/src/js/graph_view.js +++ b/addons/web_graph/static/src/js/graph_view.js @@ -81,9 +81,9 @@ instance.web_graph.GraphView = instance.web.View.extend({ this.ignore_do_search = false; return; } - var self = this, - col_group_by = self.get_groupbys_from_searchview('ColGroupBy', 'col_group_by'); + groupbys = this.get_groupbys_from_searchview(), + col_group_by = groupbys.col_group_by; if (!this.graph_widget) { if (group_by.length) { @@ -95,10 +95,10 @@ instance.web_graph.GraphView = instance.web.View.extend({ this.graph_widget = new openerp.web_graph.Graph(this, this.model, domain, this.widget_config); this.graph_widget.appendTo(this.$el); this.ViewManager.on('switch_mode', this, function (e) { - var col_gb = self.get_groupbys_from_searchview('ColGroupBy', 'col_group_by'), - row_gb = self.get_groupbys_from_searchview('GroupBy', 'group_by'); - - if (e === 'graph') this.graph_widget.set(domain, row_gb, col_gb); + if (e === 'graph') { + var group_bys = self.get_groupbys_from_searchview(); + this.graph_widget.set(domain, group_bys.group_by, group_bys.col_group_by); + } }); return; } @@ -106,19 +106,27 @@ instance.web_graph.GraphView = instance.web.View.extend({ this.graph_widget.set(domain, group_by, col_group_by); }, - extract_groupby: function (cat_field, context) { - context = (_.isString(context)) ? py.eval(context) : context; - return context[cat_field]; - }, + get_groupbys_from_searchview: function () { + var result = { group_by: [], col_group_by: []}, + searchdata = this.search_view.build_search_data(); - get_groupbys_from_searchview: function (cat_name, cat_field) { - var self=this, - facet = this.search_view.query.findWhere({category:cat_name}), - groupby_list = facet ? facet.values.models : []; - return _.map(groupby_list, function (g) { - var context = g.attributes.value.attrs.context; - return self.extract_groupby(cat_field, context); + _.each(searchdata.groupbys, function (data) { + data = (_.isString(data)) ? py.eval(data) : data; + result.group_by = result.group_by.concat(data.group_by); + if (data.col_group_by) { + result.col_group_by = result.col_group_by.concat(data.col_group_by); + } }); + + if (result.col_group_by.length) { + return result; + } + _.each(searchdata.contexts, function (context) { + if (context.col_group_by) { + result.col_group_by = result.col_group_by.concat(context.col_group_by); + } + }); + return result; }, do_show: function () { @@ -132,11 +140,20 @@ instance.web_graph.GraphView = instance.web.View.extend({ // add groupby to the search view register_groupby: function(row_groupby, col_groupby) { - var query = this.search_view.query; + var query = this.search_view.query, + groupbys = this.get_groupbys_from_searchview(), + search_row_groupby = groupbys.group_by, + search_col_groupby = groupbys.col_group_by, + row_gb_changed = !_.isEqual(_.pluck(row_groupby, 'field'), search_row_groupby), + col_gb_changed = !_.isEqual(_.pluck(col_groupby, 'field'), search_col_groupby); if (!_.has(this.search_view, '_s_groupby')) { return; } - if (row_groupby.length && col_groupby.length) { + if (!row_gb_changed && !col_gb_changed) { + return; + } + + if (row_gb_changed && col_gb_changed) { // when two changes to the search view will be done, the method do_search // will be called twice, once with the correct groupby and incorrect col_groupby, // and once with correct informations. This flag is necessary to prevent the @@ -144,26 +161,31 @@ instance.web_graph.GraphView = instance.web.View.extend({ this.ignore_do_search = true; } - // add row groupbys - var row_facet = this.make_row_groupby_facets(row_groupby), - row_search_facet = query.findWhere({category:'GroupBy'}); + if (row_gb_changed) { + // add row groupbys + var row_facet = this.make_row_groupby_facets(row_groupby), + row_search_facet = query.findWhere({category:'GroupBy'}); - if (row_search_facet) { - row_search_facet.values.reset(row_facet.values); - } else { - if (row_groupby.length) { - query.add(row_facet); + if (row_search_facet) { + row_search_facet.values.reset(row_facet.values); + } else { + if (row_groupby.length) { + query.add(row_facet); + } } } - // add col groupbys - var col_facet = this.make_col_groupby_facets(col_groupby), - col_search_facet = query.findWhere({category:'ColGroupBy'}); - if (col_search_facet) { - col_search_facet.values.reset(col_facet.values); - } else { - if (col_groupby.length) { - query.add(col_facet); + if (col_gb_changed) { + // add col groupbys + var col_facet = this.make_col_groupby_facets(col_groupby), + col_search_facet = query.findWhere({category:'ColGroupBy'}); + + if (col_search_facet) { + col_search_facet.values.reset(col_facet.values); + } else { + if (col_groupby.length) { + query.add(col_facet); + } } } }, diff --git a/addons/web_graph/static/src/js/graph_widget.js b/addons/web_graph/static/src/js/graph_widget.js index 26f479790df..f060e62fa8c 100644 --- a/addons/web_graph/static/src/js/graph_widget.js +++ b/addons/web_graph/static/src/js/graph_widget.js @@ -37,6 +37,8 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ if (this.mode !== 'pivot') { this.$('.graph_heatmap label').addClass('disabled'); + } else { + this.$('.graph_main_content').css('position', 'initial'); } return this.model.call('fields_get', []).then(function (f) { @@ -159,8 +161,10 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ if (mode === 'pivot') { this.$('.graph_heatmap label').removeClass('disabled'); + this.$('.graph_main_content').css('position', 'initial'); } else { this.$('.graph_heatmap label').addClass('disabled'); + this.$('.graph_main_content').css('position', 'relative'); } this.display_data(); }, From eb1eb48ef39a7eabcf96a2d130ddf81c360b2b53 Mon Sep 17 00:00:00 2001 From: Gery Debongnie Date: Wed, 26 Feb 2014 14:04:24 +0100 Subject: [PATCH 008/129] [IMP] optimizes pivot table so that it doesn't reload data when the groupbys have been changed in such a way that it should only fold some headers (addon web_graph) bzr revid: ged@openerp.com-20140226130424-rtb5iof0c14caf56 --- addons/web_graph/static/src/js/graph_widget.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/web_graph/static/src/js/graph_widget.js b/addons/web_graph/static/src/js/graph_widget.js index f060e62fa8c..9498fbfc93c 100644 --- a/addons/web_graph/static/src/js/graph_widget.js +++ b/addons/web_graph/static/src/js/graph_widget.js @@ -151,6 +151,13 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ return; } + if (!dom_changed && col_reduced && row_reduced) { + this.pivot.fold_with_depth(this.pivot.rows, row_gbs.length); + this.pivot.fold_with_depth(this.pivot.cols, col_gbs.length); + this.display_data(); + return; + } + if (dom_changed || row_gb_changed || col_gb_changed) { this.pivot.set(domain, row_gbs, col_gbs).then(this.proxy('display_data')); } From b453252c2dc10b8361b557229eec85b8684a6858 Mon Sep 17 00:00:00 2001 From: Gery Debongnie Date: Wed, 26 Feb 2014 15:39:19 +0100 Subject: [PATCH 009/129] [IMP] this commit adds two css classes (graph_pivot_mode and graph_chart_mode) to better separate the ui from the code in graph view (addon web_graph) bzr revid: ged@openerp.com-20140226143919-cejqoq2kd4iwrq04 --- addons/web_graph/static/src/css/graph.css | 8 ++++++-- addons/web_graph/static/src/js/graph_widget.js | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/addons/web_graph/static/src/css/graph.css b/addons/web_graph/static/src/css/graph.css index 2213f655a6b..1949028d6d8 100644 --- a/addons/web_graph/static/src/css/graph.css +++ b/addons/web_graph/static/src/css/graph.css @@ -1,5 +1,9 @@ -.graph_main_content { - position: relative; +.graph_pivot_mode { + position:initial; +} + +.graph_chart_mode { + position:relative; } .graph_main_content td { diff --git a/addons/web_graph/static/src/js/graph_widget.js b/addons/web_graph/static/src/js/graph_widget.js index 9498fbfc93c..ac2a581616d 100644 --- a/addons/web_graph/static/src/js/graph_widget.js +++ b/addons/web_graph/static/src/js/graph_widget.js @@ -37,8 +37,9 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ if (this.mode !== 'pivot') { this.$('.graph_heatmap label').addClass('disabled'); + this.$('.graph_main_content').addClass('graph_chart_mode'); } else { - this.$('.graph_main_content').css('position', 'initial'); + this.$('.graph_main_content').addClass('graph_pivot_mode'); } return this.model.call('fields_get', []).then(function (f) { @@ -168,10 +169,10 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ if (mode === 'pivot') { this.$('.graph_heatmap label').removeClass('disabled'); - this.$('.graph_main_content').css('position', 'initial'); + this.$('.graph_main_content').removeClass('graph_chart_mode').addClass('graph_pivot_mode'); } else { this.$('.graph_heatmap label').addClass('disabled'); - this.$('.graph_main_content').css('position', 'relative'); + this.$('.graph_main_content').removeClass('graph_pivot_mode').addClass('graph_chart_mode'); } this.display_data(); }, From a37f96d0438e8ab3984f659110004c19c9f01013 Mon Sep 17 00:00:00 2001 From: Gery Debongnie Date: Wed, 26 Feb 2014 15:40:32 +0100 Subject: [PATCH 010/129] [FIX] adds a missing semicolon... :/ (addon web_graph) bzr revid: ged@openerp.com-20140226144032-st0eqbkzco8n2unk --- addons/web_graph/static/src/js/graph_widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_graph/static/src/js/graph_widget.js b/addons/web_graph/static/src/js/graph_widget.js index ac2a581616d..78fa7ceff22 100644 --- a/addons/web_graph/static/src/js/graph_widget.js +++ b/addons/web_graph/static/src/js/graph_widget.js @@ -376,7 +376,7 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({ this.$('.graph_main_content svg').remove(); this.$('.graph_main_content div').remove(); this.table.empty(); - this.table.toggleClass('heatmap', this.heatmap_mode !== 'none') + this.table.toggleClass('heatmap', this.heatmap_mode !== 'none'); this.width = this.$el.width(); this.height = Math.min(Math.max(document.documentElement.clientHeight - 116 - 60, 250), Math.round(0.8*this.$el.width())); From e1daea744f65b8fc432b5aca6b57c029ccb6190b Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 26 Feb 2014 16:55:36 +0100 Subject: [PATCH 011/129] [FIX] web: bootstrap template rendered using request uid bzr revid: chs@openerp.com-20140226155536-5z7r50bzxxackg1d --- addons/web/controllers/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 03ae42f2f4e..b84efc72e14 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -616,7 +616,8 @@ def render_bootstrap_template(db, template, values=None, debug=False, lazy=False registry = openerp.modules.registry.RegistryManager.get(db) with registry.cursor() as cr: view_obj = registry["ir.ui.view"] - return view_obj.render(cr, openerp.SUPERUSER_ID, template, values) + uid = request.uid or openerp.SUPERUSER_ID + return view_obj.render(cr, uid, template, values) if lazy: return LazyResponse(callback, template=template, values=values) else: From 11e0e64769b0349ebbb203c52027c4a4fa8c988c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 26 Feb 2014 18:00:02 +0100 Subject: [PATCH 012/129] [FIX] mail: notification url: only web, not login bzr revid: tde@openerp.com-20140226170002-ndr090idip3cyx3n --- addons/mail/mail_mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 9d8de07d4f5..a33eaaea15d 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -161,7 +161,7 @@ class mail_mail(osv.Model): elif mail.model and mail.res_id: fragment.update(model=mail.model, res_id=mail.res_id) - url = urljoin(base_url, "/web/login?%s#%s" % (urlencode(query), urlencode(fragment))) + url = urljoin(base_url, "/web?%s#%s" % (urlencode(query), urlencode(fragment))) return _("""Access your messages and documents in OpenERP""") % url else: return None From 50ecb598e71b385083434ff78079ddee4a87078e Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 26 Feb 2014 18:11:14 +0100 Subject: [PATCH 013/129] [FIX] website: website settings should inherit of res.config.settings and not base.config.settings, as it create a new page settings, instead of extending one bzr revid: dle@openerp.com-20140226171114-jwthykyud66l7o6x --- addons/website/models/res_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/models/res_config.py b/addons/website/models/res_config.py index 1ae491388a3..dd86f824602 100644 --- a/addons/website/models/res_config.py +++ b/addons/website/models/res_config.py @@ -3,7 +3,7 @@ from openerp.osv import fields, osv class website_config_settings(osv.osv_memory): _name = 'website.config.settings' - _inherit = 'base.config.settings' + _inherit = 'res.config.settings' _columns = { 'website_id': fields.many2one('website', string="website", required=True), From 70d3f251a3e5d75361aadca89d9c665666afdf91 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 26 Feb 2014 18:23:00 +0100 Subject: [PATCH 014/129] [REVERT] rev 8788.1.1769: The exchange rate of the currency of the pricelist should not be applied as this price computation method does not depends of any pricelist bzr revid: dle@openerp.com-20140226172300-z2okr3vpjuwxs4mz --- addons/product/product.py | 17 ++++------------- addons/website_sale/views/website_sale.xml | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index 83e57e36489..3380db5a4aa 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -548,15 +548,10 @@ class product_product(osv.osv): _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) def _product_lst_price(self, cr, uid, ids, name, arg, context=None): - res = dict.fromkeys(ids, 0.0) + res = {} product_uom_obj = self.pool.get('product.uom') - - # retrieve pricelist - pricelist = None - if context.get('pricelist'): - pricelist = self.pool['product.pricelist'].browse(cr, uid, context.get('pricelist'), context=context) - base_currency = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id - + for id in ids: + res.setdefault(id, 0.0) for product in self.browse(cr, uid, ids, context=context): if 'uom' in context: uom = product.uos_id or product.uom_id @@ -564,11 +559,7 @@ class product_product(osv.osv): uom.id, product.list_price, context['uom']) else: res[product.id] = product.list_price - res[product.id] = (res[product.id] or 0.0) * (product.price_margin or 1.0) + product.price_extra - # update the result, according to the eventual pricelist currency - if pricelist and pricelist.currency_id: - res[product.id] = self.pool['res.currency'].compute( - cr, uid, base_currency.id, pricelist.currency_id.id, res[product.id], round=False, context=context) + res[product.id] = (res[product.id] or 0.0) * (product.price_margin or 1.0) + product.price_extra return res def _save_product_lst_price(self, cr, uid, product_id, field_name, field_value, arg, context=None): diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index f7317c9fc5e..64f5d5a9b59 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -69,7 +69,7 @@
- + div { + z-index: 1; + position: absolute; + border-style: dashed; + border-width: 1px; + border-color: #666666; + -webkit-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; + -moz-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; + box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.5), 0px 0px 0px 1px rgba(255, 255, 255, 0.5) inset; +} .oe_overlay .oe_handle.e:before, .oe_overlay .oe_handle.w:before, .oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before, .oe_overlay .oe_handle.size .oe_handle_button { + z-index: 2; position: relative; top: 50%; left: 50%; @@ -270,56 +272,70 @@ -moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); } +.oe_overlay .oe_handle.e, .oe_overlay .oe_handle.w { + top: 4px; + height: 100%; +} .oe_overlay .oe_handle.e:before, .oe_overlay .oe_handle.w:before { content: "\f0d9-\f0da"; line-height: 16px; } +.oe_overlay .oe_handle.e > div, .oe_overlay .oe_handle.w > div { + width: 0; + height: 100%; + top: 2px; + left: 8px; +} .oe_overlay .oe_handle.e { left: auto; - top: 2px; - height: 100%; right: -6px; cursor: w-resize; } .oe_overlay .oe_handle.w { - top: 2px; - height: 100%; left: -6px; cursor: e-resize; } +.oe_overlay .oe_handle.s, .oe_overlay .oe_handle.n { + left: 2px; + width: 100%; +} .oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before { - z-index: 0; content: "\f07d"; text-align: center; padding: 1px; } +.oe_overlay .oe_handle.s > div, .oe_overlay .oe_handle.n > div { + width: 100%; + height: 0; + top: 7px; + left: 1px; +} .oe_overlay .oe_handle.s { top: auto; - left: 2px; - width: 100%; - bottom: -6px; cursor: n-resize; } .oe_overlay .oe_handle.n { - left: 2px; - width: 100%; - top: -6px; cursor: s-resize; } +.oe_overlay .oe_handle.n > div { + top: 5px; +} .oe_overlay .oe_handle.size { + z-index: 3; top: auto; - left: 2px; - width: 100%; + left: 50%; bottom: -6px; } .oe_overlay .oe_handle.size .oe_handle_button { - z-index: 1; + z-index: 3; content: "Resize"; width: 64px; text-align: center; margin-left: -32px; margin-top: -10px; cursor: row-resize; + left: 0px; + top: 9px; } .oe_overlay .oe_handle.size .oe_handle_button:hover { background: rgba(30, 30, 30, 0.8); @@ -328,25 +344,20 @@ -moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7); } -.oe_overlay .oe_handle.size div { - border-style: dashed; - border-width: 0 0 1px 0; - border-color: rgba(0, 0, 0, 0.5); - position: relative; - top: 8px; -} .oe_overlay .icon.btn { display: inline-block; } .oe_overlay .oe_overlay_options { position: absolute; - width: 100%; + left: 50% !important; text-align: center; top: -11px; z-index: 1002; } +.oe_overlay .oe_overlay_options > .btn-group { + left: -50%; +} .oe_overlay .oe_overlay_options .btn, .oe_overlay .oe_overlay_options a { - pointer-events: auto; cursor: pointer; } .oe_overlay .oe_overlay_options .dropdown { @@ -505,7 +516,6 @@ box-shadow: 0px 3px 17px rgba(99, 53, 150, 0.59); } .oe_snippet_editor .oe_snippet > * { - pointer-events: none; margin-top: 16px; line-height: 1em; zoom: 0.6; diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass index d185ddd2932..afdb261a2a5 100644 --- a/addons/website/static/src/css/snippets.sass +++ b/addons/website/static/src/css/snippets.sass @@ -64,9 +64,7 @@ overflow: hidden +user-select(none) cursor: move - pointer-events: none .oe_snippet_thumbnail - pointer-events: auto text-align: center height: 100% background: transparent @@ -150,22 +148,17 @@ .oe_overlay display: none + height: 0 position: absolute background: transparent //@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.02) ,rgba(255,255,255,.02) 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 75px)) +border-radius(3px) @include transition(opacity 100ms linear) +box-sizing(border-box) - pointer-events: none &.oe_active display: block - border-style: dashed - border-width: 1px - +box-shadow(0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset) - border-color: rgba(0, 0, 0, 0.5) .oe_handle display: block !important - pointer-events: auto position: absolute top: 50% left: 50% @@ -173,7 +166,15 @@ width: 16px height: 16px margin: -2px + > div + z-index: 1 + position: absolute + border-style: dashed + border-width: 1px + border-color: #666666 + +box-shadow(0px 0px 0px 1px rgba(255,255,255,0.5), 0px 0px 0px 1px rgba(255,255,255,0.5) inset) &.e:before, &.w:before, &.s:before, &.n:before, &.size .oe_handle_button + z-index: 2 position: relative top: 50% left: 50% @@ -196,72 +197,74 @@ color: #fff +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) &.e, &.w + top: 4px + height: 100% &:before content: "\f0d9-\f0da" line-height: 16px + > div + width: 0 + height: 100% + top: 2px + left: 8px &.e left: auto - top: 2px - height: 100% right: -6px cursor: w-resize &.w - top: 2px - height: 100% left: -6px cursor: e-resize &.s, &.n + left: 2px + width: 100% &:before - z-index: 0 content: "\f07d" text-align: center padding: 1px + > div + width: 100% + height: 0 + top: 7px + left: 1px &.s top: auto - left: 2px - width: 100% - bottom: -6px cursor: n-resize &.n - left: 2px - width: 100% - top: -6px cursor: s-resize + > div + top: 5px &.size + z-index: 3 top: auto - left: 2px - width: 100% + left: 50% bottom: -6px .oe_handle_button - z-index: 1 + z-index: 3 content: "Resize" width: 64px text-align: center margin-left: -32px margin-top: -10px cursor: row-resize + left: 0px + top: 9px &:hover background: rgba(30, 30, 30, .8) color: #fff +box-shadow(0 0 5px 3px rgba(255,255,255,.7)) - div - border-style: dashed - border-width: 0 0 1px 0 - border-color: rgba(0, 0, 0, 0.5) - position: relative - top: 8px .icon.btn display: inline-block .oe_overlay_options position: absolute - width: 100% + left: 50% !important text-align: center top: -11px z-index: 1002 + > .btn-group + left: -50% .btn, a - pointer-events: auto cursor: pointer .dropdown display: inline-block @@ -392,7 +395,6 @@ border: 2px solid rgb(151, 137, 255) box-shadow: 0px 3px 17px rgba(99, 53, 150, 0.59) & > * - pointer-events: none margin-top: 16px line-height: 1em zoom: 0.6 diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index f782f2ff83a..c7a3e1ecf9f 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -839,7 +839,6 @@ document.execCommand("enableInlineTableEditing", false, "false"); } catch (e) {} - // detect & setup any CKEDITOR widget within a newly dropped // snippet. There does not seem to be a simple way to do it for // HTML not inserted via ckeditor APIs: diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8fc642bf1c6..b20e0645253 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -190,13 +190,14 @@ var mt = parseInt($target.css("margin-top") || 0); var mb = parseInt($target.css("margin-bottom") || 0); $el.css({ - 'position': 'absolute', 'width': $target.outerWidth(), - 'height': $target.outerHeight() + mt + mb+1, - 'top': pos.top - mt, + 'top': pos.top - mt - 5, 'left': pos.left }); - $el.find(".oe_handle.size").css("bottom", (mb-7)+'px'); + $el.find(">.e,>.w").css({'height': $target.outerHeight() + mt + mb+1}); + $el.find(">.s").css({'top': $target.outerHeight() + mt + mb}); + $el.find(">.size").css({'top': $target.outerHeight() + mt}); + $el.find(">.s,>.n").css({'width': $target.outerWidth()-2}); }, show: function () { this.$el.removeClass("hidden"); @@ -213,8 +214,8 @@ bind_snippet_click_editor: function () { var self = this; - $(document).on('click', "#wrapwrap", function (event) { - var $target = $(event.srcElement); + $("#wrapwrap").on('click', function (event) { + var $target = $(event.srcElement || event.target); if (!$target.attr("data-snippet-id")) { $target = $target.parents("[data-snippet-id]:first"); } @@ -256,6 +257,9 @@ } if (this.$active_snipped_id) { this.snippet_blur(this.$active_snipped_id); + this.$active_snipped_id.data("overlay").remove(); + this.$active_snipped_id.removeData("overlay"); + this.$active_snipped_id.removeData("snippet-editor"); this.$active_snipped_id = false; } if ($snippet && $snippet.length) { @@ -571,6 +575,20 @@ var $target = $(this); if (!$target.data('overlay')) { var $zone = $(openerp.qweb.render('website.snippet_overlay')); + + // fix for pointer-events: none with ie9 + if (document.body && document.body.addEventListener) { + $zone.on("click mousedown mousedown", function passThrough(event) { + event.preventDefault(); + $target.each(function() { + // check if clicked point (taken from event) is inside element + event.srcElement = this; + $(this).trigger(event.type); + }); + return false; + }); + } + $zone.appendTo('#oe_manipulators'); $zone.data('target',$target); $target.data('overlay',$zone); @@ -783,6 +801,7 @@ this.parent = parent; this.$target = $(dom); this.$overlay = this.$target.data('overlay'); + this.$overlay.find('a[data-toggle="dropdown"]').dropdown(); this.snippet_id = this.$target.data("snippet-id"); this._readXMLData(); this.load_style_options(); diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml index d73e0c3934a..ae109139e3c 100644 --- a/addons/website/static/src/xml/website.snippets.xml +++ b/addons/website/static/src/xml/website.snippets.xml @@ -46,11 +46,11 @@
-
-
-
-
Resize
-
+
+
+
+
Resize
+
Margin resize
From 01a898ab8ba9fce5bcf9613327227d38366c7417 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 27 Feb 2014 11:09:17 +0100 Subject: [PATCH 016/129] [FIX] website snippet: pointer-ie bzr revid: chm@openerp.com-20140227100917-wq54xrkgii7uhacl --- addons/website/static/src/js/website.snippets.editor.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index b20e0645253..a01d7605851 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -257,8 +257,11 @@ } if (this.$active_snipped_id) { this.snippet_blur(this.$active_snipped_id); - this.$active_snipped_id.data("overlay").remove(); - this.$active_snipped_id.removeData("overlay"); + var $overlay = this.$active_snipped_id.data("overlay"); + if ($overlay) { + $overlay.remove(); + this.$active_snipped_id.removeData("overlay"); + } this.$active_snipped_id.removeData("snippet-editor"); this.$active_snipped_id = false; } From 42e2676f4b44a0403fd5f661d9502a9b83c0e33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 27 Feb 2014 13:02:54 +0100 Subject: [PATCH 017/129] [IMP] fields: html: now supports a sanitize argument, telling whether the content of the html field should be sanitized before storage. This parameter is true by default. Example of use: html of email template is considered as html content, but contains mako strings that makes this content not valid html. Sanitizing the body content can cause issues to the template; therefore storing it as html but not sanitized allows to keep its content safe. Added a test case to ensure this behavior. bzr revid: tde@openerp.com-20140227120254-6m8gvkt4hf28nl0e --- openerp/osv/fields.py | 19 ++++++++++---- openerp/tests/test_fields.py | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 478f982cd69..d5d0ee0fa30 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -236,15 +236,24 @@ class char(_column): class text(_column): _type = 'text' + class html(text): _type = 'html' _symbol_c = '%s' - def _symbol_f(x): - if x is None or x == False: + + def _symbol_set_html(self, value): + if value is None or value is False: return None - return html_sanitize(x) - - _symbol_set = (_symbol_c, _symbol_f) + if not self._sanitize: + return value + return html_sanitize(value) + + def __init__(self, string='unknown', sanitize=True, **args): + super(html, self).__init__(string=string, **args) + self._sanitize = sanitize + # symbol_set redefinition because of sanitize specific behavior + self._symbol_f = self._symbol_set_html + self._symbol_set = (self._symbol_c, self._symbol_f) import __builtin__ diff --git a/openerp/tests/test_fields.py b/openerp/tests/test_fields.py index cb05861d301..c767c9273ea 100644 --- a/openerp/tests/test_fields.py +++ b/openerp/tests/test_fields.py @@ -180,3 +180,51 @@ class TestPropertyField(common.TransactionCase): self.partner.write(cr, alice, [partner_id], {'property_country': country_be}) self.assertEqual(self.partner.browse(cr, alice, partner_id).property_country.id, country_be, "Alice does not see the value he has set on the property field") self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Changes made by Alice have overwritten Bob's value") + + +class TestHtmlField(common.TransactionCase): + + def setUp(self): + super(TestHtmlField, self).setUp() + self.partner = self.registry('res.partner') + + def test_00_sanitize(self): + cr, uid, context = self.cr, self.uid, {} + old_columns = self.partner._columns + self.partner._columns = dict(old_columns) + self.partner._columns.update({ + 'comment': fields.html('Secure Html', sanitize=False), + }) + some_ugly_html = """

Oops this should maybe be sanitized +% if object.some_field and not object.oriented: + + % if object.other_field: + + ${object.mako_thing} + + % endif + +%if object.dummy_field: +

Youpie

+%endif""" + + pid = self.partner.create(cr, uid, { + 'name': 'Raoul Poilvache', + 'comment': some_ugly_html, + }, context=context) + partner = self.partner.browse(cr, uid, pid, context=context) + self.assertEqual(partner.comment, some_ugly_html, 'Error in HTML field: content was sanitized but field has sanitize=False') + + self.partner._columns.update({ + 'comment': fields.html('Unsecure Html', sanitize=True), + }) + self.partner.write(cr, uid, [pid], { + 'comment': some_ugly_html, + }, context=context) + partner = self.partner.browse(cr, uid, pid, context=context) + # sanitize should have closed tags left open in the original html + self.assertIn('
+
', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True') + self.assertIn('', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True') + + self.partner._columns = old_columns From e28677f727b0218f037417d912f42a143c5bc52c Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 27 Feb 2014 13:29:38 +0100 Subject: [PATCH 018/129] [FIX]project: chosend beginning stage for new project_issue sequence <= 1 (instead of =, same behavior than in tasks) bzr revid: dle@openerp.com-20140227122938-i0s6dfxd42pl6ov3 --- addons/project_issue/project_issue.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index f787ed1544e..6f072f5abd1 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -48,8 +48,9 @@ class project_issue(osv.Model): _mail_post_access = 'read' _track = { 'stage_id': { - 'project_issue.mt_issue_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence == 1, - 'project_issue.mt_issue_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence != 1, + # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages + 'project_issue.mt_issue_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, + 'project_issue.mt_issue_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence > 1, }, 'user_id': { 'project_issue.mt_issue_assigned': lambda self, cr, uid, obj, ctx=None: obj.user_id and obj.user_id.id, @@ -74,7 +75,7 @@ class project_issue(osv.Model): def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ project_id = self._get_default_project_id(cr, uid, context=context) - return self.stage_find(cr, uid, [], project_id, [('sequence', '=', 1)], context=context) + return self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context) def _resolve_project_id_from_context(self, cr, uid, context=None): """ Returns ID of project based on the value of 'default_project_id' From 6b857b6eeb59137a71385f98c82c440ac82cd45d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Feb 2014 13:59:34 +0100 Subject: [PATCH 019/129] [FIX] whitespace/indent lost by RTE Didn't manage to find RTE settings to avoid losing leading whitespace of lines, so reindeint arch after doing all integration, right before saving back to view's field. * html.fromstring(parser=HTMLParser(remove_blank_text=True) does not seem to work, so serialize to XML, and parse back with remove_blank_text. remove_blank_text necessary for lxml's pretty_print to work correctly. * pretty_print only & always uses 2 spaces/indent level. Our files (and the HTML editor's Format button) uses 4 spaces -> need a second pass to double indents. bzr revid: xmo@openerp.com-20140227125934-q8j3z440px2ic6kx --- addons/website/models/ir_ui_view.py | 26 +++++++++++++++++++++++++- addons/website/tests/test_views.py | 4 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index 27af03310a9..857b99d40d2 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import copy +import re import simplejson import werkzeug @@ -158,6 +159,29 @@ class view(osv.osv): return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context) + def _pretty_arch(self, arch): + # remove_blank_string does not seem to work on HTMLParser, and + # pretty-printing with lxml more or less requires stripping + # whitespace: http://lxml.de/FAQ.html#why-doesn-t-the-pretty-print-option-reformat-my-xml-output + # so serialize to XML, parse as XML (remove whitespace) then serialize + # as XML (pretty print) + arch_no_whitespace = etree.fromstring( + etree.tostring(arch, encoding='utf-8'), + parser=etree.XMLParser(encoding='utf-8', remove_blank_text=True)) + arch_pretty_indent_2 = etree.tostring( + arch_no_whitespace, encoding='unicode', pretty_print=True) + + # pretty_print uses a fixed indent level of 2, we want an indent of 4, + # double up leading spaces. + def repl(m): + indent = len(m.group(0)) / 2 + return u' ' * 4 * indent + # FIXME: If py2.7 only, can use re.M in sub and don't have to do replacement line by line + return u'\n'.join( + re.sub(ur'^((?: )+)', repl, line) + for line in arch_pretty_indent_2.split(u'\n') + ) + def save(self, cr, uid, res_id, value, xpath=None, context=None): """ Update a view section. The view section may embed fields to write @@ -183,5 +207,5 @@ class view(osv.osv): arch = self.replace_arch_section(cr, uid, res_id, xpath, arch_section, context=context) self.write(cr, uid, res_id, { - 'arch': etree.tostring(arch, encoding='utf-8').decode('utf-8') + 'arch': self._pretty_arch(arch) }, context=context) diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index 8041a7e5f1c..1adb11712ec 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -13,8 +13,8 @@ class TestViewSaving(common.TransactionCase): def eq(self, a, b): self.assertEqual(a.tag, b.tag) self.assertEqual(a.attrib, b.attrib) - self.assertEqual(a.text, b.text) - self.assertEqual(a.tail, b.tail) + self.assertEqual((a.text or '').strip(), (b.text or '').strip()) + self.assertEqual((a.tail or '').strip(), (b.tail or '').strip()) for ca, cb in itertools.izip_longest(a, b): self.eq(ca, cb) From 49a92c376aa3405cf0c836fdaab07ec69d73b647 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Thu, 27 Feb 2014 14:07:05 +0100 Subject: [PATCH 020/129] [FIX] Fix loop when parse hash into get param with no hash bzr revid: jke@openerp.com-20140227130705-jlvl157aio3o4df3 --- addons/auth_oauth/controllers/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index 53a6ffd5462..4d96f083cdc 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -15,6 +15,7 @@ from openerp.tools.translate import _ _logger = logging.getLogger(__name__) + #---------------------------------------------------------- # helpers #---------------------------------------------------------- @@ -30,11 +31,15 @@ def fragment_to_query_string(func): var s = l.search ? (l.search === '?' ? '' : '&') : '?'; r = l.pathname + l.search + s + q; } + if (r == l) { + r = '/'; + } window.location = r; """ return func(self, *a, **kw) return wrapper + #---------------------------------------------------------- # Controller #---------------------------------------------------------- @@ -88,6 +93,7 @@ class OAuthLogin(openerp.addons.web.controllers.main.Home): return response + class OAuthController(http.Controller): @http.route('/auth_oauth/signin', type='http', auth='none') From b1f88d634d603b74a62d051032e4003fc6f7a3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 27 Feb 2014 14:48:29 +0100 Subject: [PATCH 021/129] [IMP] [FIX] email_template: do not sanitize the template content. Indeed its content may contain invalid html that could be stripped by the sanitizer. The content generated based on the template will be sanitized when stored in the mail_mail or mail_message body field, thus after rendering. The template therefore holds html, but that is not sanitized. But that's still html, therefore using an html field. bzr revid: tde@openerp.com-20140227134829-te8mxeakc3s96fun --- addons/email_template/email_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index fcfe869da90..0b5945c1f0b 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -183,7 +183,7 @@ class email_template(osv.osv): 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, help="Optional preferred server for outgoing mails. If not set, the highest " "priority one will be used."), - 'body_html': fields.html('Body', translate=True, help="Rich-text/HTML version of the message (placeholders may be used here)"), + 'body_html': fields.html('Body', translate=True, sanitize=False, help="Rich-text/HTML version of the message (placeholders may be used here)"), 'report_name': fields.char('Report Filename', translate=True, help="Name to use for the generated report file (may contain placeholders)\n" "The extension can be omitted and will then come from the report type."), From 6a9d3326dbef5e34c3dd0896a20bc41ba2fa17d4 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 27 Feb 2014 14:51:20 +0100 Subject: [PATCH 022/129] [FIX] website_sale: 'Ship to a different address' not translatable bzr revid: chm@openerp.com-20140227135120-drw0jha7gzqptdog --- 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 64f5d5a9b59..803dc695ff0 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -784,7 +784,7 @@

From d96cf8c54ca2e81c9401e4e3658bdd26313a25a3 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 27 Feb 2014 15:00:43 +0100 Subject: [PATCH 023/129] [FIX] website: editor: can not edit a label in edit mode, must prevent the default click bzr revid: chm@openerp.com-20140227140043-8iko13704pci9823 --- addons/website/static/src/js/website.editor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 0ec881165b4..303a7ee79d4 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -20,6 +20,10 @@ website.form(this.pathname, 'POST'); }); + $(document).on('click', '.cke_editable label', function (ev) { + ev.preventDefault(); + }); + $(document).on('submit', '.cke_editable form', function (ev) { // Disable form submition in editable mode ev.preventDefault(); From 2b7de6164f3cd1dafc8d204190862f90a2a1756c Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Thu, 27 Feb 2014 15:29:33 +0100 Subject: [PATCH 024/129] [FIX] Fix notification in calendar. All peoples had all notifs :( bzr revid: jke@openerp.com-20140227142933-33bh05ozdb7uwjy5 --- addons/calendar/calendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 635e72e7b85..e25f75d803a 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -363,7 +363,7 @@ class calendar_alarm_manager(osv.AbstractModel): """ filter_user = """ - LEFT JOIN calendar_event_res_partner_rel AS part_rel ON part_rel.calendar_event_id = cal.id + RIGHT JOIN calendar_event_res_partner_rel AS part_rel ON part_rel.calendar_event_id = cal.id AND part_rel.res_partner_id = %s """ From a17c09afff29ef445472984e72126a73e7cf36ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 27 Feb 2014 15:42:28 +0100 Subject: [PATCH 025/129] [IMP] [FIX] email_template: do not sanitize the template content. Indeed its content may contain invalid html that could be stripped by the sanitizer. The content generated based on the template will be sanitized when stored in the mail_mail or mail_message body field, thus after rendering. bzr revid: tde@openerp.com-20140227144228-d275lxz6ryarkg4t --- addons/email_template/email_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index fcfe869da90..0b5945c1f0b 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -183,7 +183,7 @@ class email_template(osv.osv): 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, help="Optional preferred server for outgoing mails. If not set, the highest " "priority one will be used."), - 'body_html': fields.html('Body', translate=True, help="Rich-text/HTML version of the message (placeholders may be used here)"), + 'body_html': fields.html('Body', translate=True, sanitize=False, help="Rich-text/HTML version of the message (placeholders may be used here)"), 'report_name': fields.char('Report Filename', translate=True, help="Name to use for the generated report file (may contain placeholders)\n" "The extension can be omitted and will then come from the report type."), From 8b81179d45fd433992f3e2acec6e5f85faf96bdc Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 27 Feb 2014 16:19:59 +0100 Subject: [PATCH 026/129] [FIX] payment_paypal: create account with at least required fields bzr revid: chs@openerp.com-20140227151959-mpk2bjk9k8t0chqo --- addons/payment_paypal/models/paypal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/payment_paypal/models/paypal.py b/addons/payment_paypal/models/paypal.py index 13945bab6e8..a5e31e9571c 100644 --- a/addons/payment_paypal/models/paypal.py +++ b/addons/payment_paypal/models/paypal.py @@ -70,6 +70,7 @@ class AcquirerPaypal(osv.Model): else: paypal_view = self.pool['ir.model.data'].get_object(cr, uid, 'payment_paypal', 'paypal_acquirer_button') self.create(cr, uid, { + 'name': 'paypal', 'paypal_email_account': company_paypal_account, 'view_template_id': paypal_view.id, }, context=context) From a9a4767d17650320cba710ce99b6c737419df7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 27 Feb 2014 16:38:35 +0100 Subject: [PATCH 027/129] [FIX] email_template : - fixed composer using template that were rendering the body twice, once form the template and once from the composer body. Only the latter one is used, so avoid generating the template body that is not necessary - fixed email_template generating values for a set of given fields, ignoring the field list given into parameter - fixed post processing of templates to transform local urls into absolute urls; now urls are transformed after body generation, when sending email based on templates , or when generating the content when using the composer. bzr revid: tde@openerp.com-20140227153835-gmqnxrzed9fnbxhm --- addons/email_template/email_template.py | 6 ++-- .../wizard/mail_compose_message.py | 15 +++++--- addons/website_mail/models/__init__.py | 1 + addons/website_mail/models/email_template.py | 16 ++++----- .../models/mail_compose_message.py | 34 +++++++++++++++++++ 5 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 addons/website_mail/models/mail_compose_message.py diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 0b5945c1f0b..d9b65849fae 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -356,17 +356,17 @@ class email_template(osv.osv): results = dict() for template, template_res_ids in templates_to_res_ids.iteritems(): # generate fields value for all res_ids linked to the current template - for field in ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']: + for field in fields: generated_field_values = self.render_template_batch(cr, uid, getattr(template, field), template.model, template_res_ids, context=context) for res_id, field_value in generated_field_values.iteritems(): results.setdefault(res_id, dict())[field] = field_value # update values for all res_ids for res_id in template_res_ids: values = results[res_id] - if template.user_signature: + if 'body_html' in fields and template.user_signature: signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature values['body_html'] = tools.append_content_to_html(values['body_html'], signature) - if values['body_html']: + if values.get('body_html'): values['body'] = tools.html_sanitize(values['body_html']) values.update( mail_server_id=template.mail_server_id.id or False, diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index beede1122dc..3f926439d08 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -162,16 +162,18 @@ class mail_compose_message(osv.TransientModel): partner_ids += self.pool['res.partner'].exists(cr, SUPERUSER_ID, tpl_partner_ids, context=context) return partner_ids - def generate_email_for_composer_batch(self, cr, uid, template_id, res_ids, context=None): + def generate_email_for_composer_batch(self, cr, uid, template_id, res_ids, context=None, fields=None): """ Call email_template.generate_email(), get fields relevant for mail.compose.message, transform email_cc and email_to into partner_ids """ # filter template values - fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'attachments', 'mail_server_id'] + if fields is None: + fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'mail_server_id'] + returned_fields = fields + ['attachments'] values = dict.fromkeys(res_ids, False) - template_values = self.pool.get('email.template').generate_email_batch(cr, uid, template_id, res_ids, context=context) + template_values = self.pool.get('email.template').generate_email_batch(cr, uid, template_id, res_ids, fields=fields, context=context) for res_id in res_ids: - res_id_values = dict((field, template_values[res_id][field]) for field in fields if template_values[res_id].get(field)) + res_id_values = dict((field, template_values[res_id][field]) for field in returned_fields if template_values[res_id].get(field)) res_id_values['body'] = res_id_values.pop('body_html', '') # transform email_to, email_cc into partner_ids @@ -189,7 +191,10 @@ class mail_compose_message(osv.TransientModel): """ Override to handle templates. """ # generate template-based values if wizard.template_id: - template_values = self.generate_email_for_composer_batch(cr, uid, wizard.template_id.id, res_ids, context=context) + template_values = self.generate_email_for_composer_batch( + cr, uid, wizard.template_id.id, res_ids, + fields=['email_to', 'partner_to', 'email_cc', 'attachment_ids', 'mail_server_id'], + context=context) else: template_values = dict.fromkeys(res_ids, dict()) # generate composer values diff --git a/addons/website_mail/models/__init__.py b/addons/website_mail/models/__init__.py index e60de014149..3e374ccddd2 100644 --- a/addons/website_mail/models/__init__.py +++ b/addons/website_mail/models/__init__.py @@ -1,3 +1,4 @@ import mail_message import mail_thread import email_template +import mail_compose_message \ No newline at end of file diff --git a/addons/website_mail/models/email_template.py b/addons/website_mail/models/email_template.py index aed01c179f7..105fcff4428 100644 --- a/addons/website_mail/models/email_template.py +++ b/addons/website_mail/models/email_template.py @@ -79,12 +79,10 @@ class EmailTemplate(osv.Model): html = html[5:-6] return html - def create(self, cr, uid, values, context=None): - if 'body_html' in values: - values['body_html'] = self._postprocess_html_replace_links(cr, uid, values['body_html'], context=context) - return super(EmailTemplate, self).create(cr, uid, values, context=context) - - def write(self, cr, uid, ids, values, context=None): - if 'body_html' in values: - values['body_html'] = self._postprocess_html_replace_links(cr, uid, values['body_html'], context=context) - return super(EmailTemplate, self).write(cr, uid, ids, values, context=context) + def generate_email_batch(self, cr, uid, template_id, res_ids, context=None, fields=None): + """ Add a post processing after rendering, aka replace local URLs to absolute URLs. """ + results = super(EmailTemplate, self).generate_email_batch(cr, uid, template_id, res_ids, context=context, fields=fields) + for res_id, value in results.iteritems(): + if 'body_html' in value: + results[res_id]['body_html'] = self._postprocess_html_replace_links(cr, uid, value['body_html'], context=context) + return results diff --git a/addons/website_mail/models/mail_compose_message.py b/addons/website_mail/models/mail_compose_message.py new file mode 100644 index 00000000000..bc8258bbb6f --- /dev/null +++ b/addons/website_mail/models/mail_compose_message.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import osv + + +class MailComposeMessage(osv.Model): + _inherit = 'mail.compose.message' + + def generate_email_for_composer_batch(self, cr, uid, template_id, res_ids, context=None, fields=None): + """ Add a post processing after rendering, aka replace local URLs to absolute URLs. """ + results = super(MailComposeMessage, self).generate_email_for_composer_batch(cr, uid, template_id, res_ids, context=context, fields=fields) + for res_id, value in results.iteritems(): + if 'body' in value: + results[res_id]['body'] = self.pool['email.template']._postprocess_html_replace_links(cr, uid, value['body'], context=context) + return results From b994ae4938bd29a55ae4328d254b2773bccaed0e Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 27 Feb 2014 17:05:19 +0100 Subject: [PATCH 028/129] [FIX] website_sale: search filter with multi values for one attribute does not work. (and fix slider) bzr revid: chm@openerp.com-20140227160519-eakqgm2zwrxseya7 --- addons/website_sale/controllers/main.py | 32 ++++++++++++------- .../static/src/js/website_sale.js | 4 +++ addons/website_sale/views/website_sale.xml | 4 +-- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 3531853227c..f16ef43e179 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -181,19 +181,29 @@ class Ecommerce(http.Controller): return request.redirect(url) - def attributes_to_ids(self, attributes): - obj = request.registry.get('product.attribute.line') - domain = [] + def attributes_to_ids(self, cr, uid, attributes): + req = """ + SELECT product_tmpl_id as id, count(*) as nb_match + FROM product_attribute_line + WHERE 1!=1 + """ + nb = 0 for key_val in attributes: - domain.append(("attribute_id", "=", key_val[0])) + attribute_id = key_val[0] if isinstance(key_val[1], list): - domain.append(("value", ">=", key_val[1][0])) - domain.append(("value", "<=", key_val[1][1])) + req += " OR ( attribute_id = %s AND value >= %s AND value <= %s)" % \ + (attribute_id, key_val[1][0], key_val[1][1]) + nb += 1 else: - domain.append(("value_id", "in", key_val[1:])) - att_ids = obj.search(request.cr, request.uid, domain, context=request.context) - att = obj.read(request.cr, request.uid, att_ids, ["product_tmpl_id"], context=request.context) - return [r["product_tmpl_id"][0] for r in att] + for value_id in key_val[1:]: + req += " OR ( attribute_id = %s AND value_id = %s)" % \ + (attribute_id, value_id) + nb += 1 + + req += " GROUP BY product_tmpl_id" + cr.execute(req) + result = cr.fetchall() + return [id for id, nb_match in result if nb_match >= nb] @http.route(['/shop/pricelist'], type='http', auth="public", website=True, multilang=True) def shop_promo(self, promo=None, **post): @@ -221,7 +231,7 @@ class Ecommerce(http.Controller): if filters: filters = simplejson.loads(filters) if filters: - ids = self.attributes_to_ids(filters) + ids = self.attributes_to_ids(cr, uid, filters) domain.append(('id', 'in', ids or [0])) url = "/shop/" diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 3c9bb6ea05c..2dc908ccd13 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -107,6 +107,10 @@ $(document).ready(function () { $min.val( ui.values[ 0 ] ); $max.val( ui.values[ 1 ] ); $form.submit(); + }, + slide: function( event, ui ) { + $min.val( ui.values[ 0 ] ); + $max.val( ui.values[ 1 ] ); } }); $min.val( $slider.slider( "values", 0 ) ); diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 803dc695ff0..4f8a5404d74 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -608,9 +608,9 @@
From 82fceb46206f23d4c1a8249cfdb0094377b695d6 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 27 Feb 2014 17:14:54 +0100 Subject: [PATCH 029/129] [FIX] web: login, autofocus if login is filled bzr revid: dle@openerp.com-20140227161454-sd6qxmpgv2bvslf7 --- addons/web/views/webclient_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 0e08f743f6c..a9305015a0b 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -105,7 +105,7 @@
- +

From ee67a1ae391d65d63d603de0a93ee98c17fcd1ad Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 27 Feb 2014 17:16:38 +0100 Subject: [PATCH 030/129] [IMP] auth_signup: email first in the signup form, as is regarded as the most important variable bzr revid: dle@openerp.com-20140227161638-5rp3bunjx77fbavu --- addons/auth_signup/views/auth_signup_login.xml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/addons/auth_signup/views/auth_signup_login.xml b/addons/auth_signup/views/auth_signup_login.xml index a9e43c86f4f..e979ccb1092 100644 --- a/addons/auth_signup/views/auth_signup_login.xml +++ b/addons/auth_signup/views/auth_signup_login.xml @@ -32,18 +32,17 @@

- from
- to + From
+ To
diff --git a/addons/website_event_track/views/website_event.xml b/addons/website_event_track/views/website_event.xml index 7232160b759..65aa7610ec3 100644 --- a/addons/website_event_track/views/website_event.xml +++ b/addons/website_event_track/views/website_event.xml @@ -152,7 +152,7 @@ , -
  • +
  • @@ -230,7 +230,7 @@
    Date
    -
    +
    Duration
    minutes
    Location
    From 783a8e22a9732f546402c83ad6b76fe8a0adee37 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 28 Feb 2014 10:25:35 +0100 Subject: [PATCH 035/129] =?UTF-8?q?[FIX]=20payment=5Fogone:=20ogone=20is?= =?UTF-8?q?=20so=20bad=20with=20encoding=20that=20they=20need=20to=20dupli?= =?UTF-8?q?cate=20their=20code=20to=20handle=20utf8=20correctly=20?= =?UTF-8?q?=E0=B2=A0=5F=E0=B2=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bzr revid: chs@openerp.com-20140228092535-1tfy5832rgq79ob9 --- addons/payment_ogone/models/ogone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/payment_ogone/models/ogone.py b/addons/payment_ogone/models/ogone.py index a9a4f633887..2ae9722b655 100644 --- a/addons/payment_ogone/models/ogone.py +++ b/addons/payment_ogone/models/ogone.py @@ -30,10 +30,10 @@ class PaymentAcquirerOgone(osv.Model): @TDETODO: complete me """ return { - 'ogone_standard_order_url': 'https://secure.ogone.com/ncol/%s/orderstandard_utf8.asp' % env, - 'ogone_direct_order_url': 'https://secure.ogone.com/ncol/%s/orderdirect.asp' % env, - 'ogone_direct_query_url': 'https://secure.ogone.com/ncol/%s/querydirect.asp' % env, - 'ogone_afu_agree_url': 'https://secure.ogone.com/ncol/%s/AFU_agree.asp' % env, + 'ogone_standard_order_url': 'https://secure.ogone.com/ncol/%s/orderstandard_utf8.asp' % (env,), + 'ogone_direct_order_url': 'https://secure.ogone.com/ncol/%s/orderdirect_utf8.asp' % (env,), + 'ogone_direct_query_url': 'https://secure.ogone.com/ncol/%s/querydirect_utf8.asp' % (env,), + 'ogone_afu_agree_url': 'https://secure.ogone.com/ncol/%s/AFU_agree.asp' % (env,), } _columns = { From c11f4f9352aea4ed9febae82fab8f2e91ad9f8cd Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 28 Feb 2014 10:55:31 +0100 Subject: [PATCH 036/129] [FIX] website: snippet: continue slide cycling when the user edit the content bzr revid: chm@openerp.com-20140228095531-8c735qyacmffryno --- .../static/src/js/website.snippets.animation.js | 4 ++++ .../website/static/src/js/website.snippets.editor.js | 12 +++++++++--- addons/website/views/website_templates.xml | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index bc2ea064731..f1d19125a0c 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -64,6 +64,10 @@ start: function () { this.$target.carousel({interval: 10000}); }, + stop: function () { + this.$target.carousel('pause'); + this.$target.removeData("bs.carousel"); + }, }); website.snippet.animationRegistry.parallax = website.snippet.Animation.extend({ diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index a01d7605851..8414196b696 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -19,12 +19,11 @@ edit: function () { var self = this; $("body").off('click'); + website.snippet.stop_animation(); window.snippets = this.snippets = new website.snippet.BuildingBlock(this); this.snippets.appendTo(this.$el); - this.on('rte:ready', this, function () { self.snippets.$button.removeClass("hidden"); - website.snippet.stop_animation(); website.snippet.start_animation(); }); @@ -1305,6 +1304,14 @@ this.$target.find(".item:first").addClass("active"); } }, + onFocus : function () { + this._super(); + this.$target.carousel('pause'); + }, + onBlur : function () { + this._super(); + this.$target.carousel('cycle'); + }, start : function () { var self = this; this._super(); @@ -1315,7 +1322,6 @@ this.$editor.find(".js_add").on('click', function () {self.on_add_slide(); return false;}); this.$editor.find(".js_remove").on('click', function () {self.on_remove_slide(); return false;}); - this.$target.carousel('pause'); this.rebind_event(); }, on_add_slide: function () { diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 96e184a32a1..a8ed626866d 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -80,8 +80,8 @@ - + From 24f9dc348a69b2f504010a5f4771dea9aa27c83a Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 28 Feb 2014 11:22:57 +0100 Subject: [PATCH 037/129] [FIX] website_sale: long product title is over the price in grid view. bzr revid: chm@openerp.com-20140228102257-000435ykuv7000ub --- addons/website_sale/static/src/css/website_sale.css | 3 ++- addons/website_sale/static/src/css/website_sale.sass | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/website_sale/static/src/css/website_sale.css b/addons/website_sale/static/src/css/website_sale.css index d115ca37b90..2b23a4b4700 100644 --- a/addons/website_sale/static/src/css/website_sale.css +++ b/addons/website_sale/static/src/css/website_sale.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* ---- Default Styles ---- */ .oe_product { border: 1px solid rgba(100, 100, 100, 0.2); @@ -52,7 +53,7 @@ right: 0; bottom: 0; overflow: hidden; - padding: 0 15px; + padding: 0 15px 24px 0; max-height: 110px; min-height: 56px; border-top: 1px solid rgba(255, 255, 255, 0.2); diff --git a/addons/website_sale/static/src/css/website_sale.sass b/addons/website_sale/static/src/css/website_sale.sass index 9cf1f5dd012..55acac22ec1 100644 --- a/addons/website_sale/static/src/css/website_sale.sass +++ b/addons/website_sale/static/src/css/website_sale.sass @@ -47,7 +47,7 @@ right: 0 bottom: 0 overflow: hidden - padding: 0 15px + padding: 0 15px 24px 0 max-height: 110px min-height: 56px border-top: 1px solid rgba(255,255,255,0.2) From c8971214f905918bd5f9903fbc4bcfb4d1809b00 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 28 Feb 2014 13:34:14 +0100 Subject: [PATCH 038/129] [FIX] website_sale: allow validation of free orders (amount = 0) without payment transaction bzr revid: chs@openerp.com-20140228123414-4chmuemyi72rpwgf --- addons/website_sale/controllers/main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 8f669c1d189..8dacda09904 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -713,7 +713,6 @@ class Ecommerce(http.Controller): - UDPATE ME """ cr, uid, context = request.cr, request.uid, request.context - email_act = None sale_order_obj = request.registry['sale.order'] if transaction_id is None: @@ -727,17 +726,19 @@ class Ecommerce(http.Controller): order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) assert order.website_session_id == request.httprequest.session['website_session_id'] - if not tx or not order: + if not order: return request.redirect('/shop/') + elif order.amount_total and not tx: + return request.redirect('/shop/mycart') if not order.amount_total or tx.state == 'done': # confirm the quotation sale_order_obj.action_button_confirm(cr, SUPERUSER_ID, [order.id], context=request.context) # send by email - email_act = sale_order_obj.action_quotation_send(cr, SUPERUSER_ID, [order.id], context=request.context) + sale_order_obj.action_quotation_send(cr, SUPERUSER_ID, [order.id], context=request.context) elif tx.state == 'pending': # send by email - email_act = sale_order_obj.action_quotation_send(cr, SUPERUSER_ID, [order.id], context=request.context) + sale_order_obj.action_quotation_send(cr, SUPERUSER_ID, [order.id], context=request.context) elif tx.state == 'cancel': # cancel the quotation sale_order_obj.action_cancel(cr, SUPERUSER_ID, [order.id], context=request.context) From ce1ff0a85487bca3ef072abe6ba0c777335d3855 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 28 Feb 2014 14:17:38 +0100 Subject: [PATCH 039/129] [FIX] website snippet: Fix parallax with retro-compatibility; Crappy fix for carousel because bootstrap active cycling when user click on a bullet and don't stop when user edit a snippet inside a slider. bzr revid: chm@openerp.com-20140228131738-il41640r8x9zesll --- addons/website/static/src/css/website.css | 19 ++++++----------- addons/website/static/src/css/website.sass | 21 +++++++------------ .../static/src/js/website.snippets.editor.js | 15 ++++++------- addons/website/views/snippets.xml | 10 ++++----- 4 files changed, 24 insertions(+), 41 deletions(-) diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index db0aff3b01a..81b40ce7aca 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -394,22 +394,15 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .parallax { - position: relative; background-size: cover; - display: table; - width: 100%; - min-height: 100px; -} -.parallax.oe_small { - min-height: 200px; -} -.parallax.oe_medium { - min-height: 300px; -} -.parallax.oe_big { - min-height: 450px; } .parallax > div { + position: relative; + display: table; + width: 100%; + min-height: 200px; +} +.parallax > div > div { display: table-cell; vertical-align: middle; padding: 32px 0; diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 907b0615c0e..a9ae82cee3a 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -324,21 +324,16 @@ div.carousel[data-snippet-id="slider"] background-color: grey .parallax - position: relative background-size: cover - display: table - width: 100% - min-height: 100px - &.oe_small - min-height: 200px - &.oe_medium - min-height: 300px - &.oe_big - min-height: 450px > div - display: table-cell - vertical-align: middle - padding: 32px 0 + position: relative + display: table + width: 100% + min-height: 200px + > div + display: table-cell + vertical-align: middle + padding: 32px 0 /* Background */ diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8414196b696..41b198fddec 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -25,6 +25,8 @@ this.on('rte:ready', this, function () { self.snippets.$button.removeClass("hidden"); website.snippet.start_animation(); + // force to unactive slider cycling + setInterval(function () {$(".carousel").carousel("pause");},3000); }); return this._super.apply(this, arguments); @@ -1304,14 +1306,6 @@ this.$target.find(".item:first").addClass("active"); } }, - onFocus : function () { - this._super(); - this.$target.carousel('pause'); - }, - onBlur : function () { - this._super(); - this.$target.carousel('cycle'); - }, start : function () { var self = this; this._super(); @@ -1464,7 +1458,10 @@ self.$target.data("snippet-view").set_values(); }); this.$target.attr('contentEditable', 'false'); - this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); + + this.$target.find('> div > .oe_structure').attr('contentEditable', 'true'); // saas-3 retro-compatibility + + this.$target.find('> div > div:not(.oe_structure) > .oe_structure').attr('contentEditable', 'true'); }, scroll: function () { var self = this; diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index 11d01176da0..ff3d65dc459 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -792,7 +792,7 @@
    @@ -804,10 +804,9 @@ Parallax Slider
    -
    -
    +
    -
    -
    +
    From abcd7e4926cb5938555858a0cbf7b0c503a17947 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 28 Feb 2014 14:25:07 +0100 Subject: [PATCH 040/129] [FIX] website snippet: When duplicating a column, the column becomes a col-md-1 instead of keeping existing width bzr revid: chm@openerp.com-20140228132507-0s6y5k3vkbmusvf1 --- addons/website/static/src/js/website.snippets.editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 41b198fddec..491c0a2aba6 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -1233,7 +1233,6 @@ on_clone: function () { var $clone = this.$target.clone(false); var _class = $clone.attr("class").replace(/\s*(col-lg-offset-|col-md-offset-)([0-9-]+)/g, ''); - _class += ' col-md-1'; $clone.attr("class", _class); this.$target.after($clone); this.hide_remove_button(); From b2045b91acc8e85bb053ca324099f210c79944f9 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Fri, 28 Feb 2014 14:52:24 +0100 Subject: [PATCH 041/129] [IMP] Improve dipslay of form_embedded_html for mail Template. Removed the big margin, and add scroll on overflow bzr revid: jke@openerp.com-20140228135224-my13sabs7f7h3c60 --- addons/web/static/src/css/base.css | 9 ++++----- addons/web/static/src/css/base.sass | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index e43a2fb654e..16723695743 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,4 +1,4 @@ -@charset "UTF-8"; +@charset "utf-8"; @font-face { font-family: "mnmliconsRegular"; src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); @@ -2339,10 +2339,9 @@ } .openerp .oe_form .oe_form_embedded_html { position: relative; - width: 600px; - margin-left: 130px; - margin-top: 32px; - margin-bottom: 32px; + width: 100%; + margin: auto; + overflow: scroll; text-align: justify; } .openerp .oe_form .oe_form_field_html .oe_input_icon { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index b19db9e4ebd..50ceffa3a05 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1893,10 +1893,9 @@ $sheet-padding: 16px overflow: hidden .oe_form_embedded_html position: relative - width: 600px - margin-left: 130px - margin-top: 32px - margin-bottom: 32px + width: 100% + margin: auto + overflow: scroll text-align: justify .oe_form_field_html .oe_input_icon float: right From 015083df092fde1fa55a0efe0631da907f288471 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Fri, 28 Feb 2014 15:00:53 +0100 Subject: [PATCH 042/129] [FIX] Calendar - Tempalte Mail - Move the if condition who vas between table and tr into td, else wysiwig and some other sanitizers will move the if condition outside the table bzr revid: jke@openerp.com-20140228140053-02952ews6aadfcjf --- addons/calendar/calendar_data.xml | 238 ++++++++++++++++-------------- 1 file changed, 131 insertions(+), 107 deletions(-) diff --git a/addons/calendar/calendar_data.xml b/addons/calendar/calendar_data.xml index 4426014e509..9171bc435e0 100644 --- a/addons/calendar/calendar_data.xml +++ b/addons/calendar/calendar_data.xml @@ -151,50 +151,58 @@ - % if object.event_id.location: - + - + - % endif - % if object.event_id.description : - + + - - % endif - % if not object.event_id.allday and object.event_id.duration: - + + - - % endif +
    -
    - Where -
    -
    -
    - : ${object.event_id.location} - (View Map) - -
    + % if object.event_id.location: +
    + Where +
    + % endif
    + % if object.event_id.location: +
    + : ${object.event_id.location} + (View Map) + +
    + % endif +
    -
    - What -
    + % if object.event_id.description : +
    + What +
    + % endif
    -
    - : ${object.event_id.description} -
    +
    + % if object.event_id.description : +
    + : ${object.event_id.description} +
    + % endif
    -
    - Duration -
    + % if not object.event_id.allday and object.event_id.duration: +
    + Duration +
    + % endif
    -
    - : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} -
    + % if not object.event_id.allday and object.event_id.duration: +
    + : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} +
    + % endif
    @@ -219,9 +227,9 @@
    @@ -265,7 +273,7 @@
    - + -
    ${object.event_id.get_interval(object.event_id.date, 'dayname')}
    @@ -276,50 +284,58 @@
    - % if object.event_id.location: - + - + - % endif - % if object.event_id.description : - + + - - % endif - % if not object.event_id.allday and object.event_id.duration: - + + - - % endif +
    -
    - Where -
    -
    -
    - : ${object.event_id.location} - (View Map) - -
    + % if object.event_id.location: +
    + Where +
    + % endif
    + % if object.event_id.location: +
    + : ${object.event_id.location} + (View Map) + +
    + % endif +
    -
    - What -
    + % if object.event_id.description : +
    + What +
    + % endif
    -
    - : ${object.event_id.description} -
    +
    + % if object.event_id.description : +
    + : ${object.event_id.description} +
    + % endif
    -
    - Duration -
    + % if not object.event_id.allday and object.event_id.duration: +
    + Duration +
    + % endif
    -
    - : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} -
    + % if not object.event_id.allday and object.event_id.duration: +
    + : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} +
    + % endif
    @@ -341,12 +357,12 @@
    +
    @@ -400,50 +416,58 @@ - % if object.event_id.location: - + - + - % endif - % if object.event_id.description : - + + - - % endif - % if not object.event_id.allday and object.event_id.duration: - + + - - % endif +
    -
    - Where -
    -
    -
    - : ${object.event_id.location} - (View Map) - -
    + % if object.event_id.location: +
    + Where +
    + % endif
    + % if object.event_id.location: +
    + : ${object.event_id.location} + (View Map) + +
    + % endif +
    -
    - What -
    + % if object.event_id.description : +
    + What +
    + % endif
    -
    - : ${object.event_id.description} -
    +
    + % if object.event_id.description : +
    + : ${object.event_id.description} +
    + % endif
    -
    - Duration -
    + % if not object.event_id.allday and object.event_id.duration: +
    + Duration +
    + % endif
    -
    - : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} -
    + % if not object.event_id.allday and object.event_id.duration: +
    + : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))} +
    + % endif
    From 4954c0fa39e947c8ac77d595bbd49a2437ad1098 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Fri, 28 Feb 2014 15:39:30 +0100 Subject: [PATCH 043/129] [FIX] Allow other users to read field recurrent_id_date from private event, else we cannot see that this event is excluded from a recurrence bzr revid: jke@openerp.com-20140228143930-3eeam1a3676kgv4i --- addons/calendar/calendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index e25f75d803a..c5f2a1e9af8 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -1515,7 +1515,7 @@ class calendar_event(osv.Model): continue if r['class'] == 'private': for f in r.keys(): - if f not in ('id', 'date', 'date_deadline', 'duration', 'user_id', 'state', 'interval', 'count'): + if f not in ('id', 'date', 'date_deadline', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date'): if isinstance(r[f], list): r[f] = [] else: From 47c4728f4334756da052f92e974bf7751db4f629 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 28 Feb 2014 15:22:33 +0100 Subject: [PATCH 044/129] [FIX] mail,website_sale: rename file_type to file_type_icon to avoid name conflict with file_type field added by document module bzr revid: chs@openerp.com-20140228142233-i0mm9lav0czbydkk --- addons/mail/ir_attachment.py | 9 ++++----- addons/mail/mail_message.py | 4 ++-- addons/mail/static/src/xml/mail.xml | 6 +++--- addons/website_sale/views/website_sale.xml | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/addons/mail/ir_attachment.py b/addons/mail/ir_attachment.py index 99c5a9e054e..a5d8705c0fb 100644 --- a/addons/mail/ir_attachment.py +++ b/addons/mail/ir_attachment.py @@ -186,12 +186,11 @@ class IrAttachment(osv.Model): def get_attachment_type(self, cr, uid, ids, name, args, context=None): result = {} for attachment in self.browse(cr, uid, ids, context=context): - fileext = os.path.splitext(attachment.datas_fname)[1].lower() - if not fileext or not fileext[1:] in self._fileext_to_type: - return 'unknown' - result[attachment.id] = self._fileext_to_type[fileext[1:]] + fileext = os.path.splitext(attachment.datas_fname or '')[1].lower()[1:] + result[attachment.id] = self._fileext_to_type.get(fileext, 'unknown') return result _columns = { - 'file_type': fields.function(get_attachment_type, type='char', string='File Type'), + 'file_type_icon': fields.function(get_attachment_type, type='char', string='File Type Icon'), + 'file_type': fields.related('file_type_icon', type='char'), # FIXME remove in trunk } diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 40a51df91d2..4c05d7a6d1f 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -351,12 +351,12 @@ class mail_message(osv.Model): partner_tree = dict((partner[0], partner) for partner in partners) # 2. Attachments as SUPERUSER, because could receive msg and attachments for doc uid cannot see - attachments = ir_attachment_obj.read(cr, SUPERUSER_ID, list(attachment_ids), ['id', 'datas_fname', 'name', 'file_type'], context=context) + attachments = ir_attachment_obj.read(cr, SUPERUSER_ID, list(attachment_ids), ['id', 'datas_fname', 'name', 'file_type_icon'], context=context) attachments_tree = dict((attachment['id'], { 'id': attachment['id'], 'filename': attachment['datas_fname'], 'name': attachment['name'], - 'file_type': attachment['file_type'], + 'file_type_icon': attachment['file_type_icon'], }) for attachment in attachments) # 3. Update message dictionaries diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 6ce588e82a2..28cbbca1f2c 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -88,10 +88,10 @@ --> - +
    - +
    [
    @@ -100,7 +100,7 @@
    - +
    diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 4f8a5404d74..a1b21137d94 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -419,11 +419,11 @@
    - + - + From a5fc14193de543b71d4e7968d7692756605876fe Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 28 Feb 2014 16:15:44 +0100 Subject: [PATCH 045/129] [FIX] website_mail: forbid access to email_designer routes to public user bzr revid: chs@openerp.com-20140228151544-ipoih9c924rsbqi1 --- addons/website_mail/controllers/email_designer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website_mail/controllers/email_designer.py b/addons/website_mail/controllers/email_designer.py index 81bc703b702..3eb3c32ac96 100644 --- a/addons/website_mail/controllers/email_designer.py +++ b/addons/website_mail/controllers/email_designer.py @@ -7,7 +7,7 @@ from openerp.addons.web.http import request class WebsiteEmailDesigner(http.Controller): - @http.route('/website_mail/email_designer//', type='http', auth="public", website=True, multilang=True) + @http.route('/website_mail/email_designer//', type='http', auth="user", website=True, multilang=True) def index(self, template, **kw): values = { 'template': template, @@ -15,6 +15,6 @@ class WebsiteEmailDesigner(http.Controller): print template return request.website.render("website_mail.designer_index", values) - @http.route(['/website_mail/snippets'], type='json', auth="public", website=True) + @http.route(['/website_mail/snippets'], type='json', auth="user", website=True) def snippets(self): return request.website._render('website_mail.email_designer_snippets') From 57c469123656c4238f8e20a640916d4fc56b0566 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 28 Feb 2014 16:16:48 +0100 Subject: [PATCH 046/129] [TYPO] website_hr_recruitment: s/Kraigslist/Craigslist/g bzr revid: chs@openerp.com-20140228151648-2f33zqeffg8ipcyg --- addons/website_hr_recruitment/static/description/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website_hr_recruitment/static/description/index.html b/addons/website_hr_recruitment/static/description/index.html index 1a56da0e82c..7af603b64dc 100644 --- a/addons/website_hr_recruitment/static/description/index.html +++ b/addons/website_hr_recruitment/static/description/index.html @@ -51,14 +51,14 @@

    Post Your Jobs on Best Job Boards

    -

    LinkedIn, Monster, Kraigslist, Careerbuilder,...

    +

    LinkedIn, Monster, Craigslist, Careerbuilder,...

    From 2d903bcbb80f1b97e3c34c57e8792cc27dc14802 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 28 Feb 2014 17:31:12 +0100 Subject: [PATCH 048/129] [FIX]website: stop animation on focus (clean fix) bzr revid: dle@openerp.com-20140228163112-hru06zno1sl919uq --- .../static/src/js/website.snippets.editor.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 491c0a2aba6..adb0c704227 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -25,8 +25,17 @@ this.on('rte:ready', this, function () { self.snippets.$button.removeClass("hidden"); website.snippet.start_animation(); - // force to unactive slider cycling - setInterval(function () {$(".carousel").carousel("pause");},3000); + $(website.snippet.readyAnimation).each(function() { + var animation = $(this).data("snippet-view"); + if (animation) { + animation.$target.on('focus', '*', function(){ + animation.stop(); + }); + animation.$target.on('blur', '*', function(){ + animation.start(); + }); + } + }); }); return this._super.apply(this, arguments); From 55270edc3c948d79d55a9697a55b65401e78234a Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 28 Feb 2014 18:07:12 +0100 Subject: [PATCH 049/129] [FIX] ir.http.authenticate: avoid discarding sessions when authentication status is unknown In some cases the authentication check can fail for an unknown reason (e.g. connection pool is temporarily full). This should not be treated as an authentication failure, as the status is really unknown. Let those exceptions bubble up instead. bzr revid: odo@openerp.com-20140228170712-l8smq6u3cmvjtd5e --- openerp/addons/base/ir/ir_http.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index 18699110605..93dc4d84e76 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -77,7 +77,9 @@ class ir_http(osv.AbstractModel): # what if error in security.check() # -> res_users.check() # -> res_users.check_credentials() - except Exception: + except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException): + # All other exceptions mean undetermined status (e.g. connection pool full), + # let them bubble up request.session.logout() getattr(self, "_auth_method_%s" % auth_method)() return auth_method From 70d3570846a9786204996e71274967cc31c7fbd6 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 28 Feb 2014 18:09:00 +0100 Subject: [PATCH 050/129] [FIX]website: do not send False value to google map api to retrieve the image. Otherwise, you are in the middle of the ocean. bzr revid: dle@openerp.com-20140228170900-lagmopkmkb58exqr --- addons/website/models/website.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 99187fc7bad..8f95a4a6123 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -572,7 +572,7 @@ class res_partner(osv.osv): def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None): partner = self.browse(cr, uid, ids[0], context=context) params = { - 'center': '%s, %s %s, %s' % (partner.street, partner.city, partner.zip, partner.country_id and partner.country_id.name_get()[0][1] or ''), + 'center': '%s, %s %s, %s' % (partner.street or '', partner.city or '', partner.zip or '', partner.country_id and partner.country_id.name_get()[0][1] or ''), 'size': "%sx%s" % (height, width), 'zoom': zoom, 'sensor': 'false', From a94df36bdb33889723c6fd7e1d78d1511352ab03 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Mon, 3 Mar 2014 10:41:59 +0100 Subject: [PATCH 051/129] [FIX] Remove default tz from context, to allow website (Public user) to usecorrect timeZone. Now event (eg) will be displayed with the TimeZone of "Public User" by default bzr revid: jke@openerp.com-20140303094159-sh4ixlr6f7uojlft --- openerp/http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/http.py b/openerp/http.py index c8c1bed24eb..37017751b95 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -692,7 +692,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): self.setdefault("uid", None) self.setdefault("login", None) self.setdefault("password", None) - self.setdefault("context", {'tz': "UTC", "uid": None}) + self.setdefault("context", {}) def get_context(self): """ From 05fdc2290a2c8ef31f8a0df393cba29b196f6e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 3 Mar 2014 11:40:44 +0100 Subject: [PATCH 052/129] [IMP] email_template: relocate url rewritign stuff directly into the rendering method. bzr revid: tde@openerp.com-20140303104044-hgmii31ga6msf72z --- addons/email_template/email_template.py | 55 ++++++++++++++++++- addons/website_mail/models/__init__.py | 3 +- addons/website_mail/models/email_template.py | 50 ----------------- .../models/mail_compose_message.py | 34 ------------ 4 files changed, 54 insertions(+), 88 deletions(-) delete mode 100644 addons/website_mail/models/mail_compose_message.py diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index d9b65849fae..4a8e6f51706 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -24,6 +24,8 @@ import base64 import datetime import dateutil.relativedelta as relativedelta import logging +import lxml +import urlparse import openerp from openerp import SUPERUSER_ID @@ -70,6 +72,7 @@ try: except ImportError: _logger.warning("jinja2 not available, templating features will not work!") + class email_template(osv.osv): "Templates for sending email" _name = "email.template" @@ -82,7 +85,48 @@ class email_template(osv.osv): res['model_id'] = self.pool['ir.model'].search(cr, uid, [('model', '=', res.pop('model'))], context=context)[0] return res - def render_template_batch(self, cr, uid, template, model, res_ids, context=None): + def _replace_local_links(self, cr, uid, html, context=None): + """ Post-processing of html content to replace local links to absolute + links, using web.base.url as base url. """ + if not html: + return html + + # form a tree + root = lxml.html.fromstring(html) + if not len(root) and root.text is None and root.tail is None: + html = '
    %s
    ' % html + root = lxml.html.fromstring(html) + + base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') + (base_scheme, base_netloc, bpath, bparams, bquery, bfragment) = urlparse.urlparse(base_url) + + def _process_link(url): + new_url = url + (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) + if not scheme and not netloc: + new_url = urlparse.urlunparse((base_scheme, base_netloc, path, params, query, fragment)) + return new_url + + # check all nodes, replace : + # - img src -> check URL + # - a href -> check URL + for node in root.iter(): + if node.tag == 'a': + node.set('href', _process_link(node.get('href'))) + elif node.tag == 'img' and not node.get('src', 'data').startswith('data'): + node.set('src', _process_link(node.get('src'))) + + html = lxml.html.tostring(root, pretty_print=False, method='html') + # this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that + if html.startswith('
    ') and html.endswith('
    '): + html = html[5:-6] + return html + + def render_post_process(self, cr, uid, html, context=None): + html = self._replace_local_links(cr, uid, html, context=context) + return html + + def render_template_batch(self, cr, uid, template, model, res_ids, context=None, post_processing=None): """Render the given template text, replace mako expressions ``${expr}`` with the result of evaluating these expressions with an evaluation context containing: @@ -125,6 +169,10 @@ class email_template(osv.osv): if render_result == u"False": render_result = u"" results[res_id] = render_result + + if post_processing: + for res_id, result in results.iteritems(): + results[res_id] = self.render_post_process(cr, uid, result, context=context) return results def get_email_template_batch(self, cr, uid, template_id=False, res_ids=None, context=None): @@ -357,7 +405,10 @@ class email_template(osv.osv): for template, template_res_ids in templates_to_res_ids.iteritems(): # generate fields value for all res_ids linked to the current template for field in fields: - generated_field_values = self.render_template_batch(cr, uid, getattr(template, field), template.model, template_res_ids, context=context) + generated_field_values = self.render_template_batch( + cr, uid, getattr(template, field), template.model, template_res_ids, + post_processing=(field == 'body_html'), + context=context) for res_id, field_value in generated_field_values.iteritems(): results.setdefault(res_id, dict())[field] = field_value # update values for all res_ids diff --git a/addons/website_mail/models/__init__.py b/addons/website_mail/models/__init__.py index 3e374ccddd2..fc2ecbbc9bb 100644 --- a/addons/website_mail/models/__init__.py +++ b/addons/website_mail/models/__init__.py @@ -1,4 +1,3 @@ import mail_message import mail_thread -import email_template -import mail_compose_message \ No newline at end of file +import email_template \ No newline at end of file diff --git a/addons/website_mail/models/email_template.py b/addons/website_mail/models/email_template.py index 105fcff4428..beb02739f29 100644 --- a/addons/website_mail/models/email_template.py +++ b/addons/website_mail/models/email_template.py @@ -19,9 +19,6 @@ # ############################################################################## -import lxml -import urlparse - from openerp.osv import osv, fields from openerp.tools.translate import _ @@ -39,50 +36,3 @@ class EmailTemplate(osv.Model): help='Link to the website', ), } - - def _postprocess_html_replace_links(self, cr, uid, body_html, context=None): - """ Post-processing of body_html. Indeed the content generated by the - website builder contains references to local addresses, for example - for images. This method changes those addresses to absolute addresses. """ - html = body_html - if not body_html: - return html - - # form a tree - root = lxml.html.fromstring(html) - if not len(root) and root.text is None and root.tail is None: - html = '
    %s
    ' % html - root = lxml.html.fromstring(html) - - base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') - (base_scheme, base_netloc, bpath, bparams, bquery, bfragment) = urlparse.urlparse(base_url) - - def _process_link(url): - new_url = url - (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) - if not scheme and not netloc: - new_url = urlparse.urlunparse((base_scheme, base_netloc, path, params, query, fragment)) - return new_url - - # check all nodes, replace : - # - img src -> check URL - # - a href -> check URL - for node in root.iter(): - if node.tag == 'a': - node.set('href', _process_link(node.get('href'))) - elif node.tag == 'img' and not node.get('src', 'data').startswith('data'): - node.set('src', _process_link(node.get('src'))) - - html = lxml.html.tostring(root, pretty_print=False, method='html') - # this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that - if html.startswith('
    ') and html.endswith('
    '): - html = html[5:-6] - return html - - def generate_email_batch(self, cr, uid, template_id, res_ids, context=None, fields=None): - """ Add a post processing after rendering, aka replace local URLs to absolute URLs. """ - results = super(EmailTemplate, self).generate_email_batch(cr, uid, template_id, res_ids, context=context, fields=fields) - for res_id, value in results.iteritems(): - if 'body_html' in value: - results[res_id]['body_html'] = self._postprocess_html_replace_links(cr, uid, value['body_html'], context=context) - return results diff --git a/addons/website_mail/models/mail_compose_message.py b/addons/website_mail/models/mail_compose_message.py deleted file mode 100644 index bc8258bbb6f..00000000000 --- a/addons/website_mail/models/mail_compose_message.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2014-Today OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import osv - - -class MailComposeMessage(osv.Model): - _inherit = 'mail.compose.message' - - def generate_email_for_composer_batch(self, cr, uid, template_id, res_ids, context=None, fields=None): - """ Add a post processing after rendering, aka replace local URLs to absolute URLs. """ - results = super(MailComposeMessage, self).generate_email_for_composer_batch(cr, uid, template_id, res_ids, context=context, fields=fields) - for res_id, value in results.iteritems(): - if 'body' in value: - results[res_id]['body'] = self.pool['email.template']._postprocess_html_replace_links(cr, uid, value['body'], context=context) - return results From 9d00835b2c7ea32441097318bf9c78ef48373118 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 3 Mar 2014 14:54:26 +0100 Subject: [PATCH 053/129] [FIX] order of attachments in 'existing images' sub-dialog: put last uploaded first bzr revid: xmo@openerp.com-20140303135426-evouhwgjtwku3v2b --- addons/website/static/src/js/website.editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 303a7ee79d4..f7e3255de24 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -1463,7 +1463,7 @@ kwargs: { fields: ['name', 'website_url'], domain: [['res_model', '=', 'ir.ui.view']], - order: 'name', + order: 'id desc', context: website.get_context(), } }); From ec68d3d8bd13e84cd742e849c4ac602805a5d4c3 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 3 Mar 2014 15:24:01 +0100 Subject: [PATCH 054/129] [IMP] don't duplicate website_url function field of attachments bzr revid: xmo@openerp.com-20140303142401-2jaxc3m31tmqw9qa --- addons/website/controllers/main.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 605315050c6..856ab6ecffc 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -259,20 +259,18 @@ class Website(openerp.addons.web.controllers.main.Home): u"Image size excessive, uploaded images must be smaller " u"than 42 million pixel") - attachment_id = request.registry['ir.attachment'].create(request.cr, request.uid, { + Attachments = request.registry['ir.attachment'] + attachment_id = Attachments.create(request.cr, request.uid, { 'name': upload.filename, 'datas': image_data.encode('base64'), 'datas_fname': upload.filename, 'res_model': 'ir.ui.view', }, request.context) - url = website.urlplus('/website/image', { - 'model': 'ir.attachment', - 'id': attachment_id, - 'field': 'datas', - 'max_height': MAX_IMAGE_HEIGHT, - 'max_width': MAX_IMAGE_WIDTH, - }) + [attachment] = Attachments.read( + request.cr, request.uid, [attachment_id], ['website_url'], + context=request.context) + url = attachment['website_url'] except Exception, e: logger.exception("Failed to upload image to attachment") message = unicode(e) From a99a15c9607cfede384a86736cc4f81ab24e841a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 3 Mar 2014 16:38:55 +0100 Subject: [PATCH 055/129] [ADD] deduplication of website images being uploaded There is a deduplication in ir.attachment, but it's only for FS-stored content *and* it only deduplicates storage not models (as there are access rights issues involved). The goal here is to always return the same attachment when a user uploads the exact same image multiple times (because it's simpler or whatever). Initially tried to use a binary field & digest(), but search() blows up because it tries to utf-8 encode raw binary data. So use char & hexdigest instead. _compute_checksum returns None if the provided attachment data does not look like a website image attachment. Unhandled: multiple existing matches, maybe a UNIQUE constraint on the checksum field would be a good idea just in case. bzr revid: xmo@openerp.com-20140303153855-5f2l8v0jq2mgb26f --- addons/website/models/website.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 8f95a4a6123..4e39f291df2 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import hashlib import inspect import itertools import logging @@ -562,10 +563,37 @@ class ir_attachment(osv.osv): 'max_height': 768, }) return result + def _datas_checksum(self, cr, uid, ids, name, arg, context=None): + return dict( + (attach['id'], self._compute_checksum(attach)) + for attach in self.read( + cr, uid, ids, ['res_model', 'res_id', 'type', 'datas'], + context=context) + ) + + def _compute_checksum(self, attachment_dict): + if attachment_dict.get('res_model') == 'ir.ui.view'\ + and not attachment_dict.get('res_id')\ + and attachment_dict.get('type', 'binary') == 'binary'\ + and attachment_dict.get('datas'): + return hashlib.new('sha1', attachment_dict['datas']).hexdigest() + return None + _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') } + def create(self, cr, uid, values, context=None): + chk = self._compute_checksum(values) + if chk: + match = self.search(cr, uid, [('datas_checksum', '=', chk)], context=context) + if match: + return match[0] + return super(ir_attachment, self).create( + cr, uid, values, context=context) + class res_partner(osv.osv): _inherit = "res.partner" From f3638567738cdc242fa43dc0a1770ea7bd4b4b6e Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 3 Mar 2014 18:44:45 +0100 Subject: [PATCH 056/129] [FIX] website_hr_recruitment: missing required 'name', which leaded to crash, and the phone field is partner_phone, not just phone. Do you even test what you do ? bzr revid: dle@openerp.com-20140303174445-j733tpmccc9x855u --- addons/website_hr_recruitment/controllers/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/website_hr_recruitment/controllers/main.py b/addons/website_hr_recruitment/controllers/main.py index a3cbbe3cbc4..8140a39dc9b 100644 --- a/addons/website_hr_recruitment/controllers/main.py +++ b/addons/website_hr_recruitment/controllers/main.py @@ -86,11 +86,14 @@ class website_hr_recruitment(http.Controller): value = { 'source_id' : imd.xmlid_to_res_id(cr, SUPERUSER_ID, 'hr_recruitment.source_website_company'), + 'name': '%s\'s Application' % post.get('partner_name'), } - for f in ['phone', 'email_from', 'partner_name', 'description']: + for f in ['email_from', 'partner_name', 'description']: value[f] = post.get(f) for f in ['department_id', 'job_id']: value[f] = int(post.get(f) or 0) + # Retro-compatibility for saas-3. "phone" field should be replace by "partner_phone" in the template in trunk. + value['partner_phone'] = post.pop('phone', False) applicant_id = request.registry['hr.applicant'].create(cr, SUPERUSER_ID, value, context=context) if post['ufile']: From e78a3b18cc6c049a712d594ab70287c486447f0a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 4 Mar 2014 12:07:16 +0100 Subject: [PATCH 057/129] [ADD] tentative removal of attachments Attachments ought be removed only if they are not used in an existing page/web view. Theoretically this could be set directly in unlink(), but: * that would make a nice error message significantly harder * the expenses of performing a text search in all view archs would be a bit expensive Notes: * the views set could be reduced to only "web" views * the search is likely sensible to false negatives e.g. different order of query parameters. It *will* remove images still being used. bzr revid: xmo@openerp.com-20140304110716-u14w6uo8fbkfa42i --- addons/website/models/website.py | 28 +++++++++++++++++ addons/website/static/src/css/editor.css | 20 +++++++++++++ addons/website/static/src/css/editor.sass | 22 ++++++++++++-- .../website/static/src/js/website.editor.js | 30 +++++++++++++++++++ .../website/static/src/xml/website.editor.xml | 20 +++++++++++-- 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 4e39f291df2..5da633a27a6 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -9,6 +9,7 @@ import urlparse import werkzeug import werkzeug.exceptions +import werkzeug.utils import werkzeug.wrappers # optional python-slugify import (https://github.com/un33k/python-slugify) try: @@ -594,6 +595,33 @@ class ir_attachment(osv.osv): return super(ir_attachment, self).create( cr, uid, values, context=context) + def try_remove(self, cr, uid, ids, context=None): + """ Removes a web-based image attachment if it is used by no view + (template) + + Returns a dict mapping attachments which would not be removed (if any) + mapped to the views preventing their removal + """ + Views = self.pool['ir.ui.view'] + attachments_to_remove = [] + # views blocking removal of the attachment + removal_blocked_by = {} + + for attachment in self.browse(cr, uid, ids, context=context): + # in-document URLs are html-escaped, a straight search will not + # find them + url = werkzeug.utils.escape(attachment.website_url) + ids = Views.search(cr, uid, [('arch', 'like', url)], context=context) + + if ids: + removal_blocked_by[attachment.id] = Views.read( + cr, uid, ids, ['name'], context=context) + else: + attachments_to_remove.append(attachment.id) + if attachments_to_remove: + self.unlink(cr, uid, attachments_to_remove, context=context) + return removal_blocked_by + class res_partner(osv.osv): _inherit = "res.partner" diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index df2188e794d..da0fad43caf 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -251,6 +251,26 @@ ul.oe_menu_editor .disclose { .existing-attachments .pager .disabled { display: none; } +.existing-attachments .existing-attachment-cell { + position: relative; +} +.existing-attachments .existing-attachment-cell .img { + border: 1px solid #848490; +} +.existing-attachments .existing-attachment-cell .existing-attachment-remove { + position: absolute; + top: 0; + left: 15px; + cursor: pointer; + background: white; + padding: 2px; + border: 1px solid #848490; + border-top: none; + border-left: none; + -moz-border-radius-bottomright: 8px; + -webkit-border-bottom-right-radius: 8px; + border-bottom-right-radius: 8px; +} .cke_widget_wrapper { position: static !important; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 49b71ffefdf..4571dcd257e 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -211,9 +211,27 @@ ul.oe_menu_editor .font-icons-selected background-color: #ddd +$attachment-border-color: #848490 +.existing-attachments + .pager .disabled + display: none -.existing-attachments .pager .disabled - display: none + .existing-attachment-cell + position: relative + .img + border: 1px solid $attachment-border-color + .existing-attachment-remove + position: absolute + top: 0 + left: 15px // padding-left on col-* + + cursor: pointer + background: white + padding: 2px + border: 1px solid $attachment-border-color + border-top: none + border-left: none + +border-bottom-right-radius(8px) // wrapper positioned relatively for drag&drop widget which is disabled below. // Breaks completely horribly crazy products listing page, so take it out. diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index f7e3255de24..3915c3daaeb 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -1441,6 +1441,7 @@ this.page += $target.hasClass('previous') ? -1 : 1; this.display_attachments(); }, + 'click .existing-attachment-remove': 'try_remove', }), init: function (parent) { this.image = null; @@ -1473,6 +1474,7 @@ this.display_attachments(); }, display_attachments: function () { + this.$('.help-block').empty(); var per_screen = IMAGES_PER_ROW * IMAGES_ROWS; var from = this.page * per_screen; @@ -1500,6 +1502,34 @@ } this.close() }, + + try_remove: function (e) { + var $help_block = this.$('.help-block').empty(); + var self = this; + var id = parseInt($(e.target).data('id'), 10); + var attachment = _.findWhere(this.records, {id: id}); + + return openerp.jsonRpc('/web/dataset/call_kw', 'call', { + model: 'ir.attachment', + method: 'try_remove', + args: [], + kwargs: { + ids: [id], + context: website.get_context() + } + }).then(function (prevented) { + if (_.isEmpty(prevented)) { + self.records = _.without(self.records, attachment); + self.display_attachments(); + return; + } + $help_block.replaceWith(openerp.qweb.render( + 'website.editor.dialog.image.existing.error', { + views: prevented[id] + } + )); + }); + }, }); function get_selected_link(editor) { diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 6c1e387c958..1010ec05127 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -140,8 +140,9 @@
    -