[IMP] pre-commit run -a

This commit is contained in:
Jairo Llopis 2021-01-26 14:06:17 +00:00 committed by Jasmin Solanki
parent 29afd15f6d
commit c64663944b
13 changed files with 616 additions and 436 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data noupdate="0"> <data noupdate="0">
@ -8,7 +8,9 @@
<field name="res_id" ref="base.partner_demo" /> <field name="res_id" ref="base.partner_demo" />
<field name="message_type">comment</field> <field name="message_type">comment</field>
<field name="subtype_id" ref="mail.mt_comment" /> <field name="subtype_id" ref="mail.mt_comment" />
<field name="email_cc">acc@testmail.com,wood.corner26@example.com,toni.rhodes11@example.com</field> <field
name="email_cc"
>acc@testmail.com,wood.corner26@example.com,toni.rhodes11@example.com</field>
<field name="mail_tracking_needs_action">1</field> <field name="mail_tracking_needs_action">1</field>
<field name="body"><![CDATA[<p>This is a message with CC</p>]]></field> <field name="body"><![CDATA[<p>This is a message with CC</p>]]></field>
<field name="email_from">wood.corner26@example.com</field> <field name="email_from">wood.corner26@example.com</field>
@ -24,7 +26,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field> <field name="sender">wood.corner26@example.com</field>
<field name="state">sent</field> <field name="state">sent</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/> <field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record> </record>
<!-- Failed Message A --> <!-- Failed Message A -->
@ -48,7 +50,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field> <field name="sender">wood.corner26@example.com</field>
<field name="state">error</field> <field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/> <field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record> </record>
<!-- Failed Message B --> <!-- Failed Message B -->
@ -72,7 +74,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">jackson.group82@example.com</field> <field name="sender">jackson.group82@example.com</field>
<field name="state">error</field> <field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/> <field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record> </record>
<!-- Failed Message C --> <!-- Failed Message C -->
@ -96,7 +98,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">admin@example.com</field> <field name="sender">admin@example.com</field>
<field name="state">error</field> <field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/> <field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record> </record>
</data> </data>

View File

@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
<record model="ir.rule" id="mail_tracking_email_portal_public_rule"> <record model="ir.rule" id="mail_tracking_email_portal_public_rule">
<field name="name">mail_tracking_email: portal/public: read access on my email trackings</field> <field
<field name="model_id" ref="model_mail_tracking_email"/> name="name"
>mail_tracking_email: portal/public: read access on my email trackings</field>
<field name="model_id" ref="model_mail_tracking_email" />
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field> <field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/> <field
<field name="perm_create" eval="False"/> name="groups"
<field name="perm_unlink" eval="False"/> eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"
<field name="perm_write" eval="False"/> />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
<field name="perm_write" eval="False" />
</record> </record>
</odoo> </odoo>

View File

