[FIX] if an action descriptor is precisely ``false``, treat it as an ir_action_close...
[odoo/odoo.git] / addons / web / static / src / js / views.js
index 47a06e3..7b58818 100644 (file)
@@ -24,11 +24,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
     },
     dialog_stop: function () {
         if (this.dialog) {
-            this.dialog_widget.destroy();
-            this.dialog_widget = null;
             this.dialog.destroy();
-            this.dialog = null;
         }
+        this.dialog = null;
     },
     /**
      * Add a new item to the breadcrumb
@@ -113,6 +111,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
         var item = this.breadcrumbs[index];
         item.show(subindex);
         this.inner_widget = item.widget;
+        this.inner_action = item.action;
         return true;
     },
     clear_breadcrumbs: function() {
@@ -131,6 +130,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
             if (!dups.length) {
                 if (this.getParent().has_uncommitted_changes()) {
                     this.inner_widget = item.widget;
+                    this.inner_action = item.action;
                     this.breadcrumbs.splice(index, 0, item);
                     return false;
                 } else {
@@ -139,7 +139,10 @@ instance.web.ActionManager = instance.web.Widget.extend({
             }
         }
         var last_widget = this.breadcrumbs.slice(-1)[0];
-        this.inner_widget =  last_widget && last_widget.widget;
+        if (last_widget) {
+            this.inner_widget = last_widget.widget;
+            this.inner_action = last_widget.action;
+        }
     },
     get_title: function() {
         var titles = [];
@@ -164,15 +167,28 @@ instance.web.ActionManager = instance.web.Widget.extend({
         state = state || {};
         if (this.getParent() && this.getParent().do_push_state) {
             if (this.inner_action) {
+                if (this.inner_action._push_me === false) {
+                    // this action has been explicitly marked as not pushable
+                    return;
+                }
                 state['title'] = this.inner_action.name;
                 if(this.inner_action.type == 'ir.actions.act_window') {
                     state['model'] = this.inner_action.res_model;
                 }
+                if (this.inner_action.menu_id) {
+                    state['menu_id'] = this.inner_action.menu_id;
+                }
                 if (this.inner_action.id) {
                     state['action'] = this.inner_action.id;
                 } else if (this.inner_action.type == 'ir.actions.client') {
                     state['action'] = this.inner_action.tag;
-                    //state = _.extend(this.inner_action.params || {}, state);
+                    var params = {};
+                    _.each(this.inner_action.params, function(v, k) {
+                        if(_.isString(v) || _.isNumber(v)) {
+                            params[k] = v;
+                        }
+                    });
+                    state = _.extend(params || {}, state);
                 }
             }
             if(!this.dialog) {
@@ -185,7 +201,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
             action_loaded;
         if (state.action) {
             if (_.isString(state.action) && instance.web.client_actions.contains(state.action)) {
-                var action_client = {type: "ir.actions.client", tag: state.action, params: state};
+                var action_client = {
+                    type: "ir.actions.client",
+                    tag: state.action,
+                    params: state,
+                    _push_me: state._push_me,
+                };
                 this.null_action();
                 action_loaded = this.do_action(action_client);
             } else {
@@ -193,8 +214,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
                 if (run_action) {
                     this.null_action();
                     action_loaded = this.do_action(state.action);
-                    instance.webclient.menu.has_been_loaded.then(function() {
-                        instance.webclient.menu.open_action(state.action);
+                    $.when(action_loaded || null).done(function() {
+                        instance.webclient.menu.has_been_loaded.done(function() {
+                            if (self.inner_action && self.inner_action.id) {
+                                instance.webclient.menu.open_action(self.inner_action.id);
+                            }
+                        });
                     });
                 }
             }
@@ -211,29 +236,48 @@ instance.web.ActionManager = instance.web.Widget.extend({
         } else if (state.sa) {
             // load session action
             this.null_action();
-            action_loaded = this.rpc('/web/session/get_session_action',  {key: state.sa}).pipe(function(action) {
+            action_loaded = this.rpc('/web/session/get_session_action',  {key: state.sa}).then(function(action) {
                 if (action) {
                     return self.do_action(action);
                 }
             });
         }
 
-        $.when(action_loaded || null).then(function() {
+        $.when(action_loaded || null).done(function() {
             if (self.inner_widget && self.inner_widget.do_load_state) {
                 self.inner_widget.do_load_state(state, warm);
             }
         });
     },
-    do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
-        if (_.isString(action) && instance.web.client_actions.contains(action)) {
-            var action_client = { type: "ir.actions.client", tag: action };
-            return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+    do_action: function(action, options) {
+        options = _.defaults(options || {}, {
+            clear_breadcrumbs: false,
+            on_reverse_breadcrumb: function() {},
+            on_close: function() {},
+            action_menu_id: null,
+        });
+        if (action === false) {
+            action = { type: 'ir.actions.act_window_close' };
+        } else if (_.isString(action) && instance.web.client_actions.contains(action)) {
+            var action_client = { type: "ir.actions.client", tag: action, params: {} };
+            return this.do_action(action_client, options);
         } else if (_.isNumber(action) || _.isString(action)) {
             var self = this;
-            return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) {
-                return self.do_action(result, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+            return self.rpc("/web/action/load", { action_id: action }).then(function(result) {
+                return self.do_action(result, options);
             });
         }
+
+        // Ensure context & domain are evaluated and can be manipulated/used
+        if (action.context) {
+            action.context = instance.web.pyeval.eval(
+                'context', action.context);
+        }
+        if (action.domain) {
+            action.domain = instance.web.pyeval.eval(
+                'domain', action.domain);
+        }
+
         if (!action.type) {
             console.error("No type for action", action);
             return $.Deferred().reject();
@@ -249,11 +293,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
             pager : !popup && !inline,
             display_title : !popup
         }, action.flags || {});
+        action.menu_id = options.action_menu_id;
         if (!(type in this)) {
             console.error("Action manager can't handle action of type " + action.type, action);
             return $.Deferred().reject();
         }
-        return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+        return this[type](action, options);
     },
     null_action: function() {
         this.dialog_stop();
@@ -266,33 +311,36 @@ instance.web.ActionManager = instance.web.Widget.extend({
      * @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance
      * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new
      * @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget
-     * @param on_close
-     * @param clear_breadcrumbs
+     * @param {Object} options
      * @return {*}
      */
