[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:
committed by
Alexandre Kühn
parent
cee32aa16e
commit
ec93077b8d
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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="{
|
||||
|
||||
Reference in New Issue
Block a user