diff options
| author | Christophe Simonis <chs@odoo.com> | 2017-07-14 15:51:59 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2017-07-14 15:51:59 +0200 |
| commit | db2f82d5f1a8c4fdbbd61a0aa7daef2def0ef06e (patch) | |
| tree | 4dd6a0d816180b06963a245d01a817ebcdaf7868 /addons/mail/static | |
| parent | 55d72e1ab4a64f52e040911a8b4b4c8ce9443f65 (diff) | |
| parent | 4ca3d9cadf9607c95880bb5222041bced7b13ec0 (diff) | |
[MERGE] forward port branch saas-16 up to 4ca3d9cadf9
Diffstat (limited to 'addons/mail/static')
| -rw-r--r-- | addons/mail/static/src/js/many2many_tags_email.js | 144 | ||||
| -rw-r--r-- | addons/mail/static/src/js/thread_field.js | 4 | ||||
| -rw-r--r-- | addons/mail/static/tests/chatter_tests.js | 147 |
3 files changed, 260 insertions, 35 deletions
diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js index 52e213ddb7d..d266147bcc7 100644 --- a/addons/mail/static/src/js/many2many_tags_email.js +++ b/addons/mail/static/src/js/many2many_tags_email.js @@ -1,53 +1,131 @@ odoo.define('mail.many2manytags', function (require) { "use strict"; -// FIXME: apply https://github.com/odoo/odoo/commit/1217ae914b313df7fe8511c138871e585bb21c81 - +var BasicModel = require('web.BasicModel'); var core = require('web.core'); var form_common = require('web.view_dialogs'); var field_registry = require('web.field_registry'); var relational_fields = require('web.relational_fields'); +var M2MTags = relational_fields.FieldMany2ManyTags; var _t = core._t; -var FieldMany2ManyTags = relational_fields.FieldMany2ManyTags; +BasicModel.include({ + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- -var FieldMany2ManyTagsEmail = FieldMany2ManyTags.extend({ - init: function() { - this.values_checking = []; - this._super.apply(this, arguments); + /** + * @private + * @param {Object} record - an element from the localData + * @param {string} fieldName + * @return {Deferred<Object>} the deferred is resolved with the + * invalidPartnerIds + */ + _setInvalidMany2ManyTagsEmail: function (record, fieldName) { + var self = this; + var localID = (record._changes && fieldName in record._changes) ? + record._changes[fieldName] : + record.data[fieldName]; + var list = this.localData[localID]; + var invalidPartnerIds = []; + _.each(list.data, function (id) { + var record = self.localData[id]; + if (!record.data.email) { + invalidPartnerIds.push(record); + } + }); + var def; + if (invalidPartnerIds) { + // remove invalid partners + var changes = {operation: 'REMOVE', ids: _.pluck(invalidPartnerIds, 'id')}; + def = this._applyX2ManyChange(record, fieldName, changes); + } + return $.when(def).then(function () { + return $.when({ + invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'), + }); + }); }, +}); + +var FieldMany2ManyTagsEmail = M2MTags.extend({ + fieldsToFetch: _.extend({}, M2MTags.prototype.fieldsToFetch, { + email: {type: 'char'}, + }), + specialData: "_setInvalidMany2ManyTagsEmail", + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- - add_id: function (id) { + /** + * Open a popup for each invalid partners (without email) to fill the email. + * + * @private + * @returns {Deferred} + */ + _checkEmailPopup: function () { var self = this; - var _super = this._super.bind(this); - // check partner has email - this.trigger_up('perform_model_rpc', { - model: 'res.partner', - method: 'search', - args: [[ - ["id", "=", id], - ["email", "=", false], - ]], - on_success: function(partner_id) { - if (partner_id.length) { - // invalid partner - var pop = new form_common.FormViewDialog(self, { - res_model: 'res.partner', - res_id: partner_id[0], - title: _t("Please complete partner's informations and Email"), - }).open(); - pop.on('write_completed', self, function () { - // self.values_checking = _.without(self.values_checking, id); - _super.apply(self, [id]); - }); - } else { - // valid partner - _super.apply(self, [id]); - } + + var popupDefs = []; + var validPartners = []; + + // propose the user to correct invalid partners + _.each(this.record.specialData[this.name].invalidPartnerIds, function (resID) { + var def = $.Deferred(); + popupDefs.push(def); + + var pop = new form_common.FormViewDialog(self, { + res_model: self.field.relation, + res_id: resID, + title: _t("Please complete partner's informations and Email"), + on_saved: function (record) { + if (record.data.email) { + validPartners.push(record.res_id); + } + }, + }).open(); + pop.on('closed', self, function () { + def.resolve(); + }); + }); + return $.when.apply($, popupDefs).then(function() { + // All popups have been processed for the given ids + // It is now time to set the final value with valid partners ids. + validPartners = _.uniq(validPartners); + if (validPartners.length) { + var values = _.map(validPartners, function (id) { + return {id: id}; + }); + self._setValue({ + operation: 'ADD_M2M', + ids: values, + }); } }); }, + /** + * Override to check if all many2many values have an email set before + * rendering the widget. + * + * @override + * @private + */ + _render: function () { + var self = this; + var def = $.Deferred(); + var _super = this._super.bind(this); + if (this.record.specialData[this.name].invalidPartnerIds.length) { + def = this._checkEmailPopup(); + } else { + def.resolve(); + } + return def.then(function () { + return _super.apply(self, arguments); + }); + }, }); field_registry.add('many2many_tags_email', FieldMany2ManyTagsEmail); diff --git a/addons/mail/static/src/js/thread_field.js b/addons/mail/static/src/js/thread_field.js index 481dc49af36..0d7635cae8b 100644 --- a/addons/mail/static/src/js/thread_field.js +++ b/addons/mail/static/src/js/thread_field.js @@ -21,7 +21,7 @@ var ThreadField = AbstractField.extend(chat_mixin, { this.msgIDs = this.value.res_ids; }, willStart: function () { - return this._chatReady(); + return this.alive(this._chatReady()); }, start: function () { var self = this; @@ -43,7 +43,7 @@ var ThreadField = AbstractField.extend(chat_mixin, { var def1 = this.thread.appendTo(this.$el); var def2 = this._super.apply(this, arguments); - return $.when(def1, def2).then(function () { + return this.alive($.when(def1, def2)).then(function () { // unwrap the thread to remove an unnecessary level on div self.setElement(self.thread.$el); diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js index 2bb5742e378..f268851463d 100644 --- a/addons/mail/static/tests/chatter_tests.js +++ b/addons/mail/static/tests/chatter_tests.js @@ -11,7 +11,9 @@ var testUtils = require('web.test_utils'); var BasicComposer = Composers.BasicComposer; +var createAsyncView = testUtils.createAsyncView; var createView = testUtils.createView; +var createAsyncView = testUtils.createAsyncView; QUnit.module('mail', {}, function () { @@ -862,5 +864,150 @@ QUnit.test('followers widget: do not display follower duplications', function (a form.destroy(); }); +QUnit.test('does not render and crash when destroyed before chat system is ready', function (assert) { + assert.expect(0); + + var def = $.Deferred(); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<sheet>' + + '<field name="foo"/>' + + '</sheet>' + + '<div class="oe_chatter">' + + '<field name="message_follower_ids" widget="mail_followers"/>' + + '<field name="message_ids" widget="mail_thread"/>' + + '<field name="activity_ids" widget="mail_activity"/>' + + '</div>' + + '</form>', + res_id: 2, + mockRPC: function (route, args) { + if (route === '/mail/read_followers') { + return $.when({ + followers: [], + subtypes: [], + }); + } + return this._super(route, args); + }, + intercepts: { + chat_manager_ready: function (event) { + // we delay the return of the chat_manager ready event + event.data.callback(def); + }, + get_messages: function (event) { + event.stopPropagation(); + event.data.callback($.when([{ + attachment_ids: [], + body: "", + date: moment("2016-12-20 09:35:40"), + id: 34, + res_id: 3, + author_id: ["3", "Fu Ck Mil Grom"], + }])); + }, + get_bus: function (event) { + event.stopPropagation(); + event.data.callback(new Bus()); + }, + get_session: function (event) { + event.stopPropagation(); + event.data.callback({uid: 1}); + }, + }, + }); + + form.destroy(); + // here, the chat_manager system is ready, and the chatter can try to render + // itself. We simply make sure here that no crashes occur (since the form + // view is destroyed, all rpcs will be dropped, and many other mechanisms + // relying on events will not work, such as _getBus) + def.resolve(); +}); + +QUnit.module('FieldMany2ManyTagsEmail', { + beforeEach: function () { + this.data = { + partner: { + fields: { + display_name: { string: "Displayed name", type: "char" }, + timmy: { string: "pokemon", type: "many2many", relation: 'partner_type'}, + }, + records: [{ + id: 1, + display_name: "first record", + timmy: [], + }], + }, + partner_type: { + fields: { + name: {string: "Partner Type", type: "char"}, + email: {string: "Email", type: "char"}, + }, + records: [ + {id: 12, display_name: "gold", email: 'coucou@petite.perruche'}, + {id: 14, display_name: "silver", email: ''}, + ] + }, + }; + }, +}); + +QUnit.test('fieldmany2many tags email', function (assert) { + assert.expect(11); + var done = assert.async(); + + this.data.partner.records[0].timmy = [12, 14]; + + // the modals need to be closed before the form view rendering + createAsyncView({ + View: FormView, + model: 'partner', + data: this.data, + res_id: 1, + arch:'<form string="Partners">' + + '<sheet>' + + '<field name="display_name"/>' + + '<field name="timmy" widget="many2many_tags_email"/>' + + '</sheet>' + + '</form>', + viewOptions: { + mode: 'edit', + }, + mockRPC: function (route, args) { + if (args.method ==='read' && args.model === 'partner_type') { + assert.step(args.args[0]); + assert.deepEqual(args.args[1] , ['display_name', 'email'], "should read the email"); + } + return this._super.apply(this, arguments); + }, + archs: { + 'partner_type,false,form': '<form string="Types"><field name="display_name"/><field name="email"/></form>', + }, + }).then(function (form) { + // should read it 3 times (1 with the form view, one with the form dialog and one after save) + assert.verifySteps([[12, 14], [14], [14]]); + assert.strictEqual(form.$('.o_field_many2manytags[name="timmy"] span.o_tag_color_10').length, 2, + "the second tag should be present"); + + form.destroy(); + done(); + }); + + assert.strictEqual($('.modal-body.o_act_window').length, 1, + "there should be one modal opened to edit the empty email"); + assert.strictEqual($('.modal-body.o_act_window input[name="display_name"]').val(), "silver", + "the opened modal should be a form view dialog with the partner_type 14"); + assert.strictEqual($('.modal-body.o_act_window input[name="email"]').length, 1, + "there should be an email field in the modal"); + + // set the email and save the modal (will render the form view) + $('.modal-body.o_act_window input[name="email"]').val('coucou@petite.perruche').trigger('input'); + $('.modal-footer .btn-primary').click(); +}); + }); }); |
