[PEP8] pre-commit

This commit is contained in:
houssine 2021-11-08 12:48:44 +01:00 committed by Jasmin Solanki
parent aba9ad7561
commit 53cc2ff96f
13 changed files with 386 additions and 388 deletions

View File

@ -7,7 +7,7 @@
{
"name": "Email tracking",
"summary": "Email tracking system for all mails sent",
"version": "14.0.1.1.1",
"version": "14.0.1.1.2",
"category": "Social Network",
"website": "https://github.com/OCA/social",
"author": ("Tecnativa, " "Odoo Community Association (OCA)"),

View File

@ -310,6 +310,10 @@ class MailMessage(models.Model):
@api.model
def get_failed_messsage_info(self, ids, model):
msg_ids = self.search([('res_id', '=', ids), ('model', '=', model)])
res = [msg._prepare_dict_failed_message() for msg in msg_ids.sorted("date", reverse=True) if msg._prepare_dict_failed_message()]
msg_ids = self.search([("res_id", "=", ids), ("model", "=", model)])
res = [
msg._prepare_dict_failed_message()
for msg in msg_ids.sorted("date", reverse=True)
if msg._prepare_dict_failed_message()
]
return res

View File

@ -6,3 +6,6 @@
* Ernesto Tejeda
* Rafael Blasco
* Alexandre Díaz
* `Eezee-IT <https://www.eezee-it.com>`_:
* Asma Elferkhsi

View File

@ -1,30 +1,38 @@
odoo.define('mail_tracking/static/src/js/chatter.js', function (require) {
'use strict';
odoo.define("mail_tracking/static/src/js/chatter.js", function (require) {
"use strict";
const ChatterContainer = require('mail/static/src/components/chatter_container/chatter_container.js');
const { attr } = require('mail/static/src/model/model_field.js');
const { registerInstancePatchModel,registerFieldPatchModel, registerClassPatchModel,
} = require('mail/static/src/model/model_core.js');
registerInstancePatchModel('mail.chatter', 'mail/static/src/models/chatter/chatter.js', {
async refresh() {
this._super(...arguments);
this.thread.refreshMessagefailed()
},
toggleMessageFailedBoxVisibility() {
this.update({ isMessageFailedBoxVisible: !this.isMessageFailedBoxVisible })
},
_onThreadIdOrThreadModelChanged() {
this._super(...arguments);
this.thread.refreshMessagefailed()
}
const {attr} = require("mail/static/src/model/model_field.js");
const {
registerInstancePatchModel,
registerFieldPatchModel,
} = require("mail/static/src/model/model_core.js");
registerInstancePatchModel(
"mail.chatter",
"mail/static/src/models/chatter/chatter.js",
{
async refresh() {
this._super(...arguments);
this.thread.refreshMessagefailed();
},
toggleMessageFailedBoxVisibility() {
this.update({
isMessageFailedBoxVisible: !this.isMessageFailedBoxVisible,
});
},
_onThreadIdOrThreadModelChanged() {
this._super(...arguments);
this.thread.refreshMessagefailed();
},
}
);
registerFieldPatchModel(
"mail.chatter",
"mail/static/src/models/chatter/chatter.js",
{
isMessageFailedBoxVisible: attr({
default: true,
}),
}
);
});
registerFieldPatchModel('mail.chatter', 'mail/static/src/models/chatter/chatter.js', {
isMessageFailedBoxVisible: attr({
default: true,
}),
});
});

View File

@ -1,139 +1,148 @@
odoo.define('mail_tracking/static/src/js/discuss/discuss.js', function(require) {
'use strict';
odoo.define("mail_tracking/static/src/js/discuss/discuss.js", function (require) {
"use strict";
const {
attr
} = require('mail/static/src/model/model_field.js');
const {attr} = require("mail/static/src/model/model_field.js");
const {
registerInstancePatchModel,
registerFieldPatchModel,
registerClassPatchModel
} = require('mail/static/src/model/model_core.js');
const {
one2one,
many2one
} = require('mail/static/src/model/model_field.js');
registerClassPatchModel,
} = require("mail/static/src/model/model_core.js");
const {one2one, many2one} = require("mail/static/src/model/model_field.js");
registerInstancePatchModel('mail.messaging_initializer', 'mail/static/src/models/messaging_initializer/messaging_initializer.js', {
async start() {
this._super(...arguments);
this.messaging.update({
failedmsg: [
['create', {
id: 'failedmsg',
isServerPinned: true,
model: 'mail.box',
name: this.env._t("Failed"),
}]
],
});
},
async _init({
channel_slots,
commands = [],
current_partner,
current_user_id,
mail_failures = {},
mention_partner_suggestions = [],
menu_id,
moderation_channel_ids = [],
moderation_counter = 0,
needaction_inbox_counter = 0,
partner_root,
public_partner,
public_partners,
shortcodes = [],
starred_counter = 0,
failed_counter = 0
}) {
const discuss = this.messaging.discuss;
// partners first because the rest of the code relies on them
this._initPartners({
registerInstancePatchModel(
"mail.messaging_initializer",
"mail/static/src/models/messaging_initializer/messaging_initializer.js",
{
async start() {
this._super(...arguments);
this.messaging.update({
failedmsg: [
[
"create",
{
id: "failedmsg",
isServerPinned: true,
model: "mail.box",
name: this.env._t("Failed"),
},
],
],
});
},
async _init({
channel_slots,
commands = [],
current_partner,
current_user_id,
moderation_channel_ids,
mail_failures = {},
mention_partner_suggestions = [],
menu_id,
moderation_channel_ids = [],
moderation_counter = 0,
needaction_inbox_counter = 0,
partner_root,
public_partner,
public_partners,
});
// mailboxes after partners and before other initializers that might
// manipulate threads or messages
this._initMailboxes({
shortcodes = [],
starred_counter = 0,
failed_counter = 0,
}) {
const discuss = this.messaging.discuss;
// Partners first because the rest of the code relies on them
this._initPartners({
current_partner,
current_user_id,
moderation_channel_ids,
partner_root,
public_partner,
public_partners,
});
// Mailboxes after partners and before other initializers that might
// manipulate threads or messages
this._initMailboxes({
moderation_channel_ids,
moderation_counter,
needaction_inbox_counter,
starred_counter,
failed_counter,
});
// Various suggestions in no particular order
this._initCannedResponses(shortcodes);
this._initCommands(commands);
this._initMentionPartnerSuggestions(mention_partner_suggestions);
// Channels when the rest of messaging is ready
await this.async(() => this._initChannels(channel_slots));
// Failures after channels
this._initMailFailures(mail_failures);
discuss.update({menu_id});
},
_initMailboxes({
moderation_channel_ids,
moderation_counter,
needaction_inbox_counter,
starred_counter,
failed_counter,
});
// various suggestions in no particular order
this._initCannedResponses(shortcodes);
this._initCommands(commands);
this._initMentionPartnerSuggestions(mention_partner_suggestions);
// channels when the rest of messaging is ready
await this.async(() => this._initChannels(channel_slots));
// failures after channels
this._initMailFailures(mail_failures);
discuss.update({ menu_id });
},
_initMailboxes({
moderation_channel_ids,
moderation_counter,
needaction_inbox_counter,
starred_counter,
failed_counter,
}) {
this.env.messaging.inbox.update({ counter: needaction_inbox_counter });
this.env.messaging.starred.update({ counter: starred_counter });
this.env.messaging.failedmsg.update({ counter: failed_counter });
if (moderation_channel_ids.length > 0) {
this.messaging.moderation.update({
counter: moderation_counter,
isServerPinned: true,
});
}
}) {
this.env.messaging.inbox.update({counter: needaction_inbox_counter});
this.env.messaging.starred.update({counter: starred_counter});
this.env.messaging.failedmsg.update({counter: failed_counter});
if (moderation_channel_ids.length > 0) {
this.messaging.moderation.update({
counter: moderation_counter,
isServerPinned: true,
});
}
},
}
);
});
registerFieldPatchModel(
"mail.messaging",
"mail/static/src/models/messaging/messaging.js",
{
failedmsg: one2one("mail.thread"),
}
);
registerFieldPatchModel('mail.messaging', 'mail/static/src/models/messaging/messaging.js', {
failedmsg: one2one('mail.thread'),
});
registerInstancePatchModel('mail.thread_cache', 'mail/static/src/models/thread_cache/thread_cache.js', {
_extendMessageDomain(domain) {
const thread = this.thread;
if (thread === this.env.messaging.failedmsg) {
return domain.concat([
['is_failed_message', '=', true]
]);
} else {
registerInstancePatchModel(
"mail.thread_cache",
"mail/static/src/models/thread_cache/thread_cache.js",
{
_extendMessageDomain(domain) {
const thread = this.thread;
if (thread === this.env.messaging.failedmsg) {
return domain.concat([["is_failed_message", "=", true]]);
}
return this._super(...arguments);
}
},
}
);
});
registerFieldPatchModel(
"mail.message",
"mail/static/src/models/message/message.js",
{
messagingFailedmsg: many2one("mail.thread", {
related: "messaging.failedmsg",
}),
isFailed: attr({
default: false,
}),
}
);
registerFieldPatchModel('mail.message', 'mail/static/src/models/message/message.js', {
messagingFailedmsg: many2one('mail.thread', {
related: 'messaging.failedmsg',
}),
isFailed: attr({
default: false,
}),
});
registerClassPatchModel('mail.message', 'mail/static/src/models/message/message.js', {
convertData(data) {
const data2 = this._super(data);
if ('is_failed_message' in data) {
data2.isFailed = data.is_failed_message;
}
return data2;
},
});
});
registerClassPatchModel(
"mail.message",
"mail/static/src/models/message/message.js",
{
convertData(data) {
const data2 = this._super(data);
if ("is_failed_message" in data) {
data2.isFailed = data.is_failed_message;
}
return data2;
},
}
);
});

View File

@ -1,40 +1,30 @@
odoo.define('mail_tracking/static/src/js/failed_message/mail_failed_box.js', function(require) {
'use strict';
odoo.define("mail_tracking/static/src/js/failed_message/mail_failed_box.js", function (
require
) {
"use strict";
const components = {
Message: require('mail/static/src/components/message/message.js'),
};
const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js');
const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
const chatter = require('mail/static/src/components/chatter/chatter.js');
const {
Component,
useState
} = owl;
const chatter = require("mail/static/src/components/chatter/chatter.js");
const useStore = require("mail/static/src/component_hooks/use_store/use_store.js");
const {Component} = owl;
class MessageFailedBox extends Component {
constructor(...args) {
super(...args);
useStore(props => {
const chatter = this.env.models['mail.chatter'].get(props.chatterLocalId);
useStore((props) => {
const chatter = this.env.models["mail.chatter"].get(
props.chatterLocalId
);
const thread = chatter && chatter.thread;
return {
chatter: chatter ? chatter.__state : undefined,
thread: thread && thread.__state,
};
});
}
get chatter() {
return this.env.models['mail.chatter'].get(this.props.chatterLocalId);
return this.env.models["mail.chatter"].get(this.props.chatterLocalId);
}
_onClickTitle() {
@ -44,9 +34,7 @@ odoo.define('mail_tracking/static/src/js/failed_message/mail_failed_box.js', fun
return this.env.services.rpc({
model: "mail.message",
method: "set_need_action_done",
args: [
[id]
],
args: [[id]],
});
}
_onRetryFailedMessage(event) {
@ -54,14 +42,14 @@ odoo.define('mail_tracking/static/src/js/failed_message/mail_failed_box.js', fun
var messageID = $(event.currentTarget).data("message-id");
const thread = this.chatter.thread;
var self = this;
this.env.bus.trigger('do-action', {
action: 'mail.mail_resend_message_action',
this.env.bus.trigger("do-action", {
action: "mail.mail_resend_message_action",
options: {
additional_context: {
mail_message_to_resend: messageID,
},
on_close: () => {
self.trigger('reload', { keepChanges: true });
self.trigger("reload", {keepChanges: true});
thread.refresh();
},
},
@ -70,26 +58,18 @@ odoo.define('mail_tracking/static/src/js/failed_message/mail_failed_box.js', fun
_onMarkFailedMessageReviewed(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data("message-id");
this._markFailedMessageReviewed(messageID)
this.trigger('reload', { keepChanges: true });
this._markFailedMessageReviewed(messageID);
this.trigger("reload", {keepChanges: true});
this.chatter.thread.refreshMessagefailed();
this.chatter.thread.refresh();
}
}
MessageFailedBox.template = "mail_tracking.MessageFailedBox";
MessageFailedBox.props = {
chatterLocalId: String,
};
chatter.components = Object.assign({}, chatter.components, {
MessageFailedBox
MessageFailedBox,
});
return MessageFailedBox;
});
});

View File

@ -1,60 +1,50 @@
odoo.define('mail_tracking/static/src/js/failed_message/thread.js', function(require) {
'use strict';
const components = {
Message: require('mail/static/src/components/message/message.js'),
};
const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js');
const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
const chatter = require('mail/static/src/components/chatter/chatter.js');
const {
Component,
useState
} = owl;
odoo.define("mail_tracking/static/src/js/failed_message/thread.js", function (require) {
"use strict";
const {
registerInstancePatchModel,
registerFieldPatchModel,
} = require('mail/static/src/model/model_core.js');
const {
one2many
} = require('mail/static/src/model/model_field.js');
} = require("mail/static/src/model/model_core.js");
const {one2many} = require("mail/static/src/model/model_field.js");
registerInstancePatchModel('mail.thread', 'mail_tracking/static/src/js/failed_message/thread.js', {
async refreshMessagefailed() {
var id = this.__values.id
var model = this.__values.model
const messagefailedData = await this.async(() => this.env.services.rpc({
model: 'mail.message',
method: 'get_failed_messsage_info',
args: [id, model],
}, {
shadow: true
}));
const messagefailed = this.env.models['mail.message.failed'].insert(messagefailedData.map(
messageData => this.env.models['mail.message.failed'].convertData(messageData)
));
this.update({
messagefailed: [
['replace', messagefailed]
]
});
registerInstancePatchModel(
"mail.thread",
"mail_tracking/static/src/js/failed_message/thread.js",
{
async refreshMessagefailed() {
var id = this.__values.id;
var model = this.__values.model;
const messagefailedData = await this.async(() =>
this.env.services.rpc(
{
model: "mail.message",
method: "get_failed_messsage_info",
args: [id, model],
},
{
shadow: true,
}
)
);
const messagefailed = this.env.models["mail.message.failed"].insert(
messagefailedData.map((messageData) =>
this.env.models["mail.message.failed"].convertData(messageData)
)
);
this.update({
messagefailed: [["replace", messagefailed]],
});
},
}
);
});
registerFieldPatchModel('mail.thread', 'mail_tracking/static/src/js/failed_message/thread.js', {
messagefailed: one2many('mail.message.failed', {
inverse: 'thread',
}),
});
});
registerFieldPatchModel(
"mail.thread",
"mail_tracking/static/src/js/failed_message/thread.js",
{
messagefailed: one2many("mail.message.failed", {
inverse: "thread",
}),
}
);
});

View File

@ -1,48 +1,53 @@
odoo.define('mail_tracking/static/src/js/mail_tracking.js', function(require) {
'use strict';
odoo.define("mail_tracking/static/src/js/mail_tracking.js", function (require) {
"use strict";
const {
registerClassPatchModel,
registerFieldPatchModel,
registerInstancePatchModel,
} = require('mail/static/src/model/model_core.js');
const {
attr
} = require('mail/static/src/model/model_field.js');
} = require("mail/static/src/model/model_core.js");
const {attr} = require("mail/static/src/model/model_field.js");
registerClassPatchModel('mail.message', 'mail_tracking/static/src/js/mail_tracking.js', {
convertData(data) {
const data2 = this._super(data);
if ('partner_trackings' in data) {
data2.partner_trackings = data.partner_trackings;
}
return data2;
},
});
registerClassPatchModel(
"mail.message",
"mail_tracking/static/src/js/mail_tracking.js",
{
convertData(data) {
const data2 = this._super(data);
if ("partner_trackings" in data) {
data2.partner_trackings = data.partner_trackings;
}
return data2;
},
}
);
registerFieldPatchModel('mail.message', 'mail_tracking/static/src/js/mail_tracking.js', {
partner_trackings: attr(),
});
registerFieldPatchModel(
"mail.message",
"mail_tracking/static/src/js/mail_tracking.js",
{
partner_trackings: attr(),
}
);
registerInstancePatchModel(
"mail.model",
"mail_tracking/static/src/js/mail_tracking.js",
{
hasPartnerTrackings() {
return _.some(this.__values.partner_trackings);
},
registerInstancePatchModel('mail.model', 'mail_tracking/static/src/js/mail_tracking.js', {
hasEmailCc() {
return _.some(this._emailCc);
},
hasPartnerTrackings() {
return _.some(this.__values.partner_trackings);
},
hasEmailCc() {
return _.some(this._emailCc);
},
getPartnerTrackings: function() {
if (!this.hasPartnerTrackings()) {
return [];
}
return this.__values.partner_trackings;
},
})
});
getPartnerTrackings: function () {
if (!this.hasPartnerTrackings()) {
return [];
}
return this.__values.partner_trackings;
},
}
);
});

View File

@ -1,11 +1,8 @@
odoo.define('mail_tracking/static/src/js/message.js', function(require) {
'use strict';
const Message = require('mail/static/src/components/message/message.js');
const MessageList = require('mail/static/src/components/message_list/message_list.js');
var core = require('web.core');
var _t = core._t;
odoo.define("mail_tracking/static/src/js/message.js", function (require) {
"use strict";
const Message = require("mail/static/src/components/message/message.js");
const MessageList = require("mail/static/src/components/message_list/message_list.js");
class MessageTracking extends Message {
constructor(parent, props) {
@ -14,39 +11,37 @@ odoo.define('mail_tracking/static/src/js/message.js', function(require) {
_onTrackingStatusClick(event) {
var tracking_email_id = $(event.currentTarget).data("tracking");
event.preventDefault();
return this.env.bus.trigger('do-action', {
return this.env.bus.trigger("do-action", {
action: {
type: "ir.actions.act_window",
view_type: "form",
view_mode: "form",
res_model: "mail.tracking.email",
views: [
[false, "form"]
],
views: [[false, "form"]],
target: "new",
res_id: tracking_email_id,
}
},
});
}
// for discuss
// For discuss
_onMarkFailedMessageReviewed(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data("message-id");
this._markFailedMessageReviewed(messageID)
this._markFailedMessageReviewed(messageID);
window.location.reload();
}
_onRetryFailedMessage(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data("message-id");
this.env.bus.trigger('do-action', {
action: 'mail.mail_resend_message_action',
this.env.bus.trigger("do-action", {
action: "mail.mail_resend_message_action",
options: {
additional_context: {
mail_message_to_resend: messageID,
},
on_close: () => {
window.location.reload();
window.location.reload();
},
},
});
@ -55,13 +50,9 @@ odoo.define('mail_tracking/static/src/js/message.js', function(require) {
return this.env.services.rpc({
model: "mail.message",
method: "set_need_action_done",
args: [
[id]
],
args: [[id]],
});
}
};
}
MessageList.components.Message = MessageTracking;
});
});

View File

@ -1,53 +1,40 @@
odoo.define('mail_tracking/static/src/js/models/thread.js', function(require) {
'use strict';
odoo.define("mail_tracking/static/src/js/models/thread.js", function (require) {
"use strict";
const {
registerNewModel
} = require('mail/static/src/model/model_core.js');
const {
attr,
many2many,
many2one
} = require('mail/static/src/model/model_field.js');
const {
clear
} = require('mail/static/src/model/model_field_command.js');
const {registerNewModel} = require("mail/static/src/model/model_core.js");
const {attr, many2one} = require("mail/static/src/model/model_field.js");
function factory(dependencies) {
class MessageFailed extends dependencies['mail.model'] {
class MessageFailed extends dependencies["mail.model"] {
static convertData(data) {
const data2 = {};
if ('author' in data) {
if ("author" in data) {
if (!data.author) {
data2.author = [
['unlink-all']
];
data2.author = [["unlink-all"]];
} else {
data2.author = data.author[1];
data2.author_id = data.author[0];
}
}
if ('body' in data) {
if ("body" in data) {
data2.body = data.body;
}
if ('date' in data) {
if ("date" in data) {
data2.date = data.date;
}
if ('failed_recipients' in data) {
if ("failed_recipients" in data) {
data2.failed_recipients = data.failed_recipients;
}
if ('id' in data) {
if ("id" in data) {
data2.id = data.id;
}
return data2;
}
}
MessageFailed.fields = {
thread: many2one('mail.thread', {
inverse: 'messagefailed',
thread: many2one("mail.thread", {
inverse: "messagefailed",
}),
body: attr(),
author: attr(),
@ -57,11 +44,10 @@ odoo.define('mail_tracking/static/src/js/models/thread.js', function(require) {
id: attr(),
};
MessageFailed.modelName = 'mail.message.failed';
MessageFailed.modelName = "mail.message.failed";
return MessageFailed;
}
registerNewModel('mail.message.failed', factory);
});
registerNewModel("mail.message.failed", factory);
});

View File

@ -1,38 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-inherit="mail.ThreadIcon" t-inherit-mode="extension">
<xpath expr="//t[@t-elif='thread === env.messaging.moderation']"
position="after">
<xpath
expr="//t[@t-elif='thread === env.messaging.moderation']"
position="after"
>
<t t-elif="thread === env.messaging.failedmsg">
<div class="o_ThreadIcon_mailboxModeration fa fa-exclamation"/>
<div class="o_ThreadIcon_mailboxModeration fa fa-exclamation" />
</t>
</xpath>
</t>
<t t-inherit="mail.Message" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_Message_originThread')]"
position="inside">
<xpath expr="//div[hasclass('o_Message_originThread')]" position="inside">
<t t-if="message.isFailed">
<span t-attf-class="o_thread_icons">
<a
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link"
t-on-click="_onMarkFailedMessageReviewed"
t-att-data-message-id="message.id"
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link"
t-on-click="_onMarkFailedMessageReviewed"
t-att-data-message-id="message.id"
>
<i class="fa fa-check"/>
<i class="fa fa-check" />
Set as Reviewed
</a>
<a
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_retry o_activity_link"
t-on-click="_onRetryFailedMessage"
t-att-data-message-id="message.id"
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_retry o_activity_link"
t-on-click="_onRetryFailedMessage"
t-att-data-message-id="message.id"
>
<i class="fa fa-retweet"/>
<i class="fa fa-retweet" />
Retry
</a>
</span>
</t>
</xpath>
</t>
</templates>
</templates>

View File

@ -2,88 +2,105 @@
<templates xml:space="preserve">
<t t-name="mail_tracking.MessageFailedBox" owl="1">
<div class="o_ActivityBox">
<t t-if="chatter and chatter.thread and chatter.thread.messagefailed.length > 0">
<t
t-if="chatter and chatter.thread and chatter.thread.messagefailed.length > 0"
>
<a role="button" class="o_ActivityBox_title btn" t-on-click="_onClickTitle">
<hr class="o_ActivityBox_titleLine"/>
<hr class="o_ActivityBox_titleLine" />
<span class="o_ActivityBox_titleText">
<i class="fa fa-fw" t-att-class="chatter.isMessageFailedBoxVisible ? 'fa-caret-down' : 'fa-caret-right'"/>
<i
class="fa fa-fw"
t-att-class="chatter.isMessageFailedBoxVisible ? 'fa-caret-down' : 'fa-caret-right'"
/>
Failed messages
</span>
<t t-if="!chatter.isMessageFailedBoxVisible">
<span class="o_ActivityBox_titleBadges">
<t t-if="chatter.thread.messagefailed.length > 0">
<span class="o_ActivityBox_titleBadge badge rounded-circle badge-danger">
<t t-esc="chatter.thread.messagefailed.length"/>
<span
class="o_ActivityBox_titleBadge badge rounded-circle badge-danger"
>
<t t-esc="chatter.thread.messagefailed.length" />
</span>
</t>
</span>
</t>
<hr class="o_ActivityBox_titleLine"/>
<hr class="o_ActivityBox_titleLine" />
</a>
<t t-if="chatter.isMessageFailedBoxVisible and chatter.thread.messagefailed">
<t
t-if="chatter.isMessageFailedBoxVisible and chatter.thread.messagefailed"
>
<div class="o_ActivityList">
<t t-foreach="chatter.thread.messagefailed" t-as="messagefailed" t-key="messagefailed.localId">
<t
t-foreach="chatter.thread.messagefailed"
t-as="messagefailed"
t-key="messagefailed.localId"
>
<div class="o_Activity">
<t t-if="messagefailed">
<div class="o_Activity_sidebar">
<div class="o_Activity_user">
<t t-if="messagefailed.author">
<img class="o_Activity_userAvatar"
t-attf-src="/web/image/res.users/{{ messagefailed.author_id }}/image_128"
t-att-alt="messagefailed.author"/>
<img
class="o_Activity_userAvatar"
t-attf-src="/web/image/res.users/{{ messagefailed.author_id }}/image_128"
t-att-alt="messagefailed.author"
/>
</t>
<div class="o_Activity_iconContainer bg-danger-full"
>
<i class="o_Activity_icon fa fa-exclamation"/>
<div class="o_Activity_iconContainer bg-danger-full">
<i class="o_Activity_icon fa fa-exclamation" />
</div>
</div>
</div>
<div class="o_Activity_core">
<div class="o_Activity_info">
<div class="o_Activity_dueDateText"
t-att-class="{}"
>
<t t-esc="messagefailed.author"/>
<div class="o_Activity_dueDateText" t-att-class="{}">
<t t-esc="messagefailed.author" />
</div>
<t t-if="messagefailed.date">
<div class="o_Activity_summary text-muted">
<t t-esc="messagefailed.date"/>
<t t-esc="messagefailed.date" />
</div>
</t>
<span t-attf-class="o_thread_icons">
<a
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link" t-on-click="_onMarkFailedMessageReviewed"
t-att-data-message-id="messagefailed.id"
>
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link"
t-on-click="_onMarkFailedMessageReviewed"
t-att-data-message-id="messagefailed.id"
>
<i class="fa fa-check" /> Set as Reviewed
</a>
<a
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_retry o_activity_link" t-on-click="_onRetryFailedMessage"
t-att-data-message-id="messagefailed.id"
>
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_retry o_activity_link"
t-on-click="_onRetryFailedMessage"
t-att-data-message-id="messagefailed.id"
>
<i class="fa fa-retweet" /> Retry
</a>
</span>
</div>
<div class="o_Activity_info">
<strong class="text-danger">Failed Recipients:</strong>
<t t-foreach="messagefailed.failed_recipients" t-as="recipient">
<t
t-foreach="messagefailed.failed_recipients"
t-as="recipient"
>
<t t-if="!recipient_first">
-
</t>
<a
class="o_mail_action_tracking_partner"
t-att-data-partner="recipient[1]"
t-attf-href="#model=res.partner&amp;id={{recipient[0]}}"
>
<t t-esc="recipient[1]"/>
class="o_mail_action_tracking_partner"
t-att-data-partner="recipient[1]"
t-attf-href="#model=res.partner&amp;id={{recipient[0]}}"
>
<t t-esc="recipient[1]" />
</a>
</t>
</div>
<div class="o_thread_message_note small">
<t t-raw="messagefailed.body"/>
<t t-raw="messagefailed.body" />
</div>
</div>
</t>
@ -95,13 +112,16 @@
</div>
</t>
<t t-inherit="mail.Chatter" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_Chatter_scrollPanel')]/t[@t-if='chatter.isAttachmentBoxVisible']" position="before">
<xpath
expr="//div[hasclass('o_Chatter_scrollPanel')]/t[@t-if='chatter.isAttachmentBoxVisible']"
position="before"
>
<t t-if="chatter.thread">
<MessageFailedBox
class="o_Chatter_activityBox"
chatterLocalId="chatter.localId"
/>
class="o_Chatter_activityBox"
chatterLocalId="chatter.localId"
/>
</t>
</xpath>
</t>
</templates>
</templates>

View File

@ -299,7 +299,8 @@ class TestMailTracking(TransactionCase):
# No author_id
tracking.mail_message_id.author_id = False
values = tracking.mail_message_id.get_failed_messages()[0]
self.assertEqual(values["author"][0], -1)
if values and values.get("author"):
self.assertEqual(values["author"][0], -1)
def test_resend_failed_message(self):
# This message will generate a notification for recipient