Skip to content
Permalink
Browse files

[ADD] web: add edit tag feature on many2many_tags widget, also provid…

…e no_edit option to remove edit feature from many2many_tags widget

open a customer form from a meeting (to see the address, the phone, etc)
In case of mispelling, etc you need to go to the settings to change a m2m record. Most of the times menu items are hidden so its painful: product attributes, customer tags
the issue is worse in settings: in new settings there is a lot of m2m tag you cannot change from this screen.

with this commit clicking on m2m tag will give Edit option unless no_edit option is explicitly defined in xml view on field, edit form dialog is given to user to edit m2m tag

task-29227
PR-22957

Co-authored-by: Mohammed Shekha <msh@openerp.com>
Co-authored-by: Suraj Shukla <suh@odoo.com>
  • Loading branch information...
3 people authored and VincentSchippefilt committed Feb 9, 2018
1 parent 43132ed commit e4826f03b630075359f9413268486f8d08acedb3
@@ -1995,7 +1995,9 @@ var FieldMany2ManyTags = AbstractField.extend({
field_changed: '_onFieldChanged',
}),
events: _.extend({}, AbstractField.prototype.events, {
'click .o_badge_text': '_onClickBadge',
'click .o_delete': '_onDeleteTag',
'mousedown .o_edit_record': '_onEditRecord',
}),
fieldsToFetch: {
display_name: {type: 'char'},
@@ -2011,8 +2013,11 @@ var FieldMany2ManyTags = AbstractField.extend({
this.className += ' o_input';
}

this.can_write = 'can_write' in this.attrs ? JSON.parse(this.attrs.can_write) : true;

this.colorField = this.nodeOptions.color_field;
this.hasDropdown = false;
this.no_edit = this.nodeOptions.no_edit || !this.can_write;
},

//--------------------------------------------------------------------------
@@ -2069,6 +2074,44 @@ var FieldMany2ManyTags = AbstractField.extend({
});
}
},
/**
* @private
* @param {integer} id
*/
_editRecord: function (id) {
var self = this;
var record = _.findWhere(this.value.data, { res_id: id });
var tag_id = record.id;
var context = this.record.getContext(this.recordParams);
this._rpc({
model: record.model,
method: 'get_formview_id',
context: context,
args: [[record.res_id]],
}).then(function (view_id) {
new dialogs.FormViewDialog(self, {
res_model: record.model,
res_id: record.res_id,
title: _t("Edit: ") + self.string,
context: context,
view_id: view_id,
readonly: false,
on_saved: function (record, changed) {
if (changed) {
self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true});
self.trigger_up('reload', {db_id: tag_id});
}
},
}).open();
});
},
_getRenderDropdownContext: function (tagID) {
return {
'tag_id': tagID,
'widget': this,
'has_options': this.mode === 'edit' && !this.no_edit
};
},
/**
* Get the QWeb rendering context used by the tag template; this computation
* is placed in a separate function for other tags to override it.
@@ -2102,6 +2145,9 @@ var FieldMany2ManyTags = AbstractField.extend({
_renderEdit: function () {
var self = this;
this._renderTags();
if (this.mode === 'edit' && !this.no_edit) {
this.$(".o_badge_text").addClass("dropdown-toggle o-no-caret").attr('data-toggle', 'dropdown');
}
if (this.many2one) {
this.many2one.destroy();
}
@@ -2133,11 +2179,28 @@ var FieldMany2ManyTags = AbstractField.extend({
_renderTags: function () {
this.$el.html(qweb.render(this.tag_template, this._getRenderTagsContext()));
},
_renderTagDropdown: function (template, event) {
var tagID = $(event.currentTarget).parent().data('id');
this.$tag_dropdown = $(qweb.render(template, this._getRenderDropdownContext(tagID)));
$(event.currentTarget).after(this.$tag_dropdown);
this.$tag_dropdown.dropdown();
this.$tag_dropdown.attr("tabindex", 1).focus();
},

//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------

/**
* @private
* @param {MouseEvent} ev
*/
_onClickBadge: function (ev) {
ev.preventDefault();
if (this.mode === 'edit' && !this.no_edit) {
this._renderTagDropdown('FieldMany2ManyTag.TagDropdown', ev);
}
},
/**
* @private
* @param {MouseEvent} event
@@ -2147,6 +2210,14 @@ var FieldMany2ManyTags = AbstractField.extend({
event.stopPropagation();
this._removeTag($(event.target).parent().data('id'));
},
/**
* @private
* @param {MouseEvent} ev
*/
_onEditRecord: function (ev) {
ev.preventDefault();
this._editRecord($(ev.currentTarget).data('id'));
},
/**
* Controls the changes made in the internal m2o field.
*
@@ -2189,7 +2260,6 @@ var FieldMany2ManyTags = AbstractField.extend({

var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
events: _.extend({}, FieldMany2ManyTags.prototype.events, {
'click .dropdown-toggle': '_onOpenColorPicker',
'mousedown .o_colorpicker a': '_onUpdateColor',
'mousedown .o_colorpicker .o_hide_in_kanban': '_onUpdateColor',
}),
@@ -2210,23 +2280,20 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
* @private
* @param {MouseEvent} ev
*/
_onOpenColorPicker: function (ev) {
_onClickBadge: function (ev) {
ev.preventDefault();
var tagID = $(ev.currentTarget).parent().data('id');
var tagColor = $(ev.currentTarget).parent().data('color');
var tag = _.findWhere(this.value.data, { res_id: tagID });
if (tag && this.colorField in tag.data) { // if there is a color field on the related model
this.$color_picker = $(qweb.render('FieldMany2ManyTag.colorpicker', {
'widget': this,
'tag_id': tagID,
}));
var hasColorField = tag && this.colorField in tag.data;

$(ev.currentTarget).after(this.$color_picker);
this.$color_picker.dropdown();
this.$color_picker.attr("tabindex", 1).focus();
if (hasColorField) {
var tagColor = $(ev.currentTarget).parent().data('color');
this._renderTagDropdown('FieldMany2ManyTag.colorpicker', ev);
if (!tagColor) {
this.$('.custom-checkbox input').prop('checked', true);
this.$tag_dropdown.find('.o_checkbox input').prop('checked', true);
}
} else {
this._super.apply(this, arguments);
}
},
/**
@@ -2247,7 +2314,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
var changes = {};

if ($target.is('.o_hide_in_kanban')) {
var $checkbox = $('.o_hide_in_kanban .custom-checkbox input');
var $checkbox = $('.o_hide_in_kanban input');
$checkbox.prop('checked', !$checkbox.prop('checked')); // toggle checkbox
this.prevColors = this.prevColors ? this.prevColors : {};
if ($checkbox.is(':checked')) {
@@ -150,14 +150,31 @@
}
}

.tagcolor_dropdown_menu {
.o_tag_dropdown_menu {
min-width: 150px; // down from 160px of .dropdown-menu
margin-right: 0px; // cancel right margin of .dropdown-menu
}

.o_colorpicker > ul {
@include o-tag-colorpicker;
white-space: normal;
.o_tag_dropdown_options > ul {
margin-bottom: 0px;
padding-left: 0px;
list-style: none;
> li > a {
color: $o-main-text-color;
display: block;
padding: 3px 20px;
font-weight: normal;
&:hover {
background-color: #E5E5E5;
}
}
}
.o_colorpicker > ul {
@include o-tag-colorpicker;
white-space: normal;
margin: 5px 0;
.o_hide_in_kanban .o_checkbox {
display: inline-block;
}
}
}

@for $size from 1 through length($o-colors) {
@@ -837,47 +837,54 @@
<t t-set="colornames" t-value="['No color', 'Red', 'Orange', 'Yellow', 'Light blue', 'Dark purple', 'Salmon pink', 'Medium blue', 'Dark blue', 'Fushia', 'Green', 'Purple']"/>
<div t-attf-class="badge badge-pill #{hasDropdown ? 'dropdown' : ''} o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id" t-attf-title="Tag color: #{colornames[color]}">
<t t-set="_badge_text">
<span class="o_badge_text" t-att-title="el.display_name"><span role="img" t-attf-aria-label="Tag color: #{colornames[color]}"/><t t-esc="el.display_name"/></span>
<span><span role="img" t-attf-aria-label="Tag color: #{colornames[color]}"/><t t-esc="el.display_name"/></span>
</t>
<t t-if="hasDropdown">
<a role="button" href="#" class="dropdown-toggle o-no-caret" data-toggle="dropdown" aria-expanded="false">
<a role="button" href="#" class="o_badge_text dropdown-toggle o-no-caret" data-toggle="dropdown" aria-expanded="false" t-att-title="el.display_name">
<t t-raw="_badge_text"/>
</a>
</t>
<t t-else="">
<t t-raw="_badge_text"/>
<a href="#" class="o_badge_text" t-att-title="el.display_name"><t t-raw="_badge_text"/></a>
</t>
<a t-if="!readonly" href="#" class="fa fa-times o_delete" title="Delete" aria-label="Delete"/>
</div>
</t>
</t>
<t t-name="FieldMany2ManyTag.colorpicker">
<div class="o_colorpicker dropdown-menu tagcolor_dropdown_menu" role="menu">
<ul>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_1" data-color="1" title="Red" aria-label="Red"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_2" data-color="2" title="Orange" aria-label="Orange"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_3" data-color="3" title="Yellow" aria-label="Yellow"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_4" data-color="4" title="Light blue" aria-label="Light blue"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_5" data-color="5" title="Dark purple" aria-label="Dark purple"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_6" data-color="6" title="Salmon pink" aria-label="Salmon pink"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_7" data-color="7" title="Medium blue" aria-label="Medium blue"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_8" data-color="8" title="Dark blue" aria-label="Dark blue"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_9" data-color="9" title="Fushia" aria-label="Fushia"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_10" data-color="10" title="Green" aria-label="Green"/></li>
<li><a role="menuitem" href="#" t-att-data-id="tag_id" class="o_tag_color_11" data-color="11" title="Purple" aria-label="Purple"/></li>
<li> <!-- checkbox for tag color 0 -->
<div role="menuitem" class="o_hide_in_kanban"
t-att-data-id="tag_id"
t-att-data-color="0">
<div class="custom-control custom-checkbox">
<input type="checkbox" id="o_hide_in_kanban_checkbox" class="custom-control-input"/>
<label for="o_hide_in_kanban_checkbox" class="custom-control-label">Hide in Kanban</label>
</div>
</div>
</li>
</ul>
<t t-name="FieldMany2ManyTag.TagDropdown">
<div class="o_tag_dropdown_menu dropdown-menu" role="menu">
<div t-if="has_options" class="o_tag_dropdown_options">
<ul>
<li><a href="#" class="o_edit_record" t-att-data-id="tag_id">Edit</a></li>
</ul>
</div>
</div>
</t>
<t t-name="FieldMany2ManyTag.colorpicker" t-extend="FieldMany2ManyTag.TagDropdown">
<t t-jquery=".o_tag_dropdown_menu" t-operation="append">
<div t-if="has_options" class="divider"/>
<div class="o_colorpicker">
<ul>
<li t-foreach="11" t-as="color">
<a href="#"
t-att-data-id="tag_id"
t-att-data-color="color+1"
t-attf-class="o_tag_color_#{color+1}"/>
</li>
<li> <!-- checkbox for tag color 0 -->
<div class="o_hide_in_kanban"
t-att-data-id="tag_id"
t-att-data-color="0">
<div class="o_checkbox">
<input type="checkbox"/>
<span/>
</div> Hide in Kanban
</div>
</li>
</ul>
</div>
</t>
</t>
<t t-name="ProgressBar">
<div class="o_progressbar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div t-if="widget.title" class="o_progressbar_title"><t t-esc="widget.title"/></div><div class="o_progress">
@@ -754,15 +754,15 @@ QUnit.module('fields', {}, function () {

assert.containsOnce(form, '.o_data_row',
"the record should have been added to the relation");
assert.strictEqual(form.$('.o_data_row:first .o_badge_text').text(), 'leonardodonatello',
assert.strictEqual(form.$('.o_data_row:first .o_badge_text span').text(), 'leonardodonatello',
"inner m2m should have been fetched and correctly displayed");

await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a'));
await testUtils.dom.click($('.modal .o_data_row:first'));

assert.containsN(form, '.o_data_row', 2,
"the second record should have been added to the relation");
assert.strictEqual(form.$('.o_data_row:nth(1) .o_badge_text').text(), 'donatelloraphael',
assert.strictEqual(form.$('.o_data_row:nth(1) .o_badge_text span').text(), 'donatelloraphael',
"inner m2m should have been fetched and correctly displayed");

assert.verifySteps([
@@ -5008,14 +5008,14 @@ QUnit.module('fields', {}, function () {
'should display the line in editable mode');
assert.strictEqual(form.$('.o_field_many2one input').val(), "xpad",
'should display the product xpad');
assert.strictEqual(form.$('.o_field_many2manytags.o_input .o_badge_text').text(), "first record",
assert.strictEqual(form.$('.o_field_many2manytags.o_input .o_badge_text span').text(), "first record",
'should display the tag from the onchange');

await testUtils.dom.click(form.$('input.o_field_integer[name="int_field"]'));

assert.strictEqual(form.$('.o_data_cell.o_required_modifier').text(), "xpad",
'should display the product xpad');
assert.strictEqual(form.$('.o_field_many2manytags:not(.o_input) .o_badge_text').text(), "first record",
assert.strictEqual(form.$('.o_field_many2manytags:not(.o_input) .o_badge_text span').text(), "first record",
'should display the tag in readonly');

// enable the many2many onchange and generate it
@@ -5047,15 +5047,15 @@ QUnit.module('fields', {}, function () {
'should display the line in editable mode');
assert.strictEqual(form.$('.o_field_many2one input').val(), "xenomorphe",
'should display the product xenomorphe');
assert.strictEqual(form.$('.o_field_many2manytags.o_input .o_badge_text').text(), "second record",
assert.strictEqual(form.$('.o_field_many2manytags.o_input .o_badge_text span').text(), "second record",
'should display the tag from the onchange');

// put list in readonly mode
await testUtils.dom.click(form.$('input.o_field_integer[name="int_field"]'));

assert.strictEqual(form.$('.o_data_cell.o_required_modifier').text(), "xenomorphexphone",
'should display the product xphone and xenomorphe');
assert.strictEqual(form.$('.o_field_many2manytags:not(.o_input) .o_badge_text').text(), "second recordfirst record",
assert.strictEqual(form.$('.o_field_many2manytags:not(.o_input) .o_badge_text span').text(), "second recordfirst record",
'should display the tag in readonly (first record and second record)');

await testUtils.fields.editInput(form.$('input.o_field_integer[name="int_field"]'), '10');
@@ -5293,7 +5293,7 @@ QUnit.module('fields', {}, function () {
"one2many list should contain 2 rows");
assert.containsN(form, '.o_list_view .o_field_many2manytags[name="partner_ids"] .badge', 2,
"m2mtags should contain two tags");
assert.strictEqual(form.$('.o_list_view .o_field_many2manytags[name="partner_ids"] .o_badge_text').text(),
assert.strictEqual(form.$('.o_list_view .o_field_many2manytags[name="partner_ids"] .o_badge_text span').text(),
'aaafirst record', "tag names should have been correctly loaded");

form.destroy();
@@ -6270,7 +6270,7 @@ QUnit.module('fields', {}, function () {
"timmy should be displayed in the form view");
assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"] .badge').length, 1,
"m2mtags should contain one tag");
assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"] .o_badge_text').text(),
assert.strictEqual($('.modal .o_field_many2manytags[name="timmy"] .o_badge_text span').text(),
'gold', "tag name should have been correctly loaded");

form.destroy();
Oops, something went wrong.

0 comments on commit e4826f0

Please sign in to comment.
You can’t perform that action at this time.