[FIX] web: Kanban: prevent drag&drop for readonly

If the kanban view is grouped by a readonly field, it should not be possible
to drag&drop the record from one column to another as this will trigger a
write on this readonly field.

It should however be possible to resequence records so the option `containment`
is used in the records.

Closes #19390
This commit is contained in:
Martin Geubelle
2017-11-17 16:21:01 +01:00
parent 11ff01af9b
commit 5ba6d93810
4 changed files with 82 additions and 4 deletions
@@ -50,7 +50,7 @@ var KanbanColumn = Widget.extend({
this.grouped_by_m2o = options.grouped_by_m2o;
this.editable = options.editable;
this.deletable = options.deletable;
this.draggable = recordOptions.draggable;
this.draggable = options.draggable;
this.records_editable = options.records_editable;
this.records_deletable = options.records_deletable;
this.relation = options.relation;
@@ -87,12 +87,13 @@ var KanbanColumn = Widget.extend({
}
this.$header.tooltip();
if (config.device.size_class > config.device.SIZES.XS && this.draggable !== false) {
if (config.device.size_class > config.device.SIZES.XS) {
// deactivate sortable in mobile mode. It does not work anyway,
// and it breaks horizontal scrolling in kanban views. Someday, we
// should find a way to use the touch events to make sortable work.
this.$el.sortable({
connectWith: '.o_kanban_group',
containment: this.draggable ? '.o_kanban_view' : 'parent',
revert: 0,
delay: 0,
items: '> .o_kanban_record:not(.o_updating)',
@@ -33,7 +33,6 @@ var KanbanRecord = Widget.extend({
this.options = options;
this.editable = options.editable;
this.deletable = options.deletable;
this.draggable = options.draggable;
this.read_only_mode = options.read_only_mode;
this.qweb = options.qweb;
this.subWidgets = {};
@@ -316,7 +316,7 @@ var KanbanRenderer = BasicRenderer.extend({
var groupByFieldInfo = state.fieldsInfo.kanban[state.groupedBy[0]];
// Deactivate the drag'n'drop if the groupedBy field:
// - is a date or datetime since we group by month or
// - is readonly
// - is readonly (on the field attrs or in the view)
var draggable = true;
if (groupByFieldAttrs) {
if (groupByFieldAttrs.type === "date" || groupByFieldAttrs.type === "datetime") {
@@ -325,6 +325,11 @@ var KanbanRenderer = BasicRenderer.extend({
draggable = !(groupByFieldAttrs.readonly);
}
}
if (groupByFieldInfo) {
if (draggable && groupByFieldInfo.readonly !== undefined) {
draggable = !(groupByFieldInfo.readonly);
}
}
this.groupedByM2O = groupByFieldAttrs && (groupByFieldAttrs.type === 'many2one');
var grouped_by_field = this.groupedByM2O && groupByFieldAttrs.relation;
var groupByTooltip = groupByFieldInfo && groupByFieldInfo.options.group_by_tooltip;
@@ -581,6 +581,79 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('prevent drag and drop of record if grouped by readonly', function (assert) {
assert.expect(12);
this.data.partner.fields.foo.readonly = true;
var kanban = createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch: '<kanban>' +
'<templates>' +
'<t t-name="kanban-box"><div>' +
'<field name="foo"/>' +
'<field name="state" readonly="1"/>' +
'</div></t>' +
'</templates>' +
'</kanban>',
mockRPC: function (route, args) {
if (route === '/web/dataset/resequence') {
return $.when();
}
if (args.model === 'partner' && args.method === 'write') {
throw new Error('should not be draggable');
}
return this._super(route, args);
},
});
// simulate an update coming from the searchview, with another groupby given
kanban.update({groupBy: ['state']});
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1,
"column should contain 1 record(s)");
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 1,
"column should contain 1 record(s)");
// drag&drop a record in another column
var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first');
var $group = kanban.$('.o_kanban_group:nth-child(2)');
testUtils.dragAndDrop($record, $group);
// should not be draggable
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1,
"column should now contain 1 record(s)");
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 1,
"column should contain 1 record(s)");
// simulate an update coming from the searchview, with another groupby given
kanban.update({groupBy: ['foo']});
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1,
"column should contain 1 record(s)");
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 2,
"column should contain 2 record(s)");
// drag&drop a record in another column
$record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first');
$group = kanban.$('.o_kanban_group:nth-child(2)');
testUtils.dragAndDrop($record, $group);
// should not be draggable
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1,
"column should now contain 1 record(s)");
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 2,
"column should contain 2 record(s)");
// drag&drop a record in the same column
var $record1 = kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(0)');
var $record2 = kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(1)');
assert.strictEqual($record1.text(), "blipDEF", "first record should be DEF");
assert.strictEqual($record2.text(), "blipGHI", "second record should be GHI");
testUtils.dragAndDrop($record2, $record1, {position: 'top'});
// should still be able to resequence
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(0)').text(), "blipGHI",
"records should have been resequenced");
assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(1)').text(), "blipDEF",
"records should have been resequenced");
kanban.destroy();
});
QUnit.test('kanban view with default_group_by', function (assert) {
assert.expect(7);
this.data.partner.records.product_id = 1;