[IMP] website, website_sale: remove most escaping from Dynamic Products

Before this commit the filtering code for the Dynamic Products snippet
was rendering all its values.

After this commit the filtering code for the Dynamic Products snippet
mostly returns the values to be rendered.
Because the list_price still needs to be rendered with the correct
currency, the general rendering mechanism is being informed through the
field meta-data that the escaping was already done.

task-2446024
https://github.com/odoo/odoo/pull/65176
This commit is contained in:
Benoit Socias
2021-02-16 10:18:15 +00:00
parent df7c77fd7a
commit 1d2dd3f76f
3 changed files with 75 additions and 58 deletions
+7 -5
View File
@@ -723,10 +723,11 @@
</record>
<!-- Template for Dynamic Snippet -->
<template id="dynamic_filter_template_image_title_footer" name="Image Title Footer Card">
<t t-foreach="records" t-as="record">
<t t-foreach="to_generic(records)" t-as="record">
<t t-set="fields" t-value="list(record['fields'].values())"/>
<t t-set="fieldsImg" t-value="list(record['image_fields'].values())"/>
<div class="card h-100" t-att-data-url="record['fields']['call_to_action_url']">
<t t-set="call_to_action_url" t-value="record['call_to_action_url']"/>
<div class="card h-100" t-att-data-url="call_to_action_url">
<h4 t-if="is_sample" class="o_ribbon_right bg-warning text-uppercase">Sample</h4>
<img t-if="len(fieldsImg) > 0" class="card-img-top p-3" loading="lazy" t-att-src="fieldsImg[0]"/>
<div class="card-body">
@@ -741,7 +742,7 @@
<div t-if="len(fields) > 2" class="card-text">
<t t-raw="fields[2]"/>
</div>
<a t-if="call_to_action" class="btn btn-primary ml-auto" t-att-href="record['fields']['call_to_action_url']">
<a t-if="call_to_action" class="btn btn-primary ml-auto" t-att-href="call_to_action_url">
<i class="fa fa-fw fa-eye"/>
</a>
</div>
@@ -749,10 +750,11 @@
</t>
</template>
<template id="dynamic_filter_template_header_image_footer_card" name="Header Image Footer Card">
<t t-foreach="records" t-as="record">
<t t-foreach="to_generic(records)" t-as="record">
<t t-set="fields" t-value="list(record['fields'].values())"/>
<t t-set="fieldsImg" t-value="list(record['image_fields'].values())"/>
<div class="card h-100" t-att-data-url="record['fields']['call_to_action_url']">
<t t-set="call_to_action_url" t-value="record['call_to_action_url']"/>
<div class="card h-100" t-att-data-url="call_to_action_url">
<h4 t-if="is_sample" class="o_ribbon_right bg-warning text-uppercase">Sample</h4>
<h5 t-if="len(fields) > 0" class="card-header">
<t t-raw="fields[0]"/>
+61 -38
View File
@@ -60,15 +60,21 @@ class WebsiteSnippetFilter(models.Model):
if self.website_id and self.env['website'].get_current_website() != self.website_id:
return ''
records = self._prepare_values(limit, search_domain)
meta_data = self._get_filter_meta_data()
records = self._prepare_values(meta_data, limit, search_domain)
is_sample = with_sample and not records
if is_sample:
records = self._prepare_sample()
records = self._prepare_sample(meta_data)
View = self.env['ir.ui.view'].sudo().with_context(inherit_branding=False)
content = View._render_template(template_key, dict(records=records, is_sample=is_sample)).decode('utf-8')
escaped_records = self._record_to_escaped_values(meta_data, records)
content = View._render_template(template_key, dict(
records=escaped_records,
is_sample=is_sample,
to_generic=lambda record: self._to_generic(meta_data, records)
)).decode('utf-8')
return [ET.tostring(el) for el in ET.fromstring('<root>%s</root>' % content).getchildren()]
def _prepare_values(self, limit=None, search_domain=None):
def _prepare_values(self, meta_data, limit=None, search_domain=None):
"""Gets the data and returns it the right format for render."""
self.ensure_one()
limit = limit and min(limit, self.limit) or self.limit
@@ -85,7 +91,7 @@ class WebsiteSnippetFilter(models.Model):
order=','.join(literal_eval(filter_sudo.sort)) or None,
limit=limit
)
return self._filter_records_to_dict_values(records)
return self._filter_records_to_values(meta_data, records)
except MissingError:
_logger.warning("The provided domain %s in 'ir.filters' generated a MissingError in '%s'", domain, self._name)
return []
@@ -95,14 +101,14 @@ class WebsiteSnippetFilter(models.Model):
dynamic_filter=self,
limit=limit,
search_domain=search_domain,
get_rendering_data_structure=self._get_rendering_data_structure,
).sudo().run()
meta_data=meta_data,
).sudo().run() or []
except MissingError:
_logger.warning("The provided domain %s in 'ir.actions.server' generated a MissingError in '%s'", search_domain, self._name)
return []
def _record_to_dict_values(self, meta_data, records):
"""Converts a list of dictionary records into the structure used by the template for rendering:
def _to_generic(self, meta_data, records):
"""Converts a list of dictionary records into the structure used by generic templates for rendering:
[{
'fields':
@@ -114,12 +120,13 @@ class WebsiteSnippetFilter(models.Model):
OrderedDict([
('image', '/web/image/res.country/3/image?unique=5d9b44e')
]),
'call_to_action_url': 'some/location'
}, ... , ...]
@param meta_data: OrderedDict result of _get_filter_meta_data
@param records: records as dictionaries of non-escaped values associated to field names
@return List of Dict matching the rendering structure
@return List of Dict matching the rendering structure for generic templates
"""
result = []
for record in records:
@@ -127,20 +134,44 @@ class WebsiteSnippetFilter(models.Model):
for field_name, field_widget in meta_data.items():
value = record[field_name]
if field_widget in ('binary', 'image'):
data['image_fields'][field_name] = self.escape_falsy_as_empty(value)
data['image_fields'][field_name] = value
else:
data['fields'][field_name] = value
data['call_to_action_url'] = record['call_to_action_url']
result.append(data)
return result
def _record_to_escaped_values(self, meta_data, records):
"""Converts a list of dictionary records into rendered values
@param meta_data: OrderedDict result of _get_filter_meta_data
@param records: records as dictionaries of non-escaped values associated to field names
@return List of Dict containing rendered values of each field
"""
result = []
for record in records:
data = {}
for field_name, field_widget in meta_data.items():
value = record[field_name]
if field_widget in ('binary', 'image'):
data[field_name] = self.escape_falsy_as_empty(value)
elif field_widget == 'monetary':
FieldMonetary = self.env['ir.qweb.field.monetary']
website_currency = self._get_website_currency()
data['fields'][field_name] = FieldMonetary.value_to_html(
data[field_name] = FieldMonetary.value_to_html(
value,
{'display_currency': website_currency}
)
elif 'ir.qweb.field.%s' % field_widget in self.env:
data['fields'][field_name] = self.env['ir.qweb.field.%s' % field_widget].value_to_html(
value, {})
options = {}
if field_widget == 'html':
options['template_options'] = {}
data[field_name] = self.env['ir.qweb.field.%s' % field_widget].value_to_html(
value, options)
else:
data['fields'][field_name] = self.escape_falsy_as_empty(value)
data['fields']['call_to_action_url'] = record['call_to_action_url']
data[field_name] = self.escape_falsy_as_empty(value)
data['call_to_action_url'] = record['call_to_action_url']
result.append(data)
return result
@@ -179,10 +210,11 @@ class WebsiteSnippetFilter(models.Model):
meta_data[field_name] = field_widget
return meta_data
def _prepare_sample(self, length=4):
def _prepare_sample(self, meta_data, length=4):
"""
Generates sample data and returns it the right format for render.
@param meta_data: OrderedDict result of _get_filter_meta_data
@param length: Number of sample records to generate
@return Array of objets with a value associated to each name in field_names
@@ -190,22 +222,21 @@ class WebsiteSnippetFilter(models.Model):
if not length:
return []
sample = []
meta_data = self._get_filter_meta_data()
model = self.env[self.filter_id.model_id] if self.filter_id else (
self.action_server_id.model_id if self.action_server_id else None)
sample_data = self._get_hardcoded_sample(model)
for index in range(0, length):
single_sample_data = sample_data[index % len(sample_data)].copy()
self._fill_sample(single_sample_data, meta_data, index)
self._fill_sample(meta_data, single_sample_data, index)
sample.append(single_sample_data)
return self._record_to_dict_values(meta_data, sample)
return sample
def _fill_sample(self, sample, meta_data, index):
def _fill_sample(self, meta_data, sample, index):
"""
Fills the sample for the given model
Fills the missing fields of a sample
@param meta_data: OrderedDict result of _get_filter_meta_data
@param sample: Data structure to fill with values for each name in field_names
@param model: Model to which the sample belongs
@param index: Index of the sample within the dataset
"""
for field_name, field_widget in meta_data.items():
@@ -239,27 +270,19 @@ class WebsiteSnippetFilter(models.Model):
'image_fields': OrderedDict({}),
}
def _filter_records_to_dict_values(self, records):
"""Extract the fields from the data source and put them into a dictionary of values
def _filter_records_to_values(self, meta_data, records):
"""
Extract the fields from the data source and put them into a dictionary of values
[{
'fields':
OrderedDict([
('name', 'Afghanistan'),
('code', 'AF'),
]),
'image_fields':
OrderedDict([
('image', '/web/image/res.country/3/image?unique=5d9b44e')
]),
}, ... , ...]
@param meta_data: OrderedDict result of _get_filter_meta_data
@param records: Model records returned by the filter
@return List of dict associating the field value to each field name
"""
self.ensure_one()
values = []
model = self.env[self.filter_id.model_id]
meta_data = self._get_filter_meta_data()
Website = self.env['website']
for record in records:
data = {}
@@ -288,7 +311,7 @@ class WebsiteSnippetFilter(models.Model):
data['call_to_action_url'] = 'website_url' in record and record['website_url']
values.append(data)
return self._record_to_dict_values(meta_data, values)
return values
@api.model
def _get_website_currency(self):
+7 -15
View File
@@ -59,19 +59,14 @@ FieldMonetary = model.env['ir.qweb.field.monetary']
website = request.website.get_current_website()
dynamic_filter = model.env.context.get('dynamic_filter')
meta_data = model.env.context.get('meta_data')
limit = model.env.context.get('limit')
search_domain = model.env.context.get('search_domain')
get_rendering_data_structure = model.env.context.get('get_rendering_data_structure')
escape = dynamic_filter.escape_falsy_as_empty
domain = [('website_published', '=', True)] + website.website_domain() + (search_domain or [])
products = ProductProduct.search(domain, limit=limit)
_ = products.mapped('name')
monetary_options = {
'display_currency': request.website.get_current_pricelist().currency_id,
}
max_nb_chars = 100
res_products = []
for product in products:
@@ -80,17 +75,14 @@ for product in products:
if res_product['description_sale'] and len(res_product['description_sale']) > max_nb_chars:
res_product['description_sale'] = "%s ..." % res_product['description_sale'][:max_nb_chars]
res_product['list_price'] = FieldMonetary.value_to_html(res_product['price'], monetary_options)
data = get_rendering_data_structure()
for field_name in dynamic_filter.field_names.split(","):
data = {}
for field_name in meta_data.keys():
field = ProductProduct._fields.get(field_name)
if field and field.type == 'binary':
data['image_fields'][field_name] = escape(website.image_url(product, field_name))
elif field_name == 'list_price':
data['fields'][field_name] = res_product[field_name]
data[field_name] = website.image_url(product, field_name)
else:
data['fields'][field_name] = escape(res_product[field_name])
data['fields']['call_to_action_url'] = escape(product['website_url'])
data[field_name] = res_product[field_name]
data['call_to_action_url'] = product['website_url']
res_products.append(data)
response = res_products
@@ -99,7 +91,7 @@ response = res_products
<!-- Dynamic Filter -->
<record id="dynamic_filter_demo_products" model="website.snippet.filter">
<field name="action_server_id" ref="website_sale.dynamic_snippet_products_action"/>
<field name="field_names">display_name,description_sale,image_512,list_price</field>
<field name="field_names">display_name,description_sale,image_512,price:monetary</field>
<field name="limit" eval="16"/>
<field name="name">Products</field>
</record>