-    ir_actions_common: function(executor, on_close, clear_breadcrumbs) {
+    ir_actions_common: function(executor, options) {
         if (this.inner_widget && executor.action.target !== 'new') {
             if (this.getParent().has_uncommitted_changes()) {
                 return $.Deferred().reject();
-            } else if (clear_breadcrumbs) {
+            } else if (options.clear_breadcrumbs) {
                 this.clear_breadcrumbs();
             }
         }
         var widget = executor.widget();
         if (executor.action.target === 'new') {
-            if (this.dialog === null) {
-                // These buttons will be overwrited by <footer> if any
-                this.dialog = new instance.web.Dialog(this, {
-                    buttons: { "Close": function() { $(this).dialog("close"); }},
-                    dialogClass: executor.klass
-                });
-                if(on_close)
-                    this.dialog.on_close.add(on_close);
-            } else {
+            if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
                 this.dialog_widget.destroy();
             }
+            this.dialog_stop();
+            this.dialog = new instance.web.Dialog(this, {
+                dialogClass: executor.klass,
+            });
+            this.dialog.on("closing", null, options.on_close);
             this.dialog.dialog_title = executor.action.name;
+            if (widget instanceof instance.web.ViewManager) {
+                _.extend(widget.flags, {
+                    $buttons: this.dialog.$buttons,
+                    footer_to_buttons: true,
+                });
+            }
             this.dialog_widget = widget;
+            this.dialog_widget.setParent(this.dialog);
             var initialized = this.dialog_widget.appendTo(this.dialog.$el);
             this.dialog.open();
             return initialized;
@@ -304,84 +352,93 @@ instance.web.ActionManager = instance.web.Widget.extend({
             return this.inner_widget.appendTo(this.$el);
         }
     },
-    ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+    ir_actions_act_window: function (action, options) {
         var self = this;
 
         return this.ir_actions_common({
             widget: function () { return new instance.web.ViewManagerAction(self, action); },
             action: action,
             klass: 'oe_act_window',
-            post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); }
-        }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+            post_process: function (widget) { widget.add_breadcrumb(options.on_reverse_breadcrumb); }
+        }, options);
     },
