[IMP] mail,* : uniformized API for chatter links

* Enforce html escaping of record title
* Avoid translating html content as much as possible, to reduce translation errors.
* Uniformize/Factorize link generation, easing future tasks, code maintenance, ...

Enterprise PR: https://github.com/odoo/enterprise/pull/25357

closes odoo/odoo#84866

Related: odoo/enterprise#25357
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
This commit is contained in:
Victor Feyens
2022-03-31 12:32:47 +02:00
parent d34f7d38d8
commit 00ed6aa042
20 changed files with 84 additions and 48 deletions
+6 -3
View File
@@ -1994,8 +1994,8 @@ class AccountMove(models.Model):
)._get_default_journal().id
copied_am = super().copy(default)
copied_am._message_log(body=_(
'This entry has been duplicated from <a href=# data-oe-model=account.move data-oe-id=%(id)d>%(title)s</a>',
id=self.id, title=html_escape(self.display_name)
'This entry has been duplicated from %s',
self._get_html_link()
))
if copied_am.is_invoice(include_receipts=True):
@@ -4413,7 +4413,10 @@ class AccountMoveLine(models.Model):
for line in self.filtered(lambda l: l.move_id.id == move_id):
tracking_value_ids = line._mail_track(ref_fields, modified_lines)[1]
if tracking_value_ids:
msg = f"{html_escape(_('Journal Item'))} <a href=# data-oe-model=account.move.line data-oe-id={line.id}>#{line.id}</a> {html_escape(_('updated'))}"
msg = _(
"Journal Item %s updated",
line._get_html_link(title=f"#{line.id}")
)
line.move_id._message_log(
body=msg,
tracking_value_ids=tracking_value_ids
+8 -2
View File
@@ -895,9 +895,15 @@ class AccountPayment(models.Model):
paired_payment.move_id._post(soft=False)
payment.paired_internal_transfer_payment_id = paired_payment
body = _('This payment has been created from <a href=# data-oe-model=account.payment data-oe-id=%d>%s</a>') % (payment.id, payment.name)
body = _(
"This payment has been created from %s",
payment._get_html_link(),
)
paired_payment.message_post(body=body)
body = _('A second payment has been created: <a href=# data-oe-model=account.payment data-oe-id=%d>%s</a>') % (paired_payment.id, paired_payment.name)
body = _(
"A second payment has been created: %s",
paired_payment._get_html_link(),
)
payment.message_post(body=body)
lines = (payment.move_id.line_ids + paired_payment.move_id.line_ids).filtered(
@@ -434,8 +434,7 @@ class AutomaticEntryWizard(models.TransientModel):
return content and '<ul>' + content + '</ul>' or None
def _format_move_link(self, move):
move_link_format = "<a href=# data-oe-model=account.move data-oe-id={move_id}>{move_name}</a>"
return move_link_format.format(move_id=move.id, move_name=move.name)
return move._get_html_link()
def _format_strings(self, string, move, amount):
return string.format(
+6 -6
View File
@@ -214,12 +214,12 @@ class AccruedExpenseRevenue(models.TransientModel):
}])
reverse_move._post()
for order in orders_with_entries:
body = _('Accrual entry created on %s: <a href=# data-oe-model=account.move data-oe-id=%d>%s</a>.\
And its <a href=# data-oe-model=account.move data-oe-id=%d>reverse entry</a>.') % (
self.date,
move.id,
move.name,
reverse_move.id,
body = _(
'Accrual entry created on %(date)s: %(accrual_entry)s.\
And its reverse entry: %(reverse_entry)s.',
date=self.date,
accrual_entry=move._get_html_link(),
reverse_entry=reverse_move._get_html_link(),
)
order.message_post(body=body)
return {
@@ -68,8 +68,9 @@ class AccountDebitNote(models.TransientModel):
default_values = self._prepare_default_values(move)
new_move = move.copy(default=default_values)
move_msg = _(
"This debit note was created from:") + " <a href=# data-oe-model=account.move data-oe-id=%d>%s</a>" % (
move.id, move.name)
"This debit note was created from: %s",
move._get_html_link(),
)
new_move.message_post(body=move_msg)
new_moves |= new_move
+3 -3
View File
@@ -18,9 +18,9 @@ class AccountMove(models.Model):
posted = super()._post(soft) # We need the move name to be set, but we also need to know which move are posted for the first time.
for line in (not_posted_before & posted).line_ids.filtered(lambda ml: ml.vehicle_id and ml.move_id.move_type == 'in_invoice'):
val = line._prepare_fleet_log_service()
log = _('Service Vendor Bill: <a href=# data-oe-model=account.move data-oe-id={move_id}>{move_name}</a>').format(
move_id=line.move_id.id,
move_name=line.move_id.name,
log = _(
'Service Vendor Bill: %s',
line.move_id._get_html_link(),
)
val_list.append(val)
log_list.append(log)
+5 -2
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _
from odoo.tools import html2plaintext, html_escape
from odoo.tools import html2plaintext
class MailChannel(models.Model):
@@ -15,7 +15,10 @@ class MailChannel(models.Model):
msg = _('Create a new lead (/lead lead title)')
else:
lead = self._convert_visitor_to_lead(partner, key)
msg = _('Created a new lead: <a href="#" data-oe-id="%s" data-oe-model="crm.lead">%s</a>') % (lead.id, html_escape(lead.name))
msg = _(
'Created a new lead: %s',
lead._get_html_link(),
)
self._send_transient_message(partner, msg)
def _convert_visitor_to_lead(self, partner, key):
@@ -20,8 +20,8 @@ class SurveyInvite(models.TransientModel):
})
partner = self.applicant_id.partner_id
survey_link = '<a href="#" data-oe-model="%s" data-oe-id="%s">%s</a>' % (survey._name, survey.id, survey.title)
partner_link = '<a href="#" data-oe-model="%s" data-oe-id="%s">%s</a>' % (partner._name, partner.id, partner.name)
survey_link = survey._get_html_link(title=survey.title)
partner_link = partner._get_html_link()
content = _('The survey %(survey_link)s has been sent to %(partner_link)s', survey_link=survey_link, partner_link=partner_link)
body = '<p>%s</p>' % content
self.applicant_id.message_post(body=body)
+3 -4
View File
@@ -371,9 +371,8 @@ class Channel(models.Model):
notification = _('<div class="o_mail_notification">joined the channel</div>')
else:
notification = _(
'<div class="o_mail_notification">invited <a href="#" data-oe-model="res.partner" data-oe-id="%(new_partner_id)d">%(new_partner_name)s</a> to the channel</div>',
new_partner_id=channel_partner.partner_id.id,
new_partner_name=channel_partner.partner_id.name,
'<div class="o_mail_notification">invited %s to the channel</div>',
channel_partner.partner_id._get_html_link(),
)
channel_partner.channel_id.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
members_data.append({
@@ -1241,7 +1240,7 @@ class Channel(models.Model):
def execute_command_who(self, **kwargs):
partner = self.env.user.partner_id
members = [
f'<a href="#" data-oe-id={str(p.id)} data-oe-model="res.partner">@{html_escape(p.name)}</a>'
p._get_html_link(title=f"@{p.name}")
for p in self.channel_partner_ids[:30] if p != partner
]
if len(members) == 0:
+18
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml.builder import E
from markupsafe import Markup
from odoo import api, models, tools, _
@@ -201,3 +202,20 @@ class BaseModel(models.AbstractModel):
return {
'X-Odoo-Objects': "%s-%s" % (self._name, self.id),
}
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def _get_html_link(self, title=None):
"""Generate the record html reference for chatter use.
:param str title: optional reference title, the record display_name
is used if not provided. The title/display_name will be escaped.
:returns: generated html reference,
in the format <a href data-oe-model="..." data-oe-id="...">title</a>
:rtype: str
"""
self.ensure_one()
return Markup("<a href=# data-oe-model='%s' data-oe-id='%s'>%s</a>") % (
self._name, self.id, title or self.display_name)
+4 -2
View File
@@ -381,8 +381,10 @@ class MaintenanceRequest(models.Model):
new_user_id=request.user_id.id or request.owner_user_id.id or self.env.uid)
if not updated:
if request.equipment_id:
note = _('Request planned for <a href="#" data-oe-model="%s" data-oe-id="%s">%s</a>') % (
request.equipment_id._name, request.equipment_id.id, request.equipment_id.display_name)
note = _(
'Request planned for %s',
request.equipment_id._get_html_link()
)
else:
note = False
request.activity_schedule(
+5 -1
View File
@@ -195,7 +195,11 @@ class MrpUnbuild(models.Model):
consume_moves.mapped('move_line_ids').write({'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]})
if self.mo_id:
unbuild_msg = _(
"%s %s unbuilt in", self.product_qty, self.product_uom_id.name) + " <a href=# data-oe-model=mrp.unbuild data-oe-id=%d>%s</a>" % (self.id, self.display_name)
"%(qty)s %(measure)s unbuilt in %(order)s",
qty=self.product_qty,
measure=self.product_uom_id.name,
order=self._get_html_link(),
)
self.mo_id.message_post(
body=unbuild_msg,
subtype_id=self.env.ref('mail.mt_note').id)
-4
View File
@@ -172,10 +172,6 @@ class AccountPayment(models.Model):
action['domain'] = [('source_payment_id', '=', self.id)]
return action
def _get_payment_chatter_link(self):
self.ensure_one()
return f'<a href=# data-oe-model=account.payment data-oe-id={self.id}>{self.name}</a>'
def _create_payment_transaction(self, **extra_create_values):
for payment in self:
if payment.payment_transaction_id:
+1 -1
View File
@@ -1055,7 +1055,7 @@ class PaymentTransaction(models.Model):
if self.payment_id:
message += "<br />" + _(
"The related payment is posted: %s",
self.payment_id._get_payment_chatter_link()
self.payment_id._get_html_link(),
)
elif self.state == 'error':
message = _(
+4 -1
View File
@@ -493,7 +493,10 @@ class PosOrder(models.Model):
def _create_invoice(self, move_vals):
self.ensure_one()
new_move = self.env['account.move'].sudo().with_company(self.company_id).with_context(default_move_type=move_vals['move_type']).create(move_vals)
message = _("This invoice has been created from the point of sale session: <a href=# data-oe-model=pos.order data-oe-id=%d>%s</a>") % (self.id, self.name)
message = _(
"This invoice has been created from the point of sale session: %s",
self._get_html_link(),
)
new_move.message_post(body=message)
if self.config_id.cash_rounding:
rounding_applied = float_round(self.amount_paid - self.amount_total,
+4 -4
View File
@@ -103,10 +103,10 @@ class AccountMove(models.Model):
for move in moves:
if move.reversed_entry_id:
continue
purchase = move.line_ids.mapped('purchase_line_id.order_id')
if not purchase:
purchases = move.line_ids.purchase_line_id.order_id
if not purchases:
continue
refs = ["<a href=# data-oe-model=purchase.order data-oe-id=%s>%s</a>" % tuple(name_get) for name_get in purchase.name_get()]
refs = [purchase._get_html_link() for purchase in purchases]
message = _("This vendor bill has been created from: %s") % ','.join(refs)
move.message_post(body=message)
return moves
@@ -121,7 +121,7 @@ class AccountMove(models.Model):
continue
diff_purchases = new_purchases - old_purchases[i]
if diff_purchases:
refs = ["<a href=# data-oe-model=purchase.order data-oe-id=%s>%s</a>" % tuple(name_get) for name_get in diff_purchases.name_get()]
refs = [purchase._get_html_link() for purchase in diff_purchases]
message = _("This vendor bill has been modified from: %s") % ','.join(refs)
move.message_post(body=message)
return res
+6 -4
View File
@@ -807,7 +807,10 @@ class SaleOrder(models.Model):
lines_to_recompute.discount = 0.0
lines_to_recompute._compute_discount()
self.show_update_pricelist = False
self.message_post(body=_("Product prices have been recomputed according to pricelist <b>%s<b> ", self.pricelist_id.display_name))
self.message_post(body=_(
"Product prices have been recomputed according to pricelist %s.",
self.pricelist_id._get_html_link(),
))
# INVOICING #
@@ -1225,9 +1228,8 @@ class SaleOrder(models.Model):
if self:
self.activity_unlink(['sale.mail_act_sale_upsell'])
for order in self:
ref = "<a href='#' data-oe-model='%s' data-oe-id='%d'>%s</a>"
order_ref = ref % (order._name, order.id, order.name)
customer_ref = ref % (order.partner_id._name, order.partner_id.id, order.partner_id.display_name)
order_ref = order._get_html_link()
customer_ref = order.partner_id._get_html_link()
order.activity_schedule(
'sale.mail_act_sale_upsell',
user_id=order.user_id.id or order.partner_id.user_id.id,
@@ -84,7 +84,7 @@ class SaleOrderLine(models.Model):
line.sudo()._timesheet_service_generation()
# if the SO line created a task, post a message on the order
if line.task_id:
msg_body = _("Task Created (%s): <a href=# data-oe-model=project.task data-oe-id=%d>%s</a>") % (line.product_id.name, line.task_id.id, line.task_id.name)
msg_body = _("Task Created (%s): %s", line.product_id.name, line.task_id._get_html_link())
line.order_id.message_post(body=msg_body)
return lines
@@ -192,7 +192,7 @@ class SaleOrderLine(models.Model):
task = self.env['project.task'].sudo().create(values)
self.write({'task_id': task.id})
# post message on task
task_msg = _("This task has been created from: <a href=# data-oe-model=sale.order data-oe-id=%d>%s</a> (%s)") % (self.order_id.id, self.order_id.name, self.product_id.name)
task_msg = _("This task has been created from: %s (%s)", self.order_id._get_html_link(), self.product_id.name)
task.message_post(body=task_msg)
return task
+1 -1
View File
@@ -629,7 +629,7 @@ class StockMove(models.Model):
if not documents or not doc_orig:
return
msg = _("The deadline has been automatically updated due to a delay on <a href='#' data-oe-model='%s' data-oe-id='%s'>%s</a>.") % (doc_orig[0]._name, doc_orig[0].id, doc_orig[0].name)
msg = _("The deadline has been automatically updated due to a delay on %s.", doc_orig[0]._get_html_link())
msg_subject = _("Deadline updated due to delay on %s", doc_orig[0].name)
# write the message on each document
for doc in documents:
+2 -2
View File
@@ -1166,8 +1166,8 @@ class Picking(models.Model):
'backorder_id': picking.id
})
picking.message_post(
body=_('The backorder <a href=# data-oe-model=stock.picking data-oe-id=%d>%s</a> has been created.') % (
backorder_picking.id, backorder_picking.name))
body=_('The backorder %s has been created.', backorder_picking._get_html_link())
)
moves_to_backorder.write({'picking_id': backorder_picking.id})
moves_to_backorder.move_line_ids.package_level_id.write({'picking_id':backorder_picking.id})
moves_to_backorder.mapped('move_line_ids').write({'picking_id': backorder_picking.id})