[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:
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 = _(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user