summaryrefslogtreecommitdiff
path: root/addons/mail/static
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mail/static')
-rw-r--r--addons/mail/static/src/js/many2many_tags_email.js144
-rw-r--r--addons/mail/static/src/js/thread_field.js4
-rw-r--r--addons/mail/static/tests/chatter_tests.js147
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();
+});
+
});
});