[MERGE] Merge with trunk upto revision no 1400.
[odoo/odoo.git] / addons / web / static / src / js / views.js
index f8382cb..b6a37c8 100644 (file)
@@ -2,16 +2,21 @@
  * OpenERP web library
  *---------------------------------------------------------*/
 
-openerp.web.views = function(openerp) {
+openerp.web.views = function(session) {
 
-var _t = openerp.web._t;
+var _t = session.web._t;
 
 /**
  * Registry for all the client actions key: tag value: widget
  */
-openerp.web.client_actions = new openerp.web.Registry();
+session.web.client_actions = new session.web.Registry();
 
-openerp.web.ActionManager = openerp.web.Widget.extend({
+/**
+ * Registry for all the main views
+ */
+session.web.views = new session.web.Registry();
+
+session.web.ActionManager = session.web.Widget.extend({
     identifier_prefix: "actionmanager",
     init: function(parent) {
         this._super(parent);
@@ -19,7 +24,6 @@ openerp.web.ActionManager = openerp.web.Widget.extend({
         this.dialog = null;
         this.dialog_viewmanager = null;
         this.client_widget = null;
-        this.url = {}
     },
     render: function() {
         return "<div id='"+this.element_id+"'></div>";
@@ -43,8 +47,11 @@ openerp.web.ActionManager = openerp.web.Widget.extend({
         }
     },
     url_update: function(action) {
+        var url = {};
+        if(action.id)
+            url.action_id = action.id;
         // this.url = {
-        //     "model": action.model,
+        //     "model": action.res_model,
         //     "domain": action.domain,
         // };
         // action.res_model
@@ -55,14 +62,17 @@ openerp.web.ActionManager = openerp.web.Widget.extend({
         // action.res_id
         // mode
         // menu
+        this.do_url_set_hash(url);
     },
-    url_stringify: function(action) {
-    },
-    url_parse: function(action) {
+    do_url_set_hash: function(url) {
     },
-    on_url_update: function(url) {
-    },
-    do_url_action: function(url) {
+    on_url_hashchange: function(url) {
+        var self = this;
+        if(url && url.action_id) {
+            self.rpc("/web/action/load", { action_id: url.action_id }, function(result) {
+                    self.do_action(result.result);
+                });
+        }
     },
     do_action: function(action, on_close) {
         var type = action.type.replace(/\./g,'_');
@@ -75,28 +85,28 @@ openerp.web.ActionManager = openerp.web.Widget.extend({
             pager : !popup
         }, action.flags || {});
         if (!(type in this)) {
-            this.log("Action manager can't handle action of type " + action.type, action);
+            console.log("Action manager can't handle action of type " + action.type, action);
             return;
         }
-        this[type](action, on_close);
+        return this[type](action, on_close);
     },
     ir_actions_act_window: function (action, on_close) {
         if (action.target === 'new') {
             if (this.dialog == null) {
-                this.dialog = new openerp.web.Dialog(this, { title: action.name, width: '80%' });
+                this.dialog = new session.web.Dialog(this, { title: action.name, width: '80%' });
                 if(on_close)
                     this.dialog.on_close.add(on_close);
                 this.dialog.start();
             } else {
                 this.dialog_viewmanager.stop();
             }
-            this.dialog_viewmanager = new openerp.web.ViewManagerAction(this, action);
+            this.dialog_viewmanager = new session.web.ViewManagerAction(this, action);
             this.dialog_viewmanager.appendTo(this.dialog.$element);
             this.dialog.open();
         } else  {
             this.dialog_stop();
             this.content_stop();
-            this.inner_viewmanager = new openerp.web.ViewManagerAction(this, action);
+            this.inner_viewmanager = new session.web.ViewManagerAction(this, action);
             this.inner_viewmanager.appendTo(this.$element);
             this.url_update(action);
         }
@@ -108,47 +118,80 @@ openerp.web.ActionManager = openerp.web.Widget.extend({
         */
     },
     ir_actions_act_window_close: function (action, on_closed) {
+        if (!this.dialog && on_closed) {
+            on_closed();
+        }
+        if (this.dialog && action.context) {
+            var model = action.context.active_model;
+            if (model === 'base.module.upgrade' || model === 'base.setup.installer' || model === 'base.module.upgrade') {
+                session.webclient.do_reload();
+            }
+        }
         this.dialog_stop();
     },
     ir_actions_server: function (action, on_closed) {
         var self = this;
         this.rpc('/web/action/run', {
             action_id: action.id,
-            context: {active_id: 66, active_ids: [66], active_model: 'ir.ui.menu'}
+            context: action.context || {}
         }).then(function (action) {
             self.do_action(action, on_closed)
         });
     },
     ir_actions_client: function (action) {
         this.content_stop();
-        var ClientWidget = openerp.web.client_actions.get_object(action.tag);
+        var ClientWidget = session.web.client_actions.get_object(action.tag);
         (this.client_widget = new ClientWidget(this, action.params)).appendTo(this);
     },
     ir_actions_report_xml: function(action) {
-        this.rpc('/web/report/get_report', {
-            action: action,
-            context: {}
-        }).then(function(result) {
-            debugger;
+        var self = this;
+        $.blockUI();
+        self.rpc("/web/session/eval_domain_and_context", {
+            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: $.unblockUI
+            });
         });
+    },
+    ir_actions_act_url: function (action) {
+        window.open(action.url, action.target === 'self' ? '_self' : '_blank');
     }
 });
 
-openerp.web.ViewManager =  openerp.web.Widget.extend({
+session.web.ViewManager =  session.web.Widget.extend(/** @lends session.web.ViewManager# */{
     identifier_prefix: "viewmanager",
+    template: "ViewManager",
+    /**
+     * @constructs session.web.ViewManager
+     * @extends session.web.Widget
+     *
+     * @param parent
+     * @param dataset
+     * @param views
+     */
     init: function(parent, dataset, views) {
         this._super(parent);
-        this.model = dataset.model;
+        this.model = dataset ? dataset.model : undefined;
         this.dataset = dataset;
         this.searchview = null;
+        this.last_search = false;
         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 = {};
         this.flags = this.flags || {};
-        this.registry = openerp.web.views;
+        this.registry = session.web.views;
     },
     render: function() {
-        return QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src})
+        return session.web.qweb.render(this.template, {
+            self: this,
+            prefix: this.element_id,
+            views: this.views_src});
     },
     /**
      * @returns {jQuery.Deferred} initial view loading promise
@@ -156,7 +199,6 @@ openerp.web.ViewManager =  openerp.web.Widget.extend({
     start: function() {
         this._super();
         var self = this;
-        this.dataset.start();
         this.$element.find('.oe_vm_switch button').click(function() {
             self.on_mode_switch($(this).data('view-type'));
         });
@@ -192,40 +234,26 @@ openerp.web.ViewManager =  openerp.web.Widget.extend({
         if (!view.controller) {
             // Lazy loading of views
             var controllerclass = this.registry.get_object(view_type);
-            var controller = new controllerclass(this, this.element_id + '_view_' + view_type,
-                this.dataset, view.view_id, view.options);
+            var controller = new controllerclass(this, this.dataset, view.view_id, view.options);
             if (view.embedded_view) {
                 controller.set_embedded_view(view.embedded_view);
             }
             controller.do_switch_view.add_last(this.on_mode_switch);
-            if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
-                // In case the search view is not instantiated: manually call ListView#search
-                var domains = !_(self.action.domain).isEmpty()
-                                ? [self.action.domain] : [],
-                   contexts = !_(self.action.context).isEmpty()
-                                ? [self.action.context] : [];
-                controller.on_loaded.add({
-                    callback: function () {
-                        controller.do_search(domains, contexts, []);
-                    },
-                    position: 'last',
-                    unique: true
-                });
-            }
-            view_promise = controller.start();
+            var container = $("#" + this.element_id + '_view_' + view_type);
+            view_promise = controller.appendTo(container);
+            this.views[view_type].controller = controller;
             $.when(view_promise).then(function() {
                 self.on_controller_inited(view_type, controller);
+                if (self.searchview && view.controller.searchable !== false) {
+                    self.do_searchview_search();
+                }
             });
-            this.views[view_type].controller = controller;
+        } else if (this.searchview && view.controller.searchable !== false) {
+            self.do_searchview_search();
         }
 
-
         if (this.searchview) {
-            if (view.controller.searchable === false) {
-                this.searchview.hide();
-            } else {
-                this.searchview.show();
-            }
+            this.searchview[(view.controller.searchable === false || this.searchview.hidden) ? 'hide' : 'show']();
         }
 
         this.$element
@@ -243,20 +271,16 @@ openerp.web.ViewManager =  openerp.web.Widget.extend({
                 }
             }
         }
+        $.when(view_promise).then(function () {
+            self.$element.find('.oe_view_title:first').text(
+                    self.display_title());
+        });
         return view_promise;
     },
     /**
-     * Event launched when a controller has been inited.
-     *
-     * @param {String} view_type type of view
-     * @param {String} view the inited controller
-     */
-    on_controller_inited: function(view_type, view) {
-    },
-    /**
      * Sets up the current viewmanager's search view.
      *
-     * @param view_id the view to use or false for a default one
+     * @param {Number|false} view_id the view to use or false for a default one
      * @returns {jQuery.Deferred} search view startup deferred
      */
     setup_search_view: function(view_id, search_defaults) {
@@ -264,15 +288,38 @@ openerp.web.ViewManager =  openerp.web.Widget.extend({
         if (this.searchview) {
             this.searchview.stop();
         }
-        this.searchview = new openerp.web.SearchView(this, this.element_id + "_search", this.dataset, view_id, search_defaults);
-        if (this.flags.search_view === false) {
-            this.searchview.hide();
+        this.searchview = new session.web.SearchView(
+                this, this.dataset,
+                view_id, search_defaults, this.flags.search_view === false);
+
+        this.searchview.on_search.add(this.do_searchview_search);
+        return this.searchview.appendTo($("#" + this.element_id + "_search"));
+    },
+    do_searchview_search: function(domains, contexts, groupbys) {
+        var self = this,
+            controller = this.views[this.active_view].controller;
+        if (domains || contexts) {
+            this.rpc('/web/session/eval_domain_and_context', {
+                domains: [this.action.domain || []].concat(domains || []),
+                contexts: [this.action.context || {}].concat(contexts || []),
+                group_by_seq: groupbys || []
+            }, function (results) {
+                self.dataset.context = results.context;
+                self.dataset.domain = results.domain;
+                self.last_search = [results.domain, results.context, results.group_by];
+                controller.do_search(results.domain, results.context, results.group_by);
+            });
+        } else if (this.last_search) {
+            controller.do_search.apply(controller, this.last_search);
         }
-        this.searchview.on_search.add(function(domains, contexts, groupbys) {
-            var controller = self.views[self.active_view].controller;
-            controller.do_search.call(controller, domains, contexts, groupbys);
-        });
-        return this.searchview.start();
+    },
+    /**
+     * Event launched when a controller has been inited.
+     *
+     * @param {String} view_type type of view
+     * @param {String} view the inited controller
+     */
+    on_controller_inited: function(view_type, view) {
     },
     /**
      * Called when one of the view want to execute an action
@@ -284,65 +331,149 @@ openerp.web.ViewManager =  openerp.web.Widget.extend({
     on_remove: function() {
     },
     on_edit: function() {
+    },
+    /**
+     * Called by children view after executing an action
+     */
+    on_action_executed: function () {},
+    display_title: function () {
+        var view = this.views[this.active_view];
+        if (view) {
+            // ick
+            return view.controller.fields_view.arch.attrs.string;
+        }
+        return '';
     }
 });
 
-openerp.web.ViewManagerAction = openerp.web.ViewManager.extend({
+session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{
+    template:"ViewManagerAction",
+    /**
+     * @constructs session.web.ViewManagerAction
+     * @extends session.web.ViewManager
+     *
+     * @param {session.web.ActionManager} parent parent object/widget
+     * @param {Object} action descriptor for the action this viewmanager needs to manage its views.
+     */
     init: function(parent, action) {
+        // dataset initialization will take the session from ``this``, so if we
+        // do not have it yet (and we don't, because we've not called our own
+        // ``_super()``) rpc requests will blow up.
+        this._super(parent, null, action.views);
         this.session = parent.session;
         this.action = action;
-        var dataset;
-        if (!action.res_id) {
-            dataset = new openerp.web.DataSetSearch(this, action.res_model, action.context, action.domain);
-        } else {
-            this.action.flags.search_view = false;
-            dataset = new openerp.web.DataSetStatic(this, action.res_model, action.context, [action.res_id]);
+        var dataset = new session.web.DataSetSearch(this, action.res_model, action.context, action.domain);
+        if (action.res_id) {
+            dataset.ids.push(action.res_id);
             dataset.index = 0;
         }
-        this._super(parent, dataset, action.views);
-        this.action = action;
+        this.dataset = dataset;
         this.flags = this.action.flags || {};
         if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
-            // Not elegant but allows to avoid flickering of SearchView#do_hide
+            // Not elegant but allows to avoid form chrome (pager, save/new
+            // buttons, sidebar, ...) displaying
             this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false;
         }
+
+        // setup storage for session-wise menu hiding
+        if (this.session.hidden_menutips) {
+            return;
+        }
+        this.session.hidden_menutips = {}
     },
+    /**
+     * Initializes the ViewManagerAction: sets up the searchview (if the
+     * searchview is enabled in the manager's action flags), calls into the
+     * parent to initialize the primary view and (if the VMA has a searchview)
+     * launches an initial search after both views are done rendering.
+     */
     start: function() {
-        var inital_view_loaded = this._super();
-
-        var search_defaults = {};
+        var self = this,
+            searchview_loaded,
+            search_defaults = {};
         _.each(this.action.context, function (value, key) {
             var match = /^search_default_(.*)$/.exec(key);
             if (match) {
                 search_defaults[match[1]] = value;
             }
         });
+        // init search view
+        var searchview_id = this.action['search_view_id'] && this.action['search_view_id'][0];
 
-        if (this.flags.search_view !== false) {
-            // init search view
-            var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
+        searchview_loaded = this.setup_search_view(searchview_id || false, search_defaults);
 
-            var searchview_loaded = this.setup_search_view(
-                    searchview_id || false, search_defaults);
+        var main_view_loaded = this._super();
 
+        var manager_ready = $.when(searchview_loaded, main_view_loaded);
+        if (searchview_loaded && this.action['auto_search'] !== false) {
             // schedule auto_search
-            if (searchview_loaded != null && this.action['auto_search']) {
-                $.when(searchview_loaded, inital_view_loaded)
-                    .then(this.searchview.do_search);
+            manager_ready.then(this.searchview.do_search);
+        }
+
+        this.$element.find('.oe_get_xml_view').click(function () {
+            // TODO: add search view?
+            $('<pre>').text(session.web.json_node_to_xml(
+                self.views[self.active_view].controller.fields_view.arch, true))
+                    .dialog({ width: '95%'});
+        });
+        if (this.action.help && !this.flags.low_profile) {
+            var Users = new session.web.DataSet(self, 'res.users'),
+                header = this.$element.find('.oe-view-manager-header');
+            header.delegate('blockquote button', 'click', function() {
+                var $this = $(this);
+                //noinspection FallthroughInSwitchStatementJS
+                switch ($this.attr('name')) {
+                case 'disable':
+                    Users.write(self.session.uid, {menu_tips:false});
+                case 'hide':
+                    $this.closest('blockquote').hide();
+                    self.session.hidden_menutips[self.action.id] = true;
+                }
+            });
+            if (!(self.action.id in self.session.hidden_menutips)) {
+                Users.read_ids([this.session.uid], ['menu_tips'], function(users) {
+                    var user = users[0];
+                    if (!(user && user.id === self.session.uid)) {
+                        return;
+                    }
+                    header.find('blockquote').toggle(user.menu_tips);
+                });
             }
         }
+
+        return manager_ready;
     },
     on_mode_switch: function (view_type) {
+        var self = this;
         return $.when(
-            this._super(view_type),
-            this.shortcut_check(this.views[view_type]));
+                this._super(view_type),
+                this.shortcut_check(this.views[view_type])
+            ).then(function() {
+                var controller = self.views[self.active_view].controller,
+                    fvg = controller.fields_view,
+                    view_id = (fvg && fvg.view_id) || '--';
+                self.$element.find('.oe_get_xml_view span').text(view_id);
+                if (!self.action.name && fvg) {
+                    self.$element.find('.oe_view_title').text(fvg.arch.attrs.string || fvg.name);
+                }
+
+                var $title = self.$element.find('.oe_view_title'),
+                    $search_prefix = $title.find('span');
+                if (controller.searchable !== false) {
+                    if (!$search_prefix.length) {
+                        $title.prepend('<span>' + _t("Search:") + '</span>');
+                    }
+                } else {
+                    $search_prefix.remove();
+                }
+        });
     },
     shortcut_check : function(view) {
         var self = this;
         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 openerp.web.WebClient) ||
+        if (!(grandparent instanceof session.web.WebClient) ||
             !(view.view_type === this.views_src[0].view_type
                 && view.view_id === this.views_src[0].view_id)) {
             $shortcut_toggle.hide();
@@ -374,10 +505,42 @@ openerp.web.ViewManagerAction = openerp.web.ViewManager.extend({
                     $shortcut_toggle.addClass("oe-shortcut-remove");
                 }
             });
+    },
+    /**
+     * Intercept do_action resolution from children views
+     */
+    on_action_executed: function () {
+        return new session.web.DataSet(this, 'res.log')
+                .call('get', [], this.do_display_log);
+    },
+    /**
+     * @param {Array<Object>} log_records
+     */
+    do_display_log: function (log_records) {
+        var self = this,
+            $logs = this.$element.find('ul.oe-view-manager-logs:first').empty();
+        _(log_records).each(function (record) {
+            $(_.sprintf('<li><a href="#">%s</a></li>', record.name))
+                .appendTo($logs)
+                .delegate('a', 'click', function (e) {
+                    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']]
+                    });
+                    return false;
+                });
+        });
+    },
+    display_title: function () {
+        return this.action.name;
     }
 });
 
-openerp.web.Sidebar = openerp.web.Widget.extend({
+session.web.Sidebar = session.web.Widget.extend({
     init: function(parent, element_id) {
         this._super(parent, element_id);
         this.items = {};
@@ -386,14 +549,62 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
     start: function() {
         this._super(this);
         var self = this;
-        this.$element.html(QWeb.render('Sidebar'));
+        this.$element.html(session.web.qweb.render('Sidebar'));
         this.$element.find(".toggle-sidebar").click(function(e) {
             self.do_toggle();
         });
     },
+
+    call_default_on_sidebar: function(item) {
+        var func_name = 'on_sidebar_' + _.underscored(item.label);
+        var fn = this.widget_parent[func_name];
+        if(typeof fn === 'function') {
+            fn(item);
+        }
+    },
+
+    add_default_sections: function() {
+        this.add_section(_t('Customize'), 'customize');
+        this.add_items('customize', [
+            {
+                label: _t("Manage Views"),
+                callback: this.call_default_on_sidebar,
+                title: _t("Manage views of the current object")
+            }, {
+                label: _t("Edit Workflow"),
+                callback: this.call_default_on_sidebar,
+                title: _t("Manage views of the current object"),
+                classname: 'oe_hide oe_sidebar_edit_workflow'
+            }, {
+                label: _t("Customize Object"),
+                callback: this.call_default_on_sidebar,
+                title: _t("Manage views of the current object")
+            }
+        ]);
+
+        this.add_section(_t('Other Options'), 'other');
+        this.add_items('other', [
+            {
+                label: _t("Import"),
+                callback: this.call_default_on_sidebar
+            }, {
+                label: _t("Export"),
+                callback: this.call_default_on_sidebar
+            }, {
+                label: _t("Translate"),
+                callback: this.call_default_on_sidebar,
+                classname: 'oe_sidebar_translate oe_hide'
+            }, {
+                label: _t("View Log"),
+                callback: this.call_default_on_sidebar,
+                classname: 'oe_hide oe_sidebar_view_log'
+            }
+        ]);
+    },
+
     add_toolbar: function(toolbar) {
         var self = this;
-        _.each([['print', "Reports"], ['action', "Actions"], ['relate', "Links"]], function(type) {
+        _.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) {
             var items = toolbar[type[0]];
             if (items.length) {
                 for (var i = 0; i < items.length; i++) {
@@ -403,15 +614,30 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
                         classname: 'oe_sidebar_' + type[0]
                     }
                 }
-                self.add_section(type[0], type[1], items);
+                self.add_section(type[1], type[0]);
+                self.add_items(type[0], items);
             }
         });
     },
-    add_section: function(code, name, items) {
-        // For each section, we pass a name/label and optionally an array of items.
-        // If no items are passed, then the section will be created as a custom section
-        // returning back an element_id to be used by a custom controller.
-        // Else, the section is a standard section with items displayed as links.
+
+    add_section: function(name, code) {
+        if(!code) code = _.underscored(name);
+        var $section = this.sections[code];
+
+        if(!$section) {
+            section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
+            var $section = $(session.web.qweb.render("Sidebar.section", {
+                section_id: section_id,
+                name: name,
+                classname: 'oe_sidebar_' + code
+            }));
+            $section.appendTo(this.$element.find('div.sidebar-actions'));
+            this.sections[code] = $section;
+        }
+        return $section;
+    },
+
+    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,
@@ -420,30 +646,33 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
         //    title: optional title for the link
         // }
         // Note: The item should have one action or/and a callback
+        //
+
         var self = this,
-            section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
+            $section = this.add_section(_.titleize(section_code.replace('_', ' ')), section_code),
+            section_id = $section.attr('id');
+
         if (items) {
             for (var i = 0; i < items.length; i++) {
                 items[i].element_id = _.uniqueId(section_id + '_item_');
                 this.items[items[i].element_id] = items[i];
             }
-        }
-        var $section = $(QWeb.render("Sidebar.section", {
-            section_id: section_id,
-            name: name,
-            classname: 'oe_sidebar_' + code,
-            items: items
-        }));
-        if (items) {
-            $section.find('a.oe_sidebar_action_a').click(function() {
+
+            var $items = $(session.web.qweb.render("Sidebar.section.items", {items: items}));
+
+            $items.find('a.oe_sidebar_action_a').click(function() {
                 var item = self.items[$(this).attr('id')];
                 if (item.callback) {
-                    item.callback();
+                    item.callback.apply(self, [item]);
                 }
                 if (item.action) {
                     var ids = self.widget_parent.get_selected_ids();
                     if (ids.length == 0) {
-                        //TODO niv: maybe show a warning?
+                        //TODO: make prettier warning?
+                        $("<div />").text(_t("You must choose at least one record.")).dialog({
+                            title: _t("Warning"),
+                            modal: true
+                        });
                         return false;
                     }
                     var additional_context = {
@@ -453,7 +682,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
                     };
                     self.rpc("/web/action/load", {
                         action_id: item.action.id,
-                        context: _.extend({"bin_size": true}, additional_context)
+                        context: additional_context
                     }, function(result) {
                         result.result.context = _.extend(result.result.context || {},
                             additional_context);
@@ -464,10 +693,13 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
                 }
                 return false;
             });
+
+            var $ul = $section.find('ul');
+            if(!$ul.length) {
+                $ul = $('<ul/>').appendTo($section);
+            }
+            $items.appendTo($ul);
         }
-        $section.appendTo(this.$element.find('div.sidebar-actions'));
-        this.sections[code] = $section;
-        return section_id;
     },
     do_fold: function() {
         this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
@@ -480,7 +712,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({
     }
 });
 
-openerp.web.TranslateDialog = openerp.web.Dialog.extend({
+session.web.TranslateDialog = session.web.Dialog.extend({
     dialog_title: _t("Translations"),
     init: function(view) {
         // TODO fme: should add the language to fields_view_get because between the fields view get
@@ -500,14 +732,14 @@ openerp.web.TranslateDialog = openerp.web.Dialog.extend({
         this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name });
         this.languages = null;
         this.languages_loaded = $.Deferred();
-        (new openerp.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
+        (new session.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
             [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded);
     },
     start: function() {
         var self = this;
         this._super();
         $.when(this.languages_loaded).then(function() {
-            self.$element.html(QWeb.render('TranslateDialog', { widget: self }));
+            self.$element.html(session.web.qweb.render('TranslateDialog', { widget: self }));
             self.$element.tabs();
             if (!(self.view.translatable_fields && self.view.translatable_fields.length)) {
                 self.hide_tabs('fields');
@@ -612,7 +844,8 @@ openerp.web.TranslateDialog = openerp.web.Dialog.extend({
     }
 });
 
-openerp.web.View = openerp.web.Widget.extend({
+session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
+    template: "EmptyComponent",
     set_default_options: function(options) {
         this.options = options || {};
         _.defaults(this.options, {
@@ -625,7 +858,7 @@ openerp.web.View = openerp.web.Widget.extend({
     },
     open_translate_dialog: function(field) {
         if (!this.translate_dialog) {
-            this.translate_dialog = new openerp.web.TranslateDialog(this).start();
+            this.translate_dialog = new session.web.TranslateDialog(this).start();
         }
         this.translate_dialog.open(field);
     },
@@ -637,35 +870,53 @@ openerp.web.View = openerp.web.Widget.extend({
      * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
      * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
      * @param {Object} [action_data.context=null] additional action context, to add to the current context
-     * @param {openerp.web.DataSet} dataset a dataset object used to communicate with the server
+     * @param {session.web.DataSet} dataset a dataset object used to communicate with the server
      * @param {Object} [record_id] the identifier of the object on which the action is to be applied
      * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
      */
-    execute_action: function (action_data, dataset, record_id, on_closed) {
+    do_execute_action: function (action_data, dataset, record_id, on_closed) {
         var self = this;
+        var result_handler = function () {
+            if (on_closed) { on_closed.apply(null, arguments); }
+            if (self.widget_parent && self.widget_parent.on_action_executed) {
+                return self.widget_parent.on_action_executed.apply(null, arguments);
+            }
+        };
+        var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {});
+
         var handler = function (r) {
             var action = r.result;
             if (action && action.constructor == Object) {
-                action.context = action.context || {};
-                _.extend(action.context, {
-                    active_id: record_id || false,
-                    active_ids: [record_id || false],
-                    active_model: dataset.model
-                });
-                self.do_action(action, on_closed);
-            } else if (on_closed) {
-                on_closed(action);
+                var ncontext = new session.web.CompoundContext(context);
+                if (record_id) {
+                    ncontext.add({
+                        active_id: record_id,
+                        active_ids: [record_id],
+                        active_model: dataset.model
+                    });
+                }
+                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);
+            } else {
+                return result_handler();
             }
         };
 
-        var context = new openerp.web.CompoundContext(dataset.get_context(), action_data.context || {});
-
         if (action_data.special) {
-            handler({result: {"type":"ir.actions.act_window_close"}});
+            return handler({result: {"type":"ir.actions.act_window_close"}});
         } else if (action_data.type=="object") {
             return dataset.call_button(action_data.name, [[record_id], context], handler);
         } else if (action_data.type=="action") {
-            return this.rpc('/web/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
+            return this.rpc('/web/action/load', { action_id: parseInt(action_data.name, 10), context: context, do_not_eval: true}, handler);
         } else  {
             return dataset.exec_workflow(record_id, action_data.name, handler);
         }
@@ -673,7 +924,7 @@ openerp.web.View = openerp.web.Widget.extend({
     /**
      * Directly set a view to use instead of calling fields_view_get. This method must
      * be called before start(). When an embedded view is set, underlying implementations
-     * of openerp.web.View must use the provided view instead of any other one.
+     * of session.web.View must use the provided view instead of any other one.
      *
      * @param embedded_view A view.
      */
@@ -683,60 +934,32 @@ openerp.web.View = openerp.web.Widget.extend({
     },
     do_switch_view: function(view) {
     },
+    do_search: function(view) {
+    },
+
     set_common_sidebar_sections: function(sidebar) {
-        sidebar.add_section('customize', "Customize", [
-            {
-                label: "Manage Views",
-                callback: this.on_sidebar_manage_view,
-                title: "Manage views of the current object"
-            }, {
-                label: "Edit Workflow",
-                callback: this.on_sidebar_edit_workflow,
-                title: "Manage views of the current object",
-                classname: 'oe_hide oe_sidebar_edit_workflow'
-            }, {
-                label: "Customize Object",
-                callback: this.on_sidebar_customize_object,
-                title: "Manage views of the current object"
-            }
-        ]);
-        sidebar.add_section('other', "Other Options", [
-            {
-                label: "Import",
-                callback: this.on_sidebar_import
-            }, {
-                label: "Export",
-                callback: this.on_sidebar_export
-            }, {
-                label: "Translate",
-                callback: this.on_sidebar_translate,
-                classname: 'oe_sidebar_translate oe_hide'
-            }, {
-                label: "View Log",
-                callback: this.on_sidebar_view_log,
-                classname: 'oe_hide oe_sidebar_view_log'
-            }
-        ]);
+        sidebar.add_default_sections();
     },
-    on_sidebar_manage_view: function() {
+    on_sidebar_manage_views: function() {
         if (this.fields_view && this.fields_view.arch) {
-            $('<xmp>' + openerp.web.json_node_to_xml(this.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600});
+            var view_editor = new session.web.ViewEditor(this, this.$element, this.dataset, this.fields_view.arch);
+            view_editor.start();
         } else {
-            this.notification.warn("Manage Views", "Could not find current view declaration");
+            this.do_warn("Manage Views", "Could not find current view declaration");
         }
     },
     on_sidebar_edit_workflow: function() {
-        this.log('Todo');
+        console.log('Todo');
     },
     on_sidebar_customize_object: function() {
-        this.log('Todo');
+        console.log('Todo');
     },
     on_sidebar_import: function() {
-        var import_view = new openerp.web.DataImport(this, this.dataset);
+        var import_view = new session.web.DataImport(this, this.dataset);
         import_view.start();
     },
     on_sidebar_export: function() {
-        var export_view = new openerp.web.DataExport(this, this.dataset);
+        var export_view = new session.web.DataExport(this, this.dataset);
         export_view.start();
     },
     on_sidebar_translate: function() {
@@ -746,24 +969,19 @@ openerp.web.View = openerp.web.Widget.extend({
     }
 });
 
-/**
- * Registry for all the main views
- */
-openerp.web.views = new openerp.web.Registry();
-
-openerp.web.json_node_to_xml = function(node, single_quote, indent) {
+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
+    indent = indent || 0;
+    var sindent = (human_readable ? (new Array(indent + 1).join('\t')) : ''),
+        r = sindent + '<' + node.tag,
+        cr = human_readable ? '\n' : '';
 
     if (typeof(node) === 'string') {
-        return node;
-    }
-    else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
+        return sindent + node;
+    } else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
         throw("Node a json node");
     }
-    indent = indent || 0;
-    var sindent = new Array(indent + 1).join('\t'),
-        r = sindent + '<' + node.tag;
     for (var attr in node.attrs) {
         var vattr = node.attrs[attr];
         if (typeof(vattr) !== 'string') {
@@ -771,19 +989,19 @@ openerp.web.json_node_to_xml = function(node, single_quote, indent) {
             vattr = JSON.stringify(vattr);
         }
         vattr = vattr.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
-        if (single_quote) {
+        if (human_readable) {
             vattr = vattr.replace(/&quot;/g, "'");
         }
         r += ' ' + attr + '="' + vattr + '"';
     }
     if (node.children && node.children.length) {
-        r += '>\n';
+        r += '>' + cr;
         var childs = [];
         for (var i = 0, ii = node.children.length; i < ii; i++) {
-            childs.push(openerp.web.json_node_to_xml(node.children[i], single_quote, indent + 1));
+            childs.push(session.web.json_node_to_xml(node.children[i], human_readable, indent + 1));
         }
-        r += childs.join('\n');
-        r += '\n' + sindent + '</' + node.tag + '>';
+        r += childs.join(cr);
+        r += cr + sindent + '</' + node.tag + '>';
         return r;
     } else {
         return r + '/>';