[IMP] *: convert to private methods
render, render_template, load, activity_schedule_with_view, get_website_pages should all be private: It should not be possible to render an aribtrary template only with its name or id Still need to render some qweb views from js so the method render_template is kept public. This explains why the website editor still need read access on ir.ui.view as we want to allow any snippet to be rendered.
This commit is contained in:
@@ -16,7 +16,7 @@ class OnboardingController(http.Controller):
|
||||
return {}
|
||||
|
||||
return {
|
||||
'html': request.env.ref('account.account_invoice_onboarding_panel').render({
|
||||
'html': request.env.ref('account.account_invoice_onboarding_panel')._render({
|
||||
'company': company,
|
||||
'state': company.get_and_update_account_invoice_onboarding_state()
|
||||
})
|
||||
@@ -34,7 +34,7 @@ class OnboardingController(http.Controller):
|
||||
return {}
|
||||
|
||||
return {
|
||||
'html': request.env.ref('account.account_dashboard_onboarding_panel').render({
|
||||
'html': request.env.ref('account.account_dashboard_onboarding_panel')._render({
|
||||
'company': company,
|
||||
'state': company.get_and_update_account_dashboard_onboarding_state()
|
||||
})
|
||||
|
||||
@@ -396,7 +396,7 @@ class AccountBankStatement(models.Model):
|
||||
|
||||
# Bank statement report.
|
||||
if statement.journal_id.type == 'bank':
|
||||
content, content_type = self.env.ref('account.action_report_account_statement').render(statement.id)
|
||||
content, content_type = self.env.ref('account.action_report_account_statement')._render(statement.id)
|
||||
self.env['ir.attachment'].create({
|
||||
'name': statement.name and _("Bank Statement %s.pdf") % statement.name or _("Bank Statement.pdf"),
|
||||
'type': 'binary',
|
||||
|
||||
@@ -43,7 +43,7 @@ class AccountMove(models.Model):
|
||||
'format_date': format_date,
|
||||
'format_monetary': format_monetary,
|
||||
}
|
||||
content = self.env.ref('account_facturx.account_invoice_facturx_export').render(template_values)
|
||||
content = self.env.ref('account_facturx.account_invoice_facturx_export')._render(template_values)
|
||||
return b"<?xml version='1.0' encoding='UTF-8'?>" + content
|
||||
|
||||
def _import_facturx_invoice(self, tree):
|
||||
|
||||
@@ -76,7 +76,7 @@ class CalendarController(http.Controller):
|
||||
# - we need a template rendering which is not lazy, to render before cursor closing
|
||||
# - we need to display the template in the language of the user (not possible with
|
||||
# request.render())
|
||||
response_content = request.env['ir.ui.view'].with_context(lang=lang).render_template(
|
||||
response_content = request.env['ir.ui.view'].with_context(lang=lang)._render_template(
|
||||
'calendar.invitation_page_anonymous', {
|
||||
'event': event,
|
||||
'attendee': attendee,
|
||||
|
||||
@@ -91,7 +91,7 @@ class EventRegistration(models.Model):
|
||||
'new_ticket_name': new_event_ticket.name
|
||||
}
|
||||
user_id = registration.event_id.user_id.id or registration.sale_order_id.user_id.id or fallback_user_id
|
||||
registration.sale_order_id.activity_schedule_with_view(
|
||||
registration.sale_order_id._activity_schedule_with_view(
|
||||
'mail.mail_activity_data_warning',
|
||||
user_id=user_id,
|
||||
views_or_xmlid='event_sale.event_ticket_id_change_exception',
|
||||
|
||||
@@ -25,7 +25,7 @@ class TestMultiCompany(TestHrCommon):
|
||||
def test_multi_company_report(self):
|
||||
content, content_type = self.env.ref('hr.hr_employee_print_badge').with_user(self.res_users_hr_officer).with_context(
|
||||
allowed_company_ids=[self.company_1.id, self.company_2.id]
|
||||
).render_qweb_pdf(res_ids=self.employees.ids)
|
||||
)._render_qweb_pdf(res_ids=self.employees.ids)
|
||||
self.assertIn(b'Bidule', content)
|
||||
self.assertIn(b'Machin', content)
|
||||
|
||||
@@ -33,4 +33,4 @@ class TestMultiCompany(TestHrCommon):
|
||||
with self.assertRaises(QWebException): # CacheMiss followed by AccessError
|
||||
content, content_type = self.env.ref('hr.hr_employee_print_badge').with_user(self.res_users_hr_officer).with_company(
|
||||
self.company_1
|
||||
).render_qweb_pdf(res_ids=self.employees.ids)
|
||||
)._render_qweb_pdf(res_ids=self.employees.ids)
|
||||
|
||||
@@ -665,7 +665,7 @@ class HrExpense(models.Model):
|
||||
def _send_expense_success_mail(self, msg_dict, expense):
|
||||
mail_template_id = 'hr_expense.hr_expense_template_register' if expense.employee_id.user_id else 'hr_expense.hr_expense_template_register_no_user'
|
||||
expense_template = self.env.ref(mail_template_id)
|
||||
rendered_body = expense_template.render({'expense': expense}, engine='ir.qweb')
|
||||
rendered_body = expense_template._render({'expense': expense}, engine='ir.qweb')
|
||||
body = self.env['mail.render.mixin']._replace_local_links(rendered_body)
|
||||
# TDE TODO: seems louche, check to use notify
|
||||
if expense.employee_id.user_id.partner_id:
|
||||
|
||||
@@ -601,7 +601,7 @@ class IrHttp(models.AbstractModel):
|
||||
|
||||
@classmethod
|
||||
def _get_error_html(cls, env, code, values):
|
||||
return code, env['ir.ui.view'].render_template('http_routing.%s' % code, values)
|
||||
return code, env['ir.ui.view']._render_template('http_routing.%s' % code, values)
|
||||
|
||||
@classmethod
|
||||
def _handle_exception(cls, exception):
|
||||
@@ -658,6 +658,6 @@ class IrHttp(models.AbstractModel):
|
||||
try:
|
||||
code, html = cls._get_error_html(env, code, values)
|
||||
except Exception:
|
||||
code, html = 418, env['ir.ui.view'].render_template('http_routing.http_error', values)
|
||||
code, html = 418, env['ir.ui.view']._render_template('http_routing.http_error', values)
|
||||
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
|
||||
@@ -94,7 +94,7 @@ def authorize(env, key, account_token, credit, dbuuid=False, description=None, c
|
||||
except InsufficientCreditError as e:
|
||||
if credit_template:
|
||||
arguments = json.loads(e.args[0])
|
||||
arguments['body'] = pycompat.to_text(env['ir.qweb'].render(credit_template))
|
||||
arguments['body'] = pycompat.to_text(env['ir.qweb']._render(credit_template))
|
||||
e.args = (json.dumps(arguments),)
|
||||
raise e
|
||||
return transaction_token
|
||||
|
||||
@@ -65,7 +65,7 @@ class ImLivechatChannel(models.Model):
|
||||
}
|
||||
for record in self:
|
||||
values["channel_id"] = record.id
|
||||
record.script_external = view.render(values)
|
||||
record.script_external = view._render(values)
|
||||
|
||||
def _compute_web_page_link(self):
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
|
||||
@@ -184,7 +184,7 @@ class MailChannel(models.Model):
|
||||
"channel": self,
|
||||
}
|
||||
template = self.env.ref('im_livechat.livechat_email_template')
|
||||
mail_body = template.render(render_context, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_body = template._render(render_context, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_body = self.env['mail.render.mixin']._replace_local_links(mail_body)
|
||||
mail = self.env['mail.mail'].sudo().create({
|
||||
'subject': _('Conversation with %s') % self.livechat_operator_id.name,
|
||||
|
||||
@@ -36,10 +36,10 @@ class MailTemplate(models.Model):
|
||||
isr_report_name = 'ISR-' + inv_print_name + '.pdf'
|
||||
qr_report_name = 'QR-bill-' + inv_print_name + '.pdf'
|
||||
|
||||
isr_pdf = self.env.ref('l10n_ch.l10n_ch_isr_report').render_qweb_pdf(record.ids)[0]
|
||||
isr_pdf = self.env.ref('l10n_ch.l10n_ch_isr_report')._render_qweb_pdf(record.ids)[0]
|
||||
isr_pdf = base64.b64encode(isr_pdf)
|
||||
|
||||
qr_pdf = self.env.ref('l10n_ch.l10n_ch_qr_report').render_qweb_pdf(record.ids)[0]
|
||||
qr_pdf = self.env.ref('l10n_ch.l10n_ch_qr_report')._render_qweb_pdf(record.ids)[0]
|
||||
qr_pdf = base64.b64encode(qr_pdf)
|
||||
|
||||
new_attachments = [(isr_report_name, isr_pdf), (qr_report_name, qr_pdf)]
|
||||
|
||||
@@ -233,7 +233,7 @@ class AccountMove(models.Model):
|
||||
else:
|
||||
document_type = 'TD0X'
|
||||
|
||||
pdf = self.env.ref('account.account_invoices').render_qweb_pdf(self.id)[0]
|
||||
pdf = self.env.ref('account.account_invoices')._render_qweb_pdf(self.id)[0]
|
||||
pdf = base64.b64encode(pdf)
|
||||
pdf_name = re.sub(r'\W+', '', self.name) + '.pdf'
|
||||
|
||||
@@ -254,7 +254,7 @@ class AccountMove(models.Model):
|
||||
'pdf': pdf,
|
||||
'pdf_name': pdf_name,
|
||||
}
|
||||
content = self.env.ref('l10n_it_edi.account_invoice_it_FatturaPA_export').render(template_values)
|
||||
content = self.env.ref('l10n_it_edi.account_invoice_it_FatturaPA_export')._render(template_values)
|
||||
return content
|
||||
|
||||
def send_pec_mail(self):
|
||||
|
||||
@@ -60,7 +60,7 @@ class LunchController(http.Controller):
|
||||
|
||||
@http.route('/lunch/payment_message', type='json', auth='user')
|
||||
def payment_message(self):
|
||||
return {'message': request.env['ir.qweb'].render('lunch.lunch_payment_dialog', {})}
|
||||
return {'message': request.env['ir.qweb']._render('lunch.lunch_payment_dialog', {})}
|
||||
|
||||
@http.route('/lunch/user_location_set', type='json', auth='user')
|
||||
def set_user_location(self, location_id=None, user_id=None):
|
||||
|
||||
@@ -393,7 +393,7 @@ class MailActivity(models.Model):
|
||||
body_template = body_template.with_context(lang=activity.user_id.lang)
|
||||
activity = activity.with_context(lang=activity.user_id.lang)
|
||||
model_description = self.env['ir.model']._get(activity.res_model).display_name
|
||||
body = body_template.render(
|
||||
body = body_template._render(
|
||||
dict(
|
||||
activity=activity,
|
||||
model_description=model_description,
|
||||
@@ -798,7 +798,7 @@ class MailActivityMixin(models.AbstractModel):
|
||||
activities |= self.env['mail.activity'].create(create_vals)
|
||||
return activities
|
||||
|
||||
def activity_schedule_with_view(self, act_type_xmlid='', date_deadline=None, summary='', views_or_xmlid='', render_context=None, **act_values):
|
||||
def _activity_schedule_with_view(self, act_type_xmlid='', date_deadline=None, summary='', views_or_xmlid='', render_context=None, **act_values):
|
||||
""" Helper method: Schedule an activity on each record of the current record set.
|
||||
This method allow to the same mecanism as `activity_schedule`, but provide
|
||||
2 additionnal parameters:
|
||||
@@ -821,7 +821,7 @@ class MailActivityMixin(models.AbstractModel):
|
||||
activities = self.env['mail.activity']
|
||||
for record in self:
|
||||
render_context['object'] = record
|
||||
note = views.render(render_context, engine='ir.qweb', minimal_qcontext=True)
|
||||
note = views._render(render_context, engine='ir.qweb', minimal_qcontext=True)
|
||||
activities |= record.activity_schedule(act_type_xmlid=act_type_xmlid, date_deadline=date_deadline, summary=summary, note=note, **act_values)
|
||||
return activities
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ Only allowed people can contact us at this address.""" % message_dict.get('to'))
|
||||
else:
|
||||
body = self._get_alias_bounced_body_fallback(message_dict)
|
||||
template = self.env.ref('mail.mail_bounce_alias_security', raise_if_not_found=True)
|
||||
return template.render({
|
||||
return template._render({
|
||||
'body': body,
|
||||
'message': message_dict
|
||||
}, engine='ir.qweb', minimal_qcontext=True)
|
||||
|
||||
@@ -426,7 +426,7 @@ class Channel(models.Model):
|
||||
create_values = {
|
||||
'email_from': company.catchall_formatted or company.email_formatted,
|
||||
'author_id': self.env.user.partner_id.id,
|
||||
'body_html': view.render({'channel': self, 'partner': partner}, engine='ir.qweb', minimal_qcontext=True),
|
||||
'body_html': view._render({'channel': self, 'partner': partner}, engine='ir.qweb', minimal_qcontext=True),
|
||||
'subject': _("Guidelines of channel %s") % self.name,
|
||||
'recipient_ids': [(4, partner.id)]
|
||||
}
|
||||
|
||||
@@ -935,7 +935,7 @@ class Message(models.Model):
|
||||
MailThread.message_notify(
|
||||
partner_ids=moderator.partner_id.ids,
|
||||
subject=_('Message are pending moderation'), # tocheck: target language
|
||||
body=template.render({'record': moderator.partner_id}, engine='ir.qweb', minimal_qcontext=True),
|
||||
body=template._render({'record': moderator.partner_id}, engine='ir.qweb', minimal_qcontext=True),
|
||||
email_from=moderator.company_id.catchall_formatted or moderator.company_id.email_formatted,
|
||||
)
|
||||
|
||||
|
||||
@@ -196,9 +196,9 @@ class MailTemplate(models.Model):
|
||||
report_service = report.report_name
|
||||
|
||||
if report.report_type in ['qweb-html', 'qweb-pdf']:
|
||||
result, format = report.render_qweb_pdf([res_id])
|
||||
result, format = report._render_qweb_pdf([res_id])
|
||||
else:
|
||||
res = report.render([res_id])
|
||||
res = report._render([res_id])
|
||||
if not res:
|
||||
raise UserError(_('Unsupported report type %s found.') % report.report_type)
|
||||
result, format = res
|
||||
@@ -267,7 +267,7 @@ class MailTemplate(models.Model):
|
||||
'company': 'company_id' in record and record['company_id'] or self.env.company,
|
||||
'record': record,
|
||||
}
|
||||
body = template.render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
body = template._render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
values['body_html'] = self.env['mail.render.mixin']._replace_local_links(body)
|
||||
mail = self.env['mail.mail'].sudo().create(values)
|
||||
|
||||
|
||||
@@ -1015,7 +1015,7 @@ class MailThread(models.AbstractModel):
|
||||
# check it does not directly contact catchall
|
||||
if catchall_alias and all(email_localpart == catchall_alias for email_localpart in email_to_localparts):
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: direct write to catchall, bounce', email_from, email_to, message_id)
|
||||
body = self.env.ref('mail.mail_bounce_catchall').render({
|
||||
body = self.env.ref('mail.mail_bounce_catchall')._render({
|
||||
'message': message,
|
||||
}, engine='ir.qweb')
|
||||
self._routing_create_bounce_email(email_from, body, message, references=message_id, reply_to=self.env.company.email)
|
||||
@@ -1968,7 +1968,7 @@ class MailThread(models.AbstractModel):
|
||||
return
|
||||
for record in self:
|
||||
values['object'] = record
|
||||
rendered_template = views.render(values, engine='ir.qweb', minimal_qcontext=True)
|
||||
rendered_template = views._render(values, engine='ir.qweb', minimal_qcontext=True)
|
||||
kwargs['body'] = rendered_template
|
||||
record.message_post_with_template(False, **kwargs)
|
||||
|
||||
@@ -2294,7 +2294,7 @@ class MailThread(models.AbstractModel):
|
||||
# {actions, button_access, has_button_access, recipients}
|
||||
|
||||
if base_template:
|
||||
mail_body = base_template.render(render_values, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_body = base_template._render(render_values, engine='ir.qweb', minimal_qcontext=True)
|
||||
else:
|
||||
mail_body = message.body
|
||||
mail_body = self.env['mail.render.mixin']._replace_local_links(mail_body)
|
||||
@@ -2886,7 +2886,7 @@ class MailThread(models.AbstractModel):
|
||||
'model_description': model_description,
|
||||
'access_link': self._notify_get_action_link('view'),
|
||||
}
|
||||
assignation_msg = view.render(values, engine='ir.qweb', minimal_qcontext=True)
|
||||
assignation_msg = view._render(values, engine='ir.qweb', minimal_qcontext=True)
|
||||
assignation_msg = self.env['mail.render.mixin']._replace_local_links(assignation_msg)
|
||||
record.message_notify(
|
||||
subject=_('You have been assigned to %s') % record.display_name,
|
||||
|
||||
@@ -77,7 +77,7 @@ class MailComposeMessage(models.TransientModel):
|
||||
'email': mail_to,
|
||||
}
|
||||
if mail_values.get('body_html') and mass_mail_layout:
|
||||
mail_values['body_html'] = mass_mail_layout.render({'body': mail_values['body_html']}, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_values['body_html'] = mass_mail_layout._render({'body': mail_values['body_html']}, engine='ir.qweb', minimal_qcontext=True)
|
||||
# propagate ignored state to trace when still-born
|
||||
if mail_values.get('state') == 'cancel':
|
||||
trace_vals['ignored'] = fields.Datetime.now()
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestMassMailing(models.TransientModel):
|
||||
'reply_to': mailing.reply_to,
|
||||
'email_to': test_mail,
|
||||
'subject': mailing.subject,
|
||||
'body_html': mass_mail_layout.render({'body': body}, engine='ir.qweb', minimal_qcontext=True),
|
||||
'body_html': mass_mail_layout._render({'body': body}, engine='ir.qweb', minimal_qcontext=True),
|
||||
'notification': True,
|
||||
'mailing_id': mailing.id,
|
||||
'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids],
|
||||
|
||||
@@ -1190,7 +1190,7 @@ class MrpProduction(models.Model):
|
||||
'impacted_pickings': False,
|
||||
'cancel': cancel
|
||||
}
|
||||
return self.env.ref('mrp.exception_on_mo').render(values=values)
|
||||
return self.env.ref('mrp.exception_on_mo')._render(values=values)
|
||||
|
||||
documents = self.env['stock.picking']._log_activity_get_documents(moves_modification, 'move_dest_ids', 'DOWN', _keys_in_sorted, _keys_in_groupby)
|
||||
documents = self.env['stock.picking']._less_quantities_than_expected_add_documents(moves_modification, documents)
|
||||
@@ -1216,7 +1216,7 @@ class MrpProduction(models.Model):
|
||||
'impacted_object': impacted_object,
|
||||
'cancel': cancel
|
||||
}
|
||||
return self.env.ref('mrp.exception_on_mo').render(values=values)
|
||||
return self.env.ref('mrp.exception_on_mo')._render(values=values)
|
||||
|
||||
self.env['stock.picking']._log_activity(_render_note_exception_quantity_mo, documents)
|
||||
|
||||
|
||||
@@ -44,13 +44,13 @@ class ReportBomStructure(models.AbstractModel):
|
||||
res['lines']['report_type'] = 'html'
|
||||
res['lines']['report_structure'] = 'all'
|
||||
res['lines']['has_attachments'] = res['lines']['attachments'] or any(component['attachments'] for component in res['lines']['components'])
|
||||
res['lines'] = self.env.ref('mrp.report_mrp_bom').render({'data': res['lines']})
|
||||
res['lines'] = self.env.ref('mrp.report_mrp_bom')._render({'data': res['lines']})
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def get_bom(self, bom_id=False, product_id=False, line_qty=False, line_id=False, level=False):
|
||||
lines = self._get_bom(bom_id=bom_id, product_id=product_id, line_qty=line_qty, line_id=line_id, level=level)
|
||||
return self.env.ref('mrp.report_mrp_bom_line').render({'data': lines})
|
||||
return self.env.ref('mrp.report_mrp_bom_line')._render({'data': lines})
|
||||
|
||||
@api.model
|
||||
def get_operations(self, bom_id=False, qty=0, level=0):
|
||||
@@ -61,7 +61,7 @@ class ReportBomStructure(models.AbstractModel):
|
||||
'currency': self.env.company.currency_id,
|
||||
'operations': lines,
|
||||
}
|
||||
return self.env.ref('mrp.report_mrp_operation_line').render({'data': values})
|
||||
return self.env.ref('mrp.report_mrp_operation_line')._render({'data': values})
|
||||
|
||||
@api.model
|
||||
def _get_report_data(self, bom_id, searchQty=0, searchVariant=False):
|
||||
|
||||
@@ -478,7 +478,7 @@ class PaymentAcquirer(models.Model):
|
||||
})
|
||||
|
||||
_logger.info('payment.acquirer.render: <%s> values rendered for form payment:\n%s', self.provider, pprint.pformat(values))
|
||||
return self.view_template_id.render(values, engine='ir.qweb')
|
||||
return self.view_template_id._render(values, engine='ir.qweb')
|
||||
|
||||
def get_s2s_form_xml_id(self):
|
||||
if self.registration_view_template_id:
|
||||
|
||||
@@ -194,7 +194,7 @@ class TxPaypal(models.Model):
|
||||
if not self.acquirer_id.paypal_pdt_token and not self.acquirer_id.paypal_seller_account and status in ['Completed', 'Processed', 'Pending']:
|
||||
template = self.env.ref('payment_paypal.mail_template_paypal_invite_user_to_configure', False)
|
||||
if template:
|
||||
render_template = template.render({
|
||||
render_template = template._render({
|
||||
'acquirer': self.acquirer_id,
|
||||
}, engine='ir.qweb')
|
||||
mail_body = self.env['mail.render.mixin']._replace_local_links(render_template)
|
||||
|
||||
@@ -61,6 +61,6 @@ class PosController(http.Controller):
|
||||
@http.route('/pos/sale_details_report', type='http', auth='user')
|
||||
def print_sale_details(self, date_start=False, date_stop=False, **kw):
|
||||
r = request.env['report.point_of_sale.report_saledetails']
|
||||
pdf, _ = request.env.ref('point_of_sale.sale_details_report').with_context(date_start=date_start, date_stop=date_stop).render_qweb_pdf(r)
|
||||
pdf, _ = request.env.ref('point_of_sale.sale_details_report').with_context(date_start=date_start, date_stop=date_stop)._render_qweb_pdf(r)
|
||||
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
|
||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||
|
||||
@@ -99,7 +99,7 @@ class PosConfig(models.Model):
|
||||
|
||||
def _compute_customer_html(self):
|
||||
for config in self:
|
||||
config.customer_facing_display_html = self.env['ir.qweb'].render('point_of_sale.customer_facing_display_html')
|
||||
config.customer_facing_display_html = self.env['ir.qweb']._render('point_of_sale.customer_facing_display_html')
|
||||
|
||||
name = fields.Char(string='Point of Sale', index=True, required=True, help="An internal identification of the point of sale.")
|
||||
is_installed_account_accountant = fields.Boolean(string="Is the Full Accounting Installed",
|
||||
|
||||
@@ -542,7 +542,7 @@ class PosOrder(models.Model):
|
||||
}
|
||||
|
||||
if self.mapped('account_move'):
|
||||
report = self.env.ref('point_of_sale.pos_invoice_report').render_qweb_pdf(self.ids[0])
|
||||
report = self.env.ref('point_of_sale.pos_invoice_report')._render_qweb_pdf(self.ids[0])
|
||||
filename = name + '.pdf'
|
||||
attachment = self.env['ir.attachment'].create({
|
||||
'name': filename,
|
||||
|
||||
@@ -371,7 +371,7 @@ class CustomerPortal(Controller):
|
||||
if not isinstance(report_sudo, type(request.env['ir.actions.report'])):
|
||||
raise UserError(_("%s is not the reference of a report") % report_ref)
|
||||
|
||||
method_name = 'render_qweb_%s' % (report_type)
|
||||
method_name = '_render_qweb_%s' % (report_type)
|
||||
report = getattr(report_sudo, method_name)([model.id], data={'report_type': report_type})[0]
|
||||
reporthttpheaders = [
|
||||
('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'),
|
||||
|
||||
@@ -46,7 +46,7 @@ class MercuryTransaction(models.Model):
|
||||
data['memo'] = "Odoo " + service.common.exp_version()['server_version']
|
||||
|
||||
def _do_request(self, template, data):
|
||||
xml_transaction = self.env.ref(template).render(data).decode()
|
||||
xml_transaction = self.env.ref(template)._render(data).decode()
|
||||
|
||||
if not data['merchant_id'] or not data['merchant_pwd']:
|
||||
return "not setup"
|
||||
|
||||
@@ -22,7 +22,7 @@ class report_product_pricelist(models.AbstractModel):
|
||||
self.env.context.get('pricelist_id'),
|
||||
self.env.context.get('quantities') or [1]
|
||||
)
|
||||
return self.env.ref('product.report_pricelist_page').render(render_values)
|
||||
return self.env.ref('product.report_pricelist_page')._render(render_values)
|
||||
|
||||
def _get_report_data(self, active_model, active_ids, pricelist_id, quantities, report_type='html'):
|
||||
products = []
|
||||
|
||||
@@ -185,7 +185,7 @@ class PurchaseOrder(models.Model):
|
||||
'order_exceptions': order_exceptions.values(),
|
||||
'impacted_pickings': impacted_pickings,
|
||||
}
|
||||
return self.env.ref('purchase_stock.exception_on_po').render(values=values)
|
||||
return self.env.ref('purchase_stock.exception_on_po')._render(values=values)
|
||||
|
||||
documents = self.env['stock.picking']._log_activity_get_documents(purchase_order_lines_quantities, 'move_ids', 'DOWN', _keys_in_sorted, _keys_in_groupby)
|
||||
filtered_documents = {}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Rating(http.Controller):
|
||||
}
|
||||
rating.write({'rating': rate, 'consumed': True})
|
||||
lang = rating.partner_id.lang or get_lang(request.env).code
|
||||
return request.env['ir.ui.view'].with_context(lang=lang).render_template('rating.rating_external_page_submit', {
|
||||
return request.env['ir.ui.view'].with_context(lang=lang)._render_template('rating.rating_external_page_submit', {
|
||||
'rating': rating, 'token': token,
|
||||
'rate_names': rate_names, 'rate': rate
|
||||
})
|
||||
@@ -60,7 +60,7 @@ class Rating(http.Controller):
|
||||
record_sudo = request.env[rating.res_model].sudo().browse(rating.res_id)
|
||||
record_sudo.rating_apply(rate, token=token, feedback=kwargs.get('feedback'))
|
||||
lang = rating.partner_id.lang or get_lang(request.env).code
|
||||
return request.env['ir.ui.view'].with_context(lang=lang).render_template('rating.rating_external_page_view', {
|
||||
return request.env['ir.ui.view'].with_context(lang=lang)._render_template('rating.rating_external_page_view', {
|
||||
'web_base_url': request.env['ir.config_parameter'].sudo().get_param('web.base.url'),
|
||||
'rating': rating,
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ class OnboardingController(http.Controller):
|
||||
return {}
|
||||
|
||||
return {
|
||||
'html': request.env.ref('sale.sale_quotation_onboarding_panel').render({
|
||||
'html': request.env.ref('sale.sale_quotation_onboarding_panel')._render({
|
||||
'company': company,
|
||||
'state': company.get_and_update_sale_quotation_onboarding_state()
|
||||
})
|
||||
|
||||
@@ -231,7 +231,7 @@ class CustomerPortal(CustomerPortal):
|
||||
order_sudo.action_confirm()
|
||||
order_sudo._send_order_confirmation_mail()
|
||||
|
||||
pdf = request.env.ref('sale.action_report_saleorder').sudo().render_qweb_pdf([order_sudo.id])[0]
|
||||
pdf = request.env.ref('sale.action_report_saleorder').sudo()._render_qweb_pdf([order_sudo.id])[0]
|
||||
|
||||
_message_post_helper(
|
||||
'sale.order', order_sudo.id, _('Order signed by %s') % (name,),
|
||||
|
||||
@@ -28,7 +28,7 @@ class CustomerPortal(CustomerPortal):
|
||||
'order_line_price_subtotal': format_price(order_line.price_subtotal)
|
||||
})
|
||||
try:
|
||||
results['order_totals_table'] = request.env['ir.ui.view'].render_template('sale.sale_order_portal_content_totals_table', {'sale_order': order_sudo})
|
||||
results['order_totals_table'] = request.env['ir.ui.view']._render_template('sale.sale_order_portal_content_totals_table', {'sale_order': order_sudo})
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -58,7 +58,7 @@ class CustomerPortal(CustomerPortal):
|
||||
results = self._get_portal_order_details(order_sudo)
|
||||
results.update({
|
||||
'unlink': True,
|
||||
'sale_template': request.env['ir.ui.view'].render_template('sale.sale_order_portal_content', {
|
||||
'sale_template': request.env['ir.ui.view']._render_template('sale.sale_order_portal_content', {
|
||||
'sale_order': order_sudo,
|
||||
'report_type': "html"
|
||||
}),
|
||||
@@ -84,7 +84,7 @@ class CustomerPortal(CustomerPortal):
|
||||
|
||||
option_sudo.add_option_to_order()
|
||||
results = self._get_portal_order_details(order_sudo)
|
||||
results['sale_template'] = request.env['ir.ui.view'].render_template("sale.sale_order_portal_content", {
|
||||
results['sale_template'] = request.env['ir.ui.view']._render_template("sale.sale_order_portal_content", {
|
||||
'sale_order': option_sudo.order_id,
|
||||
'report_type': "html"
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ class ProductConfiguratorController(http.Controller):
|
||||
if pricelist:
|
||||
product_template = product_template.with_context(pricelist=pricelist.id, partner=request.env.user.partner_id)
|
||||
|
||||
return request.env['ir.ui.view'].render_template("sale_product_configurator.configure", {
|
||||
return request.env['ir.ui.view']._render_template("sale_product_configurator.configure", {
|
||||
'product': product_template,
|
||||
'pricelist': pricelist,
|
||||
'add_qty': add_qty,
|
||||
@@ -48,7 +48,7 @@ class ProductConfiguratorController(http.Controller):
|
||||
# They are kept in the context since they are not linked to this product variant
|
||||
parent_combination |= product.env.context.get('no_variant_attribute_values')
|
||||
|
||||
return request.env['ir.ui.view'].render_template("sale_product_configurator.optional_product_items", {
|
||||
return request.env['ir.ui.view']._render_template("sale_product_configurator.optional_product_items", {
|
||||
'product': product,
|
||||
'parent_name': product.name,
|
||||
'parent_combination': parent_combination,
|
||||
@@ -72,7 +72,7 @@ class ProductConfiguratorController(http.Controller):
|
||||
if no_variant_attribute_values:
|
||||
product = product.with_context(no_variant_attribute_values=no_variant_attribute_values)
|
||||
|
||||
return request.env['ir.ui.view'].render_template("sale_product_configurator.optional_products_modal", {
|
||||
return request.env['ir.ui.view']._render_template("sale_product_configurator.optional_products_modal", {
|
||||
'product': product,
|
||||
'combination': combination,
|
||||
'add_qty': add_qty,
|
||||
|
||||
@@ -58,7 +58,7 @@ class PurchaseOrder(models.Model):
|
||||
sale_to_notify_map[sale_order] |= purchase_line
|
||||
|
||||
for sale_order, purchase_order_lines in sale_to_notify_map.items():
|
||||
sale_order.activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
sale_order._activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
user_id=sale_order.user_id.id or self.env.uid,
|
||||
views_or_xmlid='sale_purchase.exception_sale_on_purchase_cancellation',
|
||||
render_context={
|
||||
|
||||
@@ -70,7 +70,7 @@ class SaleOrder(models.Model):
|
||||
purchase_to_notify_map[purchase_line.order_id] |= purchase_line.sale_line_id
|
||||
|
||||
for purchase_order, sale_order_lines in purchase_to_notify_map.items():
|
||||
purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
purchase_order._activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
user_id=purchase_order.user_id.id or self.env.uid,
|
||||
views_or_xmlid='sale_purchase.exception_purchase_on_sale_cancellation',
|
||||
render_context={
|
||||
@@ -161,7 +161,7 @@ class SaleOrderLine(models.Model):
|
||||
'sale_orders': sale_lines.mapped('order_id'),
|
||||
'origin_values': origin_values,
|
||||
}
|
||||
purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
purchase_order._activity_schedule_with_view('mail.mail_activity_data_warning',
|
||||
user_id=purchase_order.user_id.id or self.env.uid,
|
||||
views_or_xmlid='sale_purchase.exception_purchase_on_sale_quantity_decreased',
|
||||
render_context=render_context)
|
||||
|
||||
@@ -30,7 +30,7 @@ class SaleStockPortal(CustomerPortal):
|
||||
return request.redirect('/my')
|
||||
|
||||
# print report as sudo, since it require access to product, taxes, payment term etc.. and portal does not have those access rights.
|
||||
pdf = request.env.ref('stock.action_report_delivery').sudo().render_qweb_pdf([picking_sudo.id])[0]
|
||||
pdf = request.env.ref('stock.action_report_delivery').sudo()._render_qweb_pdf([picking_sudo.id])[0]
|
||||
pdfhttpheaders = [
|
||||
('Content-Type', 'application/pdf'),
|
||||
('Content-Length', len(pdf)),
|
||||
|
||||
@@ -239,7 +239,7 @@ class SaleOrder(models.Model):
|
||||
'impacted_pickings': impacted_pickings,
|
||||
'cancel': cancel
|
||||
}
|
||||
return self.env.ref('sale_stock.exception_on_so').render(values=values)
|
||||
return self.env.ref('sale_stock.exception_on_so')._render(values=values)
|
||||
|
||||
self.env['stock.picking']._log_activity(_render_note_exception_quantity_so, documents)
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class StockPicking(models.Model):
|
||||
'origin_picking': origin_picking,
|
||||
'moves_information': moves_information.values(),
|
||||
}
|
||||
return self.env.ref('sale_stock.exception_on_picking').render(values=values)
|
||||
return self.env.ref('sale_stock.exception_on_picking')._render(values=values)
|
||||
|
||||
documents = self._log_activity_get_documents(moves, 'sale_line_id', 'DOWN', _keys_in_sorted, _keys_in_groupby)
|
||||
self._log_activity(_render_note_exception_quantity, documents)
|
||||
|
||||
@@ -130,7 +130,7 @@ class SnailmailLetter(models.Model):
|
||||
else:
|
||||
report_name = 'Document'
|
||||
filename = "%s.%s" % (report_name, "pdf")
|
||||
pdf_bin, _ = report.with_context(snailmail_layout=not self.cover).render_qweb_pdf(self.res_id)
|
||||
pdf_bin, _ = report.with_context(snailmail_layout=not self.cover)._render_qweb_pdf(self.res_id)
|
||||
attachment = self.env['ir.attachment'].create({
|
||||
'name': filename,
|
||||
'datas': base64.b64encode(pdf_bin),
|
||||
|
||||
@@ -1163,7 +1163,7 @@ class Picking(models.Model):
|
||||
'moves_information': rendering_context.values(),
|
||||
'impacted_pickings': impacted_pickings,
|
||||
}
|
||||
return self.env.ref('stock.exception_on_picking').render(values=values)
|
||||
return self.env.ref('stock.exception_on_picking')._render(values=values)
|
||||
|
||||
documents = self._log_activity_get_documents(moves, 'move_dest_ids', 'DOWN', _keys_in_sorted, _keys_in_groupby)
|
||||
documents = self._less_quantities_than_expected_add_documents(moves, documents)
|
||||
@@ -1329,7 +1329,7 @@ class Picking(models.Model):
|
||||
def _attach_sign(self):
|
||||
""" Render the delivery report in pdf and attach it to the picking in `self`. """
|
||||
self.ensure_one()
|
||||
report = self.env.ref('stock.action_report_delivery').render_qweb_pdf(self.id)
|
||||
report = self.env.ref('stock.action_report_delivery')._render_qweb_pdf(self.id)
|
||||
filename = "%s_signed_delivery_slip" % self.name
|
||||
if self.partner_id:
|
||||
message = _('Order signed by %s') % (self.partner_id.name)
|
||||
|
||||
@@ -212,13 +212,13 @@ class MrpStockReport(models.TransientModel):
|
||||
if not config['test_enable']:
|
||||
context['commit_assetsbundle'] = True
|
||||
|
||||
body = self.env['ir.ui.view'].with_context(context).render_template(
|
||||
body = self.env['ir.ui.view'].with_context(context)._render_template(
|
||||
"stock.report_stock_inventory_print",
|
||||
values=dict(rcontext, lines=lines, report=self, context=self),
|
||||
)
|
||||
|
||||
header = self.env['ir.actions.report'].render_template("web.internal_layout", values=rcontext)
|
||||
header = self.env['ir.actions.report'].render_template("web.minimal_layout", values=dict(rcontext, subst=True, body=header))
|
||||
header = self.env['ir.actions.report']._render_template("web.internal_layout", values=rcontext)
|
||||
header = self.env['ir.actions.report']._render_template("web.minimal_layout", values=dict(rcontext, subst=True, body=header))
|
||||
|
||||
return self.env['ir.actions.report']._run_wkhtmltopdf(
|
||||
[body],
|
||||
@@ -232,7 +232,7 @@ class MrpStockReport(models.TransientModel):
|
||||
rcontext = {}
|
||||
context = dict(self.env.context)
|
||||
rcontext['lines'] = self.with_context(context).get_lines()
|
||||
result['html'] = self.env.ref('stock.report_stock_inventory').render(rcontext)
|
||||
result['html'] = self.env.ref('stock.report_stock_inventory')._render(rcontext)
|
||||
return result
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -24,7 +24,7 @@ class TestReports(TransactionCase):
|
||||
report = self.env.ref('stock.label_lot_template')
|
||||
target = b'\n\n\n^XA\n^FO100,50\n^A0N,44,33^FD[C418]Mellohi^FS\n^FO100,100\n^A0N,44,33^FDLN/SN:Volume-Beta^FS\n^FO100,150^BY3\n^BCN,100,Y,N,N\n^FDVolume-Beta^FS\n^XZ\n\n\n'
|
||||
|
||||
rendering, qweb_type = report.render_qweb_text(lot1.id)
|
||||
rendering, qweb_type = report._render_qweb_text(lot1.id)
|
||||
self.assertEqual(target, rendering.replace(b' ', b''), 'The rendering is not good')
|
||||
self.assertEqual(qweb_type, 'text', 'the report type is not good')
|
||||
|
||||
|
||||
@@ -318,22 +318,22 @@ class Survey(http.Controller):
|
||||
|
||||
survey_content = False
|
||||
if answer_sudo.state == 'done':
|
||||
survey_content = request.env.ref('survey.survey_fill_form_done').render(survey_data)
|
||||
survey_content = request.env.ref('survey.survey_fill_form_done')._render(survey_data)
|
||||
else:
|
||||
survey_content = request.env.ref('survey.survey_fill_form_in_progress').render(survey_data)
|
||||
survey_content = request.env.ref('survey.survey_fill_form_in_progress')._render(survey_data)
|
||||
|
||||
survey_progress = False
|
||||
if answer_sudo.state == 'in_progress' and not survey_data.get('question', request.env['survey.question']).is_page:
|
||||
if survey_sudo.questions_layout == 'page_per_section':
|
||||
page_ids = survey_sudo.page_ids.ids
|
||||
survey_progress = request.env.ref('survey.survey_progression').render({
|
||||
survey_progress = request.env.ref('survey.survey_progression')._render({
|
||||
'survey': survey_sudo,
|
||||
'page_ids': page_ids,
|
||||
'page_number': page_ids.index(survey_data['page'].id) + (1 if survey_sudo.progression_mode == 'number' else 0)
|
||||
})
|
||||
elif survey_sudo.questions_layout == 'page_per_question':
|
||||
page_ids = survey_sudo.question_ids.ids
|
||||
survey_progress = request.env.ref('survey.survey_progression').render({
|
||||
survey_progress = request.env.ref('survey.survey_progression')._render({
|
||||
'survey': survey_sudo,
|
||||
'page_ids': page_ids,
|
||||
'page_number': page_ids.index(survey_data['question'].id)
|
||||
@@ -342,7 +342,7 @@ class Survey(http.Controller):
|
||||
return {
|
||||
'survey_content': survey_content,
|
||||
'survey_progress': survey_progress,
|
||||
'survey_navigation': request.env.ref('survey.survey_navigation').render(survey_data),
|
||||
'survey_navigation': request.env.ref('survey.survey_navigation')._render(survey_data),
|
||||
}
|
||||
|
||||
@http.route('/survey/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
@@ -629,7 +629,7 @@ class Survey(http.Controller):
|
||||
return request.render('survey.survey_page_statistics', template_values)
|
||||
|
||||
def _generate_report(self, user_input, download=True):
|
||||
report = request.env.ref('survey.certification_report').sudo().render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0]
|
||||
report = request.env.ref('survey.certification_report').sudo()._render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0]
|
||||
|
||||
report_content_disposition = content_disposition('Certification.pdf')
|
||||
if not download:
|
||||
|
||||
@@ -108,7 +108,7 @@ class UserInputSession(http.Controller):
|
||||
|
||||
template_values = self._prepare_manage_session_values(survey)
|
||||
template_values['is_rpc_call'] = True
|
||||
return request.env.ref('survey.user_input_session_manage_content').render(template_values)
|
||||
return request.env.ref('survey.user_input_session_manage_content')._render(template_values)
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -146,7 +146,7 @@ class UserInputSession(http.Controller):
|
||||
# no open session
|
||||
return ''
|
||||
|
||||
return request.env.ref('survey.user_input_session_leaderboard').render({
|
||||
return request.env.ref('survey.user_input_session_leaderboard')._render({
|
||||
'animate_width': True,
|
||||
'leaderboard': survey._prepare_leaderboard_values()
|
||||
})
|
||||
|
||||
@@ -234,7 +234,7 @@ class SurveyInvite(models.TransientModel):
|
||||
'model_description': self.env['ir.model']._get('survey.survey').display_name,
|
||||
'company': self.env.company,
|
||||
}
|
||||
body = template.render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
body = template._render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_values['body_html'] = self.env['mail.render.mixin']._replace_local_links(body)
|
||||
|
||||
return self.env['mail.mail'].sudo().create(mail_values)
|
||||
|
||||
@@ -1974,14 +1974,14 @@ class ReportController(http.Controller):
|
||||
del data['context']['lang']
|
||||
context.update(data['context'])
|
||||
if converter == 'html':
|
||||
html = report.with_context(context).render_qweb_html(docids, data=data)[0]
|
||||
html = report.with_context(context)._render_qweb_html(docids, data=data)[0]
|
||||
return request.make_response(html)
|
||||
elif converter == 'pdf':
|
||||
pdf = report.with_context(context).render_qweb_pdf(docids, data=data)[0]
|
||||
pdf = report.with_context(context)._render_qweb_pdf(docids, data=data)[0]
|
||||
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
|
||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||
elif converter == 'text':
|
||||
text = report.with_context(context).render_qweb_text(docids, data=data)[0]
|
||||
text = report.with_context(context)._render_qweb_text(docids, data=data)[0]
|
||||
texthttpheaders = [('Content-Type', 'text/plain'), ('Content-Length', len(text))]
|
||||
return request.make_response(text, headers=texthttpheaders)
|
||||
else:
|
||||
|
||||
@@ -192,7 +192,7 @@ class Base(models.AbstractModel):
|
||||
@api.model
|
||||
def qweb_render_view(self, view_id, domain):
|
||||
assert view_id
|
||||
return self.env['ir.qweb'].render(
|
||||
return self.env['ir.qweb']._render(
|
||||
view_id, {
|
||||
**self.env['ir.ui.view']._prepare_qcontext(),
|
||||
**self._qweb_prepare_qcontext(view_id, domain),
|
||||
@@ -526,7 +526,7 @@ class ResCompany(models.Model):
|
||||
# One bundle for everyone, so this method
|
||||
# necessarily updates the style for every company at once
|
||||
company_ids = self.sudo().search([])
|
||||
company_styles = template_style.render({
|
||||
company_styles = template_style._render({
|
||||
'company_ids': company_ids,
|
||||
})
|
||||
return base64.b64encode((company_styles))
|
||||
|
||||
@@ -456,7 +456,7 @@ var loadAsset = (function () {
|
||||
if (tplRoute === '/web/dataset/call_kw/') {
|
||||
Object.assign(params, {
|
||||
model: 'ir.ui.view',
|
||||
method: 'render_template',
|
||||
method: 'render_public_asset',
|
||||
});
|
||||
}
|
||||
cache[xmlId] = rpc(tplRoute, params).then(function (xml) {
|
||||
|
||||
@@ -476,4 +476,4 @@ class Web_Editor(http.Controller):
|
||||
if request.env.user._is_public() \
|
||||
and xmlid in request.env['web_editor.assets']._get_public_asset_xmlids():
|
||||
View = View.sudo()
|
||||
return View.render_template(xmlid, {k: values[k] for k in values if k in trusted_value_keys})
|
||||
return View._render_template(xmlid, {k: values[k] for k in values if k in trusted_value_keys})
|
||||
|
||||
@@ -15,7 +15,7 @@ _logger = logging.getLogger(__name__)
|
||||
class IrUiView(models.Model):
|
||||
_inherit = 'ir.ui.view'
|
||||
|
||||
def render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
if values and values.get('editable'):
|
||||
try:
|
||||
self.check_access_rights('write')
|
||||
@@ -23,7 +23,7 @@ class IrUiView(models.Model):
|
||||
except AccessError:
|
||||
values['editable'] = False
|
||||
|
||||
return super(IrUiView, self).render(values=values, engine=engine, minimal_qcontext=minimal_qcontext)
|
||||
return super(IrUiView, self)._render(values=values, engine=engine, minimal_qcontext=minimal_qcontext)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Save from html
|
||||
|
||||
@@ -1034,7 +1034,7 @@ var SnippetsMenu = Widget.extend({
|
||||
}
|
||||
this._defLoadSnippets = this._rpc({
|
||||
model: 'ir.ui.view',
|
||||
method: 'render_template',
|
||||
method: 'render_public_asset',
|
||||
args: [this.options.snippets, {}],
|
||||
kwargs: {
|
||||
context: this.options.context,
|
||||
|
||||
@@ -98,7 +98,7 @@ MockServer.include({
|
||||
if (args.method === 'read_template' && args.args[0] === "web_editor.colorpicker") {
|
||||
return COLOR_PICKER_TEMPLATE;
|
||||
}
|
||||
if (args.method === 'render_template' && args.args[0] === "web_editor.snippets") {
|
||||
if (args.method === 'render_public_asset' && args.args[0] === "web_editor.snippets") {
|
||||
return SNIPPETS_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class TestConvertBack(common.TransactionCase):
|
||||
field_value = 'record.%s' % field
|
||||
e.set('t-field', field_value)
|
||||
|
||||
rendered = self.env['ir.qweb'].render(t, {'record': record})
|
||||
rendered = self.env['ir.qweb']._render(t, {'record': record})
|
||||
|
||||
element = html.fromstring(rendered, parser=html.HTMLParser(encoding='utf-8'))
|
||||
model = 'ir.qweb.field.' + element.get('data-oe-type', '')
|
||||
@@ -197,7 +197,7 @@ class TestConvertBack(common.TransactionCase):
|
||||
field_value = 'record.%s' % field
|
||||
e.set('t-field', field_value)
|
||||
|
||||
rendered = self.env['ir.qweb'].render(t, {'record': record})
|
||||
rendered = self.env['ir.qweb']._render(t, {'record': record})
|
||||
element = html.fromstring(rendered, parser=html.HTMLParser(encoding='utf-8'))
|
||||
|
||||
# emulate edition
|
||||
|
||||
@@ -167,15 +167,15 @@ class Website(Home):
|
||||
sitemaps.unlink()
|
||||
|
||||
pages = 0
|
||||
locs = request.website.with_user(request.website.user_id).enumerate_pages()
|
||||
locs = request.website.with_user(request.website.user_id)._enumerate_pages()
|
||||
while True:
|
||||
values = {
|
||||
'locs': islice(locs, 0, LOC_PER_SITEMAP),
|
||||
'url_root': request.httprequest.url_root[:-1],
|
||||
}
|
||||
urls = View.render_template('website.sitemap_locs', values)
|
||||
urls = View._render_template('website.sitemap_locs', values)
|
||||
if urls.strip():
|
||||
content = View.render_template('website.sitemap_xml', {'content': urls})
|
||||
content = View._render_template('website.sitemap_xml', {'content': urls})
|
||||
pages += 1
|
||||
last_sitemap = create_sitemap('/sitemap-%d-%d.xml' % (current_website.id, pages), content)
|
||||
else:
|
||||
@@ -194,7 +194,7 @@ class Website(Home):
|
||||
pages_with_website = ["%d-%d" % (current_website.id, p) for p in range(1, pages + 1)]
|
||||
|
||||
# Sitemaps must be split in several smaller files with a sitemap index
|
||||
content = View.render_template('website.sitemap_index_xml', {
|
||||
content = View._render_template('website.sitemap_index_xml', {
|
||||
'pages': pages_with_website,
|
||||
'url_root': request.httprequest.url_root,
|
||||
})
|
||||
@@ -238,7 +238,7 @@ class Website(Home):
|
||||
matching_urls = set(map(lambda match: match['value'], matching_pages))
|
||||
|
||||
matching_last_modified = []
|
||||
last_modified_pages = current_website.get_website_pages(order='write_date desc', limit=5)
|
||||
last_modified_pages = current_website._get_website_pages(order='write_date desc', limit=5)
|
||||
for url, name in last_modified_pages.mapped(lambda p: (p.url, p.name)):
|
||||
if needle.lower() in name.lower() or needle.lower() in url.lower() and url not in matching_urls:
|
||||
matching_last_modified.append({
|
||||
@@ -474,7 +474,7 @@ class Website(Home):
|
||||
View = request.env['ir.ui.view']
|
||||
res = {}
|
||||
for id_or_xml_id in ids_or_xml_ids:
|
||||
res[id_or_xml_id] = View.render_template(id_or_xml_id, values)
|
||||
res[id_or_xml_id] = View._render_template(id_or_xml_id, values)
|
||||
return res
|
||||
|
||||
@http.route(['/website/update_visitor_timezone'], type='json', auth="public", website=True)
|
||||
|
||||
@@ -323,7 +323,7 @@ class Http(models.AbstractModel):
|
||||
@classmethod
|
||||
def _get_error_html(cls, env, code, values):
|
||||
if code in ('page_404', 'protected_403'):
|
||||
return code.split('_')[1], env['ir.ui.view'].render_template('website.%s' % code, values)
|
||||
return code.split('_')[1], env['ir.ui.view']._render_template('website.%s' % code, values)
|
||||
return super(Http, cls)._get_error_html(env, code, values)
|
||||
|
||||
def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas',
|
||||
|
||||
@@ -384,7 +384,7 @@ class View(models.Model):
|
||||
return False
|
||||
return True
|
||||
|
||||
def render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
""" Render the template. If website is enabled on request, then extend rendering context with website values. """
|
||||
self._handle_visibility(do_raise=True)
|
||||
new_context = dict(self._context)
|
||||
@@ -411,7 +411,7 @@ class View(models.Model):
|
||||
|
||||
if self._context != new_context:
|
||||
self = self.with_context(new_context)
|
||||
return super(View, self).render(values, engine=engine, minimal_qcontext=minimal_qcontext)
|
||||
return super(View, self)._render(values, engine=engine, minimal_qcontext=minimal_qcontext)
|
||||
|
||||
@api.model
|
||||
def _prepare_qcontext(self):
|
||||
|
||||
@@ -760,7 +760,7 @@ class Website(models.Model):
|
||||
return all(p.name in rule._converters for p in params
|
||||
if p.kind in supported_kinds and has_no_default(p))
|
||||
|
||||
def enumerate_pages(self, query_string=None, force=False):
|
||||
def _enumerate_pages(self, query_string=None, force=False):
|
||||
""" Available pages in the website/CMS. This is mostly used for links
|
||||
generation and can be overridden by modules setting up new HTML
|
||||
controllers for dynamic pages (e.g. blog).
|
||||
@@ -850,7 +850,7 @@ class Website(models.Model):
|
||||
if query_string:
|
||||
domain += [('url', 'like', query_string)]
|
||||
|
||||
pages = self.get_website_pages(domain)
|
||||
pages = self._get_website_pages(domain)
|
||||
|
||||
for page in pages:
|
||||
record = {'loc': page['url'], 'id': page['id'], 'name': page['name']}
|
||||
@@ -860,7 +860,7 @@ class Website(models.Model):
|
||||
record['lastmod'] = page['write_date'].date()
|
||||
yield record
|
||||
|
||||
def get_website_pages(self, domain=[], order='name', limit=None):
|
||||
def _get_website_pages(self, domain=[], order='name', limit=None):
|
||||
domain += self.get_current_website().website_domain()
|
||||
pages = self.env['website.page'].sudo().search(domain, order=order, limit=limit)
|
||||
return pages
|
||||
@@ -868,7 +868,7 @@ class Website(models.Model):
|
||||
def search_pages(self, needle=None, limit=None):
|
||||
name = slugify(needle, max_length=50, path=True)
|
||||
res = []
|
||||
for page in self.enumerate_pages(query_string=name, force=True):
|
||||
for page in self._enumerate_pages(query_string=name, force=True):
|
||||
res.append(page)
|
||||
if len(res) == limit:
|
||||
break
|
||||
|
||||
@@ -25,7 +25,7 @@ class Menu(models.Model):
|
||||
for menu in self:
|
||||
if menu.is_mega_menu:
|
||||
if not menu.mega_menu_content:
|
||||
default_content = self.env['ir.ui.view'].render_template('website.s_mega_menu_multi_menus')
|
||||
default_content = self.env['ir.ui.view']._render_template('website.s_mega_menu_multi_menus')
|
||||
menu.mega_menu_content = default_content.decode()
|
||||
else:
|
||||
menu.mega_menu_content = False
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestQweb(TransactionCaseWithUserDemo):
|
||||
|
||||
demo_env = self.env(user=demo)
|
||||
|
||||
html = demo_env['ir.qweb'].render('website.test_template', {"user": demo}, website_id= website.id)
|
||||
html = demo_env['ir.qweb']._render('website.test_template', {"user": demo}, website_id= website.id)
|
||||
html = html.strip().decode('utf8')
|
||||
html = re.sub(r'\?unique=[^"]+', '', html).encode('utf8')
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ class TestViewSaving(common.TransactionCase):
|
||||
)
|
||||
self.assertIn(
|
||||
replacement,
|
||||
view.render().decode('utf-8'),
|
||||
view._render().decode('utf-8'),
|
||||
'inline script should not be escaped when rendering'
|
||||
)
|
||||
# common text nodes should be be escaped client side
|
||||
@@ -213,7 +213,7 @@ class TestViewSaving(common.TransactionCase):
|
||||
self.assertIn(replacement, view.arch, 'common text node should not be escaped server side')
|
||||
self.assertIn(
|
||||
replacement,
|
||||
view.render().decode('utf-8').replace(u'&', u'&'),
|
||||
view._render().decode('utf-8').replace(u'&', u'&'),
|
||||
'text node characters wrongly unescaped when rendering'
|
||||
)
|
||||
|
||||
|
||||
@@ -299,4 +299,4 @@ class WebsiteBlog(http.Controller):
|
||||
if domain:
|
||||
dom = expression.AND([dom, domain])
|
||||
posts = request.env['blog.post'].search(dom, limit=limit, order=order)
|
||||
return request.website.viewref(template).render({'posts': posts})
|
||||
return request.website.viewref(template)._render({'posts': posts})
|
||||
|
||||
@@ -258,7 +258,7 @@ class WebsiteEventController(http.Controller):
|
||||
"date": self.get_formated_date(event),
|
||||
"event": event,
|
||||
"url": event.website_url})
|
||||
return request.env['ir.ui.view'].render_template("website_event.country_events_list", result)
|
||||
return request.env['ir.ui.view']._render_template("website_event.country_events_list", result)
|
||||
|
||||
def _process_tickets_form(self, event, form_details):
|
||||
""" Process posted data about ticket order. Generic ticket are supported
|
||||
@@ -302,7 +302,7 @@ class WebsiteEventController(http.Controller):
|
||||
availability_check = False
|
||||
if not tickets:
|
||||
return False
|
||||
return request.env['ir.ui.view'].render_template("website_event.registration_attendee_details", {'tickets': tickets, 'event': event, 'availability_check': availability_check})
|
||||
return request.env['ir.ui.view']._render_template("website_event.registration_attendee_details", {'tickets': tickets, 'event': event, 'availability_check': availability_check})
|
||||
|
||||
def _process_attendees_form(self, event, form_details):
|
||||
""" Process data posted from the attendee details form.
|
||||
|
||||
@@ -42,7 +42,7 @@ class Job(models.Model):
|
||||
|
||||
def _get_default_website_description(self):
|
||||
default_description = self.env["ir.model.data"].xmlid_to_object("website_hr_recruitment.default_website_description")
|
||||
return (default_description.render() if default_description else "")
|
||||
return (default_description._render() if default_description else "")
|
||||
|
||||
website_description = fields.Html('Website description', translate=html_translate, sanitize_attributes=False, default=_get_default_website_description, prefetch=False, sanitize_form=False)
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ class MailGroup(http.Controller):
|
||||
'msg_more_count': message_count - self._replies_per_page,
|
||||
'replies_per_page': self._replies_per_page,
|
||||
}
|
||||
return request.env.ref('website_mail_channel.messages_short').render(values, engine='ir.qweb')
|
||||
return request.env.ref('website_mail_channel.messages_short')._render(values, engine='ir.qweb')
|
||||
|
||||
@http.route("/groups/<int:group_id>/get_alias_info", type='json', auth='public', website=True)
|
||||
def get_alias_info(self, group_id, **post):
|
||||
|
||||
@@ -9,7 +9,7 @@ class MassMailingPopup(models.Model):
|
||||
_description = "Mailing list popup"
|
||||
|
||||
def _default_popup_content(self):
|
||||
return self.env['ir.ui.view'].render_template('website_mass_mailing.s_newsletter_block')
|
||||
return self.env['ir.ui.view']._render_template('website_mass_mailing.s_newsletter_block')
|
||||
|
||||
mailing_list_id = fields.Many2one('mailing.list')
|
||||
website_id = fields.Many2one('website')
|
||||
|
||||
@@ -466,12 +466,12 @@ class WebsiteSale(http.Controller):
|
||||
if not display:
|
||||
return value
|
||||
|
||||
value['website_sale.cart_lines'] = request.env['ir.ui.view'].render_template("website_sale.cart_lines", {
|
||||
value['website_sale.cart_lines'] = request.env['ir.ui.view']._render_template("website_sale.cart_lines", {
|
||||
'website_sale_order': order,
|
||||
'date': fields.Date.today(),
|
||||
'suggested_products': order._cart_accessories()
|
||||
})
|
||||
value['website_sale.short_cart_summary'] = request.env['ir.ui.view'].render_template("website_sale.short_cart_summary", {
|
||||
value['website_sale.short_cart_summary'] = request.env['ir.ui.view']._render_template("website_sale.short_cart_summary", {
|
||||
'website_sale_order': order,
|
||||
})
|
||||
return value
|
||||
@@ -960,7 +960,7 @@ class WebsiteSale(http.Controller):
|
||||
|
||||
return {
|
||||
'recall': order.get_portal_last_transaction().state == 'pending',
|
||||
'message': request.env['ir.ui.view'].render_template("website_sale.payment_confirmation_status", {
|
||||
'message': request.env['ir.ui.view']._render_template("website_sale.payment_confirmation_status", {
|
||||
'order': order
|
||||
})
|
||||
}
|
||||
@@ -1025,7 +1025,7 @@ class WebsiteSale(http.Controller):
|
||||
def print_saleorder(self, **kwargs):
|
||||
sale_order_id = request.session.get('sale_last_order_id')
|
||||
if sale_order_id:
|
||||
pdf, _ = request.env.ref('sale.action_report_saleorder').sudo().render_qweb_pdf([sale_order_id])
|
||||
pdf, _ = request.env.ref('sale.action_report_saleorder').sudo()._render_qweb_pdf([sale_order_id])
|
||||
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', u'%s' % len(pdf))]
|
||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||
else:
|
||||
@@ -1243,7 +1243,7 @@ class WebsiteSale(http.Controller):
|
||||
res_product.update(combination_info)
|
||||
res_product['price'] = FieldMonetary.value_to_html(res_product['price'], monetary_options)
|
||||
if rating:
|
||||
res_product['rating'] = request.env["ir.ui.view"].render_template('portal_rating.rating_widget_stars_static', values={
|
||||
res_product['rating'] = request.env["ir.ui.view"]._render_template('portal_rating.rating_widget_stars_static', values={
|
||||
'rating_avg': product.rating_avg,
|
||||
'rating_count': product.rating_count,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ class WebsiteSaleVariantController(VariantController):
|
||||
kw.pop('pricelist_id')
|
||||
res = self.get_combination_info(product_template_id, product_id, combination, add_qty, request.website.get_current_pricelist(), **kw)
|
||||
|
||||
carousel_view = request.env['ir.ui.view'].render_template('website_sale.shop_product_carousel',
|
||||
carousel_view = request.env['ir.ui.view']._render_template('website_sale.shop_product_carousel',
|
||||
values={
|
||||
'product': request.env['product.template'].browse(res['product_template_id']),
|
||||
'product_variant': request.env['product.product'].browse(res['product_id']),
|
||||
|
||||
@@ -30,7 +30,7 @@ class WebsiteSaleProductComparison(WebsiteSale):
|
||||
prods.mapped('name')
|
||||
for prod in prods:
|
||||
ret[prod.id] = {
|
||||
'render': request.env['ir.ui.view'].render_template(
|
||||
'render': request.env['ir.ui.view']._render_template(
|
||||
"website_sale_comparison.product_product",
|
||||
{'product': prod, 'website': request.website}
|
||||
),
|
||||
|
||||
@@ -858,7 +858,7 @@ class WebsiteSlides(WebsiteProfile):
|
||||
'comment': answer['comment']
|
||||
}) for answer in answer_ids]
|
||||
})
|
||||
return request.env.ref('website_slides.lesson_content_quiz_question').render({
|
||||
return request.env.ref('website_slides.lesson_content_quiz_question')._render({
|
||||
'slide': slide,
|
||||
'question': slide_question,
|
||||
})
|
||||
|
||||
@@ -123,7 +123,7 @@ class SlideChannelInvite(models.TransientModel):
|
||||
'model_description': self.env['ir.model']._get('website_slides.slide_channel').display_name,
|
||||
'company': self.env.company,
|
||||
}
|
||||
body = template.render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
body = template._render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_values['body_html'] = self.env['mail.render.mixin']._replace_local_links(body)
|
||||
|
||||
return mail_values
|
||||
|
||||
@@ -370,7 +370,7 @@ class IrActionsReport(models.Model):
|
||||
# set context language to body language
|
||||
if node.get('data-oe-lang'):
|
||||
layout_with_lang = layout_with_lang.with_context(lang=node.get('data-oe-lang'))
|
||||
body = layout_with_lang.render(dict(subst=False, body=lxml.html.tostring(node), base_url=base_url))
|
||||
body = layout_with_lang._render(dict(subst=False, body=lxml.html.tostring(node), base_url=base_url))
|
||||
bodies.append(body)
|
||||
if node.get('data-oe-model') == self.model:
|
||||
res_ids.append(int(node.get('data-oe-id', 0)))
|
||||
@@ -388,8 +388,8 @@ class IrActionsReport(models.Model):
|
||||
if attribute[0].startswith('data-report-'):
|
||||
specific_paperformat_args[attribute[0]] = attribute[1]
|
||||
|
||||
header = layout.render(dict(subst=True, body=lxml.html.tostring(header_node), base_url=base_url))
|
||||
footer = layout.render(dict(subst=True, body=lxml.html.tostring(footer_node), base_url=base_url))
|
||||
header = layout._render(dict(subst=True, body=lxml.html.tostring(header_node), base_url=base_url))
|
||||
footer = layout._render(dict(subst=True, body=lxml.html.tostring(footer_node), base_url=base_url))
|
||||
|
||||
return bodies, res_ids, header, footer, specific_paperformat_args
|
||||
|
||||
@@ -533,7 +533,7 @@ class IrActionsReport(models.Model):
|
||||
"""
|
||||
return {}
|
||||
|
||||
def render_template(self, template, values=None):
|
||||
def _render_template(self, template, values=None):
|
||||
"""Allow to render a QWeb template python-side. This function returns the 'ir.ui.view'
|
||||
render but embellish it with some variables/methods used in reports.
|
||||
:param values: additional methods/variables used in the rendering
|
||||
@@ -561,7 +561,7 @@ class IrActionsReport(models.Model):
|
||||
website=website,
|
||||
web_base_url=self.env['ir.config_parameter'].sudo().get_param('web.base.url', default=''),
|
||||
)
|
||||
return view_obj.render_template(template, values)
|
||||
return view_obj._render_template(template, values)
|
||||
|
||||
def _post_pdf(self, save_in_attachment, pdf_content=None, res_ids=None):
|
||||
'''Merge the existing attachments by adding one by one the content of the attachments
|
||||
@@ -678,7 +678,7 @@ class IrActionsReport(models.Model):
|
||||
writer.write(result_stream)
|
||||
return result_stream.getvalue()
|
||||
|
||||
def render_qweb_pdf(self, res_ids=None, data=None):
|
||||
def _render_qweb_pdf(self, res_ids=None, data=None):
|
||||
if not data:
|
||||
data = {}
|
||||
data.setdefault('report_type', 'pdf')
|
||||
@@ -686,7 +686,7 @@ class IrActionsReport(models.Model):
|
||||
# In case of test environment without enough workers to perform calls to wkhtmltopdf,
|
||||
# fallback to render_html.
|
||||
if (tools.config['test_enable'] or tools.config['test_file']) and not self.env.context.get('force_report_rendering'):
|
||||
return self.render_qweb_html(res_ids, data=data)
|
||||
return self._render_qweb_html(res_ids, data=data)
|
||||
|
||||
# As the assets are generated during the same transaction as the rendering of the
|
||||
# templates calling them, there is a scenario where the assets are unreachable: when
|
||||
@@ -713,7 +713,7 @@ class IrActionsReport(models.Model):
|
||||
# an asset bundle during the execution of test scenarios. In this case, return
|
||||
# the html version.
|
||||
if isinstance(self.env.cr, TestCursor):
|
||||
return self.with_context(context).render_qweb_html(res_ids, data=data)[0]
|
||||
return self.with_context(context)._render_qweb_html(res_ids, data=data)[0]
|
||||
|
||||
save_in_attachment = OrderedDict()
|
||||
if res_ids:
|
||||
@@ -747,7 +747,7 @@ class IrActionsReport(models.Model):
|
||||
# bypassed
|
||||
raise UserError(_("Unable to find Wkhtmltopdf on this system. The PDF can not be created."))
|
||||
|
||||
html = self.with_context(context).render_qweb_html(res_ids, data=data)[0]
|
||||
html = self.with_context(context)._render_qweb_html(res_ids, data=data)[0]
|
||||
|
||||
# Ensure the current document is utf-8 encoded.
|
||||
html = html.decode('utf-8')
|
||||
@@ -772,22 +772,22 @@ class IrActionsReport(models.Model):
|
||||
return pdf_content, 'pdf'
|
||||
|
||||
@api.model
|
||||
def render_qweb_text(self, docids, data=None):
|
||||
def _render_qweb_text(self, docids, data=None):
|
||||
if not data:
|
||||
data = {}
|
||||
data.setdefault('report_type', 'text')
|
||||
data = self._get_rendering_context(docids, data)
|
||||
return self.render_template(self.report_name, data), 'text'
|
||||
return self._render_template(self.report_name, data), 'text'
|
||||
|
||||
@api.model
|
||||
def render_qweb_html(self, docids, data=None):
|
||||
def _render_qweb_html(self, docids, data=None):
|
||||
"""This method generates and returns html version of a report.
|
||||
"""
|
||||
if not data:
|
||||
data = {}
|
||||
data.setdefault('report_type', 'html')
|
||||
data = self._get_rendering_context(docids, data)
|
||||
return self.render_template(self.report_name, data), 'html'
|
||||
return self._render_template(self.report_name, data), 'html'
|
||||
|
||||
@api.model
|
||||
def _get_rendering_context_model(self):
|
||||
@@ -813,9 +813,9 @@ class IrActionsReport(models.Model):
|
||||
})
|
||||
return data
|
||||
|
||||
def render(self, res_ids, data=None):
|
||||
def _render(self, res_ids, data=None):
|
||||
report_type = self.report_type.lower().replace('-', '_')
|
||||
render_func = getattr(self, 'render_' + report_type, None)
|
||||
render_func = getattr(self, '_render_' + report_type, None)
|
||||
if not render_func:
|
||||
return None
|
||||
return render_func(res_ids, data=data)
|
||||
|
||||
@@ -36,7 +36,7 @@ class IrQWeb(models.AbstractModel, QWeb):
|
||||
_description = 'Qweb'
|
||||
|
||||
@api.model
|
||||
def render(self, id_or_xml_id, values=None, **options):
|
||||
def _render(self, id_or_xml_id, values=None, **options):
|
||||
""" render(id_or_xml_id, values, **options)
|
||||
|
||||
Render the template specified by the given name.
|
||||
@@ -55,7 +55,7 @@ class IrQWeb(models.AbstractModel, QWeb):
|
||||
context = dict(self.env.context, dev_mode='qweb' in tools.config['dev_mode'])
|
||||
context.update(options)
|
||||
|
||||
result = super(IrQWeb, self).render(id_or_xml_id, values=values, **context)
|
||||
result = super(IrQWeb, self)._render(id_or_xml_id, values=values, **context)
|
||||
|
||||
if b'data-pagebreak=' not in result:
|
||||
return result
|
||||
@@ -112,15 +112,16 @@ class IrQWeb(models.AbstractModel, QWeb):
|
||||
pass
|
||||
return super(IrQWeb, self).compile(id_or_xml_id, options=options)
|
||||
|
||||
def load(self, name, options):
|
||||
def _load(self, name, options):
|
||||
lang = options.get('lang', get_lang(self.env).code)
|
||||
env = self.env
|
||||
if lang != env.context.get('lang'):
|
||||
env = env(context=dict(env.context, lang=lang))
|
||||
|
||||
template = env['ir.ui.view'].sudo().read_template(name)
|
||||
view_id = self.env['ir.ui.view'].get_view_id(name)
|
||||
template = env['ir.ui.view'].sudo()._read_template(view_id)
|
||||
|
||||
# QWeb's `read_template` will check if one of the first children of
|
||||
# QWeb's `_read_template` will check if one of the first children of
|
||||
# what we send to it has a "t-name" attribute having `name` as value
|
||||
# to consider it has found it. As it'll never be the case when working
|
||||
# with view ids or children view or children primary views, force it here.
|
||||
@@ -323,7 +324,7 @@ class IrQWeb(models.AbstractModel, QWeb):
|
||||
from odoo.addons.web.controllers.main import module_boot
|
||||
return json.dumps(module_boot())
|
||||
return '[]'
|
||||
template = IrQweb.render(xmlid, {"get_modules_order": get_modules_order})
|
||||
template = IrQweb._render(xmlid, {"get_modules_order": get_modules_order})
|
||||
|
||||
files = []
|
||||
remains = []
|
||||
|
||||
@@ -681,7 +681,7 @@ class Contact(models.AbstractModel):
|
||||
'object': value,
|
||||
'options': options
|
||||
}
|
||||
return self.env['ir.qweb'].render('base.contact', val, **options.get('template_options', dict()))
|
||||
return self.env['ir.qweb']._render('base.contact', val, **options.get('template_options', dict()))
|
||||
|
||||
|
||||
class QwebView(models.AbstractModel):
|
||||
@@ -700,4 +700,4 @@ class QwebView(models.AbstractModel):
|
||||
_logger.warning("%s.%s must be a 'ir.ui.view' model." % (record, field_name))
|
||||
return None
|
||||
|
||||
return pycompat.to_text(view.render(options.get('values', {}), engine='ir.qweb'))
|
||||
return pycompat.to_text(view._render(options.get('values', {}), engine='ir.qweb'))
|
||||
|
||||
@@ -1491,6 +1491,9 @@ actual arch.
|
||||
|
||||
@api.model
|
||||
def read_template(self, xml_id):
|
||||
""" Return a template content based on external id
|
||||
Read access on ir.ui.view required
|
||||
"""
|
||||
return self._read_template(self.get_view_id(xml_id))
|
||||
|
||||
@api.model
|
||||
@@ -1599,16 +1602,23 @@ actual arch.
|
||||
return '%s.%s' % (xmlid['module'], xmlid['name'])
|
||||
|
||||
@api.model
|
||||
def render_template(self, template, values=None, engine='ir.qweb'):
|
||||
return self.browse(self.get_view_id(template)).render(values, engine)
|
||||
def render_public_asset(self, template, values=None):
|
||||
if template not in PUBLIC_ASSETS:
|
||||
_logger.warning("Add the external id %s in global variable PUBLC_ASSSETS to make the arch accessible in RPC", template)
|
||||
raise ValidationError(_("Asset %s not accessible") % template)
|
||||
template = self.browse(self.get_view_id(template))
|
||||
return template.sudo()._render(values, engine="ir.qweb")
|
||||
|
||||
def render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
def _render_template(self, template, values=None, engine='ir.qweb'):
|
||||
return self.browse(self.get_view_id(template))._render(values, engine)
|
||||
|
||||
def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
|
||||
assert isinstance(self.id, int)
|
||||
|
||||
qcontext = dict() if minimal_qcontext else self._prepare_qcontext()
|
||||
qcontext.update(values or {})
|
||||
|
||||
return self.env[engine].render(self.id, qcontext)
|
||||
return self.env[engine]._render(self.id, qcontext)
|
||||
|
||||
@api.model
|
||||
def _prepare_qcontext(self):
|
||||
|
||||
@@ -240,7 +240,7 @@ class QWeb(object):
|
||||
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
|
||||
_name_gen = count()
|
||||
|
||||
def render(self, template, values=None, **options):
|
||||
def _render(self, template, values=None, **options):
|
||||
""" render(template, values, **options)
|
||||
|
||||
Render the template specified by the given name.
|
||||
@@ -351,7 +351,7 @@ class QWeb(object):
|
||||
return (document, template)
|
||||
else:
|
||||
try:
|
||||
document = options.get('load', self.load)(template, options)
|
||||
document = options.get('load', self._load)(template, options)
|
||||
except QWebException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
@@ -375,7 +375,7 @@ class QWeb(object):
|
||||
return (node, document)
|
||||
return (element, document)
|
||||
|
||||
def load(self, template, options):
|
||||
def _load(self, template, options):
|
||||
""" Load a given template. """
|
||||
return template
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class TestQWebTField(TransactionCase):
|
||||
field = etree.Element('span', {'t-field': u'company.name'})
|
||||
company = self.env['res.company'].create({'name': "My Test Company"})
|
||||
|
||||
result = self.engine.render(field, {'company': company})
|
||||
result = self.engine._render(field, {'company': company})
|
||||
self.assertEqual(
|
||||
etree.fromstring(result),
|
||||
etree.fromstring(u'<span data-oe-model="res.company" data-oe-id="%d" '
|
||||
@@ -41,7 +41,7 @@ class TestQWebTField(TransactionCase):
|
||||
s = u"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"
|
||||
company = self.env['res.company'].create({'name': s})
|
||||
|
||||
result = self.engine.render(field, {'company': company})
|
||||
result = self.engine._render(field, {'company': company})
|
||||
self.assertEqual(
|
||||
etree.fromstring(result),
|
||||
etree.fromstring(u'<span data-oe-model="res.company" data-oe-id="%d" '
|
||||
@@ -56,13 +56,13 @@ class TestQWebTField(TransactionCase):
|
||||
field = etree.Element('td', {'t-field': u'company.name'})
|
||||
|
||||
with self.assertRaisesRegex(QWebException, r'^RTE widgets do not work correctly'):
|
||||
self.engine.render(field, {'company': None})
|
||||
self.engine._render(field, {'company': None})
|
||||
|
||||
def test_reject_t_tag(self):
|
||||
field = etree.Element('t', {'t-field': u'company.name'})
|
||||
|
||||
with self.assertRaisesRegex(QWebException, r'^t-field can not be used on a t element'):
|
||||
self.engine.render(field, {'company': None})
|
||||
self.engine._render(field, {'company': None})
|
||||
|
||||
def test_render_t_options(self):
|
||||
view1 = self.env['ir.ui.view'].create({
|
||||
@@ -72,7 +72,7 @@ class TestQWebTField(TransactionCase):
|
||||
<t t-name="base.dummy"><root><span t-esc="5" t-options="{'widget': 'char'}" t-options-widget="'float'" t-options-precision="4"/></root></t>
|
||||
"""
|
||||
})
|
||||
text = etree.fromstring(view1.render()).find('span').text
|
||||
text = etree.fromstring(view1._render()).find('span').text
|
||||
self.assertEqual(text, u'5.0000')
|
||||
|
||||
def test_xss_breakout(self):
|
||||
@@ -88,7 +88,7 @@ class TestQWebTField(TransactionCase):
|
||||
</t>
|
||||
"""
|
||||
})
|
||||
rendered = view.render({'malicious': '1</script><script>alert("pwned")</script><script>'}).decode()
|
||||
rendered = view._render({'malicious': '1</script><script>alert("pwned")</script><script>'}).decode()
|
||||
self.assertIn('alert', rendered, "%r doesn't seem to be rendered" % rendered)
|
||||
doc = etree.fromstring(rendered)
|
||||
self.assertEqual(len(doc.xpath('//script')), 1)
|
||||
@@ -119,7 +119,7 @@ class TestQWebNS(TransactionCase):
|
||||
""" % expected_result
|
||||
})
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), etree.fromstring(expected_result))
|
||||
self.assertEqual(etree.fromstring(view1._render()), etree.fromstring(expected_result))
|
||||
|
||||
def test_render_static_xml_with_namespace_2(self):
|
||||
""" Test the rendering on a namespaced view with no static content. The resulting string should be untouched.
|
||||
@@ -157,7 +157,7 @@ class TestQWebNS(TransactionCase):
|
||||
""" % expected_result
|
||||
})
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), etree.fromstring(expected_result))
|
||||
self.assertEqual(etree.fromstring(view1._render()), etree.fromstring(expected_result))
|
||||
|
||||
def test_render_static_xml_with_useless_distributed_namespace(self):
|
||||
""" Test that redundant namespaces are stripped upon rendering.
|
||||
@@ -190,7 +190,7 @@ class TestQWebNS(TransactionCase):
|
||||
</root>
|
||||
""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_static_xml_with_namespace_3(self):
|
||||
expected_result = u"""
|
||||
@@ -205,7 +205,7 @@ class TestQWebNS(TransactionCase):
|
||||
""" % expected_result
|
||||
})
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), etree.fromstring(expected_result))
|
||||
self.assertEqual(etree.fromstring(view1._render()), etree.fromstring(expected_result))
|
||||
|
||||
def test_render_dynamic_xml_with_namespace_t_esc(self):
|
||||
""" Test that rendering a template containing a node having both an ns declaration and a t-esc attribute correctly
|
||||
@@ -223,7 +223,7 @@ class TestQWebNS(TransactionCase):
|
||||
|
||||
expected_result = etree.fromstring(u"""<Invoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">test</Invoice>""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_dynamic_xml_with_namespace_t_esc_with_useless_distributed_namespace(self):
|
||||
""" Test that rendering a template containing a node having both an ns declaration and a t-esc attribute correctly
|
||||
@@ -247,7 +247,7 @@ class TestQWebNS(TransactionCase):
|
||||
</Invoice>
|
||||
""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_dynamic_xml_with_namespace_t_attf(self):
|
||||
""" Test that rendering a template containing a node having both an ns declaration and a t-attf attribute correctly
|
||||
@@ -287,7 +287,7 @@ class TestQWebNS(TransactionCase):
|
||||
</root>
|
||||
""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_dynamic_xml_with_namespace_t_attf_with_useless_distributed_namespace(self):
|
||||
""" Test that rendering a template containing a node having both an ns declaration and a t-attf attribute correctly
|
||||
@@ -329,7 +329,7 @@ class TestQWebNS(TransactionCase):
|
||||
|
||||
""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_dynamic_xml_with_namespace_2(self):
|
||||
view1 = self.env['ir.ui.view'].create({
|
||||
@@ -362,7 +362,7 @@ class TestQWebNS(TransactionCase):
|
||||
</Invoice>
|
||||
""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render({'version_id': 1.0})), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render({'version_id': 1.0})), expected_result)
|
||||
|
||||
def test_render_static_xml_with_namespaced_attributes(self):
|
||||
view1 = self.env['ir.ui.view'].create({
|
||||
@@ -377,7 +377,7 @@ class TestQWebNS(TransactionCase):
|
||||
|
||||
expected_result = etree.fromstring(u"""<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd">abc</cfdi:Comprobante>""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_dynamic_xml_with_namespaced_attributes(self):
|
||||
view1 = self.env['ir.ui.view'].create({
|
||||
@@ -392,7 +392,7 @@ class TestQWebNS(TransactionCase):
|
||||
|
||||
expected_result = etree.fromstring("""<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd">abc</cfdi:Comprobante>""")
|
||||
|
||||
self.assertEqual(etree.fromstring(view1.render()), expected_result)
|
||||
self.assertEqual(etree.fromstring(view1._render()), expected_result)
|
||||
|
||||
def test_render_static_xml_with_t_call(self):
|
||||
view1 = self.env['ir.ui.view'].create({
|
||||
@@ -426,7 +426,7 @@ class TestQWebNS(TransactionCase):
|
||||
"""
|
||||
})
|
||||
|
||||
result = view2.render()
|
||||
result = view2._render()
|
||||
result_etree = etree.fromstring(result)
|
||||
|
||||
# check that the root tag has all its xmlns
|
||||
@@ -490,7 +490,7 @@ class TestQWebNS(TransactionCase):
|
||||
""")
|
||||
|
||||
self.assertEqual(
|
||||
etree.fromstring(view1.with_context(check_view_ids=[view1.id, view2.id]).render()),
|
||||
etree.fromstring(view1.with_context(check_view_ids=[view1.id, view2.id])._render()),
|
||||
expected_result
|
||||
)
|
||||
|
||||
@@ -516,7 +516,7 @@ class TestQWebNS(TransactionCase):
|
||||
error_msg = e.args[0]
|
||||
|
||||
with self.assertRaises(QWebException, msg=error_msg):
|
||||
view1.render()
|
||||
view1._render()
|
||||
|
||||
def test_render_t_call_propagates_t_lang(self):
|
||||
current_lang = 'en_US'
|
||||
@@ -554,7 +554,7 @@ class TestQWebNS(TransactionCase):
|
||||
""" % other_lang
|
||||
})
|
||||
|
||||
rendered = view2.with_context(lang=current_lang).render().strip()
|
||||
rendered = view2.with_context(lang=current_lang)._render().strip()
|
||||
self.assertEqual(rendered, b'9/000/000*00')
|
||||
|
||||
|
||||
@@ -622,7 +622,7 @@ class TestQWeb(TransactionCase):
|
||||
|
||||
result = doc.find('result[@id="{}"]'.format(template)).text
|
||||
self.assertEqual(
|
||||
qweb.render(template, values=params, load=loader).strip(),
|
||||
qweb._render(template, values=params, load=loader).strip(),
|
||||
(result or u'').strip().encode('utf-8'),
|
||||
template
|
||||
)
|
||||
@@ -646,7 +646,7 @@ class TestPageSplit(TransactionCase):
|
||||
</t>
|
||||
'''
|
||||
})
|
||||
rendered = html.fromstring(self.env['ir.qweb'].render(t.id))
|
||||
rendered = html.fromstring(self.env['ir.qweb']._render(t.id))
|
||||
ref = E.div(
|
||||
E.table(E.tr()),
|
||||
E.div({'style': 'page-break-after: always'}),
|
||||
@@ -669,7 +669,7 @@ class TestPageSplit(TransactionCase):
|
||||
</t>
|
||||
'''
|
||||
})
|
||||
rendered = html.fromstring(self.env['ir.qweb'].render(t.id))
|
||||
rendered = html.fromstring(self.env['ir.qweb']._render(t.id))
|
||||
self.assertTreesEqual(
|
||||
rendered,
|
||||
E.div(
|
||||
@@ -694,7 +694,7 @@ class TestPageSplit(TransactionCase):
|
||||
</t>
|
||||
'''
|
||||
})
|
||||
rendered = html.fromstring(self.env['ir.qweb'].render(t.id))
|
||||
rendered = html.fromstring(self.env['ir.qweb']._render(t.id))
|
||||
self.assertTreesEqual(
|
||||
rendered,
|
||||
E.div(E.table(E.tr(), E.tr(), E.tr()))
|
||||
|
||||
@@ -28,6 +28,6 @@ class TestReports(odoo.tests.TransactionCase):
|
||||
report_records = report_records[:1]
|
||||
|
||||
# Test report generation
|
||||
report.render_qweb_html(report_records.ids)
|
||||
report._render_qweb_html(report_records.ids)
|
||||
else:
|
||||
continue
|
||||
|
||||
@@ -2855,8 +2855,8 @@ class TestQWebRender(ViewCase):
|
||||
})
|
||||
|
||||
# render view and child view with an id
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render(view1.id)
|
||||
content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render(view2.id)
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id])._render(view1.id)
|
||||
content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id])._render(view2.id)
|
||||
|
||||
self.assertEqual(content1, content2)
|
||||
|
||||
@@ -2866,14 +2866,14 @@ class TestQWebRender(ViewCase):
|
||||
self.env.cr.execute("INSERT INTO ir_model_data(name, model, res_id, module)"
|
||||
"VALUES ('dummy_ext', 'ir.ui.view', %s, 'base')" % view2.id)
|
||||
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render('base.dummy')
|
||||
content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render('base.dummy_ext')
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id])._render('base.dummy')
|
||||
content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id])._render('base.dummy_ext')
|
||||
|
||||
self.assertEqual(content1, content2)
|
||||
|
||||
# render view and primary extension with an id
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id]).render(view1.id)
|
||||
content3 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id]).render(view3.id)
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id])._render(view1.id)
|
||||
content3 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id])._render(view3.id)
|
||||
|
||||
self.assertNotEqual(content1, content3)
|
||||
|
||||
@@ -2881,8 +2881,8 @@ class TestQWebRender(ViewCase):
|
||||
self.env.cr.execute("INSERT INTO ir_model_data(name, model, res_id, module)"
|
||||
"VALUES ('dummy_primary_ext', 'ir.ui.view', %s, 'base')" % view3.id)
|
||||
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id]).render('base.dummy')
|
||||
content3 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id]).render('base.dummy_primary_ext')
|
||||
content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id])._render('base.dummy')
|
||||
content3 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id, view3.id])._render('base.dummy_primary_ext')
|
||||
|
||||
self.assertNotEqual(content1, content3)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class BaseDocumentLayout(models.TransientModel):
|
||||
for wizard in self:
|
||||
if wizard.report_layout_id:
|
||||
ir_qweb = wizard.env['ir.qweb']
|
||||
wizard.preview = ir_qweb.render('base.layout_preview', {'company': wizard})
|
||||
wizard.preview = ir_qweb._render('base.layout_preview', {'company': wizard})
|
||||
else:
|
||||
wizard.preview = False
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@ class TestAssetsBundleController(Controller):
|
||||
env = request.env(user=SUPERUSER_ID)
|
||||
bundle = env.ref('test_assetsbundle.bundle1')
|
||||
views = env['ir.ui.view'].search([('inherit_id', '=', bundle.id)])
|
||||
return views.with_context(check_view_ids=views.ids).render_template('test_assetsbundle.template1')
|
||||
return views.with_context(check_view_ids=views.ids)._render_template('test_assetsbundle.template1')
|
||||
|
||||
@@ -471,7 +471,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
|
||||
self.assertEqual(len(css_bundle), 1)
|
||||
|
||||
def test_20_exteral_lib_assets(self):
|
||||
html = self.env['ir.ui.view'].render_template('test_assetsbundle.template2')
|
||||
html = self.env['ir.ui.view']._render_template('test_assetsbundle.template2')
|
||||
attachments = self.env['ir.attachment'].search([('url', '=like', '/web/content/%-%/test_assetsbundle.bundle4.%')])
|
||||
self.assertEqual(len(attachments), 2)
|
||||
self.assertEqual(html.strip(), ("""<!DOCTYPE html>
|
||||
@@ -490,7 +490,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
|
||||
</html>""" % {"js": attachments[0].url, "css": attachments[1].url}).encode('utf8'))
|
||||
|
||||
def test_21_exteral_lib_assets_debug_mode(self):
|
||||
html = self.env['ir.ui.view'].render_template('test_assetsbundle.template2', {"debug": "assets"})
|
||||
html = self.env['ir.ui.view']._render_template('test_assetsbundle.template2', {"debug": "assets"})
|
||||
attachments = self.env['ir.attachment'].search([('url', '=like', '/web/content/%-%/test_assetsbundle.bundle4.%')])
|
||||
self.assertEqual(len(attachments), 0)
|
||||
self.assertEqual(html.strip(), ("""<!DOCTYPE html>
|
||||
|
||||
+1
-1
@@ -1219,7 +1219,7 @@ class Response(werkzeug.wrappers.Response):
|
||||
"""
|
||||
env = request.env(user=self.uid or request.uid or odoo.SUPERUSER_ID)
|
||||
self.qcontext['request'] = request
|
||||
return env["ir.ui.view"].render_template(self.template, self.qcontext)
|
||||
return env["ir.ui.view"]._render_template(self.template, self.qcontext)
|
||||
|
||||
def flatten(self):
|
||||
""" Forces the rendering of the response's template, sets the result
|
||||
|
||||
@@ -35,7 +35,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
|
||||
if not report_id:
|
||||
raise Exception("Required report does not exist: %s" % rname)
|
||||
|
||||
res_data, res_format = report_id.render(ids, data=data)
|
||||
res_data, res_format = report_id._render(ids, data=data)
|
||||
|
||||
if not res_data:
|
||||
raise ValueError("Report %s produced an empty result!" % rname)
|
||||
|
||||
Reference in New Issue
Block a user