From 07c4f41e31fa29cc8bc17ae818a5be60e64c6ca9 Mon Sep 17 00:00:00 2001 From: kbh Date: Thu, 4 Aug 2011 16:39:22 +0530 Subject: [PATCH 01/46] [FIX] Implemented Clear button for search view. bzr revid: kbh@bde-desktop-20110804110922-chcld5vyoovumzha --- addons/base/static/src/js/search.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 0a1f2396123..76cfb5864e8 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -315,10 +315,26 @@ openerp.base.SearchView = openerp.base.Widget.extend({ on_invalid: function (errors) { this.notification.notify("Invalid Search", "triggered from search view"); }, - do_clear: function (e) { - if (e && e.preventDefault) { e.preventDefault(); } - this.on_clear(); - }, + do_clear: function () { + this.$element.find(':input').each(function(){ + switch(this.type) { + case 'text': + case 'string': + $(this).val(''); + break; + } + }); + var string = ($("div.expanded").not("#oe_app_search table:first-child .expanded")); + if (string){ + $(string).removeClass('expanded'); + $(string).addClass('folded'); + } + $('#oe_app_search table:last').css('display', 'none'); + $('.searchview_extended_groups_list').empty(); + $('.filter_label').removeClass('enabled'); + this.$element.find('form').submit(); + + }, /** * Triggered when the search view gets cleared * From 081b7d77d60f583cb184f034b3424bb1a620c610 Mon Sep 17 00:00:00 2001 From: kbh Date: Fri, 12 Aug 2011 11:33:06 +0530 Subject: [PATCH 02/46] [FIX] Fixed clear option. bzr revid: kbh@bde-desktop-20110812060306-ozmfx36vvz1uy1h0 --- addons/base/static/src/js/search.js | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 76cfb5864e8..8a4f542f1c7 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -316,24 +316,13 @@ openerp.base.SearchView = openerp.base.Widget.extend({ this.notification.notify("Invalid Search", "triggered from search view"); }, do_clear: function () { - this.$element.find(':input').each(function(){ - switch(this.type) { - case 'text': - case 'string': - $(this).val(''); - break; - } - }); - var string = ($("div.expanded").not("#oe_app_search table:first-child .expanded")); - if (string){ - $(string).removeClass('expanded'); - $(string).addClass('folded'); - } - $('#oe_app_search table:last').css('display', 'none'); + var string = $('a.searchview_group_string'); + _.each(string, function(str){ + $(str).closest('div.searchview_group').removeClass("expanded").addClass('folded'); + }); + this.$element.find('table:last').hide(); $('.searchview_extended_groups_list').empty(); $('.filter_label').removeClass('enabled'); - this.$element.find('form').submit(); - }, /** * Triggered when the search view gets cleared @@ -475,7 +464,7 @@ openerp.base.search.FilterGroup = openerp.base.search.Widget.extend({ }); openerp.base.search.add_expand_listener = function($root) { $root.find('a.searchview_group_string').click(function (e) { - $root.toggleClass('folded expanded'); + $root.toggleClass('folded expanded'); e.stopPropagation(); e.preventDefault(); }); From 9c465bb106703569033b16fd1fc7b9282f96a53d Mon Sep 17 00:00:00 2001 From: vishmita Date: Fri, 19 Aug 2011 18:40:07 +0530 Subject: [PATCH 03/46] [IMP]Implement preferences functionality. bzr revid: vja@vja-desktop-20110819131007-0o2pxuvrvh9q5cc3 --- addons/base/controllers/main.py | 16 ++++- addons/base/static/src/js/chrome.js | 92 ++++++++++++++++++++++++++++- addons/base/static/src/js/form.js | 2 +- addons/base/static/src/xml/base.xml | 31 ++++++++++ 4 files changed, 137 insertions(+), 4 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index d5eaafaecee..6a7f143776c 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -278,7 +278,7 @@ class Database(openerpweb.Controller): except xmlrpclib.Fault, e: if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': return {'error': e.faultCode, 'title': 'Change Password'} - return {'error': 'Error, password not changed !', 'title': 'Change Password'} + return {'error': 'Error, password not changed !', 'title': 'Change Password'} class Session(openerpweb.Controller): _cp_path = "/base/session" @@ -293,7 +293,19 @@ class Session(openerpweb.Controller): "uid": req.session._uid, "context": ctx } - + @openerpweb.jsonrequest + def change_password (self,req,fields): + old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_pwd','confirm_pwd')( + dict(map(operator.itemgetter('name', 'value'), fields))) + try: + if req.session.model('res.users').change_password( + old_password, new_password): + req.session.password = new_password + return dict(changed=True) + raise redirect('/') + except xmlrpclib.Fault, e: + return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'} + @openerpweb.jsonrequest def sc_list(self, req): return req.session.model('ir.ui.view_sc').get_sc( diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 3aff7b5e402..861a02cc49a 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -572,6 +572,7 @@ openerp.base.Header = openerp.base.Widget.extend({ do_update: function () { this.$element.html(QWeb.render("Header", this)); this.$element.find(".logout").click(this.on_logout); + this.$element.find("a.preferences").click(this.on_preferences); return this.shortcut_load(); }, shortcut_load :function(){ @@ -621,9 +622,98 @@ openerp.base.Header = openerp.base.Widget.extend({ }); }); }, + on_action: function(action) { }, - + on_preferences: function(){ + var self = this; + var action_manager = new openerp.base.ActionManager(this); + this.rpc('/base/dataset/call', {model: 'res.users',method:"action_get",args: []},function(result){ + self.rpc('/base/action/load', { action_id: result}, function(result){ + result['result']['res_id'] = self.session.uid; + result['result']['res_model']= 'res.users'; + result['result']['view_id']=result['result']['views'][0][0]; + result['result']['flags']['action_buttons']=false; + result['result']['flags']['search_view']=false; + result['result']['flags']['sidebar']=false; + result['result']['view_mode']="form"; + result['result']['flags']['views_switcher']=false; + result['result']['flags']['pager']=false; + action_manager.do_action(result['result']); + }); + }); + this.dialog = new openerp.base.Dialog(this,{ + modal: true, + title: 'Preferences', + width: 600, + height: 500, + buttons: { + "Change password": function(){ + self.change_password(); + }, + Cancel: function(){ + $(this).dialog('destroy'); + }, + Save: function(){ + var inner_viewmanager = action_manager.inner_viewmanager; + inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save(function(){ + inner_viewmanager.start(); + }); + $(this).dialog('destroy'); + }, + }, + }); + this.dialog.start(); + this.dialog.open(); + action_manager.appendTo(this.dialog); + action_manager.render(this.dialog); + }, + + change_password :function() { + var self = this; + this.dialog = new openerp.base.Dialog(this,{ + modal : true, + title : 'Change Password', + width : 'auto', + height : 'auto', + }); + this.dialog.start(); + this.dialog.open(); + this.dialog.$element.html(QWeb.render("Change_Pwd", self)); + this.dialog.$element.find("form[name=change_password_form]").validate({ + messages: { + old_password: "Please enter your previous password", + new_password: "Please enter your new password", + confirm_password: { + required: "Please confirm your new password", + equalTo: "The confirmation does not match the password" + }, + }, + submitHandler: function (form) { + self.rpc("/base/session/change_password",{ + 'fields': $("form[name=change_password_form]").serializeArray() + }, function(result) { + if (result.error) { + self.display_error(result); + return; + } + self.notification.notify("Changed Password", "Password has been changed successfully"); + self.dialog.close(); + }); + }, + }); +}, + display_error: function (error) { + return $('
').dialog({ + modal: true, + title: error.title, + buttons: { + Ok: function() { + $(this).dialog("close"); + } + } + }).html(error.error); + }, on_logout: function() { this.$element.find('.oe-shortcuts ul').empty(); } diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index c85d1fd4cb2..66f41005c05 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -66,7 +66,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi var self = this; this.fields_view = data.fields_view; var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch); - this.$element.html(QWeb.render(this.template, { 'frame': frame, 'view': this })); _.each(this.widgets, function(w) { w.start(); @@ -578,6 +577,7 @@ openerp.base.form.Widget = openerp.base.Widget.extend({ this.$element = $('#' + this.element_id); }, stop: function() { + this.$element = $('#' + this.element_id); this.$element.remove(); }, process_modifiers: function() { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 81255fb3e75..7baf8d91c13 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1307,4 +1307,35 @@ + +
+ + + + +
+ CHANGE PASSWORD +
+ + + + + + + + + + + + + + + + +
+
+
From 754205281d966d02f0014cbdd1fc438c869701e6 Mon Sep 17 00:00:00 2001 From: vishmita Date: Tue, 23 Aug 2011 10:36:07 +0530 Subject: [PATCH 04/46] [IMP]remove title in change password dialog. bzr revid: vja@vja-desktop-20110823050607-tdu2bzwrsfz04tvf --- addons/base/static/src/xml/base.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 7baf8d91c13..31c5ac6693a 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1309,13 +1309,6 @@
- - - - -
- CHANGE PASSWORD -
From 9d93821bae21cba47b9e7237af19c9d64cafa2d0 Mon Sep 17 00:00:00 2001 From: vishmita Date: Tue, 23 Aug 2011 14:47:21 +0530 Subject: [PATCH 05/46] [IMP]remove RPC method in Preferences. bzr revid: vja@vja-desktop-20110823091721-83v50nq5fhojkf7v --- addons/base/static/src/js/chrome.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 861a02cc49a..d0901e37d07 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -628,8 +628,8 @@ openerp.base.Header = openerp.base.Widget.extend({ on_preferences: function(){ var self = this; var action_manager = new openerp.base.ActionManager(this); - this.rpc('/base/dataset/call', {model: 'res.users',method:"action_get",args: []},function(result){ - self.rpc('/base/action/load', { action_id: result}, function(result){ + var dataset = new openerp.base.DataSet (this,'res.users',this.context); + dataset.call ('action_get','',function (result){self.rpc('/base/action/load', {action_id:result}, function(result){ result['result']['res_id'] = self.session.uid; result['result']['res_model']= 'res.users'; result['result']['view_id']=result['result']['views'][0][0]; @@ -641,7 +641,7 @@ openerp.base.Header = openerp.base.Widget.extend({ result['result']['flags']['pager']=false; action_manager.do_action(result['result']); }); - }); + }); this.dialog = new openerp.base.Dialog(this,{ modal: true, title: 'Preferences', From f2325382caf64e3021cd5c0df116180802b2f89e Mon Sep 17 00:00:00 2001 From: vishmita Date: Tue, 23 Aug 2011 18:28:02 +0530 Subject: [PATCH 06/46] [IMP]Improve code in change password. bzr revid: vja@vja-desktop-20110823125802-c4jhe4qqjn2ra7n4 --- addons/base/controllers/main.py | 13 ++++++++----- addons/base/static/src/js/chrome.js | 15 ++++----------- addons/base/static/src/xml/base.xml | 16 ++++++++-------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 6a7f143776c..9493620c1cb 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -295,15 +295,18 @@ class Session(openerpweb.Controller): } @openerpweb.jsonrequest def change_password (self,req,fields): - old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_pwd','confirm_pwd')( + old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')( dict(map(operator.itemgetter('name', 'value'), fields))) + if not (old_password.strip() and new_password.strip() and confirm_password.strip()): + return {'error':'All passwords have to be filled.','title': 'Change Password'} + if new_password != confirm_password: + return {'error': 'The new password and its confirmation must be identical.','title': 'Change Password'} try: if req.session.model('res.users').change_password( old_password, new_password): - req.session.password = new_password - return dict(changed=True) - raise redirect('/') - except xmlrpclib.Fault, e: + req.session.password = new_password; + return dict(changed=True) + except: return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'} @openerpweb.jsonrequest diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index d0901e37d07..1a3289457c3 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -459,7 +459,7 @@ openerp.base.Database = openerp.base.Widget.extend({ } self.notification.notify("Changed Password", "Password has been changed successfully"); }); - } + }, }); } }); @@ -681,22 +681,15 @@ openerp.base.Header = openerp.base.Widget.extend({ this.dialog.open(); this.dialog.$element.html(QWeb.render("Change_Pwd", self)); this.dialog.$element.find("form[name=change_password_form]").validate({ - messages: { - old_password: "Please enter your previous password", - new_password: "Please enter your new password", - confirm_password: { - required: "Please confirm your new password", - equalTo: "The confirmation does not match the password" - }, - }, submitHandler: function (form) { + console.log('form',form) self.rpc("/base/session/change_password",{ - 'fields': $("form[name=change_password_form]").serializeArray() + 'fields': $(form).serializeArray() }, function(result) { if (result.error) { self.display_error(result); return; - } + }; self.notification.notify("Changed Password", "Password has been changed successfully"); self.dialog.close(); }); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 31c5ac6693a..03257f7976b 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1309,21 +1309,21 @@ -
+
- - - + + - + - + From bb00369ecc8a2d6dd09967864a67206d7782bfaf Mon Sep 17 00:00:00 2001 From: vishmita Date: Mon, 29 Aug 2011 17:49:43 +0530 Subject: [PATCH 07/46] [FIX] Need to reload searchview after Clear . bzr revid: vja@vja-desktop-20110829121943-nqakojh56mn4rezy --- addons/base/static/src/js/search.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 00481fdf992..902217bbbda 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -315,14 +315,24 @@ openerp.base.SearchView = openerp.base.Widget.extend({ this.notification.notify("Invalid Search", "triggered from search view"); }, do_clear: function () { + this.$element.find(':input').each(function(){ + switch(this.type) { + case 'text': + case 'select-one': + $(this).val(''); + break; + } + }); + $('.filter_label').removeClass('enabled'); var string = $('a.searchview_group_string'); _.each(string, function(str){ $(str).closest('div.searchview_group').removeClass("expanded").addClass('folded'); }); this.$element.find('table:last').hide(); + $('.searchview_extended_groups_list').empty(); - $('.filter_label').removeClass('enabled'); - }, + this.do_search(); + }, /** * Triggered when the search view gets cleared * From 79e182a06f69bee51ea38476fec848677c1ff9b5 Mon Sep 17 00:00:00 2001 From: vishmita Date: Tue, 30 Aug 2011 16:46:49 +0530 Subject: [PATCH 08/46] [IMP] Need to change code in ChangePassword. bzr revid: vja@vja-desktop-20110830111649-p5yzyoh5i0221bxo --- addons/base/controllers/main.py | 5 ++- addons/base/static/src/js/chrome.js | 50 ++++++++++++++++------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 9493620c1cb..8e65aad9982 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -304,11 +304,10 @@ class Session(openerpweb.Controller): try: if req.session.model('res.users').change_password( old_password, new_password): - req.session.password = new_password; - return dict(changed=True) + return {'new_password':new_password} except: return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'} - + return {'error': 'Error, password not changed !', 'title': 'Change Password'} @openerpweb.jsonrequest def sc_list(self, req): return req.session.model('ir.ui.view_sc').get_sc( diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 1a3289457c3..ff95c72d1e2 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -630,16 +630,17 @@ openerp.base.Header = openerp.base.Widget.extend({ var action_manager = new openerp.base.ActionManager(this); var dataset = new openerp.base.DataSet (this,'res.users',this.context); dataset.call ('action_get','',function (result){self.rpc('/base/action/load', {action_id:result}, function(result){ - result['result']['res_id'] = self.session.uid; - result['result']['res_model']= 'res.users'; - result['result']['view_id']=result['result']['views'][0][0]; - result['result']['flags']['action_buttons']=false; - result['result']['flags']['search_view']=false; - result['result']['flags']['sidebar']=false; - result['result']['view_mode']="form"; - result['result']['flags']['views_switcher']=false; - result['result']['flags']['pager']=false; - action_manager.do_action(result['result']); + var action = result['result']; + action['res_id'] = self.session.uid; + action['res_model']= 'res.users'; + action['view_id']=result['result']['views'][0][0]; + action['flags']['action_buttons']=false; + action['flags']['search_view']=false; + action['flags']['sidebar']=false; + action['view_mode']="form"; + action['flags']['views_switcher']=false; + action['flags']['pager']=false; + action_manager.do_action(action); }); }); this.dialog = new openerp.base.Dialog(this,{ @@ -659,12 +660,11 @@ openerp.base.Header = openerp.base.Widget.extend({ inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save(function(){ inner_viewmanager.start(); }); - $(this).dialog('destroy'); - }, - }, + $(this).dialog('destroy') + } + } }); - this.dialog.start(); - this.dialog.open(); + this.dialog.start().open(); action_manager.appendTo(this.dialog); action_manager.render(this.dialog); }, @@ -677,19 +677,25 @@ openerp.base.Header = openerp.base.Widget.extend({ width : 'auto', height : 'auto', }); - this.dialog.start(); - this.dialog.open(); + this.dialog.start().open(); this.dialog.$element.html(QWeb.render("Change_Pwd", self)); this.dialog.$element.find("form[name=change_password_form]").validate({ submitHandler: function (form) { - console.log('form',form) self.rpc("/base/session/change_password",{ 'fields': $(form).serializeArray() - }, function(result) { - if (result.error) { - self.display_error(result); + }, function(result) { + if (result.error) { + self.display_error(result); return; - }; + } + else { + if (result.new_password) { + self.session.password = result.new_password; + var session = new openerp.base.Session(self.session.server, self.session.port); + session.start(); + session.session_login(self.session.db, self.session.login, self.session.password) + } + } self.notification.notify("Changed Password", "Password has been changed successfully"); self.dialog.close(); }); From 5b8ad8068bac7f124071249899e91822f11f194f Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 31 Aug 2011 12:21:11 +0200 Subject: [PATCH 09/46] [fix] some bugs with float & date parsing + corrected tests bzr revid: nicolas.vanhoren@openerp.com-20110831102111-ure7wc02nu33v4n2 --- addons/base/static/src/js/formats.js | 8 ++++---- addons/base/static/test/formats.js | 21 ++++++++++++--------- addons/base/static/test/test.html | 6 +++++- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/addons/base/static/src/js/formats.js b/addons/base/static/src/js/formats.js index 8e2b2dd18cb..bf54290bf45 100644 --- a/addons/base/static/src/js/formats.js +++ b/addons/base/static/src/js/formats.js @@ -30,8 +30,8 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { var int_part = Math.floor(value); var dec_part = Math.abs(Math.floor((value % 1) * Math.pow(10, precision))); return _.sprintf('%d%s%d', - int_part, dec_part, - openerp.base._t.database.parameters.decimal_point); + int_part, + openerp.base._t.database.parameters.decimal_point, dec_part); case 'float_time': return _.sprintf("%02d:%02d", Math.floor(value), @@ -119,7 +119,7 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) { return tmp; throw value + " is not a valid datetime"; case 'date': - var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.shortDate); + var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate); if (tmp !== null) return tmp; tmp = Date.parse(value); @@ -127,7 +127,7 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) { return tmp; throw value + " is not a valid date"; case 'time': - var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.longTime); + var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime); if (tmp !== null) return tmp; tmp = Date.parse(value); diff --git a/addons/base/static/test/formats.js b/addons/base/static/test/formats.js index 4c46f050998..66ffd1b5c3c 100644 --- a/addons/base/static/test/formats.js +++ b/addons/base/static/test/formats.js @@ -11,17 +11,17 @@ $(document).ready(function () { test("format_datetime", function () { var date = openerp.base.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.base.format_value(date, {type:"datetime"}); - equal(str, date.format("%m/%d/%Y %H:%M:%S")); + equal(str, date.toString("M/d/yyyy h:mm:ss tt")); }); test("format_date", function () { var date = openerp.base.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.base.format_value(date, {type:"date"}); - equal(str, date.format("%m/%d/%Y")); + equal(str, date.toString("M/d/yyyy")); }); test("format_time", function () { var date = openerp.base.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.base.format_value(date, {type:"time"}); - equal(str, date.format("%H:%M:%S")); + equal(str, date.toString("h:mm:ss tt")); }); test("format_float", function () { var fl = 12.1234; @@ -30,22 +30,25 @@ $(document).ready(function () { }); test("parse_datetime", function () { var val = openerp.base.str_to_datetime("2009-05-04 12:34:23"); - var res = openerp.base.parse_value(val.format("%m/%d/%Y %H:%M:%S"), {type:"datetime"}); - equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S")); + var res = openerp.base.parse_value(val.toString("M/d/yyyy h:mm:ss tt"), {type:"datetime"}); + equal(val.toString("M/d/yyyy h:mm:ss tt"), res.toString("M/d/yyyy h:mm:ss tt")); }); test("parse_date", function () { var val = openerp.base.str_to_date("2009-05-04"); - var res = openerp.base.parse_value(val.format("%m/%d/%Y"), {type:"date"}); - equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S")); + var res = openerp.base.parse_value(val.toString("M/d/yyyy"), {type:"date"}); + equal(val.toString("M/d/yyyy"), res.toString("M/d/yyyy")); }); test("parse_time", function () { var val = openerp.base.str_to_time("12:34:23"); - var res = openerp.base.parse_value(val.format("%H:%M:%S"), {type:"time"}); - equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S")); + var res = openerp.base.parse_value(val.toString("h:mm:ss tt"), {type:"time"}); + equal(val.toString("h:mm:ss tt"), res.toString("h:mm:ss tt")); }); test("parse_float", function () { var str = "134,112.1234"; var val = openerp.base.parse_value(str, {type:"float"}); equal(val, 134112.1234); + var str = "-134,112.1234"; + var val = openerp.base.parse_value(str, {type:"float"}); + equal(val, -134112.1234); }); }); \ No newline at end of file diff --git a/addons/base/static/test/test.html b/addons/base/static/test/test.html index 9c741eb5336..957456f4280 100644 --- a/addons/base/static/test/test.html +++ b/addons/base/static/test/test.html @@ -14,7 +14,11 @@ - + + + + + From b44295bba90d1e853d0a73df3a42a6b64ba718fc Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 31 Aug 2011 14:45:38 +0200 Subject: [PATCH 10/46] [IMP] Many improvements to form frame rendering (with computation algorithm) bzr revid: fme@openerp.com-20110831124538-xt3px1rv8ys181se --- addons/base/static/src/css/base.css | 3 ++ addons/base/static/src/js/form.js | 56 ++++++++++++++++++++--------- addons/base/static/src/xml/base.xml | 13 +++---- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 4133a9c8623..a33bdce973b 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -931,6 +931,9 @@ label.error { margin: 3px 4px 3px 1px; height: 15px; } +.openerp .oe_form_frame_cell.oe_form_separator_vertical { + border-left: 1px solid #666; +} .openerp td.required input, .openerp td.required select { background-color: #D2D2FF !important; } diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index e2a99e9b8c5..11ddc1a3850 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -83,9 +83,11 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel); this.$form_header.find('button.oe_form_button_new').click(this.on_button_new); - this.$form_header.find('button.oe_get_xml_view').click(function() { - $('' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '').dialog({ width: '95%', height: 600}); - }); + if (this.session.debug) { + this.$form_header.find('button.oe_get_xml_view').click(function() { + $('' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '').dialog({ width: '95%', height: 600}); + }); + } if (this.options.sidebar && this.options.sidebar_id) { this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id); @@ -577,17 +579,23 @@ openerp.base.form.Widget = openerp.base.Widget.extend({ this.view.widgets[this.element_id] = this; this.children = node.children; - this.colspan = parseInt(node.attrs.colspan || 1); + this.colspan = parseInt(node.attrs.colspan || 1, 10); + this.decrease_max_width = 0; this.string = this.string || node.attrs.string; this.help = this.help || node.attrs.help; this.invisible = this.modifiers['invisible'] === true; + this.classname = 'oe_form_' + this.type; + + this.width = this.node.attrs.width; }, start: function() { this.$element = $('#' + this.element_id); }, stop: function() { - this.$element.remove(); + if (this.$element) { + this.$element.remove(); + } }, process_modifiers: function() { var compute_domain = openerp.base.form.compute_domain; @@ -608,7 +616,7 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({ template: 'WidgetFrame', init: function(view, node) { this._super(view, node); - this.columns = node.attrs.col || 4; + this.columns = parseInt(node.attrs.col || 4, 10); this.x = 0; this.y = 0; this.table = []; @@ -634,15 +642,21 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({ return row; }, set_row_cells_with: function(row) { + var bypass = 0, + max_width = 100; + for (var i = 0; i < row.length; i++) { + bypass += row[i].width === undefined ? 0 : 1; + max_width -= row[i].decrease_max_width; + } + var size_unit = Math.round(max_width / (this.columns - bypass)), + colspan_sum = 0; for (var i = 0; i < row.length; i++) { var w = row[i]; - if (w.is_field_label) { - w.width = "1%"; - if (row[i + 1]) { - row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%'; - } - } else if (w.width === undefined) { - w.width = Math.round((100 / this.columns) * w.colspan) + '%'; + colspan_sum += w.colspan; + if (w.width === undefined) { + var width = (i === row.length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan); + max_width -= width; + w.width = width + '%'; } } }, @@ -732,6 +746,11 @@ openerp.base.form.WidgetSeparator = openerp.base.form.Widget.extend({ init: function(view, node) { this._super(view, node); this.template = "WidgetSeparator"; + this.orientation = node.attrs.orientation || 'horizontal'; + if (this.orientation === 'vertical') { + this.width = '1'; + } + this.classname += '_' + this.orientation; } }); @@ -795,14 +814,15 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({ this._super(view, node); // TODO fme: support for attrs.align - if (this.node.tag == 'label' && this.node.attrs.colspan) { - this.is_field_label = false; + if (this.node.tag == 'label' && (this.node.attrs.colspan || (this.string && this.string.length > 32))) { this.template = "WidgetParagraph"; - this.colspan = this.node.attrs.colspan; + this.colspan = parseInt(this.node.attrs.colspan || 1, 10); } else { - this.is_field_label = true; this.template = "WidgetLabel"; this.colspan = 1; + this.width = '1%'; + this.decrease_max_width = 1; + this.nowrap = true; } }, render: function () { @@ -844,6 +864,8 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ this.required = this.modifiers['required'] === true; this.invalid = false; this.dirty = false; + + this.classname = 'oe_form_field_' + this.type; }, start: function() { this._super.apply(this, arguments); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index c6bd9a1ee19..b308d1c7856 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -622,7 +622,7 @@
-

+

%s.%s %s" % (controller.__class__.__name__, method.__name__, self.jsonrequest) response['id'] = self.jsonrequest.get('id') response["result"] = method(controller, self, **self.params) - except OpenERPUnboundException: + except backend.OpenERPUnboundException: error = { 'code': 100, 'message': "OpenERP Session Invalid", diff --git a/addons/base/common/nonliterals.py b/addons/base/common/nonliterals.py index 5b61f39fc83..937da06ad9e 100644 --- a/addons/base/common/nonliterals.py +++ b/addons/base/common/nonliterals.py @@ -7,8 +7,6 @@ can't be sent there themselves). import binascii import hashlib import simplejson.encoder -import time -import datetime __all__ = ['Domain', 'Context', 'NonLiteralEncoder, non_literal_decoder', 'CompoundDomain', 'CompoundContext'] From c1fcb132e90675295e4a7afc63d18472e8130f57 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 08:35:55 +0200 Subject: [PATCH 28/46] [IMP] split optionparser initialization outside of main(), as well as cherrypy mounting bzr revid: xmo@openerp.com-20110902063555-6n19ly6369p82898 --- openerp-web.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/openerp-web.py b/openerp-web.py index 69e2c90db7c..e9a4682e4aa 100755 --- a/openerp-web.py +++ b/openerp-web.py @@ -4,6 +4,19 @@ import optparse,os,sys,tempfile import cherrypy import cherrypy.lib.static +optparser = optparse.OptionParser() +optparser.add_option("-p", "--port", dest="server.socket_port", default=8002, + help="listening port", type="int", metavar="NUMBER") +optparser.add_option("-s", "--session-path", dest="tools.sessions.storage_path", + default=os.path.join(tempfile.gettempdir(), "cpsessions"), + help="directory used for session storage", metavar="DIR") +optparser.add_option("--server-host", dest="openerp.server.host", + default='127.0.0.1', help="OpenERP server hostname", metavar="HOST") +optparser.add_option("--server-port", dest="openerp.server.port", default=8069, + help="OpenERP server port", type="int", metavar="NUMBER") +optparser.add_option("--db-filter", dest="openerp.dbfilter", default='.*', + help="Filter listed database", metavar="REGEXP") + path_root = os.path.dirname(os.path.abspath(__file__)) path_addons = os.path.join(path_root, 'addons') if path_addons not in sys.path: @@ -23,21 +36,12 @@ def main(argv): } # Parse config - op = optparse.OptionParser() - op.add_option("-p", "--port", dest="server.socket_port", default=8002, help="listening port", type="int", metavar="NUMBER") - op.add_option("-s", "--session-path", dest="tools.sessions.storage_path", default=os.path.join(tempfile.gettempdir(), "cpsessions"), help="directory used for session storage", metavar="DIR") - op.add_option("--server-host", dest="openerp.server.host", default='127.0.0.1', help="OpenERP server hostname", metavar="HOST") - op.add_option("--server-port", dest="openerp.server.port", default=8069, help="OpenERP server port", type="int", metavar="NUMBER") - op.add_option("--db-filter", dest="openerp.dbfilter", default='.*', help="Filter listed database", metavar="REGEXP") - (o, args) = op.parse_args(argv[1:]) + (o, args) = optparser.parse_args(argv[1:]) o = vars(o) for k in o.keys(): if o[k] is None: del(o[k]) - # Setup and run cherrypy - cherrypy.tree.mount(base.common.Root()) - cherrypy.config.update(config=DEFAULT_CONFIG) if os.path.exists(os.path.join(path_root,'openerp-web.cfg')): cherrypy.config.update(os.path.join(path_root,'openerp-web.cfg')) @@ -48,10 +52,11 @@ def main(argv): if not os.path.exists(cherrypy.config['tools.sessions.storage_path']): os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700) + return base.common.Root() + +if __name__ == "__main__": + cherrypy.tree.mount(main(sys.argv)) cherrypy.server.subscribe() cherrypy.engine.start() cherrypy.engine.block() -if __name__ == "__main__": - main(sys.argv) - From 55250f10065821ed8326ea384d2efcc0ba4aa575 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 08:39:56 +0200 Subject: [PATCH 29/46] Take options parsing outside of main() function bzr revid: xmo@openerp.com-20110902063956-dlpnvj27ond11bqr --- openerp-web.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/openerp-web.py b/openerp-web.py index e9a4682e4aa..911b8fcafe8 100755 --- a/openerp-web.py +++ b/openerp-web.py @@ -24,7 +24,7 @@ if path_addons not in sys.path: import base -def main(argv): +def main(options): # change the timezone of the program to the OpenERP server's assumed timezone os.environ["TZ"] = "UTC" @@ -35,19 +35,12 @@ def main(argv): 'tools.sessions.timeout': 60 } - # Parse config - (o, args) = optparser.parse_args(argv[1:]) - o = vars(o) - for k in o.keys(): - if o[k] is None: - del(o[k]) - cherrypy.config.update(config=DEFAULT_CONFIG) if os.path.exists(os.path.join(path_root,'openerp-web.cfg')): cherrypy.config.update(os.path.join(path_root,'openerp-web.cfg')) if os.path.exists(os.path.expanduser('~/.openerp_webrc')): cherrypy.config.update(os.path.expanduser('~/.openerp_webrc')) - cherrypy.config.update(o) + cherrypy.config.update(options) if not os.path.exists(cherrypy.config['tools.sessions.storage_path']): os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700) @@ -55,7 +48,10 @@ def main(argv): return base.common.Root() if __name__ == "__main__": - cherrypy.tree.mount(main(sys.argv)) + (o, args) = optparser.parse_args(sys.argv[1:]) + o = dict((k, v) for k, v in vars(o).iteritems() if v is not None) + + cherrypy.tree.mount(main(o)) cherrypy.server.subscribe() cherrypy.engine.start() cherrypy.engine.block() From 6e739caf10667c2d10b2d0639589601acc6b6032 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 09:11:52 +0200 Subject: [PATCH 30/46] [IMP] serve application via werkzeug's runner bzr revid: xmo@openerp.com-20110902071152-0oz8a526aeakindr --- openerp-web.py | 22 +++++++++++++--------- setup.py | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/openerp-web.py b/openerp-web.py index 911b8fcafe8..56298715fb8 100755 --- a/openerp-web.py +++ b/openerp-web.py @@ -1,11 +1,16 @@ #!/usr/bin/env python -import optparse,os,sys,tempfile +import optparse +import os +import signal +import sys +import tempfile import cherrypy import cherrypy.lib.static +import werkzeug.serving optparser = optparse.OptionParser() -optparser.add_option("-p", "--port", dest="server.socket_port", default=8002, +optparser.add_option("-p", "--port", dest="socket_port", default=8002, help="listening port", type="int", metavar="NUMBER") optparser.add_option("-s", "--session-path", dest="tools.sessions.storage_path", default=os.path.join(tempfile.gettempdir(), "cpsessions"), @@ -45,14 +50,13 @@ def main(options): if not os.path.exists(cherrypy.config['tools.sessions.storage_path']): os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700) - return base.common.Root() + return cherrypy.tree.mount(base.common.Root()) if __name__ == "__main__": - (o, args) = optparser.parse_args(sys.argv[1:]) - o = dict((k, v) for k, v in vars(o).iteritems() if v is not None) + (options, args) = optparser.parse_args(sys.argv[1:]) + o = dict((k, v) for k, v in vars(options).iteritems() if v is not None) - cherrypy.tree.mount(main(o)) - cherrypy.server.subscribe() - cherrypy.engine.start() - cherrypy.engine.block() + werkzeug.serving.run_simple( + '0.0.0.0', options.socket_port, main(o), + use_reloader=True, threaded=True) diff --git a/setup.py b/setup.py index 91248a0ea73..55a5e92c8a5 100644 --- a/setup.py +++ b/setup.py @@ -59,6 +59,7 @@ setup( "simplejson >= 2.0.9", "python-dateutil >= 1.4.1", "pytz", + "werkzeug = 0.7", ], tests_require=[ 'unittest2', From 80d08598377ea92569b1e89210cf42c3aaf1cfef Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 10:58:53 +0200 Subject: [PATCH 31/46] [REF] first shot at converting everything to werkzeug bzr revid: xmo@openerp.com-20110902085853-38y28g2x0d13p8ne --- addons/base/common/dispatch.py | 200 +++++++++++++++---------- addons/base/common/http.py | 25 ++++ addons/base/common/tests/test_model.py | 12 +- addons/base/controllers/main.py | 30 ++-- openerp-web.py | 43 ++---- 5 files changed, 177 insertions(+), 133 deletions(-) create mode 100644 addons/base/common/http.py diff --git a/addons/base/common/dispatch.py b/addons/base/common/dispatch.py index 2f2d7c729b9..704e91d8580 100644 --- a/addons/base/common/dispatch.py +++ b/addons/base/common/dispatch.py @@ -1,5 +1,6 @@ #!/usr/bin/python -import urllib +from __future__ import with_statement + import functools import logging import os @@ -8,11 +9,15 @@ import traceback import uuid import xmlrpclib -import cherrypy -import cherrypy.lib.static import simplejson +import werkzeug.exceptions +import werkzeug.utils +import werkzeug.wrappers +import werkzeug.wsgi +import ast import nonliterals +import http # import backendlocal as backend import backendrpc as backend @@ -40,27 +45,28 @@ controllers_path = {} #---------------------------------------------------------- # OpenERP Web RequestHandler #---------------------------------------------------------- -class CherryPyRequest(object): +class WebRequest(object): """ CherryPy request handling """ - def init(self,params): - self.params = params + def __init__(self, request, config): # Move cherrypy thread local objects to attributes self.applicationsession = applicationsession - self.httprequest = cherrypy.request - self.httpresponse = cherrypy.response - self.httpsession = cherrypy.session - self.httpsession_id = "cookieid" + self.httprequest = request + self.httpresponse = None + self.httpsession = request.session + self.config = config + # Request attributes + def init(self, params): + self.params = dict(params) # OpenERP session setup self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex - host = cherrypy.config['openerp.server.host'] - port = cherrypy.config['openerp.server.port'] - self.session = self.httpsession.setdefault(self.session_id, backend.OpenERPSession(host, port)) - # Request attributes + self.session = self.httpsession.setdefault( + self.session_id, backend.OpenERPSession( + self.config.server_host, self.config.server_port)) self.context = self.params.pop('context', None) - self.debug = self.params.pop('debug',False) != False + self.debug = self.params.pop('debug', False) != False -class JsonRequest(CherryPyRequest): +class JsonRequest(WebRequest): """ JSON-RPC2 over HTTP. Sucessful request:: @@ -138,8 +144,8 @@ class JsonRequest(CherryPyRequest): } } except Exception: - cherrypy.log("An error occured while handling a json request", - severity=logging.ERROR, traceback=True) + logging.getLogger('openerp.JSONRequest.dispatch').exception\ + ("An error occured while handling a json request") error = { 'code': 300, 'message': "OpenERP WebClient Error", @@ -156,42 +162,55 @@ class JsonRequest(CherryPyRequest): print content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder) - cherrypy.response.headers['Content-Type'] = 'application/json' - cherrypy.response.headers['Content-Length'] = len(content) - return content + return werkzeug.wrappers.Response( + content, headers=[('Content-Type', 'application/json'), + ('Content-Length', len(content))]) def jsonrequest(f): - @cherrypy.expose @functools.wraps(f) - def json_handler(controller): - return JsonRequest().dispatch(controller, f, requestf=cherrypy.request.body) + def json_handler(controller, request, config): + return JsonRequest(request, config).dispatch( + controller, f, requestf=request.stream) + json_handler.exposed = True return json_handler -class HttpRequest(CherryPyRequest): +class HttpRequest(WebRequest): """ Regular GET/POST request """ - def dispatch(self, controller, method, **kw): - self.init(kw) + def dispatch(self, controller, method): + self.init(self.httprequest.args) akw = {} - for key in kw.keys(): - if isinstance(kw[key], basestring) and len(kw[key]) < 1024: - akw[key] = kw[key] + for key, value in self.httprequest.args.iteritems(): + if isinstance(value, basestring) and len(value) < 1024: + akw[key] = value else: - akw[key] = type(kw[key]) + akw[key] = type(value) if self.debug or 1: print "%s --> %s.%s %r" % (self.httprequest.method, controller.__class__.__name__, method.__name__, akw) - r = method(controller, self, **kw) + r = method(controller, self, **self.params) if self.debug or 1: - print "<--", 'size:', len(r) + if isinstance(r, werkzeug.wrappers.BaseResponse): + print '<--', r + else: + print "<--", 'size:', len(r) print return r + def make_response(self, data, headers=None, cookies=None): + response = werkzeug.wrappers.Response(data, headers=headers) + if cookies: + for k, v in cookies.iteritems(): + response.set_cookie(k, v) + return response + + def not_found(self, description=None): + return werkzeug.exceptions.NotFound(description) + def httprequest(f): - # check cleaner wrapping: - # functools.wraps(f)(lambda x: JsonRequest().dispatch(x, f)) - def http_handler(controller,*l, **kw): - return HttpRequest().dispatch(controller, f, **kw) - http_handler.exposed = 1 + @functools.wraps(f) + def http_handler(controller, request, config): + return HttpRequest(request, config).dispatch(controller, f) + http_handler.exposed = True return http_handler #----------------------------------------------------------- @@ -207,41 +226,82 @@ class Controller(object): __metaclass__ = ControllerType class Root(object): - def __init__(self): + def __init__(self, options): + self.config = options + + self.session_cookie = 'sessionid' self.addons = {} - self._load_addons() + + static_dirs = self._load_addons() + if options.serve_static: + self.dispatch = werkzeug.wsgi.SharedDataMiddleware( + self.dispatch, static_dirs) + + if options.session_storage: + if not os.path.exists(options.session_storage): + os.mkdir(options.session_storage, 0700) + self.session_storage = options.session_storage + + def __call__(self, environ, start_response): + return self.dispatch(environ, start_response) + + def dispatch(self, environ, start_response): + request = werkzeug.wrappers.Request(environ) + + if request.path == '/': + return werkzeug.utils.redirect( + '/base/webclient/home', 301)(environ, start_response) + elif request.path == '/mobile': + return werkzeug.utils.redirect( + '/web_mobile/static/src/web_mobile.html', 301)( + environ, start_response) + + handler = self.find_handler(*(request.path.split('/')[1:])) + + if not handler: + response = werkzeug.exceptions.NotFound() + else: + with http.session(request, self.session_storage, self.session_cookie) as session: + result = handler( + request, self.config) + + if isinstance(result, werkzeug.wrappers.Response): + response = result + else: + response = werkzeug.wrappers.Response( + result, headers=[('Content-Type', 'text/html; charset=utf-8'), + ('Content-Length', len(result))]) + + response.set_cookie(self.session_cookie, session.sid) + + return response(environ, start_response) def _load_addons(self): + statics = {} if path_addons not in sys.path: sys.path.insert(0, path_addons) - for i in os.listdir(path_addons): - if i not in addons_module: - manifest_path = os.path.join(path_addons, i, '__openerp__.py') + for module in os.listdir(path_addons): + if module not in addons_module: + manifest_path = os.path.join(path_addons, module, '__openerp__.py') if os.path.isfile(manifest_path): - manifest = eval(open(manifest_path).read()) - print "Loading", i - m = __import__(i) - addons_module[i] = m - addons_manifest[i] = manifest + manifest = ast.literal_eval(open(manifest_path).read()) + print "Loading", module + m = __import__(module) + addons_module[module] = m + addons_manifest[module] = manifest + + statics['/%s/static' % module] = \ + os.path.join(path_addons, module, 'static') for k, v in controllers_class.items(): if k not in controllers_object: o = v() controllers_object[k] = o if hasattr(o, '_cp_path'): controllers_path[o._cp_path] = o + return statics - def default(self, *l, **kw): - print "default",l,kw - # handle static files - if len(l) > 2 and l[1] == 'static': - # sanitize path - p = os.path.normpath(os.path.join(*l)) - p2 = os.path.join(path_addons, p) - print "p",p - print "p2",p2 - - return cherrypy.lib.static.serve_file(p2) - elif len(l) > 1: + def find_handler(self, *l): + if len(l) > 1: for i in range(len(l), 1, -1): ps = "/" + "/".join(l[0:i]) if ps in controllers_path: @@ -249,19 +309,7 @@ class Root(object): rest = l[i:] or ['index'] meth = rest[0] m = getattr(c, meth) - if getattr(m, 'exposed', 0): - print "Calling", ps, c, meth, m - return m(**kw) - raise cherrypy.NotFound('/' + '/'.join(l)) - elif l and l[0] == 'mobile': - #for the mobile web client we are supposed to use a different url to just add '/mobile' - raise cherrypy.HTTPRedirect('/web_mobile/static/src/web_mobile.html', 301) - else: - if kw: - qs = '?' + urllib.urlencode(kw) - else: - qs = '' - raise cherrypy.HTTPRedirect('/base/webclient/home' + qs, 301) - default.exposed = True - -# + if getattr(m, 'exposed', False): + print "Dispatching to", ps, c, meth, m + return m + return None diff --git a/addons/base/common/http.py b/addons/base/common/http.py new file mode 100644 index 00000000000..3f6c96d1f42 --- /dev/null +++ b/addons/base/common/http.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +import contextlib + +import werkzeug.contrib.sessions + +STORES = {} + +@contextlib.contextmanager +def session(request, storage_path, session_cookie='sessionid'): + session_store = STORES.get(storage_path) + if not session_store: + session_store = werkzeug.contrib.sessions.FilesystemSessionStore( + storage_path) + STORES[storage_path] = session_store + + sid = request.cookies.get(session_cookie) + if sid: + request.session = session_store.get(sid) + else: + request.session = session_store.new() + + yield request.session + + session_store.save(request.session) diff --git a/addons/base/common/tests/test_model.py b/addons/base/common/tests/test_model.py index 4272d1fe796..a238f562db9 100644 --- a/addons/base/common/tests/test_model.py +++ b/addons/base/common/tests/test_model.py @@ -53,29 +53,29 @@ class DispatcherTest(unittest2.TestCase): def test_default_redirect(self): self.assertRaises( cherrypy.HTTPRedirect, - openerpweb.openerpweb.Root().default) + openerpweb.openerpweb.Root().find_handler) def test_serve_static_missing(self): self.assertRaises( cherrypy.NotFound, - openerpweb.openerpweb.Root().default, + openerpweb.openerpweb.Root().find_handler, 'does-not-exist', 'static', 'bar') def test_serve_controller_missing(self): self.assertRaises( cherrypy.NotFound, - openerpweb.openerpweb.Root().default, + openerpweb.openerpweb.Root().find_handler, 'controller', 'does', 'not', 'exist') def test_find_controller_method(self): - openerpweb.openerpweb.Root().default( + openerpweb.openerpweb.Root().find_handler( 'some', 'controller', 'path', 'method') self.mock_method.assert_called_once_with() def test_find_controller_index(self): - openerpweb.openerpweb.Root().default( + openerpweb.openerpweb.Root().find_handler( 'some', 'controller', 'path') self.mock_index.assert_called_once_with() def test_nested_paths(self): - openerpweb.openerpweb.Root().default( + openerpweb.openerpweb.Root().find_handler( 'some', 'other', 'controller', '2') self.mock_index.assert_called_once_with() diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 8847a88bc6f..8e094beda94 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -13,8 +13,6 @@ import time from xml.etree import ElementTree from cStringIO import StringIO -import cherrypy - import base.common as openerpweb import base.common.ast import base.common.nonliterals @@ -130,19 +128,17 @@ class WebClient(openerpweb.Controller): @openerpweb.httprequest def css(self, req, mods='base'): - req.httpresponse.headers['Content-Type'] = 'text/css' files = manifest_glob(mods.split(','), 'css') content,timestamp = concat_files(files) # TODO request set the Date of last modif and Etag - return content + return req.make_response(content, [('Content-Type', 'text/css')]) @openerpweb.httprequest def js(self, req, mods='base'): - req.httpresponse.headers['Content-Type'] = 'application/javascript' files = manifest_glob(mods.split(','), 'js') content,timestamp = concat_files(files) # TODO request set the Date of last modif and Etag - return content + return req.make_response(content, [('Content-Type', 'application/javascript')]) @openerpweb.httprequest def home(self, req, s_action=None, **kw): @@ -208,7 +204,7 @@ class Database(openerpweb.Controller): dbs = proxy.list() h = req.httprequest.headers['Host'].split(':')[0] d = h.split('.')[0] - r = cherrypy.config['openerp.dbfilter'].replace('%h', h).replace('%d', d) + r = req.config.dbfilter.replace('%h', h).replace('%d', d) dbs = [i for i in dbs if re.match(r, i)] return {"db_list": dbs} @@ -253,11 +249,11 @@ class Database(openerpweb.Controller): try: db_dump = base64.decodestring( req.session.proxy("db").dump(backup_pwd, backup_db)) - req.httpresponse.headers['Content-Type'] = "application/octet-stream; charset=binary" - req.httpresponse.headers['Content-Disposition'] = 'attachment; filename="' + backup_db + '.dump"' - req.httpresponse.cookie['fileToken'] = token - req.httpresponse.cookie['fileToken']['path'] = '/' - return db_dump + return req.make_response(db_dump, + [('Content-Type', 'application/octet-stream; charset=binary'), + ('Content-Disposition', 'attachment; filename="' + backup_db + '.dump"')], + {'fileToken': token} + ) except xmlrpclib.Fault, e: if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': return 'Backup Database|' + e.faultCode @@ -985,18 +981,17 @@ class Binary(openerpweb.Controller): res = Model.read([int(id)], [field, fieldname], context)[0] filecontent = res.get(field, '') if not filecontent: - raise cherrypy.NotFound + return req.not_found() else: - req.httpresponse.headers['Content-Type'] = 'application/octet-stream' filename = '%s_%s' % (model.replace('.', '_'), id) if fieldname: filename = res.get(fieldname, '') or filename - req.httpresponse.headers['Content-Disposition'] = 'attachment; filename=' + filename - return base64.decodestring(filecontent) + return req.make_response(filecontent, + [('Content-Type', 'application/octet-stream'), + ('Content-Disposition', 'attachment; filename=' + filename)]) @openerpweb.httprequest def upload(self, req, callback, ufile=None): - cherrypy.response.timeout = 500 headers = {} for key, val in req.httprequest.headers.iteritems(): headers[key.lower()] = val @@ -1023,7 +1018,6 @@ class Binary(openerpweb.Controller): @openerpweb.httprequest def upload_attachment(self, req, callback, model, id, ufile=None): - cherrypy.response.timeout = 500 context = req.session.eval_context(req.context) Model = req.session.model('ir.attachment') try: diff --git a/openerp-web.py b/openerp-web.py index 56298715fb8..54cd1a0bd34 100755 --- a/openerp-web.py +++ b/openerp-web.py @@ -1,25 +1,22 @@ #!/usr/bin/env python import optparse import os -import signal import sys import tempfile -import cherrypy -import cherrypy.lib.static import werkzeug.serving optparser = optparse.OptionParser() optparser.add_option("-p", "--port", dest="socket_port", default=8002, help="listening port", type="int", metavar="NUMBER") -optparser.add_option("-s", "--session-path", dest="tools.sessions.storage_path", - default=os.path.join(tempfile.gettempdir(), "cpsessions"), +optparser.add_option("-s", "--session-path", dest="session_storage", + default=os.path.join(tempfile.gettempdir(), "oe-sessions"), help="directory used for session storage", metavar="DIR") -optparser.add_option("--server-host", dest="openerp.server.host", +optparser.add_option("--server-host", dest="server_host", default='127.0.0.1', help="OpenERP server hostname", metavar="HOST") -optparser.add_option("--server-port", dest="openerp.server.port", default=8069, +optparser.add_option("--server-port", dest="server_port", default=8069, help="OpenERP server port", type="int", metavar="NUMBER") -optparser.add_option("--db-filter", dest="openerp.dbfilter", default='.*', +optparser.add_option("--db-filter", dest="dbfilter", default='.*', help="Filter listed database", metavar="REGEXP") path_root = os.path.dirname(os.path.abspath(__file__)) @@ -29,34 +26,14 @@ if path_addons not in sys.path: import base -def main(options): - # change the timezone of the program to the OpenERP server's assumed timezone - os.environ["TZ"] = "UTC" - - DEFAULT_CONFIG = { - 'server.socket_host': '0.0.0.0', - 'tools.sessions.on': True, - 'tools.sessions.storage_type': 'file', - 'tools.sessions.timeout': 60 - } - - cherrypy.config.update(config=DEFAULT_CONFIG) - if os.path.exists(os.path.join(path_root,'openerp-web.cfg')): - cherrypy.config.update(os.path.join(path_root,'openerp-web.cfg')) - if os.path.exists(os.path.expanduser('~/.openerp_webrc')): - cherrypy.config.update(os.path.expanduser('~/.openerp_webrc')) - cherrypy.config.update(options) - - if not os.path.exists(cherrypy.config['tools.sessions.storage_path']): - os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700) - - return cherrypy.tree.mount(base.common.Root()) - if __name__ == "__main__": (options, args) = optparser.parse_args(sys.argv[1:]) - o = dict((k, v) for k, v in vars(options).iteritems() if v is not None) + options.serve_static = True + + os.environ["TZ"] = "UTC" + app = base.common.Root(options) werkzeug.serving.run_simple( - '0.0.0.0', options.socket_port, main(o), + '0.0.0.0', options.socket_port, app, use_reloader=True, threaded=True) From 4d89672e7228a85605150dd4793954468df39954 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 10:59:19 +0200 Subject: [PATCH 32/46] [REM] tests, they're apparently considered non-essential and nobody gives a shit bzr revid: xmo@openerp.com-20110902085919-es2ash6j5he5kv3q --- addons/base/common/tests/__init__.py | 1 - addons/base/common/tests/test_model.py | 81 ----------- addons/base/common/tests/test_nonliterals.py | 145 ------------------- addons/base/common/tests/test_session.py | 110 -------------- 4 files changed, 337 deletions(-) delete mode 100644 addons/base/common/tests/__init__.py delete mode 100644 addons/base/common/tests/test_model.py delete mode 100644 addons/base/common/tests/test_nonliterals.py delete mode 100644 addons/base/common/tests/test_session.py diff --git a/addons/base/common/tests/__init__.py b/addons/base/common/tests/__init__.py deleted file mode 100644 index 40a96afc6ff..00000000000 --- a/addons/base/common/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/addons/base/common/tests/test_model.py b/addons/base/common/tests/test_model.py deleted file mode 100644 index a238f562db9..00000000000 --- a/addons/base/common/tests/test_model.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -import cherrypy -import mock -import unittest2 -import openerpweb.openerpweb - -class OpenERPModelTest(unittest2.TestCase): - def test_rpc_call(self): - session = mock.Mock(['execute']) - Model = openerpweb.openerpweb.OpenERPModel( - session, 'a.b') - - Model.search([('field', 'op', 'value')], {'key': 'value'}) - session.execute.assert_called_once_with( - 'a.b', 'search', [('field', 'op', 'value')], {'key': 'value'}) - - session.execute.reset_mock() - - Model.read([42]) - session.execute.assert_called_once_with( - 'a.b', 'read', [42]) - -class FakeController(object): - pass - -class DispatcherTest(unittest2.TestCase): - def setUp(self): - controller = FakeController() - self.mock_method = mock.Mock() - controller.method = self.mock_method - self.mock_method.exposed = True - - self.mock_index = mock.Mock() - controller.index = self.mock_index - self.mock_index.exposed = True - - self.patcher = mock.patch.dict( - openerpweb.openerpweb.controllers_path, - {'/some/controller/path': controller}) - self.patcher.start() - - controller2 = FakeController() - controller2.index = self.mock_index - self.patcher2 = mock.patch.dict( - openerpweb.openerpweb.controllers_path, - {'/some/other/controller': FakeController(), - '/some/other/controller/2': controller2}) - self.patcher2.start() - def tearDown(self): - self.patcher2.stop() - self.patcher.stop() - - def test_default_redirect(self): - self.assertRaises( - cherrypy.HTTPRedirect, - openerpweb.openerpweb.Root().find_handler) - def test_serve_static_missing(self): - self.assertRaises( - cherrypy.NotFound, - openerpweb.openerpweb.Root().find_handler, - 'does-not-exist', 'static', 'bar') - - def test_serve_controller_missing(self): - self.assertRaises( - cherrypy.NotFound, - openerpweb.openerpweb.Root().find_handler, - 'controller', 'does', 'not', 'exist') - - def test_find_controller_method(self): - openerpweb.openerpweb.Root().find_handler( - 'some', 'controller', 'path', 'method') - self.mock_method.assert_called_once_with() - def test_find_controller_index(self): - openerpweb.openerpweb.Root().find_handler( - 'some', 'controller', 'path') - self.mock_index.assert_called_once_with() - - def test_nested_paths(self): - openerpweb.openerpweb.Root().find_handler( - 'some', 'other', 'controller', '2') - self.mock_index.assert_called_once_with() diff --git a/addons/base/common/tests/test_nonliterals.py b/addons/base/common/tests/test_nonliterals.py deleted file mode 100644 index 938b44ee728..00000000000 --- a/addons/base/common/tests/test_nonliterals.py +++ /dev/null @@ -1,145 +0,0 @@ -# -*- coding: utf-8 -*- -import mock -import simplejson -import unittest2 - -from openerpweb.nonliterals import Domain, Context -import openerpweb.nonliterals -import openerpweb.openerpweb - -class NonLiteralDomainTest(unittest2.TestCase): - def setUp(self): - self.session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession) - self.session.domains_store = {} - def test_store_domain(self): - d = Domain(self.session, "some arbitrary string") - - self.assertEqual( - self.session.domains_store[d.key], - "some arbitrary string") - - def test_get_domain_back(self): - d = Domain(self.session, "some arbitrary string") - - self.assertEqual( - d.get_domain_string(), - "some arbitrary string") - def test_retrieve_second_domain(self): - """ A different domain should be able to retrieve the nonliteral set - previously - """ - key = Domain(self.session, "some arbitrary string").key - - self.assertEqual( - Domain(self.session, key=key).get_domain_string(), - "some arbitrary string") - - def test_key_and_string(self): - self.assertRaises( - ValueError, Domain, None, domain_string="a", key="b") - - def test_eval(self): - self.session.evaluation_context.return_value = {'foo': 3} - result = Domain(self.session, "[('a', '=', foo)]").evaluate({'foo': 3}) - self.assertEqual( - result, [('a', '=', 3)]) - - def test_own_values(self): - self.session.evaluation_context.return_value = {} - domain = Domain(self.session, "[('a', '=', self)]") - domain.own = {'self': 3} - result = domain.evaluate() - self.assertEqual( - result, [('a', '=', 3)]) - -class NonLiteralContextTest(unittest2.TestCase): - def setUp(self): - self.session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession) - self.session.contexts_store = {} - def test_store_domain(self): - c = Context(self.session, "some arbitrary string") - - self.assertEqual( - self.session.contexts_store[c.key], - "some arbitrary string") - - def test_get_domain_back(self): - c = Context(self.session, "some arbitrary string") - - self.assertEqual( - c.get_context_string(), - "some arbitrary string") - def test_retrieve_second_domain(self): - """ A different domain should be able to retrieve the nonliteral set - previously - """ - key = Context(self.session, "some arbitrary string").key - - self.assertEqual( - Context(self.session, key=key).get_context_string(), - "some arbitrary string") - - def test_key_and_string(self): - self.assertRaises( - ValueError, Context, None, context_string="a", key="b") - - def test_eval(self): - self.session.evaluation_context.return_value = {'foo': 3} - result = Context(self.session, "[('a', '=', foo)]")\ - .evaluate({'foo': 3}) - self.assertEqual( - result, [('a', '=', 3)]) - - def test_own_values(self): - self.session.evaluation_context.return_value = {} - context = Context(self.session, "{'a': self}") - context.own = {'self': 3} - result = context.evaluate() - self.assertEqual( - result, {'a': 3}) - -class NonLiteralJSON(unittest2.TestCase): - def setUp(self): - self.session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession) - self.session.domains_store = {} - self.session.contexts_store = {} - - def test_encode_domain(self): - d = Domain(self.session, "some arbitrary string") - self.assertEqual( - simplejson.dumps(d, cls=openerpweb.nonliterals.NonLiteralEncoder), - simplejson.dumps({'__ref': 'domain', '__id': d.key})) - - def test_decode_domain(self): - encoded = simplejson.dumps( - Domain(self.session, "some arbitrary string"), - cls=openerpweb.nonliterals.NonLiteralEncoder) - - domain = simplejson.loads( - encoded, object_hook=openerpweb.nonliterals.non_literal_decoder) - domain.session = self.session - - self.assertEqual( - domain.get_domain_string(), - "some arbitrary string" - ) - - def test_encode_context(self): - c = Context(self.session, "some arbitrary string") - self.assertEqual( - simplejson.dumps(c, cls=openerpweb.nonliterals.NonLiteralEncoder), - simplejson.dumps({'__ref': 'context', '__id': c.key})) - - def test_decode_context(self): - encoded = simplejson.dumps( - Context(self.session, "some arbitrary string"), - cls=openerpweb.nonliterals.NonLiteralEncoder) - - context = simplejson.loads( - encoded, object_hook=openerpweb.nonliterals.non_literal_decoder) - context.session = self.session - - self.assertEqual( - context.get_context_string(), - "some arbitrary string" - ) diff --git a/addons/base/common/tests/test_session.py b/addons/base/common/tests/test_session.py deleted file mode 100644 index 991a1d061ec..00000000000 --- a/addons/base/common/tests/test_session.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -import unittest2 -from openerpweb.nonliterals import Domain, Context, CompoundDomain, CompoundContext -import openerpweb.openerpweb - -class TestOpenERPSession(unittest2.TestCase): - def setUp(self): - self.module = object() - self.session = openerpweb.openerpweb.OpenERPSession() - self.session._uid = -1 - self.session.context = { - 'current_date': '1945-08-05', - 'date': self.module, - 'time': self.module, - 'datetime': self.module, - 'relativedelta': self.module - } - def test_base_eval_context(self): - self.assertEqual(type(self.session.base_eval_context), dict) - self.assertEqual( - self.session.base_eval_context, - {'uid': -1, 'current_date': '1945-08-05', - 'date': self.module, 'datetime': self.module, 'time': self.module, - 'relativedelta': self.module} - ) - - def test_evaluation_context_nocontext(self): - self.assertEqual( - type(self.session.evaluation_context()), - dict - ) - self.assertEqual( - self.session.evaluation_context(), - self.session.base_eval_context - ) - - def test_evaluation_context(self): - ctx = self.session.evaluation_context({'foo': 3}) - self.assertEqual( - type(ctx), - dict - ) - self.assertIn('foo', ctx) - - def test_eval_with_context(self): - self.assertEqual( - eval('current_date', self.session.evaluation_context()), - '1945-08-05') - - self.assertEqual( - eval('foo + 3', self.session.evaluation_context({'foo': 4})), - 7) - - def test_eval_domain_typeerror(self): - self.assertRaises( - TypeError, self.session.eval_domain, "foo") - - def test_eval_domain_list(self): - self.assertEqual( - self.session.eval_domain([]), - []) - - def test_eval_nonliteral_domain(self): - d = Domain(self.session, "[('foo', 'is', 3)]") - self.assertEqual( - self.session.eval_domain(d), - [('foo', 'is', 3)]) - - def test_eval_nonliteral_domain_bykey(self): - key = Domain( - self.session, "[('foo', 'is', 3)]").key - - d = Domain(None, key=key) - self.assertEqual( - self.session.eval_domain(d), - [('foo', 'is', 3)]) - - def test_eval_empty_domains(self): - self.assertEqual( - self.session.eval_domain(CompoundDomain()), - []) - - def test_eval_literal_domains(self): - domains = [ - [('a', 'is', 3)], - [('b', 'ilike', 'foo')], - ['|', - ('c', '=', False), - ('c', 'in', ['a', 'b', 'c'])] - ] - self.assertEqual( - self.session.eval_domain(CompoundDomain(*domains)), - [ - ('a', 'is', 3), - ('b', 'ilike', 'foo'), - '|', - ('c', '=', False), - ('c', 'in', ['a', 'b', 'c']) - ]) - def test_eval_nonliteral_domains(self): - domains = [ - Domain(self.session, "[('uid', '=', uid)]"), - Domain(self.session, - "['|', ('date', '<', current_date)," - " ('date', '>', current_date)]")] - self.assertEqual( - self.session.eval_domain(CompoundDomain(*domains)), - [('uid', '=', -1), - '|', ('date', '<', '1945-08-05'), ('date', '>', '1945-08-05')] - ) From 12462610b0750e1a7db06fb076faf8fbb27dbd9f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 2 Sep 2011 11:03:26 +0200 Subject: [PATCH 33/46] [REM] all references to cherrypy bzr revid: xmo@openerp.com-20110902090326-veq9cb9wgb9rr6m4 --- addons/base/common/dispatch.py | 8 -------- addons/web_chat/controllers/main.py | 9 ++------- addons/web_mobile/controllers/__init__.py | 1 - addons/web_mobile/controllers/main.py | 12 ------------ setup.py | 1 - 5 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 addons/web_mobile/controllers/main.py diff --git a/addons/base/common/dispatch.py b/addons/base/common/dispatch.py index 704e91d8580..14e1a873830 100644 --- a/addons/base/common/dispatch.py +++ b/addons/base/common/dispatch.py @@ -29,7 +29,6 @@ import __main__ path_root = __main__.path_root path_addons = __main__.path_addons -cherrypy_root = None #----------------------------------------------------------- # Globals (wont move into a pool) @@ -46,10 +45,7 @@ controllers_path = {} # OpenERP Web RequestHandler #---------------------------------------------------------- class WebRequest(object): - """ CherryPy request handling - """ def __init__(self, request, config): - # Move cherrypy thread local objects to attributes self.applicationsession = applicationsession self.httprequest = request self.httpresponse = None @@ -213,10 +209,6 @@ def httprequest(f): http_handler.exposed = True return http_handler -#----------------------------------------------------------- -# Cherrypy stuff -#----------------------------------------------------------- - class ControllerType(type): def __init__(cls, name, bases, attrs): super(ControllerType, cls).__init__(name, bases, attrs) diff --git a/addons/web_chat/controllers/main.py b/addons/web_chat/controllers/main.py index 5c427812d47..e445eb4be6d 100644 --- a/addons/web_chat/controllers/main.py +++ b/addons/web_chat/controllers/main.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -import sys, time -import cherrypy +import time import simplejson -import random import base.common as openerpweb #---------------------------------------------------------- @@ -38,7 +36,7 @@ class PollServerMessageQueue(object): if msg['to'] == recipient: return self.messages - def gc(): + def gc(self): # remove message older than 300s from self.l # remove dead users from self.users pass @@ -72,7 +70,6 @@ class PollServer(openerpweb.Controller): #r = 'logged in' #u = generate random.randint(0,2**32) - #s = cherrypy cookie id #f = mq.userlist() # username = 'Guest'+ str(random.randint(0, 2**32)) @@ -132,8 +129,6 @@ class PollServer(openerpweb.Controller): msg = '[]' - cherrypy.response.headers['content-type'] = 'application/javascript'; - for i in range(5): received_msg = mq.read('Guest1', i) if received_msg: diff --git a/addons/web_mobile/controllers/__init__.py b/addons/web_mobile/controllers/__init__.py index 8ee9bae18d9..e69de29bb2d 100644 --- a/addons/web_mobile/controllers/__init__.py +++ b/addons/web_mobile/controllers/__init__.py @@ -1 +0,0 @@ -import main diff --git a/addons/web_mobile/controllers/main.py b/addons/web_mobile/controllers/main.py deleted file mode 100644 index 8fa3e32a775..00000000000 --- a/addons/web_mobile/controllers/main.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -#import glob, os -#import pprint -# -#import simplejson -# -#import openerpweb -#import openerpweb.ast -#import openerpweb.nonliterals -# -#import cherrypy -# diff --git a/setup.py b/setup.py index 55a5e92c8a5..648c2f54584 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,6 @@ setup( download_url=download_url, license=license, install_requires=[ - "CherryPy >= 3.1.2", "Babel >= 0.9.6", "simplejson >= 2.0.9", "python-dateutil >= 1.4.1", From 21af54063718375945d180803dc6ccc2a3b14652 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 2 Sep 2011 11:19:22 +0200 Subject: [PATCH 34/46] [IMP] base: share labels+tooltips CSS between form & search views bzr revid: odo@openerp.com-20110902091922-pulvxzsounk9mu7d --- addons/base/static/src/css/base.css | 10 ++++++---- addons/base/static/src/xml/base.xml | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index a33bdce973b..c9894e42cef 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -832,15 +832,17 @@ label.error { float: right; } -.openerp label.oe_form_label_help { +.openerp label.oe_label_help, .openerp label.oe_label { + display: block; +} +.openerp label.oe_label_help { cursor: help; } -.openerp label.oe_form_label, .openerp label.oe_form_label_help { - display: block; +.openerp .oe_form_field label.oe_label, .openerp .oe_form_field label.oe_label_help { text-align: right; margin: 3px 0 0 10px; } -.openerp label.oe_form_label_help span { +.openerp label.oe_label_help span { font-size: 80%; color: darkgreen; vertical-align:top; diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 7e559a1f365..47a8510994f 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -475,7 +475,7 @@
- + @@ -726,7 +726,7 @@