diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index cfe8850ab03..f9786e9867f 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -4,7 +4,11 @@ "depends" : [], 'active': True, 'js' : [ - "static/lib/datejs/date-en-US.js", + "static/lib/datejs/globalization/en-US.js", + "static/lib/datejs/core.js", + "static/lib/datejs/parser.js", + "static/lib/datejs/sugarpak.js", + "static/lib/datejs/extras.js", "static/lib/jquery/jquery-1.6.2.js", "static/lib/jquery.form/jquery.form.js", "static/lib/jquery.validate/jquery.validate.js", @@ -20,8 +24,10 @@ "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", "static/lib/underscore/underscore.string.js", + "static/lib/labjs/LAB.src.js", "static/src/js/boot.js", "static/src/js/core.js", + "static/src/js/dates.js", "static/src/js/formats.js", "static/src/js/chrome.js", "static/src/js/views.js", diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 65e061211f8..d5eaafaecee 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -67,7 +67,6 @@ def manifest_glob(addons, key): files = [] for addon in addons: globlist = openerpweb.addons_manifest.get(addon, {}).get(key, []) - print globlist for pattern in globlist: for path in glob.glob(os.path.join(openerpweb.path_addons, addon, pattern)): files.append(path[len(openerpweb.path_addons):]) @@ -79,35 +78,36 @@ def concat_files(file_list): concat: concatenation of file content timestamp: max(os.path.getmtime of file_list) """ - root = openerpweb.path_root files_content = [] files_timestamp = 0 for i in file_list: - fname = os.path.join(root, i) + fname = os.path.join(openerpweb.path_addons, i[1:]) ftime = os.path.getmtime(fname) if ftime > files_timestamp: files_timestamp = ftime - files_content = open(fname).read() + files_content.append(open(fname).read()) files_concat = "".join(files_content) - return files_concat + return (files_concat,files_timestamp) home_template = textwrap.dedent(""" OpenERP - %(javascript)s - %(css)s + %(javascript)s + @@ -125,31 +125,31 @@ class WebClient(openerpweb.Controller): @openerpweb.httprequest def css(self, req, mods='base'): - cherrypy.response.headers['Content-Type'] = 'text/css' + req.httpresponse.headers['Content-Type'] = 'text/css' files = manifest_glob(mods.split(','), 'css') - concat = concat_files(files)[0] + content,timestamp = concat_files(files) # TODO request set the Date of last modif and Etag - return concat + return content @openerpweb.httprequest def js(self, req, mods='base'): - cherrypy.response.headers['Content-Type'] = 'application/javascript' + req.httpresponse.headers['Content-Type'] = 'application/javascript' files = manifest_glob(mods.split(','), 'js') - concat = concat_files(files)[0] + content,timestamp = concat_files(files) # TODO request set the Date of last modif and Etag - return concat + return content @openerpweb.httprequest def home(self, req, s_action=None): # script tags jslist = ['/base/webclient/js'] - if 1: # debug == 1 + if req.debug: jslist = manifest_glob(['base'], 'js') js = "\n ".join([''%i for i in jslist]) # css tags csslist = ['/base/webclient/css'] - if 1: # debug == 1 + if req.debug: csslist = manifest_glob(['base'], 'css') css = "\n ".join([''%i for i in csslist]) r = home_template % { @@ -157,7 +157,7 @@ class WebClient(openerpweb.Controller): 'css': css } return r - + @openerpweb.jsonrequest def translations(self, req, mods, lang): lang_model = req.session.model('res.lang') @@ -193,7 +193,6 @@ class WebClient(openerpweb.Controller): transl["messages"].append({'id': x.id, 'string': x.string}) return {"modules": transs, "lang_parameters": lang_obj} - class Database(openerpweb.Controller): _cp_path = "/base/database" @@ -249,16 +248,16 @@ class Database(openerpweb.Controller): try: db_dump = base64.decodestring( req.session.proxy("db").dump(backup_pwd, backup_db)) - cherrypy.response.headers['Content-Type'] = "application/octet-stream; charset=binary" - cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="' + backup_db + '.dump"' - cherrypy.response.cookie['fileToken'] = token - cherrypy.response.cookie['fileToken']['path'] = '/' + 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 except xmlrpclib.Fault, e: if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': return 'Backup Database|' + e.faultCode return 'Backup Database|Could not generate database backup' - + @openerpweb.httprequest def restore(self, req, db_file, restore_pwd, new_db): try: @@ -267,9 +266,7 @@ class Database(openerpweb.Controller): return '' except xmlrpclib.Fault, e: if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': - raise cherrypy.HTTPError(403) - - raise cherrypy.HTTPError() + raise Exception("AccessDenied") @openerpweb.jsonrequest def change_password(self, req, fields): @@ -386,10 +383,10 @@ class Session(openerpweb.Controller): :return: A key identifying the saved action. :rtype: integer """ - saved_actions = cherrypy.session.get('saved_actions') + saved_actions = req.httpsession.get('saved_actions') if not saved_actions: saved_actions = {"next":0, "actions":{}} - cherrypy.session['saved_actions'] = saved_actions + req.httpsession['saved_actions'] = saved_actions # we don't allow more than 10 stored actions if len(saved_actions["actions"]) >= 10: del saved_actions["actions"][min(saved_actions["actions"].keys())] @@ -409,7 +406,7 @@ class Session(openerpweb.Controller): :return: The saved action or None. :rtype: anything """ - saved_actions = cherrypy.session.get('saved_actions') + saved_actions = req.httpsession.get('saved_actions') if not saved_actions: return None return saved_actions["actions"].get(key) @@ -439,18 +436,16 @@ def clean_action(action, session, context=None): return action # values come from the server, we can just eval them if isinstance(action.get('context'), basestring): - action['context'] = eval( - action['context'], - session.evaluation_context(context=context)) or {} + localvars = session.evaluation_context(context=context) + action['context'] = eval( action['context'], localvars ) or {} if isinstance(action.get('domain'), basestring): - action['domain'] = eval( - action['domain'], - session.evaluation_context( - action.get('context', {}))) or [] + localvars = session.evaluation_context( action.get('context', {})) + action['domain'] = eval( action['domain'], localvars ) or [] return fix_view_modes(action) +# I think generate_views,fix_view_modes should go into js ActionManager def generate_views(action): """ While the server generates a sequence called "views" computing dependencies @@ -947,10 +942,10 @@ class Binary(openerpweb.Controller): _cp_path = "/base/binary" @openerpweb.httprequest - def image(self, request, model, id, field, **kw): - cherrypy.response.headers['Content-Type'] = 'image/png' - Model = request.session.model(model) - context = request.session.eval_context(request.context) + def image(self, req, model, id, field, **kw): + req.httpresponse.headers['Content-Type'] = 'image/png' + Model = req.session.model(model) + context = req.session.eval_context(req.context) try: if not id: res = Model.default_get([field], context).get(field, '') @@ -963,26 +958,26 @@ class Binary(openerpweb.Controller): return open(os.path.join(openerpweb.path_addons, 'base', 'static', 'src', 'img', 'placeholder.png'), 'rb').read() @openerpweb.httprequest - def saveas(self, request, model, id, field, fieldname, **kw): - Model = request.session.model(model) - context = request.session.eval_context(request.context) + def saveas(self, req, model, id, field, fieldname, **kw): + Model = req.session.model(model) + context = req.session.eval_context(req.context) res = Model.read([int(id)], [field, fieldname], context)[0] filecontent = res.get(field, '') if not filecontent: raise cherrypy.NotFound else: - cherrypy.response.headers['Content-Type'] = 'application/octet-stream' + req.httpresponse.headers['Content-Type'] = 'application/octet-stream' filename = '%s_%s' % (model.replace('.', '_'), id) if fieldname: filename = res.get(fieldname, '') or filename - cherrypy.response.headers['Content-Disposition'] = 'attachment; filename=' + filename + req.httpresponse.headers['Content-Disposition'] = 'attachment; filename=' + filename return base64.decodestring(filecontent) @openerpweb.httprequest - def upload(self, request, callback, ufile=None): + def upload(self, req, callback, ufile=None): cherrypy.response.timeout = 500 headers = {} - for key, val in cherrypy.request.headers.iteritems(): + for key, val in req.httprequest.headers.iteritems(): headers[key.lower()] = val size = int(headers.get('content-length', 0)) # TODO: might be useful to have a configuration flag for max-length file uploads @@ -1006,10 +1001,10 @@ class Binary(openerpweb.Controller): return out % (simplejson.dumps(callback), simplejson.dumps(args)) @openerpweb.httprequest - def upload_attachment(self, request, callback, model, id, ufile=None): + def upload_attachment(self, req, callback, model, id, ufile=None): cherrypy.response.timeout = 500 - context = request.session.eval_context(request.context) - Model = request.session.model('ir.attachment') + context = req.session.eval_context(req.context) + Model = req.session.model('ir.attachment') try: out = """ + + + + @@ -40,4 +44,6 @@ + + diff --git a/addons/base_calendar/static/src/js/calendar.js b/addons/base_calendar/static/src/js/calendar.js index 22088493e16..19818b66380 100644 --- a/addons/base_calendar/static/src/js/calendar.js +++ b/addons/base_calendar/static/src/js/calendar.js @@ -26,6 +26,7 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900' ]; + this.color_map = {}; }, start: function() { this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id, 'toolbar': true}, this.on_loaded); @@ -136,17 +137,21 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ reload_event: function(id) { this.dataset.read_ids([id], _.keys(this.fields), this.on_events_loaded); }, - get_color: function(index) { - index = index % this.COLOR_PALETTE.length; - return this.COLOR_PALETTE[index]; + get_color: function(key) { + if (this.color_map[key]) { + return this.color_map[key]; + } + var index = _.keys(this.color_map).length % this.COLOR_PALETTE.length; + var color = this.COLOR_PALETTE[index]; + this.color_map[key] = color; + return color; }, on_events_loaded: function(events, fn_filter, no_filter_reload) { var self = this; //To parse Events we have to convert date Format var res_events = [], - sidebar_items = [], - sidebar_ids = []; + sidebar_items = {}; for (var e = 0; e < events.length; e++) { var evt = events[e]; if (!evt[this.date_start]) { @@ -157,30 +162,28 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ if (this.color_field) { var filter = evt[this.color_field]; if (filter) { - var filter_item = { - value: (typeof filter === 'object') ? filter[0] : filter, - label: (typeof filter === 'object') ? filter[1] : filter - } - if (typeof(fn_filter) === 'function' && !fn_filter(filter_item.value)) { + var filter_value = (typeof filter === 'object') ? filter[0] : filter; + if (typeof(fn_filter) === 'function' && !fn_filter(filter_value)) { continue; } - var filter_index = _.indexOf(sidebar_ids, filter_item.value); - if (filter_index === -1) { - evt.color = filter_item.color = this.get_color(sidebar_ids.length); - sidebar_items.push(filter_item); - sidebar_ids.push(filter_item.value); - } else { - evt.color = this.get_color(filter_index); + var filter_item = { + value: filter_value, + label: (typeof filter === 'object') ? filter[1] : filter, + color: this.get_color(filter_value) } + if (!sidebar_items[filter_value]) { + sidebar_items[filter_value] = filter_item; + } + evt.color = filter_item.color; evt.textColor = '#ffffff'; } } if (this.fields[this.date_start]['type'] == 'date') { - evt[this.date_start] = openerp.base.parse_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss'); + evt[this.date_start] = openerp.base.str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss'); } if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') { - evt[this.date_stop] = openerp.base.parse_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss'); + evt[this.date_stop] = openerp.base.str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss'); } res_events.push(this.convert_event(evt)); } @@ -192,8 +195,8 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ } }, convert_event: function(evt) { - var date_start = openerp.base.parse_datetime(evt[this.date_start]), - date_stop = this.date_stop ? openerp.base.parse_datetime(evt[this.date_stop]) : null, + var date_start = openerp.base.str_to_datetime(evt[this.date_start]), + date_stop = this.date_stop ? openerp.base.str_to_datetime(evt[this.date_stop]) : null, date_delay = evt[this.date_delay] || null, res_text = '', res_description = []; @@ -277,9 +280,9 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ var data = { name: event_obj.text }; - data[this.date_start] = openerp.base.format_datetime(event_obj.start_date); + data[this.date_start] = openerp.base.datetime_to_str(event_obj.start_date); if (this.date_stop) { - data[this.date_stop] = openerp.base.format_datetime(event_obj.end_date); + data[this.date_stop] = openerp.base.datetime_to_str(event_obj.end_date); } if (this.date_delay) { var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000); @@ -299,8 +302,7 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ // TODO: handle non-empty results.group_by with read_group self.dataset.context = self.context = results.context; self.dataset.domain = self.domain = results.domain; - self.dataset.read_slice({ - fields: _.keys(self.fields), + self.dataset.read_slice(_.keys(self.fields), { offset:0, limit: self.limit }, function(events) { diff --git a/addons/base_calendar/static/src/xml/base_calendar.xml b/addons/base_calendar/static/src/xml/base_calendar.xml index 299f3ba0f3c..c9e5f628b94 100644 --- a/addons/base_calendar/static/src/xml/base_calendar.xml +++ b/addons/base_calendar/static/src/xml/base_calendar.xml @@ -18,9 +18,9 @@ -
- - +
+ +
diff --git a/addons/base_dashboard/static/src/js/dashboard.js b/addons/base_dashboard/static/src/js/dashboard.js index 422d279a485..8cf94a43362 100644 --- a/addons/base_dashboard/static/src/js/dashboard.js +++ b/addons/base_dashboard/static/src/js/dashboard.js @@ -7,6 +7,7 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ this._super(view, node); this.template = 'DashBoard'; this.actions_attrs = {}; + this.action_managers = []; }, start: function() { var self = this; @@ -23,8 +24,9 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ this.$element.find('.oe-dashboard-link-reset').click(this.on_reset); this.$element.find('.oe-dashboard-link-add_widget').click(this.on_add_widget); this.$element.find('.oe-dashboard-link-change_layout').click(this.on_change_layout); - this.$element.find('.oe-dashboard-column .oe-dashboard-fold').click(this.on_fold_action); - this.$element.find('.oe-dashboard-column .ui-icon-closethick').click(this.on_close_action); + + this.$element.delegate('.oe-dashboard-column .oe-dashboard-fold', 'click', this.on_fold_action); + this.$element.delegate('.oe-dashboard-column .ui-icon-closethick', 'click', this.on_close_action); this.actions_attrs = {}; // Init actions @@ -58,7 +60,23 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ }, on_add_widget: function() { var self = this; - var action = { + var action_manager = new openerp.base.ActionManager(this); + var dialog = new openerp.base.Dialog(this, { + title : 'Actions', + width: 800, + height: 600, + buttons : { + Cancel : function() { + $(this).dialog('destroy'); + }, + Add : function() { + self.do_add_widget(action_manager.inner_viewmanager.views.list.controller); + $(this).dialog('destroy'); + } + } + }).start().open(); + action_manager.appendTo(dialog.$element); + action_manager.do_action({ res_model : 'ir.actions.actions', views : [[false, 'list']], type : 'ir.actions.act_window', @@ -69,33 +87,12 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ views_switcher : false, action_buttons : false } - }; - // TODO: create a Dialog controller which optionally takes an action - // Should set width & height automatically and take buttons & views callback - var dialog_id = _.uniqueId('act_window_dialog'); - var action_manager = new openerp.base.ActionManager(this, dialog_id); - $('
').dialog({ - modal : true, - title : 'Actions', - width : 800, - height : 600, - buttons : { - Cancel : function() { - $(this).dialog('destroy'); - }, - Add : function() { - self.do_add_widget(action_manager); - $(this).dialog('destroy'); - } - } }); - action_manager.start(); - action_manager.do_action(action); // TODO: should bind ListView#select_record in order to catch record clicking }, - do_add_widget : function(action_manager) { + do_add_widget : function(listview) { var self = this, - actions = action_manager.viewmanager.views.list.controller.groups.get_selection().ids, + actions = listview.groups.get_selection().ids, results = [], qdict = { view : this.view }; // TODO: should load multiple actions at once @@ -229,6 +226,7 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ pager: false }; var am = new openerp.base.ActionManager(this); + this.action_managers.push(am); am.appendTo($("#"+this.view.element_id + '_action_' + action.id)); am.do_action(action); }, @@ -244,8 +242,12 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ return QWeb.render(this.template, this); }, do_reload: function() { - this.view.widget_parent.stop(); - this.view.widget_parent.start(); + _.each(this.action_managers, function(am) { + am.stop(); + }); + this.action_managers = []; + this.view.stop(); + this.view.start(); } }); openerp.base.form.DashBoardLegacy = openerp.base.form.DashBoard.extend({ @@ -283,6 +285,7 @@ if (!openerp.base_dashboard) { openerp.base_dashboard = {}; } openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ + template: 'ConfigOverview', init: function (parent, element_id) { this._super(parent, element_id); this.dataset = new openerp.base.DataSetSearch( @@ -290,7 +293,7 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ this.dataset.domain = [['type', '=', 'manual']]; }, start: function () { - $.when(this.dataset.read_slice({fields: ['state', 'action_id', 'category_id']}), + $.when(this.dataset.read_slice(['state', 'action_id', 'category_id']), this.dataset.call('progress')) .then(this.on_records_loaded); }, @@ -310,7 +313,7 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ }) .groupBy(function (record) {return record.category}) .value(); - this.$element.html(QWeb.render('ConfigOverview', { + this.$element.html(QWeb.render('ConfigOverview.content', { completion: 100 * progress.done / progress.total, groups: grouped_todos })); @@ -345,16 +348,12 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ openerp.base.client_actions.add( 'board.home.applications', 'openerp.base_dashboard.ApplicationTiles'); openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({ - init: function (parent, element_id) { - this._super(parent, element_id); - this.dataset = new openerp.base.DataSetSearch( - this, 'ir.ui.menu', null, [['parent_id', '=', false]]); - }, + template: 'ApplicationTiles', start: function () { var self = this; - this.dataset.read_slice( - {fields: ['name', 'web_icon_data', 'web_icon_hover_data']}, - function (applications) { + return new openerp.base.DataSetSearch( + this, 'ir.ui.menu', null, [['parent_id', '=', false]]) + .read_slice( ['name', 'web_icon_data', 'web_icon_hover_data'], {}, function (applications) { // Create a matrix of 3*x applications var rows = []; while (applications.length) { @@ -362,31 +361,26 @@ openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({ } self.$element .append(QWeb.render( - 'ApplicationTiles', {rows: rows})) + 'ApplicationTiles.content', {rows: rows})) .find('.oe-dashboard-home-tile') .click(function () { var $this = $(this); $this.closest('.openerp') .find('.menu a[data-menu=' + $this.data('menuid') + ']') .click();}); - }); + }); } }); openerp.base.client_actions.add( 'board.home.widgets', 'openerp.base_dashboard.Widgets'); openerp.base_dashboard.Widgets = openerp.base.View.extend({ - init: function (parent, element_id) { - this._super(parent, element_id); - this.user_widgets = new openerp.base.DataSetSearch( + template: 'HomeWidgets', + start: function () { + return new openerp.base.DataSetSearch( this, 'res.widget.user', null, ['|', ['user_id', '=', false], - ['user_id', '=', parseInt(this.session.uid, 10)]]); - this.widgets = new openerp.base.DataSetSearch(this, 'res.widget'); - }, - start: function () { - this.user_widgets.read_slice( - {fields: ['widget_id', 'user_id']}, - this.on_widgets_list_loaded); + ['user_id', '=', parseInt(this.session.uid, 10)]]) + .read_slice(['widget_id', 'user_id'], {}, this.on_widgets_list_loaded); }, on_widgets_list_loaded: function (user_widgets) { var self = this; @@ -398,18 +392,19 @@ openerp.base_dashboard.Widgets = openerp.base.View.extend({ user_widget_id: widget['id'] }; }); - this.widgets.read_ids(_(user_widgets).pluck('widget_id'), ['title'], function (widgets) { - _(widgets).each(function (widget) { - _.extend(widget, widget_user[widget['id']]); + new openerp.base.DataSetSearch(this, 'res.widget') + .read_ids(_(user_widgets).pluck('widget_id'), ['title'], function (widgets) { + _(widgets).each(function (widget) { + _.extend(widget, widget_user[widget['id']]); + }); + var url = _.sprintf( + '/base_dashboard/widgets/content?session_id=%s&widget_id=', + self.session.session_id); + self.$element.html(QWeb.render('HomeWidgets', { + widgets: widgets, + url: url + })); }); - var url = _.sprintf( - '/base_dashboard/widgets/content?session_id=%s&widget_id=', - self.session.session_id); - self.$element.html(QWeb.render('HomeWidgets', { - widgets: widgets, - url: url - })); - }) } }); }; diff --git a/addons/base_dashboard/static/src/xml/base_dashboard.xml b/addons/base_dashboard/static/src/xml/base_dashboard.xml index ad432348c38..51657dfd068 100644 --- a/addons/base_dashboard/static/src/xml/base_dashboard.xml +++ b/addons/base_dashboard/static/src/xml/base_dashboard.xml @@ -64,7 +64,8 @@ -
+
+
progress: % @@ -88,30 +89,28 @@ -
-
- - -
-
-
- -
- + +
+ + + - -
+
+
+
-
-
-
-
-

-