X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fweb%2Fstatic%2Fsrc%2Fjs%2Fviews.js;h=79d3d4cd16733e0292993ba0dcf9e03d0f98b33f;hb=4ecd07837f5f28d295efd5fe13b6643f863851fc;hp=778bf6d47df3e797625b36a05f048ef114d287ff;hpb=7e652a5918985d4a05412f2cb7406878f3a8be46;p=odoo%2Fodoo.git diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 778bf6d..79d3d4c 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -6,18 +6,7 @@ openerp.web.views = function(session) { var QWeb = session.web.qweb, _t = session.web._t; -/** - * Registry for all the client actions key: tag value: widget - */ -session.web.client_actions = new session.web.Registry(); - -/** - * 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); this.inner_action = null; @@ -26,40 +15,36 @@ session.web.ActionManager = session.web.Widget.extend({ this.dialog_viewmanager = null; this.client_widget = null; }, - render: function() { - return '
'; - }, dialog_stop: function () { if (this.dialog) { - this.dialog_viewmanager.stop(); + this.dialog_viewmanager.destroy(); this.dialog_viewmanager = null; - this.dialog.stop(); + this.dialog.destroy(); this.dialog = null; } }, content_stop: function () { if (this.inner_viewmanager) { - this.inner_viewmanager.stop(); + this.inner_viewmanager.destroy(); this.inner_viewmanager = null; } if (this.client_widget) { - this.client_widget.stop(); + this.client_widget.destroy(); this.client_widget = null; } }, do_push_state: function(state) { - if (this.widget_parent && this.widget_parent.do_push_state) { + if (this.getParent() && this.getParent().do_push_state) { if (this.inner_action) { + state['model'] = this.inner_action.res_model; if (this.inner_action.id) { state['action_id'] = this.inner_action.id; - } else { - state['model'] = this.inner_action.res_model; } } - this.widget_parent.do_push_state(state); + this.getParent().do_push_state(state); } }, - do_load_state: function(state) { + do_load_state: function(state, warm) { var self = this, action_loaded; if (state.action_id) { @@ -67,27 +52,37 @@ session.web.ActionManager = session.web.Widget.extend({ if (run_action) { this.null_action(); action_loaded = this.do_action(state.action_id); + session.webclient.menu.has_been_loaded.then(function() { + session.webclient.menu.open_action(state.action_id); + }); } - } - else if (state.model && state.id) { + } else if (state.model && state.id) { // TODO handle context & domain ? this.null_action(); var action = { res_model: state.model, res_id: state.id, type: 'ir.actions.act_window', - views: [[false, 'page'], [false, 'form']] + views: [[false, 'form']] }; action_loaded = this.do_action(action); - } - else if (state.client_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); } $.when(action_loaded || null).then(function() { if (self.inner_viewmanager) { - self.inner_viewmanager.do_load_state(state); + self.inner_viewmanager.do_load_state(state, warm); } }); }, @@ -104,12 +99,13 @@ session.web.ActionManager = session.web.Widget.extend({ } var type = action.type.replace(/\./g,'_'); var popup = action.target === 'new'; + var inline = action.target === 'inline'; action.flags = _.extend({ - views_switcher : !popup, - search_view : !popup, - action_buttons : !popup, - sidebar : !popup, - pager : !popup, + views_switcher : !popup && !inline, + search_view : !popup && !inline, + action_buttons : !popup && !inline, + sidebar : !popup && !inline, + pager : !popup && !inline, display_title : !popup }, action.flags || {}); if (!(type in this)) { @@ -128,8 +124,7 @@ 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') { @@ -137,9 +132,8 @@ session.web.ActionManager = session.web.Widget.extend({ 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_viewmanager.destroy(); } this.dialog.dialog_title = action.name; this.dialog_viewmanager = new session.web.ViewManagerAction(this, action); @@ -147,7 +141,7 @@ session.web.ActionManager = session.web.Widget.extend({ this.dialog.open(); } else { if(action.menu_id) { - return this.widget_parent.do_action(action, function () { + return this.getParent().do_action(action, function () { session.webclient.menu.open_menu(action.menu_id); }); } @@ -177,7 +171,7 @@ session.web.ActionManager = session.web.Widget.extend({ this.content_stop(); this.dialog_stop(); var ClientWidget = session.web.client_actions.get_object(action.tag); - (this.client_widget = new ClientWidget(this, action.params)).appendTo(this); + (this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element); }, ir_actions_report_xml: function(action, on_closed) { var self = this; @@ -197,7 +191,8 @@ session.web.ActionManager = session.web.Widget.extend({ on_closed(); } self.dialog_stop(); - } + }, + error: session.webclient.crashmanager.on_rpc_error }) }); }, @@ -205,21 +200,12 @@ session.web.ActionManager = session.web.Widget.extend({ window.open(action.url, action.target === 'self' ? '_self' : '_blank'); }, ir_ui_menu: function (action) { - this.widget_parent.do_action(action); + this.getParent().do_action(action); } }); -session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.ViewManager# */{ - identifier_prefix: "viewmanager", +session.web.ViewManager = session.web.Widget.extend({ template: "ViewManager", - /** - * @constructs session.web.ViewManager - * @extends session.web.Widget - * - * @param parent - * @param dataset - * @param views - */ init: function(parent, dataset, views, flags) { this._super(parent); this.model = dataset ? dataset.model : undefined; @@ -243,19 +229,13 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View this.registry = session.web.views; this.views_history = []; }, - render: function() { - return session.web.qweb.render(this.template, { - self: this, - prefix: this.element_id, - views: this.views_src}); - }, /** * @returns {jQuery.Deferred} initial view loading promise */ start: function() { this._super(); var self = this; - this.$element.find('.oe_vm_switch button').click(function() { + this.$element.find('.oe_view_manager_switch a').click(function() { self.on_mode_switch($(this).data('view-type')); }); var views_ids = {}; @@ -264,7 +244,9 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View deferred : $.Deferred(), controller : null, options : _.extend({ - sidebar_id : self.element_id + '_sidebar_' + view.view_type, + $buttons : self.$element.find('.oe_view_manager_buttons'), + $sidebar : self.$element.find('.oe_view_manager_sidebar'), + $pager : self.$element.find('.oe_view_manager_pager'), action : self.action, action_views_ids : views_ids }, self.flags, self.flags[view.view_type] || {}, view.options || {}) @@ -272,7 +254,7 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View views_ids[view.view_type] = view.view_id; }); if (this.flags.views_switcher === false) { - this.$element.find('.oe_vm_switch').hide(); + this.$element.find('.oe_view_manager_switch').hide(); } // If no default view defined, switch to the first one in sequence var default_view = this.flags.default_view || this.views_src[0].view_type; @@ -286,9 +268,9 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View * @returns {jQuery.Deferred} new view loading promise */ on_mode_switch: function(view_type, no_store) { - var self = this, - view = this.views[view_type], - view_promise; + var self = this; + var view = this.views[view_type]; + var view_promise; if(!view) return $.Deferred().reject(); @@ -300,23 +282,36 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View if (!view.controller) { // Lazy loading of views var controllerclass = this.registry.get_object(view_type); - var controller = new controllerclass(this, this.dataset, view.view_id, view.options); + var options = _.clone(view.options); + if (view_type === "form") { + switch (this.action.target) { + case 'new': + case 'inline': + options.initial_mode = 'edit'; + break; + } + } + var controller = new controllerclass(this, this.dataset, view.view_id, options); if (view.embedded_view) { controller.set_embedded_view(view.embedded_view); } controller.do_switch_view.add_last(this.on_mode_switch); controller.do_prev_view.add_last(this.on_prev_view); - var container = $("#" + this.element_id + '_view_' + view_type); + var container = this.$element.find(".oe_view_manager_view_" + view_type); view_promise = controller.appendTo(container); this.views[view_type].controller = controller; 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); } @@ -325,9 +320,10 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View } this.$element - .find('.oe_vm_switch button').removeAttr('disabled') - .filter('[data-view-type="' + view_type + '"]') - .attr('disabled', true); + .find('.oe_view_manager_switch a').parent().removeClass('active') + this.$element + .find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]') + .parent().addClass('active'); $.when(view_promise).then(function () { _.each(_.keys(self.views), function(view_name) { @@ -351,19 +347,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) { var current_view = this.views_history.pop(); - var previous_view = this.views_history[this.views_history.length - 1]; - 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 (created && !previous_view && this.action && this.action.flags.default_view === 'form') { + return this.on_mode_switch('form'); + } 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('form'); } return this.on_mode_switch(previous_view, true); }, @@ -376,14 +374,12 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View setup_search_view: function(view_id, search_defaults) { var self = this; if (this.searchview) { - this.searchview.stop(); + this.searchview.destroy(); } - this.searchview = new session.web.SearchView( - this, this.dataset, - view_id, search_defaults, this.flags.search_view === false); + 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")); + return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search")); }, do_searchview_search: function(domains, contexts, groupbys) { var self = this, @@ -399,6 +395,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 || []); }); }, @@ -436,7 +435,7 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View } }); -session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{ +session.web.ViewManagerAction = session.web.ViewManager.extend({ template:"ViewManagerAction", /** * @constructs session.web.ViewManagerAction @@ -450,6 +449,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, { @@ -519,7 +521,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; @@ -529,55 +531,80 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } } - var $res_logs = this.$element.find('.oe-view-manager-logs:first'); - $res_logs.delegate('a.oe-more-logs', 'click', function () { - $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.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': - $('
').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();
+                $('
').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,
-                            form : {
-                                resize_textareas : 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();
+                    });
+                }
+                break;
+            case 'toggle_layout_outline':
+                current_view.rendering_engine.toggle_layout_debugging();
+                break;
+            case 'fields':
+                this.dataset.call_and_eval(
+                        'fields_get', [false, {}], null, 1).then(function (fields) {
+                    var $root = $('
'); + _(fields).each(function (attributes, name) { + $root.append($('
').append($('

').text(name))); + var $attrs = $('
').appendTo( + $('
').appendTo($root)); + _(attributes).each(function (def, name) { + if (def instanceof Object) { + def = JSON.stringify(def); } - } - }; - 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; + $attrs + .append($('
').text(name)) + .append($('
').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")); } - this.do_action(action); + 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) { @@ -586,14 +613,30 @@ 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').css('display','none').find('ul').empty(); - var controller = self.views[self.active_view].controller, fvg = controller.fields_view, view_id = (fvg && fvg.view_id) || '--'; @@ -617,12 +660,12 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner }); }, do_push_state: function(state) { - if (this.widget_parent && this.widget_parent.do_push_state) { + if (this.getParent() && this.getParent().do_push_state) { state["view_type"] = this.active_view; - this.widget_parent.do_push_state(state); + this.getParent().do_push_state(state); } }, - do_load_state: function(state) { + do_load_state: function(state, warm) { var self = this, defs = []; if (state.view_type && state.view_type !== this.active_view) { @@ -634,12 +677,12 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } $.when(defs).then(function() { - self.views[self.active_view].controller.do_load_state(state); + self.views[self.active_view].controller.do_load_state(state, warm); }); }, shortcut_check : function(view) { var self = this; - var grandparent = this.widget_parent && this.widget_parent.widget_parent; + var grandparent = this.getParent() && this.getParent().getParent(); // display shortcuts if on the first view for the action var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle'); if (!this.action.name || @@ -675,194 +718,127 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } }); }, - /** - * 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} log_records - */ - do_display_log: function (log_records) { - 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) { - $(_.str.sprintf('
  • %s
  • ', record.name)) - .appendTo($logs_list) - .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; } }); session.web.Sidebar = session.web.Widget.extend({ - init: function(parent, element_id) { - this._super(parent, element_id); - this.items = {}; - this.sections = {}; + init: function(parent) { + this._super(parent); + var view = this.getParent(); + this.sections = [ + { 'name' : 'print', 'label' : _t('Print'), }, + { 'name' : 'files', 'label' : _t('Attachement'), }, + { 'name' : 'other', 'label' : _t('More'), } + ]; + this.items = { + 'print' : [], + 'files' : [], + 'other' : [ + { label: _t("Import"), callback: view.on_sidebar_import }, + { label: _t("Export"), callback: view.on_sidebar_export } + ] + } + if (this.session.uid === 1) { + var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") }; + this.items.other.push(item); + } }, start: function() { - this._super(this); var self = this; - this.$element.html(session.web.qweb.render('Sidebar')); - this.$element.find(".toggle-sidebar").click(function(e) { - self.do_toggle(); + this._super(this); + this.redraw(); + this.$element.on('click','.oe_dropdown_toggle',function(event) { + $(this).parent().find('ul').toggle(); + return false; }); - }, - add_default_sections: function() { - var self = this, - view = this.widget_parent, - view_manager = view.widget_parent, - 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_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_section(_t('Other Options'), 'other'); - this.add_items('other', [ - { - label: _t("Import"), - callback: view.on_sidebar_import - }, { - 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' + this.$element.on('click','.oe_dropdown_menu li a', function(event) { + var section = $(this).data('section'); + var index = $(this).data('index'); + $(this).closest('ul').hide(); + var item = self.items[section][index]; + if (item.callback) { + item.callback.apply(self, [item]); + } else if (item.action) { + self.on_item_action_clicked(item); + } else if (item.url) { + return true; } - ]); + return false; + }); + //this.$div.html(QWeb.render('FormView.sidebar.attachments', this)); + //this.$element.find('.oe-binary-file').change(this.on_attachment_changed); + //this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete); + }, + redraw: function() { + var self = this; + self.$element.html(QWeb.render('Sidebar', {widget: self})); + this.$element.find('ul').hide(); + }, + add_section: function() { + var self = this; }, - add_toolbar: function(toolbar) { var self = this; - _.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) { - var items = toolbar[type[0]]; - if (items.length) { + _.each(['print','action','relate'], function(type) { + var items = toolbar[type]; + if (items) { for (var i = 0; i < items.length; i++) { items[i] = { label: items[i]['name'], action: items[i], - classname: 'oe_sidebar_' + type[0] + classname: 'oe_sidebar_' + type } } - self.add_section(type[1], type[0]); - self.add_items(type[0], items); + self.add_items(type=='print' ? 'print' : 'other', items); } }); }, - - add_section: function(name, code) { - if(!code) code = _.str.underscored(name); - var $section = this.sections[code]; - - if(!$section) { - var section_id = _.uniqueId(this.element_id + '_section_' + code + '_'); - $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; - }, - + /** + * For each item added to the section: + * + * ``label`` + * will be used as the item's name in the sidebar, can be html + * + * ``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'); - + var self = this; 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 $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.apply(self, [item]); - } - if (item.action) { - self.on_item_action_clicked(item); - } - return false; - }); - - var $ul = $section.find('ul'); - if(!$ul.length) { - $ul = $('
      ').appendTo($section); - } - $items.appendTo($ul); + this.items[section_code].push.apply(this.items[section_code],items); + this.redraw(); } }, on_item_action_clicked: function(item) { var self = this; - self.widget_parent.sidebar_context().then(function (context) { - var ids = self.widget_parent.get_selected_ids(); + self.getParent().sidebar_context().then(function (context) { + var ids = self.getParent().get_selected_ids(); if (ids.length == 0) { - //TODO: make prettier warning? - $("
      ").text(_t("You must choose at least one record.")).dialog({ - title: _t("Warning"), - modal: true - }); + session.web.dialog($("
      ").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true }); return false; } var additional_context = _.extend({ active_id: ids[0], active_ids: ids, - active_model: self.widget_parent.dataset.model + active_model: self.getParent().dataset.model }, context); self.rpc("/web/action/load", { action_id: item.action.id, @@ -874,19 +850,61 @@ session.web.Sidebar = session.web.Widget.extend({ result.result.flags.new_window = true; self.do_action(result.result, function () { // reload view - self.widget_parent.reload(); + self.getParent().reload(); }); }); }); }, - do_fold: function() { - this.$element.addClass('closed-sidebar').removeClass('open-sidebar'); + do_attachement_update: function(dataset, model_id) { + 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 session.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom); + ds.read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded); + } }, - do_unfold: function() { - this.$element.addClass('open-sidebar').removeClass('closed-sidebar'); + on_attachments_loaded: function(attachments) { + var self = this; + var items = []; + // TODO: preprend: _s + + var prefix = '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id='; + _.each(attachments,function(a) { + a.label = a.name; + if(a.type === "binary") { + a.url = prefix + a.id + '&t=' + (new Date().getTime()); + } + }); + attachments.push( { label: _t("Add..."), callback: self.on_attachment_add } ); + self.items['files'] = attachments; + self.redraw(); + }, + on_attachment_add: function(e) { + this.$element.find('.oe_sidebar_add').show(); + }, + on_attachment_changed: function(e) { + return; + window[this.element_id + '_iframe'] = this.do_update; + var $e = $(e.target); + if ($e.val() != '') { + this.$element.find('form.oe-binary-form').submit(); + $e.parent().find('input[type=file]').prop('disabled', true); + $e.parent().find('button').prop('disabled', true).find('img, span').toggle(); + } }, - do_toggle: function() { - this.$element.toggleClass('open-sidebar closed-sidebar'); + on_attachment_delete: function(e) { + return; + var self = this, $e = $(e.currentTarget); + var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text()); + if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) { + this.rpc('/web/dataset/unlink', { + model: 'ir.attachment', + ids: [parseInt($e.attr('data-id'))] + }, function(r) { + $e.parent().remove(); + self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted"); + }); + } } }); @@ -911,7 +929,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; @@ -980,7 +998,8 @@ session.web.TranslateDialog = session.web.Dialog.extend({ }, on_button_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]]) { @@ -993,9 +1012,10 @@ 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(); }, @@ -1004,14 +1024,53 @@ session.web.TranslateDialog = session.web.Dialog.extend({ } }); -session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ +session.web.View = session.web.Widget.extend({ template: "EmptyComponent", // name displayed in view switchers display_name: '', + /** + * Define a view type for each view to allow automatic call to fields_view_get. + */ + view_type: undefined, + init: function(parent, dataset, view_id, options) { + this._super(parent); + this.dataset = dataset; + this.view_id = view_id; + this.set_default_options(options); + }, + start: function() { + return this.load_view(); + }, + load_view: function() { + 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); + } else { + var context = new session.web.CompoundContext(this.dataset.get_context()); + if (! this.view_type) + console.warn("view_type is not defined", this); + return 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); + } + }, + /** + * Called after a successful call to fields_view_get. + * Must return a promise. + */ + on_loaded: function(fields_view_get) { + }, set_default_options: function(options) { this.options = options || {}; _.defaults(this.options, { // All possible views options should be defaulted here + $sidebar: null, sidebar_id: null, sidebar: true, action: null, @@ -1040,8 +1099,8 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ 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); + if (self.getParent() && self.getParent().on_action_executed) { + return self.getParent().on_action_executed.apply(null, arguments); } }; var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {}); @@ -1113,11 +1172,11 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ this.$element.hide(); }, do_push_state: function(state) { - if (this.widget_parent && this.widget_parent.do_push_state) { - this.widget_parent.do_push_state(state); + if (this.getParent() && this.getParent().do_push_state) { + this.getParent().do_push_state(state); } }, - do_load_state: function(state) { + do_load_state: function(state, warm) { }, /** * Switches to a specific view type @@ -1128,42 +1187,15 @@ 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() { - return this.do_action({ - res_model : 'workflow', - domain : [['osv', '=', this.dataset.model]], - views: [[false, 'list'], [false, 'form']], - type : 'ir.actions.act_window', - view_type : "list", - view_mode : "list" - }); - }, - 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(); @@ -1182,29 +1214,6 @@ 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 $.when(); }, @@ -1217,9 +1226,31 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ } }); +session.web.xml_to_json = function(node) { + switch (node.nodeType) { + case 3: + case 4: + return node.data; + break; + case 1: + var attrs = $(node).getAttributes(); + _.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) { + if (attrs[key]) { + try { + attrs[key] = JSON.parse(attrs[key]); + } catch(e) { } + } + }); + return { + tag: node.tagName.toLowerCase(), + attrs: attrs, + children: _.map(node.childNodes, session.web.xml_to_json) + } + } +} 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, @@ -1255,6 +1286,43 @@ session.web.json_node_to_xml = function(node, human_readable, indent) { return r + '/>'; } } +session.web.xml_to_str = function(node) { + if (window.ActiveXObject) { + return node.xml; + } else { + return (new XMLSerializer()).serializeToString(node); + } +} +session.web.str_to_xml = function(s) { + if (window.DOMParser) { + var dp = new DOMParser(); + var r = dp.parseFromString(s, "text/xml"); + if (r.body && r.body.firstChild && r.body.firstChild.nodeName == 'parsererror') { + throw new Error("Could not parse string to xml"); + } + return r; + } + var xDoc; + try { + xDoc = new ActiveXObject("MSXML2.DOMDocument"); + } catch (e) { + throw new Error("Could not find a DOM Parser: " + e.message); + } + xDoc.async = false; + xDoc.preserveWhiteSpace = true; + xDoc.loadXML(s); + return xDoc; +} + +/** + * Registry for all the client actions key: tag value: widget + */ +session.web.client_actions = new session.web.Registry(); + +/** + * Registry for all the main views + */ +session.web.views = new session.web.Registry(); };