Merge PR #1197 into 15.0

Signed-off-by pedrobaeza
pull/1199/head
OCA-git-bot 2023-08-10 06:17:24 +00:00
commit 164298a511
12 changed files with 63 additions and 467 deletions

View File

@ -134,6 +134,8 @@ class IrMailServer(models.Model):
smtp_user=None,
smtp_password=None,
smtp_encryption=None,
smtp_ssl_certificate=None,
smtp_ssl_private_key=None,
smtp_debug=False,
smtp_session=None,
):
@ -149,6 +151,8 @@ class IrMailServer(models.Model):
smtp_user=smtp_user,
smtp_password=smtp_password,
smtp_encryption=smtp_encryption,
smtp_ssl_certificate=smtp_ssl_certificate,
smtp_ssl_private_key=smtp_ssl_private_key,
smtp_debug=smtp_debug,
smtp_session=smtp_session,
)

View File

@ -99,46 +99,35 @@ class MailThread(models.AbstractModel):
res = super()._fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if view_type not in {"search", "form"}:
if view_type != "search":
return res
doc = etree.XML(res["arch"])
if view_type == "search":
# Modify view to add new filter element
nodes = doc.xpath("//search")
if nodes:
# Create filter element
new_filter = etree.Element(
"filter",
{
"string": _("Failed sent messages"),
"name": "failed_message_ids",
"domain": str(
# Modify view to add new filter element
nodes = doc.xpath("//search")
if nodes:
# Create filter element
new_filter = etree.Element(
"filter",
{
"string": _("Failed sent messages"),
"name": "failed_message_ids",
"domain": str(
[
[
[
"failed_message_ids.mail_tracking_ids.state",
"in",
list(self.env["mail.message"].get_failed_states()),
],
[
"failed_message_ids.mail_tracking_needs_action",
"=",
True,
],
]
),
},
)
nodes[0].append(etree.Element("separator"))
nodes[0].append(new_filter)
elif view_type == "form":
# Modify view to add new field element
nodes = doc.xpath("//field[@name='message_ids' and @widget='mail_thread']")
if nodes:
# Create field
field_failed_messages = etree.Element(
"field",
{"name": "failed_message_ids", "widget": "mail_failed_message"},
)
nodes[0].addprevious(field_failed_messages)
"failed_message_ids.mail_tracking_ids.state",
"in",
list(self.env["mail.message"].get_failed_states()),
],
[
"failed_message_ids.mail_tracking_needs_action",
"=",
True,
],
]
),
},
)
nodes[0].append(etree.Element("separator"))
nodes[0].append(new_filter)
res["arch"] = etree.tostring(doc, encoding="unicode")
return res

View File

@ -1 +1,4 @@
* Add pivot for tracking events and mail trackings
* Integrate with the core `mail.notification` model. A soft way would be to write a
notification event along with the mail.tracking.event ones. Another way could be
to merge both models and build the module features on top of it. This might imply
a refactor though.

View File

