diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 6eb8ed94909..1ccba0c9867 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1467,6 +1467,10 @@ class Binary(openerpweb.Controller): class Action(openerpweb.Controller): _cp_path = "/web/action" + + action_mapping = { + "ir.actions.act_url": "ir.actions.url", + } @openerpweb.jsonrequest def load(self, req, action_id, do_not_eval=False): @@ -1479,7 +1483,9 @@ class Action(openerpweb.Controller): if action_type[0]['type'] == 'ir.actions.report.xml': ctx.update({'bin_size': True}) ctx.update(context) - action = req.session.model(action_type[0]['type']).read([action_id], False, ctx) + action_model = action_type[0]['type'] + action_model = Action.action_mapping.get(action_model, action_model) + action = req.session.model(action_model).read([action_id], False, ctx) if action: value = clean_action(req, action[0], do_not_eval) return {'result': value} diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 2988c0aa634..5ef56c69797 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1629,7 +1629,7 @@ font-weight: bold; color: #b33630; } -.openerp .oe_form_readonly header.oe_form_topbar_hifirst button:first-child, .openerp .oe_form_readonly button.oe_form_button_hi { +.openerp .oe_form_readonly button.oe_form_button_hi { color: white; background: #dc5f59; background: -moz-linear-gradient(#dc5f59, #b33630); @@ -1639,7 +1639,7 @@ -webkit-box-shadow: none; -box-shadow: none; } -.openerp .oe_form_readonly header.oe_form_topbar_hifirst button:first-child:hover, .openerp .oe_form_readonly button.oe_form_button_hi:hover { +.openerp .oe_form_readonly button.oe_form_button_hi:hover { background: #df6b66; background: -moz-linear-gradient(#df6b66, #bf3a33); background: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33)); @@ -1667,13 +1667,13 @@ -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); -box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); } +.openerp .oe_form_invisible { + display: none; +} .openerp .oe_form_editable .oe_form_editable_hidden { display: none; } -.openerp .oe_form_readonly .oe_form_readonly_hidden { - display: none; -} -.openerp .oe_form_readonly .oe_form_field:empty { +.openerp .oe_form_readonly .oe_form_readonly_hidden, .openerp .oe_form_readonly .oe_form_field:empty { display: none; } .openerp .oe_form .oe_form_field_text { @@ -1723,7 +1723,7 @@ } .openerp .oe_form .oe_form_field { width: 100%; - display: inline-block !important; + display: inline-block; padding: 2px 2px 2px 0px; line-height: 18px; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index df40fc8d151..c4d9d565b8b 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1287,7 +1287,7 @@ $colour4: #8a89ba font-weight: bold color: #b33630 .oe_form_readonly - header.oe_form_topbar_hifirst button:first-child, button.oe_form_button_hi + button.oe_form_button_hi color: white background: #DC5F59 background: -moz-linear-gradient(#DC5F59, #B33630) @@ -1296,7 +1296,7 @@ $colour4: #8a89ba -moz-box-shadow: none -webkit-box-shadow: none -box-shadow: none - header.oe_form_topbar_hifirst button:first-child:hover, button.oe_form_button_hi:hover + button.oe_form_button_hi:hover background: #DF6B66 background: -moz-linear-gradient( #DF6B66, #BF3A33) background: -webkit-gradient(linear, left top, left bottom, from( #DF6B66), to( #BF3A33)) @@ -1321,12 +1321,13 @@ $colour4: #8a89ba @include box-shadow(0 0 10px rgba(0,0,0,0.3)) // }}} // FormView.layout {{{ + .oe_form_invisible + display: none .oe_form_editable .oe_form_editable_hidden display: none - .oe_form_readonly .oe_form_readonly_hidden - display: none - .oe_form_readonly .oe_form_field:empty - display: none + .oe_form_readonly + .oe_form_readonly_hidden, .oe_form_field:empty + display: none .oe_form .oe_form_field_text width: 100% @@ -1376,8 +1377,7 @@ $colour4: #8a89ba resize: vertical .oe_form_field width: 100% - // Using !important because jQuery's $.fn.show() will set display to 'inline' for spans - display: inline-block !important + display: inline-block padding: 2px 2px 2px 0px line-height: 18px input diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 43094a97e66..64381a66218 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -208,8 +208,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form. } } }, - do_show: function () { + do_show: function (options) { var self = this; + options = options || {}; if (this.sidebar) { this.sidebar.$element.show(); } @@ -233,6 +234,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form. }).pipe(self.on_record_loaded); } result.pipe(function() { + if (options.editable) { + self.set({mode: "edit"}); + } self.$element.css('visibility', 'visible'); }); return result; @@ -495,7 +499,8 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form. this.on_form_changed(); } if (!_.isEmpty(result.warning)) { - instance.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), { + instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), { + title:result.warning.title, modal: true, buttons: [ {text: _t("Ok"), click: function() { $(this).dialog("close"); }} @@ -642,6 +647,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form. } if (form_invalid) { self.set({'display_invalid_fields': true}); + for (var f in self.fields) { + self.fields[f]._check_css_flags(); + } first_invalid_field.focus(); self.on_invalid(); return $.Deferred().reject(); @@ -1191,19 +1199,40 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt var $page = $(this); var page_attrs = $page.getAttributes(); page_attrs.id = _.uniqueId('notebook_page_'); - pages.push(page_attrs); var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs); $page.contents().appendTo($new_page); $page.before($new_page).remove(); - self.handle_common_properties($new_page, $page); + var ic = self.handle_common_properties($new_page, $page).invisibility_changer; + page_attrs.__page = $new_page; + page_attrs.__ic = ic; + pages.push(page_attrs); + + $new_page.children().each(function() { + self.process($(this)); + }); }); var $new_notebook = this.render_element('FormRenderingNotebook', { pages : pages }); $notebook.contents().appendTo($new_notebook); $notebook.before($new_notebook).remove(); - $new_notebook.children().each(function() { - self.process($(this)); - }); + self.process($($new_notebook.children()[0])); + //tabs and invisibility handling $new_notebook.tabs(); + _.each(pages, function(page, i) { + if (! page.__ic) + return; + page.__ic.on("change:effective_invisible", null, function() { + var current = $new_notebook.tabs("option", "selected"); + if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible")) + return; + var first_visible = _.find(_.range(pages.length), function(i2) { + return (! pages[i2].__ic) || (! pages[i2].__ic.get("effective_invisible")); + }); + if (first_visible !== undefined) { + $new_notebook.tabs('select', first_visible); + } + }); + }); + this.handle_common_properties($new_notebook, $notebook); return $new_notebook; }, @@ -1241,10 +1270,12 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt handle_common_properties: function($new_element, $node) { var str_modifiers = $node.attr("modifiers") || "{}" var modifiers = JSON.parse(str_modifiers); + var ic = null; if (modifiers.invisible !== undefined) - new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element); + ic = new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element); $new_element.addClass($node.attr("class") || ""); $new_element.attr('style', $node.attr('style')); + return {invisibility_changer: ic,}; }, }); @@ -1386,11 +1417,7 @@ instance.web.form.InvisibilityChangerMixin = { this._check_visibility(); }, _check_visibility: function() { - if (this.get("effective_invisible")) { - this.$element.hide(); - } else { - this.$element.show(); - } + this.$element.toggleClass('oe_form_invisible', this.get("effective_invisible")); }, }; @@ -1879,7 +1906,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({} return true; }, is_false: function() { - return this.get('value') === ''; + return this.get('value') === '' || this._super(); }, focus: function() { this.delay_focus(this.$element.find('input:first')); @@ -2088,7 +2115,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.exten return true; }, is_false: function() { - return this.get('value') === ''; + return this.get('value') === '' || this._super(); }, focus: function() { if (this.datewidget && this.datewidget.$input) @@ -2138,7 +2165,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({} return true; }, is_false: function() { - return this.get('value') === ''; + return this.get('value') === '' || this._super(); }, focus: function($element) { this.delay_focus(this.$textarea); @@ -3267,6 +3294,9 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ self.reload_content(); this.is_setted.resolve(); }, + get_value: function() { + return [commands.replace_with(this.get('value'))]; + }, load_view: function() { var self = this; this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, { @@ -3296,7 +3326,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ }); }, dataset_changed: function() { - this.set({'value': [commands.replace_with(this.dataset.ids)]}); + this.set({'value': this.dataset.ids}); }, }); @@ -3751,7 +3781,8 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend self.view_list = new instance.web.form.SelectCreateListView(self, self.dataset, false, _.extend({'deletable': false, - 'selectable': !self.options.disable_multiple_selection + 'selectable': !self.options.disable_multiple_selection, + 'read_only': true, }, self.options.list_view_options || {})); self.view_list.popup = self; self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() { diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 081c4444a72..de73ec21884 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -21,6 +21,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi // whether the view rows can be reordered (via vertical drag & drop) 'reorderable': true, 'action_buttons': true, + // if true, the view can't be editable, ignoring the view's and the context's + // instructions + 'read_only': false, }, /** * Core class for list-type displays. @@ -339,6 +342,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.sidebar = new instance.web.Sidebar(this); this.sidebar.appendTo(this.options.$sidebar); this.sidebar.add_items('other', [ + { label: _t("Import"), callback: this.on_sidebar_import }, + { label: _t("Export"), callback: this.on_sidebar_export }, { label: _t('Delete'), callback: this.do_delete_selected }, ]); this.sidebar.add_toolbar(this.fields_view.toolbar); diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 84074c17e5e..c80509a7b2b 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -54,8 +54,7 @@ openerp.web.list_editable = function (instance) { // view' @editable is handled separately as we have not yet // fetched and processed the view at this point. this.options.editable = ( - (force && "bottom") - || this.defaults.editable); + ! this.options.read_only && ((force && "bottom") || this.defaults.editable)); }, /** * Replace do_search to handle editability process @@ -79,7 +78,7 @@ openerp.web.list_editable = function (instance) { }, on_loaded: function (data, grouped) { // tree/@editable takes priority on everything else if present. - this.options.editable = data.arch.attrs.editable || this.options.editable; + this.options.editable = ! this.options.read_only && (data.arch.attrs.editable || this.options.editable); return this._super(data, grouped); }, /** diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index b97ed94990b..753e14fa251 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -267,7 +267,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ * @param {Boolean} [no_store=false] don't store the view being switched to on the switch stack * @returns {jQuery.Deferred} new view loading promise */ - on_mode_switch: function(view_type, no_store) { + on_mode_switch: function(view_type, no_store, view_options) { var self = this; var view = this.views[view_type]; var view_promise; @@ -330,7 +330,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ var controller = self.views[view_name].controller; if (controller) { if (view_name === view_type) { - controller.do_show(); + controller.do_show(view_options || {}); } else { controller.do_hide(); } @@ -347,8 +347,8 @@ instance.web.ViewManager = instance.web.Widget.extend({ * to be extended by child classes to change the default behavior, which simply * consist to switch to the asked view. */ - switch_view: function(view_type, no_store) { - return this.on_mode_switch(view_type, no_store); + switch_view: function(view_type, no_store, options) { + return this.on_mode_switch(view_type, no_store, options); }, /** * Returns to the view preceding the caller view in this manager's @@ -651,10 +651,10 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({ }, action || {}); this.do_action(action); }, - on_mode_switch: function (view_type, no_store) { + on_mode_switch: function (view_type, no_store, options) { var self = this; - return $.when(this._super(view_type, no_store)).then(function () { + return $.when(this._super.apply(this, arguments)).then(function () { var controller = self.views[self.active_view].controller, fvg = controller.fields_view, view_id = (fvg && fvg.view_id) || '--'; @@ -706,11 +706,8 @@ instance.web.Sidebar = instance.web.Widget.extend({ this.items = { 'print' : [], 'files' : [], - 'other' : [ - { label: _t("Import"), callback: view.on_sidebar_import }, - { label: _t("Export"), callback: view.on_sidebar_export } - ] - } + 'other' : [] + }; if (this.session.uid === 1) { var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") }; this.items.other.push(item); @@ -746,6 +743,11 @@ instance.web.Sidebar = instance.web.Widget.extend({ var self = this; self.$element.html(QWeb.render('Sidebar', {widget: self})); this.$element.find('ul').hide(); + + // Hides Sidebar sections when item list is empty + this.$('.oe_form_dropdown_section').each(function() { + $(this).toggle(!!$(this).find('li').length); + }); }, /** * For each item added to the section: diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index a1c7a2c9720..3764395dc13 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -424,7 +424,7 @@ diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 4d3f09cec71..7e3d8c10e6b 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -109,11 +109,23 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ } }, transform_qweb_template: function(node) { - var qweb_prefix = QWeb.prefix; + var qweb_add_if = function(node, condition) { + if (node.attrs[QWeb.prefix + '-if']) { + condition = _.str.sprintf("(%s) and (%s)", node.attrs[QWeb.prefix + '-if'], condition); + } + node.attrs[QWeb.prefix + '-if'] = condition; + }; + // Process modifiers + if (node.tag && node.attrs.modifiers) { + var modifiers = JSON.parse(node.attrs.modifiers || '{}'); + if (modifiers.invisible) { + qweb_add_if(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible))); + } + } switch (node.tag) { case 'field': - node.tag = qweb_prefix; - node.attrs[qweb_prefix + '-esc'] = 'record.' + node.attrs['name'] + '.value'; + node.tag = QWeb.prefix; + node.attrs[QWeb.prefix + '-esc'] = 'record.' + node.attrs['name'] + '.value'; this.extract_aggregates(node); break; case 'button': @@ -126,18 +138,6 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ delete(node.attrs[k]); } }); - if (node.attrs['data-states']) { - var states = _.map(node.attrs['data-states'].split(','), function(state) { - return "record.state.raw_value == '" + _.str.trim(state) + "'"; - }); - node.attrs[qweb_prefix + '-if'] = states.join(' or '); - } - if (node.attrs['data-kanban_states']) { - var states = _.map(node.attrs['data-kanban_states'].split(','), function(state) { - return "record.kanban_state.raw_value == '" + _.str.trim(state) + "'"; - }); - node.attrs[qweb_prefix + '-if'] = states.join(' or '); - } if (node.attrs['data-string']) { node.attrs.title = node.attrs['data-string']; } @@ -325,7 +325,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }, open_record: function(id) { if (this.dataset.select_id(id)) { - this.do_switch_view('form'); + this.do_switch_view('form', null, { editable: true }); } else { this.do_warn("Kanban: could not find id#" + id); } @@ -530,7 +530,14 @@ instance.web_kanban.KanbanRecord = instance.web.OldWidget.extend({ this.state = this.view.state.records[this.id]; }, set_record: function(record) { + var self = this; this.id = record.id; + this.values = {}; + _.each(record, function(v, k) { + self.values[k] = { + value: v + }; + }); this.record = this.transform_record(record); }, start: function() { @@ -751,6 +758,9 @@ instance.web_kanban.KanbanRecord = instance.web.OldWidget.extend({ } else { return s.substr(0, size) + '...'; } + }, + kanban_compute_domain: function(domain) { + return instance.web.form.compute_domain(domain, this.values); } });