diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 8722fcd2887..c3bf6aff750 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -811,9 +811,8 @@ class Session(openerpweb.Controller): @openerpweb.jsonrequest def modules(self, req): - loaded = module_boot(req) - modules = module_installed(req) - return [module for module in modules if module not in loaded] + # return all installed modules. Web client is smart enough to not load a module twice + return module_installed(req) @openerpweb.jsonrequest def eval_domain_and_context(self, req, contexts, domains, @@ -1142,6 +1141,15 @@ class DataSet(openerpweb.Controller): def exec_workflow(self, req, model, id, signal): return req.session.exec_workflow(model, id, signal) + @openerpweb.jsonrequest + def resequence(self, req, model, ids): + m = req.session.model(model) + if not len(m.fields_get(['sequence'])): + return False + for i in range(len(ids)): + m.write([ids[i]], { 'sequence': i }) + return True + class DataGroup(openerpweb.Controller): _cp_path = "/web/group" @openerpweb.jsonrequest diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 330885de21f..a39838070cb 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1926,9 +1926,9 @@ margin: 8px; } .openerp .oe_form_nosheet > header { - margin-top: -20px; - margin-left: -20px; - margin-right: -20px; + margin-top: -8px; + margin-left: -8px; + margin-right: -8px; } .openerp .oe_form header { position: relative; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index a1726e7e1a9..f1d1f2e9f1f 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1487,9 +1487,9 @@ $sheet-max-width: 860px .oe_form_nosheet margin: 8px .oe_form_nosheet > header - margin-top: -20px - margin-left: -20px - margin-right: -20px + margin-top: -8px + margin-left: -8px + margin-right: -8px // }}} // FormView.custom tags and classes {{{ .oe_form diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index a36b89400e8..ff066c9f425 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -621,11 +621,11 @@ instance.web.Reload = instance.web.Widget.extend({ }, start: function() { var l = window.location; - var timestamp = new Date().getTime(); - var search = "?ts=" + timestamp; - if (l.search) { - search = l.search + "&ts=" + timestamp; - } + + var sobj = $.deparam(l.search.substr(1)); + sobj.ts = new Date().getTime(); + var search = '?' + $.param(sobj); + var hash = l.hash; if (this.menu_id) { hash = "#menu_id=" + this.menu_id; @@ -948,7 +948,7 @@ instance.web.Client = instance.web.Widget.extend({ instance.web.bus.on('click', this, function(ev) { $.fn.tipsy.clear(); if (!$(ev.target).is('input[type=file]')) { - self.$element.find('.oe_dropdown_menu.oe_opened').removeClass('oe_opened'); + self.$element.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened'); } }); }, diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js index d5b87d8a81d..229a82a1784 100644 --- a/addons/web/static/src/js/coresetup.js +++ b/addons/web/static/src/js/coresetup.js @@ -523,37 +523,50 @@ $.async_when = function() { /** Setup blockui */ if ($.blockUI) { $.blockUI.defaults.baseZ = 1100; - $.blockUI.defaults.message = '
'; + $.blockUI.defaults.message = '
'; $.blockUI.defaults.css.border = '0'; $.blockUI.defaults.css["background-color"] = ''; - $.blockUI.spinners = []; } + +instance.web.Throbber = instance.web.Widget.extend({ + template: "Throbber", + start: function() { + var opts = { + lines: 13, // The number of lines to draw + length: 7, // The length of each line + width: 4, // The line thickness + radius: 10, // The radius of the inner circle + rotate: 0, // The rotation offset + color: '#FFF', // #rgb or #rrggbb + speed: 1, // Rounds per second + trail: 60, // Afterglow percentage + shadow: false, // Whether to render a shadow + hwaccel: false, // Whether to use hardware acceleration + className: 'spinner', // The CSS class to assign to the spinner + zIndex: 2e9, // The z-index (defaults to 2000000000) + top: 'auto', // Top position relative to parent in px + left: 'auto' // Left position relative to parent in px + }; + this.spin = new Spinner(opts).spin(this.$element[0]); + }, + destroy: function() { + if (this.spin) + this.spin.stop(); + this._super(); + }, +}); +instance.web.Throbber.throbbers = []; + instance.web.blockUI = function() { var tmp = $.blockUI.apply($, arguments); - var target = $(".oe_blockui_spin")[0]; - var opts = { - lines: 13, // The number of lines to draw - length: 7, // The length of each line - width: 4, // The line thickness - radius: 10, // The radius of the inner circle - rotate: 0, // The rotation offset - color: '#FFF', // #rgb or #rrggbb - speed: 1, // Rounds per second - trail: 60, // Afterglow percentage - shadow: false, // Whether to render a shadow - hwaccel: false, // Whether to use hardware acceleration - className: 'spinner', // The CSS class to assign to the spinner - zIndex: 2e9, // The z-index (defaults to 2000000000) - top: 'auto', // Top position relative to parent in px - left: 'auto' // Left position relative to parent in px - }; - var spinner = new Spinner(opts).spin(target); - $.blockUI.spinners.push(spinner); + var throbber = new instance.web.Throbber(); + instance.web.Throbber.throbbers.push(throbber); + throbber.appendTo($(".oe_blockui_spin_container")); return tmp; } instance.web.unblockUI = function() { - _.each($.blockUI.spinners, function(el) { - el.stop(); + _.each(instance.web.Throbber.throbbers, function(el) { + el.destroy(); }); return $.unblockUI.apply($, arguments); } diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index e360c922918..10fd727e57a 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -706,7 +706,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp. * @returns {$.Deferred} */ call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) { - return this.rpc('/web/dataset/call', { + return instance.session.rpc('/web/dataset/call', { model: this.model, method: method, domain_id: domain_index == undefined ? null : domain_index, @@ -805,6 +805,22 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp. alter_ids: function(n_ids) { this.ids = n_ids; }, + /** + * Resequence records. + * + * @param {Array} ids identifiers of the records to resequence + * @returns {$.Deferred} + */ + resequence: function (ids, options) { + options = options || {}; + return instance.session.rpc('/web/dataset/resequence', { + model: this.model, + ids: ids, + context: this._model.context(options.context), + }).pipe(function (results) { + return results; + }); + }, }); instance.web.DataSetStatic = instance.web.DataSet.extend({ init: function(parent, model, context, ids) { diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index ef76af3d857..e253cc8da89 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -263,7 +263,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM /** * * @param {Object} [options] - * @param {Boolean} [editable=false] whether the form should be switched to edition mode. A value of ``false`` will keep the current mode. + * @param {Boolean} [mode=undefined] If specified, switch the form to specified mode. Can be "edit" or "view". * @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record * @return {$.Deferred} */ @@ -300,9 +300,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }); } return shown.pipe(function() { - if (options.editable) { - self.to_edit_mode(); - } + self._actualize_mode(options.mode || self.options.initial_mode); self.$element.css({ opacity: '1', filter: 'alpha(opacity = 100)' @@ -642,12 +640,21 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this._actualize_mode("edit"); }, /** - * Reactualize actual_mode. + * Ask the view to switch to a precise mode if possible. The view is free to + * not respect this command if the state of the dataset is not compatible with + * the new mode. For example, it is not possible to switch to edit mode if + * the current record is not yet saved in database. + * + * @param {string} [new_mode] Can be "edit", "view", "create" or undefined. If + * undefined the view will test the actual mode to check if it is still consistent + * with the dataset state. */ _actualize_mode: function(switch_to) { var mode = switch_to || this.get("actual_mode"); if (! this.datarecord.id) { mode = "create"; + } else if (mode === "create") { + mode = "edit"; } this.set({actual_mode: mode}); }, @@ -2724,6 +2731,11 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc this.floating = false; this.render_value(); }); + instance.web.bus.on('click', this, function() { + if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) { + this.$input.autocomplete("close"); + } + }); }, initialize_content: function() { if (!this.get("effective_readonly")) @@ -2975,10 +2987,12 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc }, _quick_create: function() { this.no_tipsy = true; + this.tip_def.reject(); return instance.web.form.CompletionFieldMixin._quick_create.apply(this, arguments); }, _search_create_popup: function() { this.no_tipsy = true; + this.tip_def.reject(); return instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments); }, }); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 38461c3e905..7068946333b 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -159,7 +159,9 @@ instance.web.ActionManager = instance.web.Widget.extend({ //state = _.extend(this.inner_action.params || {}, state); } } - this.getParent().do_push_state(state); + if(!this.dialog) { + this.getParent().do_push_state(state); + } } }, do_load_state: function(state, warm) { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 614c2a91d79..2d86b40f44f 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1742,5 +1742,12 @@
- + +
+
+
+
+
Loading...
+
+
diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index ae92585f67e..9feaf657e28 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -28,7 +28,6 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ width: '80%', min_width: 850 }, this.options.action_views_ids.form, dataset); - this.form_dialog.start(); this.COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00', @@ -312,28 +311,30 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ }); }, do_create_event_with_formdialog: function(event_id, event_obj) { - if (!event_obj) { - event_obj = scheduler.getEvent(event_id); - } - var self = this, - data = this.get_event_data(event_obj), - form = self.form_dialog.form, - fields_to_fetch = _(form.fields_view.fields).keys(); - this.dataset.index = null; - self.creating_event_id = event_id; - this.form_dialog.form.do_show().then(function() { - _.each(['date_start', 'date_delay', 'date_stop'], function(field) { - var field_name = self[field]; - if (field_name && form.fields[field_name]) { - var ffield = form.fields[field_name]; - ffield._dirty_flag = false; - $.when(ffield.set_value(data[field_name])).then(function() { - ffield._dirty_flag = true; - form.do_onchange(ffield); - }); - } + var self = this; + $.when(! self.form_dialog.dialog_inited ? self.form_dialog.init_dialog() : true).then(function() { + debugger; + if (!event_obj) { + event_obj = scheduler.getEvent(event_id); + } + var data = self.get_event_data(event_obj), + fields_to_fetch = _(self.form_dialog.form.fields_view.fields).keys(); + self.dataset.index = null; + self.creating_event_id = event_id; + self.form_dialog.form.do_show().then(function() { + _.each(['date_start', 'date_delay', 'date_stop'], function(field) { + var field_name = self[field]; + if (field_name && self.form_dialog.form.fields[field_name]) { + var ffield = self.form_dialog.form.fields[field_name]; + ffield._dirty_flag = false; + $.when(ffield.set_value(data[field_name])).then(function() { + ffield._dirty_flag = true; + self.form_dialog.form.do_onchange(ffield); + }); + } + }); + self.form_dialog.open(); }); - self.form_dialog.open(); }); }, do_save_event: function(event_id, event_obj) { @@ -451,12 +452,13 @@ instance.web_calendar.CalendarFormDialog = instance.web.Dialog.extend({ this.form = new instance.web.FormView(this, this.dataset, this.view_id, { pager: false }); - this.form.appendTo(this.$element); + var def = this.form.appendTo(this.$element); this.form.on_created.add_last(this.on_form_dialog_saved); this.form.on_saved.add_last(this.on_form_dialog_saved); this.form.on_button_cancel = function() { self.close(); } + return def; }, on_form_dialog_saved: function() { var id = this.dataset.ids[this.dataset.index]; diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 47d9368d9dc..d0e754b6224 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -1,3 +1,4 @@ +@charset "utf-8"; .openerp .oe_kanban_view { height: inherit; } @@ -44,8 +45,17 @@ .openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_groups { width: 100%; } -.openerp .oe_kanban_view .oe_kanban_header:hover .oe_dropdown_kanban { - display: inline-block; +.openerp .oe_kanban_view.oe_kanban_grouped_by_m2o .oe_kanban_group_title { + cursor: move; +} +.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban { + float: right; +} +.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban > span { + visibility: hidden; +} +.openerp .oe_kanban_view .oe_kanban_header:hover .oe_dropdown_kanban > span { + visibility: visible; } .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_menu { font-weight: normal; @@ -59,7 +69,6 @@ text-shadow: 0 1px 0 white; } .openerp .oe_kanban_view .oe_kanban_group_title > span { - float: left; margin-right: 4px; } .openerp .oe_kanban_view .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header { @@ -93,6 +102,9 @@ .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title_vertical { display: block; } +.openerp .oe_kanban_view .oe_kanban_group_folded .oe_dropdown_kanban { + left: -5px; +} .openerp .oe_kanban_view .oe_kanban_group_title_undefined { color: #666666; } @@ -104,20 +116,11 @@ -ms-transform: rotate(90deg); transform: rotate(90deg); width: 30px; - height: 20px; font-size: 24px; white-space: nowrap; display: none; - position: absolute; - top: 10px; -} -.openerp .oe_kanban_view .oe_kanban_fold_icon { - cursor: pointer; - float: left; - padding: 2px; - width: 16px; - height: 16px; - background: url(/web_kanban/static/src/img/minus-icon.png) no-repeat; + position: relative; + top: 5px; } .openerp .oe_kanban_view .oe_kanban_add, .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle { margin-left: 4px; @@ -462,13 +465,10 @@ top: 28px; min-width: 160px; } -.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban { - display: none; -} -.openerp .oe_kanban_view .oe_kanban_column .oe_dropdown_kanban.oe_opened > span { +.openerp .oe_kanban_view .oe_dropdown_kanban.oe_opened > span { visibility: visible; } -.openerp .oe_kanban_view .oe_kanban_column .oe_dropdown_kanban > span { +.openerp .oe_kanban_view .oe_dropdown_kanban > span { visibility: hidden; } .openerp .oe_kanban_view .oe_kanban_colorpicker { diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index 029014b1f43..af71d857d53 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -66,10 +66,16 @@ height: inherit &.oe_kanban_ungrouped .oe_kanban_groups width: 100% + &.oe_kanban_grouped_by_m2o .oe_kanban_group_title + cursor: move .oe_kanban_header + .oe_dropdown_kanban + float: right + .oe_dropdown_kanban > span + visibility: hidden &:hover - .oe_dropdown_kanban - display: inline-block + .oe_dropdown_kanban > span + visibility: visible .oe_dropdown_menu font-weight: normal font-size: 13px @@ -80,7 +86,6 @@ color: #333333 text-shadow: 0 1px 0 white > span - float: left margin-right: 4px .oe_kanban_column, .oe_kanban_group_header @@ -111,6 +116,8 @@ display: none .oe_kanban_group_title_vertical display: block + .oe_dropdown_kanban + left: -5px .oe_kanban_group_title_undefined color: #666666 .oe_kanban_group_title_vertical @@ -121,19 +128,11 @@ -ms-transform: rotate(90deg) transform: rotate(90deg) width: 30px - height: 20px font-size: 24px white-space: nowrap display: none - position: absolute - top: 10px - .oe_kanban_fold_icon - cursor: pointer - float: left - padding: 2px - width: 16px - height: 16px - background: url(/web_kanban/static/src/img/minus-icon.png) no-repeat + position: relative + top: 5px // }}} // KanbanQuickCreate {{{ .oe_kanban_add, .oe_kanban_header .oe_dropdown_toggle @@ -388,15 +387,11 @@ left: 0 top: 28px min-width: 160px - .oe_kanban_header - .oe_dropdown_kanban - display: none - .oe_kanban_column - .oe_dropdown_kanban - &.oe_opened > span - visibility: visible - > span - visibility: hidden + .oe_dropdown_kanban + &.oe_opened > span + visibility: visible + > span + visibility: hidden // }}} // KanbanColorPicker {{{ .oe_kanban_colorpicker diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 84a0a76ba0d..154df0f8b6b 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -24,6 +24,8 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.fields_view = {}; this.fields_keys = []; this.group_by = null; + this.group_by_field = {}; + this.grouped_by_m2o = false; this.state = { groups : {}, records : {} @@ -54,7 +56,8 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.$element.find('.oe_kanban_buttons').replaceWith(this.$buttons); } this.$buttons - .on('click','button.oe_kanban_button_new', this.do_add_record); + .on('click', 'button.oe_kanban_button_new', this.do_add_record) + .on('click', '.oe_kanban_add_column', this.do_add_group); this.$groups = this.$element.find('.oe_kanban_groups tr'); this.fields_keys = _.keys(this.fields_view.fields); this.add_qweb_template(); @@ -157,6 +160,39 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.dataset.index = null; this.do_switch_view('form'); }, + do_add_group: function() { + var self = this; + self.do_action({ + name: _t("Add column"), + res_model: self.group_by_field.relation, + views: [[false, 'form']], + type: 'ir.actions.act_window', + target: "new", + flags: { + action_buttons: true, + } + }); + var am = instance.webclient.action_manager; + var form = am.dialog_widget.views.form.controller; + form.on_button_cancel.add_last(am.dialog.on_close); + form.on_created.add_last(function(r) { + (new instance.web.DataSet(self, self.group_by_field.relation)).name_get([r.result]).then(function(new_record) { + am.dialog.on_close(); + var domain = self.dataset.domain.slice(0); + domain.push([self.group_by, '=', new_record[0][0]]); + var dataset = new instance.web.DataSetSearch(self, self.dataset.model, self.dataset.get_context(), domain); + var datagroup = { + value: new_record[0], + length: 0, + aggregates: {}, + }; + var new_group = new instance.web_kanban.KanbanGroup(self, [], datagroup, dataset); + self.do_add_groups([new_group]).then(function() { + $(window).scrollTo(self.groups.slice(-1)[0].$element, { axis: 'x' }); + }); + }); + }); + }, do_search: function(domain, context, group_by) { var self = this; this.$element.find('.oe_view_nocontent').remove(); @@ -165,6 +201,10 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.search_group_by = group_by; $.when(this.has_been_loaded).then(function() { self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by; + self.group_by_field = self.fields_view.fields[self.group_by] || {}; + self.grouped_by_m2o = (self.group_by_field.type === 'many2one'); + self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o); + self.$element.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o); self.datagroup = new instance.web.DataGroup(self, self.dataset.model, domain, context, self.group_by ? [self.group_by] : []); self.datagroup.list(self.fields_keys, self.do_process_groups, self.do_process_dataset); }); @@ -227,7 +267,9 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ self.groups[group.undefined_title ? 'unshift' : 'push'](group); }); var groups_started = _.map(this.groups, function(group) { - return group.insertBefore(self.$element.find('.oe_kanban_groups_headers td:last')); + if (!group.is_started) { + return group.insertBefore(self.$element.find('.oe_kanban_groups_headers td:last')); + } }); return $.when.apply(null, groups_started).then(function () { self.on_groups_started(); @@ -237,6 +279,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ var self = this; this.compute_groups_width(); if (this.group_by) { + // Kanban cards drag'n'drop this.$element.find('.oe_kanban_column').sortable({ connectWith: '.oe_kanban_column', handle : '.oe_kanban_draghandle', @@ -245,17 +288,50 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ self.currently_dragging.group = ui.item.parents('.oe_kanban_column:first').data('widget'); }, stop: function(event, ui) { - var record = ui.item.data('widget'), - old_index = self.currently_dragging.index, - new_index = ui.item.index(), - old_group = self.currently_dragging.group, - new_group = ui.item.parents('.oe_kanban_column:first').data('widget'); + var record = ui.item.data('widget'); + var old_index = self.currently_dragging.index; + var new_index = ui.item.index(); + var old_group = self.currently_dragging.group; + var new_group = ui.item.parents('.oe_kanban_column:first').data('widget'); if (!(old_group.title === new_group.title && old_group.value === new_group.value && old_index == new_index)) { self.on_record_moved(record, old_group, old_index, new_group, new_index); } }, scroll: false }); + // Kanban groups drag'n'drop + var start_index; + if (this.grouped_by_m2o) { + this.$('.oe_kanban_groups_headers').sortable({ + items: '.oe_kanban_group_header', + helper: 'clone', + axis: 'x', + opacity: 0.5, + scroll: false, + start: function(event, ui) { + start_index = ui.item.index(); + self.$('.oe_kanban_record').css({ visibility: 'hidden' }); + }, + stop: function(event, ui) { + var stop_index = ui.item.index(); + if (start_index !== stop_index) { + var $start_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(start_index); + var $stop_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index); + var method = (start_index > stop_index) ? 'insertBefore' : 'insertAfter'; + $start_column[method]($stop_column); + var tmp_group = self.groups.splice(start_index, 1)[0]; + self.groups.splice(stop_index, 0, tmp_group); + var new_sequence = _.pluck(self.groups, 'value'); + (new instance.web.DataSet(self, self.group_by_field.relation)).resequence(new_sequence).then(function(r) { + if (r === false) { + console.error("Kanban: could not resequence model '%s'. Probably no 'sequence' field.", self.group_by_field.relation); + } + }); + } + self.$('.oe_kanban_record').css({ visibility: 'visible' }); + } + }); + } } else { this.$element.find('.oe_kanban_draghandle').removeClass('oe_kanban_draghandle'); } @@ -289,7 +365,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ var self = this; _.each(this.groups, function(group) { unfolded += group.state.folded ? 0 : 1; - group.$element.css('width', ''); + group.$element.children(':first').css('width', ''); }); _.each(this.groups, function(group) { if (!group.state.folded) { @@ -321,7 +397,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }, open_record: function(id, editable) { if (this.dataset.select_id(id)) { - this.do_switch_view('form', null, { editable: editable }); + this.do_switch_view('form', null, { mode: editable ? "edit" : undefined }); } else { this.do_warn("Kanban: could not find id#" + id); } @@ -362,8 +438,8 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ this.title = this.value[1]; this.value = this.value[0]; } - var field = this.view.fields_view.fields[this.view.group_by]; - if (field) { + var field = this.view.group_by_field; + if (!_.isEmpty(field)) { try { this.title = instance.web.format_value(group.value, field, false); } catch(e) {} @@ -402,11 +478,14 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ } this.$records = $(QWeb.render('KanbanView.group_records_container', { widget : this})); this.$records.insertBefore(this.view.$element.find('.oe_kanban_groups_records td:last')); - this.$element.find(".oe_kanban_fold_icon").click(function() { - self.do_toggle_fold(); - self.view.compute_groups_width(); - return false; + + this.$element.on('click', '.oe_kanban_group_dropdown li a', function(ev) { + var fn = 'do_action_' + $(ev.target).data().action; + if (typeof(self[fn]) === 'function') { + self[fn]($(ev.target)); + } }); + this.$element.find('.oe_kanban_add').click(function () { if (self.quick) { return; } var ctx = {}; @@ -433,26 +512,27 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ this.$records.click(function (ev) { if (ev.target == ev.currentTarget) { if (!self.state.folded) { - add_btn.effect('bounce', {distance: 18, times: 5}, 150) - } + add_btn.effect('bounce', {distance: 18, times: 5}, 150); + } } }); + this.is_started = true; return def; }, compute_cards_auto_height: function() { - // oe_kanban_auto_height is an empty class used by the kanban view in order - // to normalize height amongst kanban cards. (by group) - var self = this; - var min_height = 0; - var els = []; - _.each(this.records, function(r) { - var $e = r.$element.find('.oe_kanban_auto_height').first().css('min-height', 0); - if ($e.length) { - els.push($e[0]); - min_height = Math.max(min_height, $e.outerHeight()); - } - }); - $(els).css('min-height', min_height); + // oe_kanban_no_auto_height is an empty class used to disable this feature + if (!this.view.group_by) { + var min_height = 0; + var els = []; + _.each(this.records, function(r) { + var $e = r.$element.children(':first:not(.oe_kanban_no_auto_height)').css('min-height', 0); + if ($e.length) { + els.push($e[0]); + min_height = Math.max(min_height, $e.outerHeight()); + } + }); + $(els).css('min-height', min_height); + } }, destroy: function() { this._super(); @@ -493,13 +573,46 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ do_toggle_fold: function(compute_width) { this.$element.add(this.$records).toggleClass('oe_kanban_group_folded'); this.state.folded = this.$element.is('.oe_kanban_group_folded'); + this.$("ul.oe_kanban_group_dropdown li a[data-action=toggle_fold]").text((this.state.folded) ? _t("Unfold") : _t("Fold")); + }, + do_action_toggle_fold: function() { + this.do_toggle_fold(); + this.view.compute_groups_width(); + }, + do_action_edit: function() { + var self = this; + self.do_action({ + res_id: this.group.value[0], + name: _t("Edit column"), + res_model: self.view.group_by_field.relation, + views: [[false, 'form']], + type: 'ir.actions.act_window', + target: "new", + flags: { + action_buttons: true, + } + }); + var am = instance.webclient.action_manager; + var form = am.dialog_widget.views.form.controller; + form.on_button_cancel.add_last(am.dialog.on_close); + form.on_saved.add_last(function() { + am.dialog.on_close(); + self.view.do_reload(); + }); + }, + do_action_delete: function() { + var self = this; + if (confirm(_t("Are you sure to remove this column ?"))) { + (new instance.web.DataSet(self, self.view.group_by_field.relation)).unlink([self.group.value[0]]).then(function(r) { + self.view.do_reload(); + }); + } }, do_save_sequences: function() { var self = this; if (_.indexOf(this.view.fields_keys, 'sequence') > -1) { - _.each(this.records, function(record, index) { - self.view.dataset.write(record.id, { sequence : index }); - }); + var new_sequence = _.pluck(this.records, 'id'); + self.view.dataset.resequence(new_sequence); } }, /** @@ -720,7 +833,7 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ this.view.dataset.read_ids([this.id], this.view.fields_keys.concat(['__last_update'])).then(function(records) { if (records.length) { self.set_record(records[0]); - self.replaceElement($(self.render())); + self.renderElement(); self.$element.data('widget', self); self.bind_events(); self.group.compute_cards_auto_height(); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 7e316a2b47e..d23904624c8 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -18,6 +18,10 @@ +
@@ -28,18 +32,20 @@
]
+
+ í + +
() -
- í - -
    @@ -48,7 +54,7 @@
-

+
diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index 5e04134d1ce..e52a51d125e 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -70,11 +70,10 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ this.view_edit_dialog.on_close.add_last(function(){window.location.reload();}); this.main_view_id = this.parent.fields_view.view_id; this.action_manager = new instance.web.ActionManager(this); + this.action_manager.appendTo(this.view_edit_dialog.$element); $.when(this.action_manager.do_action(action)).then(function() { - var viewmanager = self.action_manager.inner_widget, - controller = viewmanager.views[viewmanager.active_view].controller; - self.action_manager.appendTo(self.view_edit_dialog.$element); - self.action_manager.renderElement(self.view_edit_dialog); + var viewmanager = self.action_manager.inner_widget; + var controller = viewmanager.views[viewmanager.active_view].controller; controller.on_loaded.add_last(function(){ $(controller.groups).bind({ 'selected': function(e, ids, records) {