mirror of https://github.com/OCA/social.git
fixup! [IMP] mail_tracking: Failed Messages to 12.0
parent
9a18de6229
commit
322a3e0437
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
|
||||
});
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
});
|
|
@ -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;
|
||||
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
|
||||
<t t-extend="mail.widget.Thread.Message">
|
||||
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
|
||||
<a t-if="message.isFailed() && options.displayRetryButton" class="btn btn-link o_thread_icon btn-success text-success btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.getID()">
|
||||
<i class="fa fa-check"/> Set as Reviewed
|
||||
</a>
|
||||
<a t-if="message.isFailed() && options.displayReviewedButton" class="btn btn-link o_thread_icon btn-default text-warning btn-sm o_failed_message_retry" t-att-data-message-id="message.getID()">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
|
@ -25,15 +25,4 @@
|
|||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend="mail.widget.Thread.Message">
|
||||
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
|
||||
<a t-if="message.isFailed() && options.displayRetryButton" class="btn btn-link btn-success text-success btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.getID()">
|
||||
<i class="fa fa-check"/> Set as Reviewed
|
||||
</a>
|
||||
<a t-if="message.isFailed() && options.displayReviewedButton" class="btn btn-link btn-default text-warning btn-sm o_failed_message_retry" t-att-data-message-id="message.getID()">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
|
@ -28,10 +28,10 @@
|
|||
</strong>
|
||||
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small>
|
||||
<span t-attf-class="o_thread_icons">
|
||||
<a href="#" class="btn btn-link btn-success text-success btn-sm o_failed_message_reviewed o_activity_link" t-att-data-message-id="message.id">
|
||||
<a href="#" class="btn btn-link btn-success o_thread_icon text-success btn-sm o_failed_message_reviewed o_activity_link" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-check"/> Set as Reviewed
|
||||
</a>
|
||||
<a href="#" class="btn btn-link btn-default text-warning btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<a href="#" class="btn btn-link btn-default o_thread_icon text-warning btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</span>
|
|
@ -13,7 +13,9 @@
|
|||
<script type="text/javascript"
|
||||
src="/mail_tracking/static/src/js/mail_tracking.js"/>
|
||||
<script type="text/javascript"
|
||||
src="/mail_tracking/static/src/js/failed_message.js"/>
|
||||
src="/mail_tracking/static/src/js/failed_message/discuss.js"/>
|
||||
<script type="text/javascript"
|
||||
src="/mail_tracking/static/src/js/failed_message/widget.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
|
Loading…
Reference in New Issue