-    ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
+    ir_actions_client: function (action, options) {
         var self = this;
         var ClientWidget = instance.web.client_actions.get_object(action.tag);
 
         if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
             var next;
-            if (next = ClientWidget(this, action.params)) {
-                return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+            if (next = ClientWidget(this, action)) {
+                return this.do_action(next, options);
             }
             return $.when();
         }
 
         return this.ir_actions_common({
-            widget: function () { return new ClientWidget(self, action.params); },
+            widget: function () { return new ClientWidget(self, action); },
             action: action,
             klass: 'oe_act_client',
             post_process: function(widget) {
                 self.push_breadcrumb({
                     widget: widget,
                     title: action.name,
-                    on_reverse_breadcrumb: on_reverse_breadcrumb,
+                    on_reverse_breadcrumb: options.on_reverse_breadcrumb,
                 });
                 if (action.tag !== 'reload') {
                     self.do_push_state({});
                 }
             }
-        }, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
+        }, options);
     },
-    ir_actions_act_window_close: function (action, on_closed) {
-        if (!this.dialog && on_closed) {
-            on_closed();
+    ir_actions_act_window_close: function (action, options) {
+        if (!this.dialog) {
+            options.on_close();
         }
         this.dialog_stop();
+        return $.when();
     },
-    ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) {
+    ir_actions_server: function (action, options) {
         var self = this;
         this.rpc('/web/action/run', {
             action_id: action.id,
             context: action.context || {}
-        }).then(function (action) {
-            self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb)
+        }).done(function (action) {
+            self.do_action(action, options)
         });
     },
-    ir_actions_report_xml: function(action, on_closed) {
+    ir_actions_report_xml: function(action, options) {
         var self = this;
         instance.web.blockUI();
-        self.rpc("/web/session/eval_domain_and_context", {
+        return instance.web.pyeval.eval_domains_and_contexts({
             contexts: [action.context],
             domains: []
         }).then(function(res) {
             action = _.clone(action);
             action.context = res.context;
-            self.session.get_file({
-                url: '/web/report',
-                data: {action: JSON.stringify(action)},
-                complete: instance.web.unblockUI,
-                success: function(){
-                    if (!self.dialog && on_closed) {
-                        on_closed();
+            var c = instance.webclient.crashmanager;
+            return $.Deferred(function (d) {
+                self.session.get_file({
+                    url: '/web/report',
+                    data: {action: JSON.stringify(action)},
+                    complete: instance.web.unblockUI,
+                    success: function(){
+                        if (!self.dialog) {
+                            options.on_close();
+                        }
+                        self.dialog_stop();
+                        d.resolve();
+                    },
+                    error: function () {
+                        c.rpc_error.apply(c, arguments);
+                        d.reject();
                     }
-                    self.dialog_stop();
-                },
-                error: instance.webclient.crashmanager.on_rpc_error
-            })
+                })
+            });
         });
     },
     ir_actions_act_url: function (action) {
         window.open(action.url, action.target === 'self' ? '_self' : '_blank');
+        return $.when();
     },
 });
 
@@ -389,6 +446,7 @@ instance.web.ViewManager =  instance.web.Widget.extend({
     template: "ViewManager",
     init: function(parent, dataset, views, flags) {
         this._super(parent);
+        this.url_states = {};
         this.model = dataset ? dataset.model : undefined;
         this.dataset = dataset;
         this.searchview = null;
@@ -460,7 +518,7 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         } else if (this.searchview
                 && self.flags.auto_search
                 && view.controller.searchable !== false) {
-            this.searchview.ready.then(this.searchview.do_search);
+            this.searchview.ready.done(this.searchview.do_search);
         }
 
         if (this.searchview) {
@@ -472,7 +530,7 @@ instance.web.ViewManager =  instance.web.Widget.extend({
             .find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
             .parent().addClass('active');
 
-        r = $.when(view_promise).then(function () {
+        return $.when(view_promise).done(function () {
             _.each(_.keys(self.views), function(view_name) {
                 var controller = self.views[view_name].controller;
                 if (controller) {
@@ -484,18 +542,10 @@ instance.web.ViewManager =  instance.web.Widget.extend({
                         container.hide();
                         controller.do_hide();
                     }
-                    // put the <footer> in the dialog's buttonpane
-                    if (self.$el.parent('.ui-dialog-content') && self.$el.find('footer')) {
-                        self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonset').hide()
-                        self.$el.find('footer').appendTo(
-                            self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonpane')
-                        );
-                    }
                 }
             });
             self.trigger('switch_mode', view_type, no_store, view_options);
         });
-        return r;
     },
     do_create_view: function(view_type) {
         // Lazy loading of views
@@ -531,11 +581,11 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         var view_promise = controller.appendTo(container);
         this.views[view_type].controller = controller;
         this.views[view_type].deferred.resolve(view_type);
-        return $.when(view_promise).then(function() {
+        return $.when(view_promise).done(function() {
             if (self.searchview
                     && self.flags.auto_search
                     && view.controller.searchable !== false) {
-                self.searchview.ready.then(self.searchview.do_search);
+                self.searchview.ready.done(self.searchview.do_search);
             }
             self.trigger("controller_inited",view_type,controller);
         });
@@ -560,10 +610,11 @@ instance.web.ViewManager =  instance.web.Widget.extend({
             action: this.action,
             show: function(index) {
                 var view_to_select = views[index];
-                self.$el.show();
-                if (self.active_view !== view_to_select) {
-                    self.switch_mode(view_to_select);
-                }
+                var state = self.url_states[view_to_select];
+                self.do_push_state(state || {});
+                $.when(self.switch_mode(view_to_select)).done(function() {
+                    self.$el.show();
+                });
             },
             get_title: function() {
                 var id;
@@ -629,18 +680,18 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         }
         this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
 
-        this.searchview.on_search.add(this.do_searchview_search);
+        this.searchview.on('search_data', self, this.do_searchview_search);
         return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
     },
     do_searchview_search: function(domains, contexts, groupbys) {
         var self = this,
             controller = this.views[this.active_view].controller,
             action_context = this.action.context || {};
-        this.rpc('/web/session/eval_domain_and_context', {
+        instance.web.pyeval.eval_domains_and_contexts({
             domains: [this.action.domain || []].concat(domains || []),
             contexts: [action_context].concat(contexts || []),
             group_by_seq: groupbys || []
-        }).then(function (results) {
+        }).done(function (results) {
             self.dataset._model = new instance.web.Model(
                 self.dataset.model, results.context, results.domain);
             var groupby = results.group_by.length
@@ -759,13 +810,13 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
                     name: "JS Tests",
                     target: 'new',
                     type : 'ir.actions.act_url',
-                    url: '/web/static/test/test.html'
-                })
+                    url: '/web/tests?mod=*'
+                });
                 break;
             case 'perm_read':
                 var ids = current_view.get_selected_ids();
                 if (ids.length === 1) {
-                    this.dataset.call('perm_read', [ids]).then(function(result) {
+                    this.dataset.call('perm_read', [ids]).done(function(result) {
                         var dialog = new instance.web.Dialog(this, {
                             title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model),
                             width: 400
@@ -779,6 +830,9 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
             case 'toggle_layout_outline':
                 current_view.rendering_engine.toggle_layout_debugging();
                 break;
+            case 'set_defaults':
+                current_view.open_defaults_dialog();
+                break;
             case 'translate':
                 this.do_action({
                     name: "Technical Translation",
@@ -791,7 +845,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
                 });
                 break;
             case 'fields':
-                this.dataset.call('fields_get', [false, {}]).then(function (fields) {
+                this.dataset.call('fields_get', [false, {}]).done(function (fields) {
                     var $root = $('<dl>');
                     _(fields).each(function (attributes, name) {
                         $root.append($('<dt>').append($('<h4>').text(name)));
@@ -847,7 +901,11 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
                             nested: true,
                         }
                     };
-                    this.session.get_file({ url: '/web/report', data: {action: JSON.stringify(action)}, complete: instance.web.unblockUI });
+                    this.session.get_file({
+                        url: '/web/report',
+                        data: {action: JSON.stringify(action)},
+                        complete: instance.web.unblockUI
+                    });
                 }
                 break;
             default:
@@ -878,10 +936,8 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
     switch_mode: function (view_type, no_store, options) {
         var self = this;
 
-        return $.when(this._super.apply(this, arguments)).then(function () {
-            var controller = self.views[self.active_view].controller,
-                fvg = controller.fields_view,
-                view_id = (fvg && fvg.view_id) || '--';
+        return $.when(this._super.apply(this, arguments)).done(function () {
+            var controller = self.views[self.active_view].controller;
             self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
                 view: controller,
                 view_manager: self
@@ -895,12 +951,22 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
         view.set({ 'title': this.action.name });
         return r;
     },
+    get_action_manager: function() {
+        var cur = this;
+        while (cur = cur.getParent()) {
+            if (cur instanceof instance.web.ActionManager) {
+                return cur;
+            }
+        }
+        return undefined;
+    },
     set_title: function(title) {
-        this.$el.find('.oe_breadcrumb_title:first').html(this.getParent().get_title());
+        this.$el.find('.oe_breadcrumb_title:first').html(this.get_action_manager().get_title());
     },
     do_push_state: function(state) {
         if (this.getParent() && this.getParent().do_push_state) {
             state["view_type"] = this.active_view;
+            this.url_states[this.active_view] = state;
             this.getParent().do_push_state(state);
         }
     },
@@ -909,13 +975,13 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
             defs = [];
         if (state.view_type && state.view_type !== this.active_view) {
             defs.push(
-                this.views[this.active_view].deferred.pipe(function() {
+                this.views[this.active_view].deferred.then(function() {
                     return self.switch_mode(state.view_type, true);
                 })
             );
         } 
 
-        $.when(defs).then(function() {
+        $.when(defs).done(function() {
             self.views[self.active_view].controller.do_load_state(state, warm);
         });
     },
@@ -937,11 +1003,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
         this.fileupload_id = _.uniqueId('oe_fileupload');
         $(window).on(this.fileupload_id, function() {
             var args = [].slice.call(arguments).slice(1);
-            if (args[0] && args[0].error) {
-                alert(args[0].error);
-            } else {
-                self.do_attachement_update(self.dataset, self.model_id);
-            }
+            self.do_attachement_update(self.dataset, self.model_id,args);
             instance.web.unblockUI();
         });
     },
@@ -1022,51 +1084,69 @@ instance.web.Sidebar = instance.web.Widget.extend({
     },
     on_item_action_clicked: function(item) {
         var self = this;
-        self.getParent().sidebar_context().then(function (context) {
+        self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
             var ids = self.getParent().get_selected_ids();
             if (ids.length == 0) {
                 instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
                 return false;
             }
-            var additional_context = _.extend({
+            var active_ids_context = {
                 active_id: ids[0],
                 active_ids: ids,
                 active_model: self.getParent().dataset.model
-            }, context);
+            }; 
+            var c = instance.web.pyeval.eval('context',
+                new instance.web.CompoundContext(
+                    sidebar_eval_context, active_ids_context));
             self.rpc("/web/action/load", {
                 action_id: item.action.id,
-                context: additional_context
-            }).then(function(result) {
-                result.context = _.extend(result.context || {},
-                    additional_context);
+                context: c
+            }).done(function(result) {
+                result.context = new instance.web.CompoundContext(
+                    result.context || {}, active_ids_context)
+                        .set_eval_context(c);
                 result.flags = result.flags || {};
                 result.flags.new_window = true;
-                self.do_action(result, function () {
-                    // reload view
-                    self.getParent().reload();
+                self.do_action(result, {
+                    on_close: function() {
+                        // reload view
+                        self.getParent().reload();
+                    },
                 });
             });
         });
     },
-    do_attachement_update: function(dataset, model_id) {
+    do_attachement_update: function(dataset, model_id,args) {
+        var self = this;
         this.dataset = dataset;
         this.model_id = model_id;
+        if (args && args[0]["erorr"]) {
+             instance.web.dialog($('<div>'),{
+                    modal: true,
+                    title: "OpenERP " + _.str.capitalize(args[0]["title"]),
+                    buttons: [{
+                        text: _t("Ok"),
+                        click: function(){
+                            $(this).dialog("close");
+                    }}]
+              }).html(args[0]["erorr"]);
+        }
         if (!model_id) {
             this.on_attachments_loaded([]);
         } else {
             var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ];
             var ds = new instance.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom);
-            ds.read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
+            ds.read_slice(['name', 'url', 'type'], {}).done(this.on_attachments_loaded);
         }
     },
     on_attachments_loaded: function(attachments) {
         var self = this;
         var items = [];
-        var prefix = this.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id=';
+        var prefix = this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'name'});
         _.each(attachments,function(a) {
             a.label = a.name;
             if(a.type === "binary") {
-                a.url = prefix  + a.id + '&t=' + (new Date().getTime());
+                a.url = prefix  + '&id=' + a.id + '&t=' + (new Date().getTime());
             }
         });
         self.items['files'] = attachments;
@@ -1085,13 +1165,12 @@ instance.web.Sidebar = instance.web.Widget.extend({
         }
     },
     on_attachment_delete: function(e) {
-        var self = this;
         e.preventDefault();
         e.stopPropagation();
         var self = this;
         var $e = $(e.currentTarget);
         if (confirm(_t("Do you really want to delete this attachment ?"))) {
-            (new instance.web.DataSet(this, 'ir.attachment')).unlink([parseInt($e.attr('data-id'), 10)]).then(function() {
+            (new instance.web.DataSet(this, 'ir.attachment')).unlink([parseInt($e.attr('data-id'), 10)]).done(function() {
                 self.do_attachement_update(self.dataset, self.model_id);
             });
         }
@@ -1114,30 +1193,34 @@ instance.web.View = instance.web.Widget.extend({
     start: function () {
         return this.load_view();
     },
-    load_view: function() {
+    load_view: function(context) {
+        var self = this;
+        var view_loaded;
         if (this.embedded_view) {
-            var def = $.Deferred();
-            var self = this;
-            $.async_when().then(function() {def.resolve(self.embedded_view);});
-            return def.pipe(this.on_loaded);
+            view_loaded = $.Deferred();
+            $.async_when().done(function() {
+                view_loaded.resolve(self.embedded_view);
+            });
         } else {
-            var context = new instance.web.CompoundContext(this.dataset.get_context());
             if (! this.view_type)
                 console.warn("view_type is not defined", this);
-            return this.rpc("/web/view/load", {
+            view_loaded = this.rpc("/web/view/load", {
                 "model": this.dataset.model,
                 "view_id": this.view_id,
                 "view_type": this.view_type,
                 toolbar: !!this.options.$sidebar,
-                context: context
-                }).pipe(this.on_loaded);
+                context: instance.web.pyeval.eval(
+                    'context', this.dataset.get_context(context))
+            });
         }
-    },
-    /**
-     * Called after a successful call to fields_view_get.
-     * Must return a promise.
-     */
-    on_loaded: function(fields_view_get) {
+        return view_loaded.then(function(r) {
+            self.trigger('view_loaded', r);
+            // add css classes that reflect the (absence of) access rights
+            self.$el.addClass('oe_view')
+                .toggleClass('oe_cannot_create', !self.is_action_enabled('create'))
+                .toggleClass('oe_cannot_edit', !self.is_action_enabled('edit'))
+                .toggleClass('oe_cannot_delete', !self.is_action_enabled('delete'));
+        });
     },
     set_default_options: function(options) {
         this.options = options || {};
@@ -1171,8 +1254,7 @@ instance.web.View = instance.web.Widget.extend({
         };
         var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
 
-        var handler = function (r) {
-            var action = r;
+        var handler = function (action) {
             if (action && action.constructor == Object) {
                 var ncontext = new instance.web.CompoundContext(context);
                 if (record_id) {
@@ -1183,22 +1265,17 @@ instance.web.View = instance.web.Widget.extend({
                     });
                 }
                 ncontext.add(action.context || {});
-                return self.rpc('/web/session/eval_domain_and_context', {
-                    contexts: [ncontext],
-                    domains: []
-                }).pipe(function (results) {
-                    action.context = results.context;
-                    /* niv: previously we were overriding once more with action_data.context,
-                     * I assumed this was not a correct behavior and removed it
-                     */
-                    return self.do_action(action, result_handler);
-                }, null);
+                action.context = ncontext;
+                return self.do_action(action, {
+                    on_close: result_handler,
+                });
             } else {
+                self.do_action({"type":"ir.actions.act_window_close"});
                 return result_handler();
             }
         };
 
-        if (action_data.special) {
+        if (action_data.special === 'cancel') {
             return handler({"type":"ir.actions.act_window_close"});
         } else if (action_data.type=="object") {
             var args = [[record_id]], additional_args = [];
@@ -1215,7 +1292,11 @@ instance.web.View = instance.web.Widget.extend({
             args.push(context);
             return dataset.call_button(action_data.name, args).then(handler);
         } else if (action_data.type=="action") {
-            return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}).then(handler);
+            return this.rpc('/web/action/load', {
+                action_id: action_data.name,
+                context: instance.web.pyeval.eval('context', context),
+                do_not_eval: true
+            }).then(handler);
         } else  {
             return dataset.exec_workflow(record_id, action_data.name).then(handler);
         }
@@ -1236,6 +1317,27 @@ instance.web.View = instance.web.Widget.extend({
     do_hide: function () {
         this.$el.hide();
     },
+    is_active: function () {
+        var manager = this.getParent();
+        return !manager || !manager.active_view
+             || manager.views[manager.active_view].controller === this;
+    }, /**
+     * Wraps fn to only call it if the current view is the active one. If the
+     * current view is not active, doesn't call fn.
+     *
+     * fn can not return anything, as a non-call to fn can't return anything
+     * either
+     *
+     * @param {Function} fn function to wrap in the active guard
+     */
+    guard_active: function (fn) {
+        var self = this;
+        return function () {
+            if (self.is_active()) {
+                fn.apply(self, arguments);
+            }
+        }
+    },
     do_push_state: function(state) {
         if (this.getParent() && this.getParent().do_push_state) {
             this.getParent().do_push_state(state);
@@ -1245,11 +1347,9 @@ instance.web.View = instance.web.Widget.extend({
     },
     /**
      * Switches to a specific view type
-     *
-     * @param {String} view view type to switch to
      */
-    do_switch_view: function(view) { 
-        this.trigger('switch_mode',view);
+    do_switch_view: function() { 
+        this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments)));
     },
     /**
      * Cancels the switch to the current view, switches to the previous one
@@ -1264,8 +1364,8 @@ instance.web.View = instance.web.Widget.extend({
     on_sidebar_export: function() {
         new instance.web.DataExport(this, this.dataset).open();
     },
-    sidebar_context: function () {
-        return $.when();
+    sidebar_eval_context: function () {
+        return $.when({});
     },
     /**
      * Asks the view to reload itself, if the reloading is asynchronous should
@@ -1345,14 +1445,16 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) {
     } else {
         return r + '/>';
     }
-}
+};
 instance.web.xml_to_str = function(node) {
-    if (window.ActiveXObject) {
+    if (window.XMLSerializer) {
+        return (new XMLSerializer()).serializeToString(node);
+    } else if (window.ActiveXObject) {
         return node.xml;
     } else {
-        return (new XMLSerializer()).serializeToString(node);
+        throw new Error("Could not serialize XML");
     }
-}
+};
 instance.web.str_to_xml = function(s) {
     if (window.DOMParser) {
         var dp = new DOMParser();