@ -31,7 +31,7 @@
} }
i.fa-caret-down:before { i.fa-caret-down:before {
content: '\f0da'; content: "\f0da";
} }
} }
} }
@ -52,7 +52,7 @@
} }
.o_thread_typing_notification_free_space { .o_thread_typing_notification_free_space {
flex-grow: 1, flex-grow: 1;
} }
.o_thread_typing_notification_bar { .o_thread_typing_notification_bar {
@ -60,7 +60,7 @@
background-color: rgba($white, 0.75); background-color: rgba($white, 0.75);
padding: 5px; padding: 5px;
text-align: center; text-align: center;
color: gray('600'); color: gray("600");
&.o_thread_order_asc { &.o_thread_order_asc {
@include o-position-sticky($bottom: 0px); @include o-position-sticky($bottom: 0px);
@ -83,7 +83,7 @@
margin-top: 0px; margin-top: 0px;
margin-bottom: 15px; margin-bottom: 15px;
} }
border-bottom: 1px solid gray('400'); border-bottom: 1px solid gray("400");
text-align: center; text-align: center;
.o_thread_date { .o_thread_date {
@ -116,8 +116,8 @@
margin-bottom: 0px; margin-bottom: 0px;
&.o_mail_not_discussion { &.o_mail_not_discussion {
background-color: rgba(gray('300'), 0.5); background-color: rgba(gray("300"), 0.5);
border-bottom: 1px solid gray('400'); border-bottom: 1px solid gray("400");
} }
.o_thread_message_sidebar { .o_thread_message_sidebar {
@ -155,7 +155,8 @@
} }
} }
&:hover, &.o_thread_selected_message { &:hover,
&.o_thread_selected_message {
.o_thread_message_side_date { .o_thread_message_side_date {
opacity: $o-mail-thread-side-date-opacity; opacity: $o-mail-thread-side-date-opacity;
} }
@ -182,8 +183,6 @@
text-align: justify; text-align: justify;
} }
.o_mail_subject { .o_mail_subject {
font-style: italic; font-style: italic;
} }
@ -193,7 +192,8 @@
color: gray; color: gray;
} }
[summary~=o_mail_notification] { // name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present [summary~="o_mail_notification"] {
// name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
display: none; display: none;
} }
@ -224,7 +224,10 @@
} }
} }
.o_thread_message_star, .o_thread_message_needaction, .o_thread_message_reply, .o_thread_message_email { .o_thread_message_star,
.o_thread_message_needaction,
.o_thread_message_reply,
.o_thread_message_email {
padding: 4px; padding: 4px;
} }
@ -232,14 +235,16 @@
&.o_thread_message_email_ready { &.o_thread_message_email_ready {
color: grey; color: grey;
} }
&.o_thread_message_email_exception, &.o_thread_message_email_bounce { &.o_thread_message_email_exception,
&.o_thread_message_email_bounce {
color: red; color: red;
opacity: 1; opacity: 1;
cursor: pointer; cursor: pointer;
} }
} }
.o_attachments_list, .o_attachments_previews { .o_attachments_list,
.o_attachments_previews {
&:last-child { &:last-child {
margin-bottom: $grid-gutter-width; margin-bottom: $grid-gutter-width;
} }
@ -299,7 +304,7 @@
.o_activity_info { .o_activity_info {
vertical-align: baseline; vertical-align: baseline;
padding: 4px 6px; padding: 4px 6px;
background: theme-color('light'); background: theme-color("light");
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
@include o-hover-opacity(1, 1); @include o-hover-opacity(1, 1);

View File

@ -1,6 +1,6 @@
/* Copyright 2019 Alexandre Díaz /* Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.FailedMessageDiscuss', function (require) { odoo.define("mail_tracking.FailedMessageDiscuss", function(require) {
"use strict"; "use strict";
// To be considered: // To be considered:
@ -9,45 +9,40 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
// - A mailbox is a type of thread that is displayed on top of // - A mailbox is a type of thread that is displayed on top of
// the discuss menu, has a counter, etc... // the discuss menu, has a counter, etc...
var MailManagerNotif = require('mail.Manager.Notification'); var MailManagerNotif = require("mail.Manager.Notification");
var AbstractMessage = require('mail.model.AbstractMessage'); var AbstractMessage = require("mail.model.AbstractMessage");
var Message = require('mail.model.Message'); var Message = require("mail.model.Message");
var Discuss = require('mail.Discuss'); var Discuss = require("mail.Discuss");
var MailManager = require('mail.Manager'); var MailManager = require("mail.Manager");
var Mailbox = require('mail.model.Mailbox'); var Mailbox = require("mail.model.Mailbox");
var core = require('web.core'); var core = require("web.core");
var session = require('web.session'); var session = require("web.session");
var QWeb = core.qweb; var QWeb = core.qweb;
var _t = core._t; var _t = core._t;
/* The states to consider a message as failed message */ /* The states to consider a message as failed message */
var FAILED_STATES = [ var FAILED_STATES = ["error", "rejected", "spam", "bounced", "soft-bounced"];
'error', 'rejected', 'spam', 'bounced', 'soft-bounced',
];
AbstractMessage.include({ AbstractMessage.include({
/** /**
* Abstract declaration to know if a message is included in the * Abstract declaration to know if a message is included in the
* failed mailbox. By default it should be false. * failed mailbox. By default it should be false.
* *
* @returns {Boolean} * @returns {Boolean}
*/ */
isFailed: function () { isFailed: function() {
return false; return false;
}, },
}); });
Message.include({ Message.include({
/** /**
* Overrides to store information from server * Overrides to store information from server
* *
* @override * @override
*/ */
init: function (parent, data) { init: function(parent, data) {
this._isFailedMessage = data.is_failed_message; this._isFailedMessage = data.is_failed_message;
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
@ -58,8 +53,8 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
isFailed: function () { isFailed: function() {
return _.contains(this._threadIDs, 'mailbox_failed'); return _.contains(this._threadIDs, "mailbox_failed");
}, },
/** /**
@ -67,11 +62,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @param {Boolean} failed * @param {Boolean} failed
*/ */
setFailed: function (failed) { setFailed: function(failed) {
if (failed) { if (failed) {
this._addThread('mailbox_failed'); this._addThread("mailbox_failed");
} else { } else {
this.removeThread('mailbox_failed'); this.removeThread("mailbox_failed");
} }
}, },
@ -80,21 +75,20 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_processMailboxes: function () { _processMailboxes: function() {
this.setFailed(this._isFailedMessage); this.setFailed(this._isFailedMessage);
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
}); });
MailManagerNotif.include({ MailManagerNotif.include({
/** /**
* Overrides to handle changes in the 'mail_tracking_needs_action' flag * Overrides to handle changes in the 'mail_tracking_needs_action' flag
* *
* @override * @override
*/ */
_handlePartnerNotification: function (data) { _handlePartnerNotification: function(data) {
if (data.type === 'toggle_tracking_status') { if (data.type === "toggle_tracking_status") {
this._handleChangeTrackingNeedsActionNotification(data); this._handleChangeTrackingNeedsActionNotification(data);
} else { } else {
// Workaround to avoid call '_handlePartnerChannelNotification' // Workaround to avoid call '_handlePartnerChannelNotification'
@ -113,24 +107,23 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private * @private
* @param {Object} data * @param {Object} data
*/ */
_handleChangeTrackingNeedsActionNotification: function (data) { _handleChangeTrackingNeedsActionNotification: function(data) {
var self = this; var self = this;
var failed = this.getMailbox('failed'); var failed = this.getMailbox("failed");
_.each(data.message_ids, function (messageID) { _.each(data.message_ids, function(messageID) {
var message = _.find(self._messages, function (msg) { var message = _.find(self._messages, function(msg) {
return msg.getID() === messageID; return msg.getID() === messageID;
}); });
if (message) { if (message) {
message.setFailed(data.needs_actions); message.setFailed(data.needs_actions);
if (message.isFailed() === false) { if (message.isFailed() === false) {
self._removeMessageFromThread( self._removeMessageFromThread("mailbox_failed", message);
'mailbox_failed', message);
} else { } else {
self._addMessageToThreads(message, []); self._addMessageToThreads(message, []);
var channelFailed = self.getMailbox('failed'); var channelFailed = self.getMailbox("failed");
channelFailed.invalidateCaches(); channelFailed.invalidateCaches();
} }
self._mailBus.trigger('update_message', message, data.type); self._mailBus.trigger("update_message", message, data.type);
} }
}); });
@ -143,14 +136,14 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
} }
// Trigger event to refresh threads // Trigger event to refresh threads
this._mailBus.trigger('update_failed', failed.getMailboxCounter()); this._mailBus.trigger("update_failed", failed.getMailboxCounter());
}, },
}); });
Discuss.include({ Discuss.include({
events: _.extend({}, Discuss.prototype.events, { events: _.extend({}, Discuss.prototype.events, {
'click .o_failed_message_retry': '_onRetryFailedMessage', "click .o_failed_message_retry": "_onRetryFailedMessage",
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed', "click .o_failed_message_reviewed": "_onMarkFailedMessageReviewed",
}), }),
/** /**
@ -159,8 +152,8 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private * @private
* @returns {Object} * @returns {Object}
*/ */
_sidebarQWebParams: function () { _sidebarQWebParams: function() {
var failed = this.call('mail_service', 'getMailbox', 'failed'); var failed = this.call("mail_service", "getMailbox", "failed");
return { return {
activeThreadID: this._thread ? this._thread.getID() : undefined, activeThreadID: this._thread ? this._thread.getID() : undefined,
failedCounter: failed.getMailboxCounter(), failedCounter: failed.getMailboxCounter(),
@ -173,14 +166,16 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_renderSidebar: function () { _renderSidebar: function() {
var $sidebar = this._super.apply(this, arguments); var $sidebar = this._super.apply(this, arguments);
// Because Odoo implementation isn't designed to be inherited // Because Odoo implementation isn't designed to be inherited
// properly, we inject 'failed' button using jQuery. // properly, we inject 'failed' button using jQuery.
var $failed_item = $(QWeb.render('mail_tracking.SidebarFailed', var $failed_item = $(
this._sidebarQWebParams())); QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
);
$failed_item.insertAfter( $failed_item.insertAfter(
$sidebar.find(".o_mail_discuss_title_main").filter(":last")); $sidebar.find(".o_mail_discuss_title_main").filter(":last")
);
return $sidebar; return $sidebar;
}, },
@ -190,11 +185,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_renderSidebarMailboxes: function () { _renderSidebarMailboxes: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.$('.o_mail_discuss_sidebar_mailboxes').append( this.$(".o_mail_discuss_sidebar_mailboxes").append(
QWeb.render('mail_tracking.SidebarFailed', QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
this._sidebarQWebParams())); );
}, },
/** /**
@ -202,12 +197,15 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_renderButtons: function () { _renderButtons: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.$btn_set_all_reviewed = this.$buttons.find( this.$btn_set_all_reviewed = this.$buttons.find(
'.o_mail_discuss_button_set_all_reviewed'); ".o_mail_discuss_button_set_all_reviewed"
this.$btn_set_all_reviewed );
.on('click', $.proxy(this, "_onSetAllAsReviewedClicked")); this.$btn_set_all_reviewed.on(
"click",
$.proxy(this, "_onSetAllAsReviewedClicked")
);
}, },
/** /**
@ -217,11 +215,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_updateControlPanelButtons: function (thread) { _updateControlPanelButtons: function(thread) {
this.$btn_set_all_reviewed this.$btn_set_all_reviewed.toggleClass(
.toggleClass( "d-none d-md-none",
'd-none d-md-none', thread.getID() !== "mailbox_failed"
thread.getID() !== 'mailbox_failed'); );
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
@ -233,18 +231,16 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_updateButtonStatus: function (disabled, type) { _updateButtonStatus: function(disabled, type) {
if (this._thread.getID() === 'mailbox_failed') { if (this._thread.getID() === "mailbox_failed") {
this.$btn_set_all_reviewed this.$btn_set_all_reviewed.toggleClass("disabled", disabled);
.toggleClass('disabled', disabled);
// Display Rainbowman when all failed messages are reviewed // Display Rainbowman when all failed messages are reviewed
// through 'TOGGLE TRACKING STATUS' or marking last failed // through 'TOGGLE TRACKING STATUS' or marking last failed
// message as reviewed // message as reviewed
if (disabled && type === 'toggle_tracking_status') { if (disabled && type === "toggle_tracking_status") {
this.trigger_up('show_effect', { this.trigger_up("show_effect", {
message: _t( message: _t("Congratulations, your failed mailbox is empty"),
"Congratulations, your failed mailbox is empty"), type: "rainbow_man",
type: 'rainbow_man',
}); });
} }
} }
@ -255,20 +251,18 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_onMessageUpdated: function (message, type) { _onMessageUpdated: function(message, type) {
var self = this; var self = this;
var currentThreadID = this._thread.getID(); var currentThreadID = this._thread.getID();
if (currentThreadID === 'mailbox_failed' && !message.isFailed()) { if (currentThreadID === "mailbox_failed" && !message.isFailed()) {
this._thread.fetchMessages(this.domain) this._thread.fetchMessages(this.domain).then(function() {
.then(function () { var options = self._getThreadRenderingOptions();
var options = self._getThreadRenderingOptions(); self._threadWidget
self._threadWidget.removeMessageAndRender( .removeMessageAndRender(message.getID(), self._thread, options)
message.getID(), self._thread, options) .then(function() {
.then(function () { self._updateButtonStatus(!self._thread.hasMessages(), type);
self._updateButtonStatus( });
!self._thread.hasMessages(), type); });
});
});
} else { } else {
// Workaround to avoid calling '_fetchAndRenderThread' and // Workaround to avoid calling '_fetchAndRenderThread' and
// refetching thread messages because these messages are // refetching thread messages because these messages are
@ -283,9 +277,9 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_getThreadRenderingOptions: function () { _getThreadRenderingOptions: function() {
var values = this._super.apply(this, arguments); var values = this._super.apply(this, arguments);
if (this._thread.getID() === 'mailbox_failed') { if (this._thread.getID() === "mailbox_failed") {
values.displayEmailIcons = true; values.displayEmailIcons = true;
values.displayReplyIcons = false; values.displayReplyIcons = false;
values.displayRetryButton = true; values.displayRetryButton = true;
@ -299,10 +293,13 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @override * @override
*/ */
_startListening: function () { _startListening: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.call('mail_service', 'getMailBus') this.call("mail_service", "getMailBus").on(
.on('update_failed', this, this._throttledUpdateThreads); "update_failed",
this,
this._throttledUpdateThreads
);
}, },
// Handlers // Handlers
@ -312,10 +309,10 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private * @private
* @param {Event} event * @param {Event} event
*/ */
_onRetryFailedMessage: function (event) { _onRetryFailedMessage: function(event) {
event.preventDefault(); event.preventDefault();
var messageID = $(event.currentTarget).data('message-id'); var messageID = $(event.currentTarget).data("message-id");
this.do_action('mail.mail_resend_message_action', { this.do_action("mail.mail_resend_message_action", {
additional_context: { additional_context: {
mail_message_to_resend: messageID, mail_message_to_resend: messageID,
}, },
@ -329,12 +326,12 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @param {Event} event * @param {Event} event
* @returns {Promise} * @returns {Promise}
*/ */
_onMarkFailedMessageReviewed: function (event) { _onMarkFailedMessageReviewed: function(event) {
event.preventDefault(); event.preventDefault();
var messageID = $(event.currentTarget).data('message-id'); var messageID = $(event.currentTarget).data("message-id");
return this._rpc({ return this._rpc({
model: 'mail.message', model: "mail.message",
method: 'set_need_action_done', method: "set_need_action_done",
args: [[messageID]], args: [[messageID]],
context: this.getSession().user_context, context: this.getSession().user_context,
}); });
@ -345,22 +342,21 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* *
* @private * @private
*/ */
_onSetAllAsReviewedClicked: function () { _onSetAllAsReviewedClicked: function() {
this._thread.setAllMessagesAsReviewed(); this._thread.setAllMessagesAsReviewed();
}, },
}); });
MailManager.include({ MailManager.include({
/** /**
* Add the 'failed' mailbox * Add the 'failed' mailbox
* *
* @override * @override
*/ */
_updateMailboxesFromServer: function (data) { _updateMailboxesFromServer: function(data) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._addMailbox({ this._addMailbox({
id: 'failed', id: "failed",
name: _t("Failed"), name: _t("Failed"),
mailboxCounter: data.failed_counter || 0, mailboxCounter: data.failed_counter || 0,
}); });
@ -368,20 +364,19 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
}); });
Mailbox.include({ Mailbox.include({
/** /**
* Overrides to add domain for 'failed' mailbox thread * Overrides to add domain for 'failed' mailbox thread
* *
* @override * @override
*/ */
_getThreadDomain: function () { _getThreadDomain: function() {
if (this._id === 'mailbox_failed') { if (this._id === "mailbox_failed") {
return [ return [
['mail_tracking_ids.state', 'in', FAILED_STATES], ["mail_tracking_ids.state", "in", FAILED_STATES],
['mail_tracking_needs_action', '=', true], ["mail_tracking_needs_action", "=", true],
'|', "|",
['partner_ids', 'in', [session.partner_id]], ["partner_ids", "in", [session.partner_id]],
['author_id', '=', session.partner_id], ["author_id", "=", session.partner_id],
]; ];
} }
// Workaround to avoid throw 'Missing domain' exception. Call _super // Workaround to avoid throw 'Missing domain' exception. Call _super
@ -397,15 +392,14 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @returns {$.Promise} resolved when all messages have been marked as * @returns {$.Promise} resolved when all messages have been marked as
* reviewed on the server * reviewed on the server
*/ */
setAllMessagesAsReviewed: function () { setAllMessagesAsReviewed: function() {
if (this._id === 'mailbox_failed' && this.getMailboxCounter() > 0) { if (this._id === "mailbox_failed" && this.getMailboxCounter() > 0) {
return this._rpc({ return this._rpc({
model: 'mail.message', model: "mail.message",
method: 'set_all_as_reviewed', method: "set_all_as_reviewed",
}); });
} }
return $.when(); return $.when();
}, },
}); });
}); });

View File

@ -1,21 +1,20 @@
/* Copyright 2019 Alexandre Díaz /* Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.FailedMessageThread', function (require) { odoo.define("mail_tracking.FailedMessageThread", function(require) {
"use strict"; "use strict";
var AbstractField = require('web.AbstractField'); var AbstractField = require("web.AbstractField");
var BasicModel = require('web.BasicModel'); var BasicModel = require("web.BasicModel");
var BasicView = require('web.BasicView'); var BasicView = require("web.BasicView");
var Chatter = require('mail.Chatter'); var Chatter = require("mail.Chatter");
var MailThread = require('mail.widget.Thread'); var MailThread = require("mail.widget.Thread");
var utils = require('mail.utils'); var utils = require("mail.utils");
var core = require('web.core'); var core = require("web.core");
var field_registry = require('web.field_registry'); var field_registry = require("web.field_registry");
var time = require('web.time'); var time = require("web.time");
var QWeb = core.qweb; var QWeb = core.qweb;
/** /**
* Helper method to fetch failed messages * Helper method to fetch failed messages
* *
@ -24,28 +23,29 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {Array} ids * @param {Array} ids
* @returns {Array} * @returns {Array}
*/ */
function _readMessages (widget, ids) { function _readMessages(widget, ids) {
if (!ids.length) { if (!ids.length) {
return $.when([]); return $.when([]);
} }
var context = widget.record && widget.record.getContext(); var context = widget.record && widget.record.getContext();
return widget._rpc({ return widget
model: 'mail.message', ._rpc({
method: 'get_failed_messages', model: "mail.message",
args: [ids], method: "get_failed_messages",
context: context || widget.getSession().user_context, args: [ids],
}).then(function (messages) { context: context || widget.getSession().user_context,
// Convert date to moment })
_.each(messages, function (msg) { .then(function(messages) {
msg.date = moment(time.auto_str_to_date(msg.date)); // Convert date to moment
msg.hour = utils.timeFromNow(msg.date); _.each(messages, function(msg) {
msg.date = moment(time.auto_str_to_date(msg.date));
msg.hour = utils.timeFromNow(msg.date);
});
return messages;
}); });
return messages;
});
} }
BasicModel.include({ BasicModel.include({
/** /**
* Fetch data for the 'mail_failed_message' field widget in form views. * Fetch data for the 'mail_failed_message' field widget in form views.
* *
@ -54,27 +54,29 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {String} fieldName * @param {String} fieldName
* @returns {Array} * @returns {Array}
*/ */
_fetchSpecialFailedMessages: function (record, fieldName) { _fetchSpecialFailedMessages: function(record, fieldName) {
var localID = record._changes && fieldName in record._changes var localID =
? record._changes[fieldName] : record.data[fieldName]; record._changes && fieldName in record._changes
? record._changes[fieldName]
: record.data[fieldName];
return _readMessages(this, this.localData[localID].res_ids); return _readMessages(this, this.localData[localID].res_ids);
}, },
}); });
var FailedMessage = AbstractField.extend({ var FailedMessage = AbstractField.extend({
className: 'o_mail_failed_message', className: "o_mail_failed_message",
events: { events: {
'click .o_failed_message_retry': '_onRetryFailedMessage', "click .o_failed_message_retry": "_onRetryFailedMessage",
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed', "click .o_failed_message_reviewed": "_onMarkFailedMessageReviewed",
}, },
specialData: '_fetchSpecialFailedMessages', specialData: "_fetchSpecialFailedMessages",
/** /**
* Overrides to reference failed messages in a easy way * Overrides to reference failed messages in a easy way
* *
* @override * @override
*/ */
init: function () { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.failed_messages = this.record.specialData[this.name] || []; this.failed_messages = this.record.specialData[this.name] || [];
}, },
@ -84,10 +86,9 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* *
* @override * @override
*/ */
start: function () { start: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.call( this.call("bus_service", "onNotification", this, this._onNotification);
'bus_service', 'onNotification', this, this._onNotification);
}, },
/** /**
@ -96,7 +97,7 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @returns {Object} * @returns {Object}
*/ */
_failedItemsQWebParams: function () { _failedItemsQWebParams: function() {
return { return {
failed_messages: this.failed_messages, failed_messages: this.failed_messages,
nbFailedMessages: this.failed_messages.length, nbFailedMessages: this.failed_messages.length,
@ -108,11 +109,14 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
/** /**
* @private * @private
*/ */
_render: function () { _render: function() {
if (this.failed_messages.length) { if (this.failed_messages.length) {
this.$el.html(QWeb.render( this.$el.html(
'mail_tracking.failed_message_items', QWeb.render(
this._failedItemsQWebParams())); "mail_tracking.failed_message_items",
this._failedItemsQWebParams()
)
);
} else { } else {
this.$el.empty(); this.$el.empty();
} }
@ -124,7 +128,7 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @param {Object} record * @param {Object} record
*/ */
_reset: function (record) { _reset: function(record) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.failed_messages = this.record.specialData[this.name] || []; this.failed_messages = this.record.specialData[this.name] || [];
this.res_id = record.res_id; this.res_id = record.res_id;
@ -136,8 +140,8 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @param {Array} fieldsToReload * @param {Array} fieldsToReload
*/ */
_reload: function (fieldsToReload) { _reload: function(fieldsToReload) {
this.trigger_up('reload_mail_fields', fieldsToReload); this.trigger_up("reload_mail_fields", fieldsToReload);
}, },
/** /**
@ -147,10 +151,10 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {Int} id * @param {Int} id
* @returns {Promise} * @returns {Promise}
*/ */
_markFailedMessageReviewed: function (id) { _markFailedMessageReviewed: function(id) {
return this._rpc({ return this._rpc({
model: 'mail.message', model: "mail.message",
method: 'set_need_action_done', method: "set_need_action_done",
args: [[id]], args: [[id]],
context: this.record.getContext(), context: this.record.getContext(),
}); });
@ -165,13 +169,13 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @param {Array} notifs * @param {Array} notifs
*/ */
_onNotification: function (notifs) { _onNotification: function(notifs) {
var self = this; var self = this;
_.each(notifs, function (notif) { _.each(notifs, function(notif) {
var model = notif[0][1]; var model = notif[0][1];
if (model === 'res.partner') { if (model === "res.partner") {
var data = notif[1]; var data = notif[1];
if (data.type === 'toggle_tracking_status') { if (data.type === "toggle_tracking_status") {
// Reload 'mail_failed_message' widget // Reload 'mail_failed_message' widget
self._reload({failed_message: true}); self._reload({failed_message: true});
} }
@ -186,10 +190,10 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @param {Event} event * @param {Event} event
*/ */
_onRetryFailedMessage: function (event) { _onRetryFailedMessage: function(event) {
event.preventDefault(); event.preventDefault();
var messageID = $(event.currentTarget).data('message-id'); var messageID = $(event.currentTarget).data("message-id");
this.do_action('mail.mail_resend_message_action', { this.do_action("mail.mail_resend_message_action", {
additional_context: { additional_context: {
mail_message_to_resend: messageID, mail_message_to_resend: messageID,
}, },
@ -202,26 +206,26 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @param {Event} event * @param {Event} event
*/ */
_onMarkFailedMessageReviewed: function (event) { _onMarkFailedMessageReviewed: function(event) {
event.preventDefault(); event.preventDefault();
var messageID = $(event.currentTarget).data('message-id'); var messageID = $(event.currentTarget).data("message-id");
this._markFailedMessageReviewed(messageID).then( this._markFailedMessageReviewed(messageID).then(
$.proxy(this, "_reload", {failed_message: true})); $.proxy(this, "_reload", {failed_message: true})
);
}, },
}); });
field_registry.add('mail_failed_message', FailedMessage); field_registry.add("mail_failed_message", FailedMessage);
var mailWidgets = ['mail_failed_message']; var mailWidgets = ["mail_failed_message"];
BasicView.include({ BasicView.include({
/** /**
* Overrides to add 'mail_failed_message' widget as "mail widget" used * Overrides to add 'mail_failed_message' widget as "mail widget" used
* in Chatter. * in Chatter.
* *
* @override * @override
*/ */
init: function () { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
var fieldsInfo = this.fieldsInfo[this.viewType]; var fieldsInfo = this.fieldsInfo[this.viewType];
for (var fieldName in fieldsInfo) { for (var fieldName in fieldsInfo) {
@ -245,18 +249,21 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
}); });
Chatter.include({ Chatter.include({
/** /**
* Overrides to initialize 'mail_failed_message' widget. * Overrides to initialize 'mail_failed_message' widget.
* *
* @override * @override
*/ */
init: function (parent, record, mailFields, options) { init: function(parent, record, mailFields, options) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
// Initialize mail_failed_message widget // Initialize mail_failed_message widget
if (mailFields.mail_failed_message) { if (mailFields.mail_failed_message) {
this.fields.failed_message = new FailedMessage( this.fields.failed_message = new FailedMessage(
this, mailFields.mail_failed_message, record, options); this,
mailFields.mail_failed_message,
record,
options
);
} }
}, },
@ -266,12 +273,13 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private * @private
* @returns {Promise} * @returns {Promise}
*/ */
_render: function () { _render: function() {
var self = this; var self = this;
return this._super.apply(this, arguments).then(function () { return this._super.apply(this, arguments).then(function() {
if (self.fields.failed_message) { if (self.fields.failed_message) {
self.fields.failed_message.$el.insertBefore( self.fields.failed_message.$el.insertBefore(
self.$el.find('.o_mail_thread')); self.$el.find(".o_mail_thread")
);
} }
}); });
}, },
@ -281,9 +289,9 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* *
* @override * @override
*/ */
_onReloadMailFields: function (event) { _onReloadMailFields: function(event) {
if (this.fields.failed_message && event.data.failed_message) { if (this.fields.failed_message && event.data.failed_message) {
this.trigger_up('reload', { this.trigger_up("reload", {
fieldNames: [this.fields.failed_message.name], fieldNames: [this.fields.failed_message.name],
keepChanges: true, keepChanges: true,
}); });
@ -296,13 +304,12 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
}); });
MailThread.include({ MailThread.include({
/** /**
* Show 'retry' & 'Set as reviewed' buttons in the Chatter * Show 'retry' & 'Set as reviewed' buttons in the Chatter
* *
* @override * @override
*/ */
init: function () { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._enabledOptions.displayRetryButton = true; this._enabledOptions.displayRetryButton = true;
this._enabledOptions.displayReviewedButton = true; this._enabledOptions.displayReviewedButton = true;
@ -312,5 +319,4 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
}); });
return FailedMessage; return FailedMessage;
}); });

View File

@ -2,25 +2,24 @@
Copyright 2018 David Vidal - <david.vidal@tecnativa.com> Copyright 2018 David Vidal - <david.vidal@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.partner_tracking', function (require) { odoo.define("mail_tracking.partner_tracking", function(require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require("web.core");
var ActionManager = require('web.ActionManager'); var ActionManager = require("web.ActionManager");
var AbstractMessage = require('mail.model.AbstractMessage'); var AbstractMessage = require("mail.model.AbstractMessage");
var Message = require('mail.model.Message'); var Message = require("mail.model.Message");
var ThreadWidget = require('mail.widget.Thread'); var ThreadWidget = require("mail.widget.Thread");
var _t = core._t; var _t = core._t;
AbstractMessage.include({ AbstractMessage.include({
/** /**
* Messages do not have any PartnerTrackings. * Messages do not have any PartnerTrackings.
* *
* @returns {Boolean} * @returns {Boolean}
*/ */
hasPartnerTrackings: function () { hasPartnerTrackings: function() {
return false; return false;
}, },
@ -29,13 +28,13 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* *
* @returns {Boolean} * @returns {Boolean}
*/ */
hasEmailCc: function () { hasEmailCc: function() {
return false; return false;
}, },
}); });
Message.include({ Message.include({
init: function (parent, data) { init: function(parent, data) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._partnerTrackings = data.partner_trackings || []; this._partnerTrackings = data.partner_trackings || [];
this._emailCc = data.email_cc || []; this._emailCc = data.email_cc || [];
@ -48,7 +47,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @override * @override
* @returns {Boolean} * @returns {Boolean}
*/ */
hasPartnerTrackings: function () { hasPartnerTrackings: function() {
return _.some(this._partnerTrackings); return _.some(this._partnerTrackings);
}, },
@ -57,7 +56,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* *
* @returns {Boolean} * @returns {Boolean}
*/ */
hasEmailCc: function () { hasEmailCc: function() {
return _.some(this._emailCc); return _.some(this._emailCc);
}, },
@ -68,7 +67,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @override * @override
* @returns {Object[]} * @returns {Object[]}
*/ */
getPartnerTrackings: function () { getPartnerTrackings: function() {
if (!this.hasPartnerTrackings()) { if (!this.hasPartnerTrackings()) {
return []; return [];
} }
@ -81,7 +80,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* *
* @returns {Array} * @returns {Array}
*/ */
getEmailCc: function () { getEmailCc: function() {
if (!this.hasEmailCc()) { if (!this.hasEmailCc()) {
return []; return [];
} }
@ -95,19 +94,19 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @param {String} email * @param {String} email
* @returns {Boolean} * @returns {Boolean}
*/ */
isEmailCc: function (email) { isEmailCc: function(email) {
if (!this.hasEmailCc()) { if (!this.hasEmailCc()) {
return false; return false;
} }
return _.some(this._emailCc, function (item) { return _.some(this._emailCc, function(item) {
return item[0] === email; return item[0] === email;
}); });
}, },
toggleTrackingStatus: function () { toggleTrackingStatus: function() {
return this._rpc({ return this._rpc({
model: 'mail.message', model: "mail.message",
method: 'toggle_tracking_status', method: "toggle_tracking_status",
args: [[this.id]], args: [[this.id]],
}); });
}, },
@ -115,53 +114,52 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
ThreadWidget.include({ ThreadWidget.include({
events: _.extend(ThreadWidget.prototype.events, { events: _.extend(ThreadWidget.prototype.events, {
'click .o_mail_action_tracking_partner': "click .o_mail_action_tracking_partner": "on_tracking_partner_click",
'on_tracking_partner_click', "click .o_mail_action_tracking_status": "on_tracking_status_click",
'click .o_mail_action_tracking_status': 'on_tracking_status_click',
}), }),
on_tracking_partner_click: function (event) { on_tracking_partner_click: function(event) {
var partner_id = this.$el.find(event.currentTarget).data('partner'); var partner_id = this.$el.find(event.currentTarget).data("partner");
var state = { var state = {
'model': 'res.partner', model: "res.partner",
'id': partner_id, id: partner_id,
'title': _t("Tracking partner"), title: _t("Tracking partner"),
}; };
event.preventDefault(); event.preventDefault();
this.action_manager.do_push_state(state); this.action_manager.do_push_state(state);
var action = { var action = {
type:'ir.actions.act_window', type: "ir.actions.act_window",
view_type: 'form', view_type: "form",
view_mode: 'form', view_mode: "form",
res_model: 'res.partner', res_model: "res.partner",
views: [[false, 'form']], views: [[false, "form"]],
target: 'current', target: "current",
res_id: partner_id, res_id: partner_id,
}; };
this.do_action(action); this.do_action(action);
}, },
on_tracking_status_click: function (event) { on_tracking_status_click: function(event) {
var tracking_email_id = $(event.currentTarget).data('tracking'); var tracking_email_id = $(event.currentTarget).data("tracking");
var state = { var state = {
'model': 'mail.tracking.email', model: "mail.tracking.email",
'id': tracking_email_id, id: tracking_email_id,
'title': _t("Message tracking"), title: _t("Message tracking"),
}; };
event.preventDefault(); event.preventDefault();
this.action_manager.do_push_state(state); this.action_manager.do_push_state(state);
var action = { var action = {
type:'ir.actions.act_window', type: "ir.actions.act_window",
view_type: 'form', view_type: "form",
view_mode: 'form', view_mode: "form",
res_model: 'mail.tracking.email', res_model: "mail.tracking.email",
views: [[false, 'form']], views: [[false, "form"]],
target: 'new', target: "new",
res_id: tracking_email_id, res_id: tracking_email_id,
}; };
this.do_action(action); this.do_action(action);
}, },
init: function () { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.action_manager = this.findAncestor(function (ancestor) { this.action_manager = this.findAncestor(function(ancestor) {
return ancestor instanceof ActionManager; return ancestor instanceof ActionManager;
}); });
}, },

View File

@ -1,13 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<templates> <templates>
<t t-extend="mail.widget.Thread.Message"> <t t-extend="mail.widget.Thread.Message">
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append"> <t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
<a t-if="message.isFailed() &amp;&amp; options.displayRetryButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.getID()"> <a
<i class="fa fa-check"/> Set as Reviewed t-if="message.isFailed() &amp;&amp; options.displayRetryButton"
class="btn btn-link o_thread_icon btn-default text-muted 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>
<a t-if="message.isFailed() &amp;&amp; options.displayReviewedButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.getID()"> <a
<i class="fa fa-retweet"/> Retry t-if="message.isFailed() &amp;&amp; options.displayReviewedButton"
class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_retry"
t-att-data-message-id="message.getID()"
>
<i class="fa fa-retweet" /> Retry
</a> </a>
</t> </t>
</t> </t>

View File

@ -1,25 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<templates> <templates>
<t t-name="mail_tracking.SidebarFailedCounter"> <t t-name="mail_tracking.SidebarFailedCounter">
<span t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}"> <span
<t t-esc="counter"/> t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}"
>
<t t-esc="counter" />
</span> </span>
</t> </t>
<t t-name="mail_tracking.SidebarFailed"> <t t-name="mail_tracking.SidebarFailed">
<div t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}" <div
data-thread-id="mailbox_failed"> t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}"
<span class="o_thread_name"><i class="fa fa-exclamation mr8"/>Failed</span> data-thread-id="mailbox_failed"
<t t-set="counter" t-value="failedCounter"/> >
<t t-call="mail_tracking.SidebarFailedCounter"/> <span class="o_thread_name"><i class="fa fa-exclamation mr8" />Failed</span>
<t t-set="counter" t-value="failedCounter" />
<t t-call="mail_tracking.SidebarFailedCounter" />
</div> </div>
</t> </t>
<t t-extend="mail.widget.Thread.Empty"> <t t-extend="mail.widget.Thread.Empty">
<t t-jquery="t:last-child" t-operation="after"> <t t-jquery="t:last-child" t-operation="after">
<t t-if="thread.getID() === 'mailbox_failed'"> <t t-if="thread.getID() === 'mailbox_failed'">
<div class="o_thread_title">Congratulations, you don't have any failed messages</div> <div
class="o_thread_title"
>Congratulations, you don't have any failed messages</div>
<div>Failed messages appear here.</div> <div>Failed messages appear here.</div>
</t> </t>
</t> </t>
@ -27,7 +33,11 @@
<t t-extend="mail.discuss.ControlButtons"> <t t-extend="mail.discuss.ControlButtons">
<t t-jquery="div" t-operation="append"> <t t-jquery="div" t-operation="append">
<button type="button" class="btn btn-secondary o_mail_discuss_button_set_all_reviewed d-none d-md-none d-md-inline-block" title="Mark all as reviewed">Set all as reviewed</button> <button
type="button"
class="btn btn-secondary o_mail_discuss_button_set_all_reviewed d-none d-md-none d-md-inline-block"
title="Mark all as reviewed"
>Set all as reviewed</button>
</t> </t>
</t> </t>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2019 Alexandre Díaz Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
@ -7,54 +7,57 @@
<t t-name="mail.tracking.status"> <t t-name="mail.tracking.status">
<t t-if="tracking['isCc']"> <t t-if="tracking['isCc']">
<span class="mail_tracking_cc"> <span class="mail_tracking_cc">
<i class="fa fa-cc"></i> <i class="fa fa-cc" />
</span> </span>
</t> </t>
<t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']"> <t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']">
<span class="mail_anon_recipient"> <span class="mail_anon_recipient">
<i class="fa fa-low-vision"></i> <i class="fa fa-low-vision" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'unknown'"> <t t-elif="tracking['status'] === 'unknown'">
<span class="mail_tracking_unknown"> <span class="mail_tracking_unknown">
<i class="fa fa-ban"></i> <i class="fa fa-ban" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'waiting'"> <t t-elif="tracking['status'] === 'waiting'">
<span class="mail_tracking_waiting mail_tracking_pointer"> <span class="mail_tracking_waiting mail_tracking_pointer">
<i class="fa fa-clock-o"></i> <i class="fa fa-clock-o" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'error'"> <t t-elif="tracking['status'] === 'error'">
<span class="mail_tracking_error mail_tracking_pointer"> <span class="mail_tracking_error mail_tracking_pointer">
<i t-if="tracking['error_type'] === 'no_recipient'" class="fa fa-user-times"></i> <i
<i t-else="" class="fa fa-remove"></i> t-if="tracking['error_type'] === 'no_recipient'"
class="fa fa-user-times"
/>
<i t-else="" class="fa fa-remove" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'sent'"> <t t-elif="tracking['status'] === 'sent'">
<span class="mail_tracking_sent mail_tracking_pointer"> <span class="mail_tracking_sent mail_tracking_pointer">
<i class="fa fa-check"></i> <i class="fa fa-check" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'delivered'"> <t t-elif="tracking['status'] === 'delivered'">
<span class="fa-stack mail_tracking_delivered mail_tracking_pointer"> <span class="fa-stack mail_tracking_delivered mail_tracking_pointer">
<i class="fa fa-check fa-stack-1x" style="margin-left:1px"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i> <i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;" />
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'opened'"> <t t-elif="tracking['status'] === 'opened'">
<span class="fa-stack mail_tracking_opened mail_tracking_pointer"> <span class="fa-stack mail_tracking_opened mail_tracking_pointer">
<i class="fa fa-check fa-stack-1x" style="margin-left:1px"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i> <i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;" />
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span> </span>
</t> </t>
</t> </t>
<t t-extend="mail.widget.Thread.Message"> <t t-extend="mail.widget.Thread.Message">
<t t-jquery="p.o_mail_info" t-operation="after"> <t t-jquery="p.o_mail_info" t-operation="after">
<t t-if="message.hasPartnerTrackings() || message.hasEmailCc()" > <t t-if="message.hasPartnerTrackings() || message.hasEmailCc()">
<p class="o_mail_tracking"> <p class="o_mail_tracking">
<strong>To:</strong> <strong>To:</strong>
<t t-foreach="message.getPartnerTrackings()" t-as="tracking"> <t t-foreach="message.getPartnerTrackings()" t-as="tracking">
@ -62,21 +65,35 @@
- -
</t> </t>
<t t-if="tracking['partner_id']"> <t t-if="tracking['partner_id']">
<a t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}" <a
t-att-data-partner="tracking['partner_id']" t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}"
t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}"> t-att-data-partner="tracking['partner_id']"
<t t-esc="tracking['recipient']"/> t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}"
>
<t t-esc="tracking['recipient']" />
</a> </a>
</t> </t>
<t t-else=""> <t t-else="">
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t t-esc="tracking['recipient']"/></span> <span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t
t-esc="tracking['recipient']"
/></span>
</t> </t>
<t t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'" t-set="title_status" t-value="tracking['error_description']" /> <t
<t t-else="" t-set="title_status" t-value="tracking['status_human']" /> t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'"
<span class="mail_tracking o_mail_action_tracking_status" t-set="title_status"
t-att-data-tracking="tracking['tracking_id']" t-value="tracking['error_description']"
t-att-title="title_status"> />
<t t-call="mail.tracking.status"/> <t
t-else=""
t-set="title_status"
t-value="tracking['status_human']"
/>
<span
class="mail_tracking o_mail_action_tracking_status"
t-att-data-tracking="tracking['tracking_id']"
t-att-title="title_status"
>
<t t-call="mail.tracking.status" />
</span> </span>
</t> </t>
</p> </p>

View File

@ -1,21 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
<template id="assets_backend" <template
name="mail_tracking assets" id="assets_backend"
inherit_id="web.assets_backend"> name="mail_tracking assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<link rel="stylesheet" <link
href="/mail_tracking/static/src/css/mail_tracking.scss"/> rel="stylesheet"
<link rel="stylesheet" href="/mail_tracking/static/src/css/mail_tracking.scss"
href="/mail_tracking/static/src/css/failed_message.scss"/> />
<script type="text/javascript" <link
src="/mail_tracking/static/src/js/mail_tracking.js"/> rel="stylesheet"
<script type="text/javascript" href="/mail_tracking/static/src/css/failed_message.scss"
src="/mail_tracking/static/src/js/failed_message/discuss.js"/> />
<script type="text/javascript" <script
src="/mail_tracking/static/src/js/failed_message/thread.js"/> type="text/javascript"
src="/mail_tracking/static/src/js/mail_tracking.js"
/>
<script
type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/discuss.js"
/>
<script
type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/thread.js"
/>
</xpath> </xpath>
</template> </template>
</odoo> </odoo>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="view_message_form"> <record model="ir.ui.view" id="view_message_form">
<field name="model">mail.message</field> <field name="model">mail.message</field>
<field name="inherit_id" ref="mail.view_message_form"/> <field name="inherit_id" ref="mail.view_message_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="subtype_id" position="after"> <field name="subtype_id" position="after">
<field name="mail_tracking_needs_action" /> <field name="mail_tracking_needs_action" />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
@ -9,50 +9,54 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="MailTracking event" create="false" edit="false" delete="false"> <form string="MailTracking event" create="false" edit="false" delete="false">
<header> <header>
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar" />
</header> </header>
<sheet> <sheet>
<group> <group>
<field name="name"/> <field name="name" />
</group> </group>
<group> <group>
<group> <group>
<field name="mail_message_id"/> <field name="mail_message_id" />
<field name="mail_id"/> <field name="mail_id" />
<field name="partner_id"/> <field name="partner_id" />
<field name="recipient"/> <field name="recipient" />
<field name="sender"/> <field name="sender" />
</group> </group>
<group> <group>
<field name="timestamp"/> <field name="timestamp" />
<field name="time"/> <field name="time" />
<field name="date"/> <field name="date" />
</group> </group>
</group> </group>
<group attrs="{'invisible': [('bounce_type', '=', False)]}"> <group attrs="{'invisible': [('bounce_type', '=', False)]}">
<field name="bounce_type"/> <field name="bounce_type" />
<field name="bounce_description"/> <field name="bounce_description" />
</group> </group>
<group attrs="{'invisible': [('error_type', '=', False)]}"> <group attrs="{'invisible': [('error_type', '=', False)]}">
<field name="error_smtp_server" <field
attrs="{'invisible': [('error_smtp_server', '=', False)]}"/> name="error_smtp_server"
<field name="error_type"/> attrs="{'invisible': [('error_smtp_server', '=', False)]}"
<field name="error_description"/> />
<field name="error_type" />
<field name="error_description" />
</group> </group>
<label for="tracking_event_ids"/> <label for="tracking_event_ids" />
<div> <div>
<field name="tracking_event_ids"> <field name="tracking_event_ids">
<tree string="Tracking events" <tree
decoration-muted="event_type == 'deferral'" string="Tracking events"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')" decoration-muted="event_type == 'deferral'"
decoration-info="event_type in ('unsub', 'click', 'open')"> decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
<field name="time"/> decoration-info="event_type in ('unsub', 'click', 'open')"
<field name="event_type"/> >
<field name="ip"/> <field name="time" />
<field name="url"/> <field name="event_type" />
<field name="user_country_id" string="Country"/> <field name="ip" />
<field name="os_family" string="OS"/> <field name="url" />
<field name="ua_family" string="User agent"/> <field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree> </tree>
</field> </field>
</div> </div>
@ -65,17 +69,22 @@
<field name="name">mail.tracking.email.tree</field> <field name="name">mail.tracking.email.tree</field>
<field name="model">mail.tracking.email</field> <field name="model">mail.tracking.email</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="MailTracking emails" create="false" edit="false" delete="false" <tree
decoration-muted="state in (False, 'deferred')" string="MailTracking emails"
decoration-success="state == 'opened'" create="false"
decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')" edit="false"
decoration-info="state == 'unsub'"> delete="false"
<field name="time"/> decoration-muted="state in (False, 'deferred')"
<field name="date" invisible="1"/> decoration-success="state == 'opened'"
<field name="name"/> decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')"
<field name="sender" string="Sender"/> decoration-info="state == 'unsub'"
<field name="recipient" string="Recipient"/> >
<field name="state"/> <field name="time" />
<field name="date" invisible="1" />
<field name="name" />
<field name="sender" string="Sender" />
<field name="recipient" string="Recipient" />
<field name="state" />
</tree> </tree>
</field> </field>
</record> </record>
@ -85,25 +94,63 @@
<field name="model">mail.tracking.email</field> <field name="model">mail.tracking.email</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="MailTracking email search"> <search string="MailTracking email search">
<field name="display_name" string="Email" <field
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"/> name="display_name"
<field name="sender" string="Sender"/> string="Email"
<field name="recipient" string="Recipient"/> filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"
<field name="name" string="Subject"/> />
<field name="time" string="Time"/> <field name="sender" string="Sender" />
<field name="date" string="Date"/> <field name="recipient" string="Recipient" />
<filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]"/> <field name="name" string="Subject" />
<filter name="deferred" string="Deferred" domain="[('state', '=', 'deferred')]"/> <field name="time" string="Time" />
<filter name="delivered" string="Delivered" domain="[('state', 'in', ('delivered', 'opened'))]"/> <field name="date" string="Date" />
<filter name="unsub" string="Unsubscribed" domain="[('state', '=', 'unsub')]"/> <filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]" />
<filter name="exception" string="Failed" <filter
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"/> name="deferred"
<separator/> string="Deferred"
domain="[('state', '=', 'deferred')]"
/>
<filter
name="delivered"
string="Delivered"
domain="[('state', 'in', ('delivered', 'opened'))]"
/>
<filter
name="unsub"
string="Unsubscribed"
domain="[('state', '=', 'unsub')]"
/>
<filter
name="exception"
string="Failed"
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"
/>
<separator />
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="State" name="group_by_state" domain="[]" context="{'group_by': 'state'}"/> <filter
<filter string="Subject" name="group_by_subject" domain="[]" context="{'group_by': 'name'}"/> string="State"
<filter string="Sender" name="group_by_sender" domain="[]" context="{'group_by': 'sender'}"/> name="group_by_state"
<filter string="Month" name="group_by_month" domain="[]" context="{'group_by': 'date'}"/> domain="[]"
context="{'group_by': 'state'}"
/>
<filter
string="Subject"
name="group_by_subject"
domain="[]"
context="{'group_by': 'name'}"
/>
<filter
string="Sender"
name="group_by_sender"
domain="[]"
context="{'group_by': 'sender'}"
/>
<filter
string="Month"
name="group_by_month"
domain="[]"
context="{'group_by': 'date'}"
/>
</group> </group>
</search> </search>
</field> </field>
@ -114,12 +161,15 @@
<field name="name">MailTracking emails</field> <field name="name">MailTracking emails</field>
<field name="res_model">mail.tracking.email</field> <field name="res_model">mail.tracking.email</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_mail_tracking_email_search"/> <field name="search_view_id" ref="view_mail_tracking_email_search" />
</record> </record>
<!-- Add menu entry in Settings/Email --> <!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking emails" id="menu_mail_tracking_email" <menuitem
parent="base.menu_email" name="Tracking emails"
action="action_view_mail_tracking_email"/> id="menu_mail_tracking_email"
parent="base.menu_email"
action="action_view_mail_tracking_email"
/>
</odoo> </odoo>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
@ -11,40 +11,46 @@
<sheet> <sheet>
<group> <group>
<group> <group>
<field name="tracking_email_id"/> <field name="tracking_email_id" />
<field name="recipient"/> <field name="recipient" />
<field name="event_type"/> <field name="event_type" />
</group> </group>
<group> <group>
<field name="timestamp"/> <field name="timestamp" />
<field name="time"/> <field name="time" />
<field name="date"/> <field name="date" />
</group> </group>
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('sent',))]}"> <group attrs="{'invisible': [('event_type', 'not in', ('sent',))]}">
<field name="smtp_server"/> <field name="smtp_server" />
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> <group
<field name="url"/> attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<field name="url" />
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> <group
attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<group> <group>
<field name="mobile"/> <field name="mobile" />
<field name="ip"/> <field name="ip" />
<field name="user_country_id"/> <field name="user_country_id" />
</group> </group>
<group> <group>
<field name="user_agent"/> <field name="user_agent" />
<field name="ua_family"/> <field name="ua_family" />
<field name="ua_type"/> <field name="ua_type" />
<field name="os_family"/> <field name="os_family" />
</group> </group>
</group> </group>
<group string="Error" <group
attrs="{'invisible': [('error_type', '=', False)]}"> string="Error"
<field name="error_type"/> attrs="{'invisible': [('error_type', '=', False)]}"
<field name="error_description"/> >
<field name="error_details"/> <field name="error_type" />
<field name="error_description" />
<field name="error_details" />
</group> </group>
</sheet> </sheet>
</form> </form>
@ -55,21 +61,29 @@
<field name="name">mail.tracking.event.tree</field> <field name="name">mail.tracking.event.tree</field>
<field name="model">mail.tracking.event</field> <field name="model">mail.tracking.event</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="MailTracking events" create="false" edit="false" delete="false" <tree
decoration-muted="event_type == 'deferred'" string="MailTracking events"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')" create="false"
decoration-info="event_type in ('unsub', 'click', 'open')"> edit="false"
<field name="time"/> delete="false"
<field name="tracking_email_id"/> decoration-muted="event_type == 'deferred'"
<field name="recipient"/> decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
<field name="event_type"/> decoration-info="event_type in ('unsub', 'click', 'open')"
<field name="error_details" invisible="not context.get('event_error_filter', False)"/> >
<field name="date" invisible="1"/> <field name="time" />
<field name="ip"/> <field name="tracking_email_id" />
<field name="url"/> <field name="recipient" />
<field name="user_country_id" string="Country"/> <field name="event_type" />
<field name="os_family" string="OS"/> <field
<field name="ua_family" string="User agent"/> name="error_details"
invisible="not context.get('event_error_filter', False)"
/>
<field name="date" invisible="1" />
<field name="ip" />
<field name="url" />
<field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree> </tree>
</field> </field>
</record> </record>
@ -79,32 +93,88 @@
<field name="model">mail.tracking.event</field> <field name="model">mail.tracking.event</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="MailTracking event search"> <search string="MailTracking event search">
<field name="tracking_email_id" string="Message" <field
filter_domain="[('tracking_email_id', 'ilike', self)]"/> name="tracking_email_id"
<field name="recipient" string="Recipient"/> string="Message"
<field name="time" string="Time"/> filter_domain="[('tracking_email_id', 'ilike', self)]"
<field name="date" string="Date"/> />
<field name="ip" string="IP"/> <field name="recipient" string="Recipient" />
<field name="url" string="URL"/> <field name="time" string="Time" />
<filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]"/> <field name="date" string="Date" />
<filter name="delivered" string="Delivered" domain="[('event_type', '=', 'delivered')]"/> <field name="ip" string="IP" />
<filter name="click" string="Click" domain="[('event_type', '=', 'click')]"/> <field name="url" string="URL" />
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]"/> <filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]" />
<filter name="unsub" string="Unsubscribe" domain="[('event_type', '=', 'unsub')]"/> <filter
<filter name="bounce" string="Bounce" name="delivered"
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"/> string="Delivered"
<filter name="exception" string="Failed" domain="[('event_type', '=', 'delivered')]"
/>
<filter
name="click"
string="Click"
domain="[('event_type', '=', 'click')]"
/>
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]" />
<filter
name="unsub"
string="Unsubscribe"
domain="[('event_type', '=', 'unsub')]"
/>
<filter
name="bounce"
string="Bounce"
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"
/>
<filter
name="exception"
string="Failed"
domain="[('event_type', 'in', ('reject', 'spam'))]" domain="[('event_type', 'in', ('reject', 'spam'))]"
context="{'event_error_filter': True}"/> context="{'event_error_filter': True}"
<separator/> />
<separator />
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Type" name="group_by_type" domain="[]" context="{'group_by': 'event_type'}"/> <filter
<filter string="Message" name="group_by_message" domain="[]" context="{'group_by': 'tracking_email_id'}"/> string="Type"
<filter string="OS" name="group_by_os" domain="[('os_family', '!=', False)]" context="{'group_by': 'os_family'}"/> name="group_by_type"
<filter string="User agent" name="group_by_user_agent" domain="[('ua_family', '!=', False)]" context="{'group_by': 'ua_family'}"/> domain="[]"
<filter string="User agent type" name="group_by_user_agent_type" domain="[('ua_type', '!=', False)]" context="{'group_by': 'ua_type'}"/> context="{'group_by': 'event_type'}"
<filter string="Country" name="group_by_country" domain="[('user_country_id', '!=', False)]" context="{'group_by': 'user_country_id'}"/> />
<filter string="Month" name="group_by_date" domain="[]" context="{'group_by': 'date'}"/> <filter
string="Message"
name="group_by_message"
domain="[]"
context="{'group_by': 'tracking_email_id'}"
/>
<filter
string="OS"
name="group_by_os"
domain="[('os_family', '!=', False)]"
context="{'group_by': 'os_family'}"
/>
<filter
string="User agent"
name="group_by_user_agent"
domain="[('ua_family', '!=', False)]"
context="{'group_by': 'ua_family'}"
/>
<filter
string="User agent type"
name="group_by_user_agent_type"
domain="[('ua_type', '!=', False)]"
context="{'group_by': 'ua_type'}"
/>
<filter
string="Country"
name="group_by_country"
domain="[('user_country_id', '!=', False)]"
context="{'group_by': 'user_country_id'}"
/>
<filter
string="Month"
name="group_by_date"
domain="[]"
context="{'group_by': 'date'}"
/>
</group> </group>
</search> </search>
</field> </field>
@ -114,13 +184,16 @@
<field name="name">MailTracking events</field> <field name="name">MailTracking events</field>
<field name="res_model">mail.tracking.event</field> <field name="res_model">mail.tracking.event</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_mail_tracking_event_search"/> <field name="search_view_id" ref="view_mail_tracking_event_search" />
</record> </record>
<!-- Add menu entry in Settings/Email --> <!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking events" id="menu_mail_tracking_event" <menuitem
parent="base.menu_email" name="Tracking events"
action="action_view_mail_tracking_event"/> id="menu_mail_tracking_event"
parent="base.menu_email"
action="action_view_mail_tracking_event"
/>
</odoo> </odoo>