diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-08-08 11:52:13 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-08-08 11:52:13 +0200 |
| commit | b792ba7d9a80e856e3c69c2852e6d44b78d0e13f (patch) | |
| tree | a3ee5b7ef9f9317866deeb2b3e5964020c87b8fb /addons/web | |
| parent | 22e0f88bce5bf162a6c67e915a3c27b101f74f2b (diff) | |
| parent | 752b2553c6fad061ebf23e13585f7b7489b20840 (diff) | |
[MERGE] forward port branch saas-12.3 up to 752b2553c6f
Diffstat (limited to 'addons/web')
13 files changed, 123 insertions, 24 deletions
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 860cf454dd4..18a36885f5f 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -201,11 +201,11 @@ def concat_xml(file_list): :param list(str) file_list: list of files to check :returns: (concatenation_result, checksum) - :rtype: (str, str) + :rtype: (bytes, str) """ checksum = hashlib.new('sha1') if not file_list: - return '', checksum.hexdigest() + return b'', checksum.hexdigest() root = None for fname in file_list: diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 171a6da7735..160f445f1c4 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1603,8 +1603,8 @@ var FieldBinaryImage = AbstractFieldBinary.extend({ } this.$('> img').remove(); this.$el.prepend($img); + $img.one('error', function () { - self._clearFile(); $img.attr('src', self.placeholder); self.do_warn(_t("Image"), _t("Could not display the selected image.")); }); diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index bc66594fee3..d3165420ef8 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -796,6 +796,7 @@ var BasicModel = AbstractModel.extend({ }); _.each(fields, function (field) { var dataPoint; + record.data[field.name] = null; if (field.type === 'many2one') { if (field.value) { var id = _.isArray(field.value) ? field.value[0] : field.value; diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js index b3a04ec9a2b..bbfcb02def5 100644 --- a/addons/web/static/src/js/views/calendar/calendar_controller.js +++ b/addons/web/static/src/js/views/calendar/calendar_controller.js @@ -55,6 +55,7 @@ var CalendarController = AbstractController.extend({ this.readonlyFormViewId = params.readonlyFormViewId; this.mapping = params.mapping; this.context = params.context; + this.previousOpen = null; // The quickCreating attribute ensures that we don't do several create this.quickCreating = false; }, @@ -271,7 +272,8 @@ var CalendarController = AbstractController.extend({ title += ': ' + this.renderer.arch.attrs.string; } if (this.eventOpenPopup) { - new dialogs.FormViewDialog(self, { + if (this.previousOpen) { this.previousOpen.close(); } + this.previousOpen = new dialogs.FormViewDialog(self, { res_model: this.modelName, context: context, title: title, @@ -283,7 +285,8 @@ var CalendarController = AbstractController.extend({ } self.reload(); }, - }).open(); + }); + this.previousOpen.open(); } else { this.do_action({ type: 'ir.actions.act_window', diff --git a/addons/web/static/src/js/views/control_panel/control_panel_model.js b/addons/web/static/src/js/views/control_panel/control_panel_model.js index f2e28858ba3..0229daa41ee 100644 --- a/addons/web/static/src/js/views/control_panel/control_panel_model.js +++ b/addons/web/static/src/js/views/control_panel/control_panel_model.js @@ -157,6 +157,9 @@ var ControlPanelModel = mvc.Model.extend({ if (filter.autoCompleteValues) { filter.autoCompleteValues = []; } + if (filter.currentOptionId) { + filter.currentOptionId = false; + } }); // TODO: use toggleFilter here group.activeFilterIds = []; diff --git a/addons/web/static/src/js/views/control_panel/search/groupby_menu.js b/addons/web/static/src/js/views/control_panel/search/groupby_menu.js index b67bf9ee75d..fac67beb3f4 100644 --- a/addons/web/static/src/js/views/control_panel/search/groupby_menu.js +++ b/addons/web/static/src/js/views/control_panel/search/groupby_menu.js @@ -41,6 +41,7 @@ var GroupByMenu = DropdownMenu.extend({ */ init: function (parent, groupBys, fields, options) { var self = this; + options = options || {}; this._super(parent, groupBys); this.fields = fields; // determines when the 'Add custom groupby' submenu is open @@ -65,9 +66,9 @@ var GroupByMenu = DropdownMenu.extend({ this.dropdownCategory = 'groupby'; this.dropdownTitle = _t('Group By'); this.dropdownIcon = 'fa fa-bars'; - this.dropdownSymbol = this.isMobile ? 'fa fa-chevron-right float-right mt4' : false; + this.dropdownSymbol = this.isMobile && !options.noSymbol ? 'fa fa-chevron-right float-right mt4' : false; // the default style of the groupby menu can be changed here using the options key 'headerStyle' - if (options && options.headerStyle === 'primary') { + if (options.headerStyle === 'primary') { this.dropdownStyle = { el: {class: 'btn-group o_group_by_menu o_dropdown', attrs: {'role': 'group'}}, mainButton: {class: 'btn btn-primary dropdown-toggle'}, diff --git a/addons/web/static/src/js/views/control_panel/search/groupby_menu_mixin.js b/addons/web/static/src/js/views/control_panel/search/groupby_menu_mixin.js index dc7a5dc3cc8..c9a17d6225e 100644 --- a/addons/web/static/src/js/views/control_panel/search/groupby_menu_mixin.js +++ b/addons/web/static/src/js/views/control_panel/search/groupby_menu_mixin.js @@ -36,6 +36,7 @@ var GroupByMenuMixin = { * private * @param {jQuery} $node * @param {Object} groupableFields + * @param {Promise} */ _addGroupByMenu: function ($node, groupableFields) { this.sortedFieldNames = _.sortBy(Object.keys(groupableFields), function (fieldName) { @@ -43,8 +44,8 @@ var GroupByMenuMixin = { }); this.groupableFields = groupableFields; var groupBys = this._getGroupBys(this.model.get().groupBy); - this.groupByMenu = new GroupByMenu(this, groupBys, this.groupableFields); - this.groupByMenu.insertAfter($node.find('div:first')); + this.groupByMenu = new GroupByMenu(this, groupBys, this.groupableFields, {noSymbol: true}); + return this.groupByMenu.insertAfter($node.find('div:first')); }, /** * This method puts the active groupBys in a convenient form. diff --git a/addons/web/static/src/js/views/graph/graph_controller.js b/addons/web/static/src/js/views/graph/graph_controller.js index 9e2ebfc0a78..1c580a25bd6 100644 --- a/addons/web/static/src/js/views/graph/graph_controller.js +++ b/addons/web/static/src/js/views/graph/graph_controller.js @@ -96,8 +96,13 @@ var GraphController = AbstractController.extend(GroupByMenuMixin,{ this._updateButtons(); this.$buttons.appendTo($node); if (this.isEmbedded) { - this._addGroupByMenu($node, this.groupableFields); + this._addGroupByMenu($node, this.groupableFields).then(function(){ + var groupByButton = $node.find('.o_dropdown_toggler_btn'); + groupByButton.removeClass("o_dropdown_toggler_btn btn btn-secondary dropdown-toggle"); + groupByButton.addClass("btn dropdown-toggle btn-outline-secondary"); + }); } + } }, diff --git a/addons/web/static/src/js/views/graph/graph_renderer.js b/addons/web/static/src/js/views/graph/graph_renderer.js index 02fdb102358..9c70811ed18 100644 --- a/addons/web/static/src/js/views/graph/graph_renderer.js +++ b/addons/web/static/src/js/views/graph/graph_renderer.js @@ -216,6 +216,24 @@ return AbstractRenderer.extend({ return this.state.origins[dataPt.originIndex]; }, /** + * Returns an object used to style chart elements independently from the datasets. + * + * @private + * @returns {Object} + */ + _getElementOptions: function () { + var elementOptions = {}; + if (this.state.mode === 'bar') { + elementOptions.rectangle = {borderWidth: 1}; + } else if (this.state.mode === 'line') { + elementOptions.line = { + tension: 0, + fill: false, + }; + } + return elementOptions; + }, + /** * Returns a DateClasses instance used to manage equivalence of dates. * * @private @@ -250,9 +268,8 @@ return AbstractRenderer.extend({ } else { return dataPt.labels.slice(0, 1); } - } else if (i >= 0) { + } else if (i === 0) { return Array.prototype.concat.apply([], [ - dataPt.labels.slice(0, i), this.dateClasses.dateClass(dataPt.originIndex, dataPt.labels[i]), dataPt.labels.slice(i+1) ]); @@ -563,6 +580,7 @@ return AbstractRenderer.extend({ scales: this._getScaleOptions(), legend: this._getLegendOptions(datasetsCount), tooltips: this._getTooltipOptions(datasetsCount), + elements: this._getElementOptions(), }; }, /** @@ -583,7 +601,7 @@ return AbstractRenderer.extend({ if (_.contains(['bar', 'line'], this.state.mode) && i === 0) { // here label is an array of length 1 and contains a number return this.dateClasses.representative(label, originIndex) || ''; - } else if (this.state.mode === 'pie' && i >= 0) { + } else if (this.state.mode === 'pie' && i === 0) { // here label is an array of length at least one containing string or numbers var labelCopy = label.slice(0); if (originIndex !== undefined) { @@ -637,7 +655,7 @@ return AbstractRenderer.extend({ this.$el.append($canvasContainer); var i = this.state.comparisonFieldIndex; - if (i === 0 || (i > 0 && this.state.mode === 'pie')) { + if (i === 0) { this.dateClasses = this._getDateClasses(dataPoints); } if (this.state.mode === 'bar') { @@ -661,9 +679,6 @@ return AbstractRenderer.extend({ _renderBarChart: function (dataPoints) { var self = this; - // style rectangles - Chart.defaults.global.elements.rectangle.borderWidth = 1; - // prepare data var data = this._prepareData(dataPoints); data.datasets.forEach(function (dataset, index) { @@ -694,10 +709,6 @@ return AbstractRenderer.extend({ _renderLineChart: function (dataPoints) { var self = this; - // style lines - Chart.defaults.global.elements.line.tension = 0; - Chart.defaults.global.elements.line.fill = false; - // prepare data var data = this._prepareData(dataPoints); data.datasets.forEach(function (dataset, index) { diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index 2a8e44df4a2..afa8f09a857 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -761,7 +761,7 @@ var ListRenderer = BasicRenderer.extend({ } if (field.type === 'float' || field.type === 'integer' || field.type === 'monetary') { - $th.css({ textAlign: 'right' }); + $th.addClass('o_list_number_th'); } if (config.isDebug()) { diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss index a18d9f70083..21743a75751 100644 --- a/addons/web/static/src/scss/list_view.scss +++ b/addons/web/static/src/scss/list_view.scss @@ -23,6 +23,9 @@ cursor: default; } } + &.o_list_number_th { + text-align: right; + } } tbody > tr > td:not(.o_list_record_selector) { @include o-text-overflow(table-cell); diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 16e304c14a6..8a5b8343208 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -5,6 +5,7 @@ var AbstractStorageService = require('web.AbstractStorageService'); var CalendarView = require('web.CalendarView'); var CalendarRenderer = require('web.CalendarRenderer'); var Dialog = require('web.Dialog'); +var ViewDialogs = require('web.view_dialogs'); var fieldUtils = require('web.field_utils'); var mixins = require('web.mixins'); var RamStorage = require('web.RamStorage'); @@ -482,6 +483,63 @@ QUnit.module('Views', { calendar.destroy(); }); + QUnit.test('open multiple event form at the same time', async function (assert) { + assert.expect(2); + + var prom = testUtils.makeTestPromise(); + var counter = 0; + testUtils.mock.patch(ViewDialogs.FormViewDialog, { + open: function () { + counter++; + this.options = _.omit(this.options, 'fields_view'); // force loadFieldView + return this._super.apply(this, arguments); + }, + loadFieldView: function () { + var self = this; + var args = arguments; + var _super = this._super; + return prom.then(function () { + return _super.apply(self, args); + }); + }, + }); + + var event = $.Event(); + var calendar = await createCalendarView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: + '<calendar class="o_calendar_test" '+ + 'string="Events" ' + + 'event_open_popup="true" '+ + 'date_start="start" '+ + 'quick_add="False" '+ + 'date_stop="stop" '+ + 'all_day="allday" '+ + 'mode="month" '+ + 'readonly_form_view_id="1">'+ + '<field name="name"/>'+ + '</calendar>', + archs: archs, + viewOptions: { + initialDate: initialDate, + }, + }); + + var $cell = calendar.$('.fc-day-grid .fc-row:eq(2) .fc-day:eq(2)'); + for (var i = 0; i < 5; i++) { + await testUtils.dom.triggerMouseEvent($cell, "mousedown"); + await testUtils.dom.triggerMouseEvent($cell, "mouseup"); + } + prom.resolve(); + await testUtils.nextTick(); + assert.equal(counter, 5, "there should had been 5 attemps to open a modal"); + assert.containsOnce($('body'), '.modal', "there should be only one open modal"); + + calendar.destroy(); + }); + QUnit.test('create event with timezone in week mode European locale', async function (assert) { assert.expect(5); diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js index 3fe15d661dd..ccf5bf5b50d 100644 --- a/addons/web/static/tests/views/search_view_tests.js +++ b/addons/web/static/tests/views/search_view_tests.js @@ -981,8 +981,8 @@ QUnit.module('Search View', { QUnit.module('Autocompletion'); - QUnit.test('selection via autocompletion modifies appropriately submenus', async function (assert) { - assert.expect(4); + QUnit.test('selection via autocompletion and deletion modifies appropriately submenus', async function (assert) { + assert.expect(6); var actionManager = await createActionManager({ actions: this.actions, @@ -1014,6 +1014,19 @@ QUnit.module('Search View', { assert.strictEqual($('.o_group_by_menu .o_item_option a.selected').text().trim(), "Day", "The item 'Day' should be selected in the groupby menu"); + await testUtils.dom.click($('div .o_searchview_facet .o_facet_remove').eq(0)); + await testUtils.dom.click($('div .o_searchview_facet .o_facet_remove').eq(0)); + + await testUtils.dom.click($('button .fa-filter')); + await testUtils.dom.click($('.o_filters_menu .o_menu_item').eq(0)); + assert.containsNone($('.o_filters_menu .o_item_option a.selected'), + "The item 'This Month' should have unselected in the filters menu"); + + await testUtils.dom.click($('button .fa-bars')); + await testUtils.dom.click($('.o_group_by_menu .o_menu_item').eq(0)); + assert.containsNone($('.o_group_by_menu .o_item_option a.selected'), + "The item 'Day' should have been unselected in the groupby menu"); + actionManager.destroy(); }); |
