diff --git a/mail_tracking/__manifest__.py b/mail_tracking/__manifest__.py
index 6e81b83ee..f33f1d9a9 100644
--- a/mail_tracking/__manifest__.py
+++ b/mail_tracking/__manifest__.py
@@ -31,8 +31,9 @@
],
"qweb": [
"static/src/xml/mail_tracking.xml",
- "static/src/xml/failed_message.xml",
- "static/src/xml/discuss.xml",
+ "static/src/xml/failed_message/common.xml",
+ "static/src/xml/failed_message/widget.xml",
+ "static/src/xml/failed_message/discuss.xml",
],
'demo': [
'demo/demo.xml',
diff --git a/mail_tracking/static/src/js/failed_message.js b/mail_tracking/static/src/js/failed_message.js
deleted file mode 100644
index e827ef60d..000000000
--- a/mail_tracking/static/src/js/failed_message.js
+++ /dev/null
@@ -1,575 +0,0 @@
-/* Copyright 2019 Alexandre Díaz
- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
-odoo.define('mail_tracking.FailedMessage', function (require) {
- "use strict";
-
- var AbstractField = require('web.AbstractField');
- var BasicModel = require('web.BasicModel');
- var BasicView = require('web.BasicView');
- var Chatter = require('mail.Chatter');
- var Discuss = require('mail.Discuss');
- var MailManager = require('mail.Manager');
- var Mailbox = require('mail.model.Mailbox');
- var MailManagerNotif = require('mail.Manager.Notification');
- var AbstractMessage = require('mail.model.AbstractMessage');
- var Message = require('mail.model.Message');
- var utils = require('mail.utils');
- var core = require('web.core');
- var field_registry = require('web.field_registry');
- var time = require('web.time');
-
- var QWeb = core.qweb;
- var _t = core._t;
-
- var FAILED_STATES = [
- 'error', 'rejected', 'spam', 'bounced', 'soft-bounced',
- ];
-
-
- /* COMMON */
- AbstractMessage.include({
-
- /**
- * Abstract method to be implemented
- *
- * @returns {Boolean}
- */
- isFailed: function () {
- return false;
- },
- });
-
- Message.include({
-
- /**
- * Init
- *
- * @param {Widget} parent
- * @param {Object} data
- */
- init: function (parent, data) {
- this._isFailedMessage = data.is_failed_message;
- this._super.apply(this, arguments);
- },
-
- /**
- * Implementation of the abstract method
- *
- * @returns {Boolean}
- */
- isFailed: function () {
- return _.contains(this._threadIDs, 'mailbox_failed');
- },
-
- /**
- * Adds/Remove message to/from failed thread
- *
- * @param {Boolean} failed
- */
- setFailed: function (failed) {
- if (failed) {
- this._addThread('mailbox_failed');
- } else {
- this.removeThread('mailbox_failed');
- }
- },
-
- /**
- * Process message mailbox
- */
- _processMailboxes: function () {
- this.setFailed(this._isFailedMessage);
- this._super.apply(this, arguments);
- },
- });
-
- MailManagerNotif.include({
-
- /**
- * Handle partner notification
- *
- * @private
- * @param {Object} data
- */
- _handlePartnerNotification: function (data) {
- if (data.type === 'toggle_tracking_status') {
- this._handlePartnerToggleFailedNotification(data);
- } else {
- this._super.apply(this, arguments);
- }
- },
-
- /**
- * Handle partner toggle failed notification
- *
- * @private
- * @param {Object} data
- */
- _handlePartnerToggleFailedNotification: function (data) {
- var self = this;
- var failed = this.getMailbox('failed');
- _.each(data.message_ids, function (messageID) {
- var message = _.find(self._messages, function (msg) {
- return msg.getID() === messageID;
- });
- if (message) {
- message.setFailed(data.needs_actions);
- if (message.isFailed() === false) {
- self._removeMessageFromThread(
- 'mailbox_failed', message);
- } else {
- self._addMessageToThreads(message, []);
- var channelFailed = self.getMailbox('failed');
- channelFailed.invalidateCaches();
- }
- self._mailBus.trigger('update_message', message);
- }
- });
-
- if (data.needs_actions) {
- // Increase failed counter if message is marked as failed
- failed.incrementMailboxCounter(data.message_ids.length);
- } else {
- // Decrease failed counter if message is remove from failed
- failed.decrementMailboxCounter(data.message_ids.length);
- }
-
- this._mailBus.trigger('update_failed', failed.getMailboxCounter());
- },
- });
-
-
- /* DISCUSS */
- Discuss.include({
- events: _.extend({}, Discuss.prototype.events, {
- 'click .o_failed_message_retry': '_onRetryFailedMessage',
- 'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
- }),
-
- /**
- * Sidebar QWeb button params
- *
- * @private
- * @returns {Object}
- */
- _sidebarQWebParams: function () {
- var failed = this.call('mail_service', 'getMailbox', 'failed');
- return {
- activeThreadID: this._thread ? this._thread.getID() : undefined,
- failedCounter: failed.getMailboxCounter(),
- };
- },
-
- /**
- * Render sidebar failed button
- *
- * @private
- * @returns {jQueryElementt}
- */
- _renderSidebar: function () {
- var $sidebar = this._super.apply(this, arguments);
- // Because Odoo implementation isn't designed to be inherited
- // properly we inject failed button using jQuery.
- var $failed_item = $(QWeb.render('mail_tracking.SidebarFailed',
- this._sidebarQWebParams()));
- $failed_item.insertAfter(
- $sidebar.find(".o_mail_discuss_title_main").filter(":last"));
- return $sidebar;
- },
-
- /**
- * Message Updated
- *
- * @private
- * @param {Object} message
- * @param {String} type
- */
- _onMessageUpdated: function (message, type) {
- var self = this;
- var currentThreadID = this._thread.getID();
- if (currentThreadID === 'mailbox_failed' && !message.isFailed()) {
- this._thread.fetchMessages(this.domain)
- .then(function () {
- var options = self._getThreadRenderingOptions();
- self._threadWidget.removeMessageAndRender(
- message.getID(), self._thread, options)
- .then(function () {
- self._updateButtonStatus(
- !self._thread.hasMessages(), type);
- });
- });
- } else {
- // Workaround to avoid calling '_fetchAndRenderThread' when
- // it's not really needed.
- this._super.apply(this, arguments);
- }
- },
-
- /**
- * Get thread rendering options
- *
- * @private
- * @returns {Object}
- */
- _getThreadRenderingOptions: function () {
- var values = this._super.apply(this, arguments);
- if (this._thread.getID() === 'mailbox_failed') {
- values.displayEmailIcons = true;
- values.displayReplyIcons = false;
- values.displayRetryButton = true;
- values.displayReviewedButton = true;
- }
- return values;
- },
-
- /**
- * Start listening events
- *
- * @private
- */
- _startListening: function () {
- this._super.apply(this, arguments);
- this.call('mail_service', 'getMailBus')
- .on('update_failed', this, this._throttledUpdateThreads);
- },
-
- // Handlers
- /**
- * @private
- * @param {Event} event
- */
- _onRetryFailedMessage: function (event) {
- event.preventDefault();
- var messageID = $(event.currentTarget).data('message-id');
- this.do_action('mail.mail_resend_message_action', {
- additional_context: {
- mail_message_to_resend: messageID,
- },
- });
- },
-
- /**
- * @private
- * @param {Event} event
- * @returns {Promise}
- */
- _onMarkFailedMessageReviewed: function (event) {
- event.preventDefault();
- var messageID = $(event.currentTarget).data('message-id');
- return this._rpc({
- model: 'mail.message',
- method: 'toggle_tracking_status',
- args: [[messageID]],
- context: this.getSession().user_context,
- });
- },
- });
-
- MailManager.include({
-
- /**
- * Create mailbox entry
- *
- * @private
- * @param {Object} data
- */
- _updateMailboxesFromServer: function (data) {
- this._super.apply(this, arguments);
- this._addMailbox({
- id: 'failed',
- name: _t("Failed"),
- mailboxCounter: data.failed_counter || 0,
- });
- },
- });
-
- Mailbox.include({
-
- /**
- * Get thread domain
- *
- * @private
- * @returns {Array}
- */
- _getThreadDomain: function () {
- if (this._id === 'mailbox_failed') {
- // Workaround to avoid throw an exception
- return [
- ['mail_tracking_ids.state', 'in', FAILED_STATES],
- ['mail_tracking_needs_action', '=', true],
- ];
- }
- return this._super.apply(this, arguments);
- },
- });
-
-
- /* FAILED MESSAGES CHATTER WIDGET */
- /**
- * Get messages with selected ids
- *
- * @private
- * @param {Object} self
- * @param {Array} ids
- * @returns {Array}
- */
- function _readMessages (self, ids) {
- if (!ids.length) {
- return $.when([]);
- }
- var context = self.record && self.record.getContext();
- return self._rpc({
- model: 'mail.message',
- method: 'get_failed_messages',
- args: [ids],
- context: context || self.getSession().user_context,
- }).then(function (messages) {
- // Convert date to moment
- _.each(messages, function (msg) {
- msg.date = moment(time.auto_str_to_date(msg.date));
- msg.hour = utils.timeFromNow(msg.date);
- });
- return messages;
- });
- }
-
- BasicModel.include({
-
- /**
- * Fetch special failed messages
- *
- * @private
- * @param {Object} record
- * @param {String} fieldName
- * @returns {Array}
- */
- _fetchSpecialFailedMessages: function (record, fieldName) {
- var localID = record._changes && fieldName in record._changes
- ? record._changes[fieldName] : record.data[fieldName];
- return _readMessages(this, this.localData[localID].res_ids);
- },
- });
-
- var AbstractFailedMessagesField = AbstractField.extend({
-
- /**
- * Abstract method to be implemented
- *
- * @returns {Boolean}
- */
- _markFailedMessageReviewed: function () {
- return false;
- },
- });
-
- var FailedMessage = AbstractFailedMessagesField.extend({
- className: 'o_mail_failed_message',
- events: {
- 'click .o_failed_message_retry': '_onRetryFailedMessage',
- 'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
- },
- specialData: '_fetchSpecialFailedMessages',
-
- init: function () {
- this._super.apply(this, arguments);
- this.failed_messages = this.record.specialData[this.name];
- this.call(
- 'bus_service', 'onNotification', this, this._onNotification);
- },
-
- /**
- * Get message qweb parameters
- *
- * @private
- * @returns {Object}
- */
- _failedItemsQWebParams: function () {
- return {
- failed_messages: this.failed_messages,
- nbFailedMessages: this.failed_messages.length,
- date_format: time.getLangDateFormat(),
- datetime_format: time.getLangDatetimeFormat(),
- };
- },
-
- /**
- * Render failed message
- *
- * @private
- */
- _render: function () {
- if (this.failed_messages.length) {
- this.$el.html(QWeb.render(
- 'mail_tracking.failed_message_items',
- this._failedItemsQWebParams()));
- } else {
- this.$el.empty();
- }
- },
-
- /**
- * Reset
- *
- * @private
- * @param {Object} record
- */
- _reset: function (record) {
- this._super.apply(this, arguments);
- this.failed_messages = this.record.specialData[this.name];
- this.res_id = record.res_id;
- },
-
- /**
- * Reload
- *
- * @private
- * @param {Array} fieldsToReload
- */
- _reload: function (fieldsToReload) {
- this.trigger_up('reload_mail_fields', fieldsToReload);
- },
-
- /**
- * Mark failed message as reviewed
- *
- * @private
- * @param {Int} id
- * @returns {Promise}
- */
- _markFailedMessageReviewed: function (id) {
- return this._rpc({
- model: 'mail.message',
- method: 'toggle_tracking_status',
- args: [[id]],
- context: this.record.getContext(),
- });
- },
-
- // Handlers
- /**
- * Listen notification to launch reload process
- *
- * @private
- * @param {Array} notifs
- */
- _onNotification: function (notifs) {
- var self = this;
- _.each(notifs, function (notif) {
- var model = notif[0][1];
- if (model === 'res.partner') {
- var data = notif[1];
- if (data.type === 'update_failed_messages') {
- // Update failed messages
- self._reload({failed_message: true});
- }
- }
- });
- },
-
- /**
- * Handle retry failed message event
- *
- * @private
- * @param {Event} event
- */
- _onRetryFailedMessage: function (event) {
- event.preventDefault();
- var messageID = $(event.currentTarget).data('message-id');
- this.do_action('mail.mail_resend_message_action', {
- additional_context: {
- mail_message_to_resend: messageID,
- },
- });
- },
-
- /**
- * Handle mark message as reviewed event
- *
- * @private
- * @param {Event} event
- */
- _onMarkFailedMessageReviewed: function (event) {
- event.preventDefault();
- var messageID = $(event.currentTarget).data('message-id');
- this._markFailedMessageReviewed(messageID).then(
- this._reload.bind(this, {failed_message: true}));
- },
- });
-
- field_registry.add('mail_failed_message', FailedMessage);
-
- var mailWidgets = ['mail_failed_message'];
- BasicView.include({
- init: function () {
- this._super.apply(this, arguments);
- // Adds mail_failed_message as valid mail widget
- var fieldsInfo = this.fieldsInfo[this.viewType];
- for (var fieldName in fieldsInfo) {
- var fieldInfo = fieldsInfo[fieldName];
- if (_.contains(mailWidgets, fieldInfo.widget)) {
- this.mailFields[fieldInfo.widget] = fieldName;
- fieldInfo.__no_fetch = true;
- }
- }
- Object.assign(this.rendererParams.mailFields, this.mailFields);
- },
- });
-
- Chatter.include({
-
- /**
- * Init
- *
- * @private
- * @param {Widget} parent
- * @param {Object} record
- * @param {Object} mailFields
- * @param {Object} options
- */
- init: function (parent, record, mailFields, options) {
- this._super.apply(this, arguments);
- // Initialize mail_failed_message widget
- if (mailFields.mail_failed_message) {
- this.fields.failed_message = new FailedMessage(
- this, mailFields.mail_failed_message, record, options);
- }
- },
-
- /**
- * Render
- *
- * @private
- * @returns {Promise}
- */
- _render: function () {
- var self = this;
- return this._super.apply(this, arguments).then(function () {
- if (self.fields.failed_message) {
- self.fields.failed_message.$el.insertBefore(
- self.$el.find('.o_mail_thread'));
- }
- });
- },
-
- /**
- * Handle reload fields event
- *
- * @private
- * @param {Event} event
- */
- _onReloadMailFields: function (event) {
- if (this.fields.failed_message && event.data.failed_message) {
- this.trigger_up('reload', {
- fieldNames: [this.fields.failed_message.name],
- keepChanges: true,
- });
- } else {
- // Workarround to avoid trigger reload event two times.
- this._super.apply(this, arguments);
- }
- },
- });
-
- return FailedMessage;
-
-});
diff --git a/mail_tracking/static/src/js/failed_message/discuss.js b/mail_tracking/static/src/js/failed_message/discuss.js
new file mode 100644
index 000000000..313041947
--- /dev/null
+++ b/mail_tracking/static/src/js/failed_message/discuss.js
@@ -0,0 +1,312 @@
+/* Copyright 2019 Alexandre Díaz
+ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
+odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
+ "use strict";
+
+ // To be considered:
+ // - One message can be displayed in many threads
+ // - A thread can be a mailbox, channel, ...
+ // - A mailbox it's a type of thread that it's displayed on top of
+ // the discuss menu, has a counter, etc...
+
+ var MailManagerNotif = require('mail.Manager.Notification');
+ var AbstractMessage = require('mail.model.AbstractMessage');
+ var Message = require('mail.model.Message');
+ var Discuss = require('mail.Discuss');
+ var MailManager = require('mail.Manager');
+ var Mailbox = require('mail.model.Mailbox');
+ var core = require('web.core');
+
+ var QWeb = core.qweb;
+ var _t = core._t;
+
+ /* The states to consider a message as failed message */
+ var FAILED_STATES = [
+ 'error', 'rejected', 'spam', 'bounced', 'soft-bounced',
+ ];
+
+
+ AbstractMessage.include({
+
+ /**
+ * Abstract declaration to know if a message its included on the
+ * failed mailbox. By default should be false.
+ *
+ * @returns {Boolean}
+ */
+ isFailed: function () {
+ return false;
+ },
+ });
+
+ Message.include({
+
+ /**
+ * Overrides to store information from server
+ *
+ * @Override
+ */
+ init: function (parent, data) {
+ this._isFailedMessage = data.is_failed_message;
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Implementation to know if a message its included on the
+ * failed mailbox.
+ *
+ * @Override
+ */
+ isFailed: function () {
+ return _.contains(this._threadIDs, 'mailbox_failed');
+ },
+
+ /**
+ * Adds/Remove message to/from failed mailbox
+ *
+ * @param {Boolean} failed
+ */
+ setFailed: function (failed) {
+ if (failed) {
+ this._addThread('mailbox_failed');
+ } else {
+ this.removeThread('mailbox_failed');
+ }
+ },
+
+ /**
+ * Overrides to update if the message need be show in 'failed' mailbox
+ * thread
+ * @Override
+ */
+ _processMailboxes: function () {
+ this.setFailed(this._isFailedMessage);
+ this._super.apply(this, arguments);
+ },
+ });
+
+ MailManagerNotif.include({
+
+ /**
+ * Overrides to handle changes in the 'mail_tracking_needs_action' flag
+ *
+ * @Override
+ */
+ _handlePartnerNotification: function (data) {
+ if (data.type === 'toggle_tracking_status') {
+ this._handlePartnerToggleFailedNotification(data);
+ } else {
+ // Workaround to avoid call '_handlePartnerChannelNotification'
+ // because this is related with the failed mailbox, not a
+ // channel.
+ this._super.apply(this, arguments);
+ }
+ },
+
+ /**
+ * This method update messages in the failed mailbox when the flag
+ * 'mail_tracking_needs_action' is toggled. This can remove/add
+ * the message from/to failed mailbox and update mailbox counter.
+ *
+ * @private
+ * @param {Object} data
+ */
+ _handlePartnerToggleFailedNotification: function (data) {
+ var self = this;
+ var failed = this.getMailbox('failed');
+ _.each(data.message_ids, function (messageID) {
+ var message = _.find(self._messages, function (msg) {
+ return msg.getID() === messageID;
+ });
+ if (message) {
+ message.setFailed(data.needs_actions);
+ if (message.isFailed() === false) {
+ self._removeMessageFromThread(
+ 'mailbox_failed', message);
+ } else {
+ self._addMessageToThreads(message, []);
+ var channelFailed = self.getMailbox('failed');
+ channelFailed.invalidateCaches();
+ }
+ self._mailBus.trigger('update_message', message);
+ }
+ });
+
+ if (data.needs_actions) {
+ // Increase failed counter if message is marked as failed
+ failed.incrementMailboxCounter(data.message_ids.length);
+ } else {
+ // Decrease failed counter if message is remove from failed
+ failed.decrementMailboxCounter(data.message_ids.length);
+ }
+
+ // Trigger event to refresh threads
+ this._mailBus.trigger('update_failed', failed.getMailboxCounter());
+ },
+ });
+
+ Discuss.include({
+ events: _.extend({}, Discuss.prototype.events, {
+ 'click .o_failed_message_retry': '_onRetryFailedMessage',
+ 'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
+ }),
+
+ /**
+ * Paramaters used to render 'failed' mailbox entry in Discuss
+ *
+ * @private
+ * @returns {Object}
+ */
+ _sidebarQWebParams: function () {
+ var failed = this.call('mail_service', 'getMailbox', 'failed');
+ return {
+ activeThreadID: this._thread ? this._thread.getID() : undefined,
+ failedCounter: failed.getMailboxCounter(),
+ };
+ },
+
+ /**
+ * Render 'failed' mailbox menu entry in Discuss
+ *
+ * @private
+ * @returns {jQueryElementt}
+ */
+ _renderSidebar: function () {
+ var $sidebar = this._super.apply(this, arguments);
+ // Because Odoo implementation isn't designed to be inherited
+ // properly, we inject 'failed' button using jQuery.
+ var $failed_item = $(QWeb.render('mail_tracking.SidebarFailed',
+ this._sidebarQWebParams()));
+ $failed_item.insertAfter(
+ $sidebar.find(".o_mail_discuss_title_main").filter(":last"));
+ return $sidebar;
+ },
+
+ /**
+ * Overrides to update messages in 'failed' mailbox thread
+ *
+ * @Override
+ */
+ _onMessageUpdated: function (message, type) {
+ var self = this;
+ var currentThreadID = this._thread.getID();
+ if (currentThreadID === 'mailbox_failed' && !message.isFailed()) {
+ this._thread.fetchMessages(this.domain)
+ .then(function () {
+ var options = self._getThreadRenderingOptions();
+ self._threadWidget.removeMessageAndRender(
+ message.getID(), self._thread, options)
+ .then(function () {
+ self._updateButtonStatus(
+ !self._thread.hasMessages(), type);
+ });
+ });
+ } else {
+ // Workaround to avoid call '_fetchAndRenderThread' and refetch
+ // thread messages.
+ this._super.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Overrides to hide reply in the 'failed' mailbox, as this feature does
+ * not necessary in that channel. It also show retry and
+ * 'set as reviewed'.
+ *
+ * @Override
+ */
+ _getThreadRenderingOptions: function () {
+ var values = this._super.apply(this, arguments);
+ if (this._thread.getID() === 'mailbox_failed') {
+ values.displayEmailIcons = true;
+ values.displayReplyIcons = false;
+ values.displayRetryButton = true;
+ values.displayReviewedButton = true;
+ }
+ return values;
+ },
+
+ /**
+ * Overrides to listen event that refresh thread messages
+ *
+ * @Override
+ */
+ _startListening: function () {
+ this._super.apply(this, arguments);
+ this.call('mail_service', 'getMailBus')
+ .on('update_failed', this, this._throttledUpdateThreads);
+ },
+
+ // Handlers
+ /**
+ * Open the resend mail.resend.message wizard
+ *
+ * @private
+ * @param {Event} event
+ */
+ _onRetryFailedMessage: function (event) {
+ event.preventDefault();
+ var messageID = $(event.currentTarget).data('message-id');
+ this.do_action('mail.mail_resend_message_action', {
+ additional_context: {
+ mail_message_to_resend: messageID,
+ },
+ });
+ },
+
+ /**
+ * Toggle 'mail_tracking_needs_action' flag
+ *
+ * @private
+ * @param {Event} event
+ * @returns {Promise}
+ */
+ _onMarkFailedMessageReviewed: function (event) {
+ event.preventDefault();
+ var messageID = $(event.currentTarget).data('message-id');
+ return this._rpc({
+ model: 'mail.message',
+ method: 'toggle_tracking_status',
+ args: [[messageID]],
+ context: this.getSession().user_context,
+ });
+ },
+ });
+
+ MailManager.include({
+
+ /**
+ * Overrides to add the 'failed' mailbox
+ *
+ * @Override
+ */
+ _updateMailboxesFromServer: function (data) {
+ this._super.apply(this, arguments);
+ this._addMailbox({
+ id: 'failed',
+ name: _t("Failed"),
+ mailboxCounter: data.failed_counter || 0,
+ });
+ },
+ });
+
+ Mailbox.include({
+
+ /**
+ * Overrides to add domain for 'failed' mailbox thread
+ *
+ * @Override
+ */
+ _getThreadDomain: function () {
+ if (this._id === 'mailbox_failed') {
+ return [
+ ['mail_tracking_ids.state', 'in', FAILED_STATES],
+ ['mail_tracking_needs_action', '=', true],
+ ];
+ }
+ // Workaround to avoid throw 'Missing domain' exception
+ return this._super.apply(this, arguments);
+ },
+ });
+
+});
diff --git a/mail_tracking/static/src/js/failed_message/widget.js b/mail_tracking/static/src/js/failed_message/widget.js
new file mode 100644
index 000000000..00d2a5e3e
--- /dev/null
+++ b/mail_tracking/static/src/js/failed_message/widget.js
@@ -0,0 +1,311 @@
+/* Copyright 2019 Alexandre Díaz
+ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
+odoo.define('mail_tracking.FailedMessage', function (require) {
+ "use strict";
+
+ var AbstractField = require('web.AbstractField');
+ var BasicModel = require('web.BasicModel');
+ var BasicView = require('web.BasicView');
+ var Chatter = require('mail.Chatter');
+ var MailThread = require('mail.widget.Thread');
+ var utils = require('mail.utils');
+ var core = require('web.core');
+ var field_registry = require('web.field_registry');
+ var time = require('web.time');
+
+ var QWeb = core.qweb;
+
+
+ /**
+ * Helper method to fetch failed messages
+ *
+ * @private
+ * @param {Object} self
+ * @param {Array} ids
+ * @returns {Array}
+ */
+ function _readMessages (self, ids) {
+ if (!ids.length) {
+ return $.when([]);
+ }
+ var context = self.record && self.record.getContext();
+ return self._rpc({
+ model: 'mail.message',
+ method: 'get_failed_messages',
+ args: [ids],
+ context: context || self.getSession().user_context,
+ }).then(function (messages) {
+ // Convert date to moment
+ _.each(messages, function (msg) {
+ msg.date = moment(time.auto_str_to_date(msg.date));
+ msg.hour = utils.timeFromNow(msg.date);
+ });
+ return messages;
+ });
+ }
+
+ BasicModel.include({
+
+ /**
+ * Fetches the failed messages displayed by the 'mail_failed_message'
+ * field widget in form views.
+ *
+ * @private
+ * @param {Object} record
+ * @param {String} fieldName
+ * @returns {Array}
+ */
+ _fetchSpecialFailedMessages: function (record, fieldName) {
+ var localID = record._changes && fieldName in record._changes
+ ? record._changes[fieldName] : record.data[fieldName];
+ return _readMessages(this, this.localData[localID].res_ids);
+ },
+ });
+
+ var AbstractFailedMessagesField = AbstractField.extend({
+
+ /**
+ * Mark failed message as reviewed. By default should be don't reviewed.
+ *
+ * @returns {Boolean}
+ */
+ _markFailedMessageReviewed: function () {
+ return false;
+ },
+ });
+
+ var FailedMessage = AbstractFailedMessagesField.extend({
+ className: 'o_mail_failed_message',
+ events: {
+ 'click .o_failed_message_retry': '_onRetryFailedMessage',
+ 'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
+ },
+ specialData: '_fetchSpecialFailedMessages',
+
+ /**
+ * Overrides to listen bus notifications
+ *
+ * @Override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ this.failed_messages = this.record.specialData[this.name];
+ this.call(
+ 'bus_service', 'onNotification', this, this._onNotification);
+ },
+
+ /**
+ * Paremeters used to render widget
+ *
+ * @private
+ * @returns {Object}
+ */
+ _failedItemsQWebParams: function () {
+ return {
+ failed_messages: this.failed_messages,
+ nbFailedMessages: this.failed_messages.length,
+ date_format: time.getLangDateFormat(),
+ datetime_format: time.getLangDatetimeFormat(),
+ };
+ },
+
+ /**
+ * @private
+ */
+ _render: function () {
+ if (this.failed_messages.length) {
+ this.$el.html(QWeb.render(
+ 'mail_tracking.failed_message_items',
+ this._failedItemsQWebParams()));
+ } else {
+ this.$el.empty();
+ }
+ },
+
+ /**
+ * Reset widget data using selected record
+ *
+ * @private
+ * @param {Object} record
+ */
+ _reset: function (record) {
+ this._super.apply(this, arguments);
+ this.failed_messages = this.record.specialData[this.name];
+ this.res_id = record.res_id;
+ },
+
+ /**
+ * Trigger event to reload mail widgets
+ *
+ * @private
+ * @param {Array} fieldsToReload
+ */
+ _reload: function (fieldsToReload) {
+ this.trigger_up('reload_mail_fields', fieldsToReload);
+ },
+
+ /**
+ * Mark failed message as reviewed
+ *
+ * @private
+ * @param {Int} id
+ * @returns {Promise}
+ */
+ _markFailedMessageReviewed: function (id) {
+ return this._rpc({
+ model: 'mail.message',
+ method: 'toggle_tracking_status',
+ args: [[id]],
+ context: this.record.getContext(),
+ });
+ },
+
+ // Handlers
+ /**
+ * Listen bus notification to launch reload process.
+ * This bus notification is received when the user uses
+ * 'mail.resend.message' wizard.
+ *
+ * @private
+ * @param {Array} notifs
+ */
+ _onNotification: function (notifs) {
+ var self = this;
+ _.each(notifs, function (notif) {
+ var model = notif[0][1];
+ if (model === 'res.partner') {
+ var data = notif[1];
+ if (data.type === 'update_failed_messages') {
+ // Reload 'mail_failed_message' widget
+ self._reload({failed_message: true});
+ }
+ }
+ });
+ },
+
+ /**
+ * Handle retry failed message event to open the mail.resend.message
+ * wizard.
+ *
+ * @private
+ * @param {Event} event
+ */
+ _onRetryFailedMessage: function (event) {
+ event.preventDefault();
+ var messageID = $(event.currentTarget).data('message-id');
+ this.do_action('mail.mail_resend_message_action', {
+ additional_context: {
+ mail_message_to_resend: messageID,
+ },
+ });
+ },
+
+ /**
+ * Handle mark message as reviewed event
+ *
+ * @private
+ * @param {Event} event
+ */
+ _onMarkFailedMessageReviewed: function (event) {
+ event.preventDefault();
+ var messageID = $(event.currentTarget).data('message-id');
+ this._markFailedMessageReviewed(messageID).then(
+ this._reload.bind(this, {failed_message: true}));
+ },
+ });
+
+ field_registry.add('mail_failed_message', FailedMessage);
+
+ var mailWidgets = ['mail_failed_message'];
+ BasicView.include({
+
+ /**
+ * Overrides to add 'mail_failed_message' widget as "mail widget" used
+ * in Chatter.
+ *
+ * @Override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ var fieldsInfo = this.fieldsInfo[this.viewType];
+ for (var fieldName in fieldsInfo) {
+ var fieldInfo = fieldsInfo[fieldName];
+ if (_.contains(mailWidgets, fieldInfo.widget)) {
+ this.mailFields[fieldInfo.widget] = fieldName;
+ fieldInfo.__no_fetch = true;
+ }
+ }
+ Object.assign(this.rendererParams.mailFields, this.mailFields);
+ },
+ });
+
+ Chatter.include({
+
+ /**
+ * Overrides to initialize 'mail_failed_message' widget.
+ *
+ * @Override
+ */
+ init: function (parent, record, mailFields, options) {
+ this._super.apply(this, arguments);
+ // Initialize mail_failed_message widget
+ if (mailFields.mail_failed_message) {
+ this.fields.failed_message = new FailedMessage(
+ this, mailFields.mail_failed_message, record, options);
+ }
+ },
+
+ /**
+ * Injects widget before the chatter
+ *
+ * @private
+ * @returns {Promise}
+ */
+ _render: function () {
+ var self = this;
+ return this._super.apply(this, arguments).then(function () {
+ if (self.fields.failed_message) {
+ self.fields.failed_message.$el.insertBefore(
+ self.$el.find('.o_mail_thread'));
+ }
+ });
+ },
+
+ /**
+ * Overrides to reload 'mail_failed_message' widget
+ *
+ * @Override
+ */
+ _onReloadMailFields: function (event) {
+ if (this.fields.failed_message && event.data.failed_message) {
+ this.trigger_up('reload', {
+ fieldNames: [this.fields.failed_message.name],
+ keepChanges: true,
+ });
+ } else {
+ // Workarround to avoid trigger reload event two times (one for
+ // mail_failed_message and other with empty 'fieldNames'.
+ this._super.apply(this, arguments);
+ }
+ },
+ });
+
+ MailThread.include({
+
+ /**
+ * Overrides to show 'retry' & 'Set as reviewed' buttons in the Chatter
+ *
+ * @Override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ this._enabledOptions.displayRetryButton = true;
+ this._enabledOptions.displayReviewedButton = true;
+ this._disabledOptions.displayRetryButton = true;
+ this._disabledOptions.displayReviewedButton = true;
+ },
+ });
+
+ return FailedMessage;
+
+});
diff --git a/mail_tracking/static/src/xml/failed_message/common.xml b/mail_tracking/static/src/xml/failed_message/common.xml
new file mode 100644
index 000000000..05124577f
--- /dev/null
+++ b/mail_tracking/static/src/xml/failed_message/common.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Set as Reviewed
+
+
+ Retry
+
+
+
+
+
diff --git a/mail_tracking/static/src/xml/discuss.xml b/mail_tracking/static/src/xml/failed_message/discuss.xml
similarity index 60%
rename from mail_tracking/static/src/xml/discuss.xml
rename to mail_tracking/static/src/xml/failed_message/discuss.xml
index 786d57ed7..fb84da01e 100644
--- a/mail_tracking/static/src/xml/discuss.xml
+++ b/mail_tracking/static/src/xml/failed_message/discuss.xml
@@ -25,15 +25,4 @@
-
-
-
- Set as Reviewed
-
-
- Retry
-
-
-
-
diff --git a/mail_tracking/static/src/xml/failed_message.xml b/mail_tracking/static/src/xml/failed_message/widget.xml
similarity index 93%
rename from mail_tracking/static/src/xml/failed_message.xml
rename to mail_tracking/static/src/xml/failed_message/widget.xml
index d47d13b4c..cafe38d9d 100644
--- a/mail_tracking/static/src/xml/failed_message.xml
+++ b/mail_tracking/static/src/xml/failed_message/widget.xml
@@ -28,10 +28,10 @@
-
-
+
Set as Reviewed
-
+
Retry
diff --git a/mail_tracking/views/assets.xml b/mail_tracking/views/assets.xml
index 9f88fbef1..5858e841a 100644
--- a/mail_tracking/views/assets.xml
+++ b/mail_tracking/views/assets.xml
@@ -13,7 +13,9 @@
+ src="/mail_tracking/static/src/js/failed_message/discuss.js"/>
+