Files
odoo_source/addons/website/models/website_form.py
T
Romain DerieandVranckx Florian 7c6844fa6c [FIX] website: make forms work again when inside html fields
Recent commit [1] in Odoo 15 (which is a backport of commit [2] which
was merged in Odoo 17) introduced a security layer on forms but only
for forms which are inside `ir.ui.view`. The forms inside HTML fields
are thus not working anymore, because those don't receive the required
signature.

For the record:
- `ir.ui.view` = `website.page` pages, some part of the controller pages
- HTML fields = the most part of the editable areas in controller pages

[1]: https://github.com/odoo/odoo/commit/17c6f6f30bf13bd3c303b28d9a314bd76dd8f4dc
[2]: https://github.com/odoo/odoo/commit/7d25e7bf9243367c87b1b2005587ae728730b49c

opw-3586333

closes odoo/odoo#143139

closes odoo/odoo#143879

X-original-commit: 5e5a14ae4a522a589f6112d1687bb910f86f9a6d
Signed-off-by: Jérémy Kersten <jke@odoo.com>
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Co-authored-by: Vranckx Florian (flvr) <flvr@odoo.com>
Co-authored-by: Romain Derie <rde@odoo.com>
2023-11-30 07:37:14 +00:00

133 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, api, SUPERUSER_ID
from odoo.http import request
class website_form_config(models.Model):
_inherit = 'website'
def _website_form_last_record(self):
if request and request.session.form_builder_model_model:
return request.env[request.session.form_builder_model_model].browse(request.session.form_builder_id)
return False
class website_form_model(models.Model):
_name = 'ir.model'
_description = 'Models'
_inherit = 'ir.model'
website_form_access = fields.Boolean('Allowed to use in forms', help='Enable the form builder feature for this model.')
website_form_default_field_id = fields.Many2one('ir.model.fields', 'Field for custom form data', domain="[('model', '=', model), ('ttype', '=', 'text')]", help="Specify the field which will contain meta and custom form fields datas.")
website_form_label = fields.Char("Label for form action", help="Form action label. Ex: crm.lead could be 'Send an e-mail' and project.issue could be 'Create an Issue'.")
website_form_key = fields.Char(help='Used in FormBuilder Registry')
def _get_form_writable_fields(self):
"""
Restriction of "authorized fields" (fields which can be used in the
form builders) to fields which have actually been opted into form
builders and are writable. By default no field is writable by the
form builder.
"""
if self.model == "mail.mail":
included = {'email_from', 'email_to', 'email_cc', 'email_bcc', 'body', 'reply_to', 'subject'}
else:
included = {
field.name
for field in self.env['ir.model.fields'].sudo().search([
('model_id', '=', self.id),
('website_form_blacklisted', '=', False)
])
}
return {
k: v for k, v in self.get_authorized_fields(self.model).items()
if k in included
}
@api.model
def get_authorized_fields(self, model_name):
""" Return the fields of the given model name as a mapping like method `fields_get`. """
model = self.env[model_name]
fields_get = model.fields_get()
for key, val in model._inherits.items():
fields_get.pop(val, None)
# Unrequire fields with default values
default_values = model.with_user(SUPERUSER_ID).default_get(list(fields_get))
for field in [f for f in fields_get if f in default_values]:
fields_get[field]['required'] = False
# Remove readonly and magic fields
# Remove string domains which are supposed to be evaluated
# (e.g. "[('product_id', '=', product_id)]")
for field in list(fields_get):
if 'domain' in fields_get[field] and isinstance(fields_get[field]['domain'], str):
del fields_get[field]['domain']
if fields_get[field].get('readonly') or field in models.MAGIC_COLUMNS or \
fields_get[field]['type'] in ['many2one_reference', 'properties']:
del fields_get[field]
return fields_get
@api.model
def get_compatible_form_models(self):
if not self.env.user.has_group('website.group_website_restricted_editor'):
return []
return self.sudo().search_read(
[('website_form_access', '=', True)],
['id', 'model', 'name', 'website_form_label', 'website_form_key'],
)
class website_form_model_fields(models.Model):
""" fields configuration for form builder """
_name = 'ir.model.fields'
_description = 'Fields'
_inherit = 'ir.model.fields'
def init(self):
# set all existing unset website_form_blacklisted fields to ``true``
# (so that we can use it as a whitelist rather than a blacklist)
self._cr.execute('UPDATE ir_model_fields'
' SET website_form_blacklisted=true'
' WHERE website_form_blacklisted IS NULL')
# add an SQL-level default value on website_form_blacklisted to that
# pure-SQL ir.model.field creations (e.g. in _reflect) generate
# the right default value for a whitelist (aka fields should be
# blacklisted by default)
self._cr.execute('ALTER TABLE ir_model_fields '
' ALTER COLUMN website_form_blacklisted SET DEFAULT true')
@api.model
def formbuilder_whitelist(self, model, fields):
"""
:param str model: name of the model on which to whitelist fields
:param list(str) fields: list of fields to whitelist on the model
:return: nothing of import
"""
# postgres does *not* like ``in [EMPTY TUPLE]`` queries
if not fields:
return False
# only allow users who can change the website structure
if not self.env['res.users'].has_group('website.group_website_designer'):
return False
# the ORM only allows writing on custom fields and will trigger a
# registry reload once that's happened. We want to be able to
# whitelist non-custom fields and the registry reload absolutely
# isn't desirable, so go with a method and raw SQL
self.env.cr.execute(
"UPDATE ir_model_fields"
" SET website_form_blacklisted=false"
" WHERE model=%s AND name in %s", (model, tuple(fields)))
return True
website_form_blacklisted = fields.Boolean(
'Blacklisted in web forms', default=True, index=True,
help='Blacklist this field for web forms'
)