@ -7,7 +7,7 @@ import {attr} from "@mail/model/model_field";
registerInstancePatchModel(
"mail.chatter",
"mail/static/src/models/chatter/chatter.js",
"mail/static/src/models/chatter/chatter.esm.js",
{
async refresh() {
this._super(...arguments);
@ -24,8 +24,12 @@ registerInstancePatchModel(
},
}
);
registerFieldPatchModel("mail.chatter", "mail/static/src/models/chatter/chatter.js", {
isMessageFailedBoxVisible: attr({
default: true,
}),
});
registerFieldPatchModel(
"mail.chatter",
"mail/static/src/models/chatter/chatter.esm.js",
{
isMessageFailedBoxVisible: attr({
default: true,
}),
}
);

View File

@ -1,411 +0,0 @@
/* Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define("mail_tracking.FailedMessageDiscuss", function (require) {
"use strict";
// To be considered:
// - One message can be displayed in many threads
// - A thread can be a mailbox, channel, ...
// - A mailbox is a type of thread that is displayed on top of
// the discuss menu, has a counter, etc...
var MailManagerNotif = require("mail.Manager.Notification");
var AbstractMessage = require("mail.model.AbstractMessage");
var Message = require("mail.model.Message");
var Discuss = require("mail.Discuss");
var MailManager = require("mail.Manager");
var Mailbox = require("mail.model.Mailbox");
var Dialog = require("web.Dialog");
var core = require("web.core");
var QWeb = core.qweb;
var _t = core._t;
AbstractMessage.include({
/**
* Abstract declaration to know if a message is included in the
* failed mailbox. By default it should be false.
*
* @returns {Boolean}
*/
isFailed: function () {
return false;
},
});
Message.include({
/**
* Overrides to store information from server
*
* @override
*/
init: function (parent, data) {
this._isFailedMessage = data.is_failed_message;
return this._super.apply(this, arguments);
},
/**
* Implementation to know if a message is included in the
* failed mailbox.
*
* @override
*/
isFailed: function () {
return _.contains(this._threadIDs, "mailbox_failed");
},
/**
* Adds/Removes message to/from failed mailbox
*
* @param {Boolean} failed
*/
setFailed: function (failed) {
if (failed) {
this._addThread("mailbox_failed");
} else {
this.removeThread("mailbox_failed");
}
},
/**
* Include the message in the 'failed' mailbox if needed
*
* @override
*/
_processMailboxes: function () {
this.setFailed(this._isFailedMessage);
return this._super.apply(this, arguments);
},
});
MailManagerNotif.include({
/**
* Overrides to handle changes in the 'mail_tracking_needs_action' flag
*
* @override
*/
_handlePartnerNotification: function (data) {
if (data.type === "toggle_tracking_status") {
this._handleChangeTrackingNeedsActionNotification(data);
} else {
// Workaround to avoid call '_handlePartnerChannelNotification'
// because this is related with the failed mailbox, not a
// channel.
this._super.apply(this, arguments);
}
},
/**
* This method updates messages in the failed mailbox when the flag
* 'mail_tracking_needs_action' was changed to False. This can
* remove/add the message from/to failed mailbox and update mailbox
* counter.
*
* @private
* @param {Object} data
*/
_handleChangeTrackingNeedsActionNotification: function (data) {
var self = this;
var failed = this.getMailbox("failed");
_.each(data.message_ids, function (messageID) {
var message = _.find(self._messages, function (msg) {
return msg.getID() === messageID;
});
if (message) {
message.setFailed(data.needs_actions);
if (message.isFailed() === false) {
self._removeMessageFromThread("mailbox_failed", message);
} else {
self._addMessageToThreads(message, []);
var channelFailed = self.getMailbox("failed");
channelFailed.invalidateCaches();
}
self._mailBus.trigger("update_message", message, data.type);
}
});
if (data.needs_actions) {
// Increase failed counter if message is marked as failed
failed.incrementMailboxCounter(data.message_ids.length);
} else {
// Decrease failed counter if message is removed from failed
failed.decrementMailboxCounter(data.message_ids.length);
}
// Trigger event to refresh threads
this._mailBus.trigger("update_failed", failed.getMailboxCounter());
},
});
Discuss.include({
events: _.extend({}, Discuss.prototype.events, {
"click .o_failed_message_retry": "_onRetryFailedMessage",
"click .o_failed_message_reviewed": "_onMarkFailedMessageReviewed",
}),
/**
* Paramaters used to render 'failed' mailbox entry in Discuss
*
* @private
* @returns {Object}
*/
_sidebarQWebParams: function () {
var failed = this.call("mail_service", "getMailbox", "failed");
return {
activeThreadID: this._thread ? this._thread.getID() : undefined,
failedCounter: failed.getMailboxCounter(),
};
},
/**
* Render 'failed' mailbox menu entry in Discuss
* - Initial render
*
* @override
*/
_renderSidebar: function () {
var $sidebar = this._super.apply(this, arguments);
// Because Odoo implementation isn't designed to be inherited
// properly, we inject 'failed' button using jQuery.
var $failed_item = $(
QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
);
$failed_item.insertAfter(
$sidebar.find(".o_mail_discuss_title_main").filter(":last")
);
return $sidebar;
},
/**
* Render 'failed' mailbox menu entry in Discuss
* - Update status render (not called if the mailbox is empty)
*
* @override
*/
_renderSidebarMailboxes: function () {
this._super.apply(this, arguments);
this.$(".o_mail_discuss_sidebar_mailboxes").append(
QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
);
},
/**
* Overrides to listen click on 'Set all as reviewed' button
*
* @override
*/
_renderButtons: function () {
this._super.apply(this, arguments);
this.$btn_set_all_reviewed = this.$buttons.find(
".o_mail_discuss_button_set_all_reviewed"
);
this.$btn_set_all_reviewed.on(
"click",
$.proxy(this, "_onSetAllAsReviewedClicked")
);
},
/**
* Show or hide 'set all as reviewed' button in discuss mailbox
*
* This means in which thread the button should be displayed.
*
* @override
*/
_updateControlPanelButtons: function (thread) {
this.$btn_set_all_reviewed
.toggleClass("d-none", thread.getID() !== "mailbox_failed")
.toggleClass("d-md-inline-block", thread.getID() === "mailbox_failed");
return this._super.apply(this, arguments);
},
/**
* Overrides to update 'set all as reviewed' button.
*
* Disabled button if doesn't have more failed messages
*
* @override
*/
_updateButtonStatus: function (disabled, type) {
if (this._thread.getID() === "mailbox_failed") {
this.$btn_set_all_reviewed.toggleClass("disabled", disabled);
// Display Rainbowman when all failed messages are reviewed
// through 'TOGGLE TRACKING STATUS' or marking last failed
// message as reviewed
if (disabled && type === "toggle_tracking_status") {
this.trigger_up("show_effect", {
message: _t("Congratulations, your failed mailbox is empty"),
type: "rainbow_man",
});
}
}
},
/**
* Overrides to update messages in 'failed' mailbox thread
*
* @override
*/
_onMessageUpdated: function (message, type) {
var self = this;
var currentThreadID = this._thread.getID();
if (currentThreadID === "mailbox_failed" && !message.isFailed()) {
this._thread.fetchMessages(this.domain).then(function () {
var options = self._getThreadRenderingOptions();
self._threadWidget
.removeMessageAndRender(message.getID(), self._thread, options)
.then(function () {
self._updateButtonStatus(!self._thread.hasMessages(), type);
});
});
} else {
// Workaround to avoid calling '_fetchAndRenderThread' and
// refetching thread messages because these messages are
// actually fetched above.
this._super.apply(this, arguments);
}
},
/**
* Hide reply feature in the 'failed' mailbox, where it has no sense.
* Show instead 'Retry' and 'Set as reviewed' buttons.
*
* @override
*/
_getThreadRenderingOptions: function () {
var values = this._super.apply(this, arguments);
if (this._thread.getID() === "mailbox_failed") {
values.displayEmailIcons = true;
values.displayReplyIcons = false;
values.displayRetryButton = true;
values.displayReviewedButton = true;
}
return values;
},
/**
* Listen also to the event that refreshes thread messages
*
* @override
*/
_startListening: function () {
this._super.apply(this, arguments);
this.call("mail_service", "getMailBus").on(
"update_failed",
this,
this._throttledUpdateThreads
);
},
// Handlers
/**
* Open the resend mail.resend.message wizard
*
* @private
* @param {Event} event
*/
_onRetryFailedMessage: function (event) {
event.preventDefault();
var messageID = $(event.currentTarget).data("message-id");
this.do_action("mail.mail_resend_message_action", {
additional_context: {
mail_message_to_resend: messageID,
},
});
},
/**
* Toggle 'mail_tracking_needs_action' flag
*
* @private
* @param {Event} event
* @returns {Promise}
*/
_onMarkFailedMessageReviewed: function (event) {
event.preventDefault();
var messageID = $(event.currentTarget).data("message-id");
return this._rpc({
model: "mail.message",
method: "set_need_action_done",
args: [[messageID]],
context: this.getSession().user_context,
});
},
/**
* Inheritable method that call thread implementation
*
* @private
*/
_onSetAllAsReviewedClicked: function () {
var self = this;
var failed = this.call("mail_service", "getMailbox", "failed");
var failed_counter = failed.getMailboxCounter();
if (failed_counter > 0) {
var promptText = _.str.sprintf(
_t(
"Do you really want to mark as reviewed all the" +
" failed messages (%d)?"
),
failed_counter
);
Dialog.confirm(this, promptText, {
confirm_callback: function () {
self._thread.setAllMessagesAsReviewed();
},
});
}
},
});
MailManager.include({
/**
* Add the 'failed' mailbox
*
* @override
*/
_updateMailboxesFromServer: function (data) {
this._super.apply(this, arguments);
this._addMailbox({
id: "failed",
name: _t("Failed"),
mailboxCounter: data.failed_counter || 0,
});
},
});
Mailbox.include({
/**
* Overrides to add domain for 'failed' mailbox thread
*
* @override
*/
_getThreadDomain: function () {
if (this._id === "mailbox_failed") {
return [["is_failed_message", "=", true]];
}
// Workaround to avoid throw 'Missing domain' exception. Call _super
// without a valid (hard-coded) thread id causes that exeception.
return this._super.apply(this, arguments);
},
/**
* Sets all messages from the mailbox as reviewed.
*
* At the moment, this method makes only sense for 'Failed'.
*
* @returns {$.Promise} resolved when all messages have been marked as
* reviewed on the server
*/
setAllMessagesAsReviewed: function () {
if (this._id === "mailbox_failed" && this.getMailboxCounter() > 0) {
return this._rpc({
model: "mail.message",
method: "set_all_as_reviewed",
});
}
return $.when();
},
});
});

View File

@ -40,6 +40,13 @@ export class MessageFailedBox extends Component {
this.chatter.thread.refreshMessagefailed();
this.chatter.thread.refresh();
}
/**
* @returns {Chatter}
*/
get chatter() {
return this.messaging.models["mail.chatter"].get(this.props.chatterLocalId);
}
}
Object.assign(MessageFailedBox, {

View File

@ -8,7 +8,7 @@ import {one2many} from "@mail/model/model_field";
registerInstancePatchModel(
"mail.thread",
"mail_tracking/static/src/js/failed_message/thread.js",
"mail_tracking/static/src/js/failed_message/thread.esm.js",
{
async refreshMessagefailed() {
var id = this.__values.id;
@ -49,7 +49,7 @@ registerInstancePatchModel(
registerFieldPatchModel(
"mail.thread",
"mail_tracking/static/src/js/failed_message/thread.js",
"mail_tracking/static/src/js/failed_message/thread.esm.js",
{
messagefailed: one2many("mail.message.failed", {
inverse: "thread",

View File

@ -9,7 +9,7 @@ import {attr} from "@mail/model/model_field";
registerClassPatchModel(
"mail.message",
"mail_tracking/static/src/js/mail_tracking.js",
"mail_tracking/static/src/js/mail_tracking.esm.js",
{
convertData(data) {
const data2 = this._super(data);
@ -23,7 +23,7 @@ registerClassPatchModel(
registerFieldPatchModel(
"mail.message",
"mail_tracking/static/src/js/mail_tracking.js",
"mail_tracking/static/src/js/mail_tracking.esm.js",
{
partner_trackings: attr(),
}
@ -31,7 +31,7 @@ registerFieldPatchModel(
registerInstancePatchModel(
"mail.model",
"mail_tracking/static/src/js/mail_tracking.js",
"mail_tracking/static/src/js/mail_tracking.esm.js",
{
hasPartnerTrackings() {
return _.some(this.__values.partner_trackings);

View File

@ -3,7 +3,7 @@
import {Message} from "@mail/components/message/message";
import {patch} from "web.utils";
patch(Message.prototype, "mail_tracking/static/src/js/message.js", {
patch(Message.prototype, "mail_tracking/static/src/js/message.esm.js", {
constructor() {
this._super(...arguments);
},

View File

@ -43,11 +43,11 @@
<t t-if="messagefailed.author">
<img
class="o_Activity_userAvatar"
t-attf-src="/web/image/res.users/{{ messagefailed.author_id }}/image_128"
t-attf-src="/web/image/res.partner/{{ messagefailed.author_id }}/avatar_128"
t-att-alt="messagefailed.author"
/>
</t>
<div class="o_Activity_iconContainer bg-danger-full">
<div class="o_Activity_iconContainer bg-danger">
<i class="o_Activity_icon fa fa-exclamation" />
</div>
</div>
@ -116,7 +116,7 @@
expr="//div[hasclass('o_Chatter_scrollPanel')]/t[@t-if='chatter.isAttachmentBoxVisible']"
position="before"
>
<t t-if="chatter.thread">
<t>
<MessageFailedBox
class="o_Chatter_activityBox"
chatterLocalId="chatter.localId"

View File

@ -172,7 +172,7 @@
<record id="action_view_mail_tracking_email" model="ir.actions.act_window">
<field name="name">MailTracking emails</field>
<field name="res_model">mail.tracking.email</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,pivot</field>
<field name="search_view_id" ref="view_mail_tracking_email_search" />
</record>

View File

@ -195,7 +195,7 @@
<record id="action_view_mail_tracking_event" model="ir.actions.act_window">
<field name="name">MailTracking events</field>
<field name="res_model">mail.tracking.event</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,pivot</field>
<field name="search_view_id" ref="view_mail_tracking_event_search" />
</record>