1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from ast import literal_eval
from odoo import models, fields, api, SUPERUSER_ID
from odoo.http import request
from odoo.osv import expression
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, property_origins=None):
"""
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, property_origins).items()
if k in included or '_property' in v and v['_property']['field'] in included
}
@api.model
def get_authorized_fields(self, model_name, property_origins):
""" 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, JSON, and magic fields
# Remove string domains which are supposed to be evaluated
# (e.g. "[('product_id', '=', product_id)]")
# Expand properties fields
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', 'json'):
del fields_get[field]
elif fields_get[field]['type'] == 'properties':
property_field = fields_get[field]
del fields_get[field]
if property_origins:
# Add property pseudo-fields
# The properties of a property field are defined in a
# definition record (e.g. properties inside a project.task
# are defined inside its related project.project)
definition_record = property_field['definition_record']
if definition_record in property_origins:
definition_record_field = property_field['definition_record_field']
relation_field = fields_get[definition_record]
definition_model = self.env[relation_field['relation']]
if not property_origins[definition_record].isdigit():
# Do not fail on malformed forms.
continue
definition_record = definition_model.browse(int(property_origins[definition_record]))
properties_definitions = definition_record[definition_record_field]
for property_definition in properties_definitions:
if ((
property_definition['type'] in ['many2one', 'many2many']
and 'comodel' not in property_definition
) or (
property_definition['type'] == 'selection'
and not property_definition['selection']
) or (
property_definition['type'] == 'tags'
and not property_definition['tags']
) or (property_definition['type'] == 'separator')):
# Ignore non-fully defined properties
continue
property_definition['_property'] = {
'field': field,
}
property_definition['required'] = False
if 'domain' in property_definition and isinstance(property_definition['domain'], str):
property_definition['domain'] = literal_eval(property_definition['domain'])
try:
property_definition['domain'] = expression.normalize_domain(property_definition['domain'])
except Exception:
# Ignore non-fully defined properties
continue
fields_get[property_definition.get('name')] = property_definition
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.user.has_group('website.group_website_designer'):
return False
unexisting_fields = [field for field in fields if field not in self.env[model]._fields.keys()]
if unexisting_fields:
raise ValueError("Unable to whitelist field(s) %r for model %r." % (unexisting_fields, model))
# 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'
)
|