[FIX] Error while deleting a record when there is only one record in pageview
[odoo/odoo.git] / addons / web / static / src / js / views.js
index f97a09d..fd080c8 100644 (file)
@@ -16,8 +16,7 @@ session.web.client_actions = new session.web.Registry();
  */
 session.web.views = new session.web.Registry();
 
-session.web.ActionManager = session.web.Widget.extend({
-    identifier_prefix: "actionmanager",
+session.web.ActionManager = session.web.OldWidget.extend({
     init: function(parent) {
         this._super(parent);
         this.inner_action = null;
@@ -27,7 +26,7 @@ session.web.ActionManager = session.web.Widget.extend({
         this.client_widget = null;
     },
     render: function() {
-        return "<div id='"+this.element_id+"'></div>";
+        return '<div id="' + this.element_id + '" style="height: 100%;"></div>';
     },
     dialog_stop: function () {
         if (this.dialog) {
@@ -49,38 +48,60 @@ session.web.ActionManager = session.web.Widget.extend({
     },
     do_push_state: function(state) {
         if (this.widget_parent && this.widget_parent.do_push_state) {
-            if (this.inner_action && this.inner_action.id) {
-                state['action_id'] = this.inner_action.id;
+            if (this.inner_action) {
+                state['model'] = this.inner_action.res_model;
+                if (this.inner_action.id) {
+                    state['action_id'] = this.inner_action.id;
+                }
             }
             this.widget_parent.do_push_state(state);
         }
     },
-    do_load_state: function(state) {
+    do_load_state: function(state, warm) {
+        var self = this,
+            action_loaded;
         if (state.action_id) {
+            var run_action = (!this.inner_viewmanager) || this.inner_viewmanager.action.id !== state.action_id;
+            if (run_action) {
+                this.null_action();
+                action_loaded = this.do_action(state.action_id);
+            }
+        } else if (state.model && state.id) {
+            // TODO handle context & domain ?
             this.null_action();
-            this.do_action(state.action_id);
-        }
-        else if (state.model && state.id) {
-            // TODO implement it
-            //this.null_action();
-            // action = {}
-        }
-        else if (state.client_action) {
+            var action = {
+                res_model: state.model,
+                res_id: state.id,
+                type: 'ir.actions.act_window',
+                views: [[false, 'page'], [false, 'form']]
+            };
+            action_loaded = this.do_action(action);
+        } else if (state.sa) {
+            // load session action
+            var self = this;
+            this.null_action();
+            action_loaded = this.rpc('/web/session/get_session_action',  {key: state.sa}).pipe(function(action) {
+                if (action) {
+                    return self.do_action(action);
+                }
+            });
+        } else if (state.client_action) {
             this.null_action();
             this.ir_actions_client(state.client_action);
         }
 
-        if (this.inner_viewmanager) {
-            this.inner_viewmanager.do_load_state(state);
-        }
+        $.when(action_loaded || null).then(function() {
+            if (self.inner_viewmanager) {
+                self.inner_viewmanager.do_load_state(state, warm);
+            }
+        });
     },
     do_action: function(action, on_close) {
         if (_.isNumber(action)) {
             var self = this;
-            self.rpc("/web/action/load", { action_id: action }, function(result) {
+            return self.rpc("/web/action/load", { action_id: action }, function(result) {
                 self.do_action(result.result, on_close);
             });
-            return;
         }
         if (!action.type) {
             console.error("No type for action", action);
@@ -93,7 +114,8 @@ session.web.ActionManager = session.web.Widget.extend({
             search_view : !popup,
             action_buttons : !popup,
             sidebar : !popup,
-            pager : !popup
+            pager : !popup,
+            display_title : !popup
         }, action.flags || {});
         if (!(type in this)) {
             console.error("Action manager can't handle action of type " + action.type, action);
@@ -111,23 +133,27 @@ session.web.ActionManager = session.web.Widget.extend({
                 .contains(action.res_model)) {
             var old_close = on_close;
             on_close = function () {
-                session.webclient.do_reload();
-                if (old_close) { old_close(); }
+                session.webclient.do_reload().then(old_close);
             };
         }
         if (action.target === 'new') {
             if (this.dialog == null) {
-                this.dialog = new session.web.Dialog(this, { title: action.name, width: '80%' });
+                this.dialog = new session.web.Dialog(this, { width: '80%' });
                 if(on_close)
                     this.dialog.on_close.add(on_close);
-                this.dialog.start();
             } else {
                 this.dialog_viewmanager.stop();
             }
+            this.dialog.dialog_title = action.name;
             this.dialog_viewmanager = new session.web.ViewManagerAction(this, action);
             this.dialog_viewmanager.appendTo(this.dialog.$element);
             this.dialog.open();
         } else  {
+            if(action.menu_id) {
+                return this.widget_parent.do_action(action, function () {
+                    session.webclient.menu.open_menu(action.menu_id);
+                });
+            }
             this.dialog_stop();
             this.content_stop();
             this.inner_action = action;
@@ -174,7 +200,8 @@ session.web.ActionManager = session.web.Widget.extend({
                         on_closed();
                     }
                     self.dialog_stop();
-                }
+                },
+                error: session.webclient.crashmanager.on_rpc_error
             })
         });
     },
@@ -186,12 +213,11 @@ session.web.ActionManager = session.web.Widget.extend({
     }
 });
 
-session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.ViewManager# */{
-    identifier_prefix: "viewmanager",
+session.web.ViewManager =  session.web.OldWidget.extend(/** @lends session.web.ViewManager# */{
     template: "ViewManager",
     /**
      * @constructs session.web.ViewManager
-     * @extends session.web.Widget
+     * @extends session.web.OldWidget
      *
      * @param parent
      * @param dataset
@@ -203,7 +229,18 @@ session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.View
         this.dataset = dataset;
         this.searchview = null;
         this.active_view = null;
-        this.views_src = _.map(views, function(x) {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
+        this.views_src = _.map(views, function(x) {
+            if (x instanceof Array) {
+                var View = session.web.views.get_object(x[1], true);
+                return {
+                    view_id: x[0],
+                    view_type: x[1],
+                    label: View ? View.prototype.display_name : (void 'nope')
+                };
+            } else {
+                return x;
+            }
+        });
         this.views = {};
         this.flags = flags || {};
         this.registry = session.web.views;
@@ -278,11 +315,15 @@ session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.View
             this.views[view_type].deferred.resolve(view_type);
             $.when(view_promise).then(function() {
                 self.on_controller_inited(view_type, controller);
-                if (self.searchview && view.controller.searchable !== false) {
+                if (self.searchview
+                        && self.flags.auto_search
+                        && view.controller.searchable !== false) {
                     self.searchview.ready.then(self.searchview.do_search);
                 }
             });
-        } else if (this.searchview && view.controller.searchable !== false) {
+        } else if (this.searchview
+                && self.flags.auto_search
+                && view.controller.searchable !== false) {
             this.searchview.ready.then(this.searchview.do_search);
         }
 
@@ -295,17 +336,18 @@ session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.View
             .filter('[data-view-type="' + view_type + '"]')
             .attr('disabled', true);
 
-        for (var view_name in this.views) {
-            if (!this.views.hasOwnProperty(view_name)) { continue; }
-            if (this.views[view_name].controller) {
-                if (view_name === view_type) {
-                    $.when(view_promise).then(this.views[view_name].controller.do_show);
-                } else {
-                    this.views[view_name].controller.do_hide();
-                }
-            }
-        }
         $.when(view_promise).then(function () {
+            _.each(_.keys(self.views), function(view_name) {
+                var controller = self.views[view_name].controller;
+                if (controller) {
+                    if (view_name === view_type) {
+                        controller.do_show();
+                    } else {
+                        controller.do_hide();
+                    }
+                }
+            });
+
             self.$element.find('.oe_view_title_text:first').text(
                     self.display_title());
         });
@@ -316,15 +358,21 @@ session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.View
      * navigation history (the navigation history is appended to via
      * on_mode_switch)
      *
-     * @param {Boolean} [created=false] returning from a creation
+     * @param {Object} [options]
+     * @param {Boolean} [options.created=false] resource was created
+     * @param {String} [options.default=null] view to switch to if no previous view
      * @returns {$.Deferred} switching end signal
      */
-    on_prev_view: function (created) {
+    on_prev_view: function (options) {
+       options = options || {};
         var current_view = this.views_history.pop();
-        var previous_view = this.views_history[this.views_history.length - 1];
-        // APR special case: "If creation mode from list (and only from a list),
-        // after saving, go to page view (don't come back in list)"
-        if (created && current_view === 'form' && previous_view === 'list') {
+        var previous_view = this.views_history[this.views_history.length - 1] || options['default'];
+        if (options.created && current_view === 'form' && previous_view === 'list') {
+            // APR special case: "If creation mode from list (and only from a list),
+            // after saving, go to page view (don't come back in list)"
+            return this.on_mode_switch('page');
+        } else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
+            // APR special case: "If creation from dashboard, we have no previous view
             return this.on_mode_switch('page');
         }
         return this.on_mode_switch(previous_view, true);
@@ -361,6 +409,9 @@ session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.View
             var groupby = results.group_by.length
                         ? results.group_by
                         : action_context.group_by;
+            if (_.isString(groupby)) {
+                groupby = [groupby];
+            }
             controller.do_search(results.domain, results.context, groupby || []);
         });
     },
@@ -412,6 +463,9 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
         // do not have it yet (and we don't, because we've not called our own
         // ``_super()``) rpc requests will blow up.
         var flags = action.flags || {};
+        if (!('auto_search' in flags)) {
+            flags.auto_search = action.auto_search !== false;
+        }
         if (action.res_model == 'board.board' && action.view_mode === 'form') {
             // Special case for Dashboards
             _.extend(flags, {
@@ -481,7 +535,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
                 }
             });
             if (!(self.action.id in self.session.hidden_menutips)) {
-                Users.read_ids([this.session.uid], ['menu_tips'], function(users) {
+                Users.read_ids([this.session.uid], ['menu_tips']).then(function(users) {
                     var user = users[0];
                     if (!(user && user.id === self.session.uid)) {
                         return;
@@ -496,46 +550,83 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
             $res_logs.removeClass('oe-folded');
             return false;
         }).delegate('a.oe-remove-everything', 'click', function () {
-            $res_logs.removeClass('oe-has-more')
-                     .find('ul').empty();
+            $res_logs.removeClass('oe-has-more').find('ul').empty();
+            $res_logs.css('display','none');
             return false;
         });
+        $res_logs.css('display','none');
 
         return manager_ready;
     },
     on_debug_changed: function (evt) {
-        var $sel = $(evt.currentTarget),
+        var self = this,
+            $sel = $(evt.currentTarget),
             $option = $sel.find('option:selected'),
-            val = $sel.val();
+            val = $sel.val(),
+            current_view = this.views[this.active_view].controller;
         switch (val) {
             case 'fvg':
-                $('<pre>').text(session.web.json_node_to_xml(
-                    this.views[this.active_view].controller.fields_view.arch, true)
-                ).dialog({ width: '95%'});
+                var dialog = new session.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
+                $('<pre>').text(session.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element);
                 break;
-            case 'edit':
-                var model = $option.data('model'),
-                    id = $option.data('id'),
-                    domain = $option.data('domain'),
-                    action = {
-                        res_model : model,
-                        type : 'ir.actions.act_window',
-                        view_type : 'form',
-                        view_mode : 'form',
-                        target : 'new',
-                        flags : {
-                            action_buttons : true
-                        }
-                    };
-                if (id) {
-                    action.res_id = id,
-                    action.views = [[false, 'form']];
-                } else if (domain) {
-                    action.views = [[false, 'list'], [false, 'form']];
-                    action.domain = domain;
-                    action.flags.views_switcher = true;
+            case 'perm_read':
+                var ids = current_view.get_selected_ids();
+                if (ids.length === 1) {
+                    this.dataset.call('perm_read', [ids]).then(function(result) {
+                        var dialog = new session.web.Dialog(this, {
+                            title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model),
+                            width: 400
+                        }, QWeb.render('ViewManagerDebugViewLog', {
+                            perm : result[0],
+                            format : session.web.format_value
+                        })).open();
+                    });
                 }
-                this.do_action(action);
+                break;
+            case 'fields':
+                this.dataset.call_and_eval(
+                        'fields_get', [false, {}], null, 1).then(function (fields) {
+                    var $root = $('<dl>');
+                    _(fields).each(function (attributes, name) {
+                        $root.append($('<dt>').append($('<h4>').text(name)));
+                        var $attrs = $('<dl>').appendTo(
+                                $('<dd>').appendTo($root));
+                        _(attributes).each(function (def, name) {
+                            if (def instanceof Object) {
+                                def = JSON.stringify(def);
+                            }
+                            $attrs
+                                .append($('<dt>').text(name))
+                                .append($('<dd style="white-space: pre-wrap;">').text(def));
+                        });
+                    });
+                    new session.web.Dialog(self, {
+                        title: _.str.sprintf(_t("Model %s fields"),
+                                             self.dataset.model),
+                        width: '95%'}, $root).open();
+                });
+                break;
+            case 'manage_views':
+                if (current_view.fields_view && current_view.fields_view.arch) {
+                    var view_editor = new session.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch);
+                    view_editor.start();
+                } else {
+                    this.do_warn(_t("Manage Views"),
+                            _t("Could not find current view declaration"));
+                }
+                break;
+            case 'edit_workflow':
+                return this.do_action({
+                    res_model : 'workflow',
+                    domain : [['osv', '=', this.dataset.model]],
+                    views: [[false, 'list'], [false, 'form'], [false, 'diagram']],
+                    type : 'ir.actions.act_window',
+                    view_type : 'list',
+                    view_mode : 'list'
+                });
+                break;
+            case 'edit':
+                this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() });
                 break;
             default:
                 if (val) {
@@ -544,15 +635,31 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
         }
         evt.currentTarget.selectedIndex = 0;
     },
+    do_edit_resource: function(model, id, action) {
+        var action = _.extend({
+            res_model : model,
+            res_id : id,
+            type : 'ir.actions.act_window',
+            view_type : 'form',
+            view_mode : 'form',
+            views : [[false, 'form']],
+            target : 'new',
+            flags : {
+                action_buttons : true,
+                form : {
+                    resize_textareas : true
+                }
+            }
+        }, action || {});
+        this.do_action(action);
+    },
     on_mode_switch: function (view_type, no_store) {
         var self = this;
 
         return $.when(this._super(view_type, no_store)).then(function () {
             self.shortcut_check(self.views[view_type]);
 
-            self.$element.find('.oe-view-manager-logs:first')
-                .addClass('oe-folded').removeClass('oe-has-more')
-                .find('ul').empty();
+            self.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').removeClass('oe-has-more').css('display','none').find('ul').empty();
 
             var controller = self.views[self.active_view].controller,
                 fvg = controller.fields_view,
@@ -567,7 +674,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
 
             var $title = self.$element.find('.oe_view_title_text'),
                 $search_prefix = $title.find('span.oe_searchable_view');
-            if (controller.searchable !== false) {
+            if (controller.searchable !== false && self.flags.search_view !== false) {
                 if (!$search_prefix.length) {
                     $title.prepend('<span class="oe_searchable_view">' + _t("Search: ") + '</span>');
                 }
@@ -582,10 +689,19 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
             this.widget_parent.do_push_state(state);
         }
     },
-    do_load_state: function(state) {
-        var self = this;
-        $.when(this.on_mode_switch(state.view_type, true)).done(function() {
-            self.views[self.active_view].controller.do_load_state(state);
+    do_load_state: function(state, warm) {
+        var self = this,
+            defs = [];
+        if (state.view_type && state.view_type !== this.active_view) {
+            defs.push(
+                this.views[this.active_view].deferred.pipe(function() {
+                    return self.on_mode_switch(state.view_type, true);
+                })
+            );
+        } 
+
+        $.when(defs).then(function() {
+            self.views[self.active_view].controller.do_load_state(state, warm);
         });
     },
     shortcut_check : function(view) {
@@ -593,7 +709,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
         var grandparent = this.widget_parent && this.widget_parent.widget_parent;
         // display shortcuts if on the first view for the action
         var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle');
-        if (!(grandparent instanceof session.web.WebClient) ||
+        if (!this.action.name ||
             !(view.view_type === this.views_src[0].view_type
                 && view.view_id === this.views_src[0].view_id)) {
             $shortcut_toggle.hide();
@@ -637,23 +753,27 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
      * @param {Array<Object>} log_records
      */
     do_display_log: function (log_records) {
-        var self = this,
-            cutoff = 3,
-            $logs = this.$element.find('.oe-view-manager-logs:first')
-                    .addClass('oe-folded'),
-            $logs_list = $logs.find('ul').empty();
+        var self = this;
+        var cutoff = 3;
+        var $logs = this.$element.find('.oe-view-manager-logs:first').addClass('oe-folded').css('display', 'block');
+        var $logs_list = $logs.find('ul').empty();
         $logs.toggleClass('oe-has-more', log_records.length > cutoff);
         _(log_records.reverse()).each(function (record) {
+            var context = {};
+            if (record.context) {
+                try { context = py.eval(record.context).toJSON(); }
+                catch (e) { /* TODO: what do I do now? */ }
+            }
             $(_.str.sprintf('<li><a href="#">%s</a></li>', record.name))
                 .appendTo($logs_list)
-                .delegate('a', 'click', function (e) {
+                .delegate('a', 'click', function () {
                     self.do_action({
                         type: 'ir.actions.act_window',
                         res_model: record.res_model,
                         res_id: record.res_id,
                         // TODO: need to have an evaluated context here somehow
-                        //context: record.context,
-                        views: [[false, 'form']]
+                        context: context,
+                        views: [[context.view_id || false, 'form']]
                     });
                     return false;
                 });
@@ -664,7 +784,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
     }
 });
 
-session.web.Sidebar = session.web.Widget.extend({
+session.web.Sidebar = session.web.OldWidget.extend({
     init: function(parent, element_id) {
         this._super(parent, element_id);
         this.items = {};
@@ -685,26 +805,11 @@ session.web.Sidebar = session.web.Widget.extend({
             action = view_manager.action;
         if (this.session.uid === 1) {
             this.add_section(_t('Customize'), 'customize');
-            this.add_items('customize', [
-                {
-                    label: _t("Manage Views"),
-                    callback: view.on_sidebar_manage_views,
-                    title: _t("Manage views of the current object")
-                }, {
-                    label: _t("Edit Workflow"),
-                    callback: view.on_sidebar_edit_workflow,
-                    title: _t("Manage views of the current object"),
-                    classname: 'oe_hide oe_sidebar_edit_workflow'
-                }, {
-                    label: _t("Customize Object"),
-                    callback: view.on_sidebar_customize_object,
-                    title: _t("Manage views of the current object")
-                }, {
-                    label: _t("Translate"),
-                    callback: view.on_sidebar_translate,
-                    title: _t("Technical translation")
-                }
-            ]);
+            this.add_items('customize', [{
+                label: _t("Translate"),
+                callback: view.on_sidebar_translate,
+                title: _t("Technical translation")
+            }]);
         }
 
         this.add_section(_t('Other Options'), 'other');
@@ -715,10 +820,6 @@ session.web.Sidebar = session.web.Widget.extend({
             }, {
                 label: _t("Export"),
                 callback: view.on_sidebar_export
-            }, {
-                label: _t("View Log"),
-                callback: view.on_sidebar_view_log,
-                classname: 'oe_hide oe_sidebar_view_log'
             }
         ]);
     },
@@ -757,18 +858,32 @@ session.web.Sidebar = session.web.Widget.extend({
         }
         return $section;
     },
-
+    /**
+     * For each item added to the section:
+     *
+     * ``label``
+     *     will be used as the item's name in the sidebar
+     *
+     * ``action``
+     *     descriptor for the action which will be executed, ``action`` and
+     *     ``callback`` should be exclusive
+     *
+     * ``callback``
+     *     function to call when the item is clicked in the sidebar, called
+     *     with the item descriptor as its first argument (so information
+     *     can be stored as additional keys on the object passed to
+     *     ``add_items``)
+     *
+     * ``classname`` (optional)
+     *     ``@class`` set on the sidebar serialization of the item
+     *
+     * ``title`` (optional)
+     *     will be set as the item's ``@title`` (tooltip)
+     *
+     * @param {String} section_code
+     * @param {Array<{label, action | callback[, classname][, title]}>} items
+     */
     add_items: function(section_code, items) {
-        // An item is a dictonary : {
-        //    label: label to be displayed for the link,
-        //    action: action to be launch when the link is clicked,
-        //    callback: a function to be executed when the link is clicked,
-        //    classname: optional dom class name for the line,
-        //    title: optional title for the link
-        // }
-        // Note: The item should have one action or/and a callback
-        //
-
         var self = this,
             $section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code),
             section_id = $section.attr('id');
@@ -824,7 +939,10 @@ session.web.Sidebar = session.web.Widget.extend({
                     additional_context);
                 result.result.flags = result.result.flags || {};
                 result.result.flags.new_window = true;
-                self.do_action(result.result);
+                self.do_action(result.result, function () {
+                    // reload view
+                    self.widget_parent.reload();
+                });
             });
         });
     },
@@ -840,13 +958,13 @@ session.web.Sidebar = session.web.Widget.extend({
 });
 
 session.web.TranslateDialog = session.web.Dialog.extend({
-    dialog_title: _t("Translations"),
+    dialog_title: {toString: function () { return _t("Translations"); }},
     init: function(view) {
         // TODO fme: should add the language to fields_view_get because between the fields view get
         // and the moment the user opens the translation dialog, the user language could have been changed
         this.view_language = view.session.user_context.lang;
-        this['on_button' + _t("Save")] = this.on_button_Save;
-        this['on_button' + _t("Close")] = this.on_button_Close;
+        this['on_button_' + _t("Save")] = this.on_btn_save;
+        this['on_button_' + _t("Close")] = this.on_btn_close;
         this._super(view, {
             width: '80%',
             height: '80%'
@@ -860,7 +978,7 @@ session.web.TranslateDialog = session.web.Dialog.extend({
         this.languages = null;
         this.languages_loaded = $.Deferred();
         (new session.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
-            [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded);
+            [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded);
     },
     start: function() {
         var self = this;
@@ -927,9 +1045,10 @@ session.web.TranslateDialog = session.web.Dialog.extend({
             }
         });
     },
-    on_button_Save: function() {
+    on_btn_save: function() {
         var trads = {},
-            self = this;
+            self = this,
+            trads_mutex = new $.Mutex();
         self.$fields_form.find('.oe_trad_field.touched').each(function() {
             var field = $(this).attr('name').split('-');
             if (!trads[field[0]]) {
@@ -942,19 +1061,28 @@ session.web.TranslateDialog = session.web.Dialog.extend({
                 _.each(data, function(value, field) {
                     self.view.fields[field].set_value(value);
                 });
-            } else {
-                self.view.dataset.write(self.view.datarecord.id, data, { 'lang': code });
             }
+            trads_mutex.exec(function() {
+                return self.view.dataset.write(self.view.datarecord.id, data, { context : { 'lang': code } });
+            });
         });
         this.close();
     },
-    on_button_Close: function() {
+    on_btn_close: function() {
         this.close();
     }
 });
 
 session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
     template: "EmptyComponent",
+    // name displayed in view switchers
+    display_name: '',
+    init: function(parent, dataset, view_id, options) {
+        this._super(parent);
+        this.dataset = dataset;
+        this.view_id = view_id;
+        this.set_default_options(options);
+    },
     set_default_options: function(options) {
         this.options = options || {};
         _.defaults(this.options, {
@@ -1055,7 +1183,6 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
     },
     do_show: function () {
         this.$element.show();
-        this.do_push_state({});
     },
     do_hide: function () {
         this.$element.hide();
@@ -1065,7 +1192,7 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
             this.widget_parent.do_push_state(state);
         }
     },
-    do_load_state: function(state) {
+    do_load_state: function(state, warm) {
     },
     /**
      * Switches to a specific view type
@@ -1076,35 +1203,18 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
     },
     /**
      * Cancels the switch to the current view, switches to the previous one
+     *
+     * @param {Object} [options]
+     * @param {Boolean} [options.created=false] resource was created
+     * @param {String} [options.default=null] view to switch to if no previous view
      */
-    do_prev_view: function () { 
+    do_prev_view: function (options) {
     },
     do_search: function(view) {
     },
     set_common_sidebar_sections: function(sidebar) {
         sidebar.add_default_sections();
     },
-    on_sidebar_manage_views: function() {
-        if (this.fields_view && this.fields_view.arch) {
-            var view_editor = new session.web.ViewEditor(this, this.$element, this.dataset, this.fields_view.arch);
-            view_editor.start();
-        } else {
-            this.do_warn("Manage Views", "Could not find current view declaration");
-        }
-    },
-    on_sidebar_edit_workflow: function() {
-        console.log('Todo');
-    },
-    on_sidebar_customize_object: function() {
-        var self = this;
-        this.rpc('/web/dataset/search_read', {
-            model: 'ir.model',
-            fields: ['id'],
-            domain: [['model', '=', self.dataset.model]]
-        }, function (result) {
-            self.on_sidebar_edit_resource('ir.model', result.ids[0]);
-        });
-    },
     on_sidebar_import: function() {
         var import_view = new session.web.DataImport(this, this.dataset);
         import_view.start();
@@ -1123,37 +1233,21 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
             view_mode : "list"
         });
     },
-    on_sidebar_edit_resource: function(model, id, domain) {
-        var action = {
-            res_model : model,
-            type : 'ir.actions.act_window',
-            view_type : 'form',
-            view_mode : 'form',
-            target : 'new',
-            flags : {
-                action_buttons : true
-            }
-        }
-        if (id) {
-            action.res_id = id,
-            action.views = [[false, 'form']];
-        } else if (domain) {
-            action.views = [[false, 'list'], [false, 'form']];
-            action.domain = domain;
-            action.flags.views_switcher = true;
-        }
-        this.do_action(action);
-    },
-    on_sidebar_view_log: function() {
-    },
     sidebar_context: function () {
-        return $.Deferred().resolve({}).promise();
+        return $.when();
+    },
+    /**
+     * Asks the view to reload itself, if the reloading is asynchronous should
+     * return a {$.Deferred} indicating when the reloading is done.
+     */
+    reload: function () {
+        return $.when();
     }
 });
 
 session.web.json_node_to_xml = function(node, human_readable, indent) {
     // For debugging purpose, this function will convert a json node back to xml
-    // Maybe usefull for xml view editor
+    // Maybe useful for xml view editor
     indent = indent || 0;
     var sindent = (human_readable ? (new Array(indent + 1).join('\t')) : ''),
         r = sindent + '<' + node.tag,