[IMP] mail: Edited label on edited message

By this commit, If a user edits the message body or changes
the message attachments, the edited label in the blob,
shows the last edit Info.

Task-2664848

closes odoo/odoo#117896

Related: odoo/enterprise#40208
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Maryam Kia
2023-05-04 14:03:42 +02:00
committed by Alexandre Kühn
parent cee32aa16e
commit ec93077b8d
11 changed files with 66 additions and 24 deletions
+2
View File
@@ -53,6 +53,8 @@ class TestImLivechatMessage(TransactionCase):
},
'body': message.body,
'date': message.date,
'write_date': message.write_date,
'create_date': message.create_date,
'guestAuthor': [('clear',)],
'history_partner_ids': [],
'id': message.id,
+4 -4
View File
@@ -49,19 +49,19 @@ class AttachmentController(http.Controller):
@http.route("/mail/attachment/delete", methods=["POST"], type="json", auth="public")
def mail_attachment_delete(self, attachment_id, access_token=None):
attachment_sudo = request.env["ir.attachment"].browse(int(attachment_id)).sudo().exists()
guest = request.env["mail.guest"]._get_guest_from_request(request)
message_sudo = guest.env["mail.message"].sudo().search([("attachment_ids", "in", attachment_sudo.ids)], limit=1)
if not attachment_sudo:
target = request.env.user.partner_id
request.env["bus.bus"]._sendone(target, "ir.attachment/delete", {"id": attachment_id})
return
if not request.env.user.share:
# Check through standard access rights/rules for internal users.
attachment_sudo.sudo(False)._delete_and_notify()
attachment_sudo.sudo(False)._delete_and_notify(message_sudo)
return
# For non-internal users 2 cases are supported:
# - Either the attachment is linked to a message: verify the request is made by the author of the message (portal user or guest).
# - Either a valid access token is given: also verify the message is pending (because unfortunately in portal a token is also provided to guest for viewing others' attachments).
guest = request.env["mail.guest"]._get_guest_from_request(request)
message_sudo = guest.env["mail.message"].sudo().search([("attachment_ids", "in", attachment_sudo.ids)], limit=1)
if message_sudo:
if not message_sudo.is_current_user_or_guest_author:
raise NotFound()
@@ -74,4 +74,4 @@ class AttachmentController(http.Controller):
raise NotFound()
if attachment_sudo.res_model != "mail.compose.message" or attachment_sudo.res_id != 0:
raise NotFound()
attachment_sudo._delete_and_notify()
attachment_sudo._delete_and_notify(message_sudo)
@@ -703,7 +703,13 @@ class Channel(models.Model):
])
if not message_to_update:
return
message_to_update.write({'pinned_at': fields.datetime.now() if pinned else False})
message_to_update.flush_recordset(['pinned_at'])
# Use SQL because by calling write method, write_date is going to be updated, but we don't want pin/unpin
# a message changes the write_date
self.env.cr.execute("UPDATE mail_message SET pinned_at=%s WHERE id=%s",
(fields.datetime.now() if pinned else None, message_to_update.id))
message_to_update.invalidate_recordset(['pinned_at'])
self.env['bus.bus']._sendone(self, 'mail.record/insert', {
'Message': {
'id': message_id,
+4 -2
View File
@@ -38,9 +38,11 @@ class IrAttachment(models.Model):
with contextlib.suppress(AccessError):
related_record.message_main_attachment_id = self
def _delete_and_notify(self):
def _delete_and_notify(self, message=None):
if message:
message.write({}) # to make sure write_date on the message is updated
self.env['bus.bus']._sendmany((attachment._bus_notification_target(), 'ir.attachment/delete', {
'id': attachment.id,
'id': attachment.id, 'message': {'id': message.id, 'write_date': message.write_date} if message else {}
}) for attachment in self)
self.unlink()
+10 -8
View File
@@ -796,12 +796,11 @@ class Message(models.Model):
@api.model
def unstar_all(self):
""" Unstar messages for the current partner. """
partner_id = self.env.user.partner_id.id
partner = self.env.user.partner_id
starred_messages = self.search([('starred_partner_ids', 'in', partner_id)])
starred_messages.write({'starred_partner_ids': [Command.unlink(partner_id)]})
self.env['bus.bus']._sendone(self.env.user.partner_id, 'mail.message/toggle_star', {
starred_messages = self.search([('starred_partner_ids', 'in', partner.id)])
partner.starred_message_ids -= starred_messages
self.env['bus.bus']._sendone(partner, 'mail.message/toggle_star', {
'message_ids': starred_messages.ids,
'starred': False,
})
@@ -813,12 +812,13 @@ class Message(models.Model):
# a user should always be able to star a message they can read
self.check_access_rule('read')
starred = not self.starred
partner = self.env.user.partner_id
if starred:
self.sudo().write({'starred_partner_ids': [Command.link(self.env.user.partner_id.id)]})
partner.starred_message_ids |= self
else:
self.sudo().write({'starred_partner_ids': [Command.unlink(self.env.user.partner_id.id)]})
partner.starred_message_ids -= self
self.env['bus.bus']._sendone(self.env.user.partner_id, 'mail.message/toggle_star', {
self.env['bus.bus']._sendone(partner, 'mail.message/toggle_star', {
'message_ids': [self.id],
'starred': starred,
})
@@ -913,6 +913,8 @@ class Message(models.Model):
'messageReactionGroups': reaction_groups,
'pinned_at': message_sudo.pinned_at,
'record_name': record_name,
'create_date': message_sudo.create_date,
'write_date': message_sudo.write_date,
})
return vals_list
+1
View File
@@ -4062,6 +4062,7 @@ class MailThread(models.AbstractModel):
'id': message.id,
'body': message.body,
'attachment_ids': message.attachment_ids._attachment_format(),
'write_date': message.write_date,
}
})
+1
View File
@@ -23,6 +23,7 @@ class Partner(models.Model):
# tracked field used for chatter logging purposes
# we need this to be readable inline as tracking messages use inline HTML nodes
contact_address_inline = fields.Char(compute='_compute_contact_address_inline', string='Inlined Complete Address', tracking=True)
starred_message_ids = fields.Many2many('mail.message', 'mail_message_res_partner_starred_rel')
@api.depends('contact_address')
def _compute_contact_address_inline(self):
+23 -5
View File
@@ -8,6 +8,7 @@ import { _t } from "@web/core/l10n/translation";
import { url } from "@web/core/utils/urls";
import { deserializeDateTime } from "@web/core/l10n/dates";
import { createLocalId } from "../utils/misc";
import { omit } from "@web/core/utils/objects";
const { DateTime } = luxon;
@@ -66,6 +67,10 @@ export class Message {
notificationType;
/** @type {string} */
pinned_at;
/** @type {string} */
create_date;
/** @type {string} */
write_date;
/**
* We exclude the milliseconds because datetime string from the server don't
@@ -171,11 +176,12 @@ export class Message {
return `${url("/web")}#model=${this.resModel}&id=${this.id}`;
}
get isBodyEmpty() {
return (
!this.body ||
["", "<p></p>", "<p><br></p>", "<p><br/></p>"].includes(this.body.replace(/\s/g, ""))
);
get editDate() {
return this.write_date !== this.create_date ? this.write_date : false;
}
get hasTextContent() {
return (this.editDate && this.attachments.length) || !this.isBodyEmpty;
}
get isEmpty() {
@@ -186,6 +192,12 @@ export class Message {
!this.subtypeDescription
);
}
get isBodyEmpty() {
return (
!this.body ||
["", "<p></p>", "<p><br></p>", "<p><br/></p>"].includes(this.body.replace(/\s/g, ""))
);
}
/**
* Determines if the link preview is actually the main content of the
@@ -231,4 +243,10 @@ export class Message {
}
return luxon.DateTime.fromISO(new Date(this.pinned_at).toISOString());
}
get editDatetimeHuge() {
return deserializeDateTime(this.editDate).toLocaleString(
omit(DateTime.DATETIME_HUGE, "timeZoneName")
);
}
}
@@ -469,7 +469,13 @@ export class Messaging {
break;
case "ir.attachment/delete":
{
const attachment = this.store.attachments[notif.payload.id];
const { id: attachmentId, message: messageData } = notif.payload;
if (messageData) {
this.messageService.insert({
...messageData,
});
}
const attachment = this.store.attachments[attachmentId];
if (!attachment) {
return;
}
@@ -18,6 +18,10 @@
.o-mail-Message-body > p {
margin-bottom: 0;
&:last-of-type {
display: inline-block;
}
}
.o-mail-Message-body {
+3 -3
View File
@@ -92,8 +92,8 @@
'ps-2': env.inChatWindow and isAlignedRight and state.isEditing,
}"
>
<div t-if="!state.isEditing" class="o-mail-Message-content position-relative" t-att-class="{'d-flex': env.inDiscussApp}">
<t t-if="message.type !== 'notification' and !message.isTransient and (!message.isBodyEmpty or message.subtypeDescription)">
<div t-if="!state.isEditing" class="o-mail-Message-content position-relative d-flex">
<t t-if="message.type !== 'notification' and !message.isTransient and (message.hasTextContent or message.subtypeDescription)">
<t t-if="!message.linkPreviewSquash">
<div class="position-relative">
<div class="o-mail-Message-bubble rounded-bottom-3 position-absolute top-0 start-0 w-100 h-100" t-att-class="{
@@ -107,13 +107,13 @@
}" t-ref="body">
<em t-if="message.subject and !message.isSubjectSimilarToOriginThreadName and !message.isSubjectDefault" class="mb-1 me-2">Subject: <t t-esc="message.subject"/></em>
<t t-if="message.body" t-out="message.body"/>
<small t-if="message.editDate" class="ms-2 fst-italic text-muted" t-att-title="message.editDatetimeHuge">(edited)</small>
<t t-if="showSubtypeDescription" t-out="message.subtypeDescription"/>
</div>
</div>
</t>
<LinkPreviewList t-else="" linkPreviews="message.linkPreviews" deletable="false"/>
</t>
</div>
<div t-else="" class="position-relative w-100">
<div class="o-mail-Message-bubble border rounded-bottom-3 position-absolute top-0 start-0 w-100 h-100" t-att-class="{