[IMP] *: enforce usage of Markup in mail
When using message_post, the body format must be explicitly specified. If html is expected, a Markup object should be used. If text is given, the content will be escaped. Before this PR: message_post was unaware if the content of a message was HTML or text. This lead to multiple situation where the content was incorrectly considered as HTML and led to display errors. In self.message_post(body="Hello %s!" % self.name) if the name contained HTML, it would be evaluated. In self.message_post(body="Contact Raoul <raoul@caramail.be>") the email would not be displayed as considered as unknown HTML and discarded by the sanitizer Now each call must explict the type of content. Use the escape() helper to properly combine Markup and translations. It would also be acceptable to use Markup() to wrap a static translation but escape is better as one can not guarantee the content of a translation. closes odoo/odoo#111850 Related: odoo/documentation#3612 Related: odoo/enterprise#36728 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta
|
||||
from hashlib import sha256
|
||||
from json import dumps
|
||||
import logging
|
||||
from markupsafe import Markup, escape
|
||||
from psycopg2 import OperationalError
|
||||
import re
|
||||
from textwrap import shorten
|
||||
@@ -2232,12 +2233,10 @@ class AccountMove(models.Model):
|
||||
default['partner_id'] = False
|
||||
copied_am = super().copy(default)
|
||||
message_origin = '' if not copied_am.auto_post_origin_id else \
|
||||
'<br/>' + _('This recurring entry originated from %s', copied_am.auto_post_origin_id._get_html_link())
|
||||
copied_am._message_log(body=_(
|
||||
'This entry has been duplicated from %s%s',
|
||||
self._get_html_link(),
|
||||
message_origin,
|
||||
))
|
||||
(Markup('<br/>') + _('This recurring entry originated from %s')) % copied_am.auto_post_origin_id._get_html_link()
|
||||
copied_am._message_log(body=
|
||||
(escape(_('This entry has been duplicated from %s')) % self._get_html_link()) + message_origin,
|
||||
)
|
||||
|
||||
return copied_am
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from datetime import date, timedelta
|
||||
from functools import lru_cache
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, fields, models, Command, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
@@ -1448,10 +1449,7 @@ 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 = _(
|
||||
"Journal Item %s updated",
|
||||
line._get_html_link(title=f"#{line.id}")
|
||||
)
|
||||
msg = escape(_("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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import models, fields, api, _, Command
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.misc import format_date, formatLang
|
||||
@@ -896,16 +898,9 @@ 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 %s",
|
||||
payment._get_html_link(),
|
||||
)
|
||||
body = escape(_("This payment has been created from:")) + payment._get_html_link()
|
||||
paired_payment.message_post(body=body)
|
||||
body = _(
|
||||
"A second payment has been created: %s",
|
||||
paired_payment._get_html_link(),
|
||||
)
|
||||
body = escape(_("A second payment has been created:")) + paired_payment._get_html_link()
|
||||
payment.message_post(body=body)
|
||||
|
||||
lines = (payment.move_id.line_ids + paired_payment.move_id.line_ids).filtered(
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import ast
|
||||
from collections import defaultdict
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
from inspect import getmembers
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
@@ -241,7 +242,7 @@ class ResPartnerBank(models.Model):
|
||||
# EXTENDS base res.partner.bank
|
||||
res = super().create(vals_list)
|
||||
for account in res:
|
||||
msg = _("Bank Account %s created", account._get_html_link(title=f"#{account.id}"))
|
||||
msg = escape(_("Bank Account %s created")) % account._get_html_link(title=f"#{account.id}")
|
||||
account.partner_id._message_log(body=msg)
|
||||
return res
|
||||
|
||||
@@ -269,7 +270,7 @@ class ResPartnerBank(models.Model):
|
||||
for account, initial_values in account_initial_values.items():
|
||||
tracking_value_ids = account._mail_track(fields_definition, initial_values)[1]
|
||||
if tracking_value_ids:
|
||||
msg = _("Bank Account %s updated", account._get_html_link(title=f"#{account.id}"))
|
||||
msg = escape(_("Bank Account %s updated")) % account._get_html_link(title=f"#{account.id}")
|
||||
account.partner_id._message_log(body=msg, tracking_value_ids=tracking_value_ids)
|
||||
if 'partner_id' in initial_values: # notify previous partner as well
|
||||
initial_values['partner_id']._message_log(body=msg, tracking_value_ids=tracking_value_ids)
|
||||
@@ -278,7 +279,7 @@ class ResPartnerBank(models.Model):
|
||||
def unlink(self):
|
||||
# EXTENDS base res.partner.bank
|
||||
for account in self:
|
||||
msg = _("Bank Account %s with number %s deleted", account._get_html_link(title=f"#{account.id}"), account.acc_number)
|
||||
msg = escape(_("Bank Account %s with number %s deleted")) % (account._get_html_link(title=f"#{account.id}"), account.acc_number)
|
||||
account.partner_id._message_log(body=msg)
|
||||
return super().unlink()
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.misc import format_date, formatLang
|
||||
from odoo.tools.float_utils import float_repr
|
||||
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
from markupsafe import Markup, escape
|
||||
import json
|
||||
|
||||
class AutomaticEntryWizard(models.TransientModel):
|
||||
@@ -249,7 +251,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
'analytic_distribution': aml.analytic_distribution,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': self._format_strings(_('{percent:0.2f}% recognized on {new_date}'), aml.move_id),
|
||||
'name': self._format_strings(_('{percent}% recognized on {new_date}'), aml.move_id),
|
||||
'debit': reported_credit,
|
||||
'credit': reported_debit,
|
||||
'amount_currency': -reported_amount_currency,
|
||||
@@ -271,7 +273,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
'analytic_distribution': aml.analytic_distribution,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': self._format_strings(_('{percent:0.2f}% to recognize on {new_date}'), aml.move_id),
|
||||
'name': self._format_strings(_('{percent}% to recognize on {new_date}'), aml.move_id),
|
||||
'debit': reported_debit,
|
||||
'credit': reported_credit,
|
||||
'amount_currency': reported_amount_currency,
|
||||
@@ -350,17 +352,30 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
accrual_move_lines = accrual_move.mapped('line_ids').filtered(lambda line: line.account_id == accrual_account)[accrual_move_offsets[accrual_move]:accrual_move_offsets[accrual_move]+2]
|
||||
accrual_move_offsets[accrual_move] += 2
|
||||
(accrual_move_lines + destination_move_lines).filtered(lambda line: not line.currency_id.is_zero(line.balance)).reconcile()
|
||||
move.message_post(body=self._format_strings(_('Adjusting Entries have been created for this invoice:<ul><li>%(link1)s cancelling '
|
||||
'{percent:.2f}%% of {amount}</li><li>%(link0)s postponing it to {new_date}</li></ul>',
|
||||
link0=self._format_move_link(destination_move),
|
||||
link1=self._format_move_link(accrual_move),
|
||||
), move, amount))
|
||||
destination_messages += [self._format_strings(_('Adjusting Entry {link}: {percent:.2f}% of {amount} recognized from {date}'), move, amount)]
|
||||
accrual_move_messages[accrual_move] += [self._format_strings(_('Adjusting Entry for {link}: {percent:.2f}% of {amount} recognized on {new_date}'), move, amount)]
|
||||
body = Markup("%(title)s<ul><li>%(link1)s %(second)s</li><li>%(link2)s %(third)s</li></ul>") % {
|
||||
'title': _("Adjusting Entries have been created for this invoice:"),
|
||||
'link1': self._format_move_link(accrual_move),
|
||||
'second': self._format_strings(_("cancelling {percent}%% of {amount}"), move, amount),
|
||||
'link2': self._format_move_link(destination_move),
|
||||
'third': self._format_strings(_("postponing it to {new_date}"), move, amount),
|
||||
}
|
||||
move.message_post(body=body)
|
||||
destination_messages += [
|
||||
self._format_strings(
|
||||
escape(_("Adjusting Entry {link} {percent}%% of {amount} recognized from {date}")),
|
||||
move, amount,
|
||||
)
|
||||
]
|
||||
accrual_move_messages[accrual_move] += [
|
||||
self._format_strings(
|
||||
escape(_("Adjusting Entry {link} {percent}%% of {amount} recognized on {new_date}")),
|
||||
move, amount,
|
||||
)
|
||||
]
|
||||
|
||||
destination_move.message_post(body='<br/>\n'.join(destination_messages))
|
||||
destination_move.message_post(body=Markup('<br/>\n').join(destination_messages))
|
||||
for accrual_move, messages in accrual_move_messages.items():
|
||||
accrual_move.message_post(body='<br/>\n'.join(messages))
|
||||
accrual_move.message_post(body=Markup('<br/>\n').join(messages))
|
||||
|
||||
# open the generated entries
|
||||
action = {
|
||||
@@ -418,31 +433,41 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
|
||||
# Transfer utils
|
||||
def _format_new_transfer_move_log(self, acc_transfer_per_move):
|
||||
format = _("<li>{amount} ({debit_credit}) from {link}, <strong>%(account_source_name)s</strong></li>")
|
||||
rslt = _("This entry transfers the following amounts to <strong>%(destination)s</strong> <ul>", destination=self.destination_account_id.display_name)
|
||||
for move, balances_per_account in acc_transfer_per_move.items():
|
||||
for account, balance in balances_per_account.items():
|
||||
if account != self.destination_account_id: # Otherwise, logging it here is confusing for the user
|
||||
rslt += self._format_strings(format, move, balance) % {'account_source_name': account.display_name}
|
||||
|
||||
rslt += '</ul>'
|
||||
transfer_format = Markup("<li>%s, <strong>%%(account_source_name)s</strong></li>") % \
|
||||
_("{amount} ({debit_credit}) from {link}")
|
||||
rslt = Markup(_("This entry transfers the following amounts to %(destination)s") + "<ul>%(transfer_logs)s</ul>") % {
|
||||
'destination': Markup("<strong>%s</strong>") % self.destination_account_id.display_name,
|
||||
'transfer_logs': Markup().join([
|
||||
self._format_strings(transfer_format, move, balance) % {'account_source_name': account.display_name}
|
||||
for move, balances_per_account in acc_transfer_per_move.items()
|
||||
for account, balance in balances_per_account.items()
|
||||
if account != self.destination_account_id # Otherwise, logging it here is confusing for the user
|
||||
])
|
||||
}
|
||||
return rslt
|
||||
|
||||
def _format_transfer_source_log(self, balances_per_account, transfer_move):
|
||||
transfer_format = _("<li>{amount} ({debit_credit}) from <strong>%s</strong> were transferred to <strong>{account_target_name}</strong> by {link}</li>")
|
||||
content = ''
|
||||
for account, balance in balances_per_account.items():
|
||||
if account != self.destination_account_id:
|
||||
content += self._format_strings(transfer_format, transfer_move, balance) % account.display_name
|
||||
return content and '<ul>' + content + '</ul>' or None
|
||||
if not balances_per_account:
|
||||
return None
|
||||
|
||||
transfer_format = Markup(
|
||||
_("{amount} ({debit_credit}) from %s were transferred to <strong>{account_target_name}</strong> by {link}")
|
||||
)
|
||||
|
||||
return Markup("<ul>%s</ul>") % Markup().join([
|
||||
Markup("<li>%s</li>") % \
|
||||
self._format_strings(transfer_format, transfer_move, balance) % (Markup("<strong>%s</strong>") % account.display_name)
|
||||
for account, balance in balances_per_account.items()
|
||||
if account != self.destination_account_id
|
||||
])
|
||||
|
||||
def _format_move_link(self, move):
|
||||
return move._get_html_link()
|
||||
|
||||
def _format_strings(self, string, move, amount=None):
|
||||
return string.format(
|
||||
label=move.name or 'Adjusting Entry',
|
||||
percent=self.percentage,
|
||||
label=move.name or _('Adjusting Entry'),
|
||||
percent=float_repr(self.percentage, 2),
|
||||
name=move.name,
|
||||
id=move.id,
|
||||
amount=formatLang(self.env, abs(amount), currency_obj=self.company_id.currency_id) if amount else '',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from markupsafe import escape
|
||||
import json
|
||||
from odoo import models, fields, api, _, Command
|
||||
from odoo.tools import format_date
|
||||
@@ -217,13 +218,13 @@ class AccruedExpenseRevenue(models.TransientModel):
|
||||
}])
|
||||
reverse_move._post()
|
||||
for order in orders_with_entries:
|
||||
body = _(
|
||||
body = escape(_(
|
||||
'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(),
|
||||
)
|
||||
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 {
|
||||
'name': _('Accrual Moves'),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from odoo import models, fields, api
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
class AccountDebitNote(models.TransientModel):
|
||||
"""
|
||||
@@ -71,10 +71,7 @@ class AccountDebitNote(models.TransientModel):
|
||||
for move in self.move_ids.with_context(include_business_fields=True): #copy sale/purchase links
|
||||
default_values = self._prepare_default_values(move)
|
||||
new_move = move.copy(default=default_values)
|
||||
move_msg = _(
|
||||
"This debit note was created from: %s",
|
||||
move._get_html_link(),
|
||||
)
|
||||
move_msg = escape(_("This debit note was created from: %s")) % move._get_html_link()
|
||||
new_move.message_post(body=move_msg)
|
||||
new_moves |= new_move
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from odoo.tools import float_repr
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.float_utils import float_round
|
||||
|
||||
from markupsafe import Markup
|
||||
from zeep import Client
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -285,13 +286,14 @@ class AccountEdiCommon(models.AbstractModel):
|
||||
invoice.move_type = move_type
|
||||
logs = self._import_fill_invoice_form(invoice, tree, qty_factor)
|
||||
if invoice:
|
||||
body = Markup("<strong>%s</strong>") % \
|
||||
_("Format used to import the invoice: %s",
|
||||
self.env['ir.model']._get(self._name).name)
|
||||
|
||||
if logs:
|
||||
body = _(
|
||||
"<strong>Format used to import the invoice: %s</strong> <p><li> %s </li></p>",
|
||||
str(self._description), "</li><li>".join(logs)
|
||||
)
|
||||
else:
|
||||
body = _("<strong>Format used to import the invoice: %s</strong>", str(self._description))
|
||||
body += Markup("<ul>%s</ul>") % \
|
||||
Markup().join(Markup("<li>%s</li>") % l for l in logs)
|
||||
|
||||
invoice.message_post(body=body)
|
||||
|
||||
# For UBL, we should override the computed tax amount if it is less than 0.05 different of the one in the xml.
|
||||
@@ -656,22 +658,24 @@ class AccountEdiCommon(models.AbstractModel):
|
||||
for item in response['Result']:
|
||||
if item['artifactPath']:
|
||||
report.append(
|
||||
"<li><font style='color:Blue;'><strong>" + item['artifactPath'] + "</strong></font></li>")
|
||||
Markup("<li><font style='color:Blue;'><strong>%s</strong></font></li>") % item['artifactPath'])
|
||||
for detail in item['Item']:
|
||||
if detail['errorLevel'] == 'WARN':
|
||||
errors_cnt += 1
|
||||
report.append(
|
||||
"<li><font style='color:Orange;'><strong>" + detail['errorText'] + "</strong></font></li>")
|
||||
Markup("<li><font style='color:Orange;'><strong>%s</strong></font></li>") % detail['errorText'])
|
||||
elif detail['errorLevel'] == 'ERROR':
|
||||
errors_cnt += 1
|
||||
report.append(
|
||||
"<li><font style='color:Tomato;'><strong>" + detail['errorText'] + "</strong></font></li>")
|
||||
Markup("<li><font style='color:Tomato;'><strong>%s</strong></font></li>") % detail['errorText'])
|
||||
|
||||
if errors_cnt == 0:
|
||||
invoice.message_post(body=f"<font style='color:Green;'><strong>ECOSIO: All clear for format {ecosio_format}!</strong></font>")
|
||||
invoice.message_post(body=Markup("<font style='color:Green;'><strong>ECOSIO: All clear for format %s!</strong></font>") % ecosio_format)
|
||||
else:
|
||||
invoice.message_post(
|
||||
body=f"<font style='color:Tomato;'><strong>ECOSIO ERRORS/WARNINGS for format {ecosio_format}</strong></font>: <ul> "
|
||||
+ "\n".join(report) + " </ul>"
|
||||
body=Markup("<font style='color:Tomato;'><strong>ECOSIO ERRORS/WARNINGS for format %s</strong></font>: <ul>%s</<ul>") % (
|
||||
ecosio_format,
|
||||
Markup().join(report)
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import escape
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
@@ -18,10 +19,7 @@ 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: %s',
|
||||
line.move_id._get_html_link(),
|
||||
)
|
||||
log = escape(_('Service Vendor Bill: %s')) % line.move_id._get_html_link()
|
||||
val_list.append(val)
|
||||
log_list.append(log)
|
||||
log_service_ids = self.env['fleet.vehicle.log.services'].create(val_list)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import escape
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
|
||||
|
||||
@@ -214,8 +215,7 @@ class PaymentTransaction(models.Model):
|
||||
"""
|
||||
super()._finalize_post_processing()
|
||||
for tx in self.filtered('payment_id'):
|
||||
message = _(
|
||||
"The payment related to the transaction with reference %(ref)s has been posted: "
|
||||
"%(link)s", ref=tx.reference, link=tx.payment_id._get_html_link()
|
||||
)
|
||||
message = escape(_("The payment related to the transaction with reference %(ref)s has been posted: %(link)s")) % {
|
||||
'ref':tx.reference, 'link':tx.payment_id._get_html_link()
|
||||
}
|
||||
tx._log_message_on_linked_documents(message)
|
||||
|
||||
@@ -7,7 +7,7 @@ import threading
|
||||
from ast import literal_eval
|
||||
from collections import OrderedDict, defaultdict
|
||||
from datetime import date, datetime, timedelta
|
||||
from markupsafe import Markup
|
||||
from markupsafe import Markup, escape
|
||||
from psycopg2 import sql
|
||||
|
||||
from odoo import api, fields, models, tools, SUPERUSER_ID
|
||||
@@ -1306,15 +1306,16 @@ class Lead(models.Model):
|
||||
duration = _('unknown')
|
||||
else:
|
||||
duration = self.env['ir.qweb.field.duration'].value_to_html(meeting.duration, {'unit': 'hour'})
|
||||
meet_date = fields.Datetime.from_string(meeting.start)
|
||||
meeting_usertime = fields.Datetime.to_string(fields.Datetime.context_timestamp(self, meet_date))
|
||||
message = "<p>%s<p>" % Markup(_("Meeting scheduled at %(html_time)s<br/>Subject: %(subject)s<br/>Duration: %(duration)s")) % {
|
||||
'html_time': Markup("<time datetime='%(meeting_start)s+00:00'>%(meeting_user_time)s</time>") % {
|
||||
'meeting_start': meeting.start,
|
||||
'meeting_user_time': meeting_usertime,
|
||||
},
|
||||
'subject': meeting._get_html_link(), # Already Markup valid
|
||||
'duration': duration,
|
||||
meeting_usertime = fields.Datetime.to_string(fields.Datetime.context_timestamp(self, meeting.start))
|
||||
meeting_time = Markup("<time datetime='%(meeting_start)s+00:00'>%(meeting_user_time)s</time>") % {
|
||||
'meeting_start': meeting.start,
|
||||
'meeting_user_time': meeting_usertime,
|
||||
}
|
||||
message = Markup("<p>%(meeting)s<br/>%(subject_string)s %(subject_link)s<br/>%(duration)s<p>") % {
|
||||
'meeting': escape(_("Meeting scheduled at %s")) % meeting_time,
|
||||
'subject_string': _("Subject: "),
|
||||
'subject_link': meeting._get_html_link(),
|
||||
'duration': _("Duration: %s", duration),
|
||||
}
|
||||
return self.message_post(body=message)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import random
|
||||
import threading
|
||||
|
||||
from ast import literal_eval
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.osv import expression
|
||||
@@ -263,13 +264,13 @@ class Team(models.Model):
|
||||
|
||||
# format result messages
|
||||
logs = self._action_assign_leads_logs(teams_data, members_data)
|
||||
html_message = '<br />'.join(logs)
|
||||
html_message = Markup('<br />').join(logs)
|
||||
notif_message = ' '.join(logs)
|
||||
|
||||
# log a note in case of manual assign (as this method will mainly be called
|
||||
# on singleton record set, do not bother doing a specific message per team)
|
||||
log_action = _("Lead Assignment requested by %(user_name)s", user_name=self.env.user.name)
|
||||
log_message = "<p>%s<br /><br />%s</p>" % (log_action, html_message)
|
||||
log_message = Markup("<p>%s<br /><br />%s</p>") % (log_action, html_message)
|
||||
self._message_log_batch(bodies=dict((team.id, log_message) for team in self))
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import fields, models, _
|
||||
from odoo.tools.mail import is_html_empty
|
||||
|
||||
@@ -19,7 +20,7 @@ class CrmLeadLost(models.TransientModel):
|
||||
leads = self.env['crm.lead'].browse(self.env.context.get('active_ids'))
|
||||
if not is_html_empty(self.lost_feedback):
|
||||
leads._track_set_log_message(
|
||||
'<div style="margin-bottom: 4px;"><p>%s:</p>%s<br /></div>' % (
|
||||
Markup('<div style="margin-bottom: 4px;"><p>%s:</p>%s<br /></div>') % (
|
||||
_('Lost Comment'),
|
||||
self.lost_feedback
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import escape
|
||||
from odoo import models, _
|
||||
from odoo.tools import html2plaintext
|
||||
|
||||
@@ -15,10 +16,7 @@ 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: %s',
|
||||
lead._get_html_link(),
|
||||
)
|
||||
msg = escape(_('Created a new lead: %s')) % lead._get_html_link()
|
||||
self._send_transient_message(partner, msg)
|
||||
|
||||
def _convert_visitor_to_lead(self, partner, key):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
@@ -61,9 +62,9 @@ class EventBoothRegistration(models.Model):
|
||||
self._cancel_pending_registrations()
|
||||
|
||||
def _cancel_pending_registrations(self):
|
||||
body = '<p>%(message)s: <ul>%(booth_names)s</ul></p>' % {
|
||||
body = Markup('<p>%(message)s: <ul>%(booth_names)s</ul></p>') % {
|
||||
'message': _('Your order has been cancelled because the following booths have been reserved'),
|
||||
'booth_names': ''.join('<li>%s</li>' % booth.display_name for booth in self.event_booth_id)
|
||||
'booth_names': Markup().join(Markup('<li>%s</li>') % booth.display_name for booth in self.event_booth_id)
|
||||
}
|
||||
other_registrations = self.search([
|
||||
('event_booth_id', 'in', self.event_booth_id.ids),
|
||||
|
||||
@@ -6,6 +6,7 @@ import logging
|
||||
from datetime import date, timedelta
|
||||
|
||||
from dateutil.relativedelta import relativedelta, MO
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, models, fields, _, exceptions
|
||||
from odoo.tools import ustr
|
||||
@@ -694,21 +695,21 @@ class Challenge(models.Model):
|
||||
|
||||
if rewarded_users:
|
||||
user_names = rewarded_users.name_get()
|
||||
message_body += _(
|
||||
"<br/>Reward (badge %(badge_name)s) for every succeeding user was sent to %(users)s.",
|
||||
message_body += Markup("<br/>") + _(
|
||||
"Reward (badge %(badge_name)s) for every succeeding user was sent to %(users)s.",
|
||||
badge_name=challenge.reward_id.name,
|
||||
users=", ".join(name for (user_id, name) in user_names)
|
||||
)
|
||||
else:
|
||||
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewarded for this challenge.")
|
||||
message_body += Markup("<br/>") + _("Nobody has succeeded to reach every goal, no badge is rewarded for this challenge.")
|
||||
|
||||
# reward bests
|
||||
reward_message = _("<br/> %(rank)d. %(user_name)s - %(reward_name)s")
|
||||
reward_message = Markup("<br/> %(rank)d. %(user_name)s - %(reward_name)s")
|
||||
if challenge.reward_first_id:
|
||||
(first_user, second_user, third_user) = challenge._get_topN_users(MAX_VISIBILITY_RANKING)
|
||||
if first_user:
|
||||
challenge._reward_user(first_user, challenge.reward_first_id)
|
||||
message_body += _("<br/>Special rewards were sent to the top competing users. The ranking for this challenge is:")
|
||||
message_body += Markup("<br/>") + _("Special rewards were sent to the top competing users. The ranking for this challenge is:")
|
||||
message_body += reward_message % {
|
||||
'rank': 1,
|
||||
'user_name': first_user.name,
|
||||
|
||||
@@ -7,6 +7,7 @@ from functools import wraps
|
||||
from requests import HTTPError
|
||||
import pytz
|
||||
from dateutil.parser import parse
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, registry, _
|
||||
from odoo.tools import ormcache_context, email_normalize
|
||||
@@ -210,10 +211,9 @@ class GoogleSync(models.AbstractModel):
|
||||
'reason': reason}
|
||||
_logger.error(error_log)
|
||||
|
||||
body = _(
|
||||
"The following event could not be synced with Google Calendar. </br>"
|
||||
"It will not be synced as long at it is not updated.</br>"
|
||||
"%(reason)s", reason=reason)
|
||||
body = _("The following event could not be synced with Google Calendar.") + Markup("<br/>") + \
|
||||
_("It will not be synced as long at it is not updated.") + Markup("<br/>") + \
|
||||
reason
|
||||
|
||||
if event:
|
||||
event.message_post(
|
||||
|
||||
@@ -8,6 +8,7 @@ from random import choice
|
||||
from string import digits
|
||||
from werkzeug.urls import url_encode
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError, AccessError
|
||||
@@ -367,10 +368,9 @@ class HrEmployeePrivate(models.Model):
|
||||
'active_model': 'hr.employee',
|
||||
'menu_id': hr_root_menu.id,
|
||||
})
|
||||
onboarding_notes_bodies[employee.id] = _(
|
||||
onboarding_notes_bodies[employee.id] = Markup(_(
|
||||
'<b>Congratulations!</b> May I recommend you to setup an <a href="%s">onboarding plan?</a>',
|
||||
url,
|
||||
)
|
||||
)) % url
|
||||
employees._message_log_batch(onboarding_notes_bodies)
|
||||
return employees
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
@@ -94,10 +96,10 @@ class HrPlanWizard(models.TransientModel):
|
||||
activities.add(activity)
|
||||
|
||||
if activities:
|
||||
body += '<ul>'
|
||||
body += Markup('<ul>')
|
||||
for activity in activities:
|
||||
body += '<li>%s</li>' % activity
|
||||
body += '</ul>'
|
||||
body += Markup('<li>%s</li>') % activity
|
||||
body += Markup('</ul>')
|
||||
employee.message_post(body=body)
|
||||
|
||||
if len(self.employee_ids) == 1:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools.misc import frozendict
|
||||
@@ -39,7 +40,7 @@ class AccountMove(models.Model):
|
||||
|
||||
def _creation_message(self):
|
||||
if self.expense_sheet_id:
|
||||
return _("Expense entry created from: %s", self.expense_sheet_id._get_html_link())
|
||||
return escape(_("Expense entry created from: %s")) % self.expense_sheet_id._get_html_link()
|
||||
return super()._creation_message()
|
||||
|
||||
@api.depends('expense_sheet_id')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import escape
|
||||
from odoo import models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
@@ -38,5 +39,5 @@ class AccountPayment(models.Model):
|
||||
# EXTENDS mail
|
||||
self.ensure_one()
|
||||
if self.move_id.expense_sheet_id:
|
||||
return _("Payment created for: %s", self.move_id.expense_sheet_id._get_html_link())
|
||||
return escape(_("Payment created for: %s")) % self.move_id.expense_sheet_id._get_html_link()
|
||||
return super()._creation_message()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup, escape
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.tools.misc import clean_context
|
||||
|
||||
@@ -36,7 +38,10 @@ class SurveyInvite(models.TransientModel):
|
||||
partner = self.applicant_id.partner_id
|
||||
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
|
||||
content = escape(_('The survey %(survey_link)s has been sent to %(partner_link)s')) % {
|
||||
'survey_link': survey_link,
|
||||
'partner_link': partner_link,
|
||||
}
|
||||
body = Markup('<p>%s</p>') % content
|
||||
self.applicant_id.message_post(body=body)
|
||||
return super().action_invite()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup, escape
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.tools import html2plaintext
|
||||
|
||||
@@ -48,9 +50,11 @@ class SlideChannelPartner(models.Model):
|
||||
super()._send_completed_mail()
|
||||
for scp in self:
|
||||
if self.env.user.employee_ids:
|
||||
msg = _('The employee has completed the course <a href="%(link)s">%(course)s</a>',
|
||||
link=scp.channel_id.website_url,
|
||||
course=scp.channel_id.name)
|
||||
msg = escape(_('The employee has completed the course %s')) % \
|
||||
Markup('<a href="%(link)s">%(course)s</a>') % {
|
||||
'link': scp.channel_id.website_url,
|
||||
'course': scp.channel_id.name,
|
||||
}
|
||||
self.env.user.employee_id.message_post(body=msg)
|
||||
|
||||
class Channel(models.Model):
|
||||
@@ -60,7 +64,10 @@ class Channel(models.Model):
|
||||
res = super()._action_add_members(target_partners)
|
||||
for channel in self:
|
||||
channel._message_employee_chatter(
|
||||
_('The employee subscribed to the course <a href="%(link)s">%(course)s</a>', link=channel.website_url, course=channel.name),
|
||||
Markup(_('The employee subscribed to the course <a href="%(link)s">%(course)s</a>')) % {
|
||||
'link': channel.website_url,
|
||||
'course': channel.name,
|
||||
},
|
||||
target_partners)
|
||||
return res
|
||||
|
||||
@@ -71,7 +78,10 @@ class Channel(models.Model):
|
||||
|
||||
for channel in self:
|
||||
channel._message_employee_chatter(
|
||||
_('The employee left the course <a href="%(link)s">%(course)s</a>', link=channel.website_url, course=channel.name),
|
||||
Markup(_('The employee left the course <a href="%(link)s">%(course)s</a>')) % {
|
||||
'link': channel.website_url,
|
||||
'course': channel.name,
|
||||
},
|
||||
partners)
|
||||
return res
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests.common import users, tagged, TransactionCase
|
||||
|
||||
@@ -39,7 +41,7 @@ class TestImLivechatMessage(TransactionCase):
|
||||
})
|
||||
message = channel_livechat_1.message_post(
|
||||
author_id=record_rating.partner_id.id,
|
||||
body="<img src='%s' alt=':%s/5' style='width:18px;height:18px;float:left;margin-right: 5px;'/>%s"
|
||||
body=Markup("<img src='%s' alt=':%s/5' style='width:18px;height:18px;float:left;margin-right: 5px;'/>%s")
|
||||
% (record_rating.rating_image_url, record_rating.rating, record_rating.feedback),
|
||||
rating_id=record_rating.id,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup, escape
|
||||
from odoo import models, _
|
||||
|
||||
|
||||
@@ -13,13 +14,14 @@ class MailBot(models.AbstractModel):
|
||||
if odoobot_state == "onboarding_attachement" and values.get("attachment_ids"):
|
||||
self.env.user.odoobot_failed = False
|
||||
self.env.user.odoobot_state = "onboarding_canned"
|
||||
return _("That's me! 🎉<br/>Try typing <span class=\"o_odoobot_command\">:</span> to use canned responses.")
|
||||
return Markup(_("That's me! 🎉<br/>Try typing %s to use canned responses.", "<span class=\"o_odoobot_command\">:</span>"))
|
||||
elif odoobot_state == "onboarding_canned" and values.get("canned_response_ids"):
|
||||
self.env.user.odoobot_failed = False
|
||||
self.env.user.odoobot_state = "idle"
|
||||
return _("Good, you can customize canned responses in the live chat application.<br/><br/><b>It's the end of this overview</b>, enjoy discovering Odoo!")
|
||||
return Markup(_("Good, you can customize canned responses in the live chat application.<br/><br/><b>It's the end of this overview</b>, enjoy discovering Odoo!"))
|
||||
# repeat question if needed
|
||||
elif odoobot_state == 'onboarding_canned' and not self._is_help_requested(body):
|
||||
self.env.user.odoobot_failed = True
|
||||
return _("Not sure what you are doing. Please, type <span class=\"o_odoobot_command\">:</span> and wait for the propositions. Select one of them and press enter.")
|
||||
return escape(_("Not sure what you are doing. Please, type %s and wait for the propositions. Select one of them and press enter.")) % \
|
||||
Markup("<span class=\"o_odoobot_command\">:</span>")
|
||||
return super(MailBot, self)._get_answer(record, body, values, command)
|
||||
|
||||
@@ -7,6 +7,7 @@ import pytz
|
||||
import markupsafe
|
||||
|
||||
from collections import defaultdict
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools import html_escape, float_is_zero, float_compare
|
||||
@@ -125,12 +126,12 @@ class AccountEdiFormat(models.Model):
|
||||
if not response.get("error"):
|
||||
error = []
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoice.message_post(author_id=odoobot.id, body=_(
|
||||
invoice.message_post(author_id=odoobot.id, body=Markup(_(
|
||||
"Somehow this invoice had been submited to government before." \
|
||||
"<br/>Normally, this should not happen too often" \
|
||||
"<br/>Just verify value of invoice by uploade json to government website " \
|
||||
"<a href='https://einvoice1.gst.gov.in/Others/VSignedInvoice'>here<a>."
|
||||
))
|
||||
)))
|
||||
if "no-credit" in error_codes:
|
||||
return {invoice: {
|
||||
"success": False,
|
||||
@@ -180,12 +181,12 @@ class AccountEdiFormat(models.Model):
|
||||
if "9999" in error_codes:
|
||||
response = {}
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoice.message_post(author_id=odoobot.id, body=_(
|
||||
invoice.message_post(author_id=odoobot.id, body=Markup(_(
|
||||
"Somehow this invoice had been cancelled to government before." \
|
||||
"<br/>Normally, this should not happen too often" \
|
||||
"<br/>Just verify by logging into government website " \
|
||||
"<a href='https://einvoice1.gst.gov.in'>here<a>."
|
||||
))
|
||||
)))
|
||||
if "no-credit" in error_codes:
|
||||
return {invoice: {
|
||||
"success": False,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import re
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools import html_escape
|
||||
@@ -150,12 +151,12 @@ class AccountEdiFormat(models.Model):
|
||||
if "312" in error_codes:
|
||||
# E-waybill is already canceled
|
||||
# this happens when timeout from the Government portal but IRN is generated
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message") or self._l10n_in_edi_ewaybill_get_error_message(e.get('code')))) for e in error])
|
||||
error_message = Markup("<br/>").join([Markup("[%s] %s") % (e.get("code"), e.get("message") or self._l10n_in_edi_ewaybill_get_error_message(e.get('code'))) for e in error])
|
||||
error = []
|
||||
response = {"data": ""}
|
||||
odoobot = self.env.ref("base.partner_root")
|
||||
invoices.message_post(author_id=odoobot.id, body=
|
||||
"%s<br/>%s:<br/>%s" %(
|
||||
Markup("%s<br/>%s:<br/>%s") %(
|
||||
_("Somehow this E-waybill has been canceled in the government portal before. You can verify by checking the details into the government (https://ewaybillgst.gov.in/Others/EBPrintnew.asp)"),
|
||||
_("Error"),
|
||||
error_message
|
||||
@@ -168,7 +169,7 @@ class AccountEdiFormat(models.Model):
|
||||
"blocking_level": "error",
|
||||
}
|
||||
elif error:
|
||||
error_message = "<br/>".join(["[%s] %s" % (e.get("code"), html_escape(e.get("message") or self._l10n_in_edi_ewaybill_get_error_message(e.get('code')))) for e in error])
|
||||
error_message = Markup("<br/>").join([Markup("[%s] %s") % (e.get("code"), e.get("message") or self._l10n_in_edi_ewaybill_get_error_message(e.get('code'))) for e in error])
|
||||
blocking_level = "error"
|
||||
if "404" in error_codes:
|
||||
blocking_level = "warning"
|
||||
|
||||
@@ -7,6 +7,7 @@ import logging
|
||||
import re
|
||||
|
||||
from datetime import datetime
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import float_repr, float_compare
|
||||
@@ -352,7 +353,7 @@ class AccountMove(models.Model):
|
||||
})
|
||||
with other_invoice._get_edi_creation() as other_invoice:
|
||||
self.env['account.edi.format']._import_fattura_pa(tree, other_invoice)
|
||||
other_invoice.message_post(body=_("Created from attachment in %s", invoice._get_html_link()))
|
||||
other_invoice.message_post(body=escape(_("Created from attachment in %s")) % invoice._get_html_link())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from markupsafe import Markup
|
||||
from psycopg2 import IntegrityError
|
||||
from psycopg2.errorcodes import UNIQUE_VIOLATION
|
||||
|
||||
@@ -238,6 +239,8 @@ class DiscussController(http.Controller):
|
||||
thread = channel_member_sudo.channel_id
|
||||
else:
|
||||
thread = request.env[thread_model].browse(int(thread_id)).exists()
|
||||
if 'body' in post_data:
|
||||
post_data['body'] = Markup(post_data['body']) # contains HTML such as @mentions
|
||||
message_data = thread.message_post(**{key: value for key, value in post_data.items() if key in self._get_allowed_message_post_params()}).message_format()[0]
|
||||
if 'temporary_id' in request.context:
|
||||
message_data['temporary_id'] = request.context['temporary_id']
|
||||
|
||||
@@ -5,6 +5,7 @@ import base64
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from hashlib import sha512
|
||||
from markupsafe import Markup
|
||||
from secrets import choice
|
||||
from markupsafe import Markup
|
||||
|
||||
@@ -315,7 +316,7 @@ class Channel(models.Model):
|
||||
# channel_info is called before actually unpinning the channel
|
||||
channel_info['is_pinned'] = False
|
||||
self.env['bus.bus']._sendone(partner, 'mail.channel/leave', channel_info)
|
||||
notification = _('<div class="o_mail_notification">left the channel</div>')
|
||||
notification = Markup('<div class="o_mail_notification">%s</div>') % _('left the channel')
|
||||
# post 'channel left' message as root since the partner just unsubscribed from the channel
|
||||
self.sudo().message_post(body=notification, subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
self.env['bus.bus']._sendone(self, 'mail.record/insert', {
|
||||
@@ -387,15 +388,13 @@ class Channel(models.Model):
|
||||
if post_joined_message:
|
||||
# notify existing members with a new message in the channel
|
||||
if member.partner_id == self.env.user.partner_id:
|
||||
notification = _('<div class="o_mail_notification">joined the channel</div>')
|
||||
notification = Markup('<div class="o_mail_notification">%s</div>') % _('joined the channel')
|
||||
else:
|
||||
notification = _(
|
||||
'<div class="o_mail_notification">invited %s to the channel</div>',
|
||||
member.partner_id._get_html_link(),
|
||||
)
|
||||
notification = (Markup('<div class="o_mail_notification">%s</div>') % _("invited %s to the channel")) % member.partner_id._get_html_link()
|
||||
member.channel_id.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
for member in new_members.filtered(lambda member: member.guest_id):
|
||||
member.channel_id.message_post(body=_('<div class="o_mail_notification">joined the channel</div>'), message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
member.channel_id.message_post(body=Markup('<div class="o_mail_notification">%s</div>') % _('joined the channel'),
|
||||
message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
guest = member.guest_id
|
||||
if guest:
|
||||
notifications.append((guest, 'mail.channel/joined', {
|
||||
@@ -1135,7 +1134,9 @@ class Channel(models.Model):
|
||||
new_channel = self.create(vals)
|
||||
group = self.env['res.groups'].search([('id', '=', group_id)]) if group_id else None
|
||||
new_channel.group_public_id = group.id if group else None
|
||||
notification = _('<div class="o_mail_notification">created <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>', new_channel.id, new_channel.name)
|
||||
notification = (Markup('<div class="o_mail_notification">%s</div>') % _("created %(link)s")) % {
|
||||
'link': Markup('<a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a>') % (new_channel.id, new_channel.name)
|
||||
}
|
||||
new_channel.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
channel_info = new_channel.channel_info()[0]
|
||||
self.env['bus.bus']._sendone(self.env.user.partner_id, 'mail.channel/legacy_insert', channel_info)
|
||||
|
||||
@@ -23,7 +23,7 @@ from email import message_from_string
|
||||
from lxml import etree
|
||||
from werkzeug import urls
|
||||
from xmlrpc import client as xmlrpclib
|
||||
from markupsafe import Markup
|
||||
from markupsafe import Markup, escape
|
||||
|
||||
from odoo import _, api, exceptions, fields, models, tools, registry, SUPERUSER_ID, Command
|
||||
from odoo.exceptions import MissingError, AccessError
|
||||
@@ -1353,7 +1353,7 @@ class MailThread(models.AbstractModel):
|
||||
for node in to_remove:
|
||||
node.getparent().remove(node)
|
||||
if postprocessed:
|
||||
body = etree.tostring(root, pretty_print=False, encoding='unicode')
|
||||
body = Markup(etree.tostring(root, pretty_print=False, encoding='unicode'))
|
||||
return {'body': body, 'attachments': attachments}
|
||||
|
||||
def _message_parse_extract_payload(self, message, message_dict, save_original=False):
|
||||
@@ -2023,7 +2023,7 @@ class MailThread(models.AbstractModel):
|
||||
'model': self._name,
|
||||
'res_id': self.id,
|
||||
# content
|
||||
'body': body,
|
||||
'body': escape(body), # escape if text, keep if markup
|
||||
'message_type': message_type,
|
||||
'parent_id': self._message_compute_parent_id(parent_id),
|
||||
'subject': subject or False,
|
||||
@@ -2485,7 +2485,7 @@ class MailThread(models.AbstractModel):
|
||||
'record_name': False,
|
||||
'res_id': self.id if self else res_id,
|
||||
# content
|
||||
'body': body,
|
||||
'body': escape(body), # escape if text, keep if markup
|
||||
'is_internal': True,
|
||||
'message_type': 'user_notification',
|
||||
'subject': subject,
|
||||
@@ -2601,7 +2601,7 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
values_list = [dict(base_message_values,
|
||||
res_id=record.id,
|
||||
body=bodies.get(record.id, ''))
|
||||
body=escape(bodies.get(record.id, '')))
|
||||
for record in self]
|
||||
return self.sudo()._message_create(values_list)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from contextlib import contextmanager
|
||||
from markupsafe import Markup
|
||||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -407,7 +408,7 @@ class TestPartner(MailCommon):
|
||||
p1.message_subscribe(partner_ids=p3.ids)
|
||||
p1_act1 = p1.activity_schedule(act_type_xmlid='mail.mail_activity_data_todo')
|
||||
p1_msg1 = p1.message_post(
|
||||
body='<p>Log on P1</p>',
|
||||
body=Markup('<p>Log on P1</p>'),
|
||||
subtype_id=self.env.ref('mail.mt_comment').id
|
||||
)
|
||||
self.assertEqual(p1.activity_ids, p1_act1)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import itertools
|
||||
import random
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import models, _
|
||||
|
||||
|
||||
class MailBot(models.AbstractModel):
|
||||
_name = 'mail.bot'
|
||||
_description = 'Mail Bot'
|
||||
@@ -40,19 +40,19 @@ class MailBot(models.AbstractModel):
|
||||
if odoobot_state == 'onboarding_emoji' and self._body_contains_emoji(body):
|
||||
self.env.user.odoobot_state = "onboarding_command"
|
||||
self.env.user.odoobot_failed = False
|
||||
return _("Great! 👍<br/>To access special commands, <b>start your sentence with</b> <span class=\"o_odoobot_command\">/</span>. Try getting help.")
|
||||
return Markup(_("Great! 👍<br/>To access special commands, <b>start your sentence with</b> <span class=\"o_odoobot_command\">/</span>. Try getting help."))
|
||||
elif odoobot_state == 'onboarding_command' and command == 'help':
|
||||
self.env.user.odoobot_state = "onboarding_ping"
|
||||
self.env.user.odoobot_failed = False
|
||||
return _("Wow you are a natural!<br/>Ping someone with @username to grab their attention. <b>Try to ping me using</b> <span class=\"o_odoobot_command\">@OdooBot</span> in a sentence.")
|
||||
return Markup(_("Wow you are a natural!<br/>Ping someone with @username to grab their attention. <b>Try to ping me using</b> <span class=\"o_odoobot_command\">@OdooBot</span> in a sentence."))
|
||||
elif odoobot_state == 'onboarding_ping' and self._is_bot_pinged(values):
|
||||
self.env.user.odoobot_state = "onboarding_attachement"
|
||||
self.env.user.odoobot_failed = False
|
||||
return _("Yep, I am here! 🎉 <br/>Now, try <b>sending an attachment</b>, like a picture of your cute dog...")
|
||||
return Markup(_("Yep, I am here! 🎉 <br/>Now, try <b>sending an attachment</b>, like a picture of your cute dog..."))
|
||||
elif odoobot_state == 'onboarding_attachement' and values.get("attachment_ids"):
|
||||
self.env.user.odoobot_state = "idle"
|
||||
self.env.user.odoobot_failed = False
|
||||
return _("I am a simple bot, but if that's a dog, he is the cutest 😊 <br/>Congratulations, you finished this tour. You can now <b>close this chat window</b>. Enjoy discovering Odoo.")
|
||||
return Markup(_("I am a simple bot, but if that's a dog, he is the cutest 😊 <br/>Congratulations, you finished this tour. You can now <b>close this chat window</b>. Enjoy discovering Odoo."))
|
||||
elif odoobot_state in (False, "idle", "not_initialized") and (_('start the tour') in body.lower()):
|
||||
self.env.user.odoobot_state = "onboarding_emoji"
|
||||
return _("To start, try to send me an emoji :)")
|
||||
@@ -63,28 +63,28 @@ class MailBot(models.AbstractModel):
|
||||
return _("That's not nice! I'm a bot but I have feelings... 💔")
|
||||
# help message
|
||||
elif self._is_help_requested(body) or odoobot_state == 'idle':
|
||||
return _("Unfortunately, I'm just a bot 😞 I don't understand! If you need help discovering our product, please check "
|
||||
return Markup(_("Unfortunately, I'm just a bot 😞 I don't understand! If you need help discovering our product, please check "
|
||||
"<a href=\"https://www.odoo.com/documentation\" target=\"_blank\">our documentation</a> or "
|
||||
"<a href=\"https://www.odoo.com/slides\" target=\"_blank\">our videos</a>.")
|
||||
"<a href=\"https://www.odoo.com/slides\" target=\"_blank\">our videos</a>."))
|
||||
else:
|
||||
# repeat question
|
||||
if odoobot_state == 'onboarding_emoji':
|
||||
self.env.user.odoobot_failed = True
|
||||
return _("Not exactly. To continue the tour, send an emoji: <b>type</b> <span class=\"o_odoobot_command\">:)</span> and press enter.")
|
||||
return Markup(_("Not exactly. To continue the tour, send an emoji: <b>type</b> <span class=\"o_odoobot_command\">:)</span> and press enter."))
|
||||
elif odoobot_state == 'onboarding_attachement':
|
||||
self.env.user.odoobot_failed = True
|
||||
return _("To <b>send an attachment</b>, click on the <i class=\"fa fa-paperclip\" aria-hidden=\"true\"></i> icon and select a file.")
|
||||
return Markup(_("To <b>send an attachment</b>, click on the <i class=\"fa fa-paperclip\" aria-hidden=\"true\"></i> icon and select a file."))
|
||||
elif odoobot_state == 'onboarding_command':
|
||||
self.env.user.odoobot_failed = True
|
||||
return _("Not sure what you are doing. Please, type <span class=\"o_odoobot_command\">/</span> and wait for the propositions. Select <span class=\"o_odoobot_command\">help</span> and press enter")
|
||||
return Markup(_("Not sure what you are doing. Please, type <span class=\"o_odoobot_command\">/</span> and wait for the propositions. Select <span class=\"o_odoobot_command\">help</span> and press enter"))
|
||||
elif odoobot_state == 'onboarding_ping':
|
||||
self.env.user.odoobot_failed = True
|
||||
return _("Sorry, I am not listening. To get someone's attention, <b>ping him</b>. Write <span class=\"o_odoobot_command\">@OdooBot</span> and select me.")
|
||||
return Markup(_("Sorry, I am not listening. To get someone's attention, <b>ping him</b>. Write <span class=\"o_odoobot_command\">@OdooBot</span> and select me."))
|
||||
return random.choice([
|
||||
_("I'm not smart enough to answer your question.<br/>To follow my guide, ask: <span class=\"o_odoobot_command\">start the tour</span>."),
|
||||
Markup(_("I'm not smart enough to answer your question.<br/>To follow my guide, ask: <span class=\"o_odoobot_command\">start the tour</span>.")),
|
||||
_("Hmmm..."),
|
||||
_("I'm afraid I don't understand. Sorry!"),
|
||||
_("Sorry I'm sleepy. Or not! Maybe I'm just trying to hide my unawareness of human language...<br/>I can show you features if you write: <span class=\"o_odoobot_command\">start the tour</span>.")
|
||||
Markup(_("Sorry I'm sleepy. Or not! Maybe I'm just trying to hide my unawareness of human language...<br/>I can show you features if you write: <span class=\"o_odoobot_command\">start the tour</span>."))
|
||||
])
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import models, fields, _
|
||||
|
||||
class Users(models.Model):
|
||||
@@ -32,7 +34,11 @@ class Users(models.Model):
|
||||
odoobot_id = self.env['ir.model.data']._xmlid_to_res_id("base.partner_root")
|
||||
channel_info = self.env['mail.channel'].channel_get([odoobot_id, self.partner_id.id])
|
||||
channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
message = _("Hello,<br/>Odoo's chat helps employees collaborate efficiently. I'm here to help you discover its features.<br/><b>Try to send me an emoji</b> <span class=\"o_odoobot_command\">:)</span>")
|
||||
message = Markup("%s<br/>%s<br/><b>%s</b> <span class=\"o_odoobot_command\">:)</span>") % (
|
||||
_("Hello,"),
|
||||
_("Odoo's chat helps employees collaborate efficiently. I'm here to help you discover its features."),
|
||||
_("Try to send me an emoji")
|
||||
)
|
||||
channel.sudo().message_post(body=message, author_id=odoobot_id, message_type="comment", subtype_xmlid="mail.mt_comment")
|
||||
self.sudo().odoobot_state = 'onboarding_emoji'
|
||||
return channel
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import ast
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
from odoo.exceptions import UserError
|
||||
@@ -386,10 +387,7 @@ 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 %s',
|
||||
request.equipment_id._get_html_link()
|
||||
)
|
||||
note = escape(_('Request planned for %s')) % request.equipment_id._get_html_link()
|
||||
else:
|
||||
note = False
|
||||
request.activity_schedule(
|
||||
|
||||
@@ -780,8 +780,8 @@ class MassMailing(models.Model):
|
||||
])
|
||||
|
||||
opt_out_records.write({'opt_out': value})
|
||||
message = _('The recipient <strong>unsubscribed from %s</strong> mailing list(s)') \
|
||||
if value else _('The recipient <strong>subscribed to %s</strong> mailing list(s)')
|
||||
message = Markup(_('The recipient <strong>unsubscribed from %s</strong> mailing list(s)')) \
|
||||
if value else Markup(_('The recipient <strong>subscribed to %s</strong> mailing list(s)'))
|
||||
for record in records:
|
||||
# filter the list_id by record
|
||||
record_lists = opt_out_records.filtered(lambda rec: rec.contact_id.id == record.id)
|
||||
|
||||
@@ -77,17 +77,16 @@ class TestMassMailing(models.TransientModel):
|
||||
_('Test mailing successfully sent to %s', mail_sudo.email_to))
|
||||
elif mail_sudo.state == 'exception':
|
||||
notification_messages.append(
|
||||
_('Test mailing could not be sent to %s:<br>%s',
|
||||
mail_sudo.email_to,
|
||||
mail_sudo.failure_reason)
|
||||
_('Test mailing could not be sent to %s:', mail_sudo.email_to) +
|
||||
(Markup("<br/>") + mail_sudo.failure_reason)
|
||||
)
|
||||
|
||||
# manually delete the emails since we passed 'auto_delete: False'
|
||||
mails_sudo.unlink()
|
||||
|
||||
if notification_messages:
|
||||
self.mass_mailing_id._message_log(body='<ul>%s</ul>' % ''.join(
|
||||
['<li>%s</li>' % notification_message for notification_message in notification_messages]
|
||||
self.mass_mailing_id._message_log(body=Markup('<ul>%s</ul>') % ''.join(
|
||||
[Markup('<li>%s</li>') % notification_message for notification_message in notification_messages]
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import fields, models, _
|
||||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
|
||||
@@ -59,8 +60,8 @@ class MassSMSTest(models.TransientModel):
|
||||
)
|
||||
|
||||
if notification_messages:
|
||||
self.mailing_id._message_log(body='<ul>%s</ul>' % ''.join(
|
||||
['<li>%s</li>' % notification_message for notification_message in notification_messages]
|
||||
self.mailing_id._message_log(body=Markup('<ul>%s</ul>') % ''.join(
|
||||
[Markup('<li>%s</li>') % notification_message for notification_message in notification_messages]
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
@@ -7,7 +7,7 @@ from odoo.tools import float_compare, float_round
|
||||
from odoo.osv import expression
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
class MrpUnbuild(models.Model):
|
||||
_name = "mrp.unbuild"
|
||||
@@ -202,12 +202,11 @@ class MrpUnbuild(models.Model):
|
||||
produced_move_line_ids = produce_moves.mapped('move_line_ids').filtered(lambda ml: ml.qty_done > 0)
|
||||
consume_moves.mapped('move_line_ids').write({'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]})
|
||||
if self.mo_id:
|
||||
unbuild_msg = _(
|
||||
"%(qty)s %(measure)s unbuilt in %(order)s",
|
||||
qty=self.product_qty,
|
||||
measure=self.product_uom_id.name,
|
||||
order=self._get_html_link(),
|
||||
)
|
||||
unbuild_msg = escape(_("%(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_xmlid='mail.mt_note',
|
||||
|
||||
@@ -8,6 +8,7 @@ from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
from dateutil import relativedelta
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
@@ -1109,7 +1110,7 @@ class PaymentTransaction(models.Model):
|
||||
ref=self.reference, amount=formatted_amount, provider_name=self.provider_id.name
|
||||
)
|
||||
if self.state_message:
|
||||
message += "<br />" + _("Error: %s", self.state_message)
|
||||
message += Markup("<br/>") + _("Error: %s", self.state_message)
|
||||
else:
|
||||
message = _(
|
||||
("The transaction with reference %(ref)s for %(amount)s is canceled "
|
||||
@@ -1119,7 +1120,7 @@ class PaymentTransaction(models.Model):
|
||||
provider_name=self.provider_id.name
|
||||
)
|
||||
if self.state_message:
|
||||
message += "<br />" + _("Reason: %s", self.state_message)
|
||||
message += Markup("<br/>") + _("Reason: %s", self.state_message)
|
||||
return message
|
||||
|
||||
def _get_last(self):
|
||||
|
||||
@@ -6,6 +6,7 @@ from markupsafe import Markup
|
||||
from functools import partial
|
||||
from itertools import groupby
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape
|
||||
|
||||
import psycopg2
|
||||
import pytz
|
||||
@@ -501,10 +502,9 @@ 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: %s",
|
||||
self._get_html_link(),
|
||||
)
|
||||
message = escape(_("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,6 +4,7 @@
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from itertools import groupby
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _, Command
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
@@ -1500,7 +1501,7 @@ class PosSession(models.Model):
|
||||
f"{self.currency_id.round(difference)} " \
|
||||
f"{self.currency_id.symbol if self.currency_id.position == 'after' else ''}<br/>"
|
||||
if notes:
|
||||
message += notes.replace('\n', '<br/>')
|
||||
message += notes.replace('\n', Markup('<br/>'))
|
||||
if message:
|
||||
self.message_post(body=message)
|
||||
|
||||
@@ -1563,7 +1564,7 @@ class PosSession(models.Model):
|
||||
message_content = [f"Cash {extras['translatedType']}", f'- Amount: {extras["formattedAmount"]}']
|
||||
if reason:
|
||||
message_content.append(f'- Reason: {reason}')
|
||||
self.message_post(body='<br/>\n'.join(message_content))
|
||||
self.message_post(body=Markup('<br/>\n').join(message_content))
|
||||
|
||||
def get_onboarding_data(self):
|
||||
return {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import difflib
|
||||
import logging
|
||||
import time
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, fields, models, Command, _
|
||||
|
||||
@@ -130,7 +131,7 @@ class AccountMove(models.Model):
|
||||
if not purchases:
|
||||
continue
|
||||
refs = [purchase._get_html_link() for purchase in purchases]
|
||||
message = _("This vendor bill has been created from: %s") % ','.join(refs)
|
||||
message = escape(_("This vendor bill has been created from: %s")) % ','.join(refs)
|
||||
move.message_post(body=message)
|
||||
return moves
|
||||
|
||||
@@ -145,7 +146,7 @@ class AccountMove(models.Model):
|
||||
diff_purchases = new_purchases - old_purchases[i]
|
||||
if diff_purchases:
|
||||
refs = [purchase._get_html_link() for purchase in diff_purchases]
|
||||
message = _("This vendor bill has been modified from: %s") % ','.join(refs)
|
||||
message = escape(_("This vendor bill has been modified from: %s")) % ','.join(refs)
|
||||
move.message_post(body=message)
|
||||
return res
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
from itertools import groupby
|
||||
from markupsafe import Markup
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
@@ -989,10 +989,9 @@ class SaleOrder(models.Model):
|
||||
self.show_update_fpos = False
|
||||
|
||||
if self.partner_id:
|
||||
self.message_post(body=_(
|
||||
"Product taxes have been recomputed according to fiscal position %s.",
|
||||
self.message_post(body=escape(_("Product taxes have been recomputed according to fiscal position %s.")) % \
|
||||
self.fiscal_position_id._get_html_link() if self.fiscal_position_id else "",
|
||||
))
|
||||
)
|
||||
|
||||
def action_update_prices(self):
|
||||
self.ensure_one()
|
||||
@@ -1000,10 +999,8 @@ class SaleOrder(models.Model):
|
||||
self._recompute_prices()
|
||||
|
||||
if self.pricelist_id:
|
||||
message = _(
|
||||
"Product prices have been recomputed according to pricelist %s.",
|
||||
self.pricelist_id._get_html_link(),
|
||||
)
|
||||
message = escape(_("Product prices have been recomputed according to pricelist %s.")) % \
|
||||
self.pricelist_id._get_html_link()
|
||||
else:
|
||||
message = _("Product prices have been recomputed.")
|
||||
self.message_post(body=message)
|
||||
@@ -1508,7 +1505,7 @@ class SaleOrder(models.Model):
|
||||
order.activity_schedule(
|
||||
'sale.mail_act_sale_upsell',
|
||||
user_id=order.user_id.id or order.partner_id.user_id.id,
|
||||
note=_("Upsell %(order)s for customer %(customer)s", order=order_ref, customer=customer_ref))
|
||||
note=escape(_("Upsell %(order)s for customer %(customer)s")) % {"order":order_ref, "customer":customer_ref})
|
||||
|
||||
def _prepare_analytic_account_data(self, prefix=None):
|
||||
""" Prepare SO analytic account creation values.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
@@ -1017,18 +1018,18 @@ class SaleOrderLine(models.Model):
|
||||
orders = self.mapped('order_id')
|
||||
for order in orders:
|
||||
order_lines = self.filtered(lambda x: x.order_id == order)
|
||||
msg = "<b>" + _("The ordered quantity has been updated.") + "</b><ul>"
|
||||
msg = Markup("<b>%s</b><ul>") % _("The ordered quantity has been updated.")
|
||||
for line in order_lines:
|
||||
msg += "<li> %s: <br/>" % line.product_id.display_name
|
||||
msg += Markup("<li> %s: <br/>") % line.product_id.display_name
|
||||
msg += _(
|
||||
"Ordered Quantity: %(old_qty)s -> %(new_qty)s",
|
||||
old_qty=line.product_uom_qty,
|
||||
new_qty=values["product_uom_qty"]
|
||||
) + "<br/>"
|
||||
) + Markup("<br/>")
|
||||
if line.product_id.type in ('consu', 'product'):
|
||||
msg += _("Delivered Quantity: %s", line.qty_delivered) + "<br/>"
|
||||
msg += _("Invoiced Quantity: %s", line.qty_invoiced) + "<br/>"
|
||||
msg += "</ul>"
|
||||
msg += _("Delivered Quantity: %s", line.qty_delivered) + Markup("<br/>")
|
||||
msg += _("Invoiced Quantity: %s", line.qty_invoiced) + Markup("<br/>")
|
||||
msg += Markup("</ul>")
|
||||
order.message_post(body=msg)
|
||||
|
||||
def _check_line_unlink(self):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import api, Command, fields, models, _
|
||||
from odoo.tools import format_amount
|
||||
@@ -67,7 +68,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 and not has_task:
|
||||
msg_body = _("Task Created (%s): %s", line.product_id.name, line.task_id._get_html_link())
|
||||
msg_body = escape(_("Task Created (%s): %s")) % (line.product_id.name, line.task_id._get_html_link())
|
||||
line.order_id.message_post(body=msg_body)
|
||||
return lines
|
||||
|
||||
@@ -179,7 +180,10 @@ 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: %s (%s)", self.order_id._get_html_link(), self.product_id.name)
|
||||
task_msg = escape(_("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
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from markupsafe import escape
|
||||
from operator import itemgetter
|
||||
|
||||
from odoo import _, api, Command, fields, models
|
||||
@@ -740,7 +741,7 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
if not documents or not doc_orig:
|
||||
return
|
||||
|
||||
msg = _("The deadline has been automatically updated due to a delay on %s.", doc_orig[0]._get_html_link())
|
||||
msg = escape(_("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:
|
||||
|
||||
@@ -6,6 +6,7 @@ import time
|
||||
from ast import literal_eval
|
||||
from datetime import date, timedelta
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import SUPERUSER_ID, _, api, fields, models
|
||||
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
|
||||
@@ -1247,7 +1248,7 @@ class Picking(models.Model):
|
||||
'backorder_id': picking.id
|
||||
})
|
||||
picking.message_post(
|
||||
body=_('The backorder %s has been created.', backorder_picking._get_html_link())
|
||||
body=escape(_('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})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
from markupsafe import Markup
|
||||
import json
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
@@ -189,13 +190,13 @@ class StockPicking(models.Model):
|
||||
for p in previous_pickings - without_tracking:
|
||||
p.carrier_tracking_ref += "," + res['tracking_number']
|
||||
order_currency = self.sale_id.currency_id or self.company_id.currency_id
|
||||
msg = _(
|
||||
"Shipment sent to carrier %(carrier_name)s for shipping with tracking number %(ref)s<br/>Cost: %(price).2f %(currency)s",
|
||||
carrier_name=self.carrier_id.name,
|
||||
ref=self.carrier_tracking_ref,
|
||||
price=self.carrier_price,
|
||||
currency=order_currency.name
|
||||
)
|
||||
msg = _("Shipment sent to carrier %(carrier_name)s for shipping with tracking number %(ref)s",
|
||||
carrier_name=self.carrier_id.name,
|
||||
ref=self.carrier_tracking_ref) + \
|
||||
Markup("<br/>") + \
|
||||
_("Cost: %(price).2f %(currency)s",
|
||||
price=self.carrier_price,
|
||||
currency=order_currency.name)
|
||||
self.message_post(body=msg)
|
||||
self._add_delivery_cost_to_so()
|
||||
|
||||
@@ -234,9 +235,9 @@ class StockPicking(models.Model):
|
||||
except ValueError:
|
||||
carrier_trackers = self.carrier_tracking_url
|
||||
else:
|
||||
msg = "Tracking links for shipment: <br/>"
|
||||
msg = _("Tracking links for shipment:") + Markup("<br/>")
|
||||
for tracker in carrier_trackers:
|
||||
msg += '<a href=' + tracker[1] + '>' + tracker[0] + '</a><br/>'
|
||||
msg += Markup('<a href="%s">%s</a><br/>') % (tracker[1], tracker[0])
|
||||
self.message_post(body=msg)
|
||||
return self.env["ir.actions.actions"]._for_xml_id("delivery.act_delivery_trackers_url")
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
from odoo import _, api, Command, fields, models
|
||||
from odoo.osv import expression
|
||||
from odoo.exceptions import ValidationError
|
||||
@@ -245,7 +247,7 @@ class StockPicking(models.Model):
|
||||
pickings = self.filtered(lambda p: p.user_id.id != user_id)
|
||||
pickings.write({'user_id': user_id})
|
||||
for pick in pickings:
|
||||
log_message = _('Assigned to %s Responsible', (pick.batch_id._get_html_link()))
|
||||
log_message = escape(_('Assigned to %s Responsible')) % pick.batch_id._get_html_link()
|
||||
pick.message_post(body=log_message)
|
||||
|
||||
def action_view_batch(self):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv.expression import AND
|
||||
@@ -239,7 +241,7 @@ class StockPickingBatch(models.Model):
|
||||
if has_no_qty_done(picking):
|
||||
empty_pickings.add(picking.id)
|
||||
picking.message_post(
|
||||
body="<b>%s:</b> %s <a href=#id=%s&view_type=form&model=stock.picking.batch>%s</a>" % (
|
||||
body=Markup("<b>%s:</b> %s <a href=#id=%s&view_type=form&model=stock.picking.batch>%s</a>") % (
|
||||
_("Transferred by"),
|
||||
_("Batch Transfer"),
|
||||
picking.batch_id.id,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.tests import tagged, users
|
||||
@@ -660,7 +662,7 @@ class RecipientsNotificationTest(MailCommon):
|
||||
'status': 'sent', 'type': 'inbox'}],
|
||||
message_info={'content': 'User Choice Notification'}):
|
||||
test.message_post(
|
||||
body='<p>User Choice Notification</p>',
|
||||
body=Markup('<p>User Choice Notification</p>'),
|
||||
message_type='comment',
|
||||
partner_ids=shared_partner.ids,
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
|
||||
@@ -7,6 +7,7 @@ import smtplib
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from freezegun import freeze_time
|
||||
from markupsafe import Markup
|
||||
from OpenSSL.SSL import Error as SSLError
|
||||
from socket import gaierror, timeout
|
||||
from unittest.mock import call, patch
|
||||
@@ -38,9 +39,9 @@ class TestMailMail(MailCommon):
|
||||
'email_from': 'ignasse@example.com',
|
||||
}).with_context({})
|
||||
|
||||
cls.test_message = cls.test_record.message_post(body='<p>Message</p>', subject='Subject')
|
||||
cls.test_message = cls.test_record.message_post(body=Markup('<p>Message</p>'), subject='Subject')
|
||||
cls.test_mail = cls.env['mail.mail'].create([{
|
||||
'body': '<p>Body</p>',
|
||||
'body': Markup('<p>Body</p>'),
|
||||
'email_from': False,
|
||||
'email_to': 'test@example.com',
|
||||
'is_notification': True,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
from markupsafe import Markup
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user, MailCommon
|
||||
@@ -483,7 +484,7 @@ class TestMessageAccess(MailCommon):
|
||||
test_record.message_subscribe((partner_1 | self.user_admin.partner_id).ids)
|
||||
|
||||
message = test_record.message_post(
|
||||
body='<p>This is First Message</p>', subject='Subject',
|
||||
body=Markup('<p>This is First Message</p>'), subject='Subject',
|
||||
message_type='comment', subtype_xmlid='mail.mt_note')
|
||||
# portal user have no rights to read the message
|
||||
with self.assertRaises(AccessError):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import DEFAULT
|
||||
|
||||
@@ -36,7 +37,7 @@ class TestAPI(MailCommon, TestRecipients):
|
||||
# post a note
|
||||
message = ticket_record.message_post(
|
||||
attachment_ids=attachments.ids,
|
||||
body="<p>Initial Body</p>",
|
||||
body=Markup("<p>Initial Body</p>"),
|
||||
message_type="comment",
|
||||
partner_ids=self.partner_1.ids,
|
||||
)
|
||||
@@ -543,7 +544,7 @@ class TestNoThread(MailCommon, TestRecipients):
|
||||
'subtype': 'mail.mt_note',
|
||||
}]):
|
||||
_message = self.env['mail.thread'].message_notify(
|
||||
body='<p>Hello Paulo</p>',
|
||||
body=Markup('<p>Hello Paulo</p>'),
|
||||
partner_ids=self.partner_2.ids,
|
||||
subject='Test Notify',
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import base64
|
||||
from datetime import datetime, timedelta
|
||||
from freezegun import freeze_time
|
||||
from itertools import product
|
||||
from markupsafe import escape
|
||||
from markupsafe import escape, Markup
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo import tools
|
||||
@@ -295,7 +295,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_notification = test_record.message_notify(
|
||||
body='<p>You have received a notification</p>',
|
||||
body=Markup('<p>You have received a notification</p>'),
|
||||
partner_ids=[self.partner_1.id, self.partner_admin.id, self.partner_employee_2.id],
|
||||
subject='This should be a subject',
|
||||
)
|
||||
@@ -342,7 +342,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_notification = test_record.message_notify(
|
||||
body='<p>You have received a notification</p>',
|
||||
body=Markup('<p>You have received a notification</p>'),
|
||||
partner_ids=(self.partner_1 + self.partner_employee).ids,
|
||||
subject='This should be a subject',
|
||||
)
|
||||
@@ -351,7 +351,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_notification = test_record.message_notify(
|
||||
body='<p>You have received a notification</p>',
|
||||
body=Markup('<p>You have received a notification</p>'),
|
||||
notify_author=True,
|
||||
partner_ids=(self.partner_1 + self.partner_employee).ids,
|
||||
subject='This should be a subject',
|
||||
@@ -365,7 +365,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_notification = test_record.with_context(mail_notify_author=True).message_notify(
|
||||
body='<p>You have received a notification</p>',
|
||||
body=Markup('<p>You have received a notification</p>'),
|
||||
partner_ids=(self.partner_1 + self.partner_employee).ids,
|
||||
subject='This should be a subject',
|
||||
)
|
||||
@@ -383,7 +383,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
test_records.message_notify(
|
||||
body='<p>Nice notification content</p>',
|
||||
body=Markup('<p>Nice notification content</p>'),
|
||||
partner_ids=self.partner_employee_2.ids,
|
||||
subject='Notify Subject',
|
||||
)
|
||||
@@ -473,7 +473,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
self.mock_mail_gateway(), \
|
||||
self.assertRaises(ValueError):
|
||||
_new_message = test_record.message_notify(
|
||||
body='<p>You will not receive a notification</p>',
|
||||
body=Markup('<p>You will not receive a notification</p>'),
|
||||
partner_ids=self.partner_1.ids,
|
||||
subject='This should not be accepted',
|
||||
**parameters
|
||||
@@ -481,7 +481,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
|
||||
# support of subtype xml id
|
||||
new_message = test_record.message_notify(
|
||||
body='<p>You will not receive a notification</p>',
|
||||
body=Markup('<p>You will not receive a notification</p>'),
|
||||
partner_ids=self.partner_1.ids,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
@@ -494,7 +494,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
people without having a document. """
|
||||
with self.mock_mail_gateway():
|
||||
new_notification = self.env['mail.thread'].message_notify(
|
||||
body='<p>You have received a notification</p>',
|
||||
body=Markup('<p>You have received a notification</p>'),
|
||||
partner_ids=[self.partner_1.id, self.partner_admin.id, self.partner_employee_2.id],
|
||||
subject='This should be a subject',
|
||||
)
|
||||
@@ -535,7 +535,7 @@ class TestMessageLog(TestMessagePostCommon):
|
||||
test_record.message_subscribe(self.partner_employee_2.ids)
|
||||
|
||||
new_note = test_record._message_log(
|
||||
body='<p>Labrador</p>',
|
||||
body=Markup('<p>Labrador</p>'),
|
||||
)
|
||||
self.assertMessageFields(
|
||||
new_note,
|
||||
@@ -559,7 +559,7 @@ class TestMessageLog(TestMessagePostCommon):
|
||||
|
||||
new_notes = test_records._message_log_batch(
|
||||
bodies=dict(
|
||||
(test_record.id, '<p>Test _message_log_batch</p>')
|
||||
(test_record.id, Markup('<p>Test _message_log_batch</p>'))
|
||||
for test_record in test_records
|
||||
),
|
||||
)
|
||||
@@ -833,7 +833,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
self.assertMsgWithoutNotifications(), \
|
||||
self.capture_triggers(cron_id) as capt:
|
||||
msg = test_record.message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -880,7 +880,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
self.mock_mail_gateway(mail_unlink_sent=False), \
|
||||
self.capture_triggers(cron_id) as capt:
|
||||
msg = test_record.message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -908,7 +908,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
with freeze_time(now), \
|
||||
self.assertMsgWithoutNotifications():
|
||||
msg = test_record.message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -1086,7 +1086,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
]
|
||||
), patch.object(MailTestSimple, 'check_access_rights', return_value=True):
|
||||
new_msg = self.test_record.with_user(self.user_portal).message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -1095,7 +1095,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.test_record.with_user(self.user_portal).message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -1108,7 +1108,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
parent_msg = test_record.message_post(
|
||||
body='<p>Test</p>',
|
||||
body=Markup('<p>Test</p>'),
|
||||
message_type='comment',
|
||||
subject='Test Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
@@ -1121,7 +1121,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
[{'content': '<p>Test Answer</p>', 'notif': [{'partner': self.partner_1, 'type': 'email'}]}]
|
||||
):
|
||||
msg = test_record.message_post(
|
||||
body='<p>Test Answer</p>',
|
||||
body=Markup('<p>Test Answer</p>'),
|
||||
message_type='comment',
|
||||
parent_id=parent_msg.id,
|
||||
partner_ids=[self.partner_1.id],
|
||||
@@ -1144,7 +1144,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase):
|
||||
# post a reply to the reply: check parent is the first one
|
||||
with self.mock_mail_gateway():
|
||||
new_msg = test_record.message_post(
|
||||
body='<p>Test Answer Bis</p>',
|
||||
body=Markup('<p>Test Answer Bis</p>'),
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
parent_id=msg.id,
|
||||
@@ -1697,7 +1697,7 @@ class TestMessagePostLang(MailCommon, TestRecipients):
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
test_records[1].message_post(
|
||||
body='<p>Hello</p>',
|
||||
body=Markup('<p>Hello</p>'),
|
||||
email_layout_xmlid='mail.test_layout',
|
||||
message_type='comment',
|
||||
subject='Subject',
|
||||
@@ -1820,7 +1820,7 @@ class TestMessagePostLang(MailCommon, TestRecipients):
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False), \
|
||||
self.mock_mail_app():
|
||||
record.message_post(
|
||||
body='<p>Hi there</p>',
|
||||
body=Markup('<p>Hi there</p>'),
|
||||
email_layout_xmlid=email_layout_xmlid,
|
||||
message_type='comment',
|
||||
subject='TeDeum',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from contextlib import nullcontext
|
||||
from markupsafe import Markup
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
|
||||
@@ -589,7 +589,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(admin=1, employee=1):
|
||||
record._message_log(
|
||||
body='<p>Test _message_log</p>',
|
||||
body=Markup('<p>Test _message_log</p>'),
|
||||
message_type='comment')
|
||||
|
||||
@users('admin', 'employee')
|
||||
@@ -603,7 +603,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
with self.assertQueryCount(admin=1, employee=1):
|
||||
records._message_log_batch(
|
||||
bodies=dict(
|
||||
(record.id, '<p>Test _message_log</p>')
|
||||
(record.id, Markup('<p>Test _message_log</p>'))
|
||||
for record in records
|
||||
),
|
||||
message_type='comment')
|
||||
@@ -629,7 +629,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(admin=7, employee=7):
|
||||
record.message_post(
|
||||
body='<p>Test message_post as log</p>',
|
||||
body=Markup('<p>Test message_post as log</p>'),
|
||||
subtype_xmlid='mail.mt_note',
|
||||
message_type='comment')
|
||||
|
||||
@@ -640,7 +640,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(admin=7, employee=7):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances basic</p>',
|
||||
body=Markup('<p>Test Post Performances basic</p>'),
|
||||
partner_ids=[],
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
@@ -653,7 +653,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(admin=30, employee=30):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances with an email ping</p>',
|
||||
body=Markup('<p>Test Post Performances with an email ping</p>'),
|
||||
partner_ids=self.customer.ids,
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
@@ -665,7 +665,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(admin=17, employee=17):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances with an inbox ping</p>',
|
||||
body=Markup('<p>Test Post Performances with an inbox ping</p>'),
|
||||
partner_ids=self.user_test.partner_id.ids,
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
@@ -854,7 +854,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
|
||||
# about 20 (19?) queries per additional customer group
|
||||
with self.assertQueryCount(admin=53, employee=52):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances</p>',
|
||||
body=Markup('<p>Test Post Performances</p>'),
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
|
||||
@@ -1311,7 +1311,7 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
|
||||
# with self.assertQueryCount(employee=63), enable_logging:
|
||||
with self.assertQueryCount(employee=60):
|
||||
record_container.with_context({}).message_post(
|
||||
body='<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>',
|
||||
body=Markup('<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>'),
|
||||
subject='Test Subject',
|
||||
message_type='notification',
|
||||
subtype_xmlid=None,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.addons.test_mail.tests.test_performance import BaseMailPerformance
|
||||
from odoo.tests.common import users, warmup
|
||||
@@ -84,7 +85,7 @@ class TestMailPerformance(BaseMailPerformance):
|
||||
with self.assertQueryCount(employee=85): # tmf: 85
|
||||
new_message = record_ticket.message_post(
|
||||
attachment_ids=attachments.ids,
|
||||
body='<p>Test Content</p>',
|
||||
body=Markup('<p>Test Content</p>'),
|
||||
message_type='comment',
|
||||
subject='Test Subject',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import random
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import AccessDenied, AccessError, UserError
|
||||
@@ -189,23 +190,23 @@ class CrmLead(models.Model):
|
||||
return res_partner_ids
|
||||
|
||||
def partner_interested(self, comment=False):
|
||||
message = _('<p>I am interested by this lead.</p>')
|
||||
message = Markup('<p>%s</p>') % _('I am interested by this lead.')
|
||||
if comment:
|
||||
message += '<p>%s</p>' % html_escape(comment)
|
||||
message += Markup('<p>%s</p>') % comment
|
||||
for lead in self:
|
||||
lead.message_post(body=message)
|
||||
lead.sudo().convert_opportunity(lead.partner_id) # sudo required to convert partner data
|
||||
|
||||
def partner_desinterested(self, comment=False, contacted=False, spam=False):
|
||||
if contacted:
|
||||
message = '<p>%s</p>' % _('I am not interested by this lead. I contacted the lead.')
|
||||
message = Markup('<p>%s</p>') % _('I am not interested by this lead. I contacted the lead.')
|
||||
else:
|
||||
message = '<p>%s</p>' % _('I am not interested by this lead. I have not contacted the lead.')
|
||||
message = Markup('<p>%s</p>') % _('I am not interested by this lead. I have not contacted the lead.')
|
||||
partner_ids = self.env['res.partner'].search(
|
||||
[('id', 'child_of', self.env.user.partner_id.commercial_partner_id.id)])
|
||||
self.message_unsubscribe(partner_ids=partner_ids.ids)
|
||||
if comment:
|
||||
message += '<p>%s</p>' % html_escape(comment)
|
||||
message += Markup('<p>%s</p>') % comment
|
||||
self.message_post(body=message)
|
||||
values = {
|
||||
'partner_assigned_id': False,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import _, fields, models
|
||||
|
||||
|
||||
@@ -20,8 +21,8 @@ class PaymentTransaction(models.Model):
|
||||
if value:
|
||||
if hasattr(value, 'name'):
|
||||
value = value.name
|
||||
msg.append('<br/>- %s: %s' % (field_name, value))
|
||||
tx.payment_id._message_log(body=''.join(msg))
|
||||
msg.append(Markup('<br/>- %s: %s') % (field_name, value))
|
||||
tx.payment_id._message_log(body=Markup().join(msg))
|
||||
|
||||
def _send_donation_email(self, is_internal_notification=False, comment=None, recipient_email=None):
|
||||
self.ensure_one()
|
||||
|
||||
Reference in New Issue
Block a user