summaryrefslogtreecommitdiff
path: root/addons/mail/static
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-06-28 19:01:52 +0200
committerChristophe Simonis <chs@odoo.com>2018-06-28 19:01:52 +0200
commitc8bb02141d2801c243ae65ba6800b07f090bb5c6 (patch)
tree2d099c5577df4783a6db3d853cf0141bf41c1aa9 /addons/mail/static
parent4e2733e83a1f42773fb28f66431a0d6fb0393be9 (diff)
parent3cdcbce93cbde548d2d24426ee8de1d93f06706e (diff)
[MERGE] forward port branch saas-11.3 up to 3cdcbce93cb
Diffstat (limited to 'addons/mail/static')
-rw-r--r--addons/mail/static/src/js/chatter.js70
-rw-r--r--addons/mail/static/src/js/chatter_composer.js25
-rw-r--r--addons/mail/static/src/js/services/chat_window_manager.js2
-rw-r--r--addons/mail/static/src/scss/composer.scss2
-rw-r--r--addons/mail/static/tests/chatter_tests.js279
-rw-r--r--addons/mail/static/tests/helpers/test_utils.js3
6 files changed, 366 insertions, 15 deletions
diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js
index e48a7c60028..63948b8429a 100644
--- a/addons/mail/static/src/js/chatter.js
+++ b/addons/mail/static/src/js/chatter.js
@@ -24,6 +24,7 @@ var QWeb = core.qweb;
var Chatter = Widget.extend({
template: 'mail.Chatter',
custom_events: {
+ discard_record_changes: '_onDiscardRecordChanges',
reload_mail_fields: '_onReloadMailFields',
},
events: {
@@ -155,6 +156,37 @@ var Chatter = Widget.extend({
}
},
/**
+ * Discard changes on the record.
+ *
+ * @private
+ * @returns {$.Deferred} resolved if successfully discarding changes on
+ * the record, rejected otherwise
+ */
+ _discardChanges: function () {
+ var def = $.Deferred();
+ this.trigger_up('discard_changes', {
+ recordID: this.record.id,
+ onSuccess: def.resolve.bind(def),
+ onFailure: def.reject.bind(def),
+ });
+ return def;
+ },
+ /**
+ * Discard changes on the record if the message will reload the record
+ * after posting it
+ *
+ * @private
+ * @param {Object} message
+ * @return {$.Deferred} resolved if no reload or proceed to discard the
+ * changes on the record, rejected otherwise
+ */
+ _discardOnReload: function (message) {
+ if (this._reloadAfterPost(message)) {
+ return this._discardChanges();
+ }
+ return $.when();
+ },
+ /**
* @private
*/
_disableChatter: function () {
@@ -199,11 +231,13 @@ var Chatter = Widget.extend({
self.composer.focus();
}
self.composer.on('post_message', self, function (message) {
- self.fields.thread.postMessage(message).then(function () {
- self._closeComposer(true);
- if (self.postRefresh === 'always' || (self.postRefresh === 'recipients' && message.partner_ids.length)) {
- self.trigger_up('reload');
- }
+ self._discardOnReload(message).then(function () {
+ self.fields.thread.postMessage(message).then(function () {
+ self._closeComposer(true);
+ if (self._reloadAfterPost(message)) {
+ self.trigger_up('reload');
+ }
+ });
});
});
self.composer.on('need_refresh', self, self.trigger_up.bind(self, 'reload'));
@@ -216,6 +250,24 @@ var Chatter = Widget.extend({
});
},
/**
+ * State if the record will be reloaded after posting a message.
+ * Useful to warn the user of unsaved changes if the record is dirty.
+ *
+ * @private
+ * @param {Object} message
+ * @param {Array} [message.partner_ids] list of recipients of a message
+ * @return {boolean} true if record will be reloaded after posting the
+ * message, false otherwise
+ */
+ _reloadAfterPost: function (message) {
+ return this.postRefresh === 'always' ||
+ (
+ this.postRefresh === 'recipients' &&
+ message.partner_ids &&
+ message.partner_ids.length
+ );
+ },
+ /**
* @private
* @param {Deferred} def
* @returns {Deferred}
@@ -314,8 +366,16 @@ var Chatter = Widget.extend({
//--------------------------------------------------------------------------
/**
+ * Discard changes on the record.
+ * This is notified by the composer, when opening the full-composer.
+ *
* @private
+ * @param {OdooEvent} ev
+ * @param {function} ev.data.proceed callback to tell to proceed
*/
+ _onDiscardRecordChanges: function (ev) {
+ this._discardChanges().then(ev.data.proceed);
+ },
_onOpenComposerMessage: function () {
var self = this;
if (!this.suggested_partners_def) {
diff --git a/addons/mail/static/src/js/chatter_composer.js b/addons/mail/static/src/js/chatter_composer.js
index da85f331863..b5c17f3f593 100644
--- a/addons/mail/static/src/js/chatter_composer.js
+++ b/addons/mail/static/src/js/chatter_composer.js
@@ -202,14 +202,23 @@ var ChatterComposer = composer.BasicComposer.extend({
}
var self = this;
- var recipient_done = $.Deferred();
- if (this.options.is_log) {
- recipient_done.resolve([]);
- } else {
- var checked_suggested_partners = this.get_checked_suggested_partners();
- recipient_done = this.check_suggested_partners(checked_suggested_partners);
- }
- recipient_done.then(function (partner_ids) {
+ var recipientDoneDef = $.Deferred();
+
+ // any operation on the full-composer will reload the record, so
+ // warn the user that any unsaved changes on the record will be lost.
+ this.trigger_up('discard_record_changes', {
+ proceed: function () {
+ if (self.options.is_log) {
+ recipientDoneDef.resolve([]);
+ } else {
+ var checked_suggested_partners = self.get_checked_suggested_partners();
+ self.check_suggested_partners(checked_suggested_partners)
+ .then(recipientDoneDef.resolve.bind(recipientDoneDef));
+ }
+ },
+ });
+
+ recipientDoneDef.then(function (partner_ids) {
var context = {
default_parent_id: self.id,
default_body: utils.get_text2html(self.$input.val()),
diff --git a/addons/mail/static/src/js/services/chat_window_manager.js b/addons/mail/static/src/js/services/chat_window_manager.js
index bea6eaf0ace..fd45b336c23 100644
--- a/addons/mail/static/src/js/services/chat_window_manager.js
+++ b/addons/mail/static/src/js/services/chat_window_manager.js
@@ -108,7 +108,7 @@ var ChatWindowManager = AbstractService.extend({
uuid: session.uuid,
name: session.name,
keep_unread: options.passively, // don't automatically mark unread messages as seen
- window: new ExtendedChatWindow(web_client, session.id, prefix + session.name, session.is_folded, session.unread_counter, windowOptions),
+ window: new ExtendedChatWindow(this, session.id, prefix + session.name, session.is_folded, session.unread_counter, windowOptions),
};
chatSession.window.on("close_chat_session", null, function () {
self._closeChat(chatSession);
diff --git a/addons/mail/static/src/scss/composer.scss b/addons/mail/static/src/scss/composer.scss
index cb079f81558..fed625ace35 100644
--- a/addons/mail/static/src/scss/composer.scss
+++ b/addons/mail/static/src/scss/composer.scss
@@ -152,7 +152,7 @@
&.o_chat_inline_composer {
.o_composer_container {
- @include o-flex(1, 0, auto);
+ @include o-flex(1, 1, auto);
}
.o_composer {
padding: $o-mail-chatter-gap $o-mail-chatter-gap 0 $o-mail-chatter-gap;
diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js
index 46de2e22d3f..f1f6097cb97 100644
--- a/addons/mail/static/tests/chatter_tests.js
+++ b/addons/mail/static/tests/chatter_tests.js
@@ -611,6 +611,285 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert)
form.destroy();
});
+QUnit.test('chatter: discard changes on message post with post_refresh "always"', function (assert) {
+ // After posting a message that always reloads the record, if the record
+ // is dirty (= has some unsaved changes), we should warn the user that
+ // these changes will be lost if he proceeds.
+ assert.expect(2);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ services: this.services,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="foo"/>' +
+ '</sheet>' +
+ '<div class="oe_chatter">' +
+ '<field name="message_ids" widget="mail_thread"' +
+ ' options="{\'display_log_button\': True, \'post_refresh\': \'always\'}"/>' +
+ '</div>' +
+ '</form>',
+ res_id: 2,
+ session: {},
+ mockRPC: function (route, args) {
+ if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") {
+ return $.when({2: []});
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ // Make record dirty
+ form.$('.o_form_sheet input').val('trululu').trigger('input');
+
+ // Send a message
+ form.$('.o_chatter_button_new_message').click();
+ form.$('.oe_chatter .o_composer_text_field:first()').val("My first message");
+ form.$('.oe_chatter .o_composer_button_send').click();
+
+ var $modal = $('.modal-dialog');
+ assert.strictEqual($modal.length, 1, "should have a modal opened");
+ assert.strictEqual($modal.find('.modal-body').text(),
+ "The record has been modified, your changes will be discarded. Do you want to proceed?",
+ "should warn the user that any unsaved changes will be lost");
+
+ form.destroy();
+});
+
+QUnit.test('chatter: discard changes on message post without post_refresh', function (assert) {
+ // After posting a message, if the record is dirty and there are no
+ // post_refresh rule, it will not discard the changes on the record.
+ assert.expect(2);
+
+ var hasDiscardChanges = false; // set if `discard_changes` has been triggered up
+ var messages = [];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ services: this.services,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="foo"/>' +
+ '</sheet>' +
+ '<div class="oe_chatter">' +
+ '<field name="message_ids" widget="mail_thread"' +
+ ' options="{\'display_log_button\': True}"/>' +
+ '</div>' +
+ '</form>',
+ res_id: 2,
+ session: {},
+ mockRPC: function (route, args) {
+ if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") {
+ return $.when({2: []});
+ }
+ if (args.method === 'message_format') {
+ var requested_msgs = _.filter(messages, function (msg) {
+ return _.contains(args.args[0], msg.id);
+ });
+ return $.when(requested_msgs);
+ }
+ if (args.method === 'message_post') {
+ messages.push({
+ attachment_ids: [],
+ author_id: ["42", "Me"],
+ body: args.kwargs.body,
+ date: moment().format('YYYY-MM-DD HH:MM:SS'), // now
+ displayed_author: "Me",
+ id: 42,
+ is_note: args.kwargs.subtype === 'mail.mt_note',
+ is_starred: false,
+ model: 'partner',
+ res_id: 2,
+ });
+ return $.when(42);
+ }
+ return this._super(route, args);
+ },
+ intercepts: {
+ discard_changes: function () {
+ hasDiscardChanges = true; // should not do that
+ },
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ // Make record dirty
+ form.$('.o_form_sheet input').val('trululu').trigger('input');
+
+ // Send a message
+ form.$('.o_chatter_button_new_message').click();
+ form.$('.oe_chatter .o_composer_text_field:first()').val("My first message");
+ form.$('.oe_chatter .o_composer_button_send').click();
+
+ var $modal = $('.modal-dialog');
+ assert.strictEqual($modal.length, 0, "should have no modal opened");
+ assert.notOk(hasDiscardChanges);
+
+ form.destroy();
+});
+
+QUnit.test('chatter: discard changes on message post with post_refresh "recipients"', function (assert) {
+ // After posting a message with mentions, the record will be reloaded,
+ // as the rpc `message_post` may make changes on some fields of the record.
+ // If the record is dirty (= has some unsaved changes), we should warn the
+ // user that these changes will be lost if he proceeds.
+ assert.expect(2);
+ var done = assert.async();
+
+ var getSuggestionsDef = $.Deferred();
+
+ var messages = [];
+ var bus = new Bus();
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ services: this.services,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="foo"/>' +
+ '</sheet>' +
+ '<div class="oe_chatter">' +
+ '<field name="message_ids" widget="mail_thread"' +
+ ' options="{\'display_log_button\': True, \'post_refresh\': \'recipients\'}"/>' +
+ '</div>' +
+ '</form>',
+ res_id: 2,
+ session: {},
+ mockRPC: function (route, args) {
+ if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") {
+ return $.when({2: [[42, "Me"]]});
+ }
+ if (args.method === 'get_mention_suggestions') {
+ getSuggestionsDef.resolve();
+ return $.when([{email: "me@odoo.com", id: 42, name: "Me"}]);
+ }
+ if (args.method === 'message_format') {
+ var requested_msgs = _.filter(messages, function (msg) {
+ return _.contains(args.args[0], msg.id);
+ });
+ return $.when(requested_msgs);
+ }
+ if (args.method === 'message_post') {
+ messages.push({
+ attachment_ids: [],
+ author_id: ["42", "Me"],
+ body: args.kwargs.body,
+ date: moment().format('YYYY-MM-DD HH:MM:SS'), // now
+ displayed_author: "Me",
+ id: 42,
+ is_note: args.kwargs.subtype === 'mail.mt_note',
+ is_starred: false,
+ model: 'partner',
+ res_id: 2,
+ });
+ return $.when(42);
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ // Make record dirty
+ form.$('.o_form_sheet input').val('trululu').trigger('input');
+
+ // create a new message
+ form.$('.o_chatter_button_new_message').click();
+
+ // Add a user as mention
+ form.$('.oe_chatter .o_composer_text_field:first()').val("@");
+
+ var $input = form.$('.oe_chatter .o_composer_text_field:first()');
+ $input.val('@');
+ // the cursor position must be set for the mention manager to detect that we are mentionning
+ $input[0].selectionStart = 1;
+ $input[0].selectionEnd = 1;
+ $input.trigger('keyup');
+
+ getSuggestionsDef
+ .then(concurrency.delay.bind(concurrency, 0))
+ .then(function () {
+ // click on mention
+ $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER}));
+
+ // untick recipient as follower (prompts a res.partner form otherwise)
+ form.$('.o_checkbox input').prop('checked', false);
+
+ // send message
+ form.$('.oe_chatter .o_composer_button_send').click();
+
+ var $modal = $('.modal-dialog');
+ assert.strictEqual($modal.length, 1, "should have a modal opened");
+ assert.strictEqual($modal.find('.modal-body').text(),
+ "The record has been modified, your changes will be discarded. Do you want to proceed?",
+ "should warn the user that any unsaved changes will be lost");
+
+ form.destroy();
+ done();
+ });
+});
+
+QUnit.test('chatter: discard changes on opening full-composer', function (assert) {
+ // When we open the full-composer, any following operations by the user
+ // will reload the record (even closing the full-composer). Therefore,
+ // we should warn the user when we open the full-composer if the record
+ // is dirty (= has some unsaved changes).
+ assert.expect(2);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ services: this.services,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="foo"/>' +
+ '</sheet>' +
+ '<div class="oe_chatter">' +
+ '<field name="message_ids" widget="mail_thread"' +
+ ' options="{\'display_log_button\': True,' +
+ ' \'post_refresh\': \'always\'}"/>' +
+ '</div>' +
+ '</form>',
+ res_id: 2,
+ session: {},
+ mockRPC: function (route, args) {
+ if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") {
+ return $.when({2: []});
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ // Make record dirty
+ form.$('.o_form_sheet input').val('trululu').trigger('input');
+
+ // Open full-composer
+ form.$('.o_chatter_button_new_message').click();
+ form.$('.o_composer_button_full_composer').click();
+
+ var $modal = $('.modal-dialog');
+ assert.strictEqual($modal.length, 1, "should have a modal opened");
+ assert.strictEqual($modal.find('.modal-body').text(),
+ "The record has been modified, your changes will be discarded. Do you want to proceed?",
+ "should warn the user that any unsaved changes will be lost");
+
+ form.destroy();
+});
+
QUnit.test('chatter: Attachment viewer', function (assert) {
assert.expect(6);
this.data.partner.records[0].message_ids = [1];
diff --git a/addons/mail/static/tests/helpers/test_utils.js b/addons/mail/static/tests/helpers/test_utils.js
index 3382fa90dd0..527d873863e 100644
--- a/addons/mail/static/tests/helpers/test_utils.js
+++ b/addons/mail/static/tests/helpers/test_utils.js
@@ -38,6 +38,9 @@ function createBusService(bus) {
* Do nothing
*/
start_polling: function () {},
+ is_odoo_focused: function () {
+ return true;
+ },